summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CLOBBER2
-rw-r--r--README.md26
-rw-r--r--accessible/windows/ia2/ia2Accessible.cpp88
-rw-r--r--accessible/windows/ia2/ia2AccessibleAction.cpp24
-rw-r--r--accessible/windows/ia2/ia2AccessibleComponent.cpp12
-rw-r--r--accessible/windows/ia2/ia2AccessibleEditableText.cpp24
-rw-r--r--accessible/windows/ia2/ia2AccessibleHyperlink.cpp20
-rw-r--r--accessible/windows/ia2/ia2AccessibleHypertext.cpp12
-rw-r--r--accessible/windows/ia2/ia2AccessibleImage.cpp12
-rw-r--r--accessible/windows/ia2/ia2AccessibleRelation.cpp20
-rw-r--r--accessible/windows/ia2/ia2AccessibleTable.cpp124
-rw-r--r--accessible/windows/ia2/ia2AccessibleTableCell.cpp36
-rw-r--r--accessible/windows/ia2/ia2AccessibleText.cpp76
-rw-r--r--accessible/windows/ia2/ia2AccessibleValue.cpp16
-rw-r--r--accessible/windows/msaa/AccessibleWrap.cpp96
-rw-r--r--accessible/windows/msaa/ApplicationAccessibleWrap.cpp16
-rw-r--r--accessible/windows/msaa/DocAccessibleWrap.cpp4
-rw-r--r--accessible/windows/msaa/EnumVariant.cpp16
-rw-r--r--accessible/windows/msaa/IUnknownImpl.cpp17
-rw-r--r--accessible/windows/msaa/IUnknownImpl.h24
-rw-r--r--accessible/windows/sdn/sdnAccessible.cpp64
-rw-r--r--accessible/windows/sdn/sdnDocAccessible.cpp24
-rw-r--r--accessible/windows/sdn/sdnTextAccessible.cpp20
-rw-r--r--accessible/windows/uia/uiaRawElmProvider.cpp32
-rw-r--r--browser/app/profile/firefox.js7
-rw-r--r--browser/base/content/aboutDialog.js21
-rw-r--r--browser/base/content/aboutDialog.xul14
-rw-r--r--browser/base/content/browser-media.js15
-rw-r--r--browser/base/content/browser-menubar.inc11
-rw-r--r--browser/base/content/browser-places.js4
-rwxr-xr-xbrowser/base/content/browser.js33
-rw-r--r--browser/base/content/browser.xul2
-rw-r--r--browser/base/content/overrides/app-license.html3
-rw-r--r--browser/base/content/sync/customize.xul7
-rw-r--r--browser/base/content/tab-content.js4
-rw-r--r--browser/base/content/tabbrowser.xml19
-rw-r--r--browser/base/content/urlbarBindings.xml7
-rw-r--r--browser/base/content/win6BrowserOverlay.xul12
-rw-r--r--browser/base/jar.mn12
-rw-r--r--browser/branding/official/bgintro.bmpbin682144 -> 0 bytes
-rw-r--r--browser/branding/official/branding.nsi2
-rw-r--r--browser/branding/official/clock.bmpbin8982 -> 0 bytes
-rw-r--r--browser/branding/official/moz.build52
-rw-r--r--browser/branding/official/particles.bmpbin8982 -> 0 bytes
-rw-r--r--browser/branding/official/pencil-rtl.bmpbin8982 -> 0 bytes
-rw-r--r--browser/branding/official/pencil.bmpbin8982 -> 0 bytes
-rw-r--r--browser/branding/official/pref/firefox-branding.js76
-rw-r--r--browser/branding/shared/branding.mozbuild (renamed from browser/branding/branding-common.mozbuild)24
-rw-r--r--browser/branding/shared/dsstore (renamed from browser/branding/official/dsstore)bin12292 -> 12292 bytes
-rw-r--r--browser/branding/shared/newtab.ico (renamed from browser/branding/official/newtab.ico)bin6518 -> 6518 bytes
-rw-r--r--browser/branding/shared/newwindow.ico (renamed from browser/branding/official/newwindow.ico)bin6518 -> 6518 bytes
-rw-r--r--browser/branding/shared/pbmode.ico (renamed from browser/branding/official/pbmode.ico)bin6518 -> 6518 bytes
-rw-r--r--browser/branding/shared/preferences.inc33
-rw-r--r--browser/branding/shared/uaoverrides.inc38
-rw-r--r--browser/branding/unofficial/basilisk.VisualElementsManifest.xml (renamed from browser/branding/unofficial/firefox.VisualElementsManifest.xml)0
-rw-r--r--browser/branding/unofficial/bgintro.bmpbin682144 -> 0 bytes
-rw-r--r--browser/branding/unofficial/clock.bmpbin124214 -> 0 bytes
-rw-r--r--browser/branding/unofficial/moz.build4
-rw-r--r--browser/branding/unofficial/newtab.icobin6518 -> 0 bytes
-rw-r--r--browser/branding/unofficial/newwindow.icobin6518 -> 0 bytes
-rw-r--r--browser/branding/unofficial/particles.bmpbin124216 -> 0 bytes
-rw-r--r--browser/branding/unofficial/pbmode.icobin6518 -> 0 bytes
-rw-r--r--browser/branding/unofficial/pencil-rtl.bmpbin124214 -> 0 bytes
-rw-r--r--browser/branding/unofficial/pencil.bmpbin124214 -> 0 bytes
-rw-r--r--browser/branding/unofficial/pref/firefox-branding.js47
-rw-r--r--browser/components/build/moz.build22
-rw-r--r--browser/components/customizableui/CustomizableUI.jsm14
-rw-r--r--browser/components/customizableui/CustomizableWidgets.jsm5
-rw-r--r--browser/components/dirprovider/DirectoryProvider.cpp2
-rw-r--r--browser/components/feeds/nsFeedSniffer.cpp15
-rw-r--r--browser/components/feeds/nsFeedSniffer.h2
-rw-r--r--browser/components/migration/nsIEHistoryEnumerator.cpp5
-rw-r--r--browser/components/nsBrowserGlue.js4
-rw-r--r--browser/components/preferences/in-content/preferences.xul8
-rw-r--r--browser/components/preferences/in-content/security.xul4
-rw-r--r--browser/components/shell/ShellService.jsm4
-rw-r--r--browser/components/shell/nsGNOMEShellService.cpp25
-rw-r--r--browser/components/shell/nsGNOMEShellService.h2
-rw-r--r--browser/components/shell/nsMacShellService.cpp2
-rw-r--r--browser/components/shell/nsWindowsShellService.cpp25
-rw-r--r--browser/components/shell/nsWindowsShellService.h2
-rw-r--r--browser/components/webextensions/extension-win-panel.css4
-rw-r--r--browser/config/mozconfigs/win32/common-opt1
-rw-r--r--browser/config/mozconfigs/win32/debug1
-rw-r--r--browser/config/version.txt2
-rw-r--r--browser/config/version_display.txt2
-rwxr-xr-xbrowser/confvars.sh10
-rw-r--r--browser/installer/Makefile.in3
-rw-r--r--browser/installer/allowed-dupes.mn12
-rw-r--r--browser/installer/package-manifest.in12
-rw-r--r--browser/installer/windows/Makefile.in10
-rw-r--r--browser/installer/windows/app.tag2
-rw-r--r--browser/installer/windows/nsis/defines.nsi.in26
-rwxr-xr-xbrowser/installer/windows/nsis/installer.nsi61
-rw-r--r--browser/installer/windows/nsis/maintenanceservice_installer.nsi5
-rw-r--r--browser/installer/windows/nsis/oneoff_en-US.nsh12
-rwxr-xr-xbrowser/installer/windows/nsis/shared.nsh100
-rw-r--r--browser/installer/windows/nsis/stub.nsi2093
-rwxr-xr-xbrowser/installer/windows/nsis/uninstaller.nsi6
-rw-r--r--browser/installer/windows/stub.tag4
-rw-r--r--browser/locales/en-US/chrome/browser/aboutHome.dtd2
-rw-r--r--browser/locales/en-US/chrome/browser/browser.dtd6
-rw-r--r--browser/locales/en-US/chrome/browser/browser.properties9
-rw-r--r--browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties4
-rw-r--r--browser/locales/en-US/chrome/browser/migration/migration.dtd3
-rw-r--r--browser/locales/en-US/chrome/browser/preferences/preferences.dtd1
-rw-r--r--browser/locales/en-US/chrome/browser/preferences/security.dtd3
-rw-r--r--browser/locales/en-US/chrome/browser/syncCustomize.dtd3
-rw-r--r--browser/locales/en-US/chrome/browser/syncSetup.dtd2
-rw-r--r--browser/themes/shared/incontentprefs/preferences.inc.css5
-rw-r--r--browser/themes/windows/Info-XP.pngbin590 -> 0 bytes
-rw-r--r--browser/themes/windows/Privacy-16-XP.pngbin799 -> 0 bytes
-rw-r--r--browser/themes/windows/Toolbar-XP.pngbin19638 -> 0 bytes
-rw-r--r--browser/themes/windows/Toolbar-lunaSilver.pngbin19034 -> 0 bytes
-rw-r--r--browser/themes/windows/Toolbar-win7.png (renamed from browser/themes/windows/Toolbar-aero.png)bin18276 -> 18276 bytes
-rw-r--r--browser/themes/windows/Toolbar-win7@2x.png (renamed from browser/themes/windows/Toolbar-aero@2x.png)bin47581 -> 47581 bytes
-rw-r--r--browser/themes/windows/actionicon-tab-win7.png (renamed from browser/themes/windows/actionicon-tab-XPVista7.png)bin421 -> 421 bytes
-rw-r--r--browser/themes/windows/browser-aero.css488
-rw-r--r--browser/themes/windows/browser.css210
-rw-r--r--browser/themes/windows/caption-buttons.svg133
-rw-r--r--browser/themes/windows/customizableui/panelUI.css26
-rw-r--r--browser/themes/windows/devedition.css19
-rw-r--r--browser/themes/windows/downloads/allDownloadsViewOverlay.css52
-rw-r--r--browser/themes/windows/downloads/download-glow-menuPanel-win7.png (renamed from browser/themes/windows/downloads/download-glow-menuPanel-XPVista7.png)bin893 -> 893 bytes
-rw-r--r--browser/themes/windows/downloads/download-glow-win7.png (renamed from browser/themes/windows/downloads/download-glow-XPVista7.png)bin494 -> 494 bytes
-rw-r--r--browser/themes/windows/downloads/indicator.css8
-rw-r--r--browser/themes/windows/feeds/feedIcon-XP.pngbin1770 -> 0 bytes
-rw-r--r--browser/themes/windows/feeds/feedIcon16-XP.pngbin762 -> 0 bytes
-rw-r--r--browser/themes/windows/jar.mn137
-rw-r--r--browser/themes/windows/livemark-folder-XP.pngbin667 -> 0 bytes
-rw-r--r--browser/themes/windows/menu-back-XP.pngbin341 -> 0 bytes
-rw-r--r--browser/themes/windows/menu-forward-XP.pngbin342 -> 0 bytes
-rw-r--r--browser/themes/windows/pageInfo-XP.pngbin7739 -> 0 bytes
-rw-r--r--browser/themes/windows/places/allBookmarks-XP.pngbin504 -> 0 bytes
-rw-r--r--browser/themes/windows/places/autocomplete-star-win7.png (renamed from browser/themes/windows/places/autocomplete-star-XPVista7.png)bin813 -> 813 bytes
-rw-r--r--browser/themes/windows/places/bookmarksMenu-XP.pngbin334 -> 0 bytes
-rw-r--r--browser/themes/windows/places/bookmarksToolbar-XP.pngbin229 -> 0 bytes
-rw-r--r--browser/themes/windows/places/bookmarksToolbar-menuPanel-XP.pngbin689 -> 0 bytes
-rw-r--r--browser/themes/windows/places/calendar-XP.pngbin559 -> 0 bytes
-rw-r--r--browser/themes/windows/places/history-XP.pngbin821 -> 0 bytes
-rw-r--r--browser/themes/windows/places/libraryToolbar-XP.pngbin2037 -> 0 bytes
-rw-r--r--browser/themes/windows/places/organizer.css58
-rw-r--r--browser/themes/windows/places/places.css18
-rw-r--r--browser/themes/windows/places/query-XP.pngbin612 -> 0 bytes
-rw-r--r--browser/themes/windows/places/starred48-XP.pngbin1848 -> 0 bytes
-rw-r--r--browser/themes/windows/places/tag-XP.pngbin480 -> 0 bytes
-rw-r--r--browser/themes/windows/places/toolbarDropMarker-XP.pngbin219 -> 0 bytes
-rw-r--r--browser/themes/windows/places/unsortedBookmarks-XP.pngbin712 -> 0 bytes
-rw-r--r--browser/themes/windows/preferences/alwaysAsk-XP.pngbin408 -> 0 bytes
-rw-r--r--browser/themes/windows/preferences/application-XP.pngbin388 -> 0 bytes
-rw-r--r--browser/themes/windows/preferences/saveFile-XP.pngbin740 -> 0 bytes
-rw-r--r--browser/themes/windows/privatebrowsing-mask-tabstrip-win7.png (renamed from browser/themes/windows/privatebrowsing-mask-tabstrip-XPVista7.png)bin949 -> 949 bytes
-rw-r--r--browser/themes/windows/privatebrowsing-mask-titlebar-win7-tall.png (renamed from browser/themes/windows/privatebrowsing-mask-titlebar-XPVista7-tall.png)bin940 -> 940 bytes
-rw-r--r--browser/themes/windows/privatebrowsing-mask-titlebar-win7.png (renamed from browser/themes/windows/privatebrowsing-mask-titlebar-XPVista7.png)bin860 -> 860 bytes
-rw-r--r--browser/themes/windows/reload-stop-go-win7.png (renamed from browser/themes/windows/reload-stop-go-XPVista7.png)bin1944 -> 1944 bytes
-rw-r--r--browser/themes/windows/reload-stop-go-win7@2x.png (renamed from browser/themes/windows/reload-stop-go-XPVista7@2x.png)bin3661 -> 3661 bytes
-rw-r--r--browser/themes/windows/sync-horizontalbar-win7.png (renamed from browser/themes/windows/sync-horizontalbar-XPVista7.png)bin719 -> 719 bytes
-rw-r--r--browser/themes/windows/sync-horizontalbar-win7@2x.png (renamed from browser/themes/windows/sync-horizontalbar-XPVista7@2x.png)bin1702 -> 1702 bytes
-rw-r--r--browser/themes/windows/syncProgress-horizontalbar-win7.png (renamed from browser/themes/windows/syncProgress-horizontalbar-XPVista7.png)bin11892 -> 11892 bytes
-rw-r--r--browser/themes/windows/syncProgress-horizontalbar-win7@2x.png (renamed from browser/themes/windows/syncProgress-horizontalbar-XPVista7@2x.png)bin27608 -> 27608 bytes
-rw-r--r--browser/themes/windows/syncProgress-toolbar-win7.png (renamed from browser/themes/windows/syncProgress-toolbar-XPVista7.png)bin13293 -> 13293 bytes
-rw-r--r--browser/themes/windows/syncProgress-toolbar-win7@2x.png (renamed from browser/themes/windows/syncProgress-toolbar-XPVista7@2x.png)bin33662 -> 33662 bytes
-rw-r--r--browser/themes/windows/tabbrowser/newtab-inverted-win7.svg (renamed from browser/themes/windows/tabbrowser/newtab-inverted-XPVista7.svg)0
-rw-r--r--browser/themes/windows/tabbrowser/newtab-win7.svg (renamed from browser/themes/windows/tabbrowser/newtab-XPVista7.svg)0
-rw-r--r--browser/themes/windows/tabbrowser/tab-arrow-left-win7.svg (renamed from browser/themes/windows/tabbrowser/tab-arrow-left-XPVista7.svg)0
-rw-r--r--browser/themes/windows/toolbarbutton-dropdown-arrow-win7.png (renamed from browser/themes/windows/toolbarbutton-dropdown-arrow-XPVista7.png)bin208 -> 208 bytes
-rw-r--r--browser/themes/windows/urlbar-history-dropmarker-win7.png (renamed from browser/themes/windows/urlbar-history-dropmarker-XPVista7.png)bin479 -> 479 bytes
-rw-r--r--browser/themes/windows/urlbar-history-dropmarker-win7@2x.png (renamed from browser/themes/windows/urlbar-history-dropmarker-XPVista7@2x.png)bin788 -> 788 bytes
-rw-r--r--browser/themes/windows/windowsShared.inc2
-rw-r--r--build/mach_bootstrap.py2
-rw-r--r--build/moz.configure/old.configure7
-rw-r--r--config/external/nss/nss.symbols11
-rw-r--r--config/milestone.txt2
-rw-r--r--devtools/client/scratchpad/scratchpad.xul2
-rw-r--r--devtools/client/shared/curl.js8
-rw-r--r--devtools/moz.build8
-rw-r--r--devtools/server/actors/moz.build5
-rw-r--r--devtools/server/actors/webbrowser.js4
-rw-r--r--docshell/base/moz.build3
-rw-r--r--docshell/base/nsAboutRedirector.cpp10
-rw-r--r--docshell/base/nsDSURIContentListener.cpp86
-rw-r--r--docshell/base/nsDSURIContentListener.h21
-rw-r--r--docshell/base/nsDocShell.cpp23
-rw-r--r--docshell/build/moz.build3
-rw-r--r--docshell/build/nsDocShellModule.cpp2
-rw-r--r--docshell/test/file_bug1151421.html19
-rw-r--r--docshell/test/mochitest.ini2
-rw-r--r--docshell/test/navigation/file_scrollRestoration.html14
-rw-r--r--docshell/test/test_bug1151421.html61
-rw-r--r--docshell/test/test_bug1186774.html2
-rw-r--r--docshell/test/test_bug590573.html16
-rw-r--r--docshell/test/test_bug653741.html4
-rw-r--r--docshell/test/test_bug662170.html2
-rw-r--r--dom/base/Navigator.cpp10
-rw-r--r--dom/base/nsDocument.cpp10
-rw-r--r--dom/base/nsDocument.h1
-rw-r--r--dom/base/nsGlobalWindow.cpp12
-rw-r--r--dom/base/nsGlobalWindow.h14
-rw-r--r--dom/base/nsPluginArray.cpp18
-rw-r--r--dom/base/test/test_viewport_scroll.html4
-rw-r--r--dom/browser-element/mochitest/browserElement_ScrollEvent.js4
-rw-r--r--dom/interfaces/security/nsIContentSecurityPolicy.idl5
-rw-r--r--dom/ipc/ContentProcess.cpp9
-rw-r--r--dom/locales/en-US/chrome/security/csp.properties4
-rw-r--r--dom/media/DecoderDoctorDiagnostics.cpp13
-rw-r--r--dom/media/MediaManager.cpp36
-rw-r--r--dom/media/eme/MediaKeySystemAccess.cpp14
-rw-r--r--dom/media/fmp4/MP4Decoder.cpp8
-rw-r--r--dom/media/gtest/TestGMPCrossOrigin.cpp5
-rw-r--r--dom/media/platforms/PDMFactory.cpp15
-rw-r--r--dom/media/platforms/wmf/WMF.h18
-rw-r--r--dom/media/platforms/wmf/WMFUtils.cpp16
-rw-r--r--dom/media/webrtc/MediaEngineWebRTC.cpp4
-rw-r--r--dom/plugins/base/nsPluginHost.cpp68
-rw-r--r--dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest1
-rw-r--r--dom/plugins/test/unit/xpcshell.ini2
-rw-r--r--dom/security/nsCSPContext.cpp25
-rw-r--r--dom/security/nsCSPParser.cpp2
-rw-r--r--dom/security/nsCSPUtils.cpp38
-rw-r--r--dom/security/nsCSPUtils.h6
-rw-r--r--dom/security/test/csp/file_ignore_xfo.html10
-rw-r--r--dom/security/test/csp/file_ignore_xfo.html^headers^3
-rw-r--r--dom/security/test/csp/file_image_nonce.html39
-rw-r--r--dom/security/test/csp/file_image_nonce.html^headers^2
-rw-r--r--dom/security/test/csp/file_punycode_host_src.js2
-rw-r--r--dom/security/test/csp/file_punycode_host_src.sjs45
-rw-r--r--dom/security/test/csp/file_ro_ignore_xfo.html10
-rw-r--r--dom/security/test/csp/file_ro_ignore_xfo.html^headers^3
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_navigation.sjs79
-rw-r--r--dom/security/test/csp/file_websocket_explicit.html31
-rw-r--r--dom/security/test/csp/file_websocket_self.html31
-rw-r--r--dom/security/test/csp/file_websocket_self_wsh.py7
-rw-r--r--dom/security/test/csp/mochitest.ini18
-rw-r--r--dom/security/test/csp/test_ignore_xfo.html59
-rw-r--r--dom/security/test/csp/test_image_nonce.html60
-rw-r--r--dom/security/test/csp/test_punycode_host_src.html81
-rw-r--r--dom/security/test/csp/test_upgrade_insecure_navigation.html103
-rw-r--r--dom/security/test/csp/test_websocket_self.html61
-rw-r--r--dom/security/test/gtest/TestCSPParser.cpp2
-rw-r--r--dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html12
-rw-r--r--dom/url/URL.h6
-rw-r--r--dom/url/tests/test_url.html6
-rw-r--r--dom/webidl/URL.webidl5
-rw-r--r--dom/webidl/Window.webidl12
-rw-r--r--dom/webidl/moz.build8
-rwxr-xr-xgfx/angle/Makefile.in4
-rwxr-xr-xgfx/angle/moz.build3
-rwxr-xr-xgfx/angle/src/libANGLE/moz.build8
-rwxr-xr-xgfx/angle/src/libEGL/moz.build8
-rwxr-xr-xgfx/angle/src/libGLESv2/moz.build8
-rw-r--r--gfx/gl/moz.build2
-rw-r--r--gfx/thebes/gfxGDIFont.cpp3
-rw-r--r--gfx/thebes/gfxGDIFontList.cpp32
-rw-r--r--gfx/thebes/gfxGDIFontList.h3
-rwxr-xr-xgfx/thebes/gfxWindowsPlatform.cpp78
-rw-r--r--gfx/thebes/gfxWindowsPlatform.h8
-rw-r--r--hal/windows/WindowsBattery.cpp141
-rw-r--r--image/decoders/icon/win/nsIconChannel.cpp53
-rw-r--r--intl/icu/source/common/umutex.h2
-rw-r--r--ipc/app/plugin-container.exe.manifest1
-rw-r--r--ipc/chromium/src/base/process_util_win.cc37
-rw-r--r--ipc/glue/WindowsMessageLoop.cpp2
-rw-r--r--ipc/mscom/MainThreadRuntime.cpp14
-rw-r--r--js/src/jsutil.cpp2
-rw-r--r--js/src/old-configure.in10
-rw-r--r--js/src/threading/windows/ConditionVariable.cpp329
-rw-r--r--js/src/vm/Stopwatch.h6
-rw-r--r--js/xpconnect/shell/xpcshell.exe.manifest1
-rw-r--r--layout/forms/test/test_bug562447.html6
-rw-r--r--layout/forms/test/test_bug564115.html4
-rw-r--r--layout/style/nsMediaFeatures.cpp2
-rw-r--r--layout/style/nsRuleNode.cpp3
-rw-r--r--layout/style/test/chrome/bug418986-2.js2
-rw-r--r--layout/style/test/test_media_queries.html2
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/util.c15
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/util.h3
-rw-r--r--mfbt/ThreadLocal.h38
-rw-r--r--mfbt/WindowsVersion.h77
-rw-r--r--modules/libpref/init/all.js32
-rw-r--r--mozglue/build/WindowsDllBlocklist.cpp11
-rw-r--r--mozglue/misc/TimeStamp_windows.cpp56
-rw-r--r--netwerk/base/LoadInfo.cpp6
-rw-r--r--netwerk/base/LoadInfo.h1
-rw-r--r--netwerk/base/NetUtil.jsm35
-rw-r--r--netwerk/base/nsICryptoHash.idl3
-rw-r--r--netwerk/base/nsSocketTransport2.cpp16
-rw-r--r--netwerk/base/nsSocketTransportService2.cpp11
-rw-r--r--netwerk/protocol/http/nsHttpChannel.cpp24
-rw-r--r--netwerk/protocol/http/nsHttpHandler.cpp83
-rw-r--r--netwerk/protocol/http/nsHttpHandler.h4
-rw-r--r--netwerk/test/TestUDPSocket.cpp13
-rw-r--r--netwerk/test/mochitests/mochitest.ini1
-rw-r--r--netwerk/test/mochitests/test_1396395.html52
-rw-r--r--netwerk/wifi/moz.build1
-rw-r--r--netwerk/wifi/nsWifiMonitor.h2
-rw-r--r--netwerk/wifi/nsWifiScannerWin.cpp11
-rw-r--r--netwerk/wifi/win_wifiScanner.h13
-rw-r--r--netwerk/wifi/win_xp_wifiScanner.cpp399
-rw-r--r--netwerk/wifi/win_xp_wifiScanner.h25
-rw-r--r--nsprpub/TAG-INFO1
-rwxr-xr-xnsprpub/configure17
-rw-r--r--nsprpub/configure.in11
-rw-r--r--nsprpub/pr/include/md/_pth.h71
-rw-r--r--nsprpub/pr/include/prinet.h2
-rw-r--r--nsprpub/pr/include/prinit.h4
-rw-r--r--nsprpub/pr/include/private/primpl.h22
-rw-r--r--nsprpub/pr/src/Makefile.in4
-rw-r--r--nsprpub/pr/src/io/prio.c71
-rw-r--r--nsprpub/pr/src/io/prsocket.c68
-rw-r--r--nsprpub/pr/src/md/unix/unix.c17
-rw-r--r--nsprpub/pr/src/md/unix/uxproces.c4
-rw-r--r--nsprpub/pr/src/md/windows/ntthread.c23
-rw-r--r--nsprpub/pr/src/md/windows/w95sock.c5
-rw-r--r--nsprpub/pr/src/md/windows/w95thred.c51
-rw-r--r--nsprpub/pr/src/md/windows/win32_errors.c8
-rw-r--r--nsprpub/pr/src/misc/pratom.c6
-rw-r--r--nsprpub/pr/src/pthreads/ptio.c17
-rw-r--r--nsprpub/pr/src/pthreads/ptsynch.c14
-rw-r--r--nsprpub/pr/src/pthreads/ptthread.c158
-rw-r--r--nsprpub/pr/src/threads/prrwlock.c2
-rw-r--r--nsprpub/pr/tests/Makefile.in3
-rw-r--r--nsprpub/pr/tests/attach.c12
-rw-r--r--nsprpub/pr/tests/dceemu.c9
-rw-r--r--nsprpub/pr/tests/foreign.c8
-rw-r--r--nsprpub/pr/tests/forktest.c47
-rw-r--r--nsprpub/pr/tests/provider.c12
-rw-r--r--nsprpub/pr/tests/socket.c6
-rw-r--r--nsprpub/pr/tests/testfile.c6
-rw-r--r--nsprpub/pr/tests/thrpool_client.c2
-rw-r--r--nsprpub/pr/tests/thrpool_server.c2
-rw-r--r--nsprpub/pr/tests/vercheck.c7
-rw-r--r--old-configure.in179
-rw-r--r--python/mozbuild/mozbuild/mach_commands.py18
-rw-r--r--python/mozbuild/mozbuild/milestone.py12
-rw-r--r--security/nss/.taskcluster.yml2
-rw-r--r--security/nss/TAG-INFO1
-rw-r--r--security/nss/automation/abi-check/expected-report-libnspr4.so.txt8
-rw-r--r--security/nss/automation/abi-check/expected-report-libssl3.so.txt3
-rw-r--r--security/nss/automation/abi-check/previous-nss-release2
-rwxr-xr-xsecurity/nss/automation/buildbot-slave/build.sh37
-rw-r--r--security/nss/automation/clang-format/run_clang_format.sh21
-rw-r--r--security/nss/automation/clang-format/setup.sh4
-rw-r--r--security/nss/automation/release/nspr-version.txt2
-rw-r--r--security/nss/automation/taskcluster/docker-clang-3.9/setup.sh4
-rw-r--r--security/nss/automation/taskcluster/docker-decision/Dockerfile3
-rw-r--r--security/nss/automation/taskcluster/docker-decision/bin/checkout.sh5
-rw-r--r--security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile30
-rw-r--r--security/nss/automation/taskcluster/docker-gcc-4.4/bin/checkout.sh20
-rw-r--r--security/nss/automation/taskcluster/docker-gcc-4.4/setup.sh30
-rw-r--r--security/nss/automation/taskcluster/docker-hacl/Dockerfile30
-rw-r--r--security/nss/automation/taskcluster/docker-hacl/bin/checkout.sh20
-rw-r--r--security/nss/automation/taskcluster/docker-hacl/license.txt15
-rw-r--r--security/nss/automation/taskcluster/docker-hacl/setup-user.sh26
-rw-r--r--security/nss/automation/taskcluster/docker-hacl/setup.sh30
-rw-r--r--security/nss/automation/taskcluster/docker/setup.sh4
-rw-r--r--security/nss/automation/taskcluster/graph/src/context_hash.js16
-rw-r--r--security/nss/automation/taskcluster/graph/src/extend.js232
-rw-r--r--security/nss/automation/taskcluster/graph/src/image_builder.js11
-rw-r--r--security/nss/automation/taskcluster/graph/src/try_syntax.js9
-rw-r--r--security/nss/automation/taskcluster/image_builder/Dockerfile23
-rw-r--r--security/nss/automation/taskcluster/image_builder/VERSION1
-rw-r--r--security/nss/automation/taskcluster/image_builder/bin/checkout.sh15
-rwxr-xr-xsecurity/nss/automation/taskcluster/scripts/build_gyp.sh9
-rw-r--r--security/nss/automation/taskcluster/scripts/build_image.sh24
-rwxr-xr-xsecurity/nss/automation/taskcluster/scripts/gen_certs.sh9
-rw-r--r--security/nss/automation/taskcluster/scripts/run_hacl.sh40
-rw-r--r--security/nss/automation/taskcluster/scripts/split.sh6
-rw-r--r--security/nss/automation/taskcluster/windows/releng.manifest8
-rw-r--r--security/nss/automation/taskcluster/windows/setup.sh6
-rw-r--r--security/nss/automation/taskcluster/windows/setup32.sh6
-rw-r--r--security/nss/automation/taskcluster/windows/setup64.sh6
-rwxr-xr-xsecurity/nss/build.sh11
-rw-r--r--security/nss/cmd/bltest/blapitest.c72
-rw-r--r--security/nss/cmd/certcgi/certcgi.c3
-rw-r--r--security/nss/cmd/certutil/certutil.c334
-rw-r--r--security/nss/cmd/certutil/keystuff.c13
-rw-r--r--security/nss/cmd/crlutil/crlgen.c3
-rw-r--r--security/nss/cmd/fipstest/fipstest.c9
-rw-r--r--security/nss/cmd/fipstest/runtest.sh3
-rw-r--r--security/nss/cmd/lib/secutil.c127
-rw-r--r--security/nss/cmd/libpkix/pkix/util/test_list2.c6
-rw-r--r--security/nss/cmd/listsuites/listsuites.c48
-rw-r--r--security/nss/cmd/manifest.mn1
-rw-r--r--security/nss/cmd/modutil/error.h6
-rw-r--r--security/nss/cmd/modutil/install-ds.c3
-rw-r--r--security/nss/cmd/modutil/modutil.c2
-rw-r--r--security/nss/cmd/modutil/modutil.h1
-rw-r--r--security/nss/cmd/modutil/pk11.c35
-rw-r--r--security/nss/cmd/multinit/multinit.c3
-rw-r--r--security/nss/cmd/pk11mode/pk11mode.c58
-rw-r--r--security/nss/cmd/pk12util/pk12util.c107
-rw-r--r--security/nss/cmd/pp/pp.c4
-rw-r--r--security/nss/cmd/rsaperf/rsaperf.c3
-rw-r--r--security/nss/cmd/rsapoptst/rsapoptst.c185
-rw-r--r--security/nss/cmd/rsapoptst/rsapoptst.gyp25
-rw-r--r--security/nss/cmd/selfserv/selfserv.c26
-rw-r--r--security/nss/cmd/signtool/javascript.c3
-rw-r--r--security/nss/cmd/signtool/signtool.c4
-rw-r--r--security/nss/cmd/smimetools/cmsutil.c5
-rw-r--r--security/nss/cmd/ssltap/ssltap.c12
-rw-r--r--security/nss/cmd/strsclnt/strsclnt.c6
-rw-r--r--security/nss/cmd/tstclnt/tstclnt.c301
-rw-r--r--security/nss/coreconf/config.gypi11
-rw-r--r--security/nss/coreconf/config.mk11
-rw-r--r--security/nss/coreconf/coreconf.dep1
-rw-r--r--security/nss/coreconf/werror.py2
-rw-r--r--security/nss/cpputil/.clang-format4
-rw-r--r--security/nss/cpputil/Makefile49
-rw-r--r--security/nss/cpputil/README11
-rw-r--r--security/nss/cpputil/config.mk15
-rw-r--r--security/nss/cpputil/cpputil.gyp29
-rw-r--r--security/nss/cpputil/cpputil.h12
-rw-r--r--security/nss/cpputil/databuffer.cc127
-rw-r--r--security/nss/cpputil/databuffer.h110
-rw-r--r--security/nss/cpputil/dummy_io.cc225
-rw-r--r--security/nss/cpputil/dummy_io.h62
-rw-r--r--security/nss/cpputil/dummy_io_fwd.cc162
-rw-r--r--security/nss/cpputil/manifest.mn24
-rw-r--r--security/nss/cpputil/scoped_ptrs.h74
-rw-r--r--security/nss/cpputil/scoped_ptrs_util.h39
-rw-r--r--security/nss/cpputil/tls_parser.cc73
-rw-r--r--security/nss/cpputil/tls_parser.h145
-rw-r--r--security/nss/doc/certutil.xml10
-rw-r--r--security/nss/doc/html/certutil.html43
-rw-r--r--security/nss/doc/html/pk12util.html12
-rw-r--r--security/nss/doc/nroff/certutil.137
-rw-r--r--security/nss/doc/nroff/pk12util.1279
-rw-r--r--security/nss/doc/pk12util.xml74
-rw-r--r--security/nss/fuzz/config/clone_libfuzzer.sh2
-rw-r--r--security/nss/fuzz/config/git-copy.sh27
-rw-r--r--security/nss/fuzz/mpi_expmod_target.cc9
-rw-r--r--security/nss/fuzz/mpi_helper.cc6
-rw-r--r--security/nss/fuzz/mpi_helper.h1
-rw-r--r--security/nss/fuzz/tls_mutators.cc31
-rw-r--r--security/nss/fuzz/tls_socket.h1
-rw-r--r--security/nss/gtests/certdb_gtest/alg1485_unittest.cc20
-rw-r--r--security/nss/gtests/common/util.h2
-rw-r--r--security/nss/gtests/cryptohi_gtest/Makefile43
-rw-r--r--security/nss/gtests/cryptohi_gtest/cryptohi_gtest.gyp29
-rw-r--r--security/nss/gtests/cryptohi_gtest/cryptohi_unittest.cc373
-rw-r--r--security/nss/gtests/cryptohi_gtest/manifest.mn22
-rw-r--r--security/nss/gtests/freebl_gtest/blake2b_unittest.cc277
-rw-r--r--security/nss/gtests/freebl_gtest/freebl_gtest.gyp73
-rw-r--r--security/nss/gtests/freebl_gtest/kat/blake2b_kat.h4646
-rw-r--r--security/nss/gtests/freebl_gtest/rsa_unittest.cc61
-rw-r--r--security/nss/gtests/manifest.mn6
-rw-r--r--security/nss/gtests/nss_bogo_shim/Makefile6
-rw-r--r--security/nss/gtests/nss_bogo_shim/config.json15
-rw-r--r--security/nss/gtests/pk11_gtest/manifest.mn9
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc126
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc210
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_gtest.gyp2
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc157
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_signature_test.h140
-rw-r--r--security/nss/gtests/softoken_gtest/Makefile45
-rw-r--r--security/nss/gtests/softoken_gtest/manifest.mn25
-rw-r--r--security/nss/gtests/softoken_gtest/softoken_gtest.cc360
-rw-r--r--security/nss/gtests/softoken_gtest/softoken_gtest.gyp51
-rw-r--r--security/nss/gtests/ssl_gtest/Makefile4
-rw-r--r--security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc108
-rw-r--r--security/nss/gtests/ssl_gtest/libssl_internals.c139
-rw-r--r--security/nss/gtests/ssl_gtest/libssl_internals.h20
-rw-r--r--security/nss/gtests/ssl_gtest/manifest.mn7
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc271
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc17
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc32
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc25
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc53
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc503
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc24
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc74
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc742
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc22
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc1
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc80
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc10
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc63
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_gtest.cc2
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_gtest.gyp7
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc678
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc118
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc178
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc251
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc20
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_record_unittest.cc73
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc212
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc274
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc35
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc4
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc337
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc11
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_version_unittest.cc117
-rw-r--r--security/nss/gtests/ssl_gtest/test_io.cc9
-rw-r--r--security/nss/gtests/ssl_gtest/test_io.h20
-rw-r--r--security/nss/gtests/ssl_gtest/tls_agent.cc205
-rw-r--r--security/nss/gtests/ssl_gtest/tls_agent.h19
-rw-r--r--security/nss/gtests/ssl_gtest/tls_connect.cc190
-rw-r--r--security/nss/gtests/ssl_gtest/tls_connect.h31
-rw-r--r--security/nss/gtests/ssl_gtest/tls_filter.cc321
-rw-r--r--security/nss/gtests/ssl_gtest/tls_filter.h192
-rw-r--r--security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc14
-rw-r--r--security/nss/gtests/ssl_gtest/tls_protect.cc6
-rw-r--r--security/nss/gtests/ssl_gtest/tls_protect.h9
-rw-r--r--security/nss/gtests/util_gtest/manifest.mn2
-rw-r--r--security/nss/gtests/util_gtest/util_aligned_malloc_unittest.cc82
-rw-r--r--security/nss/gtests/util_gtest/util_gtest.gyp2
-rw-r--r--security/nss/gtests/util_gtest/util_memcmpzero_unittest.cc45
-rw-r--r--security/nss/help.txt48
-rw-r--r--security/nss/lib/certdb/alg1485.c22
-rw-r--r--security/nss/lib/certdb/cert.h8
-rw-r--r--security/nss/lib/certdb/certdb.c1
-rw-r--r--security/nss/lib/certdb/crl.c3
-rw-r--r--security/nss/lib/certdb/stanpcertdb.c56
-rw-r--r--security/nss/lib/ckfw/builtins/certdata.txt6981
-rw-r--r--security/nss/lib/ckfw/builtins/nssckbi.h4
-rw-r--r--security/nss/lib/ckfw/capi/cfind.c3
-rw-r--r--security/nss/lib/cryptohi/cryptohi.h61
-rw-r--r--security/nss/lib/cryptohi/keyi.h3
-rw-r--r--security/nss/lib/cryptohi/seckey.c122
-rw-r--r--security/nss/lib/cryptohi/secsign.c378
-rw-r--r--security/nss/lib/cryptohi/secvfy.c149
-rw-r--r--security/nss/lib/dbm/src/h_page.c15
-rw-r--r--security/nss/lib/dbm/src/hash.c3
-rw-r--r--security/nss/lib/dev/devutil.c8
-rw-r--r--security/nss/lib/freebl/Makefile29
-rw-r--r--security/nss/lib/freebl/aes-x86.c157
-rw-r--r--security/nss/lib/freebl/blake2b.c430
-rw-r--r--security/nss/lib/freebl/blake2b.h23
-rw-r--r--security/nss/lib/freebl/blapi.h78
-rw-r--r--security/nss/lib/freebl/blapii.h2
-rw-r--r--security/nss/lib/freebl/blapit.h32
-rw-r--r--security/nss/lib/freebl/chacha20.c104
-rw-r--r--security/nss/lib/freebl/chacha20poly1305.c51
-rw-r--r--security/nss/lib/freebl/config.mk5
-rw-r--r--security/nss/lib/freebl/crypto_primitives.c36
-rw-r--r--security/nss/lib/freebl/crypto_primitives.h51
-rw-r--r--security/nss/lib/freebl/det_rng.c27
-rw-r--r--security/nss/lib/freebl/ec.c44
-rw-r--r--security/nss/lib/freebl/ecdecode.c4
-rw-r--r--security/nss/lib/freebl/ecl/curve25519_64.c508
-rw-r--r--security/nss/lib/freebl/ecl/ecp_25519.c6
-rw-r--r--security/nss/lib/freebl/exports.gyp1
-rw-r--r--security/nss/lib/freebl/fipsfreebl.c14
-rw-r--r--security/nss/lib/freebl/freebl.gyp72
-rw-r--r--security/nss/lib/freebl/freebl_base.gypi48
-rw-r--r--security/nss/lib/freebl/gcm-x86.c127
-rw-r--r--security/nss/lib/freebl/gcm.c171
-rw-r--r--security/nss/lib/freebl/gcm.h14
-rw-r--r--security/nss/lib/freebl/ldvector.c24
-rw-r--r--security/nss/lib/freebl/loader.c111
-rw-r--r--security/nss/lib/freebl/loader.h25
-rw-r--r--security/nss/lib/freebl/manifest.mn24
-rw-r--r--security/nss/lib/freebl/mpi/README41
-rw-r--r--security/nss/lib/freebl/mpi/mpi-config.h8
-rw-r--r--security/nss/lib/freebl/mpi/mpi.c15
-rw-r--r--security/nss/lib/freebl/nsslowhash.c6
-rw-r--r--security/nss/lib/freebl/poly1305.h2
-rw-r--r--security/nss/lib/freebl/rijndael.c294
-rw-r--r--security/nss/lib/freebl/rijndael.h18
-rw-r--r--security/nss/lib/freebl/rsa.c35
-rw-r--r--security/nss/lib/freebl/sha512.c45
-rw-r--r--security/nss/lib/freebl/shvfy.c22
-rw-r--r--security/nss/lib/freebl/stubs.c67
-rw-r--r--security/nss/lib/freebl/stubs.h3
-rw-r--r--security/nss/lib/freebl/verified/FStar.c255
-rw-r--r--security/nss/lib/freebl/verified/FStar.h69
-rw-r--r--security/nss/lib/freebl/verified/Hacl_Chacha20.c270
-rw-r--r--security/nss/lib/freebl/verified/Hacl_Chacha20.h81
-rw-r--r--security/nss/lib/freebl/verified/Hacl_Curve25519.c845
-rw-r--r--security/nss/lib/freebl/verified/Hacl_Curve25519.h57
-rw-r--r--security/nss/lib/freebl/verified/Hacl_Poly1305_64.c485
-rw-r--r--security/nss/lib/freebl/verified/Hacl_Poly1305_64.h99
-rw-r--r--security/nss/lib/freebl/verified/kremlib.h672
-rw-r--r--security/nss/lib/freebl/verified/kremlib_base.h191
-rw-r--r--security/nss/lib/freebl/verified/specs/Spec.CTR.fst98
-rw-r--r--security/nss/lib/freebl/verified/specs/Spec.Chacha20.fst169
-rw-r--r--security/nss/lib/freebl/verified/specs/Spec.Curve25519.fst168
-rw-r--r--security/nss/lib/freebl/verified/specs/Spec.Poly1305.fst107
-rw-r--r--security/nss/lib/nss/nss.def18
-rw-r--r--security/nss/lib/nss/nss.h15
-rw-r--r--security/nss/lib/nss/nssoptions.c8
-rw-r--r--security/nss/lib/nss/utilwrap.c14
-rw-r--r--security/nss/lib/pk11wrap/pk11load.c3
-rw-r--r--security/nss/lib/pk11wrap/pk11merge.c6
-rw-r--r--security/nss/lib/pk11wrap/pk11obj.c39
-rw-r--r--security/nss/lib/pk11wrap/pk11pars.c3
-rw-r--r--security/nss/lib/pk11wrap/pk11pbe.c19
-rw-r--r--security/nss/lib/pk11wrap/pk11pk12.c23
-rw-r--r--security/nss/lib/pk11wrap/pk11pub.h4
-rw-r--r--security/nss/lib/pk11wrap/pk11skey.c4
-rw-r--r--security/nss/lib/pk11wrap/pk11slot.c5
-rw-r--r--security/nss/lib/pk11wrap/pk11util.c9
-rw-r--r--security/nss/lib/pk11wrap/secmodti.h1
-rw-r--r--security/nss/lib/pkcs12/p12d.c63
-rw-r--r--security/nss/lib/pkcs12/p12local.c3
-rw-r--r--security/nss/lib/pkcs7/p7create.c8
-rw-r--r--security/nss/lib/pki/pki3hack.c4
-rw-r--r--security/nss/lib/smime/cmsdecode.c3
-rw-r--r--security/nss/lib/smime/cmsencode.c3
-rw-r--r--security/nss/lib/softoken/fipstest.c9
-rw-r--r--security/nss/lib/softoken/fipstokn.c33
-rw-r--r--security/nss/lib/softoken/legacydb/keydb.c8
-rw-r--r--security/nss/lib/softoken/legacydb/lgattr.c12
-rw-r--r--security/nss/lib/softoken/legacydb/lgcreate.c15
-rw-r--r--security/nss/lib/softoken/legacydb/lgfips.c4
-rw-r--r--security/nss/lib/softoken/legacydb/lginit.c4
-rw-r--r--security/nss/lib/softoken/legacydb/lowcert.c2
-rw-r--r--security/nss/lib/softoken/legacydb/lowkey.c7
-rw-r--r--security/nss/lib/softoken/legacydb/lowkeyi.h5
-rw-r--r--security/nss/lib/softoken/legacydb/lowkeyti.h2
-rw-r--r--security/nss/lib/softoken/legacydb/pcertdb.c10
-rw-r--r--security/nss/lib/softoken/lowkey.c12
-rw-r--r--security/nss/lib/softoken/lowkeyi.h2
-rw-r--r--security/nss/lib/softoken/lowkeyti.h2
-rw-r--r--security/nss/lib/softoken/pkcs11.c66
-rw-r--r--security/nss/lib/softoken/pkcs11c.c239
-rw-r--r--security/nss/lib/softoken/pkcs11i.h1
-rw-r--r--security/nss/lib/softoken/pkcs11u.c8
-rw-r--r--security/nss/lib/softoken/sdb.c105
-rw-r--r--security/nss/lib/softoken/sdb.h4
-rw-r--r--security/nss/lib/softoken/sftkdb.c84
-rw-r--r--security/nss/lib/softoken/sftkdbti.h1
-rw-r--r--security/nss/lib/softoken/sftkpwd.c7
-rw-r--r--security/nss/lib/softoken/softkver.h10
-rw-r--r--security/nss/lib/softoken/softoknt.h3
-rw-r--r--security/nss/lib/ssl/SSLerrs.h33
-rw-r--r--security/nss/lib/ssl/authcert.c3
-rw-r--r--security/nss/lib/ssl/config.mk5
-rw-r--r--security/nss/lib/ssl/dtls13con.c457
-rw-r--r--security/nss/lib/ssl/dtls13con.h29
-rw-r--r--security/nss/lib/ssl/dtlscon.c720
-rw-r--r--security/nss/lib/ssl/dtlscon.h48
-rw-r--r--security/nss/lib/ssl/exports.gyp1
-rw-r--r--security/nss/lib/ssl/manifest.mn8
-rw-r--r--security/nss/lib/ssl/selfencrypt.c57
-rw-r--r--security/nss/lib/ssl/selfencrypt.h1
-rw-r--r--security/nss/lib/ssl/ssl.def6
-rw-r--r--security/nss/lib/ssl/ssl.gyp15
-rw-r--r--security/nss/lib/ssl/ssl.h49
-rw-r--r--security/nss/lib/ssl/ssl3con.c4622
-rw-r--r--security/nss/lib/ssl/ssl3ecc.c160
-rw-r--r--security/nss/lib/ssl/ssl3ext.c709
-rw-r--r--security/nss/lib/ssl/ssl3ext.h114
-rw-r--r--security/nss/lib/ssl/ssl3exthandle.c1311
-rw-r--r--security/nss/lib/ssl/ssl3exthandle.h174
-rw-r--r--security/nss/lib/ssl/ssl3gthr.c50
-rw-r--r--security/nss/lib/ssl/ssl3prot.h126
-rw-r--r--security/nss/lib/ssl/sslbloom.c94
-rw-r--r--security/nss/lib/ssl/sslbloom.h32
-rw-r--r--security/nss/lib/ssl/sslcert.c10
-rw-r--r--security/nss/lib/ssl/sslencode.c296
-rw-r--r--security/nss/lib/ssl/sslencode.h69
-rw-r--r--security/nss/lib/ssl/sslerr.h14
-rw-r--r--security/nss/lib/ssl/sslexp.h358
-rw-r--r--security/nss/lib/ssl/sslimpl.h483
-rw-r--r--security/nss/lib/ssl/sslinfo.c160
-rw-r--r--security/nss/lib/ssl/sslnonce.c13
-rw-r--r--security/nss/lib/ssl/sslreveal.c22
-rw-r--r--security/nss/lib/ssl/sslsecur.c141
-rw-r--r--security/nss/lib/ssl/sslsnce.c59
-rw-r--r--security/nss/lib/ssl/sslsock.c475
-rw-r--r--security/nss/lib/ssl/sslspec.c273
-rw-r--r--security/nss/lib/ssl/sslspec.h194
-rw-r--r--security/nss/lib/ssl/sslt.h41
-rw-r--r--security/nss/lib/ssl/tls13con.c2419
-rw-r--r--security/nss/lib/ssl/tls13con.h71
-rw-r--r--security/nss/lib/ssl/tls13err.h28
-rw-r--r--security/nss/lib/ssl/tls13exthandle.c813
-rw-r--r--security/nss/lib/ssl/tls13exthandle.h118
-rw-r--r--security/nss/lib/ssl/tls13hashstate.c181
-rw-r--r--security/nss/lib/ssl/tls13hashstate.h25
-rw-r--r--security/nss/lib/ssl/tls13hkdf.c53
-rw-r--r--security/nss/lib/ssl/tls13replay.c276
-rw-r--r--security/nss/lib/util/nssb64d.c2
-rw-r--r--security/nss/lib/util/nssrwlk.c2
-rw-r--r--security/nss/lib/util/nssutil.def16
-rw-r--r--security/nss/lib/util/nssutil.h6
-rw-r--r--security/nss/lib/util/pkcs11uri.c2
-rw-r--r--security/nss/lib/util/quickder.c3
-rw-r--r--security/nss/lib/util/secasn1d.c4
-rw-r--r--security/nss/lib/util/secoid.c20
-rw-r--r--security/nss/lib/util/secport.c62
-rw-r--r--security/nss/lib/util/secport.h7
-rw-r--r--security/nss/lib/util/utilmod.c190
-rw-r--r--security/nss/lib/util/utilpars.c7
-rw-r--r--security/nss/lib/util/utilpars.h6
-rw-r--r--security/nss/lib/util/utilparst.h2
-rw-r--r--security/nss/lib/util/utilrename.h2
-rw-r--r--security/nss/mach219
-rw-r--r--security/nss/nss-tool/.clang-format4
-rw-r--r--security/nss/nss-tool/common/argparse.cc23
-rw-r--r--security/nss/nss-tool/common/argparse.h30
-rw-r--r--security/nss/nss-tool/common/tool.h20
-rw-r--r--security/nss/nss-tool/common/util.cc216
-rw-r--r--security/nss/nss-tool/common/util.h32
-rw-r--r--security/nss/nss-tool/db/dbtool.cc497
-rw-r--r--security/nss/nss-tool/db/dbtool.h28
-rw-r--r--security/nss/nss-tool/digest/digesttool.cc161
-rw-r--r--security/nss/nss-tool/digest/digesttool.h20
-rw-r--r--security/nss/nss-tool/enc/enctool.cc464
-rw-r--r--security/nss/nss-tool/enc/enctool.h62
-rw-r--r--security/nss/nss-tool/nss_tool.cc70
-rw-r--r--security/nss/nss-tool/nss_tool.gyp31
-rw-r--r--security/nss/nss.gyp10
-rw-r--r--security/nss/readme.md96
-rwxr-xr-xsecurity/nss/tests/all.sh64
-rw-r--r--security/nss/tests/cert/TestCA-bogus-rsa-pss1.crt26
-rw-r--r--security/nss/tests/cert/TestCA-bogus-rsa-pss2.crt24
-rwxr-xr-xsecurity/nss/tests/cert/cert.sh560
-rwxr-xr-xsecurity/nss/tests/cipher/cipher.sh16
-rw-r--r--security/nss/tests/common/init.sh14
-rwxr-xr-xsecurity/nss/tests/gtests/gtests.sh2
-rwxr-xr-xsecurity/nss/tests/merge/merge.sh2
-rwxr-xr-xsecurity/nss/tests/pkits/pkits.sh2
-rw-r--r--security/nss/tests/remote/Makefile1
-rwxr-xr-xsecurity/nss/tests/smime/smime.sh50
-rwxr-xr-xsecurity/nss/tests/ssl/ssl.sh344
-rw-r--r--security/nss/tests/ssl/sslstress.txt1
-rwxr-xr-xsecurity/nss/tests/ssl_gtests/ssl_gtests.sh14
-rw-r--r--security/nss/tests/tools/TestOldAES128CA.p12bin0 -> 2628 bytes
-rw-r--r--security/nss/tests/tools/TestOldCA.p12bin0 -> 2588 bytes
-rw-r--r--security/nss/tests/tools/tools.sh88
-rw-r--r--services/crypto/modules/utils.js82
-rw-r--r--toolkit/components/alerts/nsAlertsService.cpp23
-rw-r--r--toolkit/components/alerts/nsAlertsService.h17
-rw-r--r--toolkit/components/alerts/resources/content/alert.js28
-rw-r--r--toolkit/components/alerts/resources/content/alert.xul1
-rw-r--r--toolkit/components/build/nsToolkitCompsModule.cpp12
-rw-r--r--toolkit/components/maintenanceservice/bootstrapinstaller/maintenanceservice_installer.nsi5
-rw-r--r--toolkit/components/maintenanceservice/maintenanceservice.exe.manifest1
-rw-r--r--toolkit/components/places/BookmarkHTMLUtils.jsm1
-rw-r--r--toolkit/content/aboutSupport.js19
-rw-r--r--toolkit/content/aboutSupport.xhtml2
-rw-r--r--toolkit/content/mozilla.xhtml6
-rw-r--r--toolkit/crashreporter/client/crashreporter.exe.manifest1
-rw-r--r--toolkit/library/libxul.mk10
-rw-r--r--toolkit/locales/en-US/chrome/global/config.dtd8
-rw-r--r--toolkit/locales/en-US/chrome/global/mozilla.dtd16
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/about.dtd9
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/blocklist.dtd17
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd236
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties177
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd15
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties10
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.dtd49
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.properties21
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/update.dtd65
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/update.properties21
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.dtd13
-rw-r--r--toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.properties16
-rw-r--r--toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd11
-rw-r--r--toolkit/locales/jar.mn14
-rw-r--r--toolkit/locales/l10n.mk4
-rw-r--r--toolkit/modules/UpdateChannel.jsm47
-rw-r--r--toolkit/modules/moz.build3
-rw-r--r--toolkit/moz.build6
-rw-r--r--toolkit/mozapps/extensions/AddonManager.jsm3026
-rw-r--r--toolkit/mozapps/extensions/AddonPathService.cpp233
-rw-r--r--toolkit/mozapps/extensions/AddonPathService.h55
-rw-r--r--toolkit/mozapps/extensions/ChromeManifestParser.jsm159
-rw-r--r--toolkit/mozapps/extensions/DeferredSave.jsm274
-rw-r--r--toolkit/mozapps/extensions/LightweightThemeManager.jsm810
-rw-r--r--toolkit/mozapps/extensions/addonManager.js204
-rw-r--r--toolkit/mozapps/extensions/amContentHandler.js100
-rw-r--r--toolkit/mozapps/extensions/amIAddonManager.idl29
-rw-r--r--toolkit/mozapps/extensions/amIAddonPathService.idl29
-rw-r--r--toolkit/mozapps/extensions/amIWebInstallListener.idl134
-rw-r--r--toolkit/mozapps/extensions/amIWebInstaller.idl82
-rw-r--r--toolkit/mozapps/extensions/amInstallTrigger.js230
-rw-r--r--toolkit/mozapps/extensions/amWebInstallListener.js342
-rw-r--r--toolkit/mozapps/extensions/content/OpenH264-license.txt59
-rw-r--r--toolkit/mozapps/extensions/content/about.js97
-rw-r--r--toolkit/mozapps/extensions/content/about.xul57
-rw-r--r--toolkit/mozapps/extensions/content/blocklist.css11
-rw-r--r--toolkit/mozapps/extensions/content/blocklist.js72
-rw-r--r--toolkit/mozapps/extensions/content/blocklist.xml58
-rw-r--r--toolkit/mozapps/extensions/content/blocklist.xul46
-rw-r--r--toolkit/mozapps/extensions/content/eula.js21
-rw-r--r--toolkit/mozapps/extensions/content/eula.xul35
-rw-r--r--toolkit/mozapps/extensions/content/extensions.css288
-rw-r--r--toolkit/mozapps/extensions/content/extensions.js3659
-rw-r--r--toolkit/mozapps/extensions/content/extensions.xml2108
-rw-r--r--toolkit/mozapps/extensions/content/extensions.xul689
-rw-r--r--toolkit/mozapps/extensions/content/gmpPrefs.xul8
-rw-r--r--toolkit/mozapps/extensions/content/list.js165
-rw-r--r--toolkit/mozapps/extensions/content/list.xul44
-rw-r--r--toolkit/mozapps/extensions/content/newaddon.js129
-rw-r--r--toolkit/mozapps/extensions/content/newaddon.xul66
-rw-r--r--toolkit/mozapps/extensions/content/pluginPrefs.xul20
-rw-r--r--toolkit/mozapps/extensions/content/selectAddons.css22
-rw-r--r--toolkit/mozapps/extensions/content/selectAddons.js343
-rw-r--r--toolkit/mozapps/extensions/content/selectAddons.xml235
-rw-r--r--toolkit/mozapps/extensions/content/selectAddons.xul124
-rw-r--r--toolkit/mozapps/extensions/content/setting.xml508
-rw-r--r--toolkit/mozapps/extensions/content/update.js663
-rw-r--r--toolkit/mozapps/extensions/content/update.xul196
-rw-r--r--toolkit/mozapps/extensions/content/updateinfo.xsl41
-rw-r--r--toolkit/mozapps/extensions/content/xpinstallConfirm.css8
-rw-r--r--toolkit/mozapps/extensions/content/xpinstallConfirm.js196
-rw-r--r--toolkit/mozapps/extensions/content/xpinstallConfirm.xul37
-rw-r--r--toolkit/mozapps/extensions/content/xpinstallItem.xml51
-rw-r--r--toolkit/mozapps/extensions/extensions.manifest20
-rw-r--r--toolkit/mozapps/extensions/internal/AddonLogging.jsm180
-rw-r--r--toolkit/mozapps/extensions/internal/AddonRepository.jsm2031
-rw-r--r--toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm518
-rw-r--r--toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm772
-rw-r--r--toolkit/mozapps/extensions/internal/Content.js31
-rw-r--r--toolkit/mozapps/extensions/internal/GMPProvider.jsm614
-rw-r--r--toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm198
-rw-r--r--toolkit/mozapps/extensions/internal/PluginProvider.jsm595
-rw-r--r--toolkit/mozapps/extensions/internal/SpellCheckDictionaryBootstrap.js17
-rw-r--r--toolkit/mozapps/extensions/internal/XPIProvider.jsm7875
-rw-r--r--toolkit/mozapps/extensions/internal/XPIProviderUtils.js1481
-rw-r--r--toolkit/mozapps/extensions/internal/moz.build40
-rw-r--r--toolkit/mozapps/extensions/jar.mn37
-rw-r--r--toolkit/mozapps/extensions/moz.build55
-rw-r--r--toolkit/mozapps/extensions/nsBlocklistService.js1478
-rw-r--r--toolkit/mozapps/extensions/test/AddonManagerTesting.jsm105
-rw-r--r--toolkit/mozapps/extensions/test/Makefile.in20
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_hard1_1/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_hard1_2/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_hard1_3/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_regexp1_1/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_regexp1_2/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_regexp1_3/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft1_1/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft1_2/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft1_3/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft2_1/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft2_2/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft2_3/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft3_1/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft3_2/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft3_3/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft4_1/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft4_2/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft4_3/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft5_1/install.rdf19
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft5_2/install.rdf19
-rw-r--r--toolkit/mozapps/extensions/test/addons/blocklist_soft5_3/install.rdf19
-rw-r--r--toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js29
-rw-r--r--toolkit/mozapps/extensions/test/addons/bootstrap_globals/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/min1max1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/min1max2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/min1max3/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/min1max3b/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/override1x2-1x3/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_AddonRepository_1/install.rdf33
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_AddonRepository_2/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/icon.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/preview.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js32
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf28
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/version.jsm3
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/bootstrap.js31
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/version.jsm3
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js31
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/version.jsm3
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap1_4/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf28
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_2/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_a_1/install.rdf21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_a_2/install.rdf21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_b_1/install.rdf20
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_b_2/install.rdf20
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_c_1/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_c_2/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_d_1/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_d_2/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_e_1/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_e_2/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_f_1/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_f_2/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_g_1/install.rdf21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug299716_g_2/install.rdf21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_1/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_2/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_3/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_4/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_5/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_6/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_7/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_8/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug324121_9/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug335238_1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug335238_2/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug335238_3/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug335238_4/install.rdf30
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug371495/install.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug394300_1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug394300_2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug397778/install.rdf78
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug425657/install.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug470377_1/install.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug470377_2/install.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug470377_3/install.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug470377_4/install.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug470377_5/install.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug521905/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug567173/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug567184/bootstrap.js7
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug567184/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug587088_1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile10
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug587088_2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile20
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug594058/directory/file10
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug594058/install.rdf21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug595573/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug655254/install.rdf18
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug655254_2/bootstrap.js9
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug655254_2/install.rdf19
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug659772/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug675371/chrome.manifest1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug675371/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug675371/test.js1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug740612_1/bootstrap.js1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug740612_1/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug740612_2/bootstrap.js23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug740612_2/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_bug757663/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_cacheflush1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_cacheflush2/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/chrome.manifest6
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/chrome.manifest7
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/chrome.manifest9
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/inner.jarbin0 -> 180 bytes
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/chrome.manifest6
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/components.manifest2
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/other/something.manifest1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/chrome.manifest7
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/chrome.manifest1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_data_directory/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf58
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf59
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary/dictionaries/ab-CD.dic2
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary_2/dictionaries/ab-CD.dic2
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary_2/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary_3/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary_4/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_dictionary_5/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_distribution1_2/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_experiment1/install.rdf16
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_filepointer/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_getresource/icon.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_getresource/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_getresource/subdir/subfile.txt1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install1/icon.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install1/icon64.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install1/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install2_1/icon.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install2_1/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install2_2/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install3/install.rdf27
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/addon4.xpibin0 -> 509 bytes
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/addon5.jarbin0 -> 512 bytes
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/addon6.xpibin0 -> 512 bytes
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/addon7.jarbin0 -> 512 bytes
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/badaddon.jar1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/badaddon.xpi1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/icon.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install4/install.rdf10
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install5/chrome.manifest1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install5/install.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_install6/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_jetpack/bootstrap.js17
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_jetpack/harness-options.json1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_jetpack/install.rdf28
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_langpack/chrome.manifest1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_langpack/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_locale/install.rdf61
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate4_6/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate4_7/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate6/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate7/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate8/chrome.manifest6
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate8/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_migrate9/install.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_theme/install.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_theme/preview.png1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_undoincompatible/bootstrap.js1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_undoincompatible/install.rdf28
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_undouninstall1/bootstrap.js1
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_undouninstall1/install.rdf28
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_update/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_update12/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_update8/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_updateid2_2/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_updateid2_5/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_updateid3_3/bootstrap.js21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_updateid3_3/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_updateid4_4/bootstrap.js21
-rw-r--r--toolkit/mozapps/extensions/test/addons/test_updateid4_4/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/Makefile.in19
-rw-r--r--toolkit/mozapps/extensions/test/browser/addon_about.xul6
-rw-r--r--toolkit/mozapps/extensions/test/browser/addon_prefs.xul6
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_1/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_10/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_2/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_3/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_4/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_5/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_6/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_7/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_8_1/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_9_1/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_1/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_2/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop1/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_experiment1/install.rdf16
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/bootstrap.js8
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/install.rdf19
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul20
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/binding.xml19
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/bootstrap.js8
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/chrome.manifest2
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/install.rdf19
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/options.xul5
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/string.dtd1
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/bootstrap.js8
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/install.rdf20
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/options.xul19
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_install1_1/install.rdf24
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_install1_2/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_installssl/install.rdf22
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_searching/bootstrap.js9
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_searching/install.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/browser/addons/browser_select_compatoverrides_1/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/browser/blockNoPlugins.xml7
-rw-r--r--toolkit/mozapps/extensions/test/browser/blockPluginHard.xml11
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser-common.ini78
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser-window.ini4
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser.ini53
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js234
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_about.js84
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_addonrepository_performance.js99
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug523784.js120
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug557943.js80
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug557956.js518
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug557956.rdf220
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug557956.xml20
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug557956_8_2.xpibin0 -> 471 bytes
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug557956_9_2.xpibin0 -> 471 bytes
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug562797.js965
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug562854.js129
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug562890.js78
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug562899.js88
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug562992.js70
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug567127.js137
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug567137.js40
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug570760.js44
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug572561.js99
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug573062.js116
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug577990.js132
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug580298.js111
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug581076.js128
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug586574.js286
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug587970.js180
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug590347.js120
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug591465.js512
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug591465.xml35
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug591663.js161
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug593535.js118
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug593535.xml34
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug596336.js180
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug608316.js65
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug610764.js34
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug616841.js21
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug618502.js44
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug679604.js29
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_bug714593.js140
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js462
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_checkAddonCompatibility.js34
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_debug_button.js112
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_details.js764
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_discovery.js637
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_discovery_install.js130
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_dragdrop.js234
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_eula.js85
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_eula.xml35
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_experiments.js645
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_globalinformations.js55
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_globalwarnings.js63
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_gmpProvider.js401
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js677
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_inlinesettings_custom.js92
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_inlinesettings_info.js569
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_install.js312
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_install.rdf27
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_install.rdf^headers^1
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_install.xml34
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_install1_3.xpibin0 -> 463 bytes
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_installssl.js374
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_list.js760
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_manualupdates.js242
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_metadataTimeout.js114
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_newaddon.js186
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_openDialog.js176
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_plugin_enabled_state_locked.js125
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_pluginprefs.js61
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_purchase.js195
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_purchase.xml180
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_recentupdates.js125
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_searching.js695
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_searching.xml277
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_searching_empty.xml3
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js116
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.xml20
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_select_confirm.js181
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_select_selection.js268
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_select_update.js181
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_sorting.js372
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_sorting_plugins.js95
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_tabsettings.js100
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_task_next_test.js17
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_types.js473
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_uninstalling.js1099
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_updateid.js80
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_updatessl.js370
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf^headers^1
-rw-r--r--toolkit/mozapps/extensions/test/browser/cancelCompatCheck.sjs43
-rw-r--r--toolkit/mozapps/extensions/test/browser/discovery.html10
-rw-r--r--toolkit/mozapps/extensions/test/browser/discovery_frame.html6
-rw-r--r--toolkit/mozapps/extensions/test/browser/discovery_install.html18
-rw-r--r--toolkit/mozapps/extensions/test/browser/head.js1393
-rw-r--r--toolkit/mozapps/extensions/test/browser/more_options.xul32
-rw-r--r--toolkit/mozapps/extensions/test/browser/moz.build10
-rw-r--r--toolkit/mozapps/extensions/test/browser/options.xul12
-rw-r--r--toolkit/mozapps/extensions/test/browser/plugin_test.html7
-rw-r--r--toolkit/mozapps/extensions/test/browser/redirect.sjs5
-rw-r--r--toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml15
-rw-r--r--toolkit/mozapps/extensions/test/mochitest/file_bug687194.xpibin0 -> 1602 bytes
-rw-r--r--toolkit/mozapps/extensions/test/mochitest/file_empty.html2
-rw-r--r--toolkit/mozapps/extensions/test/mochitest/mochitest.ini10
-rw-r--r--toolkit/mozapps/extensions/test/mochitest/test_bug609794.html27
-rw-r--r--toolkit/mozapps/extensions/test/mochitest/test_bug687194.html133
-rw-r--r--toolkit/mozapps/extensions/test/mochitest/test_bug887098.html51
-rw-r--r--toolkit/mozapps/extensions/test/moz.build20
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_change.xml31
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update1.rdf144
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update2.rdf144
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update3.rdf144
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/app_update.xml62
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml3
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/manual_update.xml27
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/bug455906_block.xml18
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/bug455906_empty.xml7
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/bug455906_start.xml30
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/bug455906_warn.xml33
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/corrupt.xpi1
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/corruptfile.xpibin0 -> 633 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/empty.xpibin0 -> 197 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/pluginInfoURL_block.xml21
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository.xml820
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_cache.xml182
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml23
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml23
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml23
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_empty.xml3
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_failed.xml21
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml187
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.rdf70
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml21
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_prefs_1.xml28
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_regexp_1.xml20
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716.rdf181
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716_2.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug324121.rdf91
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug393285.xml30
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug394300.rdf159
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug424262.xml185
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_app.xml333
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_toolkit.xml208
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug468528.xml15
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_1.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_2.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_3.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_4.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_5.rdf17
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_1.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_2.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_3.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_4.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_5.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_1.xml17
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_2.xml10
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_empty.xml4
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml13
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml13
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_1.xpibin0 -> 458 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_2.xpibin0 -> 458 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug541420.xpibin0 -> 577 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug542391.rdf25
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug554133.xml292
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug619730.xml7
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_bug655254.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_compatoverrides.xml228
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_corrupt.rdf44
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_dictionary.rdf65
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/bootstrap.js21
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/install.rdf23
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt1
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt1
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist.xml154
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist2.xml31
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml32
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml32
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_install.rdf63
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_install.xml53
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_migrate.rdf125
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_migrate4.rdf46
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/ancient.xml8
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/new.xml8
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/old.xml8
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtp.xml26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml10
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_sourceURI.xml18
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf270
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_update.xml26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf419
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.rdf26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/test_updateid.rdf86
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/data/unsigned.xpibin0 -> 452 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/head_addons.js1759
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/head_unpack.js2
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js625
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js710
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_compatmode.js90
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js108
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js550
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_LightweightThemeManager.js514
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js299
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js66
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_addon_path_service.js38
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js44
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js122
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bad_json.js54
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_badschema.js404
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js159
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js159
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js125
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js1321
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js1434
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_globals.js37
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_resource.js56
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug299716.js209
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug299716_2.js50
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug324121.js178
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js181
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug371495.js35
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug384052.js103
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug393285.js327
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug394300.js56
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug397778.js117
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug406118.js167
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug424262.js62
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug425657.js27
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug430120.js142
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug449027.js448
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js536
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug465190.js39
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug468528.js58
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1_strictcompat.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug470377_2.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3_strictcompat.js94
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug470377_4.js92
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug514327_1.js59
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug514327_2.js42
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js166
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug521905.js59
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug526598.js54
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug541420.js37
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js486
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug554133.js86
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug559800.js71
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug563256.js259
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug564030.js63
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug566626.js112
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug567184.js53
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug569138.js147
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug570173.js80
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js66
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug587088.js174
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug594058.js97
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug595081.js27
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug595573.js40
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug596343.js86
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug596607.js140
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js26
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug619730.js64
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug620837.js145
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug655254.js164
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js340
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug675371.js91
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug740612.js40
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug753900.js86
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js112
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_bug953156.js51
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js124
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js93
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_checkcompatibility.js196
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_childprocess.js21
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js259
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js403
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_corrupt_strictcompat.js402
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_corruptfile.js83
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_dataDirectory.js50
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_default_providers_pref.js13
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js801
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_disable.js194
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_distribution.js262
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_dss.js824
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js185
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_error.js90
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_experiment.js104
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_filepointer.js403
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_fuel.js164
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_general.js58
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_getresource.js94
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Device.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_DriverNew.js94
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js89
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OK.js96
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OS.js96
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js96
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js97
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js97
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Vendor.js95
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_prefs.js132
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js332
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_hasbinarycomponents.js82
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_install.js1761
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_install_icons.js61
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js1654
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_isDebuggable.js36
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_isReady.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_langpack.js339
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_locale.js149
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_locked.js529
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_locked2.js292
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js551
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_manifest.js562
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js326
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_metadata_update.js184
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js250
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js259
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js239
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js307
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js139
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js127
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_migrate_max_version.js103
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js118
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_no_addons.js98
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js66
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_overrideblocklist.js200
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_permissions.js86
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js74
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_pluginBlocklistCtp.js181
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_pluginInfoURL.js80
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_pluginchange.js292
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_plugins.js210
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_pref_properties.js206
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_provider_markSafe.js47
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_provider_shutdown.js97
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_shutdown.js61
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_startup.js53
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_registry.js151
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_safemode.js115
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js65
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_sourceURI.js66
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_startup.js917
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js203
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_syncGUID.js154
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_targetPlatforms.js146
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_theme.js1092
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_types.js65
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_undothemeuninstall.js421
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js792
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_uninstall.js216
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_update.js1310
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js142
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js184
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js98
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js1085
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js312
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_updateid.js422
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js206
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js209
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini281
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini8
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini28
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/authRedirect.sjs21
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser.ini103
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_auth.js43
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_auth2.js46
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_auth3.js54
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_auth4.js53
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_badargs.js37
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_badargs2.js41
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_badhash.js33
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_badhashtype.js33
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_bug540558.js25
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_bug611242.js34
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_bug638292.js63
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_bug645699.js36
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_bug672485.js52
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js62
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_concurrent_installs.js128
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_cookies.js30
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_cookies2.js40
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_cookies3.js44
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_cookies4.js43
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_corrupt.js32
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_datauri.js36
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_empty.js28
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_enabled.js24
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_enabled2.js27
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_enabled3.js48
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_hash.js34
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_hash2.js34
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_httphash.js39
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_httphash2.js39
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_httphash3.js39
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_httphash4.js36
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_httphash5.js40
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js83
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_installchrome.js25
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_localfile.js34
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_localfile2.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_localfile3.js40
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_localfile4.js40
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_multipackage.js53
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway.js36
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway2.js34
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway3.js75
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway4.js44
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_offline.js61
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_relative.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js72
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js67
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_signed_tampered.js33
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_signed_trigger.js41
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_signed_untrusted.js41
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_signed_url.js34
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_softwareupdate.js25
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_switchtab.js49
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_trigger_redirect.js41
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger.js50
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_iframe.js51
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_xorigin.js38
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_url.js35
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist.js46
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist2.js31
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist3.js28
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist4.js30
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist5.js25
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist6.js25
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/browser_whitelist7.js32
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/bug540558.html23
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/bug638292.html17
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/bug645699.html30
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/concurrent_installs.html39
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/cookieRedirect.sjs24
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi1
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/empty.xpibin0 -> 197 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/enabled.html23
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/hashRedirect.sjs15
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/head.js424
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/incompatible.xpibin0 -> 470 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/installchrome.html21
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/installtrigger.html43
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/installtrigger_frame.html29
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/multipackage.xpibin0 -> 2976 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/navigate.html26
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/redirect.sjs45
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/restartless.xpibin0 -> 485 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/signed-no-cn.xpibin0 -> 2241 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/signed-no-o.xpibin0 -> 2247 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/signed-tampered.xpibin0 -> 2260 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/signed-untrusted.xpibin0 -> 2237 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/signed.xpibin0 -> 2250 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/signed2.xpibin0 -> 2938 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs101
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/startsoftwareupdate.html19
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/theme.xpibin0 -> 491 bytes
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/triggerredirect.html35
-rw-r--r--toolkit/mozapps/extensions/test/xpinstall/unsigned.xpibin0 -> 452 bytes
-rw-r--r--toolkit/mozapps/installer/package-name.mk15
-rw-r--r--toolkit/mozapps/installer/packager.mk13
-rw-r--r--toolkit/mozapps/installer/upload-files.mk7
-rwxr-xr-xtoolkit/mozapps/installer/windows/nsis/common.nsh45
-rw-r--r--toolkit/mozapps/installer/windows/nsis/makensis.mk16
-rw-r--r--toolkit/mozapps/update/content/history.js2
-rw-r--r--toolkit/mozapps/update/updater/updater.exe.comctl32.manifest1
-rw-r--r--toolkit/mozapps/update/updater/updater.exe.manifest1
-rw-r--r--toolkit/mozapps/webextensions/content/extensions.js8
-rw-r--r--toolkit/mozapps/webextensions/jar.mn2
-rw-r--r--toolkit/mozapps/webextensions/test/browser/browser-window.ini14
-rw-r--r--toolkit/mozapps/webextensions/test/browser/browser.ini14
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-available.pngbin0 -> 1092 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-dictionaries.pngbin0 -> 1290 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-discover.pngbin0 -> 1482 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-experiments.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-plugins.pngbin0 -> 1172 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-recent.pngbin0 -> 2020 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-search.pngbin0 -> 2600 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/category-service.pngbin0 -> 2063 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/dictionaryGeneric-16.pngbin0 -> 584 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/dictionaryGeneric.pngbin0 -> 1290 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/experimentGeneric.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/extensionGeneric-16.pngbin0 -> 713 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/extensionGeneric.pngbin0 -> 1862 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/extensions.css956
-rw-r--r--toolkit/themes/linux/mozapps/extensions/localeGeneric.pngbin0 -> 1860 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/newaddon.css110
-rw-r--r--toolkit/themes/linux/mozapps/extensions/selectAddons.css162
-rw-r--r--toolkit/themes/linux/mozapps/extensions/themeGeneric-16.pngbin0 -> 638 bytes
-rw-r--r--toolkit/themes/linux/mozapps/extensions/themeGeneric.pngbin0 -> 1734 bytes
-rw-r--r--toolkit/themes/linux/mozapps/jar.mn24
-rw-r--r--toolkit/themes/osx/mozapps/extensions/about.css78
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-error.pngbin0 -> 3402 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.pngbin0 -> 1564 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.pngbin0 -> 1338 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-warning.pngbin0 -> 1567 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/blocklist.css20
-rw-r--r--toolkit/themes/osx/mozapps/extensions/cancel.pngbin0 -> 115 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-available.png (renamed from toolkit/themes/windows/mozapps/webextensions/category-available-XP.png)bin1671 -> 1671 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-dictionaries.pngbin0 -> 1769 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-discover.png (renamed from toolkit/themes/windows/mozapps/webextensions/category-discover-XP.png)bin1324 -> 1324 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-experiments.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-plugins.png (renamed from toolkit/themes/windows/mozapps/webextensions/category-plugins-XP.png)bin886 -> 886 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-recent.png (renamed from toolkit/themes/windows/mozapps/webextensions/category-recent-XP.png)bin1642 -> 1642 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-search.pngbin0 -> 2600 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-searchengines.pngbin0 -> 2814 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-service.pngbin0 -> 2063 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.pngbin0 -> 742 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.pngbin0 -> 1769 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/discover-logo.pngbin0 -> 12007 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/eula.css47
-rw-r--r--toolkit/themes/osx/mozapps/extensions/experimentGeneric.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.pngbin0 -> 554 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/extensionGeneric.pngbin0 -> 1302 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/extensions.css1199
-rw-r--r--toolkit/themes/osx/mozapps/extensions/heart.pngbin0 -> 2949 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/localeGeneric.png (renamed from toolkit/themes/windows/mozapps/webextensions/localeGeneric-XP.png)bin2410 -> 2410 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/navigation.pngbin0 -> 586 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/newaddon.css112
-rw-r--r--toolkit/themes/osx/mozapps/extensions/rating-not-won.pngbin0 -> 1559 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/rating-won.pngbin0 -> 1662 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/search.pngbin0 -> 423 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/selectAddons.css163
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-error.pngbin0 -> 1979 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-info-negative.pngbin0 -> 2027 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-info-positive.pngbin0 -> 1852 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-warning.pngbin0 -> 2177 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/themeGeneric-16.pngbin0 -> 710 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/themeGeneric.png (renamed from toolkit/themes/windows/mozapps/webextensions/themeGeneric-XP.png)bin2185 -> 2185 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.pngbin0 -> 147 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/update.css26
-rw-r--r--toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css90
-rw-r--r--toolkit/themes/osx/mozapps/jar.mn48
-rw-r--r--toolkit/themes/shared/extensions/utilities.svg25
-rw-r--r--toolkit/themes/windows/global/dirListing/folder-XP.pngbin446 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/dirListing/local-XP.pngbin688 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/dirListing/remote-XP.pngbin558 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/dirListing/up-XP.pngbin607 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/global.css8
-rw-r--r--toolkit/themes/windows/global/icons/Landscape-XP.pngbin801 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/Portrait-XP.pngbin837 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/Print-preview-XP.pngbin715 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/Question-XP.pngbin1693 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/Search-close-XP.pngbin586 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/Search-glass-XP.pngbin1448 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/Warning-XP.pngbin1269 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/autoscroll-XP.pngbin2305 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/blacklist_favicon-XP.pngbin603 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/blacklist_large-XP.pngbin3587 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/close-inverted-win7.png (renamed from toolkit/themes/windows/global/icons/close-inverted-XPVista7.png)bin855 -> 855 bytes
-rw-r--r--toolkit/themes/windows/global/icons/close-inverted-win7@2x.png (renamed from toolkit/themes/windows/global/icons/close-inverted-XPVista7@2x.png)bin1865 -> 1865 bytes
-rw-r--r--toolkit/themes/windows/global/icons/close-win7.png (renamed from toolkit/themes/windows/global/icons/close-XPVista7.png)bin931 -> 931 bytes
-rw-r--r--toolkit/themes/windows/global/icons/close-win7@2x.png (renamed from toolkit/themes/windows/global/icons/close-XPVista7@2x.png)bin2031 -> 2031 bytes
-rw-r--r--toolkit/themes/windows/global/icons/error-16-XP.pngbin722 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/error-64-XP.pngbin3914 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/folder-item-XP.pngbin1602 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/information-16-XP.pngbin769 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/information-24-XP.pngbin1244 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/information-32-XP.pngbin1609 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/question-16-XP.pngbin854 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/question-64-XP.pngbin4204 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/sslWarning-XP.pngbin3860 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/warning-16-XP.pngbin563 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/warning-64-XP.pngbin3525 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/warning-large-XP.pngbin2125 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/icons/windowControls-XP.pngbin2060 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/jar.mn104
-rw-r--r--toolkit/themes/windows/global/listbox.css113
-rw-r--r--toolkit/themes/windows/global/menu.css16
-rw-r--r--toolkit/themes/windows/global/menulist.css56
-rw-r--r--toolkit/themes/windows/global/popup.css4
-rw-r--r--toolkit/themes/windows/global/textbox.css6
-rw-r--r--toolkit/themes/windows/global/toolbar/spring-XP.pngbin440 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/toolbarbutton.css8
-rw-r--r--toolkit/themes/windows/global/tree.css393
-rw-r--r--toolkit/themes/windows/global/tree/sort-asc-XP.pngbin161 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/tree/sort-dsc-XP.pngbin155 -> 0 bytes
-rw-r--r--toolkit/themes/windows/global/tree/twisty-XP.svg33
-rw-r--r--toolkit/themes/windows/global/tree/twisty-preWin10.svg (renamed from toolkit/themes/windows/global/tree/twisty-Vista78.svg)0
-rw-r--r--toolkit/themes/windows/mozapps/downloads/downloadButtons-XP.pngbin3243 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/downloads/downloadIcon-XP.pngbin1376 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/about.css91
-rw-r--r--toolkit/themes/windows/mozapps/extensions/alerticon-error.pngbin0 -> 3402 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/alerticon-info-negative.pngbin0 -> 1564 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/alerticon-info-positive.pngbin0 -> 1338 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/alerticon-warning.pngbin0 -> 1567 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/blocklist.css20
-rw-r--r--toolkit/themes/windows/mozapps/extensions/cancel.pngbin0 -> 115 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-available.pngbin0 -> 2235 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-dictionaries.pngbin0 -> 1665 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-discover.pngbin0 -> 1355 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-experiments.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-plugins.pngbin0 -> 1334 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-recent.pngbin0 -> 2251 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-search.pngbin0 -> 2600 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-searchengines.pngbin0 -> 2814 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/category-service.pngbin0 -> 2063 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-16.pngbin0 -> 733 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/dictionaryGeneric.pngbin0 -> 1665 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/discover-logo.pngbin0 -> 12007 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/eula.css47
-rw-r--r--toolkit/themes/windows/mozapps/extensions/experimentGeneric.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/extensionGeneric-16.pngbin0 -> 716 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/extensionGeneric-48.pngbin0 -> 2726 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/extensionGeneric.pngbin0 -> 1615 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/extensions.css1234
-rw-r--r--toolkit/themes/windows/mozapps/extensions/heart.pngbin0 -> 2949 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/localeGeneric.pngbin0 -> 2518 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/navigation.pngbin0 -> 1411 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/newaddon.css110
-rw-r--r--toolkit/themes/windows/mozapps/extensions/rating-not-won.pngbin0 -> 1559 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/rating-won.pngbin0 -> 1662 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/selectAddons.css185
-rw-r--r--toolkit/themes/windows/mozapps/extensions/stripes-error.pngbin0 -> 1979 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/stripes-info-negative.pngbin0 -> 2027 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/stripes-info-positive.pngbin0 -> 1852 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/stripes-warning.pngbin0 -> 2177 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/themeGeneric-16.pngbin0 -> 837 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/themeGeneric.pngbin0 -> 2094 bytes
-rw-r--r--toolkit/themes/windows/mozapps/extensions/update.css26
-rw-r--r--toolkit/themes/windows/mozapps/extensions/xpinstallConfirm.css101
-rw-r--r--toolkit/themes/windows/mozapps/jar.mn79
-rw-r--r--toolkit/themes/windows/mozapps/plugins/pluginBlocked-XP.pngbin1230 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/plugins/pluginGeneric-16-XP.pngbin544 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/plugins/pluginGeneric-XP.pngbin895 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/profile/profileicon-XP.pngbin826 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/update/downloadButtons-XP.pngbin3243 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/webextensions/extensionGeneric-16-XP.pngbin398 -> 0 bytes
-rw-r--r--toolkit/themes/windows/mozapps/webextensions/themeGeneric-16-XP.pngbin842 -> 0 bytes
-rw-r--r--toolkit/toolkit.mozbuild4
-rw-r--r--toolkit/xre/nsAppRunner.cpp21
-rw-r--r--toolkit/xre/nsEmbedFunctions.cpp24
-rw-r--r--toolkit/xre/nsXREDirProvider.cpp10
-rw-r--r--toolkit/xre/test/win/TestDllInterceptor.cpp5
-rw-r--r--widget/GfxInfoBase.cpp8
-rw-r--r--widget/LookAndFeel.h4
-rw-r--r--widget/gtk/mozgtk/mozgtk.c1
-rw-r--r--widget/windows/GfxInfo.cpp79
-rw-r--r--widget/windows/KeyboardLayout.cpp62
-rw-r--r--widget/windows/KeyboardLayout.h5
-rw-r--r--widget/windows/LSPAnnotator.cpp5
-rw-r--r--widget/windows/TSFTextStore.cpp3
-rw-r--r--widget/windows/TaskbarPreview.cpp2
-rw-r--r--widget/windows/WinMouseScrollHandler.cpp7
-rw-r--r--widget/windows/WinTaskbar.cpp52
-rw-r--r--widget/windows/WinUtils.cpp59
-rw-r--r--widget/windows/WindowsUIUtils.cpp4
-rw-r--r--widget/windows/mozwrlbase.h77
-rw-r--r--widget/windows/nsDataObj.cpp3
-rw-r--r--widget/windows/nsFilePicker.cpp542
-rw-r--r--widget/windows/nsFilePicker.h37
-rw-r--r--widget/windows/nsLookAndFeel.cpp17
-rw-r--r--widget/windows/nsLookAndFeel.h13
-rw-r--r--widget/windows/nsNativeThemeWin.cpp463
-rw-r--r--widget/windows/nsNativeThemeWin.h3
-rw-r--r--widget/windows/nsUXThemeConstants.h2
-rw-r--r--widget/windows/nsUXThemeData.cpp8
-rw-r--r--widget/windows/nsWinGesture.h155
-rw-r--r--widget/windows/nsWindow.cpp93
-rw-r--r--widget/windows/nsWindow.h3
-rw-r--r--xpcom/build/XPCOMInit.cpp3
-rw-r--r--xpcom/glue/nsThreadUtils.cpp8
-rw-r--r--xpcom/io/SpecialSystemDirectory.cpp43
-rw-r--r--xpcom/io/SpecialSystemDirectory.h3
-rw-r--r--xpcom/io/nsLocalFileWin.cpp13
1724 files changed, 149407 insertions, 24435 deletions
diff --git a/CLOBBER b/CLOBBER
index d4cdfaff3..5dd4ee07f 100644
--- a/CLOBBER
+++ b/CLOBBER
@@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
-Merge day clobber \ No newline at end of file
+Clobber for un-folding browsercomps \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..32cdf9b04
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+# Unified XUL Platform (UXP)
+
+This repository holds the code for a unified application platform for XUL-based
+applications. It is a hard fork from the Mozilla code repository (mozilla-central)
+with an ESR-52 fork point.
+
+In addition to further development based on the Mozilla upstream code, and
+selective cherry-picking of directly-applicable patches, this repository has its
+own development and holds the base for a future platform to be used by XUL
+applications.
+
+This repository will contain at least one application to demonstrate and make use
+of the platform: The Basilisk web browser, a close twin to Mozilla's Firefox.
+
+### A note about trademarks and branding
+
+Although this repository is licensed under Mozilla Public License v2.0, the
+trademarks and brands contained herein remain the property of their respective
+owners. For more details, please see the notifications in the respective directories.
+
+### Foundation and maintainership
+
+This repository has been founded and is maintained by Moonchild (M.C. Straver).
+If you fork this repository to perform your own work on it, please consider
+offering improvement patches upstream to its origin to mutually improve the
+platform and build a future for XUL.
diff --git a/accessible/windows/ia2/ia2Accessible.cpp b/accessible/windows/ia2/ia2Accessible.cpp
index c72719b51..97cd0d788 100644
--- a/accessible/windows/ia2/ia2Accessible.cpp
+++ b/accessible/windows/ia2/ia2Accessible.cpp
@@ -63,8 +63,6 @@ ia2Accessible::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2Accessible::get_nRelations(long* aNRelations)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNRelations)
return E_INVALIDARG;
*aNRelations = 0;
@@ -84,16 +82,12 @@ ia2Accessible::get_nRelations(long* aNRelations)
(*aNRelations)++;
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_relation(long aRelationIndex,
IAccessibleRelation** aRelation)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRelation || aRelationIndex < 0)
return E_INVALIDARG;
*aRelation = nullptr;
@@ -124,8 +118,6 @@ ia2Accessible::get_relation(long aRelationIndex,
}
return E_INVALIDARG;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -133,8 +125,6 @@ ia2Accessible::get_relations(long aMaxRelations,
IAccessibleRelation** aRelation,
long *aNRelations)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRelation || !aNRelations || aMaxRelations <= 0)
return E_INVALIDARG;
*aNRelations = 0;
@@ -160,15 +150,11 @@ ia2Accessible::get_relations(long aMaxRelations,
}
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::role(long* aRole)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRole)
return E_INVALIDARG;
*aRole = 0;
@@ -204,15 +190,11 @@ ia2Accessible::role(long* aRole)
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::scrollTo(enum IA2ScrollType aScrollType)
{
- A11Y_TRYBLOCK_BEGIN
-
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@@ -222,16 +204,12 @@ ia2Accessible::scrollTo(enum IA2ScrollType aScrollType)
aScrollType);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::scrollToPoint(enum IA2CoordinateType aCoordType,
long aX, long aY)
{
- A11Y_TRYBLOCK_BEGIN
-
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@@ -244,8 +222,6 @@ ia2Accessible::scrollToPoint(enum IA2CoordinateType aCoordType,
acc->ScrollToPoint(geckoCoordType, aX, aY);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -253,8 +229,6 @@ ia2Accessible::get_groupPosition(long* aGroupLevel,
long* aSimilarItemsInGroup,
long* aPositionInGroup)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aGroupLevel || !aSimilarItemsInGroup || !aPositionInGroup)
return E_INVALIDARG;
@@ -279,15 +253,11 @@ ia2Accessible::get_groupPosition(long* aGroupLevel,
*aPositionInGroup = groupPos.posInSet;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_states(AccessibleStates* aStates)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStates)
return E_INVALIDARG;
*aStates = 0;
@@ -347,50 +317,36 @@ ia2Accessible::get_states(AccessibleStates* aStates)
*aStates |= IA2_STATE_PINNED;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_extendedRole(BSTR* aExtendedRole)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aExtendedRole)
return E_INVALIDARG;
*aExtendedRole = nullptr;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_localizedExtendedRole(BSTR* aLocalizedExtendedRole)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLocalizedExtendedRole)
return E_INVALIDARG;
*aLocalizedExtendedRole = nullptr;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_nExtendedStates(long* aNExtendedStates)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNExtendedStates)
return E_INVALIDARG;
*aNExtendedStates = 0;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -398,16 +354,12 @@ ia2Accessible::get_extendedStates(long aMaxExtendedStates,
BSTR** aExtendedStates,
long* aNExtendedStates)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aExtendedStates || !aNExtendedStates)
return E_INVALIDARG;
*aExtendedStates = nullptr;
*aNExtendedStates = 0;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -415,38 +367,28 @@ ia2Accessible::get_localizedExtendedStates(long aMaxLocalizedExtendedStates,
BSTR** aLocalizedExtendedStates,
long* aNLocalizedExtendedStates)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLocalizedExtendedStates || !aNLocalizedExtendedStates)
return E_INVALIDARG;
*aLocalizedExtendedStates = nullptr;
*aNLocalizedExtendedStates = 0;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_uniqueID(long* aUniqueID)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aUniqueID)
return E_INVALIDARG;
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
*aUniqueID = AccessibleWrap::GetChildIDFor(acc);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_windowHandle(HWND* aWindowHandle)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aWindowHandle)
return E_INVALIDARG;
*aWindowHandle = 0;
@@ -457,15 +399,11 @@ ia2Accessible::get_windowHandle(HWND* aWindowHandle)
*aWindowHandle = AccessibleWrap::GetHWNDFor(acc);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_indexInParent(long* aIndexInParent)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIndexInParent)
return E_INVALIDARG;
*aIndexInParent = -1;
@@ -481,15 +419,11 @@ ia2Accessible::get_indexInParent(long* aIndexInParent)
return S_FALSE;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_locale(IA2Locale* aLocale)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLocale)
return E_INVALIDARG;
@@ -532,15 +466,11 @@ ia2Accessible::get_locale(IA2Locale* aLocale)
// country abbreviations or if there are more than one subcode.
aLocale->variant = ::SysAllocString(lang.get());
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_attributes(BSTR* aAttributes)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAttributes)
return E_INVALIDARG;
*aAttributes = nullptr;
@@ -558,8 +488,6 @@ ia2Accessible::get_attributes(BSTR* aAttributes)
MOZ_ASSERT(!acc->IsProxy());
return E_UNEXPECTED;
-
- A11Y_TRYBLOCK_END
}
////////////////////////////////////////////////////////////////////////////////
@@ -568,22 +496,16 @@ ia2Accessible::get_attributes(BSTR* aAttributes)
STDMETHODIMP
ia2Accessible::get_attribute(BSTR name, VARIANT* aAttribute)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAttribute)
return E_INVALIDARG;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible,
long* aCaretOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAccessible || !aCaretOffset)
return E_INVALIDARG;
@@ -611,8 +533,6 @@ ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible,
(*aAccessible)->AddRef();
*aCaretOffset = caretOffset;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -621,8 +541,6 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
IUnknown*** aTargets,
long* aNTargets)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aTargets || !aNTargets || aMaxTargets < 0)
return E_INVALIDARG;
*aNTargets = 0;
@@ -663,16 +581,12 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2Accessible::get_selectionRanges(IA2Range** aRanges,
long *aNRanges)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRanges || !aNRanges)
return E_INVALIDARG;
@@ -714,8 +628,6 @@ ia2Accessible::get_selectionRanges(IA2Range** aRanges,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleAction.cpp b/accessible/windows/ia2/ia2AccessibleAction.cpp
index 0710b753f..ffd19f049 100644
--- a/accessible/windows/ia2/ia2AccessibleAction.cpp
+++ b/accessible/windows/ia2/ia2AccessibleAction.cpp
@@ -39,8 +39,6 @@ ia2AccessibleAction::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2AccessibleAction::nActions(long* aActionCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aActionCount)
return E_INVALIDARG;
@@ -52,30 +50,22 @@ ia2AccessibleAction::nActions(long* aActionCount)
*aActionCount = acc->ActionCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleAction::doAction(long aActionIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
uint8_t index = static_cast<uint8_t>(aActionIndex);
return acc->DoAction(index) ? S_OK : E_INVALIDARG;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleAction::get_description(long aActionIndex, BSTR *aDescription)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aDescription)
return E_INVALIDARG;
*aDescription = nullptr;
@@ -93,8 +83,6 @@ ia2AccessibleAction::get_description(long aActionIndex, BSTR *aDescription)
*aDescription = ::SysAllocStringLen(description.get(),
description.Length());
return *aDescription ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -102,8 +90,6 @@ ia2AccessibleAction::get_keyBinding(long aActionIndex, long aNumMaxBinding,
BSTR **aKeyBinding,
long *aNumBinding)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aKeyBinding)
return E_INVALIDARG;
*aKeyBinding = nullptr;
@@ -143,15 +129,11 @@ ia2AccessibleAction::get_keyBinding(long aActionIndex, long aNumMaxBinding,
*aNumBinding = 1;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleAction::get_name(long aActionIndex, BSTR *aName)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aName)
return E_INVALIDARG;
@@ -169,20 +151,14 @@ ia2AccessibleAction::get_name(long aActionIndex, BSTR *aName)
*aName = ::SysAllocStringLen(name.get(), name.Length());
return *aName ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleAction::get_localizedName(long aActionIndex, BSTR *aLocalizedName)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLocalizedName)
return E_INVALIDARG;
*aLocalizedName = nullptr;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleComponent.cpp b/accessible/windows/ia2/ia2AccessibleComponent.cpp
index f32c09d1e..2c9cd0ab4 100644
--- a/accessible/windows/ia2/ia2AccessibleComponent.cpp
+++ b/accessible/windows/ia2/ia2AccessibleComponent.cpp
@@ -41,8 +41,6 @@ ia2AccessibleComponent::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2AccessibleComponent::get_locationInParent(long* aX, long* aY)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aX || !aY)
return E_INVALIDARG;
@@ -75,15 +73,11 @@ ia2AccessibleComponent::get_locationInParent(long* aX, long* aY)
*aX = rect.x - parentRect.x;
*aY = rect.y - parentRect.y;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleComponent::get_foreground(IA2Color* aForeground)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aForeground)
return E_INVALIDARG;
@@ -98,15 +92,11 @@ ia2AccessibleComponent::get_foreground(IA2Color* aForeground)
*aForeground = frame->StyleColor()->mColor;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleComponent::get_background(IA2Color* aBackground)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aBackground)
return E_INVALIDARG;
@@ -121,7 +111,5 @@ ia2AccessibleComponent::get_background(IA2Color* aBackground)
*aBackground = frame->StyleBackground()->mBackgroundColor;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleEditableText.cpp b/accessible/windows/ia2/ia2AccessibleEditableText.cpp
index 361d6a130..0b433bf6d 100644
--- a/accessible/windows/ia2/ia2AccessibleEditableText.cpp
+++ b/accessible/windows/ia2/ia2AccessibleEditableText.cpp
@@ -22,8 +22,6 @@ using namespace mozilla::a11y;
STDMETHODIMP
ia2AccessibleEditableText::copyText(long aStartOffset, long aEndOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -35,15 +33,11 @@ ia2AccessibleEditableText::copyText(long aStartOffset, long aEndOffset)
textAcc->CopyText(aStartOffset, aEndOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleEditableText::deleteText(long aStartOffset, long aEndOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -55,15 +49,11 @@ ia2AccessibleEditableText::deleteText(long aStartOffset, long aEndOffset)
textAcc->DeleteText(aStartOffset, aEndOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleEditableText::insertText(long aOffset, BSTR *aText)
{
- A11Y_TRYBLOCK_BEGIN
-
uint32_t length = ::SysStringLen(*aText);
nsAutoString text(*aText, length);
MOZ_ASSERT(!HyperTextProxyFor(this));
@@ -77,15 +67,11 @@ ia2AccessibleEditableText::insertText(long aOffset, BSTR *aText)
textAcc->InsertText(text, aOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleEditableText::cutText(long aStartOffset, long aEndOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -97,15 +83,11 @@ ia2AccessibleEditableText::cutText(long aStartOffset, long aEndOffset)
textAcc->CutText(aStartOffset, aEndOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleEditableText::pasteText(long aOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -117,16 +99,12 @@ ia2AccessibleEditableText::pasteText(long aOffset)
textAcc->PasteText(aOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleEditableText::replaceText(long aStartOffset, long aEndOffset,
BSTR *aText)
{
- A11Y_TRYBLOCK_BEGIN
-
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
if (textAcc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@@ -141,8 +119,6 @@ ia2AccessibleEditableText::replaceText(long aStartOffset, long aEndOffset,
textAcc->InsertText(text, aStartOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
diff --git a/accessible/windows/ia2/ia2AccessibleHyperlink.cpp b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
index c6d564103..d36c0ef25 100644
--- a/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
+++ b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
@@ -45,8 +45,6 @@ ia2AccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAnchor)
return E_INVALIDARG;
@@ -77,15 +75,11 @@ ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor)
aAnchor->punkVal = static_cast<IUnknown*>(instancePtr);
aAnchor->vt = VT_UNKNOWN;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAnchorTarget) {
return E_INVALIDARG;
}
@@ -124,15 +118,11 @@ ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
aAnchorTarget->bstrVal = ::SysAllocStringLen(stringURI.get(),
stringURI.Length());
return aAnchorTarget->bstrVal ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHyperlink::get_startIndex(long* aIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIndex)
return E_INVALIDARG;
@@ -149,15 +139,11 @@ ia2AccessibleHyperlink::get_startIndex(long* aIndex)
*aIndex = thisObj->StartOffset();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHyperlink::get_endIndex(long* aIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIndex)
return E_INVALIDARG;
@@ -174,15 +160,11 @@ ia2AccessibleHyperlink::get_endIndex(long* aIndex)
*aIndex = thisObj->EndOffset();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHyperlink::get_valid(boolean* aValid)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aValid)
return E_INVALIDARG;
@@ -199,7 +181,5 @@ ia2AccessibleHyperlink::get_valid(boolean* aValid)
*aValid = thisObj->IsLinkValid();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleHypertext.cpp b/accessible/windows/ia2/ia2AccessibleHypertext.cpp
index f4b3bdaf2..555bde686 100644
--- a/accessible/windows/ia2/ia2AccessibleHypertext.cpp
+++ b/accessible/windows/ia2/ia2AccessibleHypertext.cpp
@@ -19,8 +19,6 @@ using namespace mozilla::a11y;
STDMETHODIMP
ia2AccessibleHypertext::get_nHyperlinks(long* aHyperlinkCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aHyperlinkCount)
return E_INVALIDARG;
@@ -34,16 +32,12 @@ ia2AccessibleHypertext::get_nHyperlinks(long* aHyperlinkCount)
*aHyperlinkCount = hyperText->LinkCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHypertext::get_hyperlink(long aLinkIndex,
IAccessibleHyperlink** aHyperlink)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aHyperlink)
return E_INVALIDARG;
@@ -65,15 +59,11 @@ ia2AccessibleHypertext::get_hyperlink(long aLinkIndex,
static_cast<IAccessibleHyperlink*>(hyperLink);
(*aHyperlink)->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHypertext::get_hyperlinkIndex(long aCharIndex, long* aHyperlinkIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aHyperlinkIndex)
return E_INVALIDARG;
@@ -87,7 +77,5 @@ ia2AccessibleHypertext::get_hyperlinkIndex(long aCharIndex, long* aHyperlinkInde
*aHyperlinkIndex = hyperAcc->LinkIndexAtOffset(aCharIndex);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleImage.cpp b/accessible/windows/ia2/ia2AccessibleImage.cpp
index 989fde925..7dc7f765c 100644
--- a/accessible/windows/ia2/ia2AccessibleImage.cpp
+++ b/accessible/windows/ia2/ia2AccessibleImage.cpp
@@ -43,8 +43,6 @@ ia2AccessibleImage::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2AccessibleImage::get_description(BSTR* aDescription)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aDescription)
return E_INVALIDARG;
@@ -61,8 +59,6 @@ ia2AccessibleImage::get_description(BSTR* aDescription)
*aDescription = ::SysAllocStringLen(description.get(), description.Length());
return *aDescription ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -70,8 +66,6 @@ ia2AccessibleImage::get_imagePosition(enum IA2CoordinateType aCoordType,
long* aX,
long* aY)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aX || !aY)
return E_INVALIDARG;
@@ -90,15 +84,11 @@ ia2AccessibleImage::get_imagePosition(enum IA2CoordinateType aCoordType,
*aX = pos.x;
*aY = pos.y;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleImage::get_imageSize(long* aHeight, long* aWidth)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aHeight || !aWidth)
return E_INVALIDARG;
@@ -113,7 +103,5 @@ ia2AccessibleImage::get_imageSize(long* aHeight, long* aWidth)
*aHeight = size.width;
*aWidth = size.height;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleRelation.cpp b/accessible/windows/ia2/ia2AccessibleRelation.cpp
index cc6fd83c8..67435ee52 100644
--- a/accessible/windows/ia2/ia2AccessibleRelation.cpp
+++ b/accessible/windows/ia2/ia2AccessibleRelation.cpp
@@ -34,8 +34,6 @@ IMPL_IUNKNOWN_QUERY_TAIL
STDMETHODIMP
ia2AccessibleRelation::get_relationType(BSTR* aRelationType)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRelationType)
return E_INVALIDARG;
@@ -51,43 +49,31 @@ ia2AccessibleRelation::get_relationType(BSTR* aRelationType)
}
return *aRelationType ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleRelation::get_localizedRelationType(BSTR *aLocalizedRelationType)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLocalizedRelationType)
return E_INVALIDARG;
*aLocalizedRelationType = nullptr;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleRelation::get_nTargets(long *aNTargets)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNTargets)
return E_INVALIDARG;
*aNTargets = mTargets.Length();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleRelation::get_target(long aTargetIndex, IUnknown **aTarget)
{
- A11Y_TRYBLOCK_BEGIN
-
if (aTargetIndex < 0 || (uint32_t)aTargetIndex >= mTargets.Length() || !aTarget)
return E_INVALIDARG;
@@ -97,16 +83,12 @@ ia2AccessibleRelation::get_target(long aTargetIndex, IUnknown **aTarget)
(*aTarget)->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleRelation::get_targets(long aMaxTargets, IUnknown **aTargets,
long *aNTargets)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNTargets || !aTargets)
return E_INVALIDARG;
@@ -120,6 +102,4 @@ ia2AccessibleRelation::get_targets(long aMaxTargets, IUnknown **aTargets,
*aNTargets = maxTargets;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleTable.cpp b/accessible/windows/ia2/ia2AccessibleTable.cpp
index 6ac6bbab4..a99f72def 100644
--- a/accessible/windows/ia2/ia2AccessibleTable.cpp
+++ b/accessible/windows/ia2/ia2AccessibleTable.cpp
@@ -60,8 +60,6 @@ ia2AccessibleTable::get_accessibleAt(long aRowIdx, long aColIdx,
STDMETHODIMP
ia2AccessibleTable::get_caption(IUnknown** aAccessible)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAccessible)
return E_INVALIDARG;
@@ -75,16 +73,12 @@ ia2AccessibleTable::get_caption(IUnknown** aAccessible)
(*aAccessible = static_cast<IAccessible*>(caption))->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_childIndex(long aRowIdx, long aColIdx,
long* aChildIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aChildIdx)
return E_INVALIDARG;
@@ -99,15 +93,11 @@ ia2AccessibleTable::get_childIndex(long aRowIdx, long aColIdx,
*aChildIdx = mTable->CellIndexAt(aRowIdx, aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_columnDescription(long aColIdx, BSTR* aDescription)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aDescription)
return E_INVALIDARG;
@@ -125,16 +115,12 @@ ia2AccessibleTable::get_columnDescription(long aColIdx, BSTR* aDescription)
*aDescription = ::SysAllocStringLen(descr.get(), descr.Length());
return *aDescription ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_columnExtentAt(long aRowIdx, long aColIdx,
long* aSpan)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aSpan)
return E_INVALIDARG;
@@ -149,31 +135,23 @@ ia2AccessibleTable::get_columnExtentAt(long aRowIdx, long aColIdx,
*aSpan = mTable->ColExtentAt(aRowIdx, aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_columnHeader(IAccessibleTable** aAccessibleTable,
long* aStartingRowIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAccessibleTable || !aStartingRowIndex)
return E_INVALIDARG;
*aAccessibleTable = nullptr;
*aStartingRowIndex = -1;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_columnIndex(long aCellIdx, long* aColIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aColIdx)
return E_INVALIDARG;
@@ -187,15 +165,11 @@ ia2AccessibleTable::get_columnIndex(long aCellIdx, long* aColIdx)
*aColIdx = mTable->ColIndexAt(aCellIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_nColumns(long* aColCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aColCount)
return E_INVALIDARG;
@@ -205,15 +179,11 @@ ia2AccessibleTable::get_nColumns(long* aColCount)
*aColCount = mTable->ColCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_nRows(long* aRowCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRowCount)
return E_INVALIDARG;
@@ -223,8 +193,6 @@ ia2AccessibleTable::get_nRows(long* aRowCount)
*aRowCount = mTable->RowCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -236,8 +204,6 @@ ia2AccessibleTable::get_nSelectedChildren(long* aChildCount)
STDMETHODIMP
ia2AccessibleTable::get_nSelectedColumns(long* aColCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aColCount)
return E_INVALIDARG;
@@ -247,15 +213,11 @@ ia2AccessibleTable::get_nSelectedColumns(long* aColCount)
*aColCount = mTable->SelectedColCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_nSelectedRows(long* aRowCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRowCount)
return E_INVALIDARG;
@@ -266,15 +228,11 @@ ia2AccessibleTable::get_nSelectedRows(long* aRowCount)
*aRowCount = mTable->SelectedRowCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_rowDescription(long aRowIdx, BSTR* aDescription)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aDescription)
return E_INVALIDARG;
@@ -292,15 +250,11 @@ ia2AccessibleTable::get_rowDescription(long aRowIdx, BSTR* aDescription)
*aDescription = ::SysAllocStringLen(descr.get(), descr.Length());
return *aDescription ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_rowExtentAt(long aRowIdx, long aColIdx, long* aSpan)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aSpan)
return E_INVALIDARG;
@@ -315,31 +269,23 @@ ia2AccessibleTable::get_rowExtentAt(long aRowIdx, long aColIdx, long* aSpan)
*aSpan = mTable->RowExtentAt(aRowIdx, aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_rowHeader(IAccessibleTable** aAccessibleTable,
long* aStartingColumnIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAccessibleTable || !aStartingColumnIndex)
return E_INVALIDARG;
*aAccessibleTable = nullptr;
*aStartingColumnIndex = -1;
return E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_rowIndex(long aCellIdx, long* aRowIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRowIdx)
return E_INVALIDARG;
@@ -353,16 +299,12 @@ ia2AccessibleTable::get_rowIndex(long aCellIdx, long* aRowIdx)
*aRowIdx = mTable->RowIndexAt(aCellIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_selectedChildren(long aMaxChildren, long** aChildren,
long* aNChildren)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aChildren || !aNChildren)
return E_INVALIDARG;
@@ -384,36 +326,24 @@ ia2AccessibleTable::get_selectedChildren(long aMaxChildren, long** aChildren,
(*aChildren)[i] = cellIndices[i];
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_selectedColumns(long aMaxColumns, long** aColumns,
long* aNColumns)
{
- A11Y_TRYBLOCK_BEGIN
-
return get_selectedColumns(aColumns, aNColumns);
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_selectedRows(long aMaxRows, long** aRows, long* aNRows)
{
- A11Y_TRYBLOCK_BEGIN
-
return get_selectedRows(aRows, aNRows);
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_summary(IUnknown** aAccessible)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAccessible)
return E_INVALIDARG;
@@ -424,15 +354,11 @@ ia2AccessibleTable::get_summary(IUnknown** aAccessible)
*aAccessible = nullptr;
return S_FALSE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_isColumnSelected(long aColIdx, boolean* aIsSelected)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIsSelected)
return E_INVALIDARG;
@@ -445,15 +371,11 @@ ia2AccessibleTable::get_isColumnSelected(long aColIdx, boolean* aIsSelected)
*aIsSelected = mTable->IsColSelected(aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_isRowSelected(long aRowIdx, boolean* aIsSelected)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIsSelected)
return E_INVALIDARG;
@@ -466,16 +388,12 @@ ia2AccessibleTable::get_isRowSelected(long aRowIdx, boolean* aIsSelected)
*aIsSelected = mTable->IsRowSelected(aRowIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_isSelected(long aRowIdx, long aColIdx,
boolean* aIsSelected)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIsSelected)
return E_INVALIDARG;
@@ -490,15 +408,11 @@ ia2AccessibleTable::get_isSelected(long aRowIdx, long aColIdx,
*aIsSelected = mTable->IsCellSelected(aRowIdx, aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::selectRow(long aRowIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!mTable)
return CO_E_OBJNOTCONNECTED;
@@ -507,15 +421,11 @@ ia2AccessibleTable::selectRow(long aRowIdx)
mTable->SelectRow(aRowIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::selectColumn(long aColIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!mTable)
return CO_E_OBJNOTCONNECTED;
@@ -524,15 +434,11 @@ ia2AccessibleTable::selectColumn(long aColIdx)
mTable->SelectCol(aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::unselectRow(long aRowIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!mTable)
return CO_E_OBJNOTCONNECTED;
@@ -541,15 +447,11 @@ ia2AccessibleTable::unselectRow(long aRowIdx)
mTable->UnselectRow(aRowIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::unselectColumn(long aColIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!mTable)
return CO_E_OBJNOTCONNECTED;
@@ -558,8 +460,6 @@ ia2AccessibleTable::unselectColumn(long aColIdx)
mTable->UnselectCol(aColIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -569,8 +469,6 @@ ia2AccessibleTable::get_rowColumnExtentsAtIndex(long aCellIdx, long* aRowIdx,
long* aColExtents,
boolean* aIsSelected)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRowIdx || !aColIdx || !aRowExtents || !aColExtents || !aIsSelected)
return E_INVALIDARG;
@@ -595,8 +493,6 @@ ia2AccessibleTable::get_rowColumnExtentsAtIndex(long aCellIdx, long* aRowIdx,
*aIsSelected = mTable->IsCellSelected(rowIdx, colIdx);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -611,8 +507,6 @@ ia2AccessibleTable::get_modelChange(IA2TableModelChange* aModelChange)
STDMETHODIMP
ia2AccessibleTable::get_cellAt(long aRowIdx, long aColIdx, IUnknown** aCell)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCell)
return E_INVALIDARG;
@@ -628,15 +522,11 @@ ia2AccessibleTable::get_cellAt(long aRowIdx, long aColIdx, IUnknown** aCell)
(*aCell = static_cast<IAccessible*>(cell))->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_nSelectedCells(long* aCellCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCellCount)
return E_INVALIDARG;
@@ -646,15 +536,11 @@ ia2AccessibleTable::get_nSelectedCells(long* aCellCount)
*aCellCount = mTable->SelectedCellCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_selectedCells(IUnknown*** aCells, long* aNSelectedCells)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCells || !aNSelectedCells)
return E_INVALIDARG;
@@ -682,15 +568,11 @@ ia2AccessibleTable::get_selectedCells(IUnknown*** aCells, long* aNSelectedCells)
*aNSelectedCells = cells.Length();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_selectedColumns(long** aColumns, long* aNColumns)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aColumns || !aNColumns)
return E_INVALIDARG;
@@ -712,15 +594,11 @@ ia2AccessibleTable::get_selectedColumns(long** aColumns, long* aNColumns)
(*aColumns)[i] = colIndices[i];
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTable::get_selectedRows(long** aRows, long* aNRows)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRows || !aNRows)
return E_INVALIDARG;
@@ -742,6 +620,4 @@ ia2AccessibleTable::get_selectedRows(long** aRows, long* aNRows)
(*aRows)[i] = rowIndices[i];
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleTableCell.cpp b/accessible/windows/ia2/ia2AccessibleTableCell.cpp
index e625a7049..19cfc55da 100644
--- a/accessible/windows/ia2/ia2AccessibleTableCell.cpp
+++ b/accessible/windows/ia2/ia2AccessibleTableCell.cpp
@@ -46,8 +46,6 @@ ia2AccessibleTableCell::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2AccessibleTableCell::get_table(IUnknown** aTable)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aTable)
return E_INVALIDARG;
@@ -63,15 +61,11 @@ ia2AccessibleTableCell::get_table(IUnknown** aTable)
*aTable = static_cast<IAccessible*>(wrap);
(*aTable)->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_columnExtent(long* aSpan)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aSpan)
return E_INVALIDARG;
@@ -82,16 +76,12 @@ ia2AccessibleTableCell::get_columnExtent(long* aSpan)
*aSpan = mTableCell->ColExtent();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_columnHeaderCells(IUnknown*** aCellAccessibles,
long* aNColumnHeaderCells)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCellAccessibles || !aNColumnHeaderCells)
return E_INVALIDARG;
@@ -118,15 +108,11 @@ ia2AccessibleTableCell::get_columnHeaderCells(IUnknown*** aCellAccessibles,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_columnIndex(long* aColIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aColIdx)
return E_INVALIDARG;
@@ -136,15 +122,11 @@ ia2AccessibleTableCell::get_columnIndex(long* aColIdx)
*aColIdx = mTableCell->ColIdx();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_rowExtent(long* aSpan)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aSpan)
return E_INVALIDARG;
@@ -154,16 +136,12 @@ ia2AccessibleTableCell::get_rowExtent(long* aSpan)
*aSpan = mTableCell->RowExtent();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_rowHeaderCells(IUnknown*** aCellAccessibles,
long* aNRowHeaderCells)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCellAccessibles || !aNRowHeaderCells)
return E_INVALIDARG;
@@ -189,15 +167,11 @@ ia2AccessibleTableCell::get_rowHeaderCells(IUnknown*** aCellAccessibles,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_rowIndex(long* aRowIdx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRowIdx)
return E_INVALIDARG;
@@ -207,8 +181,6 @@ ia2AccessibleTableCell::get_rowIndex(long* aRowIdx)
*aRowIdx = mTableCell->RowIdx();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -217,8 +189,6 @@ ia2AccessibleTableCell::get_rowColumnExtents(long* aRowIdx, long* aColIdx,
long* aColExtents,
boolean* aIsSelected)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRowIdx || !aColIdx || !aRowExtents || !aColExtents || !aIsSelected)
return E_INVALIDARG;
@@ -234,15 +204,11 @@ ia2AccessibleTableCell::get_rowColumnExtents(long* aRowIdx, long* aColIdx,
*aIsSelected = mTableCell->Selected();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleTableCell::get_isSelected(boolean* aIsSelected)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aIsSelected)
return E_INVALIDARG;
@@ -252,6 +218,4 @@ ia2AccessibleTableCell::get_isSelected(boolean* aIsSelected)
*aIsSelected = mTableCell->Selected();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/ia2/ia2AccessibleText.cpp b/accessible/windows/ia2/ia2AccessibleText.cpp
index 7ac766f30..86b3abdf6 100644
--- a/accessible/windows/ia2/ia2AccessibleText.cpp
+++ b/accessible/windows/ia2/ia2AccessibleText.cpp
@@ -28,8 +28,6 @@ bool ia2AccessibleText::sLastTextChangeWasInsert = false;
STDMETHODIMP
ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -38,16 +36,12 @@ ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset)
return textAcc->AddToSelection(aStartOffset, aEndOffset) ?
S_OK : E_INVALIDARG;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_attributes(long aOffset, long *aStartOffset,
long *aEndOffset, BSTR *aTextAttributes)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStartOffset || !aEndOffset || !aTextAttributes)
return E_INVALIDARG;
@@ -74,15 +68,11 @@ ia2AccessibleText::get_attributes(long aOffset, long *aStartOffset,
*aEndOffset = endOffset;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_caretOffset(long *aOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aOffset)
return E_INVALIDARG;
@@ -97,8 +87,6 @@ ia2AccessibleText::get_caretOffset(long *aOffset)
*aOffset = textAcc->CaretOffset();
return *aOffset != -1 ? S_OK : S_FALSE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -107,8 +95,6 @@ ia2AccessibleText::get_characterExtents(long aOffset,
long* aX, long* aY,
long* aWidth, long* aHeight)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aX || !aY || !aWidth || !aHeight)
return E_INVALIDARG;
*aX = *aY = *aWidth = *aHeight = 0;
@@ -129,15 +115,11 @@ ia2AccessibleText::get_characterExtents(long aOffset,
*aWidth = rect.width;
*aHeight = rect.height;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_nSelections(long* aNSelections)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNSelections)
return E_INVALIDARG;
*aNSelections = 0;
@@ -151,8 +133,6 @@ ia2AccessibleText::get_nSelections(long* aNSelections)
*aNSelections = textAcc->SelectionCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -160,8 +140,6 @@ ia2AccessibleText::get_offsetAtPoint(long aX, long aY,
enum IA2CoordinateType aCoordType,
long* aOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aOffset)
return E_INVALIDARG;
*aOffset = 0;
@@ -179,16 +157,12 @@ ia2AccessibleText::get_offsetAtPoint(long aX, long aY,
*aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType);
return *aOffset == -1 ? S_FALSE : S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset,
long* aEndOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStartOffset || !aEndOffset)
return E_INVALIDARG;
*aStartOffset = *aEndOffset = 0;
@@ -207,15 +181,11 @@ ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset,
*aStartOffset = startOffset;
*aEndOffset = endOffset;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_text(long aStartOffset, long aEndOffset, BSTR* aText)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aText)
return E_INVALIDARG;
@@ -239,8 +209,6 @@ ia2AccessibleText::get_text(long aStartOffset, long aEndOffset, BSTR* aText)
*aText = ::SysAllocStringLen(text.get(), text.Length());
return *aText ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -249,8 +217,6 @@ ia2AccessibleText::get_textBeforeOffset(long aOffset,
long* aStartOffset, long* aEndOffset,
BSTR* aText)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStartOffset || !aEndOffset || !aText)
return E_INVALIDARG;
@@ -287,8 +253,6 @@ ia2AccessibleText::get_textBeforeOffset(long aOffset,
*aText = ::SysAllocStringLen(text.get(), text.Length());
return *aText ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -297,8 +261,6 @@ ia2AccessibleText::get_textAfterOffset(long aOffset,
long* aStartOffset, long* aEndOffset,
BSTR* aText)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStartOffset || !aEndOffset || !aText)
return E_INVALIDARG;
@@ -335,8 +297,6 @@ ia2AccessibleText::get_textAfterOffset(long aOffset,
*aText = ::SysAllocStringLen(text.get(), text.Length());
return *aText ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -345,8 +305,6 @@ ia2AccessibleText::get_textAtOffset(long aOffset,
long* aStartOffset, long* aEndOffset,
BSTR* aText)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStartOffset || !aEndOffset || !aText)
return E_INVALIDARG;
@@ -381,15 +339,11 @@ ia2AccessibleText::get_textAtOffset(long aOffset,
*aText = ::SysAllocStringLen(text.get(), text.Length());
return *aText ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::removeSelection(long aSelectionIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -398,15 +352,11 @@ ia2AccessibleText::removeSelection(long aSelectionIndex)
return textAcc->RemoveFromSelection(aSelectionIndex) ?
S_OK : E_INVALIDARG;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::setCaretOffset(long aOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -418,16 +368,12 @@ ia2AccessibleText::setCaretOffset(long aOffset)
textAcc->SetCaretOffset(aOffset);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset,
long aEndOffset)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -436,15 +382,11 @@ ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset,
return textAcc->SetSelectionBoundsAt(aSelectionIndex, aStartOffset, aEndOffset) ?
S_OK : E_INVALIDARG;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_nCharacters(long* aNCharacters)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNCharacters)
return E_INVALIDARG;
*aNCharacters = 0;
@@ -457,16 +399,12 @@ ia2AccessibleText::get_nCharacters(long* aNCharacters)
*aNCharacters = textAcc->CharacterCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex,
enum IA2ScrollType aScrollType)
{
- A11Y_TRYBLOCK_BEGIN
-
MOZ_ASSERT(!HyperTextProxyFor(this));
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
@@ -478,8 +416,6 @@ ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex,
textAcc->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -487,8 +423,6 @@ ia2AccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex,
enum IA2CoordinateType aCoordType,
long aX, long aY)
{
- A11Y_TRYBLOCK_BEGIN
-
uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
@@ -505,28 +439,18 @@ ia2AccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex,
textAcc->ScrollSubstringToPoint(aStartIndex, aEndIndex,
geckoCoordType, aX, aY);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_newText(IA2TextSegment *aNewText)
{
- A11Y_TRYBLOCK_BEGIN
-
return GetModifiedText(true, aNewText);
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleText::get_oldText(IA2TextSegment *aOldText)
{
- A11Y_TRYBLOCK_BEGIN
-
return GetModifiedText(false, aOldText);
-
- A11Y_TRYBLOCK_END
}
// ia2AccessibleText
diff --git a/accessible/windows/ia2/ia2AccessibleValue.cpp b/accessible/windows/ia2/ia2AccessibleValue.cpp
index e33442295..44d2c31fb 100644
--- a/accessible/windows/ia2/ia2AccessibleValue.cpp
+++ b/accessible/windows/ia2/ia2AccessibleValue.cpp
@@ -46,8 +46,6 @@ ia2AccessibleValue::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ia2AccessibleValue::get_currentValue(VARIANT* aCurrentValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCurrentValue)
return E_INVALIDARG;
@@ -68,15 +66,11 @@ ia2AccessibleValue::get_currentValue(VARIANT* aCurrentValue)
aCurrentValue->vt = VT_R8;
aCurrentValue->dblVal = currentValue;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleValue::setCurrentValue(VARIANT aValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (aValue.vt != VT_R8)
return E_INVALIDARG;
@@ -87,15 +81,11 @@ ia2AccessibleValue::setCurrentValue(VARIANT aValue)
return CO_E_OBJNOTCONNECTED;
return valueAcc->SetCurValue(aValue.dblVal) ? S_OK : E_FAIL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleValue::get_maximumValue(VARIANT* aMaximumValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aMaximumValue)
return E_INVALIDARG;
@@ -116,15 +106,11 @@ ia2AccessibleValue::get_maximumValue(VARIANT* aMaximumValue)
aMaximumValue->vt = VT_R8;
aMaximumValue->dblVal = maximumValue;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleValue::get_minimumValue(VARIANT* aMinimumValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aMinimumValue)
return E_INVALIDARG;
@@ -145,7 +131,5 @@ ia2AccessibleValue::get_minimumValue(VARIANT* aMinimumValue)
aMinimumValue->vt = VT_R8;
aMinimumValue->dblVal = minimumValue;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp
index 6112c370a..426270814 100644
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -110,8 +110,6 @@ AccessibleWrap::Shutdown()
STDMETHODIMP
AccessibleWrap::QueryInterface(REFIID iid, void** ppv)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!ppv)
return E_INVALIDARG;
@@ -165,8 +163,6 @@ AccessibleWrap::QueryInterface(REFIID iid, void** ppv)
(reinterpret_cast<IUnknown*>(*ppv))->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
//-----------------------------------------------------
@@ -176,8 +172,6 @@ AccessibleWrap::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!ppdispParent)
return E_INVALIDARG;
@@ -208,15 +202,11 @@ AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
*ppdispParent = NativeAccessible(xpParentAcc);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pcountChildren)
return E_INVALIDARG;
@@ -230,8 +220,6 @@ AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
*pcountChildren = ChildCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -239,8 +227,6 @@ AccessibleWrap::get_accChild(
/* [in] */ VARIANT varChild,
/* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!ppdispChild)
return E_INVALIDARG;
@@ -265,8 +251,6 @@ AccessibleWrap::get_accChild(
child.forget(ppdispChild);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
/**
@@ -331,8 +315,6 @@ AccessibleWrap::get_accName(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszName)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszName || varChild.vt != VT_I4)
return E_INVALIDARG;
@@ -361,8 +343,6 @@ AccessibleWrap::get_accName(
if (!*pszName)
return E_OUTOFMEMORY;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
@@ -371,8 +351,6 @@ AccessibleWrap::get_accValue(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszValue)
return E_INVALIDARG;
@@ -401,16 +379,12 @@ AccessibleWrap::get_accValue(
if (!*pszValue)
return E_OUTOFMEMORY;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleWrap::get_accDescription(VARIANT varChild,
BSTR __RPC_FAR *pszDescription)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszDescription)
return E_INVALIDARG;
@@ -432,8 +406,6 @@ AccessibleWrap::get_accDescription(VARIANT varChild,
*pszDescription = ::SysAllocStringLen(description.get(),
description.Length());
return *pszDescription ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -441,8 +413,6 @@ AccessibleWrap::get_accRole(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pvarRole)
return E_INVALIDARG;
@@ -532,8 +502,6 @@ AccessibleWrap::get_accRole(
}
return E_FAIL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -541,8 +509,6 @@ AccessibleWrap::get_accState(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ VARIANT __RPC_FAR *pvarState)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pvarState)
return E_INVALIDARG;
@@ -574,8 +540,6 @@ AccessibleWrap::get_accState(
nsAccUtils::To32States(state, &msaaState, nullptr);
pvarState->lVal = msaaState;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
@@ -584,15 +548,11 @@ AccessibleWrap::get_accHelp(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszHelp)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszHelp)
return E_INVALIDARG;
*pszHelp = nullptr;
return S_FALSE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -601,16 +561,12 @@ AccessibleWrap::get_accHelpTopic(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ long __RPC_FAR *pidTopic)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszHelpFile || !pidTopic)
return E_INVALIDARG;
*pszHelpFile = nullptr;
*pidTopic = 0;
return S_FALSE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -618,8 +574,6 @@ AccessibleWrap::get_accKeyboardShortcut(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszKeyboardShortcut)
return E_INVALIDARG;
*pszKeyboardShortcut = nullptr;
@@ -645,16 +599,12 @@ AccessibleWrap::get_accKeyboardShortcut(
*pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
shortcut.Length());
return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleWrap::get_accFocus(
/* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pvarChild)
return E_INVALIDARG;
@@ -685,8 +635,6 @@ AccessibleWrap::get_accFocus(
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
/**
@@ -723,8 +671,6 @@ private:
STDMETHODIMP
AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
{
- A11Y_TRYBLOCK_BEGIN
-
if (iid == IID_IEnumVARIANT) {
*ppvObject = static_cast<IEnumVARIANT*>(this);
AddRef();
@@ -738,15 +684,11 @@ AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
*ppvObject = nullptr;
return E_NOINTERFACE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched)
{
- A11Y_TRYBLOCK_BEGIN
-
uint32_t length = mArray.Length();
HRESULT hr = S_OK;
@@ -766,29 +708,21 @@ AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long
*pceltFetched = celt;
return hr;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum)
{
- A11Y_TRYBLOCK_BEGIN
-
*ppenum = new AccessibleEnumerator(*this);
if (!*ppenum)
return E_OUTOFMEMORY;
NS_ADDREF(*ppenum);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleEnumerator::Skip(unsigned long celt)
{
- A11Y_TRYBLOCK_BEGIN
-
uint32_t length = mArray.Length();
// Check if we can skip the requested number of elements
if (celt > length - mCurIndex) {
@@ -797,8 +731,6 @@ AccessibleEnumerator::Skip(unsigned long celt)
}
mCurIndex += celt;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
/**
@@ -821,8 +753,6 @@ AccessibleEnumerator::Skip(unsigned long celt)
STDMETHODIMP
AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pvarChildren)
return E_INVALIDARG;
@@ -842,8 +772,6 @@ AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
NS_ADDREF(pvarChildren->punkVal = pEnum);
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -851,8 +779,6 @@ AccessibleWrap::get_accDefaultAction(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pszDefaultAction)
return E_INVALIDARG;
@@ -874,8 +800,6 @@ AccessibleWrap::get_accDefaultAction(
*pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
defaultAction.Length());
return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -883,8 +807,6 @@ AccessibleWrap::accSelect(
/* [in] */ long flagsSelect,
/* [optional][in] */ VARIANT varChild)
{
- A11Y_TRYBLOCK_BEGIN
-
RefPtr<IAccessible> accessible;
HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
if (FAILED(hr)) {
@@ -926,8 +848,6 @@ AccessibleWrap::accSelect(
}
return E_FAIL;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -938,8 +858,6 @@ AccessibleWrap::accLocation(
/* [out] */ long __RPC_FAR *pcyHeight,
/* [optional][in] */ VARIANT varChild)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)
return E_INVALIDARG;
@@ -966,8 +884,6 @@ AccessibleWrap::accLocation(
*pcxWidth = rect.width;
*pcyHeight = rect.height;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -976,8 +892,6 @@ AccessibleWrap::accNavigate(
/* [optional][in] */ VARIANT varStart,
/* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pvarEndUpAt)
return E_INVALIDARG;
@@ -1060,8 +974,6 @@ AccessibleWrap::accNavigate(
pvarEndUpAt->pdispVal = NativeAccessible(navAccessible);
pvarEndUpAt->vt = VT_DISPATCH;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -1070,8 +982,6 @@ AccessibleWrap::accHitTest(
/* [in] */ long yTop,
/* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!pvarChild)
return E_INVALIDARG;
@@ -1098,16 +1008,12 @@ AccessibleWrap::accHitTest(
return S_FALSE;
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
AccessibleWrap::accDoDefaultAction(
/* [optional][in] */ VARIANT varChild)
{
- A11Y_TRYBLOCK_BEGIN
-
RefPtr<IAccessible> accessible;
HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
if (FAILED(hr)) {
@@ -1119,8 +1025,6 @@ AccessibleWrap::accDoDefaultAction(
}
return DoAction(0) ? S_OK : E_INVALIDARG;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
diff --git a/accessible/windows/msaa/ApplicationAccessibleWrap.cpp b/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
index b78a8dd55..ec9d511e4 100644
--- a/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
+++ b/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
@@ -69,8 +69,6 @@ ApplicationAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
STDMETHODIMP
ApplicationAccessibleWrap::get_appName(BSTR* aName)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aName)
return E_INVALIDARG;
@@ -86,15 +84,11 @@ ApplicationAccessibleWrap::get_appName(BSTR* aName)
*aName = ::SysAllocStringLen(name.get(), name.Length());
return *aName ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ApplicationAccessibleWrap::get_appVersion(BSTR* aVersion)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aVersion)
return E_INVALIDARG;
@@ -110,15 +104,11 @@ ApplicationAccessibleWrap::get_appVersion(BSTR* aVersion)
*aVersion = ::SysAllocStringLen(version.get(), version.Length());
return *aVersion ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ApplicationAccessibleWrap::get_toolkitName(BSTR* aName)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aName)
return E_INVALIDARG;
@@ -132,15 +122,11 @@ ApplicationAccessibleWrap::get_toolkitName(BSTR* aName)
*aName = ::SysAllocStringLen(name.get(), name.Length());
return *aName ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ApplicationAccessibleWrap::get_toolkitVersion(BSTR* aVersion)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aVersion)
return E_INVALIDARG;
@@ -156,7 +142,5 @@ ApplicationAccessibleWrap::get_toolkitVersion(BSTR* aVersion)
*aVersion = ::SysAllocStringLen(version.get(), version.Length());
return *aVersion ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/msaa/DocAccessibleWrap.cpp b/accessible/windows/msaa/DocAccessibleWrap.cpp
index 6fb89816d..895fe9192 100644
--- a/accessible/windows/msaa/DocAccessibleWrap.cpp
+++ b/accessible/windows/msaa/DocAccessibleWrap.cpp
@@ -65,8 +65,6 @@ DocAccessibleWrap::get_accParent(
STDMETHODIMP
DocAccessibleWrap::get_accValue(VARIANT aVarChild, BSTR __RPC_FAR* aValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aValue)
return E_INVALIDARG;
*aValue = nullptr;
@@ -90,8 +88,6 @@ DocAccessibleWrap::get_accValue(VARIANT aVarChild, BSTR __RPC_FAR* aValue)
*aValue = ::SysAllocStringLen(url.get(), url.Length());
return *aValue ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/accessible/windows/msaa/EnumVariant.cpp b/accessible/windows/msaa/EnumVariant.cpp
index 86c81a105..2344208a3 100644
--- a/accessible/windows/msaa/EnumVariant.cpp
+++ b/accessible/windows/msaa/EnumVariant.cpp
@@ -21,8 +21,6 @@ STDMETHODIMP
ChildrenEnumVariant::Next(ULONG aCount, VARIANT FAR* aItems,
ULONG FAR* aCountFetched)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aItems || !aCountFetched)
return E_INVALIDARG;
@@ -55,15 +53,11 @@ ChildrenEnumVariant::Next(ULONG aCount, VARIANT FAR* aItems,
(*aCountFetched) = countFetched;
return countFetched < aCount ? S_FALSE : S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ChildrenEnumVariant::Skip(ULONG aCount)
{
- A11Y_TRYBLOCK_BEGIN
-
if (mAnchorAcc->IsDefunct() || mAnchorAcc->GetChildAt(mCurIndex) != mCurAcc)
return CO_E_OBJNOTCONNECTED;
@@ -71,15 +65,11 @@ ChildrenEnumVariant::Skip(ULONG aCount)
mCurAcc = mAnchorAcc->GetChildAt(mCurIndex);
return mCurAcc ? S_OK : S_FALSE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ChildrenEnumVariant::Reset()
{
- A11Y_TRYBLOCK_BEGIN
-
if (mAnchorAcc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@@ -87,15 +77,11 @@ ChildrenEnumVariant::Reset()
mCurAcc = mAnchorAcc->GetChildAt(0);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
ChildrenEnumVariant::Clone(IEnumVARIANT** aEnumVariant)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aEnumVariant)
return E_INVALIDARG;
@@ -103,6 +89,4 @@ ChildrenEnumVariant::Clone(IEnumVARIANT** aEnumVariant)
(*aEnumVariant)->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/msaa/IUnknownImpl.cpp b/accessible/windows/msaa/IUnknownImpl.cpp
index 4a9fa5383..c74f86e33 100644
--- a/accessible/windows/msaa/IUnknownImpl.cpp
+++ b/accessible/windows/msaa/IUnknownImpl.cpp
@@ -37,22 +37,5 @@ GetHRESULT(nsresult aResult)
}
}
-int
-FilterExceptions(unsigned int aCode, EXCEPTION_POINTERS* aExceptionInfo)
-{
- if (aCode == EXCEPTION_ACCESS_VIOLATION) {
-#ifdef MOZ_CRASHREPORTER
- // MSAA swallows crashes (because it is COM-based) but we still need to
- // learn about those crashes so we can fix them. Make sure to pass them to
- // the crash reporter.
- CrashReporter::WriteMinidumpForException(aExceptionInfo);
-#endif
- } else {
- NS_NOTREACHED("We should only be catching crash exceptions");
- }
-
- return EXCEPTION_CONTINUE_SEARCH;
-}
-
} // namespace a11y
} // namespace mozilla
diff --git a/accessible/windows/msaa/IUnknownImpl.h b/accessible/windows/msaa/IUnknownImpl.h
index dbf6c1374..d939a4dfa 100644
--- a/accessible/windows/msaa/IUnknownImpl.h
+++ b/accessible/windows/msaa/IUnknownImpl.h
@@ -79,7 +79,6 @@ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**); \
STDMETHODIMP \
Class::QueryInterface(REFIID aIID, void** aInstancePtr) \
{ \
- A11Y_TRYBLOCK_BEGIN \
if (!aInstancePtr) \
return E_INVALIDARG; \
*aInstancePtr = nullptr; \
@@ -88,17 +87,14 @@ Class::QueryInterface(REFIID aIID, void** aInstancePtr) \
#define IMPL_IUNKNOWN_QUERY_TAIL \
return hr; \
- A11Y_TRYBLOCK_END \
}
#define IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(Member) \
return Member->QueryInterface(aIID, aInstancePtr); \
- A11Y_TRYBLOCK_END \
}
#define IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(BaseClass) \
return BaseClass::QueryInterface(aIID, aInstancePtr); \
- A11Y_TRYBLOCK_END \
}
#define IMPL_IUNKNOWN_QUERY_IFACE(Iface) \
@@ -158,21 +154,6 @@ Class::QueryInterface(REFIID aIID, void** aInstancePtr) \
IMPL_IUNKNOWN_QUERY_CLASS(Super2); \
IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(Super0)
-
-/**
- * Wrap every method body by these macroses to pass exception to the crash
- * reporter.
- */
-#define A11Y_TRYBLOCK_BEGIN \
- MOZ_SEH_TRY {
-
-#define A11Y_TRYBLOCK_END \
- } MOZ_SEH_EXCEPT(mozilla::a11y::FilterExceptions(::GetExceptionCode(), \
- GetExceptionInformation())) \
- { } \
- return E_FAIL;
-
-
namespace mozilla {
namespace a11y {
@@ -181,11 +162,6 @@ namespace a11y {
*/
HRESULT GetHRESULT(nsresult aResult);
-/**
- * Used to pass an exception to the crash reporter.
- */
-int FilterExceptions(unsigned int aCode, EXCEPTION_POINTERS* aExceptionInfo);
-
} // namespace a11y;
} //namespace mozilla;
diff --git a/accessible/windows/sdn/sdnAccessible.cpp b/accessible/windows/sdn/sdnAccessible.cpp
index 909b0779c..d90631c8f 100644
--- a/accessible/windows/sdn/sdnAccessible.cpp
+++ b/accessible/windows/sdn/sdnAccessible.cpp
@@ -28,8 +28,6 @@ using namespace mozilla::a11y;
STDMETHODIMP
sdnAccessible::QueryInterface(REFIID aREFIID, void** aInstancePtr)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aInstancePtr)
return E_FAIL;
*aInstancePtr = nullptr;
@@ -53,8 +51,6 @@ sdnAccessible::QueryInterface(REFIID aREFIID, void** aInstancePtr)
}
return E_NOINTERFACE;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -65,8 +61,6 @@ sdnAccessible::get_nodeInfo(BSTR __RPC_FAR* aNodeName,
unsigned int __RPC_FAR* aUniqueID,
unsigned short __RPC_FAR* aNodeType)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNodeName || !aNameSpaceID || !aNodeValue || !aNumChildren ||
!aUniqueID || !aNodeType)
return E_INVALIDARG;
@@ -114,8 +108,6 @@ sdnAccessible::get_nodeInfo(BSTR __RPC_FAR* aNodeName,
*aNumChildren = mNode->GetChildCount();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -125,8 +117,6 @@ sdnAccessible::get_attributes(unsigned short aMaxAttribs,
BSTR __RPC_FAR* aAttribValues,
unsigned short __RPC_FAR* aNumAttribs)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAttribNames || !aNameSpaceIDs || !aAttribValues || !aNumAttribs)
return E_INVALIDARG;
@@ -159,8 +149,6 @@ sdnAccessible::get_attributes(unsigned short aMaxAttribs,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -169,8 +157,6 @@ sdnAccessible::get_attributesForNames(unsigned short aMaxAttribs,
short __RPC_FAR* aNameSpaceID,
BSTR __RPC_FAR* aAttribValues)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAttribNames || !aNameSpaceID || !aAttribValues)
return E_INVALIDARG;
@@ -207,8 +193,6 @@ sdnAccessible::get_attributesForNames(unsigned short aMaxAttribs,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -218,8 +202,6 @@ sdnAccessible::get_computedStyle(unsigned short aMaxStyleProperties,
BSTR __RPC_FAR* aStyleValues,
unsigned short __RPC_FAR* aNumStyleProperties)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStyleProperties || aStyleValues || !aNumStyleProperties)
return E_INVALIDARG;
@@ -257,8 +239,6 @@ sdnAccessible::get_computedStyle(unsigned short aMaxStyleProperties,
*aNumStyleProperties = static_cast<unsigned short>(realIndex);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -267,8 +247,6 @@ sdnAccessible::get_computedStyleForProperties(unsigned short aNumStyleProperties
BSTR __RPC_FAR* aStyleProperties,
BSTR __RPC_FAR* aStyleValues)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aStyleProperties || !aStyleValues)
return E_INVALIDARG;
@@ -291,15 +269,11 @@ sdnAccessible::get_computedStyleForProperties(unsigned short aNumStyleProperties
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::scrollTo(boolean aScrollTopLeft)
{
- A11Y_TRYBLOCK_BEGIN
-
DocAccessible* document = GetDocument();
if (!document) // that's IsDefunct check
return CO_E_OBJNOTCONNECTED;
@@ -313,15 +287,11 @@ sdnAccessible::scrollTo(boolean aScrollTopLeft)
nsCoreUtils::ScrollTo(document->PresShell(), mNode->AsContent(), scrollType);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_parentNode(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNode)
return E_INVALIDARG;
*aNode = nullptr;
@@ -336,15 +306,11 @@ sdnAccessible::get_parentNode(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_firstChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNode)
return E_INVALIDARG;
*aNode = nullptr;
@@ -359,15 +325,11 @@ sdnAccessible::get_firstChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_lastChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNode)
return E_INVALIDARG;
*aNode = nullptr;
@@ -382,15 +344,11 @@ sdnAccessible::get_lastChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_previousSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNode)
return E_INVALIDARG;
*aNode = nullptr;
@@ -405,15 +363,11 @@ sdnAccessible::get_previousSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_nextSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNode)
return E_INVALIDARG;
*aNode = nullptr;
@@ -428,16 +382,12 @@ sdnAccessible::get_nextSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_childAt(unsigned aChildIndex,
ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNode)
return E_INVALIDARG;
*aNode = nullptr;
@@ -453,15 +403,11 @@ sdnAccessible::get_childAt(unsigned aChildIndex,
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_innerHTML(BSTR __RPC_FAR* aInnerHTML)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aInnerHTML)
return E_INVALIDARG;
*aInnerHTML = nullptr;
@@ -482,15 +428,11 @@ sdnAccessible::get_innerHTML(BSTR __RPC_FAR* aInnerHTML)
return E_OUTOFMEMORY;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_localInterface(void __RPC_FAR *__RPC_FAR* aLocalInterface)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLocalInterface)
return E_INVALIDARG;
*aLocalInterface = nullptr;
@@ -502,15 +444,11 @@ sdnAccessible::get_localInterface(void __RPC_FAR *__RPC_FAR* aLocalInterface)
AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnAccessible::get_language(BSTR __RPC_FAR* aLanguage)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aLanguage)
return E_INVALIDARG;
*aLanguage = nullptr;
@@ -534,6 +472,4 @@ sdnAccessible::get_language(BSTR __RPC_FAR* aLanguage)
return E_OUTOFMEMORY;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/sdn/sdnDocAccessible.cpp b/accessible/windows/sdn/sdnDocAccessible.cpp
index 07b39e66f..a1c5be872 100644
--- a/accessible/windows/sdn/sdnDocAccessible.cpp
+++ b/accessible/windows/sdn/sdnDocAccessible.cpp
@@ -24,8 +24,6 @@ IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAccessible)
STDMETHODIMP
sdnDocAccessible::get_URL(BSTR __RPC_FAR* aURL)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aURL)
return E_INVALIDARG;
*aURL = nullptr;
@@ -40,15 +38,11 @@ sdnDocAccessible::get_URL(BSTR __RPC_FAR* aURL)
*aURL = ::SysAllocStringLen(URL.get(), URL.Length());
return *aURL ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnDocAccessible::get_title(BSTR __RPC_FAR* aTitle)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aTitle)
return E_INVALIDARG;
*aTitle = nullptr;
@@ -60,15 +54,11 @@ sdnDocAccessible::get_title(BSTR __RPC_FAR* aTitle)
mAccessible->Title(title);
*aTitle = ::SysAllocStringLen(title.get(), title.Length());
return *aTitle ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnDocAccessible::get_mimeType(BSTR __RPC_FAR* aMimeType)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aMimeType)
return E_INVALIDARG;
*aMimeType = nullptr;
@@ -83,15 +73,11 @@ sdnDocAccessible::get_mimeType(BSTR __RPC_FAR* aMimeType)
*aMimeType = ::SysAllocStringLen(mimeType.get(), mimeType.Length());
return *aMimeType ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnDocAccessible::get_docType(BSTR __RPC_FAR* aDocType)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aDocType)
return E_INVALIDARG;
*aDocType = nullptr;
@@ -106,16 +92,12 @@ sdnDocAccessible::get_docType(BSTR __RPC_FAR* aDocType)
*aDocType = ::SysAllocStringLen(docType.get(), docType.Length());
return *aDocType ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnDocAccessible::get_nameSpaceURIForID(short aNameSpaceID,
BSTR __RPC_FAR* aNameSpaceURI)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aNameSpaceURI)
return E_INVALIDARG;
*aNameSpaceURI = nullptr;
@@ -138,20 +120,14 @@ sdnDocAccessible::get_nameSpaceURIForID(short aNameSpaceID,
nameSpaceURI.Length());
return *aNameSpaceURI ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnDocAccessible::put_alternateViewMediaTypes(BSTR __RPC_FAR* aCommaSeparatedMediaTypes)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aCommaSeparatedMediaTypes)
return E_INVALIDARG;
*aCommaSeparatedMediaTypes = nullptr;
return mAccessible->IsDefunct() ? CO_E_OBJNOTCONNECTED : E_NOTIMPL;
-
- A11Y_TRYBLOCK_END
}
diff --git a/accessible/windows/sdn/sdnTextAccessible.cpp b/accessible/windows/sdn/sdnTextAccessible.cpp
index b51caf44e..b6f580ba3 100644
--- a/accessible/windows/sdn/sdnTextAccessible.cpp
+++ b/accessible/windows/sdn/sdnTextAccessible.cpp
@@ -33,8 +33,6 @@ IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAccessible)
STDMETHODIMP
sdnTextAccessible::get_domText(BSTR __RPC_FAR* aText)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aText)
return E_INVALIDARG;
*aText = nullptr;
@@ -51,8 +49,6 @@ sdnTextAccessible::get_domText(BSTR __RPC_FAR* aText)
*aText = ::SysAllocStringLen(nodeValue.get(), nodeValue.Length());
return *aText ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -63,8 +59,6 @@ sdnTextAccessible::get_clippedSubstringBounds(unsigned int aStartIndex,
int __RPC_FAR* aWidth,
int __RPC_FAR* aHeight)
{
- A11Y_TRYBLOCK_BEGIN
-
nscoord x = 0, y = 0, width = 0, height = 0;
HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex,
&x, &y, &width, &height);
@@ -86,8 +80,6 @@ sdnTextAccessible::get_clippedSubstringBounds(unsigned int aStartIndex,
*aWidth = clippedRect.width;
*aHeight = clippedRect.height;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
@@ -98,8 +90,6 @@ sdnTextAccessible::get_unclippedSubstringBounds(unsigned int aStartIndex,
int __RPC_FAR* aWidth,
int __RPC_FAR* aHeight)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aX || !aY || !aWidth || !aHeight)
return E_INVALIDARG;
*aX = *aY = *aWidth = *aHeight = 0;
@@ -135,16 +125,12 @@ sdnTextAccessible::get_unclippedSubstringBounds(unsigned int aStartIndex,
*aHeight = presContext->AppUnitsToDevPixels(sum.height);
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnTextAccessible::scrollToSubstring(unsigned int aStartIndex,
unsigned int aEndIndex)
{
- A11Y_TRYBLOCK_BEGIN
-
if (mAccessible->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@@ -159,15 +145,11 @@ sdnTextAccessible::scrollToSubstring(unsigned int aStartIndex,
nsCoreUtils::ScrollSubstringTo(mAccessible->GetFrame(), range,
nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE);
return GetHRESULT(rv);
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
sdnTextAccessible::get_fontFamily(BSTR __RPC_FAR* aFontFamily)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aFontFamily)
return E_INVALIDARG;
*aFontFamily = nullptr;
@@ -189,8 +171,6 @@ sdnTextAccessible::get_fontFamily(BSTR __RPC_FAR* aFontFamily)
*aFontFamily = ::SysAllocStringLen(name.get(), name.Length());
return *aFontFamily ? S_OK : E_OUTOFMEMORY;
-
- A11Y_TRYBLOCK_END
}
nsIFrame*
diff --git a/accessible/windows/uia/uiaRawElmProvider.cpp b/accessible/windows/uia/uiaRawElmProvider.cpp
index 54e54766d..3121661e1 100644
--- a/accessible/windows/uia/uiaRawElmProvider.cpp
+++ b/accessible/windows/uia/uiaRawElmProvider.cpp
@@ -28,24 +28,18 @@ STDMETHODIMP
uiaRawElmProvider::GetObjectForChild(long aIdChild,
__RPC__deref_out_opt IAccessibleEx** aAccEx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAccEx)
return E_INVALIDARG;
*aAccEx = nullptr;
return mAcc->IsDefunct() ? CO_E_OBJNOTCONNECTED : S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
uiaRawElmProvider::GetIAccessiblePair(__RPC__deref_out_opt IAccessible** aAcc,
__RPC__out long* aIdChild)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aAcc || !aIdChild)
return E_INVALIDARG;
@@ -60,15 +54,11 @@ uiaRawElmProvider::GetIAccessiblePair(__RPC__deref_out_opt IAccessible** aAcc,
mAcc->AddRef();
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
uiaRawElmProvider::GetRuntimeId(__RPC__deref_out_opt SAFEARRAY** aRuntimeIds)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRuntimeIds)
return E_INVALIDARG;
@@ -81,16 +71,12 @@ uiaRawElmProvider::GetRuntimeId(__RPC__deref_out_opt SAFEARRAY** aRuntimeIds)
SafeArrayPutElement(*aRuntimeIds, &i, (void*)&(ids[i]));
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
uiaRawElmProvider::ConvertReturnedElement(__RPC__in_opt IRawElementProviderSimple* aRawElmProvider,
__RPC__deref_out_opt IAccessibleEx** aAccEx)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRawElmProvider || !aAccEx)
return E_INVALIDARG;
@@ -102,8 +88,6 @@ uiaRawElmProvider::ConvertReturnedElement(__RPC__in_opt IRawElementProviderSimpl
*aAccEx = static_cast<IAccessibleEx*>(instancePtr);
return hr;
-
- A11Y_TRYBLOCK_END
}
////////////////////////////////////////////////////////////////////////////////
@@ -112,39 +96,29 @@ uiaRawElmProvider::ConvertReturnedElement(__RPC__in_opt IRawElementProviderSimpl
STDMETHODIMP
uiaRawElmProvider::get_ProviderOptions(__RPC__out enum ProviderOptions* aOptions)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aOptions)
return E_INVALIDARG;
// This method is not used with IAccessibleEx implementations.
*aOptions = ProviderOptions_ServerSideProvider;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
uiaRawElmProvider::GetPatternProvider(PATTERNID aPatternId,
__RPC__deref_out_opt IUnknown** aPatternProvider)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aPatternProvider)
return E_INVALIDARG;
*aPatternProvider = nullptr;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
__RPC__out VARIANT* aPropertyValue)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aPropertyValue)
return E_INVALIDARG;
@@ -226,21 +200,15 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
}
return S_OK;
-
- A11Y_TRYBLOCK_END
}
STDMETHODIMP
uiaRawElmProvider::get_HostRawElementProvider(__RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider)
{
- A11Y_TRYBLOCK_BEGIN
-
if (!aRawElmProvider)
return E_INVALIDARG;
// This method is not used with IAccessibleEx implementations.
*aRawElmProvider = nullptr;
return S_OK;
-
- A11Y_TRYBLOCK_END
}
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 465b5349f..b46d944e8 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -900,12 +900,7 @@ pref("app.support.baseURL", "https://support.mozilla.org/1/firefox/%VERSION%/%OS
pref("app.support.e10sAccessibilityUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/accessibility-ppt");
// base url for web-based feedback pages
-#ifdef MOZ_DEV_EDITION
-pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/firefoxdev/%VERSION%/");
-#else
-pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/%APP%/%VERSION%/");
-#endif
-
+pref("app.feedback.baseURL", "https://forum.palemoon.org/viewforum.php?f=61");
// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
pref("security.alternate_certificate_error_page", "certerror");
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
index b024d2d52..f9571621f 100644
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -40,16 +40,23 @@ function init(aEvent)
// Pref is unset
}
- // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
+ // Include the build ID
let versionField = document.getElementById("version");
let version = Services.appinfo.version;
+ let buildID = Services.appinfo.appBuildID;
+ let year = buildID.slice(0, 4);
+ let month = buildID.slice(4, 6);
+ let day = buildID.slice(6, 8);
+ let hour = buildID.slice(8, 10);
+ let minute = buildID.slice(10, 12);
+ if (Services.prefs.getBoolPref("general.useragent.appVersionIsBuildID")) {
+ versionField.textContent = `${year}.${month}.${day}`;
+ } else {
+ versionField.textContent = `v` + version + ` (${year}-${month}-${day})`;
+ }
+
+ // Display warning if this is an "a#" (nightly or aurora) build
if (/a\d+$/.test(version)) {
- let buildID = Services.appinfo.appBuildID;
- let year = buildID.slice(0, 4);
- let month = buildID.slice(4, 6);
- let day = buildID.slice(6, 8);
- versionField.textContent += ` (${year}-${month}-${day})`;
-
document.getElementById("experimental").hidden = false;
document.getElementById("communityDesc").hidden = true;
}
diff --git a/browser/base/content/aboutDialog.xul b/browser/base/content/aboutDialog.xul
index ef2804f31..f64e79681 100644
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -45,7 +45,7 @@
<vbox id="leftBox" flex="1"/>
<vbox id="rightBox" flex="1">
<hbox align="baseline">
-#expand <label id="version">__MOZ_APP_VERSION_DISPLAY__</label>
+#expand <label id="version"></label>
#ifndef NIGHTLY_BUILD
<label id="releasenotes" class="text-link">&releaseNotes.link;</label>
#endif
@@ -120,19 +120,13 @@
<vbox id="experimental" hidden="true">
<description class="text-blurb" id="warningDesc">
&warningDesc.version;
-#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
- &warningDesc.telemetryDesc;
-#endif
- </description>
- <description class="text-blurb" id="communityExperimentalDesc">
- &community.exp.start;<label class="text-link" href="http://www.mozilla.org/">&community.exp.mozillaLink;</label>&community.exp.middle;<label class="text-link" useoriginprincipal="true" href="about:credits">&community.exp.creditsLink;</label>&community.exp.end;
</description>
</vbox>
<description class="text-blurb" id="communityDesc">
- &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" useoriginprincipal="true" href="about:credits">&community.creditsLink;</label>&community.end3;
+ Basilisk is community software released by <label class="text-link" href="http://www.palemoon.org/">the Pale Moon team</label> and Mozilla developers. Learn <label class="text-link" useoriginprincipal="true" href="about:credits">who contributed</label> to this software.
</description>
<description class="text-blurb" id="contributeDesc">
- &helpus.start;<label class="text-link" href="https://sendto.mozilla.org/page/contribute/Give-Now?source=mozillaorg_default_footer&#38;ref=firefox_about&#38;utm_campaign=firefox_about&#38;tm_source=firefox&#38;tm_medium=referral&#38;utm_content=20140929_FireFoxAbout">&helpus.donateLink;</label>&helpus.middle;<label class="text-link" href="http://www.mozilla.org/contribute/">&helpus.getInvolvedLink;</label>&helpus.end;
+ Want to help? Please consider <label class="text-link" href="https://www.palemoon.org/donations.shtml">donating</label> or get involved with our <label class="text-link" href="https://github.com/MoonchildProductions/moebius">development</label> of the Unified XUL Platform.
</description>
</vbox>
</vbox>
@@ -141,7 +135,7 @@
<hbox pack="center">
<label class="text-link bottom-link" useoriginprincipal="true" href="about:license">&bottomLinks.license;</label>
<label class="text-link bottom-link" useoriginprincipal="true" href="about:rights">&bottomLinks.rights;</label>
- <label class="text-link bottom-link" href="https://www.mozilla.org/privacy/">&bottomLinks.privacy;</label>
+ <label class="text-link bottom-link" href="https://www.palemoon.org/privacy.shtml">&bottomLinks.privacy;</label>
</hbox>
<description id="trademark">&trademarkInfo.part1;</description>
</vbox>
diff --git a/browser/base/content/browser-media.js b/browser/base/content/browser-media.js
index 81e7faf17..a013dbd8a 100644
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -201,29 +201,16 @@ let gDecoderDoctorHandler = {
getLabelForNotificationBox(type) {
if (type == "adobe-cdm-not-found" &&
AppConstants.platform == "win") {
- if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
- // We supply our own Learn More button so we don't need to populate the message here.
- return gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]);
- }
return gNavigatorBundle.getString("decoder.noCodecs.message");
}
if (type == "adobe-cdm-not-activated" &&
AppConstants.platform == "win") {
- if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
- return gNavigatorBundle.getString("decoder.noCodecsXP.message");
- }
- if (!AppConstants.isPlatformAndVersionAtLeast("win", "6.1")) {
- return gNavigatorBundle.getString("decoder.noCodecsVista.message");
- }
return gNavigatorBundle.getString("decoder.noCodecs.message");
}
if (type == "platform-decoder-not-found") {
- if (AppConstants.isPlatformAndVersionAtLeast("win", "6.1")) {
+ if (AppConstants.platform == "win") {
return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
}
- if (AppConstants.isPlatformAndVersionAtLeast("win", "6")) {
- return gNavigatorBundle.getString("decoder.noHWAccelerationVista.message");
- }
if (AppConstants.platform == "linux") {
return gNavigatorBundle.getString("decoder.noCodecsLinux.message");
}
diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc
index e952bc3ca..3fc098755 100644
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -179,15 +179,6 @@
label="&bidiSwitchTextDirectionItem.label;"
accesskey="&bidiSwitchTextDirectionItem.accesskey;"
hidden="true"/>
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
- <menuseparator/>
- <menuitem id="menu_preferences"
- label="&preferencesCmdUnix.label;"
- accesskey="&preferencesCmdUnix.accesskey;"
- oncommand="openPreferences();"/>
-#endif
-#endif
</menupopup>
</menu>
@@ -521,13 +512,11 @@
<menupopup id="menu_mirrorTab-popup"
onpopupshowing="populateMirrorTabMenu(this)"/>
</menu>
-#ifndef XP_UNIX
<menuseparator id="prefSep"/>
<menuitem id="menu_preferences"
label="&preferencesCmd2.label;"
accesskey="&preferencesCmd2.accesskey;"
oncommand="openPreferences();"/>
-#endif
</menupopup>
</menu>
diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js
index 14e90cde2..de880d11d 100644
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1555,6 +1555,10 @@ var BookmarkingUI = {
// so kill current view and let popupshowing generate a new one.
if (this.button._placesView)
this.button._placesView.uninit();
+ // ...and do the same for the menu bar.
+ let menubar = document.getElementById("bookmarksMenu");
+ if (menubar._placesview)
+ menubar._placesview.uninit();
// We have to do the same thing for the "special" views underneath the
// the bookmarks menu.
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index d813a55cc..910dc4f68 100755
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -97,11 +97,6 @@ XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
return new scope.CustomizeMode(window);
});
-XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
- // Only show resizers on Windows 2000 and XP
- return AppConstants.isPlatformAndVersionAtMost("win", "5.9");
-});
-
XPCOMUtils.defineLazyGetter(this, "gPrefService", function() {
return Services.prefs;
});
@@ -473,9 +468,7 @@ var gPopupBlockerObserver = {
var brandShortName = brandBundle.getString("brandShortName");
var popupCount = gBrowser.selectedBrowser.blockedPopups.length;
- var stringKey = AppConstants.platform == "win"
- ? "popupWarningButton"
- : "popupWarningButtonUnix";
+ var stringKey = "popupWarningButton";
var popupButtonText = gNavigatorBundle.getString(stringKey);
var popupButtonAccesskey = gNavigatorBundle.getString(stringKey + ".accesskey");
@@ -3374,8 +3367,6 @@ var PrintPreviewListener = {
this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
notificationBox.notificationsHidden = true;
- gBrowser.updateWindowResizers();
-
this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
if (gFindBarInitialized)
gFindBar.close();
@@ -5224,7 +5215,6 @@ function setToolbarVisibility(toolbar, isVisible, persist=true) {
PlacesToolbarHelper.init();
BookmarkingUI.onToolbarVisibilityChange();
- gBrowser.updateWindowResizers();
if (isVisible)
ToolbarIconColor.inferFromText();
}
@@ -8075,6 +8065,7 @@ var ToolbarIconColor = {
window.addEventListener("activate", this);
window.addEventListener("deactivate", this);
Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
+ gPrefService.addObserver("ui.colorChanged", this, false);
// If the window isn't active now, we assume that it has never been active
// before and will soon become active such that inferFromText will be
@@ -8089,6 +8080,7 @@ var ToolbarIconColor = {
window.removeEventListener("activate", this);
window.removeEventListener("deactivate", this);
Services.obs.removeObserver(this, "lightweight-theme-styling-update");
+ gPrefService.removeObserver("ui.colorChanged", this);
},
handleEvent: function (event) {
@@ -8107,6 +8099,18 @@ var ToolbarIconColor = {
// lightweight-theme-styling-update observer.
setTimeout(() => { this.inferFromText(); }, 0);
break;
+ case "nsPref:changed":
+ // system color change
+ var colorChangedPref = false;
+ try {
+ colorChangedPref = gPrefService.getBoolPref("ui.colorChanged");
+ } catch(e) { }
+ // if pref indicates change, call inferFromText() on a small delay
+ if (colorChangedPref == true)
+ setTimeout(() => { this.inferFromText(); }, 300);
+ break;
+ default:
+ console.error("ToolbarIconColor: Uncaught topic " + aTopic);
}
},
@@ -8130,16 +8134,19 @@ var ToolbarIconColor = {
let luminances = new Map;
for (let toolbar of document.querySelectorAll(toolbarSelector)) {
let [r, g, b] = parseRGB(getComputedStyle(toolbar).color);
- let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
+ let luminance = (2 * r + 5 * g + b) / 8;
luminances.set(toolbar, luminance);
}
for (let [toolbar, luminance] of luminances) {
- if (luminance <= 110)
+ if (luminance <= 128)
toolbar.removeAttribute("brighttext");
else
toolbar.setAttribute("brighttext", "true");
}
+
+ // Clear pref if set, since we're done applying the color changes.
+ gPrefService.clearUserPref("ui.colorChanged");
}
}
diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul
index 2c74aecdf..be17fb0e2 100644
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -546,10 +546,8 @@
#ifdef MENUBAR_CAN_AUTOHIDE
toolbarname="&menubarCmd.label;"
accesskey="&menubarCmd.accesskey;"
-#if defined(MOZ_WIDGET_GTK)
autohide="true"
#endif
-#endif
context="toolbar-context-menu">
<toolbaritem id="menubar-items" align="center">
# The entire main menubar is placed into browser-menubar.inc, so that it can be shared by
diff --git a/browser/base/content/overrides/app-license.html b/browser/base/content/overrides/app-license.html
index e7a158c79..0a1f0d8f5 100644
--- a/browser/base/content/overrides/app-license.html
+++ b/browser/base/content/overrides/app-license.html
@@ -2,5 +2,6 @@
- License, v. 2.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.mozilla.org/">Mozilla Project</a> under the Mozilla
+ <a href="http://www.palemoon.org/">Pale Moon project team</a> and
+ <a href="http://www.moonchildproductions.info/">Moonchild Productions</a> under the Mozilla
Public License 2.0 (MPL). <a href="about:rights">Know your rights</a>.</p>
diff --git a/browser/base/content/sync/customize.xul b/browser/base/content/sync/customize.xul
index d95536d9a..827edf565 100644
--- a/browser/base/content/sync/customize.xul
+++ b/browser/base/content/sync/customize.xul
@@ -31,12 +31,7 @@
<label id="sync-customize-title" value="&syncCustomize.title;"/>
<description id="sync-customize-subtitle"
-#ifdef XP_UNIX
- value="&syncCustomizeUnix.description;"
-#else
- value="&syncCustomize.description;"
-#endif
- />
+ value="&syncCustomize.description;"/>
<vbox align="start">
<checkbox label="&engine.tabs.label;"
diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js
index 06fa3d9cc..05f8e00ab 100644
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -9,7 +9,9 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
+#ifdef MOZ_WEBEXTENSIONS
Cu.import("resource://gre/modules/ExtensionContent.jsm");
+#endif
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
"resource:///modules/E10SUtils.jsm");
@@ -929,11 +931,13 @@ var UserContextIdNotifier = {
UserContextIdNotifier.init();
+#ifdef MOZ_WEBEXTENSIONS
ExtensionContent.init(this);
addEventListener("unload", () => {
ExtensionContent.uninit(this);
RefreshBlocker.uninit();
});
+#endif
addMessageListener("AllowScriptsToClose", () => {
content.QueryInterface(Ci.nsIInterfaceRequestor)
diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml
index 3f4c3518e..0cb0f3bd6 100644
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -214,18 +214,6 @@
]]></body>
</method>
- <method name="updateWindowResizers">
- <body><![CDATA[
- if (!window.gShowPageResizers)
- return;
-
- var show = window.windowState == window.STATE_NORMAL;
- for (let i = 0; i < this.browsers.length; i++) {
- this.browsers[i].showWindowResizer = show;
- }
- ]]></body>
- </method>
-
<method name="_setCloseKeyState">
<parameter name="aEnabled"/>
<body><![CDATA[
@@ -1970,10 +1958,6 @@
b.QueryInterface(Ci.nsIFrameLoaderOwner).presetOpenerWindow(aParams.opener);
}
- if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
- b.setAttribute("showresizer", "true");
- }
-
if (!aParams.isPreloadBrowser && this.hasAttribute("autocompletepopup")) {
b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
}
@@ -4835,7 +4819,6 @@
this.appendChild(this._autoScrollPopup);
this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
- this.updateWindowResizers();
// Hook up the event listeners to the first browser
var tabListener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false);
@@ -5854,8 +5837,6 @@
this._handleTabSelect();
this.mTabstripWidth = width;
}
-
- this.tabbrowser.updateWindowResizers();
break;
case "mouseout":
// If the "related target" (the node to which the pointer went) is not
diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml
index 689c7c5a7..eb3150581 100644
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -56,10 +56,11 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
<field name="AppConstants" readonly="true">
(Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
</field>
-
+#ifdef MOZ_WEBEXTENSIONS
<field name="ExtensionSearchHandler" readonly="true">
(Components.utils.import("resource://gre/modules/ExtensionSearchHandler.jsm", {})).ExtensionSearchHandler;
</field>
+#endif
<constructor><![CDATA[
this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
@@ -487,6 +488,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
actionDetails
);
break;
+#ifdef MOZ_WEBEXTENSIONS
case "extension":
this.handleRevert();
// Give the extension control of handling the command.
@@ -494,6 +496,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
let keyword = action.params.keyword;
this.ExtensionSearchHandler.handleInputEntered(keyword, searchString, where);
return;
+#endif
}
} else {
// This is a fallback for add-ons and old testing code that directly
@@ -1211,9 +1214,11 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
this._clearNoActions();
this.formatValue();
}
+#ifdef MOZ_WEBEXTENSIONS
if (ExtensionSearchHandler.hasActiveInputSession()) {
ExtensionSearchHandler.handleInputCancelled();
}
+#endif
]]></handler>
<handler event="dragstart" phase="capturing"><![CDATA[
diff --git a/browser/base/content/win6BrowserOverlay.xul b/browser/base/content/win6BrowserOverlay.xul
deleted file mode 100644
index a69e3f6bd..000000000
--- a/browser/base/content/win6BrowserOverlay.xul
+++ /dev/null
@@ -1,12 +0,0 @@
-<?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/. -->
-
-<overlay id="win6-browser-overlay"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <toolbar id="toolbar-menubar"
- autohide="true"/>
-</overlay>
diff --git a/browser/base/jar.mn b/browser/base/jar.mn
index 38b103c43..3fa5ea408 100644
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -8,9 +8,6 @@ browser.jar:
% overlay chrome://mozapps/content/update/updates.xul chrome://browser/content/softwareUpdateOverlay.xul
% overlay chrome://global/content/console.xul chrome://browser/content/jsConsoleOverlay.xul
#endif
-#ifdef XP_WIN
-% overlay chrome://browser/content/browser.xul chrome://browser/content/win6BrowserOverlay.xul os=WINNT osversion>=6
-#endif
% overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul
% overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul
@@ -99,7 +96,7 @@ browser.jar:
#endif
content/browser/browser-thumbnails.js (content/browser-thumbnails.js)
content/browser/browser-trackingprotection.js (content/browser-trackingprotection.js)
- content/browser/tab-content.js (content/tab-content.js)
+* content/browser/tab-content.js (content/tab-content.js)
content/browser/content.js (content/content.js)
content/browser/social-content.js (content/social-content.js)
content/browser/defaultthemes/1.footer.jpg (content/defaultthemes/1.footer.jpg)
@@ -152,7 +149,7 @@ browser.jar:
content/browser/sync/genericChange.js (content/sync/genericChange.js)
content/browser/sync/key.xhtml (content/sync/key.xhtml)
content/browser/sync/utils.js (content/sync/utils.js)
-* content/browser/sync/customize.xul (content/sync/customize.xul)
+ content/browser/sync/customize.xul (content/sync/customize.xul)
content/browser/sync/customize.js (content/sync/customize.js)
content/browser/sync/customize.css (content/sync/customize.css)
content/browser/safeMode.css (content/safeMode.css)
@@ -166,7 +163,7 @@ browser.jar:
content/browser/contentSearchUI.css (content/contentSearchUI.css)
content/browser/tabbrowser.css (content/tabbrowser.css)
content/browser/tabbrowser.xml (content/tabbrowser.xml)
- content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
+* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
content/browser/utilityOverlay.js (content/utilityOverlay.js)
content/browser/usercontext.svg (content/usercontext.svg)
content/browser/web-panels.js (content/web-panels.js)
@@ -186,9 +183,6 @@ browser.jar:
* content/browser/webrtcIndicator.xul (content/webrtcIndicator.xul)
content/browser/webrtcIndicator.js (content/webrtcIndicator.js)
#endif
-#ifdef XP_WIN
- content/browser/win6BrowserOverlay.xul (content/win6BrowserOverlay.xul)
-#endif
# the following files are browser-specific overrides
* content/browser/license.html (/toolkit/content/license.html)
% override chrome://global/content/license.html chrome://browser/content/license.html
diff --git a/browser/branding/official/bgintro.bmp b/browser/branding/official/bgintro.bmp
deleted file mode 100644
index 9c2fc80cf..000000000
--- a/browser/branding/official/bgintro.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/official/branding.nsi b/browser/branding/official/branding.nsi
index 24b0f4bba..58d7554df 100644
--- a/browser/branding/official/branding.nsi
+++ b/browser/branding/official/branding.nsi
@@ -20,7 +20,7 @@
!define OFFICIAL
!define URLStubDownload ""
!define URLManualDownload ""
-!define URLSystemRequirements ""
+!define URLSystemRequirements "http://www.basilisk-browser.org/requirements.shtml"
!define Channel "release"
# The installer's certificate name and issuer expected by the stub installer
diff --git a/browser/branding/official/clock.bmp b/browser/branding/official/clock.bmp
deleted file mode 100644
index 7da034d3b..000000000
--- a/browser/branding/official/clock.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/official/moz.build b/browser/branding/official/moz.build
index f1a839c9d..e3b7fd1ab 100644
--- a/browser/branding/official/moz.build
+++ b/browser/branding/official/moz.build
@@ -9,53 +9,5 @@ DIRS += ['content', 'locales']
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
-JS_PREFERENCE_FILES += [
- 'pref/firefox-branding.js',
-]
-
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
- FINAL_TARGET_FILES['..'] += [
- 'basilisk.VisualElementsManifest.xml',
- ]
- FINAL_TARGET_FILES.VisualElements += [
- 'VisualElements_150.png',
- 'VisualElements_70.png',
- ]
- BRANDING_FILES += [
- 'appname.bmp',
- 'bgintro.bmp',
- 'branding.nsi',
- 'clock.bmp',
- 'document.ico',
- 'firefox.ico',
- 'newtab.ico',
- 'newwindow.ico',
- 'particles.bmp',
- 'pbmode.ico',
- 'pencil-rtl.bmp',
- 'pencil.bmp',
- 'wizHeader.bmp',
- 'wizHeaderRTL.bmp',
- 'wizWatermark.bmp',
- ]
-elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
- BRANDING_FILES += [
- 'background.png',
- 'disk.icns',
- 'document.icns',
- 'dsstore',
- 'firefox.icns',
- ]
-elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
- BRANDING_FILES += [
- 'default16.png',
- 'default32.png',
- 'default48.png',
- 'mozicon128.png',
- ]
- FINAL_TARGET_FILES.icons += ['mozicon128.png']
- FINAL_TARGET_FILES.chrome.icons.default += [
- 'default16.png',
- 'default32.png',
- 'default48.png',
- ]
+include('../shared/branding.mozbuild')
+ApplicationBranding() \ No newline at end of file
diff --git a/browser/branding/official/particles.bmp b/browser/branding/official/particles.bmp
deleted file mode 100644
index d523606c2..000000000
--- a/browser/branding/official/particles.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/official/pencil-rtl.bmp b/browser/branding/official/pencil-rtl.bmp
deleted file mode 100644
index 67d2fe5d2..000000000
--- a/browser/branding/official/pencil-rtl.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/official/pencil.bmp b/browser/branding/official/pencil.bmp
deleted file mode 100644
index 7dd55741f..000000000
--- a/browser/branding/official/pencil.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/official/pref/firefox-branding.js b/browser/branding/official/pref/firefox-branding.js
index a3d6927bb..190b84e78 100644
--- a/browser/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -2,55 +2,47 @@
* License, v. 2.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("startup.homepage_override_url", "https://www.basilisk-browser.org/releasenotes.shtml");
-pref("startup.homepage_welcome_url", "http://www.basilisk-browser.org/firstrun/");
+#filter substitution
+#filter emptyLines
+
+// Set defines to construct URLs
+#define BRANDING_BASEURL basilisk-browser.org
+#define BRANDING_SITEURL www.@BRANDING_BASEURL@
+#define BRANDING_RELNOTESPATH releasenotes.shtml
+#define BRANDING_FIRSTRUNPATH firstrun/
+#define BRANDING_APPUPDATEURL aus.@BRANDING_BASEURL@
+#define BRANDING_APPUPDATEPATH ?application=%PRODUCT%&version=%VERSION%&arch=%BUILD_TARGET%&buildid=%BUILD_ID%&channel=%CHANNEL%
+
+// Shared Branding Preferences
+// XXX: These should REALLY go back to application preferences
+#include ../../shared/preferences.inc
+
+// Branding Specific Preferences
+pref("startup.homepage_override_url", "https://@BRANDING_SITEURL@/@BRANDING_RELNOTESPATH@");
+pref("startup.homepage_welcome_url", "http://@BRANDING_SITEURL@/@BRANDING_FIRSTRUNPATH@");
pref("startup.homepage_welcome_url.additional", "");
-pref("app.update.url", "https://aus.basilisk-browser.org/?application=%PRODUCT%&version=%VERSION%&arch=%BUILD_TARGET%&buildid=%BUILD_ID%&channel=%CHANNEL%");
-// Interval: Time between checks for a new version (in seconds)
-pref("app.update.interval", 86400); // 1 day
-// The time interval between the downloading of mar file chunks in the
-// background (in seconds)
-// 0 means "download everything at once"
-pref("app.update.download.backgroundInterval", 0);
-// Give the user x seconds to react before showing the big UI. default=192 hours
-pref("app.update.promptWaitTime", 691200);
+// Version release notes
+pref("app.releaseNotesURL", "http://@BRANDING_SITEURL@/@BRANDING_RELNOTESPATH@");
+
+// Vendor home page
+pref("app.vendorURL", "http://@BRANDING_SITEURL@/");
+
+pref("app.update.url", "https://@BRANDING_APPUPDATEURL@/@BRANDING_APPUPDATEPATH@");
+
// URL user can browse to manually if for some reason all update installation
// attempts fail.
-pref("app.update.url.manual", "https://www.basilisk-browser.org/");
+pref("app.update.url.manual", "https://@BRANDING_SITEURL@/");
// 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", "https://www.basilisk-browser.org/releasenotes.shtml");
+pref("app.update.url.details", "https://@BRANDING_SITEURL@/@BRANDING_RELNOTESPATH@");
+
+// Provide UA Gecko and Firefox slices for web compatibility
+pref("general.useragent.compatMode.firefox",true);
+pref("general.useragent.compatMode.gecko",true);
// Switch Application Updates off for now
pref("app.update.enabled", false);
-// Version release notes
-pref("app.releaseNotesURL", "http://www.basilisk-browser.org/releasenotes.shtml");
-// Vendor home page
-pref("app.vendorURL", "http://www.basilisk-browser.org/");
-
-// The number of days a binary is permitted to be old
-// without checking for an update. This assumes that
-// app.update.checkInstallTime is true.
-pref("app.update.checkInstallTime.days", 14);
-
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=immediately
-pref("app.update.badgeWaitTime", 0);
-
-// 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", 100);
-
-// Disable Google Safebrowsing by default. Without an API key, this won't work.
-pref("browser.safebrowsing.phishing.enabled", false);
-pref("browser.safebrowsing.malware.enabled", false);
-pref("browser.safebrowsing.downloads.enabled", false);
-pref("browser.safebrowsing.downloads.remote.enabled", false);
-// Disable the UI controls for it as well for Basilisk-official.
-pref("browser.safebrowsing.UI.enabled", false);
-
-// FxA override
-pref("general.useragent.override.accounts.firefox.com", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Goanna/4.0 Basilisk/55.0.0");
-
+// Shared User Agent Overrides
+#include ../../shared/uaoverrides.inc
diff --git a/browser/branding/branding-common.mozbuild b/browser/branding/shared/branding.mozbuild
index f74724f4a..29d25094f 100644
--- a/browser/branding/branding-common.mozbuild
+++ b/browser/branding/shared/branding.mozbuild
@@ -5,42 +5,37 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
@template
-def FirefoxBranding():
- JS_PREFERENCE_FILES += [
+def ApplicationBranding():
+ JS_PREFERENCE_PP_FILES += [
'pref/firefox-branding.js',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
FINAL_TARGET_FILES['..'] += [
- 'firefox.VisualElementsManifest.xml',
+ 'basilisk.VisualElementsManifest.xml',
]
FINAL_TARGET_FILES.VisualElements += [
'VisualElements_150.png',
'VisualElements_70.png',
]
BRANDING_FILES += [
+ '../shared/newtab.ico',
+ '../shared/newwindow.ico',
+ '../shared/pbmode.ico',
'appname.bmp',
- 'bgintro.bmp',
'branding.nsi',
- 'clock.bmp',
'document.ico',
'firefox.ico',
- 'newtab.ico',
- 'newwindow.ico',
- 'particles.bmp',
- 'pbmode.ico',
- 'pencil-rtl.bmp',
- 'pencil.bmp',
'wizHeader.bmp',
'wizHeaderRTL.bmp',
'wizWatermark.bmp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
BRANDING_FILES += [
- 'background.png',
+ '../shared/dsstore',
'disk.icns',
'document.icns',
- 'dsstore',
+ 'background.png',
'firefox.icns',
]
elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
@@ -56,3 +51,6 @@ def FirefoxBranding():
'default32.png',
'default48.png',
]
+ DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
+ DEFINES['MOZ_BRANDING_DIRECTORY'] = CONFIG['MOZ_BRANDING_DIRECTORY']
+ DEFINES['MOZILLA_UAVERSION_U'] = CONFIG['MOZILLA_UAVERSION_U'] \ No newline at end of file
diff --git a/browser/branding/official/dsstore b/browser/branding/shared/dsstore
index 8ea703674..8ea703674 100644
--- a/browser/branding/official/dsstore
+++ b/browser/branding/shared/dsstore
Binary files differ
diff --git a/browser/branding/official/newtab.ico b/browser/branding/shared/newtab.ico
index a9b37c08c..a9b37c08c 100644
--- a/browser/branding/official/newtab.ico
+++ b/browser/branding/shared/newtab.ico
Binary files differ
diff --git a/browser/branding/official/newwindow.ico b/browser/branding/shared/newwindow.ico
index 553720771..553720771 100644
--- a/browser/branding/official/newwindow.ico
+++ b/browser/branding/shared/newwindow.ico
Binary files differ
diff --git a/browser/branding/official/pbmode.ico b/browser/branding/shared/pbmode.ico
index 47677c13f..47677c13f 100644
--- a/browser/branding/official/pbmode.ico
+++ b/browser/branding/shared/pbmode.ico
Binary files differ
diff --git a/browser/branding/shared/preferences.inc b/browser/branding/shared/preferences.inc
new file mode 100644
index 000000000..08f6c950b
--- /dev/null
+++ b/browser/branding/shared/preferences.inc
@@ -0,0 +1,33 @@
+// Interval: Time between checks for a new version (in seconds)
+pref("app.update.interval", 86400); // 1 day
+
+// The time interval between the downloading of mar file chunks in the
+// background (in seconds)
+// 0 means "download everything at once"
+pref("app.update.download.backgroundInterval", 0);
+
+// Give the user x seconds to react before showing the big UI. default=192 hours
+pref("app.update.promptWaitTime", 691200);
+
+// The number of days a binary is permitted to be old
+// without checking for an update. This assumes that
+// app.update.checkInstallTime is true.
+pref("app.update.checkInstallTime.days", 14);
+
+// Give the user x seconds to reboot before showing a badge on the hamburger
+// button. default=immediately
+pref("app.update.badgeWaitTime", 0);
+
+// 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", 100);
+
+// Disable Google Safebrowsing by default. Without an API key, this won't work.
+pref("browser.safebrowsing.phishing.enabled", false);
+pref("browser.safebrowsing.malware.enabled", false);
+pref("browser.safebrowsing.downloads.enabled", false);
+pref("browser.safebrowsing.downloads.remote.enabled", false);
+
+// Disable the UI controls for it as well for Basilisk-official.
+pref("browser.safebrowsing.UI.enabled", false);
+
diff --git a/browser/branding/shared/uaoverrides.inc b/browser/branding/shared/uaoverrides.inc
new file mode 100644
index 000000000..13a89ed7f
--- /dev/null
+++ b/browser/branding/shared/uaoverrides.inc
@@ -0,0 +1,38 @@
+#define GUAO_PREF general.useragent.override
+
+#define GRE_VERSION @MOZILLA_UAVERSION_U@
+#define GRE_VERSION_SLICE Goanna/@GRE_VERSION@
+#define GRE_DATE_SLICE Goanna/20170101
+#define APP_SLICE Basilisk/@MOZ_APP_VERSION@
+
+#define GK_VERSION 52.0
+#define GK_SLICE Gecko/20100101
+#define FX_SLICE Firefox/@GK_VERSION@
+
+#ifdef XP_UNIX
+#ifndef XP_MACOSX
+#define OS_SLICE X11; Linux x86_64;
+#else
+#define OS_SLICE Macintosh; Intel Mac OS X 10.11;
+#endif
+#else
+#define OS_SLICE Windows NT 6.1; WOW64;
+#endif
+
+// FxA override
+pref("@GUAO_PREF@.accounts.firefox.com", "Mozilla/5.0 (@OS_SLICE@ rv:@GK_VERSION@) @GK_SLICE@ @FX_SLICE@");
+
+// Required for domains that have proven unresponsive to requests from users
+
+// The never-ending Facebook debacle...
+
+// UA-Sniffing domains below are pending responses from their operators - temp workaround
+
+// The following requires native mode. Or it blocks.. "too old firefox", breakage, etc.
+
+// UA-Sniffing domains below have indicated no interest in supporting Pale Moon (BOO!)
+
+// UA-sniffing domains that are "app/vendor-specific" and do not like Pale Moon
+
+// The following domains do not like the Goanna slice
+
diff --git a/browser/branding/unofficial/firefox.VisualElementsManifest.xml b/browser/branding/unofficial/basilisk.VisualElementsManifest.xml
index 7654e0ab7..7654e0ab7 100644
--- a/browser/branding/unofficial/firefox.VisualElementsManifest.xml
+++ b/browser/branding/unofficial/basilisk.VisualElementsManifest.xml
diff --git a/browser/branding/unofficial/bgintro.bmp b/browser/branding/unofficial/bgintro.bmp
deleted file mode 100644
index 9f2a0a6e0..000000000
--- a/browser/branding/unofficial/bgintro.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/clock.bmp b/browser/branding/unofficial/clock.bmp
deleted file mode 100644
index c74398edb..000000000
--- a/browser/branding/unofficial/clock.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/moz.build b/browser/branding/unofficial/moz.build
index 9045cee11..0d829fa5e 100644
--- a/browser/branding/unofficial/moz.build
+++ b/browser/branding/unofficial/moz.build
@@ -9,5 +9,5 @@ DIRS += ['content', 'locales']
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
-include('../branding-common.mozbuild')
-FirefoxBranding()
+include('../shared/branding.mozbuild')
+ApplicationBranding()
diff --git a/browser/branding/unofficial/newtab.ico b/browser/branding/unofficial/newtab.ico
deleted file mode 100644
index a9b37c08c..000000000
--- a/browser/branding/unofficial/newtab.ico
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/newwindow.ico b/browser/branding/unofficial/newwindow.ico
deleted file mode 100644
index 553720771..000000000
--- a/browser/branding/unofficial/newwindow.ico
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/particles.bmp b/browser/branding/unofficial/particles.bmp
deleted file mode 100644
index ab74ce047..000000000
--- a/browser/branding/unofficial/particles.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/pbmode.ico b/browser/branding/unofficial/pbmode.ico
deleted file mode 100644
index 47677c13f..000000000
--- a/browser/branding/unofficial/pbmode.ico
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/pencil-rtl.bmp b/browser/branding/unofficial/pencil-rtl.bmp
deleted file mode 100644
index e50d92db7..000000000
--- a/browser/branding/unofficial/pencil-rtl.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/pencil.bmp b/browser/branding/unofficial/pencil.bmp
deleted file mode 100644
index 252c10f41..000000000
--- a/browser/branding/unofficial/pencil.bmp
+++ /dev/null
Binary files differ
diff --git a/browser/branding/unofficial/pref/firefox-branding.js b/browser/branding/unofficial/pref/firefox-branding.js
index b20a3a309..a5b617a53 100644
--- a/browser/branding/unofficial/pref/firefox-branding.js
+++ b/browser/branding/unofficial/pref/firefox-branding.js
@@ -2,32 +2,39 @@
* License, v. 2.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
+#filter emptyLines
+
+// Set defines to construct URLs
+#define BRANDING_BASEURL basilisk-browser.org
+#define BRANDING_SITEURL www.@BRANDING_BASEURL@
+
+// Shared Branding Preferences
+// XXX: These should REALLY go back to application preferences
+#include ../../shared/preferences.inc
+
+// Branding Specific Preferences
pref("startup.homepage_override_url", "");
pref("startup.homepage_welcome_url", "");
pref("startup.homepage_welcome_url.additional", "");
-// The time interval between checks for a new version (in seconds)
-pref("app.update.interval", 86400); // 24 hours
-// The time interval between the downloading of mar file chunks in the
-// background (in seconds)
-pref("app.update.download.backgroundInterval", 60);
-// Give the user x seconds to react before showing the big UI. default=24 hours
-pref("app.update.promptWaitTime", 86400);
+
+// Version release notes
+pref("app.releaseNotesURL", "about:blank");
+
+// Vendor home page
+pref("app.vendorURL", "about:");
+
+pref("app.update.url", "");
+
// URL user can browse to manually if for some reason all update installation
// attempts fail.
-pref("app.update.url.manual", "https://nightly.mozilla.org");
+pref("app.update.url.manual", "about:");
// 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", "https://nightly.mozilla.org");
-
-// The number of days a binary is permitted to be old
-// without checking for an update. This assumes that
-// app.update.checkInstallTime is true.
-pref("app.update.checkInstallTime.days", 2);
+pref("app.update.url.details", "about:");
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=immediately
-pref("app.update.badgeWaitTime", 0);
+// Switch Application Updates off for unofficial branding
+pref("app.update.enabled", false);
-// 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", 0);
+// Shared User Agent Overrides
+#include ../../shared/uaoverrides.inc
diff --git a/browser/components/build/moz.build b/browser/components/build/moz.build
index 8c99b74dd..622cf449c 100644
--- a/browser/components/build/moz.build
+++ b/browser/components/build/moz.build
@@ -12,8 +12,7 @@ SOURCES += [
'nsModule.cpp',
]
-Library('browsercomps')
-FINAL_LIBRARY = 'xul'
+XPCOMBinaryComponent('browsercomps')
LOCAL_INCLUDES += [
'../about',
@@ -22,3 +21,22 @@ LOCAL_INCLUDES += [
'../migration',
'../shell',
]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ OS_LIBS += [
+ 'esent',
+ 'netapi32',
+ 'ole32',
+ 'shell32',
+ 'shlwapi',
+ 'version',
+ ]
+ DELAYLOAD_DLLS += [
+ 'esent.dll',
+ 'netapi32.dll',
+ ]
+
+# Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
+# GTK2: Need to link with glib for GNOME shell service
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'gtk2', 'gtk3'):
+ OS_LIBS += CONFIG['TK_LIBS']
diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm
index 86ff2708b..01389108d 100644
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -262,24 +262,14 @@ var CustomizableUIInternal = {
defaultCollapsed: false,
}, true);
- if (AppConstants.platform != "macosx") {
+ if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
this.registerArea(CustomizableUI.AREA_MENUBAR, {
legacy: true,
type: CustomizableUI.TYPE_TOOLBAR,
defaultPlacements: [
"menubar-items",
],
- get defaultCollapsed() {
- if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
- if (AppConstants.platform == "linux") {
- return true;
- }
- // This is duplicated logic from /browser/base/jar.mn
- // for win6BrowserOverlay.xul.
- return AppConstants.isPlatformAndVersionAtLeast("win", 6);
- }
- return false;
- }
+ defaultCollapsed: true,
}, true);
}
diff --git a/browser/components/customizableui/CustomizableWidgets.jsm b/browser/components/customizableui/CustomizableWidgets.jsm
index 907e2e0f7..1e68b01c1 100644
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -1187,10 +1187,7 @@ let preferencesButton = {
win.openPreferences();
}
};
-if (AppConstants.platform == "win") {
- preferencesButton.label = "preferences-button.labelWin";
- preferencesButton.tooltiptext = "preferences-button.tooltipWin2";
-} else if (AppConstants.platform == "macosx") {
+if (AppConstants.platform == "macosx") {
preferencesButton.tooltiptext = "preferences-button.tooltiptext.withshortcut";
preferencesButton.shortcutId = "key_preferencesCmdMac";
} else {
diff --git a/browser/components/dirprovider/DirectoryProvider.cpp b/browser/components/dirprovider/DirectoryProvider.cpp
index 7b4f81c7d..8b7c0b9e0 100644
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -20,7 +20,7 @@
#include "nsDirectoryServiceUtils.h"
#include "mozilla/ModuleUtils.h"
#include "nsServiceManagerUtils.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "nsXULAppAPI.h"
#include "nsIPrefLocalizedString.h"
diff --git a/browser/components/feeds/nsFeedSniffer.cpp b/browser/components/feeds/nsFeedSniffer.cpp
index f2d0da776..f314d3d3b 100644
--- a/browser/components/feeds/nsFeedSniffer.cpp
+++ b/browser/components/feeds/nsFeedSniffer.cpp
@@ -185,15 +185,9 @@ IsDocumentElement(const char *start, const char* end)
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)){
+ int32_t offset = dataString.Find(substring);
+ if (offset == -1)
return false;
- }
-
- auto offset = start.get() - dataString.Data();
const char *begin = dataString.BeginReading();
@@ -318,10 +312,9 @@ nsFeedSniffer::GetMIMETypeFromContent(nsIRequest* request,
// RSS 1.0
if (!isFeed) {
- bool foundNS_RDF = FindInReadable(NS_LITERAL_CSTRING(NS_RDF), dataString);
- bool foundNS_RSS = FindInReadable(NS_LITERAL_CSTRING(NS_RSS), dataString);
isFeed = ContainsTopLevelSubstring(dataString, "<rdf:RDF") &&
- foundNS_RDF && foundNS_RSS;
+ dataString.Find(NS_RDF) != -1 &&
+ dataString.Find(NS_RSS) != -1;
}
// If we sniffed a feed, coerce our internal type
diff --git a/browser/components/feeds/nsFeedSniffer.h b/browser/components/feeds/nsFeedSniffer.h
index b7ac002bd..a0eb9862c 100644
--- a/browser/components/feeds/nsFeedSniffer.h
+++ b/browser/components/feeds/nsFeedSniffer.h
@@ -6,7 +6,7 @@
#include "nsIContentSniffer.h"
#include "nsIStreamListener.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "mozilla/Attributes.h"
class nsFeedSniffer final : public nsIContentSniffer,
diff --git a/browser/components/migration/nsIEHistoryEnumerator.cpp b/browser/components/migration/nsIEHistoryEnumerator.cpp
index 116e9a860..0b377d27e 100644
--- a/browser/components/migration/nsIEHistoryEnumerator.cpp
+++ b/browser/components/migration/nsIEHistoryEnumerator.cpp
@@ -9,10 +9,9 @@
#include "nsArrayEnumerator.h"
#include "nsCOMArray.h"
-#include "nsIURI.h"
#include "nsIVariant.h"
#include "nsNetUtil.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "nsWindowsMigrationUtils.h"
#include "prtime.h"
@@ -23,7 +22,7 @@ NS_IMPL_ISUPPORTS(nsIEHistoryEnumerator, nsISimpleEnumerator)
nsIEHistoryEnumerator::nsIEHistoryEnumerator()
{
- ::CoInitialize(nullptr);
+ ::CoInitialize(nullptr);
}
nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index d69a11f6b..f97c173a0 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -64,6 +64,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-s
["Task", "resource://gre/modules/Task.jsm"],
["UITour", "resource:///modules/UITour.jsm"],
["URLBarZoom", "resource:///modules/URLBarZoom.jsm"],
+ ["UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm"],
["WebChannel", "resource://gre/modules/WebChannel.jsm"],
["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
["webrtcUI", "resource:///modules/webrtcUI.jsm"],
@@ -662,6 +663,8 @@ BrowserGlue.prototype = {
}
} catch (ex) { /* missing any of the prefs is not critical */ }
+ UserAgentOverrides.init();
+
PageThumbs.init();
webrtcUI.init();
AboutHome.init();
@@ -1042,6 +1045,7 @@ BrowserGlue.prototype = {
BrowserUsageTelemetry.uninit();
SelfSupportBackend.uninit();
+ UserAgentOverrides.uninit();
PageThumbs.uninit();
NewTabMessages.uninit();
AboutNewTab.uninit();
diff --git a/browser/components/preferences/in-content/preferences.xul b/browser/components/preferences/in-content/preferences.xul
index e9664eaf4..7ec7ef119 100644
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -55,18 +55,10 @@
%advancedDTD;
]>
-#ifdef XP_WIN
-#define USE_WIN_TITLE_STYLE
-#endif
-
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
disablefastfind="true"
-#ifdef USE_WIN_TITLE_STYLE
- title="&prefWindow.titleWin;">
-#else
title="&prefWindow.title;">
-#endif
<html:link rel="shortcut icon"
href="chrome://browser/skin/preferences/in-content/favicon.ico"/>
diff --git a/browser/components/preferences/in-content/security.xul b/browser/components/preferences/in-content/security.xul
index a10576c25..b7bdb9361 100644
--- a/browser/components/preferences/in-content/security.xul
+++ b/browser/components/preferences/in-content/security.xul
@@ -45,6 +45,7 @@
<!-- Passwords -->
<preference id="signon.rememberSignons" name="signon.rememberSignons" type="bool"/>
+ <preference id="signon.autofillForms" name="signon.autofillForms" type="bool"/>
</preferences>
@@ -103,6 +104,9 @@
accesskey="&passwordExceptions.accesskey;"
preference="pref.privacy.disable_button.view_passwords_exceptions"/>
</hbox>
+ <checkbox id="autofillPasswords" flex="1"
+ label="&autofillPasswords.label;" accesskey="&autofillPasswords.accesskey;"
+ preference="signon.autofillForms"/>
<grid id="passwordGrid">
<columns>
<column flex="1"/>
diff --git a/browser/components/shell/ShellService.jsm b/browser/components/shell/ShellService.jsm
index 2a3400b60..cc225eae0 100644
--- a/browser/components/shell/ShellService.jsm
+++ b/browser/components/shell/ShellService.jsm
@@ -62,10 +62,10 @@ let ShellServiceInternal = {
if (AppConstants.platform == "win") {
let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- "Software\\Mozilla\\Firefox",
+ "Software\\Mozilla\\Basilisk",
"DefaultBrowserOptOut");
WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- "Software\\Mozilla\\Firefox",
+ "Software\\Mozilla\\Basilisk",
"DefaultBrowserOptOut");
if (optOutValue == "True") {
Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false);
diff --git a/browser/components/shell/nsGNOMEShellService.cpp b/browser/components/shell/nsGNOMEShellService.cpp
index f6c2613d4..613b5bffc 100644
--- a/browser/components/shell/nsGNOMEShellService.cpp
+++ b/browser/components/shell/nsGNOMEShellService.cpp
@@ -14,7 +14,7 @@
#include "nsDirectoryServiceDefs.h"
#include "nsIPrefService.h"
#include "prenv.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "nsIGConfService.h"
#include "nsIGIOService.h"
#include "nsIGSettingsService.h"
@@ -70,16 +70,16 @@ static const MimeTypeAssociation appTypes[] = {
// GConf registry key constants
#define DG_BACKGROUND "/desktop/gnome/background"
-#define kDesktopImageKey DG_BACKGROUND "/picture_filename"
-#define kDesktopOptionsKey DG_BACKGROUND "/picture_options"
-#define kDesktopDrawBGKey DG_BACKGROUND "/draw_background"
-#define kDesktopColorKey DG_BACKGROUND "/primary_color"
+static const char kDesktopImageKey[] = DG_BACKGROUND "/picture_filename";
+static const char kDesktopOptionsKey[] = DG_BACKGROUND "/picture_options";
+static const char kDesktopDrawBGKey[] = DG_BACKGROUND "/draw_background";
+static const char kDesktopColorKey[] = DG_BACKGROUND "/primary_color";
-#define kDesktopBGSchema "org.gnome.desktop.background"
-#define kDesktopImageGSKey "picture-uri"
-#define kDesktopOptionGSKey "picture-options"
-#define kDesktopDrawBGGSKey "draw-background"
-#define kDesktopColorGSKey "primary-color"
+static const char kDesktopBGSchema[] = "org.gnome.desktop.background";
+static const char kDesktopImageGSKey[] = "picture-uri";
+static const char kDesktopOptionGSKey[] = "picture-options";
+static const char kDesktopDrawBGGSKey[] = "draw-background";
+static const char kDesktopColorGSKey[] = "primary-color";
nsresult
nsGNOMEShellService::Init()
@@ -451,7 +451,7 @@ nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement,
// Set the image to an empty string first to force a refresh
// (since we could be writing a new image on top of an existing
- // Firefox_wallpaper.png and nautilus doesn't monitor the file for changes)
+ // Basilisk_wallpaper.png and nautilus doesn't monitor the file for changes)
gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey),
EmptyCString());
@@ -508,8 +508,7 @@ static void
ColorToCString(uint32_t aColor, nsCString& aResult)
{
// The #rrrrggggbbbb format is used to match gdk_color_to_string()
- aResult.SetLength(13);
- char *buf = aResult.BeginWriting();
+ char *buf = aResult.BeginWriting(13);
if (!buf)
return;
diff --git a/browser/components/shell/nsGNOMEShellService.h b/browser/components/shell/nsGNOMEShellService.h
index b3ef1a918..a7b003802 100644
--- a/browser/components/shell/nsGNOMEShellService.h
+++ b/browser/components/shell/nsGNOMEShellService.h
@@ -7,7 +7,7 @@
#define nsgnomeshellservice_h____
#include "nsIGNOMEShellService.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "mozilla/Attributes.h"
class nsGNOMEShellService final : public nsIGNOMEShellService
diff --git a/browser/components/shell/nsMacShellService.cpp b/browser/components/shell/nsMacShellService.cpp
index 48db4896b..d8d64039d 100644
--- a/browser/components/shell/nsMacShellService.cpp
+++ b/browser/components/shell/nsMacShellService.cpp
@@ -20,7 +20,7 @@
#include "nsIProperties.h"
#include "nsServiceManagerUtils.h"
#include "nsShellService.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "nsIDocShell.h"
#include "nsILoadContext.h"
diff --git a/browser/components/shell/nsWindowsShellService.cpp b/browser/components/shell/nsWindowsShellService.cpp
index 53d128cb9..879b0c7f0 100644
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -12,7 +12,6 @@
#include "nsIDOMElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsIImageLoadingContent.h"
-#include "nsIOutputStream.h"
#include "nsIPrefService.h"
#include "nsIPrefLocalizedString.h"
#include "nsIServiceManager.h"
@@ -136,7 +135,7 @@ OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
// The following keys are set to make Basilisk appear in the Start Menu as the
// browser:
//
-// HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\
+// HKCU\SOFTWARE\Clients\StartMenuInternet\BASILISK.EXE\
// (default) REG_SZ <appname>
// DefaultIcon (default) REG_SZ <apppath>,0
// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
@@ -466,7 +465,7 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
return NS_OK;
}
- res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, keyName.get(),
+ res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, PromiseFlatString(keyName).get(),
0, KEY_SET_VALUE, &theKey);
if (REG_FAILED(res)) {
// If updating the open command fails try to update it using the helper
@@ -475,9 +474,10 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
return NS_OK;
}
+ const nsString &flatValue = PromiseFlatString(valueData);
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
- (const BYTE *) valueData.get(),
- (valueData.Length() + 1) * sizeof(char16_t));
+ (const BYTE *) flatValue.get(),
+ (flatValue.Length() + 1) * sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
if (REG_FAILED(res)) {
@@ -529,8 +529,9 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
if (REG_FAILED(res) || char16_t('\0') != *currValue) {
// Key wasn't set or was set to something other than our registry entry.
// Delete the key along with all of its childrean and then recreate it.
- ::SHDeleteKeyW(HKEY_CURRENT_USER, keyName.get());
- res = ::RegCreateKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, nullptr,
+ const nsString &flatName = PromiseFlatString(keyName);
+ ::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get());
+ res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
nullptr, &theKey, nullptr);
if (REG_FAILED(res)) {
@@ -582,9 +583,10 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
NS_ConvertUTF8toUTF16 valueData(VAL_OPEN);
valueData.Replace(offset, 9, appLongPath);
+ const nsString &flatValue = PromiseFlatString(valueData);
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
- (const BYTE *) valueData.get(),
- (valueData.Length() + 1) * sizeof(char16_t));
+ (const BYTE *) flatValue.get(),
+ (flatValue.Length() + 1) * sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
// If updating the FTP protocol handlers shell open command fails try to
@@ -646,11 +648,6 @@ nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
nsresult
nsWindowsShellService::LaunchControlPanelDefaultPrograms()
{
- // This Default Programs feature is Win7+ only.
- if (!IsWin7OrLater()) {
- return NS_ERROR_FAILURE;
- }
-
// Build the path control.exe path safely
WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
diff --git a/browser/components/shell/nsWindowsShellService.h b/browser/components/shell/nsWindowsShellService.h
index b9c473a15..06c6c3c9b 100644
--- a/browser/components/shell/nsWindowsShellService.h
+++ b/browser/components/shell/nsWindowsShellService.h
@@ -7,7 +7,7 @@
#define nswindowsshellservice_h____
#include "nscore.h"
-#include "nsString.h"
+#include "nsStringAPI.h"
#include "nsIWindowsShellService.h"
#include "nsITimer.h"
diff --git a/browser/components/webextensions/extension-win-panel.css b/browser/components/webextensions/extension-win-panel.css
index ddafe3ea5..9da6da14c 100644
--- a/browser/components/webextensions/extension-win-panel.css
+++ b/browser/components/webextensions/extension-win-panel.css
@@ -1,6 +1,4 @@
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
body {
border-radius: 4px;
}
diff --git a/browser/config/mozconfigs/win32/common-opt b/browser/config/mozconfigs/win32/common-opt
index d43a9878b..816c8926a 100644
--- a/browser/config/mozconfigs/win32/common-opt
+++ b/browser/config/mozconfigs/win32/common-opt
@@ -4,7 +4,6 @@
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-jemalloc
-ac_add_options --enable-require-all-d3dc-versions
if [ -f /c/builds/gapi.data ]; then
_gapi_keyfile=c:/builds/gapi.data
diff --git a/browser/config/mozconfigs/win32/debug b/browser/config/mozconfigs/win32/debug
index 6beee93c2..815276d0f 100644
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -6,7 +6,6 @@ ac_add_options --enable-debug
ac_add_options --enable-dmd
ac_add_options --enable-profiling # needed for --enable-dmd to work on Windows
ac_add_options --enable-verify-mar
-ac_add_options --enable-require-all-d3dc-versions
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
diff --git a/browser/config/version.txt b/browser/config/version.txt
index b406fbef6..d90a8c220 100644
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1 +1 @@
-55.0.0
+52.9.0
diff --git a/browser/config/version_display.txt b/browser/config/version_display.txt
index b406fbef6..d90a8c220 100644
--- a/browser/config/version_display.txt
+++ b/browser/config/version_display.txt
@@ -1 +1 @@
-55.0.0
+52.9.0
diff --git a/browser/confvars.sh b/browser/confvars.sh
index d4c2c008d..0cccede38 100755
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -18,7 +18,7 @@ if test "$OS_ARCH" = "WINNT"; then
MOZ_MAINTENANCE_SERVICE=
fi
-# For Basilisk we want to use 55.0.YYYY.MM.DD as MOZ_APP_VERSION in release
+# For Basilisk we want to use 52.9.YYYY.MM.DD as MOZ_APP_VERSION in release
# builds so add-on developers have something to target while maintaining
# Firefox compatiblity.
# To enable add "export BASILISK_VERSION=1" to the .mozconfig file.
@@ -26,7 +26,7 @@ fi
# don't export the variable if you are in development or don't care.
# When not exported we fall back the value in the version*.txt file.
if test -n "$BASILISK_VERSION" ; then
- MOZ_APP_VERSION=55.0.`date --utc '+%Y.%m.%d'`
+ MOZ_APP_VERSION=52.9.`date --utc '+%Y.%m.%d'`
MOZ_APP_VERSION_DISPLAY=`date --utc '+%Y.%m.%d'`
else
MOZ_APP_VERSION=`cat ${_topsrcdir}/$MOZ_BUILD_APP/config/version.txt`
@@ -62,4 +62,8 @@ MOZ_ADDON_SIGNING=0
MOZ_REQUIRE_SIGNING=0
# Include the DevTools client, not just the server (which is the default)
-MOZ_DEVTOOLS=all
+if test -n "$BASILISK_DISABLE_DEVTOOLS" ; then
+MOZ_DEVTOOLS=
+else
+MOZ_DEVTOOLS=1
+fi
diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in
index 55df797ef..ccfce3cd9 100644
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -61,9 +61,6 @@ DEFINES += -DMOZ_ANGLE_RENDERER=$(MOZ_ANGLE_RENDERER)
ifdef MOZ_D3DCOMPILER_VISTA_DLL
DEFINES += -DMOZ_D3DCOMPILER_VISTA_DLL=$(MOZ_D3DCOMPILER_VISTA_DLL)
endif
-ifdef MOZ_D3DCOMPILER_XP_DLL
-DEFINES += -DMOZ_D3DCOMPILER_XP_DLL=$(MOZ_D3DCOMPILER_XP_DLL)
-endif
endif
DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn
index 2ea55aff4..7baa6ebed 100644
--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -171,7 +171,7 @@ chrome/toolkit/skin/classic/global/dialog.css
chrome/toolkit/skin/classic/global/dropmarker.css
chrome/toolkit/skin/classic/global/global.css
chrome/toolkit/skin/classic/global/groupbox.css
-chrome/toolkit/skin/classic/global/icons/close-XPVista7.png
+chrome/toolkit/skin/classic/global/icons/close-win7.png
chrome/toolkit/skin/classic/global/icons/tabprompts-bgtexture.png
chrome/toolkit/skin/classic/global/listbox.css
chrome/toolkit/skin/classic/global/media/clicktoplay-bgtexture.png
@@ -196,15 +196,21 @@ chrome/toolkit/skin/classic/global/toolbarbutton.css
chrome/toolkit/skin/classic/global/tree.css
chrome/toolkit/skin/classic/global/wizard.css
chrome/toolkit/skin/classic/mozapps/downloads/buttons.png
-chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons-XP.png
chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons.png
chrome/toolkit/skin/classic/mozapps/extensions/category-dictionaries.png
chrome/toolkit/skin/classic/mozapps/extensions/category-experiments.png
chrome/toolkit/skin/classic/mozapps/extensions/dictionaryGeneric.png
chrome/toolkit/skin/classic/mozapps/extensions/experimentGeneric.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-themes.png
+chrome/toolkit/skin/classic/mozapps/extensions/themeGeneric.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-languages.png
+chrome/toolkit/skin/classic/mozapps/extensions/localeGeneric.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-extensions.png
+chrome/toolkit/skin/classic/mozapps/extensions/extensionGeneric.png
chrome/toolkit/skin/classic/mozapps/update/buttons.png
-chrome/toolkit/skin/classic/mozapps/update/downloadButtons-XP.png
chrome/toolkit/skin/classic/mozapps/update/downloadButtons.png
+chrome/toolkit/skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png
+
components/FxAccountsPush.js
crashreporter.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
crashreporter.app/Contents/Resources/English.lproj/MainMenuRTL.nib/classes.nib
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index 1ad59925e..c2269c069 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -156,6 +156,7 @@
@RESPATH@/browser/components/prebuilt-interfaces.manifest
@RESPATH@/browser/components/interfaces.xpt
#endif
+@RESPATH@/browser/components/components.manifest
@RESPATH@/components/alerts.xpt
#ifdef ACCESSIBILITY
#ifdef XP_WIN32
@@ -416,10 +417,14 @@
@RESPATH@/components/addonManager.js
@RESPATH@/components/amContentHandler.js
@RESPATH@/components/amInstallTrigger.js
+#ifdef MOZ_WEBEXTENSIONS
@RESPATH@/components/amWebAPI.js
+#endif
@RESPATH@/components/amWebInstallListener.js
@RESPATH@/components/nsBlocklistService.js
+#ifdef MOZ_WEBEXTENSIONS
@RESPATH@/components/nsBlocklistServiceContent.js
+#endif
#ifdef MOZ_UPDATER
@RESPATH@/components/nsUpdateService.manifest
@RESPATH@/components/nsUpdateService.js
@@ -441,6 +446,7 @@
@RESPATH@/browser/components/nsSessionStore.js
@RESPATH@/components/nsURLFormatter.manifest
@RESPATH@/components/nsURLFormatter.js
+@RESPATH@/browser/components/@DLL_PREFIX@browsercomps@DLL_SUFFIX@
@RESPATH@/components/txEXSLTRegExFunctions.manifest
@RESPATH@/components/txEXSLTRegExFunctions.js
@RESPATH@/components/toolkitplaces.manifest
@@ -567,9 +573,11 @@
@RESPATH@/components/TestInterfaceJSMaplike.js
#endif
+#ifdef MOZ_WEBEXTENSIONS
; [Extensions]
@RESPATH@/components/extensions-toolkit.manifest
@RESPATH@/browser/components/extensions-browser.manifest
+#endif
; Modules
@RESPATH@/browser/modules/*
@@ -599,10 +607,6 @@
#ifdef MOZ_D3DCOMPILER_VISTA_DLL
@BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
#endif
-
-#ifdef MOZ_D3DCOMPILER_XP_DLL
-@BINPATH@/@MOZ_D3DCOMPILER_XP_DLL@
-#endif
#endif # MOZ_ANGLE_RENDERER
; [Browser Chrome Files]
diff --git a/browser/installer/windows/Makefile.in b/browser/installer/windows/Makefile.in
index fa6fd5ba9..a8305d077 100644
--- a/browser/installer/windows/Makefile.in
+++ b/browser/installer/windows/Makefile.in
@@ -11,9 +11,7 @@ INSTALLER_FILES = \
app.tag \
nsis/installer.nsi \
nsis/uninstaller.nsi \
- nsis/stub.nsi \
nsis/shared.nsh \
- stub.tag \
$(NULL)
ifdef MOZ_MAINTENANCE_SERVICE
@@ -25,11 +23,6 @@ endif
BRANDING_FILES = \
branding.nsi \
appname.bmp \
- bgintro.bmp \
- clock.bmp \
- particles.bmp \
- pencil.bmp \
- pencil-rtl.bmp \
wizHeader.bmp \
wizHeaderRTL.bmp \
wizWatermark.bmp \
@@ -90,9 +83,6 @@ $(CONFIG_DIR)/setup.exe::
--preprocess-single-file $(topsrcdir) \
$(PPL_LOCALE_ARGS) $(CONFIG_DIR) \
nsisstrings.properties nsisstrings.nlf
- $(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
- --convert-utf8-utf16le \
- $(srcdir)/nsis/oneoff_en-US.nsh $(CONFIG_DIR)/oneoff_en-US.nsh
GARBARGE_DIRS += instgen
diff --git a/browser/installer/windows/app.tag b/browser/installer/windows/app.tag
index 479d9f714..7be6d2e42 100644
--- a/browser/installer/windows/app.tag
+++ b/browser/installer/windows/app.tag
@@ -1,4 +1,4 @@
;!@Install@!UTF-8!
-Title="Mozilla Firefox"
+Title="Basilisk"
RunProgram="setup.exe"
;!@InstallEnd@! \ No newline at end of file
diff --git a/browser/installer/windows/nsis/defines.nsi.in b/browser/installer/windows/nsis/defines.nsi.in
index ffb23ff1c..5ad9b7966 100644
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -21,15 +21,15 @@
!endif
# These defines should match application.ini settings
-!define AppName "Firefox"
+!define AppName "Basilisk"
!define AppVersion "@APP_VERSION@"
!define GREVersion @MOZILLA_VERSION@
!define AB_CD "@AB_CD@"
!define FileMainEXE "@MOZ_APP_NAME@.exe"
-!define WindowClass "FirefoxMessageWindow"
-!define DDEApplication "Firefox"
-!define AppRegName "Firefox"
+!define WindowClass "BasiliskMessageWindow"
+!define DDEApplication "Basilisk"
+!define AppRegName "Basilisk"
!ifndef DEV_EDITION
!define BrandShortName "@MOZ_APP_DISPLAYNAME@"
@@ -55,14 +55,6 @@
!define UpdateChannel "@MOZ_UPDATE_CHANNEL@"
!endif
-# Due to official and beta using the same branding this is needed to
-# differentiante between the url used by the stub for downloading.
-!if "@MOZ_UPDATE_CHANNEL@" == "beta"
-!define BETA_UPDATE_CHANNEL
-!endif
-
-!define BaseURLStubPing "http://download-stats.mozilla.org/stub"
-
# 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
@@ -71,7 +63,7 @@
!define MinSupportedVer "Microsoft Windows 7 x64"
#else
!define ARCH "x86"
-!define MinSupportedVer "Microsoft Windows XP SP2"
+!define MinSupportedVer "Microsoft Windows 7"
#endif
!define MinSupportedCPU "SSE2"
@@ -85,7 +77,7 @@ VIProductVersion "1.0.0.0"
VIAddVersionKey "ProductName" "${BrandShortName}"
VIAddVersionKey "CompanyName" "${CompanyName}"
#ifdef MOZ_OFFICIAL_BRANDING
-VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of The Mozilla Foundation."
+VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of Moonchild Productions."
#endif
VIAddVersionKey "LegalCopyright" "${CompanyName}"
VIAddVersionKey "FileVersion" "${AppVersion}"
@@ -93,12 +85,6 @@ VIAddVersionKey "ProductVersion" "${AppVersion}"
# Comments is not used but left below commented out for future reference
# VIAddVersionKey "Comments" "Comments"
-# It isn't possible to get the size of the installation prior to downloading
-# so the stub installer uses an estimate. The size is derived from the size of
-# the complete installer, the size of the extracted complete installer, and at
-# least 15 MB additional for working room.
-!define APPROXIMATE_REQUIRED_SPACE_MB "145"
-
# Control positions in Dialog Units so they are placed correctly with
# non-default DPI settings
!define OPTIONS_ITEM_EDGE_DU 90u
diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi
index aed5808cd..994c09279 100755
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -1139,13 +1139,11 @@ Function .onInit
; 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.
+ ; SSE2 instruction set is available. Result returned in $R7.
System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R7"
-!ifdef HAVE_64BIT_BUILD
- ; Restrict x64 builds from being installed on x86 and pre Win7
- ${Unless} ${RunningX64}
- ${OrUnless} ${AtLeastWin7}
+ ; Windows NT 6.0 and lower are not supported on any architecture.
+ ${Unless} ${AtLeastWin7}
${If} "$R7" == "0"
strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
${Else}
@@ -1156,59 +1154,28 @@ Function .onInit
Quit
${EndUnless}
- SetRegView 64
-!else
- StrCpy $R8 "0"
- ${If} ${AtMostWin2000}
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} ${IsWinXP}
- ${AndIf} ${AtMostServicePack} 1
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} $R8 == "1"
- ; XXX-rstrong - some systems failed the AtLeastWin2000 test that we
- ; used to use for an unknown reason and likely fail the AtMostWin2000
- ; and possibly the IsWinXP test as well. To work around this also
- ; check if the Windows NT registry Key exists and if it does if the
- ; first char in CurrentVersion is equal to 3 (Windows NT 3.5 and
- ; 3.5.1), 4 (Windows NT 4), or 5 (Windows 2000 and Windows XP).
- StrCpy $R8 ""
- ClearErrors
- ReadRegStr $R8 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
- StrCpy $R8 "$R8" 1
- ${If} ${Errors}
- ${OrIf} "$R8" == "3"
- ${OrIf} "$R8" == "4"
- ${OrIf} "$R8" == "5"
- ${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
- ${EndIf}
- ${EndUnless}
-!endif
-
+ ; SSE2 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)"
; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
; removed after we require NSIS 3.0a2 or greater.
!ifndef NSIS_PACKEDVERSION
- ${If} ${AtLeastWinVista}
- System::Call 'user32::SetProcessDPIAware()'
- ${EndIf}
+ System::Call 'user32::SetProcessDPIAware()'
!endif
!insertmacro InitInstallOptionsFile "options.ini"
diff --git a/browser/installer/windows/nsis/maintenanceservice_installer.nsi b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
index 4bc9d2bed..1f73bac6a 100644
--- a/browser/installer/windows/nsis/maintenanceservice_installer.nsi
+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
@@ -120,10 +120,7 @@ Function .onInit
SetSilent silent
- ; On Windows 2000 we do not install the maintenance service.
- ; We won't run this installer from the parent installer, but just in case
- ; someone tries to execute it on Windows 2000...
- ${Unless} ${AtLeastWinXP}
+ ${Unless} ${AtLeastWin7}
Abort
${EndUnless}
FunctionEnd
diff --git a/browser/installer/windows/nsis/oneoff_en-US.nsh b/browser/installer/windows/nsis/oneoff_en-US.nsh
deleted file mode 100644
index 62d95ade9..000000000
--- a/browser/installer/windows/nsis/oneoff_en-US.nsh
+++ /dev/null
@@ -1,12 +0,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/.
-
-; Custom strings for en-US. This is not in the locale directory so these strings
-; aren't translated.
-!define INDENT " "
-!define INTRO_BLURB "Thanks for choosing $BrandFullName. We’re not just designed to be different, we’re different by design."
-!define INSTALL_BLURB1 "You're about to enjoy the very latest in speed, flexibility and security so you're always in control."
-!define INSTALL_BLURB2 "And you're joining a global community of users, contributors and developers working to make the best browser in the world."
-!define INSTALL_BLURB3 "You even get a haiku:$\n${INDENT}Proudly non-profit$\n${INDENT}Free to innovate for you$\n${INDENT}And a better Web"
-!undef INDENT
diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh
index 8d7eea618..fb2239d24 100755
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -10,12 +10,12 @@
System::Call "kernel32::ProcessIdToSessionId(i $0, *i ${NSIS_MAX_STRLEN} r9)"
; Determine if we're the protected UserChoice default or not. If so fix the
- ; start menu tile. In case there are 2 Firefox installations, we only do
+ ; start menu tile. In case there are 2 Basilisk installations, we only do
; this if the application being updated is the default.
ReadRegStr $0 HKCU "Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice" "ProgId"
- ${If} $0 == "FirefoxURL"
+ ${If} $0 == "BasiliskURL"
${AndIf} $9 != 0 ; We're not running in session 0
- ReadRegStr $0 HKCU "Software\Classes\FirefoxURL\shell\open\command" ""
+ ReadRegStr $0 HKCU "Software\Classes\BasiliskURL\shell\open\command" ""
${GetPathFromString} "$0" $0
${GetParent} "$0" $0
${If} ${FileExists} "$0"
@@ -327,13 +327,13 @@
ClearErrors
EnumRegKey $7 HKCR "${FILE_TYPE}" 0
${If} ${Errors}
- WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}" "" "FirefoxHTML"
+ WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}" "" "BasiliskHTML"
${EndIf}
- WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}\OpenWithProgids" "FirefoxHTML" ""
+ WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}\OpenWithProgids" "BasiliskHTML" ""
!macroend
!define AddAssociationIfNoneExist "!insertmacro AddAssociationIfNoneExist"
-; Adds the protocol and file handler registry entries for making Firefox the
+; Adds the protocol and file handler registry entries for making Basilisk the
; default handler (uses SHCTX).
!macro SetHandlers
${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
@@ -341,30 +341,30 @@
StrCpy $0 "SOFTWARE\Classes"
StrCpy $2 "$\"$8$\" -osint -url $\"%1$\""
- ; Associate the file handlers with FirefoxHTML
+ ; Associate the file handlers with BasiliskHTML
ReadRegStr $6 SHCTX "$0\.htm" ""
- ${If} "$6" != "FirefoxHTML"
- WriteRegStr SHCTX "$0\.htm" "" "FirefoxHTML"
+ ${If} "$6" != "BasiliskHTML"
+ WriteRegStr SHCTX "$0\.htm" "" "BasiliskHTML"
${EndIf}
ReadRegStr $6 SHCTX "$0\.html" ""
- ${If} "$6" != "FirefoxHTML"
- WriteRegStr SHCTX "$0\.html" "" "FirefoxHTML"
+ ${If} "$6" != "BasiliskHTML"
+ WriteRegStr SHCTX "$0\.html" "" "BasiliskHTML"
${EndIf}
ReadRegStr $6 SHCTX "$0\.shtml" ""
- ${If} "$6" != "FirefoxHTML"
- WriteRegStr SHCTX "$0\.shtml" "" "FirefoxHTML"
+ ${If} "$6" != "BasiliskHTML"
+ WriteRegStr SHCTX "$0\.shtml" "" "BasiliskHTML"
${EndIf}
ReadRegStr $6 SHCTX "$0\.xht" ""
- ${If} "$6" != "FirefoxHTML"
- WriteRegStr SHCTX "$0\.xht" "" "FirefoxHTML"
+ ${If} "$6" != "BasiliskHTML"
+ WriteRegStr SHCTX "$0\.xht" "" "BasiliskHTML"
${EndIf}
ReadRegStr $6 SHCTX "$0\.xhtml" ""
- ${If} "$6" != "FirefoxHTML"
- WriteRegStr SHCTX "$0\.xhtml" "" "FirefoxHTML"
+ ${If} "$6" != "BasiliskHTML"
+ WriteRegStr SHCTX "$0\.xhtml" "" "BasiliskHTML"
${EndIf}
${AddAssociationIfNoneExist} ".pdf"
@@ -374,12 +374,12 @@
${AddAssociationIfNoneExist} ".pdf"
${AddAssociationIfNoneExist} ".webm"
- ; An empty string is used for the 5th param because FirefoxHTML is not a
+ ; An empty string is used for the 5th param because BasiliskHTML is not a
; protocol handler
- ${AddDisabledDDEHandlerValues} "FirefoxHTML" "$2" "$8,1" \
+ ${AddDisabledDDEHandlerValues} "BasiliskHTML" "$2" "$8,1" \
"${AppRegName} HTML Document" ""
- ${AddDisabledDDEHandlerValues} "FirefoxURL" "$2" "$8,1" "${AppRegName} URL" \
+ ${AddDisabledDDEHandlerValues} "BasiliskURL" "$2" "$8,1" "${AppRegName} URL" \
"true"
; An empty string is used for the 4th & 5th params because the following
; protocol handlers already have a display name and the additional keys
@@ -449,35 +449,35 @@
WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationIcon" "$8,0"
WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationName" "${BrandShortName}"
- WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".htm" "FirefoxHTML"
- WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".html" "FirefoxHTML"
- WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".shtml" "FirefoxHTML"
- WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xht" "FirefoxHTML"
- WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "FirefoxHTML"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".htm" "BasiliskHTML"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".html" "BasiliskHTML"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".shtml" "BasiliskHTML"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xht" "BasiliskHTML"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "BasiliskHTML"
WriteRegStr ${RegKey} "$0\Capabilities\StartMenu" "StartMenuInternet" "$R9"
- WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "ftp" "FirefoxURL"
- WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http" "FirefoxURL"
- WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https" "FirefoxURL"
+ WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "ftp" "BasiliskURL"
+ WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http" "BasiliskURL"
+ WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https" "BasiliskURL"
; Vista Registered Application
WriteRegStr ${RegKey} "Software\RegisteredApplications" "${AppRegName}" "$0\Capabilities"
!macroend
!define SetStartMenuInternet "!insertmacro SetStartMenuInternet"
-; The IconHandler reference for FirefoxHTML can end up in an inconsistent state
+; The IconHandler reference for BasiliskHTML can end up in an inconsistent state
; due to changes not being detected by the IconHandler for side by side
; installs (see bug 268512). The symptoms can be either an incorrect icon or no
-; icon being displayed for files associated with Firefox (does not use SHCTX).
+; icon being displayed for files associated with Basilisk (does not use SHCTX).
!macro FixShellIconHandler RegKey
ClearErrors
- ReadRegStr $1 ${RegKey} "Software\Classes\FirefoxHTML\ShellEx\IconHandler" ""
+ ReadRegStr $1 ${RegKey} "Software\Classes\BasiliskHTML\ShellEx\IconHandler" ""
${Unless} ${Errors}
- ReadRegStr $1 ${RegKey} "Software\Classes\FirefoxHTML\DefaultIcon" ""
+ ReadRegStr $1 ${RegKey} "Software\Classes\BasiliskHTML\DefaultIcon" ""
${GetLongPath} "$INSTDIR\${FileMainEXE}" $2
${If} "$1" != "$2,1"
- WriteRegStr ${RegKey} "Software\Classes\FirefoxHTML\DefaultIcon" "" "$2,1"
+ WriteRegStr ${RegKey} "Software\Classes\BasiliskHTML\DefaultIcon" "" "$2,1"
${EndIf}
${EndUnless}
!macroend
@@ -612,7 +612,7 @@
; HKCU Software\Classes keys when associating handlers. The fix uses the merged
; view in HKCR to check for existance of an existing association. This macro
; cleans affected installations by removing the HKLM and HKCU value if it is set
-; to FirefoxHTML when there is a value for PersistentHandler or by removing the
+; to BasiliskHTML when there is a value for PersistentHandler or by removing the
; HKCU value when the HKLM value has a value other than an empty string.
!macro FixBadFileAssociation FILE_TYPE
; Only delete the default value in case the key has values for OpenWithList,
@@ -621,16 +621,16 @@
ReadRegStr $1 HKLM "Software\Classes\${FILE_TYPE}" ""
ReadRegStr $2 HKCR "${FILE_TYPE}\PersistentHandler" ""
${If} "$2" != ""
- ; Since there is a persistent handler remove FirefoxHTML as the default
- ; value from both HKCU and HKLM if it set to FirefoxHTML.
- ${If} "$0" == "FirefoxHTML"
+ ; Since there is a persistent handler remove BasiliskHTML as the default
+ ; value from both HKCU and HKLM if it set to BasiliskHTML.
+ ${If} "$0" == "BasiliskHTML"
DeleteRegValue HKCU "Software\Classes\${FILE_TYPE}" ""
${EndIf}
- ${If} "$1" == "FirefoxHTML"
+ ${If} "$1" == "BasiliskHTML"
DeleteRegValue HKLM "Software\Classes\${FILE_TYPE}" ""
${EndIf}
- ${ElseIf} "$0" == "FirefoxHTML"
- ; Since KHCU is set to FirefoxHTML remove FirefoxHTML as the default value
+ ${ElseIf} "$0" == "BasiliskHTML"
+ ; Since KHCU is set to BasiliskHTML remove BasiliskHTML as the default value
; from HKCU if HKLM is set to a value other than an empty string.
${If} "$1" != ""
DeleteRegValue HKCU "Software\Classes\${FILE_TYPE}" ""
@@ -686,17 +686,17 @@
; Only set the file and protocol handlers if the existing one under HKCR is
; for this install location.
- ${IsHandlerForInstallDir} "FirefoxHTML" $R9
+ ${IsHandlerForInstallDir} "BasiliskHTML" $R9
${If} "$R9" == "true"
- ; An empty string is used for the 5th param because FirefoxHTML is not a
+ ; An empty string is used for the 5th param because BasiliskHTML is not a
; protocol handler.
- ${AddDisabledDDEHandlerValues} "FirefoxHTML" "$2" "$8,1" \
+ ${AddDisabledDDEHandlerValues} "BasiliskHTML" "$2" "$8,1" \
"${AppRegName} HTML Document" ""
${EndIf}
- ${IsHandlerForInstallDir} "FirefoxURL" $R9
+ ${IsHandlerForInstallDir} "BasiliskURL" $R9
${If} "$R9" == "true"
- ${AddDisabledDDEHandlerValues} "FirefoxURL" "$2" "$8,1" \
+ ${AddDisabledDDEHandlerValues} "BasiliskURL" "$2" "$8,1" \
"${AppRegName} URL" "true"
${EndIf}
@@ -780,8 +780,8 @@
${RegCleanAppHandler} "chrome"
; Remove protocol handler registry keys added by the MS shim
- DeleteRegKey HKLM "Software\Classes\Firefox.URL"
- DeleteRegKey HKCU "Software\Classes\Firefox.URL"
+ DeleteRegKey HKLM "Software\Classes\Basilisk.URL"
+ DeleteRegKey HKCU "Software\Classes\Basilisk.URL"
; Delete gopher from Capabilities\URLAssociations if it is present.
${StrFilter} "${FileMainEXE}" "+" "" "" $R9
@@ -792,10 +792,10 @@
DeleteRegValue HKLM "$0\Capabilities\URLAssociations" "gopher"
${EndUnless}
- ; Delete gopher from the user's UrlAssociations if it points to FirefoxURL.
+ ; Delete gopher from the user's UrlAssociations if it points to BasiliskURL.
StrCpy $0 "Software\Microsoft\Windows\Shell\Associations\UrlAssociations\gopher"
ReadRegStr $2 HKCU "$0\UserChoice" "Progid"
- ${If} "$2" == "FirefoxURL"
+ ${If} "$2" == "BasiliskURL"
DeleteRegKey HKCU "$0"
${EndIf}
!macroend
@@ -943,7 +943,7 @@
${If} $AddTaskbarSC == ""
; No need to check the default on Win8 and later
${If} ${AtMostWin2008R2}
- ; Check if the Firefox is the http handler for this user
+ ; Check if the Basilisk is the http handler for this user
SetShellVarContext current ; Set SHCTX to the current user
${IsHandlerForInstallDir} "http" $R9
${If} $TmpVal == "HKLM"
diff --git a/browser/installer/windows/nsis/stub.nsi b/browser/installer/windows/nsis/stub.nsi
deleted file mode 100644
index 5c19c10fe..000000000
--- a/browser/installer/windows/nsis/stub.nsi
+++ /dev/null
@@ -1,2093 +0,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/.
-
-# Required Plugins:
-# AppAssocReg
-# CertCheck
-# InetBgDL
-# ShellLink
-# UAC
-
-; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
-!verbose 3
-
-SetDatablockOptimize on
-SetCompress off
-CRCCheck on
-
-RequestExecutionLevel user
-
-; The commands inside this ifdef require NSIS 3.0a2 or greater so the ifdef can
-; be removed after we require NSIS 3.0a2 or greater.
-!ifdef NSIS_PACKEDVERSION
- Unicode true
- ManifestSupportedOS all
- ManifestDPIAware true
-!endif
-
-!addplugindir ./
-
-Var Dialog
-Var Progressbar
-Var ProgressbarMarqueeIntervalMS
-Var LabelDownloading
-Var LabelInstalling
-Var LabelFreeSpace
-Var CheckboxSetAsDefault
-Var CheckboxShortcutOnBar ; Used for Quicklaunch or Taskbar as appropriate
-Var CheckboxShortcutInStartMenu
-Var CheckboxShortcutOnDesktop
-Var CheckboxSendPing
-Var CheckboxInstallMaintSvc
-Var DirRequest
-Var ButtonBrowse
-Var LabelBlurb1
-Var LabelBlurb2
-Var LabelBlurb3
-Var BitmapBlurb1
-Var BitmapBlurb2
-Var BitmapBlurb3
-Var HwndBitmapBlurb1
-Var HwndBitmapBlurb2
-Var HWndBitmapBlurb3
-
-Var FontNormal
-Var FontItalic
-Var FontBlurb
-
-Var WasOptionsButtonClicked
-Var CanWriteToInstallDir
-Var HasRequiredSpaceAvailable
-Var IsDownloadFinished
-Var DownloadSizeBytes
-Var HalfOfDownload
-Var DownloadReset
-Var ExistingTopDir
-Var SpaceAvailableBytes
-Var InitialInstallDir
-Var HandleDownload
-Var CanSetAsDefault
-Var InstallCounterStep
-Var InstallStepSize
-Var InstallTotalSteps
-Var ProgressCompleted
-Var ProgressTotal
-Var TmpVal
-
-Var ExitCode
-Var BasiliskLaunchCode
-
-; The first three tick counts are for the start of a phase and equate equate to
-; the display of individual installer pages.
-Var StartIntroPhaseTickCount
-Var StartOptionsPhaseTickCount
-Var StartDownloadPhaseTickCount
-; Since the Intro and Options pages can be displayed multiple times the total
-; seconds spent on each of these pages is reported.
-Var IntroPhaseSeconds
-Var OptionsPhaseSeconds
-; The tick count for the last download.
-Var StartLastDownloadTickCount
-; The number of seconds from the start of the download phase until the first
-; bytes are received. This is only recorded for first request so it is possible
-; to determine connection issues for the first request.
-Var DownloadFirstTransferSeconds
-; The last four tick counts are for the end of a phase in the installation page.
-Var EndDownloadPhaseTickCount
-Var EndPreInstallPhaseTickCount
-Var EndInstallPhaseTickCount
-Var EndFinishPhaseTickCount
-
-Var InitialInstallRequirementsCode
-Var ExistingProfile
-Var ExistingVersion
-Var ExistingBuildID
-Var DownloadedBytes
-Var DownloadRetryCount
-Var OpenedDownloadPage
-Var DownloadServerIP
-Var PostSigningData
-
-Var ControlHeightPX
-Var ControlRightPX
-
-; Uncomment the following to prevent pinging the metrics server when testing
-; the stub installer
-;!define STUB_DEBUG
-
-!define StubURLVersion "v7"
-
-; Successful install exit code
-!define ERR_SUCCESS 0
-
-/**
- * The following errors prefixed with ERR_DOWNLOAD apply to the download phase.
- */
-; The download was cancelled by the user
-!define ERR_DOWNLOAD_CANCEL 10
-
-; Too many attempts to download. The maximum attempts is defined in
-; DownloadMaxRetries.
-!define ERR_DOWNLOAD_TOO_MANY_RETRIES 11
-
-/**
- * The following errors prefixed with ERR_PREINSTALL apply to the pre-install
- * check phase.
- */
-; Unable to acquire a file handle to the downloaded file
-!define ERR_PREINSTALL_INVALID_HANDLE 20
-
-; The downloaded file's certificate is not trusted by the certificate store.
-!define ERR_PREINSTALL_CERT_UNTRUSTED 21
-
-; The downloaded file's certificate attribute values were incorrect.
-!define ERR_PREINSTALL_CERT_ATTRIBUTES 22
-
-; The downloaded file's certificate is not trusted by the certificate store and
-; certificate attribute values were incorrect.
-!define ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES 23
-
-/**
- * The following errors prefixed with ERR_INSTALL apply to the install phase.
- */
-; The installation timed out. The installation timeout is defined by the number
-; of progress steps defined in InstallTotalSteps and the install timer
-; interval defined in InstallIntervalMS
-!define ERR_INSTALL_TIMEOUT 30
-
-; Maximum times to retry the download before displaying an error
-!define DownloadMaxRetries 9
-
-; Minimum size expected to download in bytes
-!define DownloadMinSizeBytes 15728640 ; 15 MB
-
-; Maximum size expected to download in bytes
-!define DownloadMaxSizeBytes 73400320 ; 70 MB
-
-; Interval before retrying to download. 3 seconds is used along with 10
-; attempted downloads (the first attempt along with 9 retries) to give a
-; minimum of 30 seconds or retrying before giving up.
-!define DownloadRetryIntervalMS 3000
-
-; Interval for the download timer
-!define DownloadIntervalMS 200
-
-; Interval for the install timer
-!define InstallIntervalMS 100
-
-; The first step for the install progress bar. By starting with a large step
-; immediate feedback is given to the user.
-!define InstallProgressFirstStep 20
-
-; The finish step size to quickly increment the progress bar after the
-; installation has finished.
-!define InstallProgressFinishStep 40
-
-; Number of steps for the install progress.
-; This might not be enough when installing on a slow network drive so it will
-; fallback to downloading the full installer if it reaches this number. The size
-; of the install progress step is increased when the full installer finishes
-; instead of waiting.
-
-; Approximately 150 seconds with a 100 millisecond timer and a first step of 20
-; as defined by InstallProgressFirstStep.
-!define /math InstallCleanTotalSteps ${InstallProgressFirstStep} + 1500
-
-; Approximately 165 seconds (minus 0.2 seconds for each file that is removed)
-; with a 100 millisecond timer and a first step of 20 as defined by
-; InstallProgressFirstStep .
-!define /math InstallPaveOverTotalSteps ${InstallProgressFirstStep} + 1800
-
-; On Vista and above attempt to elevate Standard Users in addition to users that
-; are a member of the Administrators group.
-!define NONADMIN_ELEVATE
-
-!define CONFIG_INI "config.ini"
-
-!ifndef FILE_SHARE_READ
- !define FILE_SHARE_READ 1
-!endif
-!ifndef GENERIC_READ
- !define GENERIC_READ 0x80000000
-!endif
-!ifndef OPEN_EXISTING
- !define OPEN_EXISTING 3
-!endif
-!ifndef INVALID_HANDLE_VALUE
- !define INVALID_HANDLE_VALUE -1
-!endif
-
-!include "nsDialogs.nsh"
-!include "LogicLib.nsh"
-!include "FileFunc.nsh"
-!include "TextFunc.nsh"
-!include "WinVer.nsh"
-!include "WordFunc.nsh"
-
-!insertmacro GetParameters
-!insertmacro GetOptions
-!insertmacro LineFind
-!insertmacro StrFilter
-
-!include "locales.nsi"
-!include "branding.nsi"
-
-!include "defines.nsi"
-
-; Must be included after defines.nsi
-!include "locale-fonts.nsh"
-
-; The OFFICIAL define is a workaround to support different urls for Release and
-; Beta since they share the same branding when building with other branches that
-; set the update channel to beta.
-!ifdef OFFICIAL
-!ifdef BETA_UPDATE_CHANNEL
-!undef URLStubDownload
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
-!undef URLManualDownload
-!define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=beta&installer_lang=${AB_CD}"
-!undef Channel
-!define Channel "beta"
-!endif
-!endif
-
-!include "common.nsh"
-
-!insertmacro ElevateUAC
-!insertmacro GetLongPath
-!insertmacro GetPathFromString
-!insertmacro GetParent
-!insertmacro GetSingleInstallPath
-!insertmacro GetTextWidthHeight
-!insertmacro IsUserAdmin
-!insertmacro RemovePrecompleteEntries
-!insertmacro SetBrandNameVars
-!insertmacro ITBL3Create
-!insertmacro UnloadUAC
-
-VIAddVersionKey "FileDescription" "${BrandShortName} Stub Installer"
-VIAddVersionKey "OriginalFilename" "setup-stub.exe"
-
-Name "$BrandFullName"
-OutFile "setup-stub.exe"
-icon "setup.ico"
-XPStyle on
-BrandingText " "
-ChangeUI all "nsisui.exe"
-!ifdef HAVE_64BIT_BUILD
- InstallDir "$PROGRAMFILES64\${BrandFullName}\"
-!else
- InstallDir "$PROGRAMFILES32\${BrandFullName}\"
-!endif
-
-!ifdef ${AB_CD}_rtl
- LoadLanguageFile "locale-rtl.nlf"
-!else
- LoadLanguageFile "locale.nlf"
-!endif
-
-!include "nsisstrings.nlf"
-
-!if "${AB_CD}" == "en-US"
- ; Custom strings for en-US. This is done here so they aren't translated.
- !include oneoff_en-US.nsh
-!else
- !define INTRO_BLURB "$(INTRO_BLURB1)"
- !define INSTALL_BLURB1 "$(INSTALL_BLURB1)"
- !define INSTALL_BLURB2 "$(INSTALL_BLURB2)"
- !define INSTALL_BLURB3 "$(INSTALL_BLURB3)"
-!endif
-
-Caption "$(WIN_CAPTION)"
-
-Page custom createDummy ; Needed to enable the Intro page's back button
-Page custom createIntro leaveIntro ; Introduction page
-Page custom createOptions leaveOptions ; Options page
-Page custom createInstall ; Download / Installation page
-
-Function .onInit
- ; Remove the current exe directory from the search order.
- ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
- System::Call 'kernel32::SetDllDirectoryW(w "")'
-
- StrCpy $LANGUAGE 0
- ; This macro is used to set the brand name variables but the ini file method
- ; isn't supported for the stub installer.
- ${SetBrandNameVars} "$PLUGINSDIR\ignored.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.
- System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R7"
-
-!ifdef HAVE_64BIT_BUILD
- ; Restrict x64 builds from being installed on x86 and pre Win7
- ${Unless} ${RunningX64}
- ${OrUnless} ${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}
-
- SetRegView 64
-!else
- StrCpy $R8 "0"
- ${If} ${AtMostWin2000}
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} ${IsWinXP}
- ${AndIf} ${AtMostServicePack} 1
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} $R8 == "1"
- ; XXX-rstrong - some systems failed the AtLeastWin2000 test that we
- ; used to use for an unknown reason and likely fail the AtMostWin2000
- ; and possibly the IsWinXP test as well. To work around this also
- ; check if the Windows NT registry Key exists and if it does if the
- ; first char in CurrentVersion is equal to 3 (Windows NT 3.5 and
- ; 3.5.1), 4 (Windows NT 4), or 5 (Windows 2000 and Windows XP).
- StrCpy $R8 ""
- ClearErrors
- ReadRegStr $R8 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
- StrCpy $R8 "$R8" 1
- ${If} ${Errors}
- ${OrIf} "$R8" == "3"
- ${OrIf} "$R8" == "4"
- ${OrIf} "$R8" == "5"
- ${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
- ${EndIf}
- ${EndUnless}
-!endif
-
- ${If} "$R7" == "0"
- MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
- ExecShell "open" "${URLSystemRequirements}"
- Quit
- ${EndIf}
-
- ; Require elevation if the user can elevate
- ${ElevateUAC}
-
-; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
-; removed after we require NSIS 3.0a2 or greater.
-!ifndef NSIS_PACKEDVERSION
- ${If} ${AtLeastWinVista}
- System::Call 'user32::SetProcessDPIAware()'
- ${EndIf}
-!endif
-
- SetShellVarContext all ; Set SHCTX to HKLM
- ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
-
- ${If} "$R9" == "false"
- SetShellVarContext current ; Set SHCTX to HKCU
- ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
-
- ${If} ${RunningX64}
- ; In HKCU there is no WOW64 redirection, which means we may have gotten
- ; the path to a 32-bit install even though we're 64-bit, or vice-versa.
- ; In that case, just use the default path instead of offering an upgrade.
- ; But only do that override if the existing install is in Program Files,
- ; because that's the only place we can be sure is specific
- ; to either 32 or 64 bit applications.
- ; The WordFind syntax below searches for the first occurence of the
- ; "delimiter" (the Program Files path) in the install path and returns
- ; anything that appears before that. If nothing appears before that,
- ; then the install is under Program Files (32 or 64).
-!ifdef HAVE_64BIT_BUILD
- ${WordFind} $R9 $PROGRAMFILES32 "+1{" $0
-!else
- ${WordFind} $R9 $PROGRAMFILES64 "+1{" $0
-!endif
- ${If} $0 == ""
- StrCpy $R9 "false"
- ${EndIf}
- ${EndIf}
- ${EndIf}
-
- ${If} "$R9" != "false"
- StrCpy $INSTDIR "$R9"
- ${EndIf}
-
- ; Used to determine if the default installation directory was used.
- StrCpy $InitialInstallDir "$INSTDIR"
-
- ClearErrors
- WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
- "Write Test"
-
- ; Only display set as default when there is write access to HKLM and on Win7
- ; and below.
- ${If} ${Errors}
- ${OrIf} ${AtLeastWin8}
- StrCpy $CanSetAsDefault "false"
- StrCpy $CheckboxSetAsDefault "0"
- ${Else}
- DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
- StrCpy $CanSetAsDefault "true"
- ${EndIf}
-
- ; The interval in MS used for the progress bars set as marquee.
- ${If} ${AtLeastWinVista}
- StrCpy $ProgressbarMarqueeIntervalMS "10"
- ${Else}
- StrCpy $ProgressbarMarqueeIntervalMS "50"
- ${EndIf}
-
- ; Initialize the majority of variables except those that need to be reset
- ; when a page is displayed.
- StrCpy $IntroPhaseSeconds "0"
- StrCpy $OptionsPhaseSeconds "0"
- StrCpy $EndPreInstallPhaseTickCount "0"
- StrCpy $EndInstallPhaseTickCount "0"
- StrCpy $InitialInstallRequirementsCode ""
- StrCpy $IsDownloadFinished ""
- StrCpy $BasiliskLaunchCode "0"
- StrCpy $CheckboxShortcutOnBar "1"
- StrCpy $CheckboxShortcutInStartMenu "1"
- StrCpy $CheckboxShortcutOnDesktop "1"
- StrCpy $CheckboxSendPing "1"
-!ifdef MOZ_MAINTENANCE_SERVICE
- StrCpy $CheckboxInstallMaintSvc "1"
-!else
- StrCpy $CheckboxInstallMaintSvc "0"
-!endif
- StrCpy $WasOptionsButtonClicked "0"
-
- StrCpy $0 ""
-!ifdef FONT_FILE1
- ${If} ${FileExists} "$FONTS\${FONT_FILE1}"
- StrCpy $0 "${FONT_NAME1}"
- ${EndIf}
-!endif
-
-!ifdef FONT_FILE2
- ${If} $0 == ""
- ${AndIf} ${FileExists} "$FONTS\${FONT_FILE2}"
- StrCpy $0 "${FONT_NAME2}"
- ${EndIf}
-!endif
-
- ${If} $0 == ""
- StrCpy $0 "$(^Font)"
- ${EndIf}
-
- CreateFont $FontBlurb "$0" "12" "500"
- CreateFont $FontNormal "$0" "11" "500"
- CreateFont $FontItalic "$0" "11" "500" /ITALIC
-
- InitPluginsDir
- File /oname=$PLUGINSDIR\bgintro.bmp "bgintro.bmp"
- File /oname=$PLUGINSDIR\appname.bmp "appname.bmp"
- File /oname=$PLUGINSDIR\clock.bmp "clock.bmp"
- File /oname=$PLUGINSDIR\particles.bmp "particles.bmp"
-!ifdef ${AB_CD}_rtl
- ; The horizontally flipped pencil looks better in RTL
- File /oname=$PLUGINSDIR\pencil.bmp "pencil-rtl.bmp"
-!else
- File /oname=$PLUGINSDIR\pencil.bmp "pencil.bmp"
-!endif
-FunctionEnd
-
-; .onGUIInit isn't needed except for RTL locales
-!ifdef ${AB_CD}_rtl
-Function .onGUIInit
- ; Since NSIS RTL support doesn't mirror progress bars use Windows mirroring.
- ${NSD_AddExStyle} $HWNDPARENT ${WS_EX_LAYOUTRTL}
- ${RemoveExStyle} $HWNDPARENT ${WS_EX_RTLREADING}
- ${RemoveExStyle} $HWNDPARENT ${WS_EX_RIGHT}
- ${NSD_AddExStyle} $HWNDPARENT ${WS_EX_LEFT}|${WS_EX_LTRREADING}
-FunctionEnd
-!endif
-
-Function .onGUIEnd
- Delete "$PLUGINSDIR\_temp"
- Delete "$PLUGINSDIR\download.exe"
- Delete "$PLUGINSDIR\${CONFIG_INI}"
-
- ${UnloadUAC}
-FunctionEnd
-
-Function .onUserAbort
- ${NSD_KillTimer} StartDownload
- ${NSD_KillTimer} OnDownload
- ${NSD_KillTimer} CheckInstall
- ${NSD_KillTimer} FinishInstall
- ${NSD_KillTimer} FinishProgressBar
- ${NSD_KillTimer} DisplayDownloadError
-
- ${If} "$IsDownloadFinished" != ""
- Call DisplayDownloadError
- ; Aborting the abort will allow SendPing which is called by
- ; DisplayDownloadError to hide the installer window and close the installer
- ; after it sends the metrics ping.
- Abort
- ${EndIf}
-FunctionEnd
-
-Function SendPing
- HideWindow
- ; Try to send a ping if a download was attempted
- ${If} $CheckboxSendPing == 1
- ${AndIf} $IsDownloadFinished != ""
- ; Get the tick count for the completion of all phases.
- System::Call "kernel32::GetTickCount()l .s"
- Pop $EndFinishPhaseTickCount
-
- ; When the value of $IsDownloadFinished is false the download was started
- ; but didn't finish. In this case the tick count stored in
- ; $EndFinishPhaseTickCount is used to determine how long the download was
- ; in progress.
- ${If} "$IsDownloadFinished" == "false"
- ${OrIf} "$EndDownloadPhaseTickCount" == ""
- StrCpy $EndDownloadPhaseTickCount "$EndFinishPhaseTickCount"
- ; Cancel the download in progress
- InetBgDL::Get /RESET /END
- ${EndIf}
-
-
- ; When $DownloadFirstTransferSeconds equals an empty string the download
- ; never successfully started so set the value to 0. It will be possible to
- ; determine that the download didn't successfully start from the seconds for
- ; the last download.
- ${If} "$DownloadFirstTransferSeconds" == ""
- StrCpy $DownloadFirstTransferSeconds "0"
- ${EndIf}
-
- ; When $StartLastDownloadTickCount equals an empty string the download never
- ; successfully started so set the value to $EndDownloadPhaseTickCount to
- ; compute the correct value.
- ${If} $StartLastDownloadTickCount == ""
- ; This could happen if the download never successfully starts
- StrCpy $StartLastDownloadTickCount "$EndDownloadPhaseTickCount"
- ${EndIf}
-
- ; When $EndPreInstallPhaseTickCount equals 0 the installation phase was
- ; never completed so set its value to $EndFinishPhaseTickCount to compute
- ; the correct value.
- ${If} "$EndPreInstallPhaseTickCount" == "0"
- StrCpy $EndPreInstallPhaseTickCount "$EndFinishPhaseTickCount"
- ${EndIf}
-
- ; When $EndInstallPhaseTickCount equals 0 the installation phase was never
- ; completed so set its value to $EndFinishPhaseTickCount to compute the
- ; correct value.
- ${If} "$EndInstallPhaseTickCount" == "0"
- StrCpy $EndInstallPhaseTickCount "$EndFinishPhaseTickCount"
- ${EndIf}
-
- ; Get the seconds elapsed from the start of the download phase to the end of
- ; the download phase.
- ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$EndDownloadPhaseTickCount" $0
-
- ; Get the seconds elapsed from the start of the last download to the end of
- ; the last download.
- ${GetSecondsElapsed} "$StartLastDownloadTickCount" "$EndDownloadPhaseTickCount" $1
-
- ; Get the seconds elapsed from the end of the download phase to the
- ; completion of the pre-installation check phase.
- ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $2
-
- ; Get the seconds elapsed from the end of the pre-installation check phase
- ; to the completion of the installation phase.
- ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3
-
- ; Get the seconds elapsed from the end of the installation phase to the
- ; completion of all phases.
- ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4
-
-!ifdef HAVE_64BIT_BUILD
- StrCpy $R0 "1"
-!else
- StrCpy $R0 "0"
-!endif
-
- ${If} ${RunningX64}
- StrCpy $R1 "1"
- ${Else}
- StrCpy $R1 "0"
- ${EndIf}
-
- ; Though these values are sometimes incorrect due to bug 444664 it happens
- ; so rarely it isn't worth working around it by reading the registry values.
- ${WinVerGetMajor} $5
- ${WinVerGetMinor} $6
- ${WinVerGetBuild} $7
- ${WinVerGetServicePackLevel} $8
- ${If} ${IsServerOS}
- StrCpy $9 "1"
- ${Else}
- StrCpy $9 "0"
- ${EndIf}
-
- ${If} "$ExitCode" == "${ERR_SUCCESS}"
- ReadINIStr $R5 "$INSTDIR\application.ini" "App" "Version"
- ReadINIStr $R6 "$INSTDIR\application.ini" "App" "BuildID"
- ${Else}
- StrCpy $R5 "0"
- StrCpy $R6 "0"
- ${EndIf}
-
- ; Whether installed into the default installation directory
- ${GetLongPath} "$INSTDIR" $R7
- ${GetLongPath} "$InitialInstallDir" $R8
- ${If} "$R7" == "$R8"
- StrCpy $R7 "1"
- ${Else}
- StrCpy $R7 "0"
- ${EndIf}
-
- ClearErrors
- WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
- "Write Test"
- ${If} ${Errors}
- StrCpy $R8 "0"
- ${Else}
- DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} "$DownloadServerIP" == ""
- StrCpy $DownloadServerIP "Unknown"
- ${EndIf}
-
- StrCpy $R2 ""
- SetShellVarContext current ; Set SHCTX to the current user
- ReadRegStr $R2 HKCU "Software\Classes\http\shell\open\command" ""
- ${If} $R2 != ""
- ${GetPathFromString} "$R2" $R2
- ${GetParent} "$R2" $R3
- ${GetLongPath} "$R3" $R3
- ${If} $R3 == $INSTDIR
- StrCpy $R2 "1" ; This Basilisk install is set as default.
- ${Else}
- StrCpy $R2 "$R2" "" -11 # length of firefox.exe
- ${If} "$R2" == "${FileMainEXE}"
- StrCpy $R2 "2" ; Another Basilisk install is set as default.
- ${Else}
- StrCpy $R2 "0"
- ${EndIf}
- ${EndIf}
- ${Else}
- StrCpy $R2 "0" ; Basilisk is not set as default.
- ${EndIf}
-
- ${If} "$R2" == "0"
- ${AndIf} ${AtLeastWinVista}
- ; Check to see if this install location is currently set as the default
- ; browser by Default Programs which is only available on Vista and above.
- ClearErrors
- ReadRegStr $R3 HKLM "Software\RegisteredApplications" "${AppRegName}"
- ${Unless} ${Errors}
- AppAssocReg::QueryAppIsDefaultAll "${AppRegName}" "effective"
- Pop $R3
- ${If} $R3 == "1"
- StrCpy $R3 ""
- ReadRegStr $R2 HKLM "Software\Classes\http\shell\open\command" ""
- ${If} $R2 != ""
- ${GetPathFromString} "$R2" $R2
- ${GetParent} "$R2" $R3
- ${GetLongPath} "$R3" $R3
- ${If} $R3 == $INSTDIR
- StrCpy $R2 "1" ; This Basilisk install is set as default.
- ${Else}
- StrCpy $R2 "$R2" "" -11 # length of firefox.exe
- ${If} "$R2" == "${FileMainEXE}"
- StrCpy $R2 "2" ; Another Basilisk install is set as default.
- ${Else}
- StrCpy $R2 "0"
- ${EndIf}
- ${EndIf}
- ${Else}
- StrCpy $R2 "0" ; Basilisk is not set as default.
- ${EndIf}
- ${EndIf}
- ${EndUnless}
- ${EndIf}
-
- ${If} $CanSetAsDefault == "true"
- ${If} $CheckboxSetAsDefault == "1"
- StrCpy $R3 "2"
- ${Else}
- StrCpy $R3 "3"
- ${EndIf}
- ${Else}
- ${If} ${AtLeastWin8}
- StrCpy $R3 "1"
- ${Else}
- StrCpy $R3 "0"
- ${EndIf}
- ${EndIf}
-
-!ifdef STUB_DEBUG
- MessageBox MB_OK "${BaseURLStubPing} \
- $\nStub URL Version = ${StubURLVersion}${StubURLVersionAppend} \
- $\nBuild Channel = ${Channel} \
- $\nUpdate Channel = ${UpdateChannel} \
- $\nLocale = ${AB_CD} \
- $\nBasilisk x64 = $R0 \
- $\nRunning x64 Windows = $R1 \
- $\nMajor = $5 \
- $\nMinor = $6 \
- $\nBuild = $7 \
- $\nServicePack = $8 \
- $\nIsServer = $9 \
- $\nExit Code = $ExitCode \
- $\nBasilisk Launch Code = $BasiliskLaunchCode \
- $\nDownload Retry Count = $DownloadRetryCount \
- $\nDownloaded Bytes = $DownloadedBytes \
- $\nDownload Size Bytes = $DownloadSizeBytes \
- $\nIntroduction Phase Seconds = $IntroPhaseSeconds \
- $\nOptions Phase Seconds = $OptionsPhaseSeconds \
- $\nDownload Phase Seconds = $0 \
- $\nLast Download Seconds = $1 \
- $\nDownload First Transfer Seconds = $DownloadFirstTransferSeconds \
- $\nPreinstall Phase Seconds = $2 \
- $\nInstall Phase Seconds = $3 \
- $\nFinish Phase Seconds = $4 \
- $\nInitial Install Requirements Code = $InitialInstallRequirementsCode \
- $\nOpened Download Page = $OpenedDownloadPage \
- $\nExisting Profile = $ExistingProfile \
- $\nExisting Version = $ExistingVersion \
- $\nExisting Build ID = $ExistingBuildID \
- $\nNew Version = $R5 \
- $\nNew Build ID = $R6 \
- $\nDefault Install Dir = $R7 \
- $\nHas Admin = $R8 \
- $\nDefault Status = $R2 \
- $\nSet As Sefault Status = $R3 \
- $\nDownload Server IP = $DownloadServerIP \
- $\nPost-Signing Data = $PostSigningData"
- ; The following will exit the installer
- SetAutoClose true
- StrCpy $R9 "2"
- Call RelativeGotoPage
-!else
- ${NSD_CreateTimer} OnPing ${DownloadIntervalMS}
- InetBgDL::Get "${BaseURLStubPing}/${StubURLVersion}${StubURLVersionAppend}/${Channel}/${UpdateChannel}/${AB_CD}/$R0/$R1/$5/$6/$7/$8/$9/$ExitCode/$BasiliskLaunchCode/$DownloadRetryCount/$DownloadedBytes/$DownloadSizeBytes/$IntroPhaseSeconds/$OptionsPhaseSeconds/$0/$1/$DownloadFirstTransferSeconds/$2/$3/$4/$InitialInstallRequirementsCode/$OpenedDownloadPage/$ExistingProfile/$ExistingVersion/$ExistingBuildID/$R5/$R6/$R7/$R8/$R2/$R3/$DownloadServerIP/$PostSigningData" \
- "$PLUGINSDIR\_temp" /END
-!endif
- ${Else}
- ${If} "$IsDownloadFinished" == "false"
- ; Cancel the download in progress
- InetBgDL::Get /RESET /END
- ${EndIf}
- ; The following will exit the installer
- SetAutoClose true
- StrCpy $R9 "2"
- Call RelativeGotoPage
- ${EndIf}
-FunctionEnd
-
-Function createDummy
-FunctionEnd
-
-Function createIntro
- nsDialogs::Create /NOUNLOAD 1018
- Pop $Dialog
-
- GetFunctionAddress $0 OnBack
- nsDialogs::OnBack /NOUNLOAD $0
-
-!ifdef ${AB_CD}_rtl
- ; For RTL align the text with the top of the F in the Basilisk bitmap
- StrCpy $0 "${INTRO_BLURB_RTL_TOP_DU}"
-!else
- ; For LTR align the text with the top of the x in the Basilisk bitmap
- StrCpy $0 "${INTRO_BLURB_LTR_TOP_DU}"
-!endif
- ${NSD_CreateLabel} ${INTRO_BLURB_EDGE_DU} $0 ${INTRO_BLURB_WIDTH_DU} 76u "${INTRO_BLURB}"
- Pop $0
- SendMessage $0 ${WM_SETFONT} $FontBlurb 0
- SetCtlColors $0 ${INTRO_BLURB_TEXT_COLOR} transparent
-
- SetCtlColors $HWNDPARENT ${FOOTER_CONTROL_TEXT_COLOR_NORMAL} ${FOOTER_BKGRD_COLOR}
- GetDlgItem $0 $HWNDPARENT 10 ; Default browser checkbox
- ${If} "$CanSetAsDefault" == "true"
- ; The uxtheme must be disabled on checkboxes in order to override the
- ; system font color.
- System::Call 'uxtheme::SetWindowTheme(i $0 , w " ", w " ")'
- SendMessage $0 ${WM_SETFONT} $FontNormal 0
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(MAKE_DEFAULT)"
- SendMessage $0 ${BM_SETCHECK} 1 0
- SetCtlColors $0 ${FOOTER_CONTROL_TEXT_COLOR_NORMAL} ${FOOTER_BKGRD_COLOR}
- ${Else}
- ShowWindow $0 ${SW_HIDE}
- ${EndIf}
- GetDlgItem $0 $HWNDPARENT 11
- ShowWindow $0 ${SW_HIDE}
-
- ${NSD_CreateBitmap} ${APPNAME_BMP_EDGE_DU} ${APPNAME_BMP_TOP_DU} \
- ${APPNAME_BMP_WIDTH_DU} ${APPNAME_BMP_HEIGHT_DU} ""
- Pop $2
- ${SetStretchedTransparentImage} $2 $PLUGINSDIR\appname.bmp $0
-
- ${NSD_CreateBitmap} 0 0 100% 100% ""
- Pop $2
- ${NSD_SetStretchedImage} $2 $PLUGINSDIR\bgintro.bmp $1
-
- GetDlgItem $0 $HWNDPARENT 1 ; Install button
- ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(UPGRADE_BUTTON)"
- ${Else}
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(INSTALL_BUTTON)"
- ${EndIf}
- ${NSD_SetFocus} $0
-
- GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(CANCEL_BUTTON)"
-
- GetDlgItem $0 $HWNDPARENT 3 ; Back button used for Options
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(OPTIONS_BUTTON)"
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $StartIntroPhaseTickCount
-
- LockWindow off
- nsDialogs::Show
-
- ${NSD_FreeImage} $0
- ${NSD_FreeImage} $1
-FunctionEnd
-
-Function leaveIntro
- LockWindow on
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $0
- ${GetSecondsElapsed} "$StartIntroPhaseTickCount" "$0" $IntroPhaseSeconds
- ; It is possible for this value to be 0 if the user clicks fast enough so
- ; increment the value by 1 if it is 0.
- ${If} $IntroPhaseSeconds == 0
- IntOp $IntroPhaseSeconds $IntroPhaseSeconds + 1
- ${EndIf}
-
- SetShellVarContext all ; Set SHCTX to All Users
- ; If the user doesn't have write access to the installation directory set
- ; the installation directory to a subdirectory of the All Users application
- ; directory and if the user can't write to that location set the installation
- ; directory to a subdirectory of the users local application directory
- ; (e.g. non-roaming).
- Call CanWrite
- ${If} "$CanWriteToInstallDir" == "false"
- StrCpy $INSTDIR "$APPDATA\${BrandFullName}\"
- Call CanWrite
- ${If} "$CanWriteToInstallDir" == "false"
- ; This should never happen but just in case.
- StrCpy $CanWriteToInstallDir "false"
- ${Else}
- StrCpy $INSTDIR "$LOCALAPPDATA\${BrandFullName}\"
- Call CanWrite
- ${EndIf}
- ${EndIf}
-
- Call CheckSpace
-
- ${If} ${FileExists} "$INSTDIR"
- ; Always display the long path if the path exists.
- ${GetLongPath} "$INSTDIR" $INSTDIR
- ${EndIf}
-
-FunctionEnd
-
-Function createOptions
- ; Check whether the install requirements are satisfied using the default
- ; values for metrics.
- ${If} "$InitialInstallRequirementsCode" == ""
- ${If} "$CanWriteToInstallDir" != "true"
- ${AndIf} "$HasRequiredSpaceAvailable" != "true"
- StrCpy $InitialInstallRequirementsCode "1"
- ${ElseIf} "$CanWriteToInstallDir" != "true"
- StrCpy $InitialInstallRequirementsCode "2"
- ${ElseIf} "$HasRequiredSpaceAvailable" != "true"
- StrCpy $InitialInstallRequirementsCode "3"
- ${Else}
- StrCpy $InitialInstallRequirementsCode "0"
- ${EndIf}
- ${EndIf}
-
- ; Skip the options page unless the Options button was clicked as long as the
- ; installation directory can be written to and there is the minimum required
- ; space available.
- ${If} "$WasOptionsButtonClicked" != "1"
- ${If} "$CanWriteToInstallDir" == "true"
- ${AndIf} "$HasRequiredSpaceAvailable" == "true"
- Abort ; Skip the options page
- ${EndIf}
- ${EndIf}
-
- StrCpy $ExistingTopDir ""
-
- nsDialogs::Create /NOUNLOAD 1018
- Pop $Dialog
- ; Since the text color for controls is set in this Dialog the foreground and
- ; background colors of the Dialog must also be hardcoded.
- SetCtlColors $Dialog ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-
- ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 18u ${OPTIONS_ITEM_WIDTH_DU} \
- 12u "$(CREATE_SHORTCUTS)"
- Pop $0
- SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $0 ${WM_SETFONT} $FontNormal 0
-
- ${If} ${AtLeastWin7}
- StrCpy $0 "$(ADD_SC_TASKBAR)"
- ${Else}
- StrCpy $0 "$(ADD_SC_QUICKLAUNCHBAR)"
- ${EndIf}
- ${NSD_CreateCheckbox} ${OPTIONS_SUBITEM_EDGE_DU} 38u \
- ${OPTIONS_SUBITEM_WIDTH_DU} 12u "$0"
- Pop $CheckboxShortcutOnBar
- ; The uxtheme must be disabled on checkboxes in order to override the system
- ; font color.
- System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutOnBar, w " ", w " ")'
- SetCtlColors $CheckboxShortcutOnBar ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $CheckboxShortcutOnBar ${WM_SETFONT} $FontNormal 0
- ${NSD_Check} $CheckboxShortcutOnBar
-
- ${NSD_CreateCheckbox} ${OPTIONS_SUBITEM_EDGE_DU} 54u ${OPTIONS_SUBITEM_WIDTH_DU} \
- 12u "$(ADD_CheckboxShortcutInStartMenu)"
- Pop $CheckboxShortcutInStartMenu
- ; The uxtheme must be disabled on checkboxes in order to override the system
- ; font color.
- System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutInStartMenu, w " ", w " ")'
- SetCtlColors $CheckboxShortcutInStartMenu ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $CheckboxShortcutInStartMenu ${WM_SETFONT} $FontNormal 0
- ${NSD_Check} $CheckboxShortcutInStartMenu
-
- ${NSD_CreateCheckbox} ${OPTIONS_SUBITEM_EDGE_DU} 70u ${OPTIONS_SUBITEM_WIDTH_DU} \
- 12u "$(ADD_CheckboxShortcutOnDesktop)"
- Pop $CheckboxShortcutOnDesktop
- ; The uxtheme must be disabled on checkboxes in order to override the system
- ; font color.
- System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutOnDesktop, w " ", w " ")'
- SetCtlColors $CheckboxShortcutOnDesktop ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $CheckboxShortcutOnDesktop ${WM_SETFONT} $FontNormal 0
- ${NSD_Check} $CheckboxShortcutOnDesktop
-
- ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 100u ${OPTIONS_ITEM_WIDTH_DU} \
- 12u "$(DEST_FOLDER)"
- Pop $0
- SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $0 ${WM_SETFONT} $FontNormal 0
-
- ${NSD_CreateDirRequest} ${OPTIONS_SUBITEM_EDGE_DU} 116u 159u 14u "$INSTDIR"
- Pop $DirRequest
- SetCtlColors $DirRequest ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $DirRequest ${WM_SETFONT} $FontNormal 0
- System::Call shlwapi::SHAutoComplete(i $DirRequest, i ${SHACF_FILESYSTEM})
- ${NSD_OnChange} $DirRequest OnChange_DirRequest
-
-!ifdef ${AB_CD}_rtl
- ; Remove the RTL styling from the directory request text box
- ${RemoveStyle} $DirRequest ${SS_RIGHT}
- ${RemoveExStyle} $DirRequest ${WS_EX_RIGHT}
- ${RemoveExStyle} $DirRequest ${WS_EX_RTLREADING}
- ${NSD_AddStyle} $DirRequest ${SS_LEFT}
- ${NSD_AddExStyle} $DirRequest ${WS_EX_LTRREADING}|${WS_EX_LEFT}
-!endif
-
- ${NSD_CreateBrowseButton} 280u 116u 50u 14u "$(BROWSE_BUTTON)"
- Pop $ButtonBrowse
- SetCtlColors $ButtonBrowse "" ${COMMON_BKGRD_COLOR}
- ${NSD_OnClick} $ButtonBrowse OnClick_ButtonBrowse
-
- ; Get the number of pixels from the left of the Dialog to the right side of
- ; the "Space Required:" and "Space Available:" labels prior to setting RTL so
- ; the correct position of the controls can be set by NSIS for RTL locales.
-
- ; Get the width and height of both labels and use the tallest for the height
- ; and the widest to calculate where to place the labels after these labels.
- ${GetTextExtent} "$(SPACE_REQUIRED)" $FontItalic $0 $1
- ${GetTextExtent} "$(SPACE_AVAILABLE)" $FontItalic $2 $3
- ${If} $1 > $3
- StrCpy $ControlHeightPX "$1"
- ${Else}
- StrCpy $ControlHeightPX "$3"
- ${EndIf}
-
- IntOp $0 $0 + 8 ; Add padding to the control's width
- ; Make both controls the same width as the widest control
- ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 134u $0 $ControlHeightPX "$(SPACE_REQUIRED)"
- Pop $5
- SetCtlColors $5 ${COMMON_TEXT_COLOR_FADED} ${COMMON_BKGRD_COLOR}
- SendMessage $5 ${WM_SETFONT} $FontItalic 0
-
- IntOp $2 $2 + 8 ; Add padding to the control's width
- ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 145u $2 $ControlHeightPX "$(SPACE_AVAILABLE)"
- Pop $6
- SetCtlColors $6 ${COMMON_TEXT_COLOR_FADED} ${COMMON_BKGRD_COLOR}
- SendMessage $6 ${WM_SETFONT} $FontItalic 0
-
- ; Use the widest label for aligning the labels next to them
- ${If} $0 > $2
- StrCpy $6 "$5"
- ${EndIf}
- FindWindow $1 "#32770" "" $HWNDPARENT
- ${GetDlgItemEndPX} $6 $ControlRightPX
-
- IntOp $ControlRightPX $ControlRightPX + 6
-
- ${NSD_CreateLabel} $ControlRightPX 134u 100% $ControlHeightPX \
- "${APPROXIMATE_REQUIRED_SPACE_MB} $(MEGA)$(BYTE)"
- Pop $7
- SetCtlColors $7 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $7 ${WM_SETFONT} $FontNormal 0
-
- ; Create the free space label with an empty string and update it by calling
- ; UpdateFreeSpaceLabel
- ${NSD_CreateLabel} $ControlRightPX 145u 100% $ControlHeightPX " "
- Pop $LabelFreeSpace
- SetCtlColors $LabelFreeSpace ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $LabelFreeSpace ${WM_SETFONT} $FontNormal 0
-
- Call UpdateFreeSpaceLabel
-
- ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 168u ${OPTIONS_SUBITEM_WIDTH_DU} \
- 12u "$(SEND_PING)"
- Pop $CheckboxSendPing
- ; The uxtheme must be disabled on checkboxes in order to override the system
- ; font color.
- System::Call 'uxtheme::SetWindowTheme(i $CheckboxSendPing, w " ", w " ")'
- SetCtlColors $CheckboxSendPing ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $CheckboxSendPing ${WM_SETFONT} $FontNormal 0
- ${NSD_Check} $CheckboxSendPing
-
-!ifdef MOZ_MAINTENANCE_SERVICE
- ; We can only install the maintenance service if the user is an admin.
- Call IsUserAdmin
- Pop $0
-
- ; Only show the maintenance service checkbox if we're on XP SP3 or higher;
- ; we don't ever want to install it on XP without at least SP3 installed.
- ${If} $0 == "true"
- ${AndIf} ${IsWinXP}
- ${AndIf} ${AtMostServicePack} 2
- StrCpy $0 "false"
- ${EndIf}
-
- ; Only show the maintenance service checkbox if we have write access to HKLM
- ClearErrors
- WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
- "Write Test"
- ${If} ${Errors}
- ${OrIf} $0 != "true"
- StrCpy $CheckboxInstallMaintSvc "0"
- ${Else}
- DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
- ; Read the registry instead of using ServicesHelper::IsInstalled so the
- ; plugin isn't included in the stub installer to lessen its size.
- ClearErrors
- ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\services\MozillaMaintenance" "ImagePath"
- ${If} ${Errors}
- ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 184u ${OPTIONS_ITEM_WIDTH_DU} \
- 12u "$(INSTALL_MAINT_SERVICE)"
- Pop $CheckboxInstallMaintSvc
- System::Call 'uxtheme::SetWindowTheme(i $CheckboxInstallMaintSvc, w " ", w " ")'
- SetCtlColors $CheckboxInstallMaintSvc ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
- SendMessage $CheckboxInstallMaintSvc ${WM_SETFONT} $FontNormal 0
- ${NSD_Check} $CheckboxInstallMaintSvc
- ${EndIf}
- ${EndIf}
-!endif
-
- GetDlgItem $0 $HWNDPARENT 1 ; Install button
- ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(UPGRADE_BUTTON)"
- ${Else}
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(INSTALL_BUTTON)"
- ${EndIf}
- ${NSD_SetFocus} $0
-
- GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(CANCEL_BUTTON)"
-
- GetDlgItem $0 $HWNDPARENT 3 ; Back button used for Options
- EnableWindow $0 0
- ShowWindow $0 ${SW_HIDE}
-
- ; If the option button was not clicked display the reason for what needs to be
- ; resolved to continue the installation.
- ${If} "$WasOptionsButtonClicked" != "1"
- ${If} "$CanWriteToInstallDir" == "false"
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_WRITE_ACCESS)"
- ${ElseIf} "$HasRequiredSpaceAvailable" == "false"
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_DISK_SPACE)"
- ${EndIf}
- ${EndIf}
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $StartOptionsPhaseTickCount
-
- LockWindow off
- nsDialogs::Show
-FunctionEnd
-
-Function leaveOptions
- LockWindow on
-
- ${GetRoot} "$INSTDIR" $0
- ${GetLongPath} "$INSTDIR" $INSTDIR
- ${GetLongPath} "$0" $0
- ${If} "$INSTDIR" == "$0"
- LockWindow off
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_ROOT_INSTALL)"
- Abort ; Stay on the page
- ${EndIf}
-
- Call CanWrite
- ${If} "$CanWriteToInstallDir" == "false"
- LockWindow off
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_WRITE_ACCESS)"
- Abort ; Stay on the page
- ${EndIf}
-
- Call CheckSpace
- ${If} "$HasRequiredSpaceAvailable" == "false"
- LockWindow off
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_DISK_SPACE)"
- Abort ; Stay on the page
- ${EndIf}
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $0
- ${GetSecondsElapsed} "$StartOptionsPhaseTickCount" "$0" $OptionsPhaseSeconds
- ; It is possible for this value to be 0 if the user clicks fast enough so
- ; increment the value by 1 if it is 0.
- ${If} $OptionsPhaseSeconds == 0
- IntOp $OptionsPhaseSeconds $OptionsPhaseSeconds + 1
- ${EndIf}
-
- ${NSD_GetState} $CheckboxShortcutOnBar $CheckboxShortcutOnBar
- ${NSD_GetState} $CheckboxShortcutInStartMenu $CheckboxShortcutInStartMenu
- ${NSD_GetState} $CheckboxShortcutOnDesktop $CheckboxShortcutOnDesktop
- ${NSD_GetState} $CheckboxSendPing $CheckboxSendPing
-!ifdef MOZ_MAINTENANCE_SERVICE
- ${NSD_GetState} $CheckboxInstallMaintSvc $CheckboxInstallMaintSvc
-!endif
-
-FunctionEnd
-
-Function createInstall
- nsDialogs::Create /NOUNLOAD 1018
- Pop $Dialog
- ; Since the text color for controls is set in this Dialog the foreground and
- ; background colors of the Dialog must also be hardcoded.
- SetCtlColors $Dialog ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-
- ${NSD_CreateLabel} 0 0 49u 64u ""
- Pop $0
- ${GetDlgItemWidthHeight} $0 $1 $2
- System::Call 'user32::DestroyWindow(i r0)'
-
- ${NSD_CreateLabel} 0 0 11u 16u ""
- Pop $0
- ${GetDlgItemWidthHeight} $0 $3 $4
- System::Call 'user32::DestroyWindow(i r0)'
-
- FindWindow $7 "#32770" "" $HWNDPARENT
- ${GetDlgItemWidthHeight} $7 $8 $9
-
- ; Allow a maximum text width of half of the Dialog's width
- IntOp $R0 $8 / 2
-
- ${GetTextWidthHeight} "${INSTALL_BLURB1}" $FontBlurb $R0 $5 $6
- IntOp $R1 $1 + $3
- IntOp $R1 $R1 + $5
- IntOp $R1 $8 - $R1
- IntOp $R1 $R1 / 2
- ${NSD_CreateBitmap} $R1 ${INSTALL_BLURB_TOP_DU} 49u 64u ""
- Pop $BitmapBlurb1
- ${SetStretchedTransparentImage} $BitmapBlurb1 $PLUGINSDIR\clock.bmp $HwndBitmapBlurb1
- IntOp $R1 $R1 + $1
- IntOp $R1 $R1 + $3
- ${NSD_CreateLabel} $R1 ${INSTALL_BLURB_TOP_DU} $5 $6 "${INSTALL_BLURB1}"
- Pop $LabelBlurb1
- SendMessage $LabelBlurb1 ${WM_SETFONT} $FontBlurb 0
- SetCtlColors $LabelBlurb1 ${INSTALL_BLURB_TEXT_COLOR} transparent
-
- ${GetTextWidthHeight} "${INSTALL_BLURB2}" $FontBlurb $R0 $5 $6
- IntOp $R1 $1 + $3
- IntOp $R1 $R1 + $5
- IntOp $R1 $8 - $R1
- IntOp $R1 $R1 / 2
- ${NSD_CreateBitmap} $R1 ${INSTALL_BLURB_TOP_DU} 49u 64u ""
- Pop $BitmapBlurb2
- ${SetStretchedTransparentImage} $BitmapBlurb2 $PLUGINSDIR\particles.bmp $HwndBitmapBlurb2
- IntOp $R1 $R1 + $1
- IntOp $R1 $R1 + $3
- ${NSD_CreateLabel} $R1 ${INSTALL_BLURB_TOP_DU} $5 $6 "${INSTALL_BLURB2}"
- Pop $LabelBlurb2
- SendMessage $LabelBlurb2 ${WM_SETFONT} $FontBlurb 0
- SetCtlColors $LabelBlurb2 ${INSTALL_BLURB_TEXT_COLOR} transparent
- ShowWindow $BitmapBlurb2 ${SW_HIDE}
- ShowWindow $LabelBlurb2 ${SW_HIDE}
-
- ${GetTextWidthHeight} "${INSTALL_BLURB3}" $FontBlurb $R0 $5 $6
- IntOp $R1 $1 + $3
- IntOp $R1 $R1 + $5
- IntOp $R1 $8 - $R1
- IntOp $R1 $R1 / 2
- ${NSD_CreateBitmap} $R1 ${INSTALL_BLURB_TOP_DU} 49u 64u ""
- Pop $BitmapBlurb3
- ${SetStretchedTransparentImage} $BitmapBlurb3 $PLUGINSDIR\pencil.bmp $HWndBitmapBlurb3
- IntOp $R1 $R1 + $1
- IntOp $R1 $R1 + $3
- ${NSD_CreateLabel} $R1 ${INSTALL_BLURB_TOP_DU} $5 $6 "${INSTALL_BLURB3}"
- Pop $LabelBlurb3
- SendMessage $LabelBlurb3 ${WM_SETFONT} $FontBlurb 0
- SetCtlColors $LabelBlurb3 ${INSTALL_BLURB_TEXT_COLOR} transparent
- ShowWindow $BitmapBlurb3 ${SW_HIDE}
- ShowWindow $LabelBlurb3 ${SW_HIDE}
-
- ${NSD_CreateProgressBar} 103u 166u 241u 9u ""
- Pop $Progressbar
- ${NSD_AddStyle} $Progressbar ${PBS_MARQUEE}
- SendMessage $Progressbar ${PBM_SETMARQUEE} 1 \
- $ProgressbarMarqueeIntervalMS ; start=1|stop=0 interval(ms)=+N
-
- ${NSD_CreateLabelCenter} 103u 180u 241u 20u "$(DOWNLOADING_LABEL)"
- Pop $LabelDownloading
- SendMessage $LabelDownloading ${WM_SETFONT} $FontNormal 0
- SetCtlColors $LabelDownloading ${INSTALL_PROGRESS_TEXT_COLOR_NORMAL} transparent
-
- ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
- ${NSD_CreateLabelCenter} 103u 180u 241u 20u "$(UPGRADING_LABEL)"
- ${Else}
- ${NSD_CreateLabelCenter} 103u 180u 241u 20u "$(INSTALLING_LABEL)"
- ${EndIf}
- Pop $LabelInstalling
- SendMessage $LabelInstalling ${WM_SETFONT} $FontNormal 0
- SetCtlColors $LabelInstalling ${INSTALL_PROGRESS_TEXT_COLOR_NORMAL} transparent
- ShowWindow $LabelInstalling ${SW_HIDE}
-
- ${NSD_CreateBitmap} ${APPNAME_BMP_EDGE_DU} ${APPNAME_BMP_TOP_DU} \
- ${APPNAME_BMP_WIDTH_DU} ${APPNAME_BMP_HEIGHT_DU} ""
- Pop $2
- ${SetStretchedTransparentImage} $2 $PLUGINSDIR\appname.bmp $0
-
- GetDlgItem $0 $HWNDPARENT 1 ; Install button
- EnableWindow $0 0
- ShowWindow $0 ${SW_HIDE}
-
- GetDlgItem $0 $HWNDPARENT 3 ; Back button used for Options
- EnableWindow $0 0
- ShowWindow $0 ${SW_HIDE}
-
- GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(CANCEL_BUTTON)"
- ; Focus the Cancel button otherwise it isn't possible to tab to it since it is
- ; the only control that can be tabbed to.
- ${NSD_SetFocus} $0
- ; Kill the Cancel button's focus so pressing enter won't cancel the install.
- SendMessage $0 ${WM_KILLFOCUS} 0 0
-
- ${If} "$CanSetAsDefault" == "true"
- GetDlgItem $0 $HWNDPARENT 10 ; Default browser checkbox
- SendMessage $0 ${BM_GETCHECK} 0 0 $CheckboxSetAsDefault
- EnableWindow $0 0
- ShowWindow $0 ${SW_HIDE}
- ${EndIf}
-
- GetDlgItem $0 $HWNDPARENT 11
- ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(ONE_MOMENT_UPGRADE)"
- ${Else}
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(ONE_MOMENT_INSTALL)"
- ${EndIf}
- SendMessage $0 ${WM_SETFONT} $FontNormal 0
- SetCtlColors $0 ${FOOTER_CONTROL_TEXT_COLOR_FADED} ${FOOTER_BKGRD_COLOR}
- ShowWindow $0 ${SW_SHOW}
-
- ; Set $DownloadReset to true so the first download tick count is measured.
- StrCpy $DownloadReset "true"
- StrCpy $IsDownloadFinished "false"
- StrCpy $DownloadRetryCount "0"
- StrCpy $DownloadedBytes "0"
- StrCpy $StartLastDownloadTickCount ""
- StrCpy $EndDownloadPhaseTickCount ""
- StrCpy $DownloadFirstTransferSeconds ""
- StrCpy $ExitCode "${ERR_DOWNLOAD_CANCEL}"
- StrCpy $OpenedDownloadPage "0"
-
- ClearErrors
- ReadINIStr $ExistingVersion "$INSTDIR\application.ini" "App" "Version"
- ${If} ${Errors}
- StrCpy $ExistingVersion "0"
- ${EndIf}
-
- ClearErrors
- ReadINIStr $ExistingBuildID "$INSTDIR\application.ini" "App" "BuildID"
- ${If} ${Errors}
- StrCpy $ExistingBuildID "0"
- ${EndIf}
-
- ${If} ${FileExists} "$LOCALAPPDATA\Mozilla\Basilisk"
- StrCpy $ExistingProfile "1"
- ${Else}
- StrCpy $ExistingProfile "0"
- ${EndIf}
-
- StrCpy $DownloadServerIP ""
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $StartDownloadPhaseTickCount
-
- ${If} ${FileExists} "$INSTDIR\uninstall\uninstall.log"
- StrCpy $InstallTotalSteps ${InstallPaveOverTotalSteps}
- ${Else}
- StrCpy $InstallTotalSteps ${InstallCleanTotalSteps}
- ${EndIf}
-
- ${ITBL3Create}
- ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
-
- ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
-
- LockWindow off
- nsDialogs::Show
-
- ${NSD_FreeImage} $0
- ${NSD_FreeImage} $HwndBitmapBlurb1
- ${NSD_FreeImage} $HwndBitmapBlurb2
- ${NSD_FreeImage} $HWndBitmapBlurb3
-FunctionEnd
-
-Function StartDownload
- ${NSD_KillTimer} StartDownload
- InetBgDL::Get "${URLStubDownload}${URLStubDownloadAppend}" "$PLUGINSDIR\download.exe" \
- /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
- StrCpy $4 ""
- ${NSD_CreateTimer} OnDownload ${DownloadIntervalMS}
- ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
- RmDir /r "$INSTDIR\${TO_BE_DELETED}"
- ${EndIf}
-FunctionEnd
-
-Function SetProgressBars
- SendMessage $Progressbar ${PBM_SETPOS} $ProgressCompleted 0
- ${ITBL3SetProgressValue} "$ProgressCompleted" "$ProgressTotal"
-FunctionEnd
-
-Function RemoveFileProgressCallback
- IntOp $InstallCounterStep $InstallCounterStep + 2
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
-FunctionEnd
-
-Function OnDownload
- InetBgDL::GetStats
- # $0 = HTTP status code, 0=Completed
- # $1 = Completed files
- # $2 = Remaining files
- # $3 = Number of downloaded bytes for the current file
- # $4 = Size of current file (Empty string if the size is unknown)
- # /RESET must be used if status $0 > 299 (e.g. failure)
- # When status is $0 =< 299 it is handled by InetBgDL
- StrCpy $DownloadServerIP "$5"
- ${If} $0 > 299
- ${NSD_KillTimer} OnDownload
- IntOp $DownloadRetryCount $DownloadRetryCount + 1
- ${If} "$DownloadReset" != "true"
- StrCpy $DownloadedBytes "0"
- ${NSD_AddStyle} $Progressbar ${PBS_MARQUEE}
- SendMessage $Progressbar ${PBM_SETMARQUEE} 1 \
- $ProgressbarMarqueeIntervalMS ; start=1|stop=0 interval(ms)=+N
- ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
- ${EndIf}
- InetBgDL::Get /RESET /END
- StrCpy $DownloadSizeBytes ""
- StrCpy $DownloadReset "true"
-
- ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
- StrCpy $ExitCode "${ERR_DOWNLOAD_TOO_MANY_RETRIES}"
- ; Use a timer so the UI has a chance to update
- ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
- ${Else}
- ${NSD_CreateTimer} StartDownload ${DownloadRetryIntervalMS}
- ${EndIf}
- Return
- ${EndIf}
-
- ${If} "$DownloadReset" == "true"
- System::Call "kernel32::GetTickCount()l .s"
- Pop $StartLastDownloadTickCount
- StrCpy $DownloadReset "false"
- ; The seconds elapsed from the start of the download phase until the first
- ; bytes are received are only recorded for the first request so it is
- ; possible to determine connection issues for the first request.
- ${If} "$DownloadFirstTransferSeconds" == ""
- ; Get the seconds elapsed from the start of the download phase until the
- ; first bytes are received.
- ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$StartLastDownloadTickCount" $DownloadFirstTransferSeconds
- ${EndIf}
- ${EndIf}
-
- ${If} "$DownloadSizeBytes" == ""
- ${AndIf} "$4" != ""
- ; Handle the case where the size of the file to be downloaded is less than
- ; the minimum expected size or greater than the maximum expected size at the
- ; beginning of the download.
- ${If} $4 < ${DownloadMinSizeBytes}
- ${OrIf} $4 > ${DownloadMaxSizeBytes}
- ${NSD_KillTimer} OnDownload
- InetBgDL::Get /RESET /END
- StrCpy $DownloadReset "true"
-
- ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
- ; Use a timer so the UI has a chance to update
- ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
- ${Else}
- ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
- ${EndIf}
- Return
- ${EndIf}
-
- StrCpy $DownloadSizeBytes "$4"
- System::Int64Op $4 / 2
- Pop $HalfOfDownload
- System::Int64Op $HalfOfDownload / $InstallTotalSteps
- Pop $InstallStepSize
- SendMessage $Progressbar ${PBM_SETMARQUEE} 0 0 ; start=1|stop=0 interval(ms)=+N
- ${RemoveStyle} $Progressbar ${PBS_MARQUEE}
- System::Int64Op $HalfOfDownload + $DownloadSizeBytes
- Pop $ProgressTotal
- StrCpy $ProgressCompleted 0
- SendMessage $Progressbar ${PBM_SETRANGE32} $ProgressCompleted $ProgressTotal
- ${EndIf}
-
- ; Don't update the status until after the download starts
- ${If} $2 != 0
- ${AndIf} "$4" == ""
- Return
- ${EndIf}
-
- ; Handle the case where the downloaded size is greater than the maximum
- ; expected size during the download.
- ${If} $DownloadedBytes > ${DownloadMaxSizeBytes}
- InetBgDL::Get /RESET /END
- StrCpy $DownloadReset "true"
-
- ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
- ; Use a timer so the UI has a chance to update
- ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
- ${Else}
- ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
- ${EndIf}
- Return
- ${EndIf}
-
- ${If} $IsDownloadFinished != "true"
- ${If} $2 == 0
- ${NSD_KillTimer} OnDownload
- StrCpy $IsDownloadFinished "true"
- ; The first step of the install progress bar is determined by the
- ; InstallProgressFirstStep define and provides the user with immediate
- ; feedback.
- StrCpy $InstallCounterStep "${InstallProgressFirstStep}"
- System::Call "kernel32::GetTickCount()l .s"
- Pop $EndDownloadPhaseTickCount
-
- StrCpy $DownloadedBytes "$DownloadSizeBytes"
-
- ; When a download has finished handle the case where the downloaded size
- ; is less than the minimum expected size or greater than the maximum
- ; expected size during the download.
- ${If} $DownloadedBytes < ${DownloadMinSizeBytes}
- ${OrIf} $DownloadedBytes > ${DownloadMaxSizeBytes}
- InetBgDL::Get /RESET /END
- StrCpy $DownloadReset "true"
-
- ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
- ; Use a timer so the UI has a chance to update
- ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
- ${Else}
- ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
- ${EndIf}
- Return
- ${EndIf}
-
- LockWindow on
- ; Update the progress bars first in the UI change so they take affect
- ; before other UI changes.
- StrCpy $ProgressCompleted "$DownloadSizeBytes"
- Call SetProgressBars
- System::Int64Op $InstallStepSize * ${InstallProgressFirstStep}
- Pop $R9
- System::Int64Op $ProgressCompleted + $R9
- Pop $ProgressCompleted
- Call SetProgressBars
- ShowWindow $LabelDownloading ${SW_HIDE}
- ShowWindow $LabelInstalling ${SW_SHOW}
- ShowWindow $LabelBlurb2 ${SW_HIDE}
- ShowWindow $BitmapBlurb2 ${SW_HIDE}
- ShowWindow $LabelBlurb3 ${SW_SHOW}
- ShowWindow $BitmapBlurb3 ${SW_SHOW}
- ; Disable the Cancel button during the install
- GetDlgItem $5 $HWNDPARENT 2
- EnableWindow $5 0
- LockWindow off
-
- ; Open a handle to prevent modification of the full installer
- StrCpy $R9 "${INVALID_HANDLE_VALUE}"
- System::Call 'kernel32::CreateFileW(w "$PLUGINSDIR\download.exe", \
- i ${GENERIC_READ}, \
- i ${FILE_SHARE_READ}, i 0, \
- i ${OPEN_EXISTING}, i 0, i 0) i .R9'
- StrCpy $HandleDownload "$R9"
-
- ${If} $HandleDownload == ${INVALID_HANDLE_VALUE}
- StrCpy $ExitCode "${ERR_PREINSTALL_INVALID_HANDLE}"
- StrCpy $0 "0"
- StrCpy $1 "0"
- ${Else}
- CertCheck::VerifyCertTrust "$PLUGINSDIR\download.exe"
- Pop $0
- CertCheck::VerifyCertNameIssuer "$PLUGINSDIR\download.exe" \
- "${CertNameDownload}" "${CertIssuerDownload}"
- Pop $1
- ${If} $0 == 0
- ${AndIf} $1 == 0
- StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES}"
- ${ElseIf} $0 == 0
- StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED}"
- ${ElseIf} $1 == 0
- StrCpy $ExitCode "${ERR_PREINSTALL_CERT_ATTRIBUTES}"
- ${EndIf}
- ${EndIf}
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $EndPreInstallPhaseTickCount
-
- ${If} $0 == 0
- ${OrIf} $1 == 0
- ; Use a timer so the UI has a chance to update
- ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
- Return
- ${EndIf}
-
- ; Instead of extracting the files we use the downloaded installer to
- ; install in case it needs to perform operations that the stub doesn't
- ; know about.
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "InstallDirectoryPath" "$INSTDIR"
- ; Don't create the QuickLaunch or Taskbar shortcut from the launched installer
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "QuickLaunchShortcut" "false"
-
- ; Either avoid or force adding a taskbar pin based on the checkbox value:
- ${If} $CheckboxShortcutOnBar == 0
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "false"
- ${Else}
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "true"
- ${EndIf}
-
- ${If} $CheckboxShortcutOnDesktop == 1
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "true"
- ${Else}
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "false"
- ${EndIf}
-
- ${If} $CheckboxShortcutInStartMenu == 1
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "true"
- ${Else}
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "false"
- ${EndIf}
-
-!ifdef MOZ_MAINTENANCE_SERVICE
- ${If} $CheckboxInstallMaintSvc == 1
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "true"
- ${Else}
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
- ${EndIf}
-!else
- WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
-!endif
-
- ; Delete the taskbar shortcut history to ensure we do the right thing based on
- ; the config file above.
- ${GetShortcutsLogPath} $0
- Delete "$0"
-
- GetFunctionAddress $0 RemoveFileProgressCallback
- ${RemovePrecompleteEntries} $0
-
- ; Delete the install.log and let the full installer create it. When the
- ; installer closes it we can detect that it has completed.
- Delete "$INSTDIR\install.log"
-
- ; Delete firefox.exe.moz-upgrade and firefox.exe.moz-delete if it exists
- ; since it being present will require an OS restart for the full
- ; installer.
- Delete "$INSTDIR\${FileMainEXE}.moz-upgrade"
- Delete "$INSTDIR\${FileMainEXE}.moz-delete"
-
- System::Call "kernel32::GetTickCount()l .s"
- Pop $EndPreInstallPhaseTickCount
-
- Exec "$\"$PLUGINSDIR\download.exe$\" /INI=$PLUGINSDIR\${CONFIG_INI}"
- ${NSD_CreateTimer} CheckInstall ${InstallIntervalMS}
- ${Else}
- ${If} $HalfOfDownload != "true"
- ${AndIf} $3 > $HalfOfDownload
- StrCpy $HalfOfDownload "true"
- LockWindow on
- ShowWindow $LabelBlurb1 ${SW_HIDE}
- ShowWindow $BitmapBlurb1 ${SW_HIDE}
- ShowWindow $LabelBlurb2 ${SW_SHOW}
- ShowWindow $BitmapBlurb2 ${SW_SHOW}
- LockWindow off
- ${EndIf}
- StrCpy $DownloadedBytes "$3"
- StrCpy $ProgressCompleted "$DownloadedBytes"
- Call SetProgressBars
- ${EndIf}
- ${EndIf}
-FunctionEnd
-
-Function OnPing
- InetBgDL::GetStats
- # $0 = HTTP status code, 0=Completed
- # $1 = Completed files
- # $2 = Remaining files
- # $3 = Number of downloaded bytes for the current file
- # $4 = Size of current file (Empty string if the size is unknown)
- # /RESET must be used if status $0 > 299 (e.g. failure)
- # When status is $0 =< 299 it is handled by InetBgDL
- ${If} $2 == 0
- ${OrIf} $0 > 299
- ${NSD_KillTimer} OnPing
- ${If} $0 > 299
- InetBgDL::Get /RESET /END
- ${EndIf}
- ; The following will exit the installer
- SetAutoClose true
- StrCpy $R9 "2"
- Call RelativeGotoPage
- ${EndIf}
-FunctionEnd
-
-Function CheckInstall
- IntOp $InstallCounterStep $InstallCounterStep + 1
- ${If} $InstallCounterStep >= $InstallTotalSteps
- ${NSD_KillTimer} CheckInstall
- ; Close the handle that prevents modification of the full installer
- System::Call 'kernel32::CloseHandle(i $HandleDownload)'
- StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
- ; Use a timer so the UI has a chance to update
- ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
- Return
- ${EndIf}
-
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
-
- ${If} ${FileExists} "$INSTDIR\install.log"
- Delete "$INSTDIR\install.tmp"
- CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
-
- ; The unfocus and refocus that happens approximately here is caused by the
- ; installer calling SHChangeNotify to refresh the shortcut icons.
-
- ; When the full installer completes the installation the install.log will no
- ; longer be in use.
- ClearErrors
- Delete "$INSTDIR\install.log"
- ${Unless} ${Errors}
- ${NSD_KillTimer} CheckInstall
- ; Close the handle that prevents modification of the full installer
- System::Call 'kernel32::CloseHandle(i $HandleDownload)'
- Rename "$INSTDIR\install.tmp" "$INSTDIR\install.log"
- Delete "$PLUGINSDIR\download.exe"
- Delete "$PLUGINSDIR\${CONFIG_INI}"
- System::Call "kernel32::GetTickCount()l .s"
- Pop $EndInstallPhaseTickCount
- System::Int64Op $InstallStepSize * ${InstallProgressFinishStep}
- Pop $InstallStepSize
- ${NSD_CreateTimer} FinishInstall ${InstallIntervalMS}
- ${EndUnless}
- ${EndIf}
-FunctionEnd
-
-Function FinishInstall
- ; The full installer has completed but the progress bar still needs to finish
- ; so increase the size of the step.
- IntOp $InstallCounterStep $InstallCounterStep + ${InstallProgressFinishStep}
- ${If} $InstallTotalSteps < $InstallCounterStep
- StrCpy $InstallCounterStep "$InstallTotalSteps"
- ${EndIf}
-
- ${If} $InstallTotalSteps != $InstallCounterStep
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
- Return
- ${EndIf}
-
- ${NSD_KillTimer} FinishInstall
-
- StrCpy $ProgressCompleted "$ProgressTotal"
- Call SetProgressBars
-
- ${If} "$CheckboxSetAsDefault" == "1"
- ; NB: this code is duplicated in installer.nsi. Please keep in sync.
- ; For data migration in the app, we want to know what the default browser
- ; value was before we changed it. To do so, we read it here and store it
- ; in our own registry key.
- StrCpy $0 ""
- ${If} ${AtLeastWinVista}
- AppAssocReg::QueryCurrentDefault "http" "protocol" "effective"
- Pop $1
- ; If the method hasn't failed, $1 will contain the progid. Check:
- ${If} "$1" != "method failed"
- ${AndIf} "$1" != "method not available"
- ; Read the actual command from the progid
- ReadRegStr $0 HKCR "$1\shell\open\command" ""
- ${EndIf}
- ${EndIf}
- ; If using the App Association Registry didn't happen or failed, fall back
- ; to the effective http default:
- ${If} "$0" == ""
- ReadRegStr $0 HKCR "http\shell\open\command" ""
- ${EndIf}
- ; If we have something other than empty string now, write the value.
- ${If} "$0" != ""
- ClearErrors
- WriteRegStr HKCU "Software\Mozilla\Basilisk" "OldDefaultBrowserCommand" "$0"
- ${EndIf}
-
- ${GetParameters} $0
- ClearErrors
- ${GetOptions} "$0" "/UAC:" $0
- ${If} ${Errors} ; Not elevated
- Call ExecSetAsDefaultAppUser
- ${Else} ; Elevated - execute the function in the unelevated process
- GetFunctionAddress $0 ExecSetAsDefaultAppUser
- UAC::ExecCodeSegment $0
- ${EndIf}
- ${EndIf}
-
- ${If} $CheckboxShortcutOnBar == 1
- ${If} ${AtMostWinVista}
- ClearErrors
- ${GetParameters} $0
- ClearErrors
- ${GetOptions} "$0" "/UAC:" $0
- ${If} ${Errors}
- Call AddQuickLaunchShortcut
- ${Else}
- GetFunctionAddress $0 AddQuickLaunchShortcut
- UAC::ExecCodeSegment $0
- ${EndIf}
- ${EndIf}
- ${EndIf}
-
- ${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
- Delete "$INSTDIR\${FileMainEXE}"
- Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
- ${EndIf}
-
- StrCpy $ExitCode "${ERR_SUCCESS}"
-
- StrCpy $InstallCounterStep 0
- ${NSD_CreateTimer} FinishProgressBar ${InstallIntervalMS}
-FunctionEnd
-
-Function FinishProgressBar
- IntOp $InstallCounterStep $InstallCounterStep + 1
-
- ${If} $InstallCounterStep < 10
- Return
- ${EndIf}
-
- ${NSD_KillTimer} FinishProgressBar
-
- Call CopyPostSigningData
- Call LaunchApp
- Call SendPing
-FunctionEnd
-
-Function OnBack
- StrCpy $WasOptionsButtonClicked "1"
- StrCpy $R9 "1" ; Goto the next page
- Call RelativeGotoPage
- ; The call to Abort prevents NSIS from trying to move to the previous or the
- ; next page.
- Abort
-FunctionEnd
-
-Function RelativeGotoPage
- IntCmp $R9 0 0 Move Move
- StrCmp $R9 "X" 0 Move
- StrCpy $R9 "120"
-
- Move:
- SendMessage $HWNDPARENT "0x408" "$R9" ""
-FunctionEnd
-
-Function UpdateFreeSpaceLabel
- ; Only update when $ExistingTopDir isn't set
- ${If} "$ExistingTopDir" != ""
- StrLen $5 "$ExistingTopDir"
- StrLen $6 "$INSTDIR"
- ${If} $5 <= $6
- StrCpy $7 "$INSTDIR" $5
- ${If} "$7" == "$ExistingTopDir"
- Return
- ${EndIf}
- ${EndIf}
- ${EndIf}
-
- Call CheckSpace
-
- StrCpy $0 "$SpaceAvailableBytes"
-
- StrCpy $1 "$(BYTE)"
-
- ${If} $0 > 1024
- ${OrIf} $0 < 0
- System::Int64Op $0 / 1024
- Pop $0
- StrCpy $1 "$(KILO)$(BYTE)"
- ${If} $0 > 1024
- ${OrIf} $0 < 0
- System::Int64Op $0 / 1024
- Pop $0
- StrCpy $1 "$(MEGA)$(BYTE)"
- ${If} $0 > 1024
- ${OrIf} $0 < 0
- System::Int64Op $0 / 1024
- Pop $0
- StrCpy $1 "$(GIGA)$(BYTE)"
- ${EndIf}
- ${EndIf}
- ${EndIf}
-
- SendMessage $LabelFreeSpace ${WM_SETTEXT} 0 "STR:$0 $1"
-FunctionEnd
-
-Function OnChange_DirRequest
- Pop $0
- System::Call 'user32::GetWindowTextW(i $DirRequest, w .r0, i ${NSIS_MAX_STRLEN})'
- StrCpy $1 "$0" 1 ; the first character
- ${If} "$1" == "$\""
- StrCpy $1 "$0" "" -1 ; the last character
- ${If} "$1" == "$\""
- StrCpy $0 "$0" "" 1 ; all but the first character
- StrCpy $0 "$0" -1 ; all but the last character
- ${EndIf}
- ${EndIf}
-
- StrCpy $INSTDIR "$0"
- Call UpdateFreeSpaceLabel
-
- GetDlgItem $0 $HWNDPARENT 1 ; Install button
- ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(UPGRADE_BUTTON)"
- ${Else}
- SendMessage $0 ${WM_SETTEXT} 0 "STR:$(INSTALL_BUTTON)"
- ${EndIf}
-FunctionEnd
-
-Function OnClick_ButtonBrowse
- StrCpy $0 "$INSTDIR"
- nsDialogs::SelectFolderDialog /NOUNLOAD "$(SELECT_FOLDER_TEXT)" $0
- Pop $0
- ${If} $0 == "error" ; returns 'error' if 'cancel' was pressed?
- Return
- ${EndIf}
-
- ${If} $0 != ""
- StrCpy $INSTDIR "$0"
- System::Call 'user32::SetWindowTextW(i $DirRequest, w "$INSTDIR")'
- ${EndIf}
-FunctionEnd
-
-Function CheckSpace
- ${If} "$ExistingTopDir" != ""
- StrLen $0 "$ExistingTopDir"
- StrLen $1 "$INSTDIR"
- ${If} $0 <= $1
- StrCpy $2 "$INSTDIR" $3
- ${If} "$2" == "$ExistingTopDir"
- Return
- ${EndIf}
- ${EndIf}
- ${EndIf}
-
- StrCpy $ExistingTopDir "$INSTDIR"
- ${DoUntil} ${FileExists} "$ExistingTopDir"
- ${GetParent} "$ExistingTopDir" $ExistingTopDir
- ${If} "$ExistingTopDir" == ""
- StrCpy $SpaceAvailableBytes "0"
- StrCpy $HasRequiredSpaceAvailable "false"
- Return
- ${EndIf}
- ${Loop}
-
- ${GetLongPath} "$ExistingTopDir" $ExistingTopDir
-
- ; GetDiskFreeSpaceExW requires a backslash.
- StrCpy $0 "$ExistingTopDir" "" -1 ; the last character
- ${If} "$0" != "\"
- StrCpy $0 "\"
- ${Else}
- StrCpy $0 ""
- ${EndIf}
-
- System::Call 'kernel32::GetDiskFreeSpaceExW(w, *l, *l, *l) i("$ExistingTopDir$0", .r1, .r2, .r3) .'
- StrCpy $SpaceAvailableBytes "$1"
-
- System::Int64Op $SpaceAvailableBytes / 1048576
- Pop $1
- System::Int64Op $1 > ${APPROXIMATE_REQUIRED_SPACE_MB}
- Pop $1
- ${If} $1 == 1
- StrCpy $HasRequiredSpaceAvailable "true"
- ${Else}
- StrCpy $HasRequiredSpaceAvailable "false"
- ${EndIf}
-FunctionEnd
-
-Function CanWrite
- StrCpy $CanWriteToInstallDir "false"
-
- StrCpy $0 "$INSTDIR"
- ; Use the existing directory when it exists
- ${Unless} ${FileExists} "$INSTDIR"
- ; Get the topmost directory that exists for new installs
- ${DoUntil} ${FileExists} "$0"
- ${GetParent} "$0" $0
- ${If} "$0" == ""
- Return
- ${EndIf}
- ${Loop}
- ${EndUnless}
-
- GetTempFileName $2 "$0"
- Delete $2
- CreateDirectory "$2"
- ${If} ${FileExists} "$2"
- ${If} ${FileExists} "$INSTDIR"
- GetTempFileName $3 "$INSTDIR"
- ${Else}
- GetTempFileName $3 "$2"
- ${EndIf}
- ${If} ${FileExists} "$3"
- Delete "$3"
- StrCpy $CanWriteToInstallDir "true"
- ${EndIf}
- RmDir "$2"
- ${EndIf}
-FunctionEnd
-
-Function AddQuickLaunchShortcut
- CreateShortCut "$QUICKLAUNCH\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
- ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
- ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandFullName}.lnk" \
- "$INSTDIR"
- ${EndIf}
-FunctionEnd
-
-Function ExecSetAsDefaultAppUser
- ; Using the helper.exe lessens the stub installer size.
- ; This could ask for elevatation when the user doesn't install as admin.
- Exec "$\"$INSTDIR\uninstall\helper.exe$\" /SetAsDefaultAppUser"
-FunctionEnd
-
-Function LaunchApp
-!ifndef DEV_EDITION
- FindWindow $0 "${WindowClass}"
- ${If} $0 <> 0 ; integer comparison
- StrCpy $BasiliskLaunchCode "1"
- MessageBox MB_OK|MB_ICONQUESTION "$(WARN_MANUALLY_CLOSE_APP_LAUNCH)"
- Return
- ${EndIf}
-!endif
-
- StrCpy $BasiliskLaunchCode "2"
-
- ; Set the current working directory to the installation directory
- SetOutPath "$INSTDIR"
- ClearErrors
- ${GetParameters} $0
- ${GetOptions} "$0" "/UAC:" $1
- ${If} ${Errors}
- Exec "$\"$INSTDIR\${FileMainEXE}$\""
- ${Else}
- GetFunctionAddress $0 LaunchAppFromElevatedProcess
- UAC::ExecCodeSegment $0
- ${EndIf}
-FunctionEnd
-
-Function LaunchAppFromElevatedProcess
- ; 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
- ; Set the current working directory to the installation directory
- ${GetParent} "$0" $1
- SetOutPath "$1"
- Exec "$\"$0$\""
-FunctionEnd
-
-Function CopyPostSigningData
- ${LineRead} "$EXEDIR\postSigningData" "1" $PostSigningData
- ${If} ${Errors}
- ClearErrors
- StrCpy $PostSigningData "0"
- ${Else}
- CreateDirectory "$LOCALAPPDATA\Mozilla\Basilisk"
- CopyFiles /SILENT "$EXEDIR\postSigningData" "$LOCALAPPDATA\Mozilla\Basilisk"
- ${Endif}
-FunctionEnd
-
-Function DisplayDownloadError
- ${NSD_KillTimer} DisplayDownloadError
- ; To better display the error state on the taskbar set the progress completed
- ; value to the total value.
- ${ITBL3SetProgressValue} "100" "100"
- ${ITBL3SetProgressState} "${TBPF_ERROR}"
- MessageBox MB_OKCANCEL|MB_ICONSTOP "$(ERROR_DOWNLOAD)" IDCANCEL +2 IDOK +1
- StrCpy $OpenedDownloadPage "1" ; Already initialized to 0
-
- ${If} "$OpenedDownloadPage" == "1"
- ClearErrors
- ${GetParameters} $0
- ${GetOptions} "$0" "/UAC:" $1
- ${If} ${Errors}
- Call OpenManualDownloadURL
- ${Else}
- GetFunctionAddress $0 OpenManualDownloadURL
- UAC::ExecCodeSegment $0
- ${EndIf}
- ${EndIf}
-
- Call SendPing
-FunctionEnd
-
-Function OpenManualDownloadURL
- ExecShell "open" "${URLManualDownload}${URLManualDownloadAppend}"
-FunctionEnd
-
-Section
-SectionEnd
diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi
index 687bcd6eb..87f4d838a 100755
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -351,7 +351,7 @@ Section "Uninstall"
StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
DeleteRegKey HKLM "$0"
DeleteRegKey HKCU "$0"
- StrCpy $0 "Software\Classes\MIME\Database\Content Type\application/x-xpinstall;app=firefox"
+ StrCpy $0 "Software\Classes\MIME\Database\Content Type\application/x-xpinstall;app=Basilisk"
DeleteRegKey HKLM "$0"
DeleteRegKey HKCU "$0"
${Else}
@@ -419,8 +419,8 @@ Section "Uninstall"
; Remove the installation directory if it is empty
RmDir "$INSTDIR"
- ; If firefox.exe was successfully deleted yet we still need to restart to
- ; remove other files create a dummy firefox.exe.moz-delete to prevent the
+ ; If Basilisk.exe was successfully deleted yet we still need to restart to
+ ; remove other files create a dummy Basilisk.exe.moz-delete to prevent the
; installer from allowing an install without restart when it is required
; to complete an uninstall.
${If} ${RebootFlag}
diff --git a/browser/installer/windows/stub.tag b/browser/installer/windows/stub.tag
deleted file mode 100644
index f32bef36e..000000000
--- a/browser/installer/windows/stub.tag
+++ /dev/null
@@ -1,4 +0,0 @@
-;!@Install@!UTF-8!
-Title="Mozilla Firefox"
-RunProgram="setup-stub.exe"
-;!@InstallEnd@! \ No newline at end of file
diff --git a/browser/locales/en-US/chrome/browser/aboutHome.dtd b/browser/locales/en-US/chrome/browser/aboutHome.dtd
index 7e3b57a79..17b401c6c 100644
--- a/browser/locales/en-US/chrome/browser/aboutHome.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutHome.dtd
@@ -26,7 +26,7 @@
<!ENTITY abouthome.historyButton.label "History">
<!-- LOCALIZATION NOTE (abouthome.preferencesButtonWin.label): The label for the
preferences/options item on about:home on Windows -->
-<!ENTITY abouthome.preferencesButtonWin.label "Options">
+<!ENTITY abouthome.preferencesButtonWin.label "Preferences">
<!-- LOCALIZATION NOTE (abouthome.preferencesButtonUnix.label): The label for the
preferences/options item on about:home on Linux and OS X -->
<!ENTITY abouthome.preferencesButtonUnix.label "Preferences">
diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd
index f6fc6e3fd..6de17b64f 100644
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -315,10 +315,8 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY selectAllCmd.label "Select All">
<!ENTITY selectAllCmd.key "A">
<!ENTITY selectAllCmd.accesskey "A">
-<!ENTITY preferencesCmd2.label "Options">
-<!ENTITY preferencesCmd2.accesskey "O">
-<!ENTITY preferencesCmdUnix.label "Preferences">
-<!ENTITY preferencesCmdUnix.accesskey "n">
+<!ENTITY preferencesCmd2.label "Preferences">
+<!ENTITY preferencesCmd2.accesskey "P">
<!ENTITY clearRecentHistory.label "Clear Recent History…">
diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties
index 21e794f08..6f3aab397 100644
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -128,10 +128,8 @@ lwthemeNeedsRestart.accesskey=R
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is brandShortName and #2 is the number of pop-ups blocked.
popupWarning.message=#1 prevented this site from opening a pop-up window.;#1 prevented this site from opening #2 pop-up windows.
-popupWarningButton=Options
-popupWarningButton.accesskey=O
-popupWarningButtonUnix=Preferences
-popupWarningButtonUnix.accesskey=P
+popupWarningButton=Preferences
+popupWarningButton.accesskey=P
popupAllow=Allow pop-ups for %S
popupBlock=Block pop-ups for %S
popupWarningDontShowFromMessage=Don’t show this message when pop-ups are blocked
@@ -733,11 +731,8 @@ pendingCrashReports.alwaysSend = Always Send
decoder.noCodecs.button = Learn how
decoder.noCodecs.accesskey = L
decoder.noCodecs.message = To play video, you may need to install Microsoft’s Media Feature Pack.
-decoder.noCodecsVista.message = To play video, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
-decoder.noCodecsXP.message = To play video, you may need to enable Adobe’s Primetime Content Decryption Module.
decoder.noCodecsLinux.message = To play video, you may need to install the required video codecs.
decoder.noHWAcceleration.message = To improve video quality, you may need to install Microsoft’s Media Feature Pack.
-decoder.noHWAccelerationVista.message = To improve video quality, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
decoder.noPulseAudio.message = To play audio, you may need to install the required PulseAudio software.
decoder.unsupportedLibavcodec.message = libavcodec may be vulnerable or is not supported, and should be updated to play video.
diff --git a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
index a467aef69..a68f59fe3 100644
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -39,10 +39,6 @@ add-ons-button.tooltiptext3 = Manage your add-ons (%S)
preferences-button.label = Preferences
preferences-button.tooltiptext2 = Open preferences
preferences-button.tooltiptext.withshortcut = Open preferences (%S)
-# LOCALIZATION NOTE (preferences-button.labelWin): Windows-only label for Options
-preferences-button.labelWin = Options
-# LOCALIZATION NOTE (preferences-button.tooltipWin): Windows-only tooltip for Options
-preferences-button.tooltipWin2 = Open options
zoom-controls.label = Zoom Controls
zoom-controls.tooltiptext2 = Zoom controls
diff --git a/browser/locales/en-US/chrome/browser/migration/migration.dtd b/browser/locales/en-US/chrome/browser/migration/migration.dtd
index df6938c51..ad9293a5d 100644
--- a/browser/locales/en-US/chrome/browser/migration/migration.dtd
+++ b/browser/locales/en-US/chrome/browser/migration/migration.dtd
@@ -5,8 +5,7 @@
<!ENTITY migrationWizard.title "Import Wizard">
-<!ENTITY importFrom.label "Import Options, Bookmarks, History, Passwords and other data from:">
-<!ENTITY importFromUnix.label "Import Preferences, Bookmarks, History, Passwords and other data from:">
+<!ENTITY importFrom.label "Import Preferences, Bookmarks, History, Passwords and other data from:">
<!ENTITY importFromBookmarks.label "Import Bookmarks from:">
<!ENTITY importFromIE.label "Microsoft Internet Explorer">
diff --git a/browser/locales/en-US/chrome/browser/preferences/preferences.dtd b/browser/locales/en-US/chrome/browser/preferences/preferences.dtd
index 380da7178..7702c8c51 100644
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.dtd
@@ -3,7 +3,6 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!ENTITY prefWindow.titleWin "Options">
<!ENTITY prefWindow.title "Preferences">
<!-- LOCALIZATION NOTE (prefWindow.titleGNOME): This is not used for in-content preferences -->
<!ENTITY prefWindow.titleGNOME "&brandShortName; Preferences">
diff --git a/browser/locales/en-US/chrome/browser/preferences/security.dtd b/browser/locales/en-US/chrome/browser/preferences/security.dtd
index ca9420401..7ccc9af50 100644
--- a/browser/locales/en-US/chrome/browser/preferences/security.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/security.dtd
@@ -31,6 +31,9 @@
<!ENTITY passwordExceptions.label "Exceptions…">
<!ENTITY passwordExceptions.accesskey "x">
+<!ENTITY autofillPasswords.label "Automatically fill in log-in details">
+<!ENTITY autofillPasswords.accesskey "A">
+
<!ENTITY useMasterPassword.label "Use a master password">
<!ENTITY useMasterPassword.accesskey "U">
<!ENTITY changeMasterPassword.label "Change Master Password…">
diff --git a/browser/locales/en-US/chrome/browser/syncCustomize.dtd b/browser/locales/en-US/chrome/browser/syncCustomize.dtd
index 3375c48ce..e59d1db25 100644
--- a/browser/locales/en-US/chrome/browser/syncCustomize.dtd
+++ b/browser/locales/en-US/chrome/browser/syncCustomize.dtd
@@ -6,8 +6,7 @@
<!ENTITY syncCustomize.acceptButton.label "Start">
<!ENTITY syncCustomize.title "What would you like to sync?">
-<!ENTITY syncCustomize.description "You can change this selection in Options.">
-<!ENTITY syncCustomizeUnix.description "You can change this selection in Preferences.">
+<!ENTITY syncCustomize.description "You can change this selection in Preferences.">
<!--
These engine names are the same as in browser/preferences/sync.dtd except
diff --git a/browser/locales/en-US/chrome/browser/syncSetup.dtd b/browser/locales/en-US/chrome/browser/syncSetup.dtd
index 1fd46942d..950a83553 100644
--- a/browser/locales/en-US/chrome/browser/syncSetup.dtd
+++ b/browser/locales/en-US/chrome/browser/syncSetup.dtd
@@ -72,7 +72,7 @@
<!-- 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; Options on your other device, and selecting &#x0022;My Recovery Key&#x0022; under &#x0022;Manage Account&#x0022;.">
+<!ENTITY existingRecoveryKey.description "You can get a copy of your Recovery Key by going to &syncBrand.shortName.label; Preferences 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.">
diff --git a/browser/themes/shared/incontentprefs/preferences.inc.css b/browser/themes/shared/incontentprefs/preferences.inc.css
index 577baa6ed..0e62660de 100644
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -173,6 +173,11 @@ treecol {
margin-inline-start: 0;
}
+#browserHomePage:-moz-locale-dir(rtl) input {
+ unicode-bidi: plaintext;
+ direction: rtl;
+}
+
/* Content pane */
#playDRMContentLink {
/* Line up with the buttons in the other grid bits: */
diff --git a/browser/themes/windows/Info-XP.png b/browser/themes/windows/Info-XP.png
deleted file mode 100644
index c20f66ce6..000000000
--- a/browser/themes/windows/Info-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/Privacy-16-XP.png b/browser/themes/windows/Privacy-16-XP.png
deleted file mode 100644
index 335febbb3..000000000
--- a/browser/themes/windows/Privacy-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/Toolbar-XP.png b/browser/themes/windows/Toolbar-XP.png
deleted file mode 100644
index dff60911f..000000000
--- a/browser/themes/windows/Toolbar-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/Toolbar-lunaSilver.png b/browser/themes/windows/Toolbar-lunaSilver.png
deleted file mode 100644
index 30c425c26..000000000
--- a/browser/themes/windows/Toolbar-lunaSilver.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/Toolbar-aero.png b/browser/themes/windows/Toolbar-win7.png
index b191ce2aa..b191ce2aa 100644
--- a/browser/themes/windows/Toolbar-aero.png
+++ b/browser/themes/windows/Toolbar-win7.png
Binary files differ
diff --git a/browser/themes/windows/Toolbar-aero@2x.png b/browser/themes/windows/Toolbar-win7@2x.png
index 033d87dff..033d87dff 100644
--- a/browser/themes/windows/Toolbar-aero@2x.png
+++ b/browser/themes/windows/Toolbar-win7@2x.png
Binary files differ
diff --git a/browser/themes/windows/actionicon-tab-XPVista7.png b/browser/themes/windows/actionicon-tab-win7.png
index 8437c7655..8437c7655 100644
--- a/browser/themes/windows/actionicon-tab-XPVista7.png
+++ b/browser/themes/windows/actionicon-tab-win7.png
Binary files differ
diff --git a/browser/themes/windows/browser-aero.css b/browser/themes/windows/browser-aero.css
index 5ff9d8250..dbac6bb7a 100644
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -46,8 +46,7 @@
color: graytext;
}
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+ @media (-moz-os-version: windows-win7) {
.sidebar-header:not(:-moz-lwtheme),
#sidebar-header:not(:-moz-lwtheme) {
background-color: #EEF3FA;
@@ -83,190 +82,353 @@
-moz-appearance: -moz-win-exclude-glass;
}
- @media not all and (-moz-os-version: windows-vista) {
- @media not all and (-moz-os-version: windows-win7) {
- @media not all and (-moz-os-version: windows-win8) {
- @media (-moz-windows-default-theme) {
- #main-window {
- background-color: hsl(0, 0%, 78%);
- }
-
- :root[tabsintitlebar] .tab-label:-moz-window-inactive {
- /* Calculated to match the opacity change of Windows Explorer
- titlebar text change for inactive windows. */
- opacity: .6;
- }
- }
+ @media (-moz-os-version: windows-win10) {
+ /* Draw XUL caption buttons and background on Windows 10 */
+ @media (-moz-windows-default-theme) {
+ #main-window {
+ background-color: hsl(0, 0%, 78%);
+ }
- @media not all and (-moz-windows-default-theme) {
- #main-window {
- background-color: transparent;
- }
- }
+ :root[tabsintitlebar] .tab-label:-moz-window-inactive {
+ /* Calculated to match the opacity change of Windows Explorer
+ titlebar text change for inactive windows. */
+ opacity: .6;
+ }
+ }
- #titlebar-buttonbox,
- .titlebar-button {
- -moz-appearance: none !important;
- }
+ @media not all and (-moz-windows-accent-color-applies) {
+ /* Default styling for when no accent color is applied */
+ #main-window:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ background-color: hsl(235, 33%, 19%);
+ }
+
+ :root:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ --titlebar-text-color: white;
+ }
+
+ #titlebar-min:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highlight);
+ }
- .titlebar-button {
- border: none;
- margin: 0 !important;
- padding: 10px 17px;
- }
+ #titlebar-max:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highlight);
+ }
- #main-window[sizemode=maximized] .titlebar-button {
- padding-top: 8px;
- padding-bottom: 8px;
- }
+ #main-window[sizemode="maximized"] #titlebar-max:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highlight);
+ }
+
+ #titlebar-close:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highlight);
+ }
- .titlebar-button > .toolbarbutton-icon {
- width: 12px;
- height: 12px;
- }
+ .titlebar-button:not(#titlebar-close):not(:-moz-window-inactive):not(:-moz-lwtheme):hover {
+ background-color: hsla(0, 0%, 100%, .17);
+ }
+
+ .titlebar-button:not(#titlebar-close):not(:-moz-window-inactive):not(:-moz-lwtheme):hover:active {
+ background-color: hsla(0, 0%, 100%, .27);
+ transition: none;
+ }
+
+ #titlebar-close:not(:-moz-window-inactive):not(:-moz-lwtheme):hover {
+ background-color: hsla(0, 86%, 49%, 1);
+ }
+
+ #titlebar-close:not(:-moz-window-inactive):not(:-moz-lwtheme):hover:active {
+ background-color: hsla(0, 60%, 39%, 1);
+ transition: none;
+ }
+ }
+
+ @media (-moz-windows-accent-color-applies) {
+ /* Styling for when an accent color is applied to the titlebar */
+ #main-window:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ background-color: -moz-win-accentcolor;
+ }
+
+ :root:not(:-moz-window-inactive):not(:-moz-lwtheme) {
+ --titlebar-text-color: -moz-win-accentcolortext;
+ }
+
+ #titlebar-min {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
+ }
+
+ #titlebar-max {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize);
+ }
+
+ #main-window[sizemode="maximized"] #titlebar-max {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
+ }
+
+ #titlebar-close {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
+ }
+
+ .titlebar-button:hover {
+ background-color: hsla(0, 0%, 0%, .17);
+ }
+ .titlebar-button:hover:active {
+ background-color: hsla(0, 0%, 0%, .27);
+ transition: none;
+ }
+
+ @media (-moz-windows-accent-color-is-dark) {
+ /* dark accent color */
#titlebar-min {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highlight);
}
#titlebar-max {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize);
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highlight);
}
#main-window[sizemode="maximized"] #titlebar-max {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highlight);
}
#titlebar-close {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
- }
- #titlebar-close:hover {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highlight);
}
-
- #titlebar-min:-moz-lwtheme {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-themes);
+
+ .titlebar-button:not(#titlebar-close):not(:-moz-window-inactive):not(:-moz-lwtheme):hover {
+ background-color: hsla(0, 0%, 100%, .17);
}
- #titlebar-max:-moz-lwtheme {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-themes);
+ .titlebar-button:not(#titlebar-close):not(:-moz-window-inactive):not(:-moz-lwtheme):hover:active {
+ background-color: hsla(0, 0%, 100%, .27);
+ transition: none;
}
- #main-window[sizemode="maximized"] #titlebar-max:-moz-lwtheme {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-themes);
+
+ #titlebar-close:not(:-moz-window-inactive):not(:-moz-lwtheme):hover {
+ background-color: hsla(0, 86%, 49%, 1);
}
- #titlebar-close:-moz-lwtheme {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-themes);
+
+ #titlebar-close:not(:-moz-window-inactive):not(:-moz-lwtheme):hover:active {
+ background-color: hsla(0, 60%, 39%, 1);
+ transition: none;
}
+ }
+ } /* Windows 10 accent color applies */
+ @media not all and (-moz-windows-default-theme) {
+ #main-window {
+ background-color: transparent;
+ }
+ }
- /* the 12px image renders a 10px icon, and the 10px upscaled gets rounded to 12.5, which
- * rounds up to 13px, which makes the icon one pixel too big on 1.25dppx. Fix: */
- @media (min-resolution: 1.20dppx) and (max-resolution: 1.45dppx) {
- .titlebar-button > .toolbarbutton-icon {
- width: 11.5px;
- height: 11.5px;
- }
- }
+ #titlebar-buttonbox,
+ .titlebar-button {
+ -moz-appearance: none !important;
+ }
- /* 175% dpi should result in the same device pixel sizes as 150% dpi. */
- @media (min-resolution: 1.70dppx) and (max-resolution: 1.95dppx) {
- .titlebar-button {
- padding-left: 14.1px;
- padding-right: 14.1px;
- }
-
- .titlebar-button > .toolbarbutton-icon {
- width: 10.8px;
- height: 10.8px;
- }
- }
+ .titlebar-button {
+ border: none;
+ margin: 0 !important;
+ padding: 9px 17px;
+ transition: background-color linear 120ms;
+ }
+
+ #main-window[sizemode="maximized"][tabsontop=true] #tabbrowser-tabs {
+ min-height: 28px;
+ }
- /* 225% dpi should result in the same device pixel sizes as 200% dpi. */
- @media (min-resolution: 2.20dppx) and (max-resolution: 2.45dppx) {
- .titlebar-button {
- padding-left: 15.3333px;
- padding-right: 15.3333px;
- }
-
- .titlebar-button > .toolbarbutton-icon {
- width: 10.8px;
- height: 10.8px;
- }
- }
+ #main-window[sizemode=maximized] .titlebar-button {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ }
- /* 275% dpi should result in the same device pixel sizes as 250% dpi. */
- @media (min-resolution: 2.70dppx) and (max-resolution: 2.95dppx) {
- /* NB: todo: this should also change padding on the buttons
- * themselves, but without a device to test this on, it's
- * impossible to know by how much. */
- .titlebar-button > .toolbarbutton-icon {
- width: 10.8px;
- height: 10.8px;
- }
- }
+ .titlebar-button > .toolbarbutton-icon {
+ width: 12px;
+ height: 12px;
+ }
- @media (-moz-windows-default-theme) {
- .titlebar-button:hover {
- background-color: hsla(0, 0%, 0%, .12);
- }
+ .titlebar-button:not(:hover) > .toolbarbutton-icon:-moz-window-inactive {
+ opacity: 0.5;
+ }
- .titlebar-button:hover:active {
- background-color: hsla(0, 0%, 0%, .22);
- }
+ #main-window[chromemargin^="0,"][sizemode=normal] #navigator-toolbox {
+ margin-top: -4px;
+ }
- .titlebar-button:not(:hover) > .toolbarbutton-icon:-moz-window-inactive {
- opacity: 0.5;
- }
+ #main-window[sizemode="maximized"] #titlebar-close {
+ padding-right: 19px;
+ }
+
+ #titlebar-close:hover {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highlight);
+ background-color: hsla(0, 86%, 49%, 1);
+ }
- #titlebar-close:hover {
- background-color: hsl(355, 86%, 49%);
- }
+ #titlebar-close:hover:active {
+ background-color: hsla(0, 86%, 49%, 0.6);
+ transition: none;
+ }
+
+ /* inactive window */
- #titlebar-close:hover:active {
- background-color: hsl(355, 82%, 69%);
- }
- }
- @media not all and (-moz-windows-default-theme) {
- .titlebar-button {
- background-color: -moz-field;
- }
- .titlebar-button:hover {
- background-color: Highlight;
- }
-
- #titlebar-min {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast);
- }
- #titlebar-min:hover {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast-hover);
- }
-
- #titlebar-max {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highcontrast);
- }
- #titlebar-max:hover {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highcontrast-hover);
- }
-
- #main-window[sizemode="maximized"] #titlebar-max {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highcontrast);
- }
- #main-window[sizemode="maximized"] #titlebar-max:hover {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highcontrast-hover);
- }
-
- #titlebar-close {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highcontrast);
- }
- #titlebar-close:hover {
- list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highcontrast-hover);
- }
- }
+ #titlebar-min:-moz-window-inactive:not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
+ }
+
+ #titlebar-max:-moz-window-inactive:not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize);
+ }
+
+ #main-window[sizemode="maximized"] #titlebar-max:-moz-window-inactive:not(:-moz-lwtheme) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
+ }
+
+ #titlebar-close:-moz-window-inactive:not(:-moz-lwtheme):not(:hover) {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
+ }
+
+ .titlebar-button:-moz-window-inactive:not(:-moz-lwtheme):hover {
+ background-color: hsla(0, 0%, 0%, .17);
+ }
+
+ .titlebar-button:-moz-window-inactive:not(:-moz-lwtheme):hover:active {
+ background-color: hsla(0, 0%, 0%, .27);
+ transition: none;
+ }
+
+ /* light persona */
+
+ .titlebar-button:-moz-lwtheme-darktext:hover {
+ background-color: hsla(0, 0%, 0%, .17);
+ }
+
+ .titlebar-button:-moz-lwtheme-darktext:hover:active {
+ background-color: hsla(0, 0%, 0%, .27);
+ transition: none;
+ }
+
+ #titlebar-min:-moz-lwtheme-darktext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-outline);
+ }
+
+ #titlebar-max:-moz-lwtheme-darktext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-outline);
+ }
+
+ #main-window[sizemode="maximized"]:-moz-lwtheme-darktext #titlebar-max:-moz-lwtheme-darktext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-outline);
+ }
+
+ #titlebar-close:-moz-lwtheme-darktext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-outline);
+ }
+ #titlebar-close:hover:-moz-lwtheme-darktext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-outline);
+ }
+
+ /* dark persona */
+
+ .titlebar-button:-moz-lwtheme-brighttext:hover {
+ background-color: hsla(0, 0%, 100%, .27);
+ }
+
+ .titlebar-button:-moz-lwtheme-brighttext:hover:active {
+ background-color: hsla(0, 0%, 100%, .37);
+ transition: none;
+ }
+
+ #titlebar-min:-moz-lwtheme-brighttext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-outline-inverted);
+ }
+
+ #titlebar-max:-moz-lwtheme-brighttext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-outline-inverted);
+ }
+
+ #main-window[sizemode="maximized"]:-moz-lwtheme-brighttext #titlebar-max:-moz-lwtheme-brighttext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-outline-inverted);
+ }
+
+ #titlebar-close:-moz-lwtheme-brighttext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-outline-inverted);
+ }
+ #titlebar-close:hover:-moz-lwtheme-brighttext {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-outline-inverted);
+ }
+
+ /* the 12px image renders a 10px icon, and the 10px upscaled gets rounded to 12.5, which
+ * rounds up to 13px, which makes the icon one pixel too big on 1.25dppx. Fix: */
+ @media (min-resolution: 1.20dppx) and (max-resolution: 1.45dppx) {
+ .titlebar-button > .toolbarbutton-icon {
+ width: 11.5px;
+ height: 11.5px;
}
}
- }
+
+ /* 175% dpi should result in the same device pixel sizes as 150% dpi. */
+ @media (min-resolution: 1.70dppx) and (max-resolution: 1.95dppx) {
+ .titlebar-button {
+ padding-left: 14.1px;
+ padding-right: 14.1px;
+ }
+ .titlebar-button > .toolbarbutton-icon {
+ width: 10.8px;
+ height: 10.8px;
+ }
+ }
+
+ /* 225% dpi should result in the same device pixel sizes as 200% dpi. */
+ @media (min-resolution: 2.20dppx) and (max-resolution: 2.45dppx) {
+ .titlebar-button {
+ padding-left: 15.3333px;
+ padding-right: 15.3333px;
+ }
+ .titlebar-button > .toolbarbutton-icon {
+ width: 10.8px;
+ height: 10.8px;
+ }
+ }
+
+ /* 275% dpi should result in the same device pixel sizes as 250% dpi. */
+ @media (min-resolution: 2.70dppx) and (max-resolution: 2.95dppx) {
+ /* NB: todo: this should also change padding on the buttons
+ * themselves, but without a device to test this on, it's
+ * impossible to know by how much. */
+ .titlebar-button > .toolbarbutton-icon {
+ width: 10.8px;
+ height: 10.8px;
+ }
+ }
+
+
+ @media not all and (-moz-windows-default-theme) {
+ .titlebar-button {
+ background-color: -moz-field;
+ }
+ .titlebar-button:hover {
+ background-color: Highlight;
+ }
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7),
+ #titlebar-min {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-outine-inverted);
+ }
+
+ #titlebar-max {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-outine-inverted);
+ }
+
+ #main-window[sizemode="maximized"] #titlebar-max {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-outine-inverted);
+ }
+
+ #titlebar-close {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-outine-inverted);
+ }
+ #titlebar-close:hover {
+ list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-outine-inverted);
+ }
+ }
+ } /* Win 10 styling */
+
+ @media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {
#main-window[sizemode="maximized"] #titlebar-buttonbox {
margin-inline-end: 3px;
@@ -283,7 +445,7 @@
}
/* The borders on the glass frame are ours, and inside #browser, and on
- * vista and win7 we want to make sure they are "glassy", so we can't use
+ * win7 we want to make sure they are "glassy", so we can't use
* #browser as the exclude-glass container. We use #appcontent instead. */
#browser {
-moz-appearance: none;
@@ -296,18 +458,13 @@
@media (-moz-os-version: windows-win8) {
/* Artificially draw window borders that are covered by lwtheme, see bug 591930.
- * Borders for vista/win7 are below, win10 doesn't need them. */
+ * Borders for win7 are below, win10 doesn't need them. */
#main-window[sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme {
border-top: 1px solid @toolbarShadowColor@;
}
}
@media (-moz-windows-default-theme) {
- #toolbar-menubar:not(:-moz-lwtheme),
- #TabsToolbar:not(:-moz-lwtheme) {
- color: black;
- }
-
#main-menubar > menu:not(:-moz-lwtheme) {
color: inherit;
}
@@ -316,11 +473,9 @@
* On aero, the menubar fog disappears for inactive windows, and renders gray
* illegible.
*/
- @media not all and (-moz-os-version: windows-vista) {
- @media not all and (-moz-os-version: windows-win7) {
- #toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive {
- color: ThreeDShadow;
- }
+ @media not all and (-moz-os-version: windows-win7) {
+ #toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive {
+ color: ThreeDShadow;
}
}
}
@@ -330,9 +485,8 @@
color: white;
}
- /* Show borders on vista through win8, but not on win10 and later: */
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7),
+ /* Show borders on win7 and win8, but not on win10 and later: */
+ @media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {
/* Vertical toolbar border */
#main-window:not([customizing])[sizemode=normal] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme),
diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css
index 2de5a6545..a0cdabfb2 100644
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -16,6 +16,8 @@
%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
:root {
+ --titlebar-text-color: currentColor;
+
--space-above-tabbar: 15px;
--backbutton-urlbar-overlap: 6px;
@@ -129,8 +131,7 @@ toolbar:-moz-lwtheme {
}
@media (-moz-windows-default-theme) {
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+ @media (-moz-os-version: windows-win7) {
#navigator-toolbox::after {
border-bottom-color: #aabccf;
}
@@ -158,9 +159,7 @@ toolbar:-moz-lwtheme {
background-image: linear-gradient(@toolbarHighlight@, @toolbarHighlight@);
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
#nav-bar {
background-image: linear-gradient(@toolbarHighlight@, transparent) !important;
}
@@ -187,19 +186,22 @@ toolbar:-moz-lwtheme {
transition: min-height 170ms ease-out, max-height 170ms ease-out, visibility 170ms linear;
}
+#toolbar-menubar,
+#TabsToolbar {
+ color: var(--titlebar-text-color);
+}
+
@media not all and (-moz-windows-compositor),
not all and (-moz-windows-default-theme) {
/* Please keep the menu text colors in this media block in sync with
* devedition.css, minus the :not(:-moz-lwtheme) condition - see Bug 1165718.
*/
- #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:not(:-moz-lwtheme),
- #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme) {
- color: CaptionText;
+ :root[tabsintitlebar]:not([inFullscreen]):not(:-moz-lwtheme) {
+ --titlebar-text-color: CaptionText;
}
- #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive,
- #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme):-moz-window-inactive {
- color: InactiveCaptionText;
+ :root[tabsintitlebar]:not([inFullscreen]):not(:-moz-lwtheme):-moz-window-inactive {
+ --titlebar-text-color: InactiveCaptionText;
}
}
@@ -313,64 +315,6 @@ toolbar:-moz-lwtheme {
}
}
-/* Render a window top border for lwthemes on WinXP modern themes: */
-@media (-moz-windows-theme: luna-blue) {
- #main-window[tabsintitlebar][sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme {
- background-image: linear-gradient(to bottom,
- rgb(8, 49, 216) 0, rgb(8, 49, 216) 1px,
- rgb(15, 77, 227) 1px, rgb(15, 77, 227) 2px,
- rgb(22, 106, 238) 2px, rgb(22, 106, 238) 3px,
- rgb(8, 85, 221) 3px, rgb(8, 85, 221) 4px,
- transparent 4px);
- }
-
- #main-window[tabsintitlebar][sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme:-moz-window-inactive {
- background-image: linear-gradient(to bottom,
- rgb(91, 104, 205) 0, rgb(91, 104, 205) 1px,
- rgb(116, 128, 220) 1px, rgb(116, 128, 220) 2px,
- rgb(117, 140, 221) 2px, rgb(117, 140, 221) 4px,
- transparent 4px);
- }
-}
-
-@media (-moz-windows-theme: luna-silver) {
- #main-window[tabsintitlebar][sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme {
- background-image: linear-gradient(to bottom,
- rgb(102,102,126) 0, rgb(102,102,126) 1px,
- rgb(168,167,191) 1px, rgb(168,167,191) 2px,
- white 2px, white 3px,
- rgb(188,188,207) 3px, rgb(188,188,207) 4px,
- transparent 4px);
- }
-
- #main-window[tabsintitlebar][sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme:-moz-window-inactive {
- background-image: linear-gradient(to bottom,
- rgb(186,186,197) 0, rgb(186,186,197) 1px,
- rgb(236,238,245) 1px, rgb(236,238,245) 2px,
- white 2px, white 3px,
- rgb(215,215,227) 3px, rgb(215,215,227) 4px,
- transparent 4px);
- }
-}
-
-@media (-moz-windows-theme: luna-olive) {
- #main-window[tabsintitlebar][sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme {
- background-image: linear-gradient(to bottom,
- rgb(139,161,105) 0, rgb(139,161,105) 1px,
- rgb(171, 189, 133) 1px, rgb(171, 189, 133) 2px,
- rgb(164,178,127) 2px, rgb(164,178,127) 3px,
- transparent 3px);
- }
-
- #main-window[tabsintitlebar][sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme:-moz-window-inactive {
- background-image: linear-gradient(to bottom,
- rgb(207, 214, 188) 0, rgb(207, 214, 188) 1px,
- rgb(224, 226, 200) 1px, rgb(224, 226, 200) 2px,
- rgb(214, 216, 190) 2px, rgb(214, 216, 190) 3px,
- transparent 3px);
- }
-}
-
#TabsToolbar:not([collapsed="true"]) + #nav-bar {
/* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
margin-top: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
@@ -401,12 +345,6 @@ toolbar:-moz-lwtheme {
background-color: -moz-dialog;
}
-@media (-moz-os-version: windows-xp) and (-moz-windows-default-theme) {
- #main-window[tabsintitlebar][sizemode="normal"] #toolbar-menubar {
- margin-top: 4px;
- }
-}
-
/* ::::: titlebar ::::: */
#main-window[sizemode="normal"] > #titlebar {
@@ -427,7 +365,7 @@ toolbar:-moz-lwtheme {
* click and hover mouse events to work properly for the button in the restored
* window state. Otherwise, elements in the navigator-toolbox, like the menubar,
* can swallow those events. It will also place the buttons above the fog on
- * themes with Aero Glass.
+ * Windows 7 with Aero Glass.
*/
#titlebar-buttonbox {
z-index: 1;
@@ -437,12 +375,6 @@ toolbar:-moz-lwtheme {
margin-left: 22px; /* space needed for Aero Snap */
}
-@media (-moz-os-version: windows-xp) {
- .titlebar-placeholder[type="caption-buttons"] {
- margin-left: 10px; /* less space needed on XP because there's no Aero Snap */
- }
-}
-
/* titlebar command buttons */
#titlebar-min {
@@ -665,13 +597,6 @@ menuitem.bookmark-item {
%include ../shared/toolbarbuttons.inc.css
-@media (-moz-windows-theme: luna-silver) and (max-resolution: 1dppx) {
- :-moz-any(@primaryToolbarButtons@),
- #bookmarks-menu-button.toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
- list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png");
- }
-}
-
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
@@ -775,10 +700,7 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
max-width: 32px;
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
- /* < Win8 */
+@media (-moz-os-version: windows-win7) {
:root {
--toolbarbutton-hover-background: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
--toolbarbutton-hover-bordercolor: hsla(210,54%,20%,.15) hsla(210,54%,20%,.2) hsla(210,54%,20%,.25);
@@ -1096,9 +1018,7 @@ toolbar[brighttext] #close-button {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
#window-controls {
margin-inline-start: 4px;
}
@@ -1154,8 +1074,7 @@ toolbar[brighttext] #close-button {
}
}
-@media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
#window-controls {
-moz-box-align: start;
}
@@ -1199,8 +1118,7 @@ toolbar[brighttext] #close-button {
}
@media (-moz-windows-default-theme) {
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7),
+ @media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {
#main-window:not(:-moz-lwtheme) {
--urlbar-border-color: hsla(210,54%,20%,.25) hsla(210,54%,20%,.27) hsla(210,54%,20%,.3);
@@ -1237,8 +1155,7 @@ toolbar[brighttext] #close-button {
border-radius: 1px;
}
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7),
+ @media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {
#urlbar:not(:-moz-lwtheme),
.searchbar-textbox:not(:-moz-lwtheme) {
@@ -1261,11 +1178,9 @@ toolbar[brighttext] #close-button {
}
}
- @media not all and (-moz-os-version: windows-xp) {
- #urlbar:not(:-moz-lwtheme)[focused],
- .searchbar-textbox:not(:-moz-lwtheme)[focused] {
- border-color: Highlight;
- }
+ #urlbar:not(:-moz-lwtheme)[focused],
+ .searchbar-textbox:not(:-moz-lwtheme)[focused] {
+ border-color: Highlight;
}
}
@@ -2018,9 +1933,7 @@ html|span.ac-emphasize-text-url {
}
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
#sidebar-header > .close-icon {
padding-top: 4px;
padding-bottom: 4px;
@@ -2041,39 +1954,28 @@ html|span.ac-emphasize-text-url {
margin-bottom: calc(-1 * var(--tab-toolbar-navbar-overlap)); /* overlap the nav-bar's top border */
}
-@media (-moz-os-version: windows-xp) and (-moz-windows-default-theme) {
- #main-window[sizemode=normal] #TabsToolbar {
- padding-left: 2px;
- padding-right: 2px;
- }
-}
-
%include ../shared/tabs.inc.css
/* Remove border between tab strip and navigation toolbar on Windows 10+ */
-@media not all and (-moz-os-version: windows-xp) {
- @media not all and (-moz-os-version: windows-vista) {
- @media not all and (-moz-os-version: windows-win7) {
- @media not all and (-moz-os-version: windows-win8) {
- @media (-moz-windows-default-theme) {
- .tab-background-end[selected=true]::after,
- .tab-background-start[selected=true]::after {
- content: none;
- }
-
- #TabsToolbar {
- --tab-stroke-background-size: 0 0;
- }
-
- :root {
- --tab-toolbar-navbar-overlap: 0px;
- }
-
- #nav-bar {
- border-top-style: none !important;
- box-shadow: none;
- }
- }
+@media not all and (-moz-os-version: windows-win7) {
+ @media not all and (-moz-os-version: windows-win8) {
+ @media (-moz-windows-default-theme) {
+ .tab-background-end[selected=true]::after,
+ .tab-background-start[selected=true]::after {
+ content: none;
+ }
+
+ #TabsToolbar {
+ --tab-stroke-background-size: 0 0;
+ }
+
+ :root {
+ --tab-toolbar-navbar-overlap: 0px;
+ }
+
+ #nav-bar {
+ border-top-style: none !important;
+ box-shadow: none;
}
}
}
@@ -2558,22 +2460,6 @@ notification.pluginVulnerable > .notification-inner > .messageCloseButton {
position: relative;
}
-@media (-moz-os-version: windows-xp) {
- @media not all and (-moz-windows-classic) {
- #private-browsing-indicator-titlebar > .private-browsing-indicator {
- background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7-tall.png");
- height: 28px;
- }
-
- #main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
- top: -5px;
- }
- #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
- top: -1px;
- }
- }
-}
-
@media (-moz-windows-classic) {
/**
* We have to use top instead of background-position in this case, otherwise
@@ -2585,8 +2471,7 @@ notification.pluginVulnerable > .notification-inner > .messageCloseButton {
}
}
-@media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
@media (-moz-windows-glass) {
#main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
top: 1px;
@@ -2603,7 +2488,7 @@ notification.pluginVulnerable > .notification-inner > .messageCloseButton {
@media (-moz-windows-default-theme) {
@media not all and (-moz-windows-compositor) {
#main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
- background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7-tall.png");
+ background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-win7-tall.png");
height: 28px;
}
}
@@ -2636,19 +2521,14 @@ notification.pluginVulnerable > .notification-inner > .messageCloseButton {
margin-top: -4px;
}
-
-@media not all and (-moz-os-version: windows-xp) {
%include browser-aero.css
-}
.browser-extension-panel > .panel-arrowcontainer > .panel-arrowcontent {
padding: 0;
overflow: hidden;
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
.cui-widget-panelview[id^=PanelUI-webext-] {
border-radius: 4px;
}
diff --git a/browser/themes/windows/caption-buttons.svg b/browser/themes/windows/caption-buttons.svg
index 3ba4f95a1..9cb42d539 100644
--- a/browser/themes/windows/caption-buttons.svg
+++ b/browser/themes/windows/caption-buttons.svg
@@ -1,49 +1,61 @@
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.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 width="12" height="12" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
+ g {
+ stroke: ButtonText;
+ stroke-width: 0.9px;
+ fill: none;
+ }
+
g:not(:target) {
display: none;
}
+
use:target > g {
display: initial;
}
- g {
- stroke: ButtonText;
- stroke-width: 0.9px;
- fill: none;
- }
- g:not([id|="close"]) {
- shape-rendering: crispEdges;
+ .highlight > g {
+ stroke: HighlightText;
}
- .highcontrast {
- stroke-width: 1.9px;
+ .inactive > g {
+ stroke: black;
}
- .highcontrast-hover > g {
- stroke: HighlightText;
+
+ .bolder {
+ stroke-width: 1.6px;
+ stroke: black;
}
- .white > g {
- stroke: #fff;
+
+ .outline {
+ stroke-width: 4px;
+ stroke: white;
+ opacity: 0.75;
}
- .themes {
- stroke: #fff;
- stroke-width: 1.9px;
+
+ .inverted {
+ stroke-width: 1.6px;
+ stroke: white;
}
- .outer-stroke {
- stroke: #000;
- stroke-width: 3.6;
- opacity: .75;
+ .outline-inverted {
+ stroke-width: 4px;
+ stroke: black;
+ opacity: 0.75;
}
- .restore-background-window {
- stroke-width: .9;
+
+ .outline-thinner {
+ stroke-width: 3.6px;
}
+
</style>
+
<g id="close">
- <path d="M1,1 l 10,10 M1,11 l 10,-10"/>
+ <line x1="1" y1="1" x2="11" y2="11" stroke-width="1px"/>
+ <line x1="11" y1="1" x2="1" y2="11" stroke-width="1px"/>
</g>
<g id="maximize">
<rect x="1.5" y="1.5" width="9" height="9"/>
@@ -56,45 +68,54 @@
<polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5"/>
</g>
- <use id="close-white" class="white" xlink:href="#close"/>
- <use id="maximize-white" class="white" xlink:href="#maximize"/>
- <use id="minimize-white" class="white" xlink:href="#minimize"/>
- <use id="restore-white" class="white" xlink:href="#restore"/>
-
- <g id="close-highcontrast" class="highcontrast">
- <path d="M1,1 l 10,10 M1,11 l 10,-10"/>
+ <g id="close-outline">
+ <line x1="1" y1="1" x2="11" y2="11" stroke-linecap="round" class="outline"/>
+ <line x1="11" y1="1" x2="1" y2="11" stroke-linecap="round" class="outline"/>
+ <line x1="1" y1="1" x2="11" y2="11" class="bolder"/>
+ <line x1="11" y1="1" x2="1" y2="11" class="bolder"/>
</g>
- <g id="maximize-highcontrast" class="highcontrast">
- <rect x="2" y="2" width="8" height="8"/>
+ <g id="maximize-outline">
+ <rect x="1.2" y="1.2" width="9.6" height="9.6" stroke-linecap="round" class="outline"/>
+ <rect x="1.5" y="1.5" width="9" height="9" class="bolder"/>
</g>
- <g id="minimize-highcontrast" class="highcontrast">
- <line x1="1" y1="6" x2="11" y2="6"/>
+ <g id="minimize-outline">
+ <line x1="1" y1="5.5" x2="11" y2="5.5" stroke-linecap="round" class="outline outline-thinner"/>
+ <line x1="1" y1="5.5" x2="11" y2="5.5" class="bolder"/>
</g>
- <g id="restore-highcontrast" class="highcontrast">
- <rect x="2" y="4" width="6" height="6"/>
- <polyline points="3.5,1.5 10.5,1.5 10.5,8.5" class="restore-background-window"/>
+ <g id="restore-outline">
+ <rect x="1.5" y="3.5" width="7" height="7" stroke-linecap="round" class="outline"/>
+ <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5" stroke-linecap="round" class="outline"/>
+ <rect x="1.5" y="3.5" width="7" height="7" class="bolder"/>
+ <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5" class="bolder"/>
</g>
- <use id="close-highcontrast-hover" class="highcontrast-hover" xlink:href="#close-highcontrast"/>
- <use id="maximize-highcontrast-hover" class="highcontrast-hover" xlink:href="#maximize-highcontrast"/>
- <use id="minimize-highcontrast-hover" class="highcontrast-hover" xlink:href="#minimize-highcontrast"/>
- <use id="restore-highcontrast-hover" class="highcontrast-hover" xlink:href="#restore-highcontrast"/>
-
- <g id="close-themes" class="themes">
- <path d="M1,1 l 10,10 M1,11 l 10,-10" class="outer-stroke" />
- <path d="M1.75,1.75 l 8.5,8.5 M1.75,10.25 l 8.5,-8.5"/>
+ <g id="close-outline-inverted">
+ <line x1="1" y1="1" x2="11" y2="11" stroke-linecap="round" class="outline-inverted"/>
+ <line x1="11" y1="1" x2="1" y2="11" stroke-linecap="round" class="outline-inverted"/>
+ <line x1="1" y1="1" x2="11" y2="11" class="inverted"/>
+ <line x1="11" y1="1" x2="1" y2="11" class="inverted"/>
</g>
- <g id="maximize-themes" class="themes">
- <rect x="2" y="2" width="8" height="8" class="outer-stroke"/>
- <rect x="2" y="2" width="8" height="8"/>
+ <g id="maximize-outline-inverted">
+ <rect x="1.2" y="1.2" width="9.6" height="9.6" stroke-linecap="round" class="outline-inverted"/>
+ <rect x="1.5" y="1.5" width="9" height="9" class="inverted"/>
</g>
- <g id="minimize-themes" class="themes">
- <line x1="0" y1="6" x2="12" y2="6" class="outer-stroke"/>
- <line x1="1" y1="6" x2="11" y2="6"/>
+ <g id="minimize-outline-inverted">
+ <line x1="1" y1="5.5" x2="11" y2="5.5" stroke-linecap="round" class="outline-inverted outline-thinner"/>
+ <line x1="1" y1="5.5" x2="11" y2="5.5" class="inverted"/>
</g>
- <g id="restore-themes" class="themes">
- <path d="M2,4 l 6,0 l 0,6 l -6,0z M2.5,1.5 l 8,0 l 0,8" class="outer-stroke"/>
- <rect x="2" y="4" width="6" height="6"/>
- <polyline points="3.5,1.5 10.5,1.5 10.5,8.5" class="restore-background-window"/>
+ <g id="restore-outline-inverted">
+ <rect x="1.5" y="3.5" width="7" height="7" stroke-linecap="round" class="outline-inverted"/>
+ <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5" stroke-linecap="round" class="outline-inverted"/>
+ <rect x="1.5" y="3.5" width="7" height="7" class="inverted"/>
+ <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5" class="inverted"/>
</g>
+
+ <use id="close-highlight" class="highlight" xlink:href="#close"/>
+ <use id="maximize-highlight" class="highlight" xlink:href="#maximize"/>
+ <use id="minimize-highlight" class="highlight" xlink:href="#minimize"/>
+ <use id="restore-highlight" class="highlight" xlink:href="#restore"/>
+ <use id="close-inactive" class="inactive" xlink:href="#close"/>
+ <use id="maximize-inactive" class="inactive" xlink:href="#maximize"/>
+ <use id="minimize-inactive" class="inactive" xlink:href="#minimize"/>
+ <use id="restore-inactive" class="inactive" xlink:href="#restore"/>
</svg>
diff --git a/browser/themes/windows/customizableui/panelUI.css b/browser/themes/windows/customizableui/panelUI.css
index 92080d599..189a163f3 100644
--- a/browser/themes/windows/customizableui/panelUI.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -131,21 +131,17 @@ menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
}
/* Win8 and beyond. */
-@media not all and (-moz-os-version: windows-xp) {
- @media not all and (-moz-os-version: windows-vista) {
- @media not all and (-moz-os-version: windows-win7) {
- panelview .toolbarbutton-1,
- .subviewbutton,
- .widget-overflow-list .toolbarbutton-1,
- .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
- #BMB_bookmarksPopup menupopup[placespopup=true] > hbox,
- #edit-controls@inAnyPanel@,
- #zoom-controls@inAnyPanel@,
- #edit-controls@inAnyPanel@ > toolbarbutton,
- #zoom-controls@inAnyPanel@ > toolbarbutton {
- border-radius: 0;
- }
- }
+@media not all and (-moz-os-version: windows-win7) {
+ panelview .toolbarbutton-1,
+ .subviewbutton,
+ .widget-overflow-list .toolbarbutton-1,
+ .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
+ #BMB_bookmarksPopup menupopup[placespopup=true] > hbox,
+ #edit-controls@inAnyPanel@,
+ #zoom-controls@inAnyPanel@,
+ #edit-controls@inAnyPanel@ > toolbarbutton,
+ #zoom-controls@inAnyPanel@ > toolbarbutton {
+ border-radius: 0;
}
}
diff --git a/browser/themes/windows/devedition.css b/browser/themes/windows/devedition.css
index 4c25f33a1..bdf4bb80e 100644
--- a/browser/themes/windows/devedition.css
+++ b/browser/themes/windows/devedition.css
@@ -16,7 +16,7 @@
/* The window background is white due to no accentcolor in the lightweight
theme. It can't be changed to transparent when there is no compositor
- (Win XP or 7 in classic / basic theme), or else dragging and focus become
+ (Win 7 in classic / basic theme), or else dragging and focus become
broken. So instead just show the normal titlebar in that case, and override
the window color as transparent when the compositor is available. */
@media not all and (-moz-windows-compositor) {
@@ -116,9 +116,7 @@
}
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7),
+@media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {
:root {
--space-above-tabbar: 15px;
@@ -193,14 +191,12 @@
/* Use proper menu text styling in Win7 classic mode (copied from browser.css) */
@media not all and (-moz-windows-compositor),
not all and (-moz-windows-default-theme) {
- #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar,
- #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar {
- color: CaptionText;
+ :root[tabsintitlebar]:not([inFullscreen]) {
+ --titlebar-text-color: CaptionText;
}
- #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:-moz-window-inactive,
- #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:-moz-window-inactive {
- color: InactiveCaptionText;
+ :root[tabsintitlebar]:not([inFullscreen]):-moz-window-inactive {
+ --titlebar-text-color: InactiveCaptionText;
}
#main-window[tabsintitlebar] #main-menubar > menu {
@@ -263,8 +259,7 @@
color: var(--chrome-color);
}
-@media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7),
+@media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {
/* And then we add them back on toolbars so that they don't look borderless: */
#main-window:not([customizing])[sizemode=normal] #navigator-toolbox::after,
diff --git a/browser/themes/windows/downloads/allDownloadsViewOverlay.css b/browser/themes/windows/downloads/allDownloadsViewOverlay.css
index e288f1e90..0ee83f669 100644
--- a/browser/themes/windows/downloads/allDownloadsViewOverlay.css
+++ b/browser/themes/windows/downloads/allDownloadsViewOverlay.css
@@ -18,32 +18,30 @@
/*** Highlighted list items ***/
-@media not all and (-moz-os-version: windows-xp) {
- @media (-moz-windows-default-theme) {
- /*
- -moz-appearance: menuitem is almost right, but the hover effect is not
- transparent and is lighter than desired.
-
- Copied from the autocomplete richlistbox styling in
- toolkit/themes/windows/global/autocomplete.css
-
- This styling should be kept in sync with the style from the above file.
- */
- @itemFocused@ {
- color: inherit;
- background-color: transparent;
- /* four gradients for the bevel highlights on each edge, one for blue background */
- background-image:
- linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, transparent 3px),
- linear-gradient(to right, rgba(255,255,255,0.5) 3px, transparent 3px),
- linear-gradient(to left, rgba(255,255,255,0.5) 3px, transparent 3px),
- linear-gradient(to top, rgba(255,255,255,0.4) 3px, transparent 3px),
- linear-gradient(to bottom, rgba(163,196,247,0.3), rgba(122,180,246,0.3));
- background-clip: content-box;
- border-radius: 6px;
- outline: 1px solid rgb(124,163,206);
- -moz-outline-radius: 3px;
- outline-offset: -2px;
- }
+@media (-moz-windows-default-theme) {
+ /*
+ -moz-appearance: menuitem is almost right, but the hover effect is not
+ transparent and is lighter than desired.
+
+ Copied from the autocomplete richlistbox styling in
+ toolkit/themes/windows/global/autocomplete.css
+
+ This styling should be kept in sync with the style from the above file.
+ */
+ @itemFocused@ {
+ color: inherit;
+ background-color: transparent;
+ /* four gradients for the bevel highlights on each edge, one for blue background */
+ background-image:
+ linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, transparent 3px),
+ linear-gradient(to right, rgba(255,255,255,0.5) 3px, transparent 3px),
+ linear-gradient(to left, rgba(255,255,255,0.5) 3px, transparent 3px),
+ linear-gradient(to top, rgba(255,255,255,0.4) 3px, transparent 3px),
+ linear-gradient(to bottom, rgba(163,196,247,0.3), rgba(122,180,246,0.3));
+ background-clip: content-box;
+ border-radius: 6px;
+ outline: 1px solid rgb(124,163,206);
+ -moz-outline-radius: 3px;
+ outline-offset: -2px;
}
}
diff --git a/browser/themes/windows/downloads/download-glow-menuPanel-XPVista7.png b/browser/themes/windows/downloads/download-glow-menuPanel-win7.png
index 7ff7e6a03..7ff7e6a03 100644
--- a/browser/themes/windows/downloads/download-glow-menuPanel-XPVista7.png
+++ b/browser/themes/windows/downloads/download-glow-menuPanel-win7.png
Binary files differ
diff --git a/browser/themes/windows/downloads/download-glow-XPVista7.png b/browser/themes/windows/downloads/download-glow-win7.png
index e7415e83d..e7415e83d 100644
--- a/browser/themes/windows/downloads/download-glow-XPVista7.png
+++ b/browser/themes/windows/downloads/download-glow-win7.png
Binary files differ
diff --git a/browser/themes/windows/downloads/indicator.css b/browser/themes/windows/downloads/indicator.css
index 627265088..7f921f8de 100644
--- a/browser/themes/windows/downloads/indicator.css
+++ b/browser/themes/windows/downloads/indicator.css
@@ -166,13 +166,9 @@ toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #dow
font-size: 9px;
line-height: 9px;
text-align: center;
-}
-@media not all and (-moz-os-version: windows-xp) {
- #downloads-indicator-counter {
- /* Bug 812345 added this... */
- margin-bottom: -1px;
- }
+ /* Bug 812345 added this... */
+ margin-bottom: -1px;
}
toolbar[brighttext] #downloads-indicator-counter {
diff --git a/browser/themes/windows/feeds/feedIcon-XP.png b/browser/themes/windows/feeds/feedIcon-XP.png
deleted file mode 100644
index d0cafb1d4..000000000
--- a/browser/themes/windows/feeds/feedIcon-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/feeds/feedIcon16-XP.png b/browser/themes/windows/feeds/feedIcon16-XP.png
deleted file mode 100644
index dd7821f8d..000000000
--- a/browser/themes/windows/feeds/feedIcon16-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn
index 89c589aba..10abfd001 100644
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -11,21 +11,17 @@ browser.jar:
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
skin/classic/browser/actionicon-tab.png
skin/classic/browser/actionicon-tab@2x.png
- skin/classic/browser/actionicon-tab-XPVista7.png
+ skin/classic/browser/actionicon-tab-win7.png
* skin/classic/browser/browser.css
* skin/classic/browser/devedition.css
* skin/classic/browser/browser-lightweightTheme.css
skin/classic/browser/caption-buttons.svg
skin/classic/browser/click-to-play-warning-stripes.png
skin/classic/browser/Info.png
- skin/classic/browser/Info-XP.png
skin/classic/browser/keyhole-forward-mask.svg
skin/classic/browser/livemark-folder.png
- skin/classic/browser/livemark-folder-XP.png
skin/classic/browser/menu-back.png
- skin/classic/browser/menu-back-XP.png
skin/classic/browser/menu-forward.png
- skin/classic/browser/menu-forward-XP.png
skin/classic/browser/menuPanel-customize.png
skin/classic/browser/menuPanel-customize@2x.png
skin/classic/browser/menuPanel-exit.png
@@ -36,36 +32,33 @@ browser.jar:
skin/classic/browser/monitor_16-10.png
skin/classic/browser/pageInfo.css
skin/classic/browser/pageInfo.png
- skin/classic/browser/pageInfo-XP.png
skin/classic/browser/privatebrowsing-mask-tabstrip.png
- skin/classic/browser/privatebrowsing-mask-tabstrip-XPVista7.png
+ skin/classic/browser/privatebrowsing-mask-tabstrip-win7.png
skin/classic/browser/privatebrowsing-mask-titlebar.png
- skin/classic/browser/privatebrowsing-mask-titlebar-XPVista7.png
- skin/classic/browser/privatebrowsing-mask-titlebar-XPVista7-tall.png
+ skin/classic/browser/privatebrowsing-mask-titlebar-win7.png
+ skin/classic/browser/privatebrowsing-mask-titlebar-win7-tall.png
skin/classic/browser/reload-stop-go.png
skin/classic/browser/reload-stop-go@2x.png
- skin/classic/browser/reload-stop-go-XPVista7.png
- skin/classic/browser/reload-stop-go-XPVista7@2x.png
+ skin/classic/browser/reload-stop-go-win7.png
+ skin/classic/browser/reload-stop-go-win7@2x.png
skin/classic/browser/searchbar.css
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/slowStartup-16.png
skin/classic/browser/Toolbar.png
skin/classic/browser/Toolbar@2x.png
- skin/classic/browser/Toolbar-aero.png
- skin/classic/browser/Toolbar-aero@2x.png
+ skin/classic/browser/Toolbar-win7.png
+ skin/classic/browser/Toolbar-win7@2x.png
skin/classic/browser/Toolbar-inverted.png
skin/classic/browser/Toolbar-inverted@2x.png
- skin/classic/browser/Toolbar-lunaSilver.png
skin/classic/browser/Toolbar-win8.png
skin/classic/browser/Toolbar-win8@2x.png
- skin/classic/browser/Toolbar-XP.png
- skin/classic/browser/toolbarbutton-dropdown-arrow-XPVista7.png
+ skin/classic/browser/toolbarbutton-dropdown-arrow-win7.png
skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
skin/classic/browser/urlbar-popup-blocked.png
skin/classic/browser/urlbar-history-dropmarker.png
skin/classic/browser/urlbar-history-dropmarker@2x.png
- skin/classic/browser/urlbar-history-dropmarker-XPVista7.png
- skin/classic/browser/urlbar-history-dropmarker-XPVista7@2x.png
+ skin/classic/browser/urlbar-history-dropmarker-win7.png
+ skin/classic/browser/urlbar-history-dropmarker-win7@2x.png
skin/classic/browser/webRTC-indicator.css
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
@@ -78,57 +71,40 @@ browser.jar:
* skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
- skin/classic/browser/downloads/download-glow-menuPanel-XPVista7.png (downloads/download-glow-menuPanel-XPVista7.png)
+ skin/classic/browser/downloads/download-glow-menuPanel-win7.png (downloads/download-glow-menuPanel-win7.png)
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
* skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
- skin/classic/browser/feeds/feedIcon-XP.png (feeds/feedIcon-XP.png)
- skin/classic/browser/feeds/feedIcon16-XP.png (feeds/feedIcon16-XP.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/places/autocomplete-star.png (places/autocomplete-star.png)
skin/classic/browser/places/autocomplete-star@2x.png (places/autocomplete-star@2x.png)
- skin/classic/browser/places/autocomplete-star-XPVista7.png (places/autocomplete-star-XPVista7.png)
+ skin/classic/browser/places/autocomplete-star-win7.png (places/autocomplete-star-win7.png)
skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/query.png (places/query.png)
- skin/classic/browser/places/query-XP.png (places/query-XP.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
- skin/classic/browser/places/bookmarksMenu-XP.png (places/bookmarksMenu-XP.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
- skin/classic/browser/places/bookmarksToolbar-XP.png (places/bookmarksToolbar-XP.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
- skin/classic/browser/places/bookmarksToolbar-menuPanel-XP.png (places/bookmarksToolbar-menuPanel-XP.png)
skin/classic/browser/places/bookmarks-notification-finish.png (places/bookmarks-notification-finish.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
- skin/classic/browser/places/calendar-XP.png (places/calendar-XP.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
- skin/classic/browser/places/toolbarDropMarker-XP.png (places/toolbarDropMarker-XP.png)
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/libraryToolbar.png (places/libraryToolbar.png)
- skin/classic/browser/places/libraryToolbar-XP.png (places/libraryToolbar-XP.png)
skin/classic/browser/places/starred48.png (places/starred48.png)
- skin/classic/browser/places/starred48-XP.png (places/starred48-XP.png)
skin/classic/browser/places/unstarred48.png (places/unstarred48.png)
skin/classic/browser/places/tag.png (places/tag.png)
- skin/classic/browser/places/tag-XP.png (places/tag-XP.png)
skin/classic/browser/places/history.png (places/history.png)
- skin/classic/browser/places/history-XP.png (places/history-XP.png)
skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png)
- skin/classic/browser/places/allBookmarks-XP.png (places/allBookmarks-XP.png)
skin/classic/browser/places/unsortedBookmarks.png (places/unsortedBookmarks.png)
- skin/classic/browser/places/unsortedBookmarks-XP.png (places/unsortedBookmarks-XP.png)
skin/classic/browser/places/downloads.png (places/downloads.png)
skin/classic/browser/places/livemark-item.png (places/livemark-item.png)
skin/classic/browser/preferences/alwaysAsk.png (preferences/alwaysAsk.png)
- skin/classic/browser/preferences/alwaysAsk-XP.png (preferences/alwaysAsk-XP.png)
skin/classic/browser/preferences/application.png (preferences/application.png)
- skin/classic/browser/preferences/application-XP.png (preferences/application-XP.png)
skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png)
- skin/classic/browser/preferences/saveFile-XP.png (preferences/saveFile-XP.png)
skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
* skin/classic/browser/preferences/in-content/dialog.css (preferences/in-content/dialog.css)
@@ -136,13 +112,13 @@ browser.jar:
skin/classic/browser/social/services-16.png (social/services-16.png)
skin/classic/browser/social/services-64.png (social/services-64.png)
skin/classic/browser/tabbrowser/newtab.svg (tabbrowser/newtab.svg)
- skin/classic/browser/tabbrowser/newtab-XPVista7.svg (tabbrowser/newtab-XPVista7.svg)
+ skin/classic/browser/tabbrowser/newtab-win7.svg (tabbrowser/newtab-win7.svg)
skin/classic/browser/tabbrowser/newtab-inverted.svg (tabbrowser/newtab-inverted.svg)
- skin/classic/browser/tabbrowser/newtab-inverted-XPVista7.svg (tabbrowser/newtab-inverted-XPVista7.svg)
+ skin/classic/browser/tabbrowser/newtab-inverted-win7.svg (tabbrowser/newtab-inverted-win7.svg)
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
skin/classic/browser/tabbrowser/tab-arrow-left.svg (tabbrowser/tab-arrow-left.svg)
- skin/classic/browser/tabbrowser/tab-arrow-left-XPVista7.svg (tabbrowser/tab-arrow-left-XPVista7.svg)
+ skin/classic/browser/tabbrowser/tab-arrow-left-win7.svg (tabbrowser/tab-arrow-left-win7.svg)
skin/classic/browser/tabbrowser/tab-arrow-left-inverted.svg (tabbrowser/tab-arrow-left-inverted.svg)
skin/classic/browser/tabbrowser/tab-background-start.png (tabbrowser/tab-background-start.png)
skin/classic/browser/tabbrowser/tab-background-start@2x.png (tabbrowser/tab-background-start@2x.png)
@@ -174,8 +150,8 @@ browser.jar:
skin/classic/browser/sync-desktopIcon.svg (../shared/sync-desktopIcon.svg)
skin/classic/browser/sync-horizontalbar.png
skin/classic/browser/sync-horizontalbar@2x.png
- skin/classic/browser/sync-horizontalbar-XPVista7.png
- skin/classic/browser/sync-horizontalbar-XPVista7@2x.png
+ skin/classic/browser/sync-horizontalbar-win7.png
+ skin/classic/browser/sync-horizontalbar-win7@2x.png
skin/classic/browser/sync-mobileIcon.svg (../shared/sync-mobileIcon.svg)
skin/classic/browser/sync-notification-24.png
skin/classic/browser/syncSetup.css
@@ -183,16 +159,16 @@ browser.jar:
skin/classic/browser/syncQuota.css
skin/classic/browser/syncProgress-horizontalbar.png
skin/classic/browser/syncProgress-horizontalbar@2x.png
- skin/classic/browser/syncProgress-horizontalbar-XPVista7.png
- skin/classic/browser/syncProgress-horizontalbar-XPVista7@2x.png
+ skin/classic/browser/syncProgress-horizontalbar-win7.png
+ skin/classic/browser/syncProgress-horizontalbar-win7@2x.png
skin/classic/browser/syncProgress-menuPanel.png
skin/classic/browser/syncProgress-menuPanel@2x.png
skin/classic/browser/syncProgress-toolbar.png
skin/classic/browser/syncProgress-toolbar@2x.png
skin/classic/browser/syncProgress-toolbar-inverted.png
skin/classic/browser/syncProgress-toolbar-inverted@2x.png
- skin/classic/browser/syncProgress-toolbar-XPVista7.png
- skin/classic/browser/syncProgress-toolbar-XPVista7@2x.png
+ skin/classic/browser/syncProgress-toolbar-win7.png
+ skin/classic/browser/syncProgress-toolbar-win7@2x.png
#ifdef E10S_TESTING_ONLY
skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
#endif
@@ -205,58 +181,31 @@ browser.jar:
% override chrome://browser/skin/feeds/videoFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
% override chrome://browser/skin/aboutSessionRestore-window-icon.png chrome://browser/skin/preferences/application.png os!=WINNT
-% override chrome://browser/skin/aboutSessionRestore-window-icon.png chrome://browser/skin/preferences/application.png os=WINNT osversion<6
-% override chrome://browser/skin/Info.png chrome://browser/skin/Info-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/livemark-folder.png chrome://browser/skin/livemark-folder-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/menu-back.png chrome://browser/skin/menu-back-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/menu-forward.png chrome://browser/skin/menu-forward-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/pageInfo.png chrome://browser/skin/pageInfo-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/feeds/feedIcon.png chrome://browser/skin/feeds/feedIcon-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/feeds/feedIcon16.png chrome://browser/skin/feeds/feedIcon16-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/query.png chrome://browser/skin/places/query-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/bookmarksMenu.png chrome://browser/skin/places/bookmarksMenu-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/bookmarksToolbar.png chrome://browser/skin/places/bookmarksToolbar-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/bookmarksToolbar-menuPanel.png chrome://browser/skin/places/bookmarksToolbar-menuPanel-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/calendar.png chrome://browser/skin/places/calendar-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/toolbarDropMarker.png chrome://browser/skin/places/toolbarDropMarker-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/libraryToolbar.png chrome://browser/skin/places/libraryToolbar-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/starred48.png chrome://browser/skin/places/starred48-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/tag.png chrome://browser/skin/places/tag-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/history.png chrome://browser/skin/places/history-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/allBookmarks.png chrome://browser/skin/places/allBookmarks-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/places/unsortedBookmarks.png chrome://browser/skin/places/unsortedBookmarks-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/preferences/alwaysAsk.png chrome://browser/skin/preferences/alwaysAsk-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/preferences/application.png chrome://browser/skin/preferences/application-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/preferences/saveFile.png chrome://browser/skin/preferences/saveFile-XP.png os=WINNT osversion<6
+% override chrome://browser/skin/actionicon-tab.png chrome://browser/skin/actionicon-tab-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/privatebrowsing-mask-tabstrip.png chrome://browser/skin/privatebrowsing-mask-tabstrip-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/privatebrowsing-mask-titlebar.png chrome://browser/skin/privatebrowsing-mask-titlebar-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/reload-stop-go.png chrome://browser/skin/reload-stop-go-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/reload-stop-go@2x.png chrome://browser/skin/reload-stop-go-win7@2x.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/sync-horizontalbar.png chrome://browser/skin/sync-horizontalbar-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/sync-horizontalbar@2x.png chrome://browser/skin/sync-horizontalbar-win7@2x.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/syncProgress-horizontalbar.png chrome://browser/skin/syncProgress-horizontalbar-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/syncProgress-horizontalbar@2x.png chrome://browser/skin/syncProgress-horizontalbar-win7@2x.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/syncProgress-toolbar.png chrome://browser/skin/syncProgress-toolbar-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/syncProgress-toolbar@2x.png chrome://browser/skin/syncProgress-toolbar-win7@2x.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/toolbarbutton-dropdown-arrow.png chrome://browser/skin/toolbarbutton-dropdown-arrow-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/urlbar-history-dropmarker.png chrome://browser/skin/urlbar-history-dropmarker-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/urlbar-history-dropmarker@2x.png chrome://browser/skin/urlbar-history-dropmarker-win7@2x.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/downloads/download-glow-menuPanel.png chrome://browser/skin/downloads/download-glow-menuPanel-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/places/autocomplete-star.png chrome://browser/skin/places/autocomplete-star-win7.png os=WINNT osversion<=6.1
+% override chrome://browser/skin/tabbrowser/newtab.svg chrome://browser/skin/tabbrowser/newtab-win7.svg os=WINNT osversion<=6.1
+% override chrome://browser/skin/tabbrowser/newtab-inverted.svg chrome://browser/skin/tabbrowser/newtab-inverted-win7.svg os=WINNT osversion<=6.1
+% override chrome://browser/skin/tabbrowser/tab-arrow-left.svg chrome://browser/skin/tabbrowser/tab-arrow-left-win7.svg os=WINNT osversion<=6.1
-% override chrome://browser/skin/actionicon-tab.png chrome://browser/skin/actionicon-tab-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/privatebrowsing-mask-tabstrip.png chrome://browser/skin/privatebrowsing-mask-tabstrip-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/privatebrowsing-mask-titlebar.png chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/reload-stop-go.png chrome://browser/skin/reload-stop-go-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/reload-stop-go@2x.png chrome://browser/skin/reload-stop-go-XPVista7@2x.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/sync-horizontalbar.png chrome://browser/skin/sync-horizontalbar-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/sync-horizontalbar@2x.png chrome://browser/skin/sync-horizontalbar-XPVista7@2x.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/syncProgress-horizontalbar.png chrome://browser/skin/syncProgress-horizontalbar-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/syncProgress-horizontalbar@2x.png chrome://browser/skin/syncProgress-horizontalbar-XPVista7@2x.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/syncProgress-toolbar.png chrome://browser/skin/syncProgress-toolbar-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/syncProgress-toolbar@2x.png chrome://browser/skin/syncProgress-toolbar-XPVista7@2x.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/toolbarbutton-dropdown-arrow.png chrome://browser/skin/toolbarbutton-dropdown-arrow-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/urlbar-history-dropmarker.png chrome://browser/skin/urlbar-history-dropmarker-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/urlbar-history-dropmarker@2x.png chrome://browser/skin/urlbar-history-dropmarker-XPVista7@2x.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/downloads/download-glow-menuPanel.png chrome://browser/skin/downloads/download-glow-menuPanel-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/places/autocomplete-star.png chrome://browser/skin/places/autocomplete-star-XPVista7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/tabbrowser/newtab.svg chrome://browser/skin/tabbrowser/newtab-XPVista7.svg os=WINNT osversion<=6.1
-% override chrome://browser/skin/tabbrowser/newtab-inverted.svg chrome://browser/skin/tabbrowser/newtab-inverted-XPVista7.svg os=WINNT osversion<=6.1
-% override chrome://browser/skin/tabbrowser/tab-arrow-left.svg chrome://browser/skin/tabbrowser/tab-arrow-left-XPVista7.svg os=WINNT osversion<=6.1
-
-% override chrome://browser/skin/Toolbar@2x.png chrome://browser/skin/Toolbar-aero@2x.png os=WINNT osversion=6
-% override chrome://browser/skin/Toolbar@2x.png chrome://browser/skin/Toolbar-aero@2x.png os=WINNT osversion=6.1
+% override chrome://browser/skin/Toolbar@2x.png chrome://browser/skin/Toolbar-win7@2x.png os=WINNT osversion=6.1
% override chrome://browser/skin/Toolbar@2x.png chrome://browser/skin/Toolbar-win8@2x.png os=WINNT osversion=6.2
% override chrome://browser/skin/Toolbar@2x.png chrome://browser/skin/Toolbar-win8@2x.png os=WINNT osversion=6.3
-% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-XP.png os=WINNT osversion<6
-% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-aero.png os=WINNT osversion=6
-% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-aero.png os=WINNT osversion=6.1
+% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-win7.png os=WINNT osversion=6.1
% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-win8.png os=WINNT osversion=6.2
% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-win8.png os=WINNT osversion=6.3
diff --git a/browser/themes/windows/livemark-folder-XP.png b/browser/themes/windows/livemark-folder-XP.png
deleted file mode 100644
index 00aa0364d..000000000
--- a/browser/themes/windows/livemark-folder-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/menu-back-XP.png b/browser/themes/windows/menu-back-XP.png
deleted file mode 100644
index ecb8ccd1a..000000000
--- a/browser/themes/windows/menu-back-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/menu-forward-XP.png b/browser/themes/windows/menu-forward-XP.png
deleted file mode 100644
index a7460dc27..000000000
--- a/browser/themes/windows/menu-forward-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/pageInfo-XP.png b/browser/themes/windows/pageInfo-XP.png
deleted file mode 100644
index bbf257237..000000000
--- a/browser/themes/windows/pageInfo-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/allBookmarks-XP.png b/browser/themes/windows/places/allBookmarks-XP.png
deleted file mode 100644
index f7903cc5f..000000000
--- a/browser/themes/windows/places/allBookmarks-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/autocomplete-star-XPVista7.png b/browser/themes/windows/places/autocomplete-star-win7.png
index af694e91c..af694e91c 100644
--- a/browser/themes/windows/places/autocomplete-star-XPVista7.png
+++ b/browser/themes/windows/places/autocomplete-star-win7.png
Binary files differ
diff --git a/browser/themes/windows/places/bookmarksMenu-XP.png b/browser/themes/windows/places/bookmarksMenu-XP.png
deleted file mode 100644
index 8f0c8bf58..000000000
--- a/browser/themes/windows/places/bookmarksMenu-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/bookmarksToolbar-XP.png b/browser/themes/windows/places/bookmarksToolbar-XP.png
deleted file mode 100644
index 9e988de20..000000000
--- a/browser/themes/windows/places/bookmarksToolbar-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/bookmarksToolbar-menuPanel-XP.png b/browser/themes/windows/places/bookmarksToolbar-menuPanel-XP.png
deleted file mode 100644
index 0e4247adb..000000000
--- a/browser/themes/windows/places/bookmarksToolbar-menuPanel-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/calendar-XP.png b/browser/themes/windows/places/calendar-XP.png
deleted file mode 100644
index 7645af5cd..000000000
--- a/browser/themes/windows/places/calendar-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/history-XP.png b/browser/themes/windows/places/history-XP.png
deleted file mode 100644
index fcc89bbbf..000000000
--- a/browser/themes/windows/places/history-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/libraryToolbar-XP.png b/browser/themes/windows/places/libraryToolbar-XP.png
deleted file mode 100644
index 75b390ff6..000000000
--- a/browser/themes/windows/places/libraryToolbar-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/organizer.css b/browser/themes/windows/places/organizer.css
index 4de603b9f..9ae4c8370 100644
--- a/browser/themes/windows/places/organizer.css
+++ b/browser/themes/windows/places/organizer.css
@@ -20,13 +20,6 @@
list-style-image: url("chrome://browser/skin/Toolbar.png");
}
-@media (-moz-windows-theme: luna-silver) {
- #back-button,
- #forward-button {
- list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png");
- }
-}
-
#back-button {
-moz-image-region: rect(0, 54px, 18px, 36px);
}
@@ -119,11 +112,6 @@
-moz-image-region: rect(16px, 48px, 32px, 32px);
}
-/* Root View */
-#placesView {
- border-top: 1px solid ThreeDDarkShadow;
-}
-
/* Info box */
#detailsDeck {
border-top: 1px solid ThreeDShadow;
@@ -151,35 +139,28 @@
padding-inline-end: 9px;
}
-
-@media not all and (-moz-os-version: windows-xp) {
- #placesView {
- border-top: none;
+@media not all and (-moz-windows-classic) {
+ #placesToolbox {
+ -moz-appearance: none;
+ background-color: transparent;
}
- @media not all and (-moz-windows-classic) {
- #placesToolbox {
- -moz-appearance: none;
- background-color: transparent;
- }
-
- #placesToolbar {
- -moz-appearance: none;
- background-color: -moz-Dialog;
- color: -moz-dialogText;
- }
+ #placesToolbar {
+ -moz-appearance: none;
+ background-color: -moz-Dialog;
+ color: -moz-dialogText;
}
+}
- @media (-moz-windows-default-theme) {
- #placesView > splitter {
- border: 0;
- border-inline-end: 1px solid #A9B7C9;
- min-width: 0;
- width: 3px;
- background-color: transparent;
- margin-inline-start: -3px;
- position: relative;
- }
+@media (-moz-windows-default-theme) {
+ #placesView > splitter {
+ border: 0;
+ border-inline-end: 1px solid #A9B7C9;
+ min-width: 0;
+ width: 3px;
+ background-color: transparent;
+ margin-inline-start: -3px;
+ position: relative;
}
}
@@ -193,8 +174,7 @@
}
}
-@media (-moz-windows-default-theme) and (-moz-os-version: windows-vista),
- (-moz-windows-default-theme) and (-moz-os-version: windows-win7) {
+@media (-moz-windows-default-theme) and (-moz-os-version: windows-win7) {
#placesView,
#infoPane,
#placesList,
diff --git a/browser/themes/windows/places/places.css b/browser/themes/windows/places/places.css
index 4ec8f6555..769cfcc25 100644
--- a/browser/themes/windows/places/places.css
+++ b/browser/themes/windows/places/places.css
@@ -24,21 +24,17 @@
cursor: default;
}
-/* Style Places sidebars as Vista media collection */
@media (-moz-windows-default-theme) {
- @media not all and (-moz-os-version: windows-xp) {
- .sidebar-placesTree {
- background-color: transparent;
- border-top: none;
- }
+ .sidebar-placesTree {
+ background-color: transparent;
+ border-top: none;
+ }
- .sidebar-placesTreechildren::-moz-tree-cell-text(leaf, hover) {
- text-decoration: none;
- }
+ .sidebar-placesTreechildren::-moz-tree-cell-text(leaf, hover) {
+ text-decoration: none;
}
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+ @media (-moz-os-version: windows-win7) {
#bookmarksPanel,
#history-panel,
#tabs-panel {
diff --git a/browser/themes/windows/places/query-XP.png b/browser/themes/windows/places/query-XP.png
deleted file mode 100644
index 9e79fc791..000000000
--- a/browser/themes/windows/places/query-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/starred48-XP.png b/browser/themes/windows/places/starred48-XP.png
deleted file mode 100644
index 1cb7bc57d..000000000
--- a/browser/themes/windows/places/starred48-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/tag-XP.png b/browser/themes/windows/places/tag-XP.png
deleted file mode 100644
index 4b4a13e66..000000000
--- a/browser/themes/windows/places/tag-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/toolbarDropMarker-XP.png b/browser/themes/windows/places/toolbarDropMarker-XP.png
deleted file mode 100644
index 9173b7a7a..000000000
--- a/browser/themes/windows/places/toolbarDropMarker-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/places/unsortedBookmarks-XP.png b/browser/themes/windows/places/unsortedBookmarks-XP.png
deleted file mode 100644
index cf73f9464..000000000
--- a/browser/themes/windows/places/unsortedBookmarks-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/preferences/alwaysAsk-XP.png b/browser/themes/windows/preferences/alwaysAsk-XP.png
deleted file mode 100644
index 8693211ac..000000000
--- a/browser/themes/windows/preferences/alwaysAsk-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/preferences/application-XP.png b/browser/themes/windows/preferences/application-XP.png
deleted file mode 100644
index 7d279ff84..000000000
--- a/browser/themes/windows/preferences/application-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/preferences/saveFile-XP.png b/browser/themes/windows/preferences/saveFile-XP.png
deleted file mode 100644
index e115eaa9f..000000000
--- a/browser/themes/windows/preferences/saveFile-XP.png
+++ /dev/null
Binary files differ
diff --git a/browser/themes/windows/privatebrowsing-mask-tabstrip-XPVista7.png b/browser/themes/windows/privatebrowsing-mask-tabstrip-win7.png
index bd5d46a76..bd5d46a76 100644
--- a/browser/themes/windows/privatebrowsing-mask-tabstrip-XPVista7.png
+++ b/browser/themes/windows/privatebrowsing-mask-tabstrip-win7.png
Binary files differ
diff --git a/browser/themes/windows/privatebrowsing-mask-titlebar-XPVista7-tall.png b/browser/themes/windows/privatebrowsing-mask-titlebar-win7-tall.png
index 4a723c54e..4a723c54e 100644
--- a/browser/themes/windows/privatebrowsing-mask-titlebar-XPVista7-tall.png
+++ b/browser/themes/windows/privatebrowsing-mask-titlebar-win7-tall.png
Binary files differ
diff --git a/browser/themes/windows/privatebrowsing-mask-titlebar-XPVista7.png b/browser/themes/windows/privatebrowsing-mask-titlebar-win7.png
index 835912b53..835912b53 100644
--- a/browser/themes/windows/privatebrowsing-mask-titlebar-XPVista7.png
+++ b/browser/themes/windows/privatebrowsing-mask-titlebar-win7.png
Binary files differ
diff --git a/browser/themes/windows/reload-stop-go-XPVista7.png b/browser/themes/windows/reload-stop-go-win7.png
index 3ef32c3ce..3ef32c3ce 100644
--- a/browser/themes/windows/reload-stop-go-XPVista7.png
+++ b/browser/themes/windows/reload-stop-go-win7.png
Binary files differ
diff --git a/browser/themes/windows/reload-stop-go-XPVista7@2x.png b/browser/themes/windows/reload-stop-go-win7@2x.png
index 38b27bf0c..38b27bf0c 100644
--- a/browser/themes/windows/reload-stop-go-XPVista7@2x.png
+++ b/browser/themes/windows/reload-stop-go-win7@2x.png
Binary files differ
diff --git a/browser/themes/windows/sync-horizontalbar-XPVista7.png b/browser/themes/windows/sync-horizontalbar-win7.png
index 2c97ce6db..2c97ce6db 100644
--- a/browser/themes/windows/sync-horizontalbar-XPVista7.png
+++ b/browser/themes/windows/sync-horizontalbar-win7.png
Binary files differ
diff --git a/browser/themes/windows/sync-horizontalbar-XPVista7@2x.png b/browser/themes/windows/sync-horizontalbar-win7@2x.png
index ee117ab73..ee117ab73 100644
--- a/browser/themes/windows/sync-horizontalbar-XPVista7@2x.png
+++ b/browser/themes/windows/sync-horizontalbar-win7@2x.png
Binary files differ
diff --git a/browser/themes/windows/syncProgress-horizontalbar-XPVista7.png b/browser/themes/windows/syncProgress-horizontalbar-win7.png
index 48cd11055..48cd11055 100644
--- a/browser/themes/windows/syncProgress-horizontalbar-XPVista7.png
+++ b/browser/themes/windows/syncProgress-horizontalbar-win7.png
Binary files differ
diff --git a/browser/themes/windows/syncProgress-horizontalbar-XPVista7@2x.png b/browser/themes/windows/syncProgress-horizontalbar-win7@2x.png
index 741dd2ed4..741dd2ed4 100644
--- a/browser/themes/windows/syncProgress-horizontalbar-XPVista7@2x.png
+++ b/browser/themes/windows/syncProgress-horizontalbar-win7@2x.png
Binary files differ
diff --git a/browser/themes/windows/syncProgress-toolbar-XPVista7.png b/browser/themes/windows/syncProgress-toolbar-win7.png
index 49e224f0d..49e224f0d 100644
--- a/browser/themes/windows/syncProgress-toolbar-XPVista7.png
+++ b/browser/themes/windows/syncProgress-toolbar-win7.png
Binary files differ
diff --git a/browser/themes/windows/syncProgress-toolbar-XPVista7@2x.png b/browser/themes/windows/syncProgress-toolbar-win7@2x.png
index fd2038725..fd2038725 100644
--- a/browser/themes/windows/syncProgress-toolbar-XPVista7@2x.png
+++ b/browser/themes/windows/syncProgress-toolbar-win7@2x.png
Binary files differ
diff --git a/browser/themes/windows/tabbrowser/newtab-inverted-XPVista7.svg b/browser/themes/windows/tabbrowser/newtab-inverted-win7.svg
index 10ffbc745..10ffbc745 100644
--- a/browser/themes/windows/tabbrowser/newtab-inverted-XPVista7.svg
+++ b/browser/themes/windows/tabbrowser/newtab-inverted-win7.svg
diff --git a/browser/themes/windows/tabbrowser/newtab-XPVista7.svg b/browser/themes/windows/tabbrowser/newtab-win7.svg
index 3f431c9db..3f431c9db 100644
--- a/browser/themes/windows/tabbrowser/newtab-XPVista7.svg
+++ b/browser/themes/windows/tabbrowser/newtab-win7.svg
diff --git a/browser/themes/windows/tabbrowser/tab-arrow-left-XPVista7.svg b/browser/themes/windows/tabbrowser/tab-arrow-left-win7.svg
index 41bb5ab13..41bb5ab13 100644
--- a/browser/themes/windows/tabbrowser/tab-arrow-left-XPVista7.svg
+++ b/browser/themes/windows/tabbrowser/tab-arrow-left-win7.svg
diff --git a/browser/themes/windows/toolbarbutton-dropdown-arrow-XPVista7.png b/browser/themes/windows/toolbarbutton-dropdown-arrow-win7.png
index 5f892f532..5f892f532 100644
--- a/browser/themes/windows/toolbarbutton-dropdown-arrow-XPVista7.png
+++ b/browser/themes/windows/toolbarbutton-dropdown-arrow-win7.png
Binary files differ
diff --git a/browser/themes/windows/urlbar-history-dropmarker-XPVista7.png b/browser/themes/windows/urlbar-history-dropmarker-win7.png
index b03338822..b03338822 100644
--- a/browser/themes/windows/urlbar-history-dropmarker-XPVista7.png
+++ b/browser/themes/windows/urlbar-history-dropmarker-win7.png
Binary files differ
diff --git a/browser/themes/windows/urlbar-history-dropmarker-XPVista7@2x.png b/browser/themes/windows/urlbar-history-dropmarker-win7@2x.png
index bff2997f8..bff2997f8 100644
--- a/browser/themes/windows/urlbar-history-dropmarker-XPVista7@2x.png
+++ b/browser/themes/windows/urlbar-history-dropmarker-win7@2x.png
Binary files differ
diff --git a/browser/themes/windows/windowsShared.inc b/browser/themes/windows/windowsShared.inc
index 0cb2ab163..199a62292 100644
--- a/browser/themes/windows/windowsShared.inc
+++ b/browser/themes/windows/windowsShared.inc
@@ -8,6 +8,4 @@
%define fgTabTexture linear-gradient(transparent 2px, @toolbarHighlight@ 2px, @toolbarHighlight@)
%define fgTabBackgroundColor -moz-dialog
%define fgTabTextureLWT @fgTabTexture@
-
-% Aero-only defines
%define customToolbarColor hsl(210,75%,92%)
diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py
index 8cdb8f0cd..bdf7c9b03 100644
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -130,7 +130,7 @@ MACH_MODULES = [
'mobile/android/mach_commands.py',
]
-if os.path.exists('addon-sdk'):
+if os.path.exists('addon-sdk/mach_commands.py'):
MACH_MODULES += [ 'addon-sdk/mach_commands.py' ]
CATEGORIES = {
diff --git a/build/moz.configure/old.configure b/build/moz.configure/old.configure
index 581fa9c50..b33f18623 100644
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -169,6 +169,9 @@ def old_configure_options(*options):
'--enable-crashreporter',
'--enable-dbus',
'--enable-debug-js-modules',
+ '--enable-jetpack',
+ '--enable-devtools-server',
+ '--enable-devtools',
'--enable-directshow',
'--enable-dtrace',
'--enable-dump-painting',
@@ -215,7 +218,6 @@ def old_configure_options(*options):
'--enable-readline',
'--enable-reflow-perf',
'--enable-release',
- '--enable-require-all-d3dc-versions',
'--enable-safe-browsing',
'--enable-sandbox',
'--enable-signmar',
@@ -302,6 +304,9 @@ def old_configure_options(*options):
'--enable-mapi',
'--enable-calendar',
'--enable-incomplete-external-linkage',
+
+ # Below are configure flags used by Basilisk
+ '--disable-webextensions',
)
@imports(_from='__builtin__', _import='compile')
@imports(_from='__builtin__', _import='open')
diff --git a/config/external/nss/nss.symbols b/config/external/nss/nss.symbols
index 592c50b1f..ba5492c37 100644
--- a/config/external/nss/nss.symbols
+++ b/config/external/nss/nss.symbols
@@ -271,6 +271,7 @@ NSS_IsInitialized
NSS_OptionSet
NSS_NoDB_Init
NSS_SecureMemcmp
+NSS_SecureMemcmpZero
NSS_SetAlgorithmPolicy
NSS_SetDomesticPolicy
NSS_Shutdown
@@ -278,6 +279,9 @@ NSSSMIME_GetVersion
NSS_SMIMESignerInfo_SaveSMIMEProfile
NSS_SMIMEUtil_FindBulkAlgForRecipients
NSSSSL_GetVersion
+#ifdef XP_WIN
+_NSSUTIL_Access
+#endif
NSSUTIL_ArgDecodeNumber
NSSUTIL_ArgFetchValue
NSSUTIL_ArgGetLabel
@@ -299,6 +303,9 @@ NSSUTIL_MkModuleSpec
NSSUTIL_MkNSSString
NSSUTIL_MkSlotString
NSSUTIL_Quote
+#ifdef XP_WIN
+_NSSUTIL_UTF8ToWide
+#endif
PK11_AlgtagToMechanism
PK11_Authenticate
PK11_ChangePW
@@ -367,6 +374,7 @@ PK11_GetLowLevelKeyIDForPrivateKey
PK11_GetMechanism
PK11_GetMinimumPwdLength
PK11_GetModInfo
+PK11_GetModuleURI
PK11_GetNextSafe
PK11_GetNextSymKey
PK11_GetPadMechanism
@@ -380,6 +388,8 @@ PK11_GetSlotSeries
PK11_GetSymKeyNickname
PK11_GetTokenInfo
PK11_GetTokenName
+PK11_GetTokenURI
+PK11_HasAttributeSet
PK11_HashBuf
PK11_HasRootCerts
PK11_ImportCert
@@ -480,6 +490,7 @@ PORT_UCS2_ASCIIConversion_Util
PORT_UCS2_UTF8Conversion
PORT_UCS2_UTF8Conversion_Util
PORT_ZAlloc
+PORT_ZAllocAlignedOffset_Util
PORT_ZAlloc_Util
PORT_ZFree_Util
SEC_AnyTemplate_Util @DATA@
diff --git a/config/milestone.txt b/config/milestone.txt
index 48a995e3c..05d41f934 100644
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
-52.6.0
+4.1.0
diff --git a/devtools/client/scratchpad/scratchpad.xul b/devtools/client/scratchpad/scratchpad.xul
index 0603fa95e..3712f163d 100644
--- a/devtools/client/scratchpad/scratchpad.xul
+++ b/devtools/client/scratchpad/scratchpad.xul
@@ -121,7 +121,7 @@
<key id="sp-key-reloadAndRun"
key="&reloadAndRun.key;"
command="sp-cmd-reloadAndRun"
- modifiers="accel,shift"/>
+ modifiers="accel,alt"/>
<key id="sp-key-evalFunction"
key="&evalFunction.key;"
command="sp-cmd-evalFunction"
diff --git a/devtools/client/shared/curl.js b/devtools/client/shared/curl.js
index 978cbad9c..44465193f 100644
--- a/devtools/client/shared/curl.js
+++ b/devtools/client/shared/curl.js
@@ -107,7 +107,13 @@ const Curl = {
// Add http version.
if (data.httpVersion && data.httpVersion != DEFAULT_HTTP_VERSION) {
- command.push("--" + data.httpVersion.split("/")[1]);
+ let version = data.httpVersion.split("/")[1];
+ // curl accepts --http1.0, --http1.1 and --http2 for HTTP/1.0, HTTP/1.1
+ // and HTTP/2 protocols respectively. But the corresponding values in
+ // data.httpVersion are HTTP/1.0, HTTP/1.1 and HTTP/2.0
+ // So in case of HTTP/2.0 (which should ideally be HTTP/2) we are using
+ // only major version, and full version in other cases
+ command.push("--http" + (version == "2.0" ? version.split(".")[0] : version));
}
// Add request headers.
diff --git a/devtools/moz.build b/devtools/moz.build
index 79787d019..8e368facb 100644
--- a/devtools/moz.build
+++ b/devtools/moz.build
@@ -4,13 +4,9 @@
# License, v. 2.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_DEVTOOLS'] and CONFIG['MOZ_DEVTOOLS'] not in ('all', 'server'):
- error('Unsupported MOZ_DEVTOOLS value: %s' % (CONFIG['MOZ_DEVTOOLS']))
+if CONFIG['MOZ_DEVTOOLS']:
+ DIRS += ['client']
-if CONFIG['MOZ_DEVTOOLS'] == 'all':
- DIRS += [
- 'client',
- ]
DIRS += [
'server',
diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build
index 5980876e2..ddefc3e9e 100644
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -61,9 +61,12 @@ DevToolsModules(
'stylesheets.js',
'timeline.js',
'webaudio.js',
- 'webbrowser.js',
'webconsole.js',
'webextension.js',
'webgl.js',
'worker.js',
)
+
+FINAL_TARGET_PP_FILES.chrome.devtools.modules.devtools.server.actors += [
+ 'webbrowser.js',
+] \ No newline at end of file
diff --git a/devtools/server/actors/webbrowser.js b/devtools/server/actors/webbrowser.js
index 0edcdc187..1808895b1 100644
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -30,7 +30,9 @@ loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker", true);
loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
+#ifdef MOZ_WEBEXTENSIONS
loader.lazyImporter(this, "ExtensionContent", "resource://gre/modules/ExtensionContent.jsm");
+#endif
// Assumptions on events module:
// events needs to be dispatched synchronously,
@@ -982,6 +984,7 @@ TabActor.prototype = {
return null;
},
+#ifdef MOZ_WEBEXTENSIONS
/**
* Getter for the WebExtensions ContentScript globals related to the
* current tab content's DOM window.
@@ -994,6 +997,7 @@ TabActor.prototype = {
return [];
},
+#endif
/**
* Getter for the list of all content DOM windows in this tabActor
diff --git a/docshell/base/moz.build b/docshell/base/moz.build
index 1eb04c227..6ea3e6d28 100644
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -81,8 +81,5 @@ LOCAL_INCLUDES += [
if CONFIG['MOZ_TOOLKIT_SEARCH']:
DEFINES['MOZ_TOOLKIT_SEARCH'] = True
-if CONFIG['MOZ_DEVTOOLS'] == 'all':
- DEFINES['MOZ_DEVTOOLS_ALL'] = True
-
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']
diff --git a/docshell/base/nsAboutRedirector.cpp b/docshell/base/nsAboutRedirector.cpp
index f300e0ce2..e7d362864 100644
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -41,7 +41,8 @@ static RedirEntry kRedirMap[] = {
},
{
"buildconfig", "chrome://global/content/buildconfig.html",
- nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::MAKE_LINKABLE
},
{
"checkerboard", "chrome://global/content/aboutCheckerboard.xhtml",
@@ -53,10 +54,11 @@ static RedirEntry kRedirMap[] = {
{ "crashes", "chrome://global/content/crashes.xhtml", 0 },
#endif
{
- "credits", "https://www.mozilla.org/credits/",
- nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
+ "credits", "http://www.palemoon.org/Contributors.shtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::MAKE_LINKABLE
},
-#ifdef MOZ_DEVTOOLS_ALL
+#ifdef MOZ_DEVTOOLS
{
"debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
nsIAboutModule::ALLOW_SCRIPT
diff --git a/docshell/base/nsDSURIContentListener.cpp b/docshell/base/nsDSURIContentListener.cpp
index cfac54f7f..93ce3cb26 100644
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -22,6 +22,7 @@
#include "nsIScriptError.h"
#include "nsDocShellLoadTypes.h"
#include "nsIMultiPartChannel.h"
+#include "mozilla/dom/nsCSPUtils.h"
using namespace mozilla;
@@ -84,14 +85,6 @@ nsDSURIContentListener::DoContent(const nsACString& aContentType,
NS_ENSURE_ARG_POINTER(aContentHandler);
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
- // Check whether X-Frame-Options permits us to load this content in an
- // iframe and abort the load (unless we've disabled x-frame-options
- // checking).
- if (!CheckFrameOptions(aRequest)) {
- *aAbortProcess = true;
- return NS_OK;
- }
-
*aAbortProcess = false;
// determine if the channel has just been retargeted to us...
@@ -265,9 +258,10 @@ nsDSURIContentListener::SetParentContentListener(
return NS_OK;
}
-bool
+/* static */ bool
nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
- const nsAString& aPolicy)
+ const nsAString& aPolicy,
+ nsIDocShell* aDocShell)
{
static const char allowFrom[] = "allow-from";
const uint32_t allowFromLen = ArrayLength(allowFrom) - 1;
@@ -285,7 +279,7 @@ nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
aHttpChannel->GetURI(getter_AddRefs(uri));
// XXXkhuey when does this happen? Is returning true safe here?
- if (!mDocShell) {
+ if (!aDocShell) {
return true;
}
@@ -293,7 +287,7 @@ nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
// window, if we're not the top. X-F-O: SAMEORIGIN requires that the
// document must be same-origin with top window. X-F-O: DENY requires that
// the document must never be framed.
- nsCOMPtr<nsPIDOMWindowOuter> thisWindow = mDocShell->GetWindow();
+ nsCOMPtr<nsPIDOMWindowOuter> thisWindow = aDocShell->GetWindow();
// If we don't have DOMWindow there is no risk of clickjacking
if (!thisWindow) {
return true;
@@ -313,7 +307,7 @@ nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
// content-type docshell doesn't work because some chrome documents are
// loaded in content docshells (see bug 593387).
nsCOMPtr<nsIDocShellTreeItem> thisDocShellItem(
- do_QueryInterface(static_cast<nsIDocShell*>(mDocShell)));
+ do_QueryInterface(static_cast<nsIDocShell*>(aDocShell)));
nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
nsCOMPtr<nsIDocShellTreeItem> curDocShellItem = thisDocShellItem;
nsCOMPtr<nsIDocument> topDoc;
@@ -402,22 +396,66 @@ nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
return true;
}
+// Ignore x-frame-options if CSP with frame-ancestors exists
+static bool
+ShouldIgnoreFrameOptions(nsIChannel* aChannel, nsIPrincipal* aPrincipal)
+{
+ NS_ENSURE_TRUE(aChannel, false);
+ NS_ENSURE_TRUE(aPrincipal, false);
+
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ aPrincipal->GetCsp(getter_AddRefs(csp));
+ if (!csp) {
+ // if there is no CSP, then there is nothing to do here
+ return false;
+ }
+
+ bool enforcesFrameAncestors = false;
+ csp->GetEnforcesFrameAncestors(&enforcesFrameAncestors);
+ if (!enforcesFrameAncestors) {
+ // if CSP does not contain frame-ancestors, then there
+ // is nothing to do here.
+ return false;
+ }
+
+ // log warning to console that xfo is ignored because of CSP
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+ uint64_t innerWindowID = loadInfo ? loadInfo->GetInnerWindowID() : 0;
+ const char16_t* params[] = { u"x-frame-options",
+ u"frame-ancestors" };
+ CSP_LogLocalizedStr(u"IgnoringSrcBecauseOfDirective",
+ params, ArrayLength(params),
+ EmptyString(), // no sourcefile
+ EmptyString(), // no scriptsample
+ 0, // no linenumber
+ 0, // no columnnumber
+ nsIScriptError::warningFlag,
+ "CSP", innerWindowID);
+
+ return true;
+}
+
// Check if X-Frame-Options permits this document to be loaded as a subdocument.
// This will iterate through and check any number of X-Frame-Options policies
// in the request (comma-separated in a header, multiple headers, etc).
-bool
-nsDSURIContentListener::CheckFrameOptions(nsIRequest* aRequest)
+/* static */ bool
+nsDSURIContentListener::CheckFrameOptions(nsIChannel* aChannel,
+ nsIDocShell* aDocShell,
+ nsIPrincipal* aPrincipal)
{
- nsresult rv;
- nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
- if (!chan) {
+ if (!aChannel || !aDocShell) {
return true;
}
- nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(chan);
+ if (ShouldIgnoreFrameOptions(aChannel, aPrincipal)) {
+ return true;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
if (!httpChannel) {
// check if it is hiding in a multipart channel
- rv = mDocShell->GetHttpChannel(chan, getter_AddRefs(httpChannel));
+ rv = nsDocShell::Cast(aDocShell)->GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
if (NS_FAILED(rv)) {
return false;
}
@@ -442,11 +480,11 @@ nsDSURIContentListener::CheckFrameOptions(nsIRequest* aRequest)
nsCharSeparatedTokenizer tokenizer(xfoHeaderValue, ',');
while (tokenizer.hasMoreTokens()) {
const nsSubstring& tok = tokenizer.nextToken();
- if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) {
+ if (!CheckOneFrameOptionsPolicy(httpChannel, tok, aDocShell)) {
// cancel the load and display about:blank
httpChannel->Cancel(NS_BINDING_ABORTED);
- if (mDocShell) {
- nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
+ if (aDocShell) {
+ nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(aDocShell));
if (webNav) {
webNav->LoadURI(u"about:blank",
0, nullptr, nullptr, nullptr);
@@ -459,7 +497,7 @@ nsDSURIContentListener::CheckFrameOptions(nsIRequest* aRequest)
return true;
}
-void
+/* static */ void
nsDSURIContentListener::ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
nsIURI* aThisURI,
XFOHeader aHeader)
diff --git a/docshell/base/nsDSURIContentListener.h b/docshell/base/nsDSURIContentListener.h
index f33d1c045..432813471 100644
--- a/docshell/base/nsDSURIContentListener.h
+++ b/docshell/base/nsDSURIContentListener.h
@@ -28,6 +28,12 @@ public:
nsresult Init();
+ // Determine if X-Frame-Options allows content to be framed
+ // as a subdocument
+ static bool CheckFrameOptions(nsIChannel* aChannel,
+ nsIDocShell* aDocShell,
+ nsIPrincipal* aPrincipal);
+
protected:
explicit nsDSURIContentListener(nsDocShell* aDocShell);
virtual ~nsDSURIContentListener();
@@ -39,12 +45,9 @@ protected:
mExistingJPEGStreamListener = nullptr;
}
- // Determine if X-Frame-Options allows content to be framed
- // as a subdocument
- bool CheckFrameOptions(nsIRequest* aRequest);
- bool CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
- const nsAString& aPolicy);
-
+ static bool CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
+ const nsAString& aPolicy,
+ nsIDocShell* aDocShell);
enum XFOHeader
{
eDENY,
@@ -52,9 +55,9 @@ protected:
eALLOWFROM
};
- void ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
- nsIURI* aThisURI,
- XFOHeader aHeader);
+ static void ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
+ nsIURI* aThisURI,
+ XFOHeader aHeader);
protected:
nsDocShell* mDocShell;
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index ab119a016..2e08e6720 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11025,6 +11025,29 @@ nsDocShell::DoURILoad(nsIURI* aURI,
}
}
+ // Navigational requests that are same origin need to be upgraded in case
+ // upgrade-insecure-requests is present. Please note that in that case
+ // the triggeringPrincipal is holding the CSP that potentially
+ // holds upgrade-insecure-requests.
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ aTriggeringPrincipal->GetCsp(getter_AddRefs(csp));
+ if (csp) {
+ bool upgradeInsecureRequests = false;
+ csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
+ if (upgradeInsecureRequests) {
+ // only upgrade if the navigation is same origin
+ nsCOMPtr<nsIPrincipal> resultPrincipal;
+ rv = nsContentUtils::GetSecurityManager()->
+ GetChannelResultPrincipal(channel,
+ getter_AddRefs(resultPrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (resultPrincipal->Equals(aTriggeringPrincipal)) {
+ static_cast<mozilla::LoadInfo*>(loadInfo.get())->SetUpgradeInsecureRequests();
+ }
+ }
+ }
+
+
nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
do_QueryInterface(channel);
if (appCacheChannel) {
diff --git a/docshell/build/moz.build b/docshell/build/moz.build
index 130ec8736..e47bd89ed 100644
--- a/docshell/build/moz.build
+++ b/docshell/build/moz.build
@@ -23,6 +23,3 @@ LOCAL_INCLUDES += [
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
-
-if CONFIG['MOZ_DEVTOOLS'] == 'all':
- DEFINES['MOZ_DEVTOOLS_ALL'] = True
diff --git a/docshell/build/nsDocShellModule.cpp b/docshell/build/nsDocShellModule.cpp
index 1e62e1479..d43c305f9 100644
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -169,7 +169,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "crashes", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
#endif
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "credits", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
-#ifdef MOZ_DEVTOOLS_ALL
+#ifdef MOZ_DEVTOOLS
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "debugging", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
#endif
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
diff --git a/docshell/test/file_bug1151421.html b/docshell/test/file_bug1151421.html
new file mode 100644
index 000000000..7bb8c8f36
--- /dev/null
+++ b/docshell/test/file_bug1151421.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<style>
+body, html {
+ height: 100%;
+}
+.spacer {
+ height: 80%;
+}
+</style>
+</head>
+<body onload='(parent || opener).childLoad()'>
+
+<div class="spacer"></div>
+<div id="content">content</div>
+<div class="spacer"></div>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest.ini b/docshell/test/mochitest.ini
index 725486b77..7b27908fb 100644
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -32,6 +32,7 @@ support-files =
file_bug680257.html
file_bug703855.html
file_bug728939.html
+ file_bug1151421.html
file_pushState_after_document_open.html
historyframes.html
@@ -85,6 +86,7 @@ support-files = file_bug668513.html
[test_bug797909.html]
[test_bug1045096.html]
[test_bug1121701.html]
+[test_bug1151421.html]
[test_bug1186774.html]
[test_forceinheritprincipal_overrule_owner.html]
[test_framedhistoryframes.html]
diff --git a/docshell/test/navigation/file_scrollRestoration.html b/docshell/test/navigation/file_scrollRestoration.html
index 5450c2724..92e43d7fb 100644
--- a/docshell/test/navigation/file_scrollRestoration.html
+++ b/docshell/test/navigation/file_scrollRestoration.html
@@ -26,7 +26,7 @@
}
case 2: {
opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
- opener.isnot(window.scrollY, 0, "Should have restored scrolling.");
+ opener.isnot(Math.round(window.scrollY), 0, "Should have restored scrolling.");
opener.is(history.scrollRestoration, "auto", "Should have the same scrollRestoration as before reload.");
history.scrollRestoration = "manual";
window.onunload = function() {} // Disable bfcache.
@@ -45,7 +45,7 @@
}
case 4: {
opener.is(event.persisted, true, "Should have persisted session history entry.");
- opener.isnot(window.scrollY, 0, "Should have kept the old scroll position.");
+ opener.isnot(Math.round(window.scrollY), 0, "Should have kept the old scroll position.");
opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload.");
window.scrollTo(0, 0);
window.location.hash = "hash";
@@ -53,7 +53,7 @@
break;
}
case 5: {
- opener.isnot(window.scrollY, 0, "Should have scrolled to #hash.");
+ opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled to #hash.");
opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation.");
window.onunload = function() {} // Disable bfcache.
opener.setTimeout("is(testWindow.history.scrollRestoration, 'auto'); testWindow.history.back();", 250);
@@ -70,7 +70,7 @@
history.pushState({ state: "state2" }, "state2");
window.scrollTo(0, 0);
history.back();
- opener.isnot(window.scrollY, 0, "Should have scrolled back to the state1's position");
+ opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled back to the state1's position");
opener.is(history.state.state, "state1", "Unexpected state.");
history.scrollRestoration = "manual";
@@ -79,17 +79,17 @@
history.pushState({ state: "state4" }, "state4");
window.scrollTo(0, 0);
history.back();
- opener.is(window.scrollY, 0, "Shouldn't have scrolled back to the state3's position");
+ opener.is(Math.round(window.scrollY), 0, "Shouldn't have scrolled back to the state3's position");
opener.is(history.state.state, "state3", "Unexpected state.");
history.pushState({ state: "state5" }, "state5");
history.scrollRestoration = "auto";
document.getElementById("bottom").scrollIntoView();
- opener.isnot(window.scrollY, 0, "Should have scrolled to 'bottom'.");
+ opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled to 'bottom'.");
history.back();
window.scrollTo(0, 0);
history.forward();
- opener.isnot(window.scrollY, 0, "Should have scrolled back to the state5's position");
+ opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled back to the state5's position");
var ifr = document.createElement("iframe");
ifr.src = "data:text/html,";
diff --git a/docshell/test/test_bug1151421.html b/docshell/test/test_bug1151421.html
new file mode 100644
index 000000000..76e34d502
--- /dev/null
+++ b/docshell/test/test_bug1151421.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151421
+-->
+<head>
+ <title>Test for Bug 1151421</title>
+ <script type="application/javascript" 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=1151421">Mozilla Bug 1151421</a>
+
+<script type="application/javascript">
+
+/** Test for Bug 1151421 **/
+SimpleTest.waitForExplicitFinish();
+
+function childLoad() {
+ // Spin the event loop so we leave the onload handler.
+ SimpleTest.executeSoon(childLoad2);
+}
+
+function childLoad2() {
+ let cw = iframe.contentWindow;
+ let content = cw.document.getElementById("content");
+
+ // Create a function to calculate an invariant.
+ let topPlusOffset = function()
+ {
+ return Math.round(content.getBoundingClientRect().top + cw.pageYOffset);
+ }
+
+ let initialTPO = topPlusOffset();
+
+ // Scroll the iframe to various positions, and check the TPO.
+ // Scrolling down to the bottom will adjust the page offset by a fractional amount.
+ let positions = [-100, 0.17, 0, 1.5, 10.41, 1e6, 12.1];
+
+ // Run some tests with scrollTo() and ensure we have the same invariant after scrolling.
+ positions.forEach(function(pos) {
+ cw.scrollTo(0, pos);
+ is(topPlusOffset(), initialTPO, "Top plus offset should remain invariant across scrolling.");
+ });
+
+ positions.reverse().forEach(function(pos) {
+ cw.scrollTo(0, pos);
+ is(topPlusOffset(), initialTPO, "(reverse) Top plus offset should remain invariant across scrolling.");
+ });
+
+ SimpleTest.finish();
+}
+
+</script>
+
+<!-- When the iframe loads, it calls childLoad(). -->
+<br>
+<iframe height='100px' id='iframe' src='file_bug1151421.html'></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/test_bug1186774.html b/docshell/test/test_bug1186774.html
index 52ef5f62c..623e7996b 100644
--- a/docshell/test/test_bug1186774.html
+++ b/docshell/test/test_bug1186774.html
@@ -28,7 +28,7 @@ function runTest() {
}
child.onpopstate = function() {
- is(child.scrollY, 6000, "Shouldn't have scrolled before popstate");
+ is(Math.round(child.scrollY), 6000, "Shouldn't have scrolled before popstate");
child.close();
SimpleTest.finish();
}
diff --git a/docshell/test/test_bug590573.html b/docshell/test/test_bug590573.html
index aa6d3bd79..e218140ea 100644
--- a/docshell/test/test_bug590573.html
+++ b/docshell/test/test_bug590573.html
@@ -147,21 +147,21 @@ function* testBody()
popup.scroll(0, 100);
popup.history.pushState('', '', '?pushed');
- is(popup.scrollY, 100, "test 2");
+ is(Math.round(popup.scrollY), 100, "test 2");
popup.scroll(0, 200); // set state-2's position to 200
popup.history.back();
- is(popup.scrollY, 100, "test 3");
+ is(Math.round(popup.scrollY), 100, "test 3");
popup.scroll(0, 150); // set original page's position to 150
popup.history.forward();
- is(popup.scrollY, 200, "test 4");
+ is(Math.round(popup.scrollY), 200, "test 4");
popup.history.back();
- is(popup.scrollY, 150, "test 5");
+ is(Math.round(popup.scrollY), 150, "test 5");
popup.history.forward();
- is(popup.scrollY, 200, "test 6");
+ is(Math.round(popup.scrollY), 200, "test 6");
// At this point, the history looks like:
// PATH POSITION
@@ -202,13 +202,13 @@ function* testBody()
is(popup.location.search, "?pushed");
ok(popup.document.getElementById('div1'), 'page should have div1.');
- is(popup.scrollY, 200, "test 8");
+ is(Math.round(popup.scrollY), 200, "test 8");
popup.history.back();
- is(popup.scrollY, 150, "test 9");
+ is(Math.round(popup.scrollY), 150, "test 9");
popup.history.forward();
- is(popup.scrollY, 200, "test 10");
+ is(Math.round(popup.scrollY), 200, "test 10");
// Spin one last time...
setTimeout(pageLoad, 0);
diff --git a/docshell/test/test_bug653741.html b/docshell/test/test_bug653741.html
index f4d4587b8..a1faf5e2d 100644
--- a/docshell/test/test_bug653741.html
+++ b/docshell/test/test_bug653741.html
@@ -27,7 +27,7 @@ function childLoad2() {
// Save the Y offset. For sanity's sake, make sure it's not 0, because we
// should be at the bottom of the page!
- let origYOffset = cw.pageYOffset;
+ let origYOffset = Math.round(cw.pageYOffset);
ok(origYOffset != 0, 'Original Y offset is not 0.');
// Scroll the iframe to the top, then navigate to #bottom again.
@@ -37,7 +37,7 @@ function childLoad2() {
// bottom again.
cw.location = cw.location + '';
- is(cw.pageYOffset, origYOffset, 'Correct offset after reloading page.');
+ is(Math.round(cw.pageYOffset), origYOffset, 'Correct offset after reloading page.');
SimpleTest.finish();
}
diff --git a/docshell/test/test_bug662170.html b/docshell/test/test_bug662170.html
index 514bb55b1..0e626fed4 100644
--- a/docshell/test/test_bug662170.html
+++ b/docshell/test/test_bug662170.html
@@ -32,7 +32,7 @@ function childLoad2() {
cw.scrollTo(0, 300);
// Did we actually scroll somewhere?
- isnot(cw.pageYOffset, 0, 'Y offset should be non-zero after scrolling.');
+ isnot(Math.round(cw.pageYOffset), 0, 'Y offset should be non-zero after scrolling.');
// Now load file_bug662170.html#, which should take us to the top of the
// page.
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index 290af152b..8b20d0196 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1854,16 +1854,6 @@ Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
{
MOZ_ASSERT(NS_IsMainThread());
- if (!aIsCallerChrome) {
- const nsAdoptingString& override =
- mozilla::Preferences::GetString("general.useragent.override");
-
- if (override) {
- aUserAgent = override;
- return NS_OK;
- }
- }
-
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
index 8e6920a0e..4926b6c0a 100644
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -61,6 +61,7 @@
#include "nsGenericHTMLElement.h"
#include "mozilla/dom/CDATASection.h"
#include "mozilla/dom/ProcessingInstruction.h"
+#include "nsDSURIContentListener.h"
#include "nsDOMString.h"
#include "nsNodeUtils.h"
#include "nsLayoutUtils.h" // for GetFrameForPoint
@@ -2456,6 +2457,15 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
+ // XFO needs to be checked after CSP because it is ignored if
+ // the CSP defines frame-ancestors.
+ if (!nsDSURIContentListener::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
+ MOZ_LOG(gCspPRLog, LogLevel::Debug,
+ ("XFO doesn't like frame's ancestry, not loading."));
+ // stop! ERROR page!
+ aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
+ }
+
return NS_OK;
}
diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h
index 17d936055..fc6749c9f 100644
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1491,7 +1491,6 @@ private:
void PostUnblockOnloadEvent();
void DoUnblockOnload();
- nsresult CheckFrameOptions();
nsresult InitCSP(nsIChannel* aChannel);
/**
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index 8ff4b84ce..f784031f6 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6187,7 +6187,7 @@ nsGlobalWindow::GetScrollMaxY(ErrorResult& aError)
FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
}
-CSSIntPoint
+CSSPoint
nsGlobalWindow::GetScrollXY(bool aDoFlush)
{
MOZ_ASSERT(IsOuterWindow());
@@ -6211,30 +6211,30 @@ nsGlobalWindow::GetScrollXY(bool aDoFlush)
return GetScrollXY(true);
}
- return sf->GetScrollPositionCSSPixels();
+ return CSSPoint::FromAppUnits(scrollPos);
}
-int32_t
+double
nsGlobalWindow::GetScrollXOuter()
{
MOZ_RELEASE_ASSERT(IsOuterWindow());
return GetScrollXY(false).x;
}
-int32_t
+double
nsGlobalWindow::GetScrollX(ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
}
-int32_t
+double
nsGlobalWindow::GetScrollYOuter()
{
MOZ_RELEASE_ASSERT(IsOuterWindow());
return GetScrollXY(false).y;
}
-int32_t
+double
nsGlobalWindow::GetScrollY(ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
index eab91c2e4..dbceeab74 100644
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1050,15 +1050,15 @@ public:
void SetInnerHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
mozilla::dom::CallerType aCallerType,
mozilla::ErrorResult& aError);
- int32_t GetScrollXOuter();
- int32_t GetScrollX(mozilla::ErrorResult& aError);
- int32_t GetPageXOffset(mozilla::ErrorResult& aError)
+ double GetScrollXOuter();
+ double GetScrollX(mozilla::ErrorResult& aError);
+ double GetPageXOffset(mozilla::ErrorResult& aError)
{
return GetScrollX(aError);
}
- int32_t GetScrollYOuter();
- int32_t GetScrollY(mozilla::ErrorResult& aError);
- int32_t GetPageYOffset(mozilla::ErrorResult& aError)
+ double GetScrollYOuter();
+ double GetScrollY(mozilla::ErrorResult& aError);
+ double GetPageYOffset(mozilla::ErrorResult& aError)
{
return GetScrollY(aError);
}
@@ -1579,7 +1579,7 @@ public:
// If aDoFlush is true, we'll flush our own layout; otherwise we'll try to
// just flush our parent and only flush ourselves if we think we need to.
// Outer windows only.
- mozilla::CSSIntPoint GetScrollXY(bool aDoFlush);
+ mozilla::CSSPoint GetScrollXY(bool aDoFlush);
int32_t GetScrollBoundaryOuter(mozilla::Side aSide);
diff --git a/dom/base/nsPluginArray.cpp b/dom/base/nsPluginArray.cpp
index b9c946ca3..5b9378ae0 100644
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -372,9 +372,21 @@ nsPluginArray::EnsurePlugins()
nsCString permString;
nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString);
if (rv == NS_OK) {
- nsIPrincipal* principal = mWindow->GetExtantDoc()->NodePrincipal();
- nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
- permMgr->TestPermissionFromPrincipal(principal, permString.get(), &permission);
+ nsCOMPtr<nsIDocument> currentDoc = mWindow->GetExtantDoc();
+
+ // The top-level content document gets the final say on whether or not
+ // a plugin is going to be hidden or not, regardless of the origin
+ // that a subframe is hosted at. This is to avoid spamming the user
+ // with the hidden plugin notification bar when third-party iframes
+ // attempt to access navigator.plugins after the user has already
+ // expressed that the top-level document has this permission.
+ nsCOMPtr<nsIDocument> topDoc = currentDoc->GetTopLevelContentDocument();
+
+ if (topDoc) {
+ nsIPrincipal* principal = topDoc->NodePrincipal();
+ nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+ permMgr->TestPermissionFromPrincipal(principal, permString.get(), &permission);
+ }
}
}
}
diff --git a/dom/base/test/test_viewport_scroll.html b/dom/base/test/test_viewport_scroll.html
index 9b812360b..7db02b781 100644
--- a/dom/base/test/test_viewport_scroll.html
+++ b/dom/base/test/test_viewport_scroll.html
@@ -28,10 +28,10 @@ function subtest(winProp, elemProp, win, correctElement, elemToSet, otherElem1,
win.scrollTo(50, 50);
elemToSet[elemProp] = 100;
if (elemToSet == correctElement) {
- is(win[winProp], 100, "Setting " + elemToSet.name + "." + elemProp + " should scroll");
+ is(Math.round(win[winProp]), 100, "Setting " + elemToSet.name + "." + elemProp + " should scroll");
is(elemToSet[elemProp], 100, "Reading back " + elemToSet.name + "." + elemProp + " after scrolling");
} else {
- is(win[winProp], 50, "Setting " + elemToSet.name + "." + elemProp + " should not scroll");
+ is(Math.round(win[winProp]), 50, "Setting " + elemToSet.name + "." + elemProp + " should not scroll");
is(elemToSet[elemProp], 0, "Reading back " + elemToSet.name + "." + elemProp + " after not scrolling");
}
if (otherElem1 == correctElement) {
diff --git a/dom/browser-element/mochitest/browserElement_ScrollEvent.js b/dom/browser-element/mochitest/browserElement_ScrollEvent.js
index 5c4b4dcf9..06dc91b86 100644
--- a/dom/browser-element/mochitest/browserElement_ScrollEvent.js
+++ b/dom/browser-element/mochitest/browserElement_ScrollEvent.js
@@ -16,8 +16,8 @@ function runTest() {
iframe.addEventListener("mozbrowserscroll", function(e) {
ok(true, "got mozbrowserscroll event.");
ok(e.detail, "event.detail is not null.");
- ok(e.detail.top === 4000, "top position is correct.");
- ok(e.detail.left === 4000, "left position is correct.");
+ ok(Math.round(e.detail.top) == 4000, "top position is correct.");
+ ok(Math.round(e.detail.left) == 4000, "left position is correct.");
SimpleTest.finish();
});
diff --git a/dom/interfaces/security/nsIContentSecurityPolicy.idl b/dom/interfaces/security/nsIContentSecurityPolicy.idl
index ade5b1243..51ca46f2a 100644
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -98,6 +98,11 @@ interface nsIContentSecurityPolicy : nsISerializable
readonly attribute bool blockAllMixedContent;
/**
+ * Returns whether this policy enforces the frame-ancestors directive.
+ */
+ readonly attribute bool enforcesFrameAncestors;
+
+ /**
* Obtains the referrer policy (as integer) for this browsing context as
* specified in CSP. If there are multiple policies and...
* - only one sets a referrer policy: that policy is returned
diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp
index 66125f332..2413d8808 100644
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -8,10 +8,6 @@
#include "ContentProcess.h"
-#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-#include "mozilla/WindowsVersion.h"
-#endif
-
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
#include <stdlib.h>
#endif
@@ -33,9 +29,8 @@ static bool
IsSandboxTempDirRequired()
{
// On Windows, a sandbox-writable temp directory is only used
- // for Vista or later with sandbox pref level >= 1.
- return (IsVistaOrLater() &&
- (Preferences::GetInt("security.sandbox.content.level") >= 1));
+ // when sandbox pref level >= 1.
+ return Preferences::GetInt("security.sandbox.content.level") >= 1;
}
static void
diff --git a/dom/locales/en-US/chrome/security/csp.properties b/dom/locales/en-US/chrome/security/csp.properties
index fc7fc04ba..4124ef8aa 100644
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -91,6 +91,10 @@ ignoringReportOnlyDirective = Ignoring sandbox directive when delivered in a rep
# LOCALIZATION NOTE (deprecatedReferrerDirective):
# %1$S is the value of the deprecated Referrer Directive.
deprecatedReferrerDirective = Referrer Directive ‘%1$S’ has been deprecated. Please use the Referrer-Policy header instead.
+# LOCALIZATION NOTE (IgnoringSrcBecauseOfDirective):
+# %1$S is the name of the src that is ignored.
+# %2$S is the name of the directive that causes the src to be ignored.
+IgnoringSrcBecauseOfDirective=Ignoring ‘%1$S’ because of ‘%2$S’ directive.
# CSP Errors:
# LOCALIZATION NOTE (couldntParseInvalidSource):
diff --git a/dom/media/DecoderDoctorDiagnostics.cpp b/dom/media/DecoderDoctorDiagnostics.cpp
index 91c2d8dfb..778e8c4c5 100644
--- a/dom/media/DecoderDoctorDiagnostics.cpp
+++ b/dom/media/DecoderDoctorDiagnostics.cpp
@@ -576,16 +576,9 @@ DecoderDoctorDocumentWatcher::SynthesizeAnalysis()
// going through expected decoders from most to least desirable.
#if defined(XP_WIN)
if (!formatsRequiringWMF.IsEmpty()) {
- if (IsVistaOrLater()) {
- DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because WMF was not found",
- this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringWMF).get());
- ReportAnalysis(mDocument, sMediaWMFNeeded, false, formatsRequiringWMF);
- } else {
- DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media before Windows Vista",
- this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringWMF).get());
- ReportAnalysis(mDocument, sMediaUnsupportedBeforeWindowsVista,
- false, formatsRequiringWMF);
- }
+ DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because WMF was not found",
+ this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringWMF).get());
+ ReportAnalysis(mDocument, sMediaWMFNeeded, false, formatsRequiringWMF);
return;
}
#endif
diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp
index 96e2c23e0..97a6855d9 100644
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1853,20 +1853,18 @@ MediaManager::GetNonE10sParent()
MediaManager::StartupInit()
{
#ifdef WIN32
- if (IsVistaOrLater() && !IsWin8OrLater()) {
- // Bug 1107702 - Older Windows fail in GetAdaptersInfo (and others) if the
- // first(?) call occurs after the process size is over 2GB (kb/2588507).
- // Attempt to 'prime' the pump by making a call at startup.
- unsigned long out_buf_len = sizeof(IP_ADAPTER_INFO);
- PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *) moz_xmalloc(out_buf_len);
- if (GetAdaptersInfo(pAdapterInfo, &out_buf_len) == ERROR_BUFFER_OVERFLOW) {
- free(pAdapterInfo);
- pAdapterInfo = (IP_ADAPTER_INFO *) moz_xmalloc(out_buf_len);
- GetAdaptersInfo(pAdapterInfo, &out_buf_len);
- }
- if (pAdapterInfo) {
- free(pAdapterInfo);
- }
+ // Bug 1107702 - Some Windows versions fail in GetAdaptersInfo (and others)
+ // if the first(?) call occurs after the process size is over 2GB (kb/2588507).
+ // Attempt to 'prime' the pump by making a call at startup.
+ unsigned long out_buf_len = sizeof(IP_ADAPTER_INFO);
+ PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *) moz_xmalloc(out_buf_len);
+ if (GetAdaptersInfo(pAdapterInfo, &out_buf_len) == ERROR_BUFFER_OVERFLOW) {
+ free(pAdapterInfo);
+ pAdapterInfo = (IP_ADAPTER_INFO *) moz_xmalloc(out_buf_len);
+ GetAdaptersInfo(pAdapterInfo, &out_buf_len);
+ }
+ if (pAdapterInfo) {
+ free(pAdapterInfo);
}
#endif
}
@@ -2130,19 +2128,11 @@ if (privileged) {
case MediaSourceEnum::Application:
case MediaSourceEnum::Window:
// Deny screensharing request if support is disabled, or
- // the requesting document is not from a host on the whitelist, or
- // we're on WinXP until proved that it works
+ // the requesting document is not from a host on the whitelist
if (!Preferences::GetBool(((videoType == MediaSourceEnum::Browser)?
"media.getusermedia.browser.enabled" :
"media.getusermedia.screensharing.enabled"),
false) ||
-#if defined(XP_WIN)
- (
- // Allow tab sharing for all platforms including XP
- (videoType != MediaSourceEnum::Browser) &&
- !Preferences::GetBool("media.getusermedia.screensharing.allow_on_old_platforms",
- false) && !IsVistaOrLater()) ||
-#endif
(!privileged && !HostIsHttps(*docURI))) {
RefPtr<MediaStreamError> error =
new MediaStreamError(aWindow,
diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp
index 7007d3a03..4cff464e7 100644
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -140,26 +140,12 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
aOutMessage = NS_LITERAL_CSTRING("Adobe EME disabled");
return MediaKeySystemStatus::Cdm_disabled;
}
-#ifdef XP_WIN
- // Win Vista and later only.
- if (!IsVistaOrLater()) {
- aOutMessage = NS_LITERAL_CSTRING("Minimum Windows version (Vista) not met for Adobe EME");
- return MediaKeySystemStatus::Cdm_not_supported;
- }
-#endif
return EnsureCDMInstalled(aKeySystem, aOutMessage);
}
}
if (IsWidevineKeySystem(aKeySystem)) {
if (Preferences::GetBool("media.gmp-widevinecdm.visible", false)) {
-#ifdef XP_WIN
- // Win Vista and later only.
- if (!IsVistaOrLater()) {
- aOutMessage = NS_LITERAL_CSTRING("Minimum Windows version (Vista) not met for Widevine EME");
- return MediaKeySystemStatus::Cdm_not_supported;
- }
-#endif
if (!Preferences::GetBool("media.gmp-widevinecdm.enabled", false)) {
aOutMessage = NS_LITERAL_CSTRING("Widevine EME disabled");
return MediaKeySystemStatus::Cdm_disabled;
diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp
index 4cf07ddbd..fdd6f2c7e 100644
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -52,14 +52,6 @@ IsWhitelistedH264Codec(const nsAString& aCodec)
return false;
}
-#ifdef XP_WIN
- // Disable 4k video on windows vista since it performs poorly.
- if (!IsWin7OrLater() &&
- level >= H264_LEVEL_5) {
- return false;
- }
-#endif
-
// Just assume what we can play on all platforms the codecs/formats that
// WMF can play, since we don't have documentation about what other
// platforms can play... According to the WMF documentation:
diff --git a/dom/media/gtest/TestGMPCrossOrigin.cpp b/dom/media/gtest/TestGMPCrossOrigin.cpp
index 036282153..33ac98388 100644
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -1521,11 +1521,6 @@ TEST(GeckoMediaPlugins, GMPPluginVoucher) {
#if defined(XP_WIN)
TEST(GeckoMediaPlugins, GMPOutputProtection) {
- // Output Protection is not available pre-Vista.
- if (!IsVistaOrLater()) {
- return;
- }
-
RefPtr<GMPStorageTest> runner = new GMPStorageTest();
runner->DoTest(&GMPStorageTest::TestOutputProtection);
}
diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp
index a72d910f5..c1e58fdc2 100644
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -47,10 +47,6 @@
#include "MP4Decoder.h"
#include "mozilla/dom/RemoteVideoDecoder.h"
-#ifdef XP_WIN
-#include "mozilla/WindowsVersion.h"
-#endif
-
#include "mp4_demuxer/H264.h"
namespace mozilla {
@@ -367,16 +363,7 @@ PDMFactory::CreatePDMs()
}
#endif
#ifdef XP_WIN
- if (MediaPrefs::PDMWMFEnabled() && IsVistaOrLater() && !IsWin7AndPre2000Compatible()) {
- // *Only* use WMF on Vista and later, as if Firefox is run in Windows 95
- // compatibility mode on Windows 7 (it does happen!) we may crash trying
- // to startup WMF. So we need to detect the OS version here, as in
- // compatibility mode IsVistaOrLater() and friends behave as if we're on
- // the emulated version of Windows. See bug 1279171.
- // Additionally, we don't want to start the RemoteDecoderModule if we
- // expect it's not going to work (i.e. on Windows older than Vista).
- // IsWin7AndPre2000Compatible() uses GetVersionEx as the user specified OS version can
- // be reflected when compatibility mode is in effect.
+ if (MediaPrefs::PDMWMFEnabled()) {
m = new WMFDecoderModule();
RefPtr<PlatformDecoderModule> remote = new dom::RemoteDecoderModule(m);
StartupPDM(remote);
diff --git a/dom/media/platforms/wmf/WMF.h b/dom/media/platforms/wmf/WMF.h
index 5ede0d361..6988ef083 100644
--- a/dom/media/platforms/wmf/WMF.h
+++ b/dom/media/platforms/wmf/WMF.h
@@ -7,18 +7,6 @@
#ifndef WMF_H_
#define WMF_H_
-#if WINVER < _WIN32_WINNT_WIN7
-#error \
-You must include WMF.h before including mozilla headers, \
-otherwise mozconfig.h will be included \
-and that sets WINVER to WinXP, \
-which makes Windows Media Foundation unavailable.
-#endif
-
-#pragma push_macro("WINVER")
-#undef WINVER
-#define WINVER _WIN32_WINNT_WIN7
-
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
@@ -35,7 +23,7 @@ which makes Windows Media Foundation unavailable.
#include <codecapi.h>
// The Windows headers helpfully declare min and max macros, which don't
-// compile in the prescence of std::min and std::max and unified builds.
+// compile in the presence of std::min and std::max and unified builds.
// So undef them here.
#ifdef min
#undef min
@@ -97,8 +85,4 @@ HRESULT MFCreateDXGISurfaceBuffer(REFIID riid,
} // end namespace wmf
} // end namespace mozilla
-
-
-#pragma pop_macro("WINVER")
-
#endif
diff --git a/dom/media/platforms/wmf/WMFUtils.cpp b/dom/media/platforms/wmf/WMFUtils.cpp
index 8aec8a8af..055012d0f 100644
--- a/dom/media/platforms/wmf/WMFUtils.cpp
+++ b/dom/media/platforms/wmf/WMFUtils.cpp
@@ -205,31 +205,17 @@ LoadDLLs()
HRESULT
MFStartup()
{
- if (!IsVistaOrLater() || IsWin7AndPre2000Compatible()) {
- // *Only* use WMF on Vista and later, as if Firefox is run in Windows 95
- // compatibility mode on Windows 7 (it does happen!) we may crash trying
- // to startup WMF. So we need to detect the OS version here, as in
- // compatibility mode IsVistaOrLater() and friends behave as if we're on
- // the emulated version of Windows. See bug 1279171.
- // Using GetVersionEx API which takes compatibility mode into account.
- return E_FAIL;
- }
-
HRESULT hr = LoadDLLs();
if (FAILED(hr)) {
return hr;
}
- const int MF_VISTA_VERSION = (0x0001 << 16 | MF_API_VERSION);
const int MF_WIN7_VERSION = (0x0002 << 16 | MF_API_VERSION);
// decltype is unusable for functions having default parameters
DECL_FUNCTION_PTR(MFStartup, ULONG, DWORD);
ENSURE_FUNCTION_PTR_(MFStartup, Mfplat.dll)
- if (!IsWin7OrLater())
- return MFStartupPtr(MF_VISTA_VERSION, MFSTARTUP_FULL);
- else
- return MFStartupPtr(MF_WIN7_VERSION, MFSTARTUP_FULL);
+ return MFStartupPtr(MF_WIN7_VERSION, MFSTARTUP_FULL);
}
HRESULT
diff --git a/dom/media/webrtc/MediaEngineWebRTC.cpp b/dom/media/webrtc/MediaEngineWebRTC.cpp
index 522f23f61..1a2dc9a04 100644
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -268,11 +268,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
bool
MediaEngineWebRTC::SupportsDuplex()
{
-#ifndef XP_WIN
return mFullDuplex;
-#else
- return IsVistaOrLater() && mFullDuplex;
-#endif
}
void
diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp
index 6ee23f38b..bd71d6f65 100644
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2024,45 +2024,9 @@ struct CompareFilesByTime
} // namespace
-bool
-nsPluginHost::ShouldAddPlugin(nsPluginTag* aPluginTag)
-{
-#if defined(XP_WIN) && (defined(__x86_64__) || defined(_M_X64))
- // On 64-bit windows, the only plugins we should load are flash and
- // silverlight. Use library filename and MIME type to check.
- if (StringBeginsWith(aPluginTag->FileName(), NS_LITERAL_CSTRING("NPSWF"), nsCaseInsensitiveCStringComparator()) &&
- (aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-shockwave-flash")) ||
- aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-shockwave-flash-test")))) {
- return true;
- }
- if (StringBeginsWith(aPluginTag->FileName(), NS_LITERAL_CSTRING("npctrl"), nsCaseInsensitiveCStringComparator()) &&
- (aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-silverlight-test")) ||
- aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-silverlight-2")) ||
- aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-silverlight")))) {
- return true;
- }
- // Accept the test plugin MIME types, so mochitests still work.
- if (aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-test")) ||
- aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-Second-Test")) ||
- aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-java-test"))) {
- return true;
- }
-#ifdef PLUGIN_LOGGING
- PLUGIN_LOG(PLUGIN_LOG_NORMAL,
- ("ShouldAddPlugin : Ignoring non-flash plugin library %s\n", aPluginTag->FileName().get()));
-#endif // PLUGIN_LOGGING
- return false;
-#else
- return true;
-#endif // defined(XP_WIN) && (defined(__x86_64__) || defined(_M_X64))
-}
-
void
nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
{
- if (!ShouldAddPlugin(aPluginTag)) {
- return;
- }
aPluginTag->mNext = mPlugins;
mPlugins = aPluginTag;
@@ -2078,22 +2042,6 @@ nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
}
}
-static bool
-PluginInfoIsFlash(const nsPluginInfo& info)
-{
- if (!info.fName || strcmp(info.fName, "Shockwave Flash") != 0) {
- return false;
- }
- for (uint32_t i = 0; i < info.fVariantCount; ++i) {
- if (info.fMimeTypeArray[i] &&
- (!strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash") ||
- !strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash-test"))) {
- return true;
- }
- }
- return false;
-}
-
typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
@@ -2114,8 +2062,6 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
#endif
- bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
-
nsCOMPtr<nsISimpleEnumerator> iter;
rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
if (NS_FAILED(rv))
@@ -2218,8 +2164,7 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
res = pluginFile.GetPluginInfo(info, &library);
}
// if we don't have mime type don't proceed, this is not a plugin
- if (NS_FAILED(res) || !info.fMimeTypeArray ||
- (flashOnly && !PluginInfoIsFlash(info))) {
+ if (NS_FAILED(res) || !info.fMimeTypeArray) {
RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
fileModTime);
pluginFile.FreePluginInfo(info);
@@ -2867,14 +2812,12 @@ nsPluginHost::WritePluginInfo()
return rv;
}
- bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
-
PR_fprintf(fd, "Generated File. Do not edit.\n");
PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c%c\nArch%c%s%c%c\n",
PLUGIN_REGISTRY_FIELD_DELIMITER,
kPluginRegistryVersion,
- flashOnly ? 't' : 'f',
+ 'f', //flashOnly
PLUGIN_REGISTRY_FIELD_DELIMITER,
PLUGIN_REGISTRY_END_OF_LINE_MARKER,
PLUGIN_REGISTRY_FIELD_DELIMITER,
@@ -3071,9 +3014,8 @@ nsPluginHost::ReadPluginInfo()
// If we're reading an old registry, ignore it
// If we flipped the flash-only pref, ignore it
- bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
nsAutoCString expectedVersion(kPluginRegistryVersion);
- expectedVersion.Append(flashOnly ? 't' : 'f');
+ expectedVersion.Append('f'); //flashOnly
if (!expectedVersion.Equals(values[1])) {
return rv;
@@ -3214,10 +3156,6 @@ nsPluginHost::ReadPluginInfo()
MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->FileName().get()));
- if (!ShouldAddPlugin(tag)) {
- continue;
- }
-
tag->mNext = mCachedPlugins;
mCachedPlugins = tag;
}
diff --git a/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
index 8d6149c58..f5b7345f9 100644
--- a/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
+++ b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
@@ -32,7 +32,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/dom/plugins/test/unit/xpcshell.ini b/dom/plugins/test/unit/xpcshell.ini
index 8dae66b20..69b6731b2 100644
--- a/dom/plugins/test/unit/xpcshell.ini
+++ b/dom/plugins/test/unit/xpcshell.ini
@@ -5,7 +5,7 @@ tail =
tags = addons
firefox-appdir = browser
support-files =
- !/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+ !/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
[test_allowed_types.js]
skip-if = appname == "thunderbird"
diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp
index 815c7734d..5e435d4ca 100644
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -156,10 +156,13 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
nsAutoString nonce;
bool parserCreated = false;
if (!isPreload) {
- nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aRequestContext);
- if (htmlElement) {
- rv = htmlElement->GetAttribute(NS_LITERAL_STRING("nonce"), nonce);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (aContentType == nsIContentPolicy::TYPE_SCRIPT ||
+ aContentType == nsIContentPolicy::TYPE_STYLESHEET) {
+ nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aRequestContext);
+ if (htmlElement) {
+ rv = htmlElement->GetAttribute(NS_LITERAL_STRING("nonce"), nonce);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
}
nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aRequestContext);
@@ -343,6 +346,20 @@ nsCSPContext::GetBlockAllMixedContent(bool *outBlockAllMixedContent)
}
NS_IMETHODIMP
+nsCSPContext::GetEnforcesFrameAncestors(bool *outEnforcesFrameAncestors)
+{
+ *outEnforcesFrameAncestors = false;
+ for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+ if (!mPolicies[i]->getReportOnlyFlag() &&
+ mPolicies[i]->hasDirective(nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)) {
+ *outEnforcesFrameAncestors = true;
+ return NS_OK;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsCSPContext::GetReferrerPolicy(uint32_t* outPolicy, bool* outIsSet)
{
*outIsSet = false;
diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp
index f1b5d8ba7..86aa4e001 100644
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -532,7 +532,7 @@ nsCSPParser::keywordSource()
// Special case handling for 'self' which is not stored internally as a keyword,
// but rather creates a nsCSPHostSrc using the selfURI
if (CSP_IsKeyword(mCurToken, CSP_SELF)) {
- return CSP_CreateHostSrcFromURI(mSelfURI);
+ return CSP_CreateHostSrcFromSelfURI(mSelfURI);
}
if (CSP_IsKeyword(mCurToken, CSP_STRICT_DYNAMIC)) {
diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp
index b074a980c..a5f683b01 100644
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -266,20 +266,21 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType)
}
nsCSPHostSrc*
-CSP_CreateHostSrcFromURI(nsIURI* aURI)
+CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI)
{
// Create the host first
nsCString host;
- aURI->GetHost(host);
+ aSelfURI->GetAsciiHost(host);
nsCSPHostSrc *hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host));
+ hostsrc->setGeneratedFromSelfKeyword();
// Add the scheme.
nsCString scheme;
- aURI->GetScheme(scheme);
+ aSelfURI->GetScheme(scheme);
hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme));
int32_t port;
- aURI->GetPort(&port);
+ aSelfURI->GetPort(&port);
// Only add port if it's not default port.
if (port > 0) {
nsAutoString portStr;
@@ -348,13 +349,17 @@ CSP_IsQuotelessKeyword(const nsAString& aKey)
* @param aUpgradeInsecure
* Whether the policy makes use of the directive
* 'upgrade-insecure-requests'.
+ * @param aFromSelfURI
+ * Whether a scheme was generated from the keyword 'self'
+ * which then allows schemeless sources to match ws and wss.
*/
bool
permitsScheme(const nsAString& aEnforcementScheme,
nsIURI* aUri,
bool aReportOnly,
- bool aUpgradeInsecure)
+ bool aUpgradeInsecure,
+ bool aFromSelfURI)
{
nsAutoCString scheme;
nsresult rv = aUri->GetScheme(scheme);
@@ -373,8 +378,20 @@ permitsScheme(const nsAString& aEnforcementScheme,
// allow scheme-less sources where the protected resource is http
// and the load is https, see:
// http://www.w3.org/TR/CSP2/#match-source-expression
- if (aEnforcementScheme.EqualsASCII("http") &&
- scheme.EqualsASCII("https")) {
+ if (aEnforcementScheme.EqualsASCII("http")) {
+ if (scheme.EqualsASCII("https")) {
+ return true;
+ }
+ if ((scheme.EqualsASCII("ws") || scheme.EqualsASCII("wss")) && aFromSelfURI) {
+ return true;
+ }
+ }
+ if (aEnforcementScheme.EqualsASCII("https")) {
+ if (scheme.EqualsLiteral("wss") && aFromSelfURI) {
+ return true;
+ }
+ }
+ if (aEnforcementScheme.EqualsASCII("ws") && scheme.EqualsASCII("wss")) {
return true;
}
@@ -483,7 +500,7 @@ nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirect
if (mInvalidated) {
return false;
}
- return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure);
+ return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false);
}
bool
@@ -503,6 +520,7 @@ nsCSPSchemeSrc::toString(nsAString& outStr) const
nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost)
: mHost(aHost)
+ , mGeneratedFromSelfKeyword(false)
, mWithinFrameAncstorsDir(false)
{
ToLowerCase(mHost);
@@ -612,7 +630,7 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected
// http://www.w3.org/TR/CSP11/#match-source-expression
// 4.3) scheme matching: Check if the scheme matches.
- if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure)) {
+ if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, mGeneratedFromSelfKeyword)) {
return false;
}
@@ -643,7 +661,7 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected
// Before we can check if the host matches, we have to
// extract the host part from aUri.
nsAutoCString uriHost;
- nsresult rv = aUri->GetHost(uriHost);
+ nsresult rv = aUri->GetAsciiHost(uriHost);
NS_ENSURE_SUCCESS(rv, false);
nsString decodedUriHost;
diff --git a/dom/security/nsCSPUtils.h b/dom/security/nsCSPUtils.h
index 468c734a2..cfbe83256 100644
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -186,7 +186,7 @@ nsresult CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp,
class nsCSPHostSrc;
-nsCSPHostSrc* CSP_CreateHostSrcFromURI(nsIURI* aURI);
+nsCSPHostSrc* CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI);
bool CSP_IsValidDirective(const nsAString& aDir);
bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
@@ -256,6 +256,9 @@ class nsCSPHostSrc : public nsCSPBaseSrc {
void setPort(const nsAString& aPort);
void appendPath(const nsAString &aPath);
+ inline void setGeneratedFromSelfKeyword() const
+ { mGeneratedFromSelfKeyword = true;}
+
inline void setWithinFrameAncestorsDir(bool aValue) const
{ mWithinFrameAncstorsDir = aValue; }
@@ -276,6 +279,7 @@ class nsCSPHostSrc : public nsCSPBaseSrc {
nsString mHost;
nsString mPort;
nsString mPath;
+ mutable bool mGeneratedFromSelfKeyword;
mutable bool mWithinFrameAncstorsDir;
};
diff --git a/dom/security/test/csp/file_ignore_xfo.html b/dom/security/test/csp/file_ignore_xfo.html
new file mode 100644
index 000000000..6746a3adb
--- /dev/null
+++ b/dom/security/test/csp/file_ignore_xfo.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1024557: Ignore x-frame-options if CSP with frame-ancestors exists</title>
+</head>
+<body>
+<div id="cspmessage">Ignoring XFO because of CSP</div>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_ignore_xfo.html^headers^ b/dom/security/test/csp/file_ignore_xfo.html^headers^
new file mode 100644
index 000000000..e93f9e3ec
--- /dev/null
+++ b/dom/security/test/csp/file_ignore_xfo.html^headers^
@@ -0,0 +1,3 @@
+Content-Security-Policy: frame-ancestors http://mochi.test:8888
+X-Frame-Options: deny
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_image_nonce.html b/dom/security/test/csp/file_image_nonce.html
new file mode 100644
index 000000000..5d57bb837
--- /dev/null
+++ b/dom/security/test/csp/file_image_nonce.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>Bug 1355801: Nonce should not apply to images</title>
+ </head>
+<body>
+
+<img id='matchingNonce' src='http://mochi.test:8888/tests/image/test/mochitest/blue.png?a' nonce='abc'></img>
+<img id='nonMatchingNonce' src='http://mochi.test:8888/tests/image/test/mochitest/blue.png?b' nonce='bca'></img>
+<img id='noNonce' src='http://mochi.test:8888/tests/image/test/mochitest/blue.png?c'></img>
+
+<script type='application/javascript'>
+ var matchingNonce = document.getElementById('matchingNonce');
+ matchingNonce.onload = function(e) {
+ window.parent.postMessage({result: 'img-with-matching-nonce-loaded'}, '*');
+ };
+ matchingNonce.onerror = function(e) {
+ window.parent.postMessage({result: 'img-with-matching-nonce-blocked'}, '*');
+ }
+
+ var nonMatchingNonce = document.getElementById('nonMatchingNonce');
+ nonMatchingNonce.onload = function(e) {
+ window.parent.postMessage({result: 'img-with_non-matching-nonce-loaded'}, '*');
+ };
+ nonMatchingNonce.onerror = function(e) {
+ window.parent.postMessage({result: 'img-with_non-matching-nonce-blocked'}, '*');
+ }
+
+ var noNonce = document.getElementById('noNonce');
+ noNonce.onload = function(e) {
+ window.parent.postMessage({result: 'img-without-nonce-loaded'}, '*');
+ };
+ noNonce.onerror = function(e) {
+ window.parent.postMessage({result: 'img-without-nonce-blocked'}, '*');
+ }
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_image_nonce.html^headers^ b/dom/security/test/csp/file_image_nonce.html^headers^
new file mode 100644
index 000000000..0d63558c4
--- /dev/null
+++ b/dom/security/test/csp/file_image_nonce.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: img-src 'nonce-abc';
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_punycode_host_src.js b/dom/security/test/csp/file_punycode_host_src.js
new file mode 100644
index 000000000..3505faf70
--- /dev/null
+++ b/dom/security/test/csp/file_punycode_host_src.js
@@ -0,0 +1,2 @@
+const LOADED = true;
+parent.postMessage({result: 'script-allowed'}, "*"); \ No newline at end of file
diff --git a/dom/security/test/csp/file_punycode_host_src.sjs b/dom/security/test/csp/file_punycode_host_src.sjs
new file mode 100644
index 000000000..3189cc063
--- /dev/null
+++ b/dom/security/test/csp/file_punycode_host_src.sjs
@@ -0,0 +1,45 @@
+// custom *.sjs for Bug 1224225
+// Punycode in CSP host sources
+
+const HTML_PART1 =
+ "<!DOCTYPE HTML>" +
+ "<html><head><meta charset=\"utf-8\">" +
+ "<title>Bug 1224225 - CSP source matching should work for punycoded domain names</title>" +
+ "</head>" +
+ "<body>" +
+ "<script id='script' src='";
+
+const TESTCASE1 = "http://sub2.ält.example.org/";
+const TESTCASE2 = "http://sub2.xn--lt-uia.example.org/"
+
+const HTML_PART2 = "tests/dom/security/test/csp/file_punycode_host_src.js'></script>" +
+ "</body>" +
+ "</html>";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ Components.utils.importGlobalProperties(["URLSearchParams"]);
+ const query = new URLSearchParams(request.queryString);
+
+
+ if (query.get("csp")) {
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+ }
+ if (query.get("action") == "script-unicode-csp-punycode") {
+ response.write(HTML_PART1 + TESTCASE1 + HTML_PART2);
+ return
+ }
+ if (query.get("action") == "script-punycode-csp-punycode") {
+ response.write(HTML_PART1 + TESTCASE2 + HTML_PART2);
+ return
+ }
+
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_ro_ignore_xfo.html b/dom/security/test/csp/file_ro_ignore_xfo.html
new file mode 100644
index 000000000..85e7f0092
--- /dev/null
+++ b/dom/security/test/csp/file_ro_ignore_xfo.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1024557: Ignore x-frame-options if CSP with frame-ancestors exists</title>
+</head>
+<body>
+<div id="cspmessage">Ignoring XFO because of CSP_RO</div>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/security/test/csp/file_ro_ignore_xfo.html^headers^ b/dom/security/test/csp/file_ro_ignore_xfo.html^headers^
new file mode 100644
index 000000000..ab8366f06
--- /dev/null
+++ b/dom/security/test/csp/file_ro_ignore_xfo.html^headers^
@@ -0,0 +1,3 @@
+Content-Security-Policy-Report-Only: frame-ancestors http://mochi.test:8888
+X-Frame-Options: deny
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_upgrade_insecure_navigation.sjs b/dom/security/test/csp/file_upgrade_insecure_navigation.sjs
new file mode 100644
index 000000000..51afa39bf
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_navigation.sjs
@@ -0,0 +1,79 @@
+// Custom *.sjs file specifically for the needs of
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1271173
+
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const TEST_NAVIGATIONAL_UPGRADE = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <a href="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_navigation.sjs?action=framenav" id="testlink">clickme</a>
+ <script type="text/javascript">
+ // before navigating the current frame we open the window and check that uir applies
+ var myWin = window.open("http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_navigation.sjs?action=docnav");
+
+ window.addEventListener("message", receiveMessage, false);
+ function receiveMessage(event) {
+ myWin.close();
+ var link = document.getElementById('testlink');
+ link.click();
+ }
+ </script>
+ </body>
+ </html>`;
+
+const FRAME_NAV = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <script type="text/javascript">
+ parent.postMessage({result: document.documentURI}, "*");
+ </script>
+ </body>
+ </html>`;
+
+const DOC_NAV = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <script type="text/javascript">
+ // call back to the main testpage signaling whether the upgraded succeeded
+ window.opener.parent.postMessage({result: document.documentURI}, "*");
+ // let the opener (iframe) now that we can now close the window and move on with the test.
+ window.opener.postMessage({result: "readyToMoveOn"}, "*");
+ </script>
+ </body>
+ </html>`;
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+ if (query.get("csp")) {
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+ }
+
+ if (query.get("action") === "perform_navigation") {
+ response.write(TEST_NAVIGATIONAL_UPGRADE);
+ return;
+ }
+
+ if (query.get("action") === "framenav") {
+ response.write(FRAME_NAV);
+ return;
+ }
+
+ if (query.get("action") === "docnav") {
+ response.write(DOC_NAV);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_websocket_explicit.html b/dom/security/test/csp/file_websocket_explicit.html
new file mode 100644
index 000000000..51462ab74
--- /dev/null
+++ b/dom/security/test/csp/file_websocket_explicit.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1345615: Allow websocket schemes when using 'self' in CSP</title>
+ <meta http-equiv="Content-Security-Policy" content="connect-src ws:">
+</head>
+<body>
+ <script type="application/javascript">
+ /* load socket using ws */
+ var wsSocket = new WebSocket("ws://example.com/tests/dom/security/test/csp/file_websocket_self");
+ wsSocket.onopen = function(e) {
+ window.parent.postMessage({result: "explicit-ws-loaded"}, "*");
+ wsSocket.close();
+ };
+ wsSocket.onerror = function(e) {
+ window.parent.postMessage({result: "explicit-ws-blocked"}, "*");
+ };
+
+ /* load socket using wss */
+ var wssSocket = new WebSocket("wss://example.com/tests/dom/security/test/csp/file_websocket_self");
+ wssSocket.onopen = function(e) {
+ window.parent.postMessage({result: "explicit-wss-loaded"}, "*");
+ wssSocket.close();
+ };
+ wssSocket.onerror = function(e) {
+ window.parent.postMessage({result: "explicit-wss-blocked"}, "*");
+ };
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_websocket_self.html b/dom/security/test/csp/file_websocket_self.html
new file mode 100644
index 000000000..3ff5f0558
--- /dev/null
+++ b/dom/security/test/csp/file_websocket_self.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1345615: Allow websocket schemes when using 'self' in CSP</title>
+ <meta http-equiv="Content-Security-Policy" content="connect-src 'self'">
+</head>
+<body>
+ <script type="application/javascript">
+ /* load socket using ws */
+ var wsSocket = new WebSocket("ws://example.com/tests/dom/security/test/csp/file_websocket_self");
+ wsSocket.onopen = function(e) {
+ window.parent.postMessage({result: "self-ws-loaded"}, "*");
+ wsSocket.close();
+ };
+ wsSocket.onerror = function(e) {
+ window.parent.postMessage({result: "self-ws-blocked"}, "*");
+ };
+
+ /* load socket using wss */
+ var wssSocket = new WebSocket("wss://example.com/tests/dom/security/test/csp/file_websocket_self");
+ wssSocket.onopen = function(e) {
+ window.parent.postMessage({result: "self-wss-loaded"}, "*");
+ wssSocket.close();
+ };
+ wssSocket.onerror = function(e) {
+ window.parent.postMessage({result: "self-wss-blocked"}, "*");
+ };
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_websocket_self_wsh.py b/dom/security/test/csp/file_websocket_self_wsh.py
new file mode 100644
index 000000000..5fe508a91
--- /dev/null
+++ b/dom/security/test/csp/file_websocket_self_wsh.py
@@ -0,0 +1,7 @@
+from mod_pywebsocket import msgutil
+
+def web_socket_do_extra_handshake(request):
+ pass
+
+def web_socket_transfer_data(request):
+ pass
diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini
index 8add999c3..2102cbe70 100644
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -206,6 +206,18 @@ support-files =
file_iframe_srcdoc.sjs
file_iframe_sandbox_srcdoc.html
file_iframe_sandbox_srcdoc.html^headers^
+ file_ignore_xfo.html
+ file_ignore_xfo.html^headers^
+ file_ro_ignore_xfo.html
+ file_ro_ignore_xfo.html^headers^
+ file_upgrade_insecure_navigation.sjs
+ file_image_nonce.html
+ file_image_nonce.html^headers^
+ file_punycode_host_src.sjs
+ file_punycode_host_src.js
+ file_websocket_self.html
+ file_websocket_explicit.html
+ file_websocket_self_wsh.py
[test_base-uri.html]
[test_blob_data_schemes.html]
@@ -292,9 +304,15 @@ tags = mcb
[test_strict_dynamic.html]
[test_strict_dynamic_parser_inserted.html]
[test_strict_dynamic_default_src.html]
+[test_upgrade_insecure_navigation.html]
[test_iframe_sandbox_srcdoc.html]
[test_iframe_srcdoc.html]
[test_sandbox_allow_scripts.html]
support-files =
file_sandbox_allow_scripts.html
file_sandbox_allow_scripts.html^headers^
+[test_ignore_xfo.html]
+[test_image_nonce.html]
+[test_punycode_host_src.html]
+[test_websocket_self.html]
+skip-if = toolkit == 'android'
diff --git a/dom/security/test/csp/test_ignore_xfo.html b/dom/security/test/csp/test_ignore_xfo.html
new file mode 100644
index 000000000..fb3aadc6c
--- /dev/null
+++ b/dom/security/test/csp/test_ignore_xfo.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1024557: Ignore x-frame-options if CSP with frame-ancestors exists</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="csp_testframe"></iframe>
+<iframe style="width:100%;" id="csp_ro_testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * We load two frames using:
+ * x-frame-options: deny
+ * where the first frame uses a csp and the second a csp_ro including frame-ancestors.
+ * We make sure that xfo is ignored for regular csp but not for csp_ro.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var testcounter = 0;
+function checkFinished() {
+ testcounter++;
+ if (testcounter < 2) {
+ return;
+ }
+ SimpleTest.finish();
+}
+
+// 1) test XFO with CSP
+var csp_testframe = document.getElementById("csp_testframe");
+csp_testframe.onload = function() {
+ var msg = csp_testframe.contentWindow.document.getElementById("cspmessage");
+ is(msg.innerHTML, "Ignoring XFO because of CSP", "Loading frame with with XFO and CSP");
+ checkFinished();
+}
+csp_testframe.onerror = function() {
+ ok(false, "sanity: should not fire onerror for csp_testframe");
+}
+csp_testframe.src = "file_ignore_xfo.html";
+
+// 2) test XFO with CSP_RO
+var csp_ro_testframe = document.getElementById("csp_ro_testframe");
+csp_ro_testframe.onload = function() {
+ var msg = csp_ro_testframe.contentWindow.document.getElementById("cspmessage");
+ is(msg, null, "Blocking frame with with XFO and CSP_RO");
+ checkFinished();
+}
+csp_ro_testframe.onerror = function() {
+ ok(false, "sanity: should not fire onerror for csp_ro_testframe");
+}
+csp_ro_testframe.src = "file_ro_ignore_xfo.html";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_image_nonce.html b/dom/security/test/csp/test_image_nonce.html
new file mode 100644
index 000000000..ff6d636b6
--- /dev/null
+++ b/dom/security/test/csp/test_image_nonce.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load three images: (a) with a matching nonce,
+ (b) with a non matching nonce,
+ * (c) with no nonce
+ * and make sure that all three images get blocked because
+ * "img-src nonce-bla" should not allow an image load, not
+ * even if the nonce matches*.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var counter = 0;
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+function checkResults(aResult) {
+ counter++;
+ if (aResult === "img-with-matching-nonce-blocked" ||
+ aResult === "img-with_non-matching-nonce-blocked" ||
+ aResult === "img-without-nonce-blocked") {
+ ok (true, "correct result for: " + aResult);
+ }
+ else {
+ ok(false, "unexpected result: " + aResult + "\n\n");
+ }
+ if (counter < 3) {
+ return;
+ }
+ finishTest();
+}
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to bubble up results back to this main page.
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+document.getElementById("testframe").src = "file_image_nonce.html";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_punycode_host_src.html b/dom/security/test/csp/test_punycode_host_src.html
new file mode 100644
index 000000000..8d891725c
--- /dev/null
+++ b/dom/security/test/csp/test_punycode_host_src.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1224225 - CSP source matching should work for punycoded domain names</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load scripts within an iframe and make sure that the
+ * CSP matching is same for punycode domain names as well as IDNA.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+
+var curTest;
+var counter = -1;
+
+const tests = [
+ { // test 1
+ description: "loads script as sub2.ält.example.org, but whitelist in CSP as sub2.xn--lt-uia.example.org",
+ action: "script-unicode-csp-punycode",
+ csp: "script-src http://sub2.xn--lt-uia.example.org;",
+ expected: "script-allowed",
+
+ },
+ { // test 2
+ description: "loads script as sub2.xn--lt-uia.example.org, and whitelist in CSP as sub2.xn--lt-uia.example.org",
+ action: "script-punycode-csp-punycode",
+ csp: "script-src http://sub2.xn--lt-uia.example.org;",
+ expected: "script-allowed",
+
+ },
+ { // test 3
+ description: "loads script as sub2.xn--lt-uia.example.org, and whitelist in CSP as sub2.xn--lt-uia.example.org",
+ action: "script-punycode-csp-punycode",
+ csp: "script-src *.xn--lt-uia.example.org;",
+ expected: "script-allowed",
+
+ },
+
+];
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+function checkResults(result) {
+ is(result, curTest.expected, curTest.description);
+ loadNextTest();
+}
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+ curTest = tests[counter];
+ var testframe = document.getElementById("testframe");
+ testframe.src = `file_punycode_host_src.sjs?action=${curTest.action}&csp=${curTest.csp}`;
+}
+
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_upgrade_insecure_navigation.html b/dom/security/test/csp/test_upgrade_insecure_navigation.html
new file mode 100644
index 000000000..db6a6a1be
--- /dev/null
+++ b/dom/security/test/csp/test_upgrade_insecure_navigation.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1271173 - Missing spec on Upgrade Insecure Requests(Navigational Upgrades) </title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+<iframe style="width:100%;" id="sandboxedtestframe"
+ sandbox="allow-scripts allow-top-navigation allow-same-origin allow-pointer-lock allow-popups"></iframe>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * We load a page into an iframe that performs a navigational request.
+ * We make sure that upgrade-insecure-requests applies and the page
+ * gets upgraded to https if same origin.
+ * Please note that uir only applies to sandboxed iframes if
+ * the value 'allow-same-origin' is specified.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ {
+ csp: "upgrade-insecure-requests;",
+ result: "https",
+ origin: "http://example.com",
+ desc: "upgrade-insecure-requests same origin should upgrade"
+ },
+ {
+ csp: "",
+ result: "http",
+ origin: "http://example.com",
+ desc: "No upgrade-insecure-requests same origin should not upgrade"
+ },
+ {
+ csp: "upgrade-insecure-requests;",
+ result: "http",
+ origin: "http://mochi.test:8888",
+ desc: "upgrade-insecure-requests cross origin should not upgrade"
+ },
+ {
+ csp: "",
+ result: "http",
+ origin: "http://mochi.test:8888",
+ desc: "No upgrade-insecure-requests cross origin should not upgrade"
+ },
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+var subtests = 0;
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ var result = event.data.result;
+ // query the scheme from the URL before comparing the result
+ var scheme = result.substring(0, result.indexOf(":"));
+ is(scheme, tests[counter].result, tests[counter].desc);
+
+ // @hardcoded 4:
+ // each test run contains of two subtests (frame and top-level)
+ // and we load each test into a regular iframe and into a
+ // sandboxed iframe. only move on to the next test once all
+ // four results from the subtests have bubbled up.
+ subtests++;
+ if (subtests != 4) {
+ return;
+ }
+ subtests = 0;
+ loadNextTest();
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+
+ var src = tests[counter].origin;
+ src += "/tests/dom/security/test/csp/file_upgrade_insecure_navigation.sjs";
+ src += "?csp=" + escape(tests[counter].csp);
+ src += "&action=perform_navigation";
+ document.getElementById("testframe").src = src;
+ document.getElementById("sandboxedtestframe").src = src;
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_websocket_self.html b/dom/security/test/csp/test_websocket_self.html
new file mode 100644
index 000000000..a03c32704
--- /dev/null
+++ b/dom/security/test/csp/test_websocket_self.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1345615: Allow websocket schemes when using 'self' in CSP</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="test_ws_self_frame"></iframe>
+<iframe style="width:100%;" id="test_ws_explicit_frame"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load an iframe using connect-src 'self' and one
+ * iframe using connect-src ws: and make
+ * sure that in both cases ws: as well as wss: is allowed to load.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+const TOTAL_TESTS = 4;
+var counter = 0;
+
+function checkResults(result) {
+ counter++;
+ if (result === "self-ws-loaded" || result === "self-wss-loaded" ||
+ result === "explicit-ws-loaded" || result === "explicit-wss-loaded") {
+ ok(true, "Evaluating: " + result);
+ }
+ else {
+ ok(false, "Evaluating: " + result);
+ }
+ if (counter < TOTAL_TESTS) {
+ return;
+ }
+ finishTest();
+}
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+const HOST = "http://example.com/tests/dom/security/test/csp/";
+var test_ws_self_frame = document.getElementById("test_ws_self_frame");
+test_ws_self_frame.src = HOST + "file_websocket_self.html";
+
+var test_ws_explicit_frame = document.getElementById("test_ws_explicit_frame");
+test_ws_explicit_frame.src = HOST + "file_websocket_explicit.html";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/gtest/TestCSPParser.cpp b/dom/security/test/gtest/TestCSPParser.cpp
index fafa7b5d9..8d168d81c 100644
--- a/dom/security/test/gtest/TestCSPParser.cpp
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -204,6 +204,8 @@ TEST(CSPParser, Directives)
{
static const PolicyTest policies[] =
{
+ { "connect-src xn--mnchen-3ya.de",
+ "connect-src http://xn--mnchen-3ya.de"},
{ "default-src http://www.example.com",
"default-src http://www.example.com" },
{ "script-src http://www.example.com",
diff --git a/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html b/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html
index c6ee89ee3..cf27e5d87 100644
--- a/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html
+++ b/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html
@@ -31,13 +31,13 @@
function checkGetScrollXYState(flush, vals, testName) {
let scrollX = {}, scrollY = {};
domWindowUtils.getScrollXY(flush, scrollX, scrollY);
- is(scrollX.value, vals[0], "getScrollXY x for test: " + testName);
- is(scrollY.value, vals[1], "getScrollXY y for test: " + testName);
+ is(Math.round(scrollX.value), vals[0], "getScrollXY x for test: " + testName);
+ is(Math.round(scrollY.value), vals[1], "getScrollXY y for test: " + testName);
}
function checkWindowScrollState(vals, testName) {
- is(cwindow.scrollX, vals[0], "scrollX for test: " + testName);
- is(cwindow.scrollY, vals[1], "scrollY for test: " + testName);
+ is(Math.round(cwindow.scrollX), vals[0], "scrollX for test: " + testName);
+ is(Math.round(cwindow.scrollY), vals[1], "scrollY for test: " + testName);
}
// Check initial state (0, 0)
@@ -67,8 +67,8 @@
let scrollX = {}, scrollY = {};
domWindowUtils.getScrollXY(false, scrollX, scrollY);
- is(scrollX.value, 0, "scrollX is zero for display:none iframe");
- is(scrollY.value, 0, "scrollY is zero for display:none iframe");
+ is(Math.round(scrollX.value), 0, "scrollX is zero for display:none iframe");
+ is(Math.round(scrollY.value), 0, "scrollY is zero for display:none iframe");
}
SimpleTest.waitForExplicitFinish();
diff --git a/dom/url/URL.h b/dom/url/URL.h
index 16b4678ba..45e4dd289 100644
--- a/dom/url/URL.h
+++ b/dom/url/URL.h
@@ -155,6 +155,12 @@ public:
GetHref(aRetval, aRv);
}
+ void
+ ToJSON(nsAString& aResult, ErrorResult& aRv) const
+ {
+ GetHref(aResult, aRv);
+ }
+
// URLSearchParamsObserver
void
URLSearchParamsUpdated(URLSearchParams* aSearchParams) override;
diff --git a/dom/url/tests/test_url.html b/dom/url/tests/test_url.html
index 3f3f727d6..d07a752bb 100644
--- a/dom/url/tests/test_url.html
+++ b/dom/url/tests/test_url.html
@@ -438,5 +438,11 @@
url = new URL("data:text/html,<a href=\"http://example.org/?q\">Link</a>");
is(url.href, "data:text/html,<a%20href=\"http://example.org/?q\">Link</a>");
</script>
+
+ <script>
+ var u = new URL('http://www.example.org');
+ ok(u.toJSON(), 'http://www.example.org', "URL.toJSON()");
+ is(JSON.stringify(u), "\"http://www.example.org/\"", "JSON.stringify(u) works");
+ </script>
</body>
</html>
diff --git a/dom/webidl/URL.webidl b/dom/webidl/URL.webidl
index 0baa9913c..4d491e1b3 100644
--- a/dom/webidl/URL.webidl
+++ b/dom/webidl/URL.webidl
@@ -44,9 +44,12 @@ interface URL {
attribute USVString pathname;
[Throws]
attribute USVString search;
- readonly attribute URLSearchParams searchParams;
+ [SameObject] readonly attribute URLSearchParams searchParams;
[Throws]
attribute USVString hash;
+
+ [Throws]
+ USVString toJSON();
};
partial interface URL {
diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl
index 055a274cc..36b1f0313 100644
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -182,14 +182,10 @@ partial interface Window {
[ChromeOnly] void mozScrollSnap();
// The four properties below are double per spec at the moment, but whether
// that will continue is unclear.
- //[Replaceable, Throws] readonly attribute double scrollX;
- //[Replaceable, Throws] readonly attribute double pageXOffset;
- //[Replaceable, Throws] readonly attribute double scrollY;
- //[Replaceable, Throws] readonly attribute double pageYOffset;
- [Replaceable, Throws] readonly attribute long scrollX;
- [Replaceable, Throws] readonly attribute long pageXOffset;
- [Replaceable, Throws] readonly attribute long scrollY;
- [Replaceable, Throws] readonly attribute long pageYOffset;
+ [Replaceable, Throws] readonly attribute double scrollX;
+ [Throws] readonly attribute double pageXOffset;
+ [Replaceable, Throws] readonly attribute double scrollY;
+ [Throws] readonly attribute double pageYOffset;
// client
// These are writable because we allow chrome to write them. And they need
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index 0b415d448..f24c366e8 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -18,7 +18,6 @@ PREPROCESSED_WEBIDL_FILES = [
WEBIDL_FILES = [
'AbstractWorker.webidl',
- 'AddonManager.webidl',
'AnalyserNode.webidl',
'Animatable.webidl',
'Animation.webidl',
@@ -592,6 +591,9 @@ WEBIDL_FILES = [
'XULElement.webidl',
]
+if CONFIG['MOZ_WEBEXTENSIONS']:
+ WEBIDL_FILES += ['AddonManager.webidl']
+
if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
WEBIDL_FILES += [
'AudioChannelManager.webidl',
@@ -692,7 +694,6 @@ else:
]
GENERATED_EVENTS_WEBIDL_FILES = [
- 'AddonEvent.webidl',
'AnimationPlaybackEvent.webidl',
'AutocompleteErrorEvent.webidl',
'BlobEvent.webidl',
@@ -734,6 +735,9 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'WebGLContextEvent.webidl',
]
+if CONFIG['MOZ_WEBEXTENSIONS']:
+ GENERATED_EVENTS_WEBIDL_FILES += ['AddonEvent.webidl']
+
if CONFIG['MOZ_WEBRTC']:
GENERATED_EVENTS_WEBIDL_FILES += [
'RTCDataChannelEvent.webidl',
diff --git a/gfx/angle/Makefile.in b/gfx/angle/Makefile.in
index da63a55ca..0394b352b 100755
--- a/gfx/angle/Makefile.in
+++ b/gfx/angle/Makefile.in
@@ -9,10 +9,6 @@ ifdef MOZ_D3DCOMPILER_VISTA_DLL_PATH
cp -fp "$(MOZ_D3DCOMPILER_VISTA_DLL_PATH)" "$(DIST)/bin"
endif
-ifdef MOZ_D3DCOMPILER_XP_CAB
- expand '$(MOZ_D3DCOMPILER_XP_CAB)' -F:$(MOZ_D3DCOMPILER_XP_DLL) '$(DIST)/bin'
-endif
-
endif
include $(topsrcdir)/config/rules.mk
diff --git a/gfx/angle/moz.build b/gfx/angle/moz.build
index 1da8356a5..2a40e0ac6 100755
--- a/gfx/angle/moz.build
+++ b/gfx/angle/moz.build
@@ -139,9 +139,6 @@ if CONFIG['GNU_CXX']:
'-Wno-shadow-local',
]
-if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
- LOCAL_INCLUDES += ['%' + '%s/include/' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
-
DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
DEFINES['_HAS_EXCEPTIONS'] = 0
diff --git a/gfx/angle/src/libANGLE/moz.build b/gfx/angle/src/libANGLE/moz.build
index 0a2a4437f..e127ccce1 100755
--- a/gfx/angle/src/libANGLE/moz.build
+++ b/gfx/angle/src/libANGLE/moz.build
@@ -353,9 +353,6 @@ if CONFIG['GNU_CXX']:
'-Wno-shadow-local',
]
-if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
- LOCAL_INCLUDES += ['%' + '%s/include/' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
-
DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
DEFINES['_HAS_EXCEPTIONS'] = 0
@@ -388,11 +385,6 @@ DEFINES['ANGLE_DEFAULT_D3D11'] = "0"
if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
OS_LIBS += [ 'd3d9', 'dxguid' ]
-else:
- EXTRA_DSO_LDOPTS += [
- '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
- '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
- ]
Library('libANGLE')
diff --git a/gfx/angle/src/libEGL/moz.build b/gfx/angle/src/libEGL/moz.build
index 8e99d44ff..c1e33c86e 100755
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -32,9 +32,6 @@ if CONFIG['GNU_CXX']:
'-Wno-shadow-local',
]
-if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
- LOCAL_INCLUDES += ['%' + '%s/include/' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
-
DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
DEFINES['_HAS_EXCEPTIONS'] = 0
@@ -67,11 +64,6 @@ DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1"
if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
OS_LIBS += [ 'd3d9', 'dxguid' ]
-else:
- EXTRA_DSO_LDOPTS += [
- '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
- '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
- ]
GeckoSharedLibrary('libEGL', linkage=None)
diff --git a/gfx/angle/src/libGLESv2/moz.build b/gfx/angle/src/libGLESv2/moz.build
index 1d40b3b67..08c60c213 100755
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -39,9 +39,6 @@ if CONFIG['GNU_CXX']:
'-Wno-shadow-local',
]
-if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
- LOCAL_INCLUDES += ['%' + '%s/include/' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
-
DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
DEFINES['_HAS_EXCEPTIONS'] = 0
@@ -73,11 +70,6 @@ DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1"
if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
OS_LIBS += [ 'd3d9', 'dxguid' ]
-else:
- EXTRA_DSO_LDOPTS += [
- '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
- '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
- ]
GeckoSharedLibrary('libGLESv2', linkage=None)
diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build
index 596612bb8..6f43a495d 100644
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -150,8 +150,6 @@ FINAL_LIBRARY = 'xul'
if CONFIG['MOZ_D3DCOMPILER_VISTA_DLL']:
DEFINES['MOZ_D3DCOMPILER_VISTA_DLL'] = CONFIG['MOZ_D3DCOMPILER_VISTA_DLL']
-if CONFIG['MOZ_D3DCOMPILER_XP_DLL']:
- DEFINES['MOZ_D3DCOMPILER_XP_DLL'] = CONFIG['MOZ_D3DCOMPILER_XP_DLL']
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp
index e6ceeaf6d..d6e98105c 100644
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -461,8 +461,7 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
weight = mNeedsBold ? 700 : fe->Weight();
}
- fe->FillLogFont(&aLogFont, weight, aSize,
- (mAntialiasOption == kAntialiasSubpixel) ? true : false);
+ fe->FillLogFont(&aLogFont, weight, aSize);
// If GDI synthetic italic is wanted, force the lfItalic field to true
if (aUseGDIFakeItalic) {
diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp
index d80c49356..cc047ef38 100644
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -37,11 +37,6 @@ using namespace mozilla;
#define ROUND(x) floor((x) + 0.5)
-
-#ifndef CLEARTYPE_QUALITY
-#define CLEARTYPE_QUALITY 5
-#endif
-
#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
LogLevel::Debug, args)
#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
@@ -222,16 +217,7 @@ GDIFontEntry::IsSymbolFont()
gfxFont *
GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold)
{
- bool isXP = !IsVistaOrLater();
-
- bool useClearType = isXP && !aFontStyle->systemFont &&
- (gfxWindowsPlatform::GetPlatform()->UseClearTypeAlways() ||
- (mIsDataUserFont &&
- gfxWindowsPlatform::GetPlatform()->UseClearTypeForDownloadableFonts()));
-
- return new gfxGDIFont(this, aFontStyle, aNeedsBold,
- (useClearType ? gfxFont::kAntialiasSubpixel
- : gfxFont::kAntialiasDefault));
+ return new gfxGDIFont(this, aFontStyle, aNeedsBold);
}
nsresult
@@ -263,8 +249,7 @@ GDIFontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
void
GDIFontEntry::FillLogFont(LOGFONTW *aLogFont,
- uint16_t aWeight, gfxFloat aSize,
- bool aUseCleartype)
+ uint16_t aWeight, gfxFloat aSize)
{
memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
@@ -290,8 +275,6 @@ GDIFontEntry::FillLogFont(LOGFONTW *aLogFont,
if (mIsDataUserFont) {
aLogFont->lfItalic = 0;
}
-
- aLogFont->lfQuality = (aUseCleartype ? CLEARTYPE_QUALITY : DEFAULT_QUALITY);
}
#define MISSING_GLYPH 0x1F // glyph index returned for missing characters
@@ -879,15 +862,8 @@ gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
aStyle, w, aStretch, winUserFontData, false);
- if (!fe)
- return fe;
-
- fe->mIsDataUserFont = true;
-
- // Uniscribe doesn't place CFF fonts loaded privately
- // via AddFontMemResourceEx on XP/Vista
- if (isCFF && !IsWin7OrLater()) {
- fe->mForceGDI = true;
+ if (fe) {
+ fe->mIsDataUserFont = true;
}
return fe;
diff --git a/gfx/thebes/gfxGDIFontList.h b/gfx/thebes/gfxGDIFontList.h
index ffb513d64..60fb292e8 100644
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -112,8 +112,7 @@ public:
virtual bool IsSymbolFont();
- void FillLogFont(LOGFONTW *aLogFont, uint16_t aWeight, gfxFloat aSize,
- bool aUseCleartype);
+ void FillLogFont(LOGFONTW *aLogFont, uint16_t aWeight, gfxFloat aSize);
static gfxWindowsFontType DetermineFontType(const NEWTEXTMETRICW& metrics,
DWORD fontType)
diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp
index be1780797..84199170b 100755
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -146,9 +146,6 @@ public:
NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter)
-#define GFX_USE_CLEARTYPE_ALWAYS "gfx.font_rendering.cleartype.always_use_for_content"
-#define GFX_DOWNLOADABLE_FONTS_USE_CLEARTYPE "gfx.font_rendering.cleartype.use_for_downloadable_fonts"
-
#define GFX_CLEARTYPE_PARAMS "gfx.font_rendering.cleartype_params."
#define GFX_CLEARTYPE_PARAMS_GAMMA "gfx.font_rendering.cleartype_params.gamma"
#define GFX_CLEARTYPE_PARAMS_CONTRAST "gfx.font_rendering.cleartype_params.enhanced_contrast"
@@ -194,10 +191,6 @@ public:
HMODULE gdi32Handle;
PFND3DKMTQS queryD3DKMTStatistics = nullptr;
- // GPU memory reporting is not available before Windows 7
- if (!IsWin7OrLater())
- return NS_OK;
-
if ((gdi32Handle = LoadLibrary(TEXT("gdi32.dll"))))
queryD3DKMTStatistics = (PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
@@ -320,9 +313,6 @@ NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)
gfxWindowsPlatform::gfxWindowsPlatform()
: mRenderMode(RENDER_GDI)
{
- mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
- mUseClearTypeAlways = UNINITIALIZED_VALUE;
-
/*
* Initialize COM
*/
@@ -402,10 +392,6 @@ gfxWindowsPlatform::CanUseHardwareVideoDecoding()
bool
gfxWindowsPlatform::InitDWriteSupport()
{
- if (!IsVistaOrLater()) {
- return false;
- }
-
// DWrite is only supported on Windows 7 with the platform update and higher.
// We check this by seeing if D2D1 support is available.
if (!Factory::SupportsD2D1()) {
@@ -997,26 +983,6 @@ gfxWindowsPlatform::GetPlatformCMSOutputProfile(void* &mem, size_t &mem_size)
#endif // _WIN32
}
-bool
-gfxWindowsPlatform::UseClearTypeForDownloadableFonts()
-{
- if (mUseClearTypeForDownloadableFonts == UNINITIALIZED_VALUE) {
- mUseClearTypeForDownloadableFonts = Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_USE_CLEARTYPE, true);
- }
-
- return mUseClearTypeForDownloadableFonts;
-}
-
-bool
-gfxWindowsPlatform::UseClearTypeAlways()
-{
- if (mUseClearTypeAlways == UNINITIALIZED_VALUE) {
- mUseClearTypeAlways = Preferences::GetBool(GFX_USE_CLEARTYPE_ALWAYS, false);
- }
-
- return mUseClearTypeAlways;
-}
-
void
gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath, nsAString& aVersion)
{
@@ -1160,14 +1126,7 @@ gfxWindowsPlatform::FontsPrefsChanged(const char *aPref)
gfxPlatform::FontsPrefsChanged(aPref);
- if (!aPref) {
- mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
- mUseClearTypeAlways = UNINITIALIZED_VALUE;
- } else if (!strcmp(GFX_DOWNLOADABLE_FONTS_USE_CLEARTYPE, aPref)) {
- mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
- } else if (!strcmp(GFX_USE_CLEARTYPE_ALWAYS, aPref)) {
- mUseClearTypeAlways = UNINITIALIZED_VALUE;
- } else if (!strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
+ if (aPref && !strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
SetupClearTypeParams();
} else {
clearTextFontCaches = false;
@@ -1415,17 +1374,13 @@ gfxWindowsPlatform::InitializeD3D9Config()
return;
}
- if (!IsVistaOrLater()) {
- d3d9.EnableByDefault();
- } else {
- d3d9.SetDefaultFromPref(
- gfxPrefs::GetLayersAllowD3D9FallbackPrefName(),
- true,
- gfxPrefs::GetLayersAllowD3D9FallbackPrefDefault());
+ d3d9.SetDefaultFromPref(
+ gfxPrefs::GetLayersAllowD3D9FallbackPrefName(),
+ true,
+ gfxPrefs::GetLayersAllowD3D9FallbackPrefDefault());
- if (!d3d9.IsEnabled() && gfxPrefs::LayersPreferD3D9()) {
- d3d9.UserEnable("Direct3D9 enabled via layers.prefer-d3d9");
- }
+ if (!d3d9.IsEnabled() && gfxPrefs::LayersPreferD3D9()) {
+ d3d9.UserEnable("Direct3D9 enabled via layers.prefer-d3d9");
}
nsCString message;
@@ -1596,11 +1551,6 @@ gfxWindowsPlatform::InitializeD2DConfig()
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
return;
}
- if (!IsVistaOrLater()) {
- d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D is not available on Windows XP",
- NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_XP"));
- return;
- }
d2d1.SetDefaultFromPref(
gfxPrefs::GetDirect2DDisabledPrefName(),
@@ -1692,14 +1642,12 @@ gfxWindowsPlatform::InitGPUProcessSupport()
"Not using GPU Process since D3D11 is unavailable",
NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_D3D11"));
} else if (!IsWin7SP1OrLater()) {
- // For Windows XP, we simply don't care enough to support this
- // configuration. On Windows Vista and 7 Pre-SP1, DXGI 1.2 is not
- // available and remote presentation for D3D11 will not work. Rather
- // than take a regression and use D3D9, we revert back to in-process
- // rendering.
+ // On Windows 7 Pre-SP1, DXGI 1.2 is not available and remote presentation
+ // for D3D11 will not work. Rather than take a regression and use D3D9, we
+ // revert back to in-process rendering.
gpuProc.Disable(
FeatureStatus::Unavailable,
- "Windows XP, Vista, and 7 Pre-SP1 cannot use the GPU process",
+ "Windows 7 Pre-SP1 cannot use the GPU process",
NS_LITERAL_CSTRING("FEATURE_FAILURE_OLD_WINDOWS"));
} else if (!IsWin8OrLater()) {
// Windows 7 SP1 can have DXGI 1.2 only via the Platform Update, so we
@@ -1722,10 +1670,6 @@ gfxWindowsPlatform::InitGPUProcessSupport()
bool
gfxWindowsPlatform::DwmCompositionEnabled()
{
- if (!IsVistaOrLater()) {
- return false;
- }
-
MOZ_ASSERT(WinUtils::dwmIsCompositionEnabledPtr);
BOOL dwmEnabled = false;
diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h
index 8db7cf575..f77d9a87a 100644
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -184,11 +184,6 @@ public:
mozilla::gfx::BackendType GetContentBackendFor(mozilla::layers::LayersBackend aLayers) override;
- // ClearType is not always enabled even when available (e.g. Windows XP)
- // if either of these prefs are enabled and apply, use ClearType rendering
- bool UseClearTypeForDownloadableFonts();
- bool UseClearTypeAlways();
-
static void GetDLLVersion(char16ptr_t aDLLPath, nsAString& aVersion);
// returns ClearType tuning information for each display
@@ -247,9 +242,6 @@ protected:
protected:
RenderMode mRenderMode;
- int8_t mUseClearTypeForDownloadableFonts;
- int8_t mUseClearTypeAlways;
-
private:
void Init();
void InitAcceleration() override;
diff --git a/hal/windows/WindowsBattery.cpp b/hal/windows/WindowsBattery.cpp
index c1b9a31e6..520330242 100644
--- a/hal/windows/WindowsBattery.cpp
+++ b/hal/windows/WindowsBattery.cpp
@@ -5,45 +5,20 @@
#include "Hal.h"
#include "HalImpl.h"
-#include "nsITimer.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/battery/Constants.h"
-#include "nsComponentManagerUtils.h"
#include <windows.h>
-#include "mozilla/WindowsVersion.h"
using namespace mozilla::dom::battery;
namespace mozilla {
namespace hal_impl {
-static nsCOMPtr<nsITimer> sUpdateTimer;
-
-/* Power Event API is Vista or later */
-static decltype(RegisterPowerSettingNotification)* sRegisterPowerSettingNotification = nullptr;
-static decltype(UnregisterPowerSettingNotification)* sUnregisterPowerSettingNotification = nullptr;
static HPOWERNOTIFY sPowerHandle = nullptr;
static HPOWERNOTIFY sCapacityHandle = nullptr;
static HWND sHWnd = nullptr;
-static void
-UpdateHandler(nsITimer* aTimer, void* aClosure) {
- NS_ASSERTION(!IsVistaOrLater(),
- "We shouldn't call this function for Vista or later version!");
-
- static hal::BatteryInformation sLastInfo;
- hal::BatteryInformation currentInfo;
-
- hal_impl::GetCurrentBatteryInformation(&currentInfo);
- if (sLastInfo.level() != currentInfo.level() ||
- sLastInfo.charging() != currentInfo.charging() ||
- sLastInfo.remainingTime() != currentInfo.remainingTime()) {
- hal::NotifyBatteryChange(currentInfo);
- sLastInfo = currentInfo;
- }
-}
-
static
LRESULT CALLBACK
BatteryWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
@@ -63,94 +38,56 @@ BatteryWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
void
EnableBatteryNotifications()
{
- if (IsVistaOrLater()) {
- // RegisterPowerSettingNotification is from Vista or later.
- // Use this API if available.
- HMODULE hUser32 = GetModuleHandleW(L"USER32.DLL");
- if (!sRegisterPowerSettingNotification)
- sRegisterPowerSettingNotification = (decltype(RegisterPowerSettingNotification)*)
- GetProcAddress(hUser32, "RegisterPowerSettingNotification");
- if (!sUnregisterPowerSettingNotification)
- sUnregisterPowerSettingNotification = (decltype(UnregisterPowerSettingNotification)*)
- GetProcAddress(hUser32, "UnregisterPowerSettingNotification");
-
- if (!sRegisterPowerSettingNotification ||
- !sUnregisterPowerSettingNotification) {
- NS_ASSERTION(false, "Canot find PowerSettingNotification functions.");
- return;
+ // Create custom window to watch battery event
+ // If we can get Gecko's window handle, this is unnecessary.
+
+ if (sHWnd == nullptr) {
+ WNDCLASSW wc;
+ HMODULE hSelf = GetModuleHandle(nullptr);
+
+ if (!GetClassInfoW(hSelf, L"MozillaBatteryClass", &wc)) {
+ ZeroMemory(&wc, sizeof(WNDCLASSW));
+ wc.hInstance = hSelf;
+ wc.lpfnWndProc = BatteryWindowProc;
+ wc.lpszClassName = L"MozillaBatteryClass";
+ RegisterClassW(&wc);
}
- // Create custom window to watch battery event
- // If we can get Gecko's window handle, this is unnecessary.
-
- if (sHWnd == nullptr) {
- WNDCLASSW wc;
- HMODULE hSelf = GetModuleHandle(nullptr);
-
- if (!GetClassInfoW(hSelf, L"MozillaBatteryClass", &wc)) {
- ZeroMemory(&wc, sizeof(WNDCLASSW));
- wc.hInstance = hSelf;
- wc.lpfnWndProc = BatteryWindowProc;
- wc.lpszClassName = L"MozillaBatteryClass";
- RegisterClassW(&wc);
- }
-
- sHWnd = CreateWindowW(L"MozillaBatteryClass", L"Battery Watcher",
- 0, 0, 0, 0, 0,
- nullptr, nullptr, hSelf, nullptr);
- }
-
- if (sHWnd == nullptr) {
- return;
- }
+ sHWnd = CreateWindowW(L"MozillaBatteryClass", L"Battery Watcher",
+ 0, 0, 0, 0, 0,
+ nullptr, nullptr, hSelf, nullptr);
+ }
- sPowerHandle =
- sRegisterPowerSettingNotification(sHWnd,
- &GUID_ACDC_POWER_SOURCE,
- DEVICE_NOTIFY_WINDOW_HANDLE);
- sCapacityHandle =
- sRegisterPowerSettingNotification(sHWnd,
- &GUID_BATTERY_PERCENTAGE_REMAINING,
- DEVICE_NOTIFY_WINDOW_HANDLE);
- } else
- {
- // for Windows XP. If we remove Windows XP support,
- // we should remove timer-based power notification
- sUpdateTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
- if (sUpdateTimer) {
- sUpdateTimer->InitWithFuncCallback(UpdateHandler,
- nullptr,
- Preferences::GetInt("dom.battery.timer",
- 30000 /* 30s */),
- nsITimer::TYPE_REPEATING_SLACK);
- }
+ if (sHWnd == nullptr) {
+ return;
}
+
+ sPowerHandle =
+ RegisterPowerSettingNotification(sHWnd,
+ &GUID_ACDC_POWER_SOURCE,
+ DEVICE_NOTIFY_WINDOW_HANDLE);
+ sCapacityHandle =
+ RegisterPowerSettingNotification(sHWnd,
+ &GUID_BATTERY_PERCENTAGE_REMAINING,
+ DEVICE_NOTIFY_WINDOW_HANDLE);
}
void
DisableBatteryNotifications()
{
- if (IsVistaOrLater()) {
- if (sPowerHandle) {
- sUnregisterPowerSettingNotification(sPowerHandle);
- sPowerHandle = nullptr;
- }
+ if (sPowerHandle) {
+ UnregisterPowerSettingNotification(sPowerHandle);
+ sPowerHandle = nullptr;
+ }
- if (sCapacityHandle) {
- sUnregisterPowerSettingNotification(sCapacityHandle);
- sCapacityHandle = nullptr;
- }
+ if (sCapacityHandle) {
+ UnregisterPowerSettingNotification(sCapacityHandle);
+ sCapacityHandle = nullptr;
+ }
- if (sHWnd) {
- DestroyWindow(sHWnd);
- sHWnd = nullptr;
- }
- } else
- {
- if (sUpdateTimer) {
- sUpdateTimer->Cancel();
- sUpdateTimer = nullptr;
- }
+ if (sHWnd) {
+ DestroyWindow(sHWnd);
+ sHWnd = nullptr;
}
}
diff --git a/image/decoders/icon/win/nsIconChannel.cpp b/image/decoders/icon/win/nsIconChannel.cpp
index 9ddcbbc48..04680627a 100644
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -29,11 +29,6 @@
#include "nsContentSecurityManager.h"
#include "nsContentUtils.h"
-#ifdef _WIN32_WINNT
-#undef _WIN32_WINNT
-#endif
-#define _WIN32_WINNT 0x0600
-
// we need windows.h to read out registry information...
#include <windows.h>
#include <shellapi.h>
@@ -416,41 +411,27 @@ nsIconChannel::GetStockHIcon(nsIMozIconURI* aIconURI,
{
nsresult rv = NS_OK;
- // We can only do this on Vista or above
- HMODULE hShellDLL = ::LoadLibraryW(L"shell32.dll");
- decltype(SHGetStockIconInfo)* pSHGetStockIconInfo =
- (decltype(SHGetStockIconInfo)*) ::GetProcAddress(hShellDLL,
- "SHGetStockIconInfo");
-
- if (pSHGetStockIconInfo) {
- uint32_t desiredImageSize;
- aIconURI->GetImageSize(&desiredImageSize);
- nsAutoCString stockIcon;
- aIconURI->GetStockIcon(stockIcon);
-
- SHSTOCKICONID stockIconID = GetStockIconIDForName(stockIcon);
- if (stockIconID == SIID_INVALID) {
- return NS_ERROR_NOT_AVAILABLE;
- }
+ uint32_t desiredImageSize;
+ aIconURI->GetImageSize(&desiredImageSize);
+ nsAutoCString stockIcon;
+ aIconURI->GetStockIcon(stockIcon);
- UINT infoFlags = SHGSI_ICON;
- infoFlags |= GetSizeInfoFlag(desiredImageSize);
+ SHSTOCKICONID stockIconID = GetStockIconIDForName(stockIcon);
+ if (stockIconID == SIID_INVALID) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
- SHSTOCKICONINFO sii = {0};
- sii.cbSize = sizeof(sii);
- HRESULT hr = pSHGetStockIconInfo(stockIconID, infoFlags, &sii);
+ UINT infoFlags = SHGSI_ICON;
+ infoFlags |= GetSizeInfoFlag(desiredImageSize);
- if (SUCCEEDED(hr)) {
- *hIcon = sii.hIcon;
- } else {
- rv = NS_ERROR_FAILURE;
- }
- } else {
- rv = NS_ERROR_NOT_AVAILABLE;
- }
+ SHSTOCKICONINFO sii = {0};
+ sii.cbSize = sizeof(sii);
+ HRESULT hr = SHGetStockIconInfo(stockIconID, infoFlags, &sii);
- if (hShellDLL) {
- ::FreeLibrary(hShellDLL);
+ if (SUCCEEDED(hr)) {
+ *hIcon = sii.hIcon;
+ } else {
+ rv = NS_ERROR_FAILURE;
}
return rv;
diff --git a/intl/icu/source/common/umutex.h b/intl/icu/source/common/umutex.h
index b52010f8a..e26ac3555 100644
--- a/intl/icu/source/common/umutex.h
+++ b/intl/icu/source/common/umutex.h
@@ -86,7 +86,9 @@ U_NAMESPACE_END
// Original plan was to use gcc atomics for MinGW, but they
// aren't supported, so we fold MinGW into this path.
+# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
+# endif
# define VC_EXTRALEAN
# define NOUSER
# define NOSERVICE
diff --git a/ipc/app/plugin-container.exe.manifest b/ipc/app/plugin-container.exe.manifest
index d61ddec1a..303109523 100644
--- a/ipc/app/plugin-container.exe.manifest
+++ b/ipc/app/plugin-container.exe.manifest
@@ -37,7 +37,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/ipc/chromium/src/base/process_util_win.cc b/ipc/chromium/src/base/process_util_win.cc
index 12fbc23a5..f22f7216f 100644
--- a/ipc/chromium/src/base/process_util_win.cc
+++ b/ipc/chromium/src/base/process_util_win.cc
@@ -297,43 +297,6 @@ bool LaunchApp(const std::wstring& cmdline,
startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
- // Don't even bother trying pre-Vista...
- if (mozilla::IsVistaOrLater()) {
- // setup our handle array first - if we end up with no handles that can
- // be inherited we can avoid trying to do the ThreadAttributeList dance...
- HANDLE handlesToInherit[2];
- int handleCount = 0;
- HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
- HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE);
-
- if (IsInheritableHandle(stdOut))
- handlesToInherit[handleCount++] = stdOut;
- if (stdErr != stdOut && IsInheritableHandle(stdErr))
- handlesToInherit[handleCount++] = stdErr;
-
- if (handleCount) {
- lpAttributeList = CreateThreadAttributeList(handlesToInherit, handleCount);
- if (lpAttributeList) {
- // it's safe to inherit handles, so arrange for that...
- startup_info.cb = sizeof(startup_info_ex);
- startup_info.dwFlags |= STARTF_USESTDHANDLES;
- startup_info.hStdOutput = stdOut;
- startup_info.hStdError = stdErr;
- startup_info.hStdInput = INVALID_HANDLE_VALUE;
- startup_info_ex.lpAttributeList = lpAttributeList;
- dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
- bInheritHandles = TRUE;
- }
- }
- } else if (PR_GetEnv("MOZ_WIN_INHERIT_STD_HANDLES_PRE_VISTA")) {
- // Even if we can't limit what gets inherited, we sometimes want to inherit
- // stdout/err for testing purposes.
- startup_info.dwFlags |= STARTF_USESTDHANDLES;
- startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
- startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
- startup_info.hStdInput = INVALID_HANDLE_VALUE;
- bInheritHandles = TRUE;
- }
PROCESS_INFORMATION process_info;
BOOL createdOK = CreateProcess(NULL,
diff --git a/ipc/glue/WindowsMessageLoop.cpp b/ipc/glue/WindowsMessageLoop.cpp
index 8057ee25d..8f3471efc 100644
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -1034,7 +1034,7 @@ MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
#if defined(ACCESSIBILITY)
- if (IsVistaOrLater() && (mFlags & REQUIRE_A11Y_REENTRY)) {
+ if (mFlags & REQUIRE_A11Y_REENTRY) {
MOZ_ASSERT(!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION));
return WaitForSyncNotifyWithA11yReentry();
}
diff --git a/ipc/mscom/MainThreadRuntime.cpp b/ipc/mscom/MainThreadRuntime.cpp
index d86317966..a061eaf54 100644
--- a/ipc/mscom/MainThreadRuntime.cpp
+++ b/ipc/mscom/MainThreadRuntime.cpp
@@ -44,13 +44,6 @@ MainThreadRuntime::MainThreadRuntime()
return;
}
- // Windows XP doesn't support setting of the COM exception policy, so we'll
- // just stop here in that case.
- if (!IsVistaOrLater()) {
- mInitResult = S_OK;
- return;
- }
-
// We are required to initialize security in order to configure global options.
mInitResult = InitializeSecurity();
MOZ_ASSERT(SUCCEEDED(mInitResult));
@@ -67,11 +60,8 @@ MainThreadRuntime::MainThreadRuntime()
return;
}
- // Windows 7 has a policy that is even more strict. We should use that one
- // whenever possible.
- ULONG_PTR exceptionSetting = IsWin7OrLater() ?
- COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
- COMGLB_EXCEPTION_DONOT_HANDLE;
+ // Use the strictest policy available.
+ ULONG_PTR exceptionSetting = COMGLB_EXCEPTION_DONOT_HANDLE_ANY;
mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
MOZ_ASSERT(SUCCEEDED(mInitResult));
}
diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp
index bb9f33df9..705d21975 100644
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -39,7 +39,7 @@ mozilla::Atomic<AutoEnterOOMUnsafeRegion*> AutoEnterOOMUnsafeRegion::owner_;
namespace oom {
JS_PUBLIC_DATA(uint32_t) targetThread = 0;
-JS_PUBLIC_DATA(MOZ_THREAD_LOCAL(uint32_t)) threadType;
+MOZ_THREAD_LOCAL(uint32_t) threadType;
JS_PUBLIC_DATA(uint64_t) maxAllocations = UINT64_MAX;
JS_PUBLIC_DATA(uint64_t) counter = 0;
JS_PUBLIC_DATA(bool) failAlways = true;
diff --git a/js/src/old-configure.in b/js/src/old-configure.in
index e4589b951..5da81ce3e 100644
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -143,8 +143,7 @@ MOZ_TOOL_VARIABLES
dnl Special win32 checks
dnl ========================================================
-# Target the Windows 8.1 SDK by default
-WINVER=502
+WINVER=601
case "$target" in
*-mingw*)
@@ -722,12 +721,7 @@ case "$target" in
IMPORT_LIB_SUFFIX=lib
MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
- dnl Set subsystem version 5 for Windows XP.
- if test "$CPU_ARCH" = "x86"; then
- WIN32_SUBSYSTEM_VERSION=5.01
- else
- WIN32_SUBSYSTEM_VERSION=6.01
- fi
+ WIN32_SUBSYSTEM_VERSION=6.01
WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
DSO_LDOPTS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
diff --git a/js/src/threading/windows/ConditionVariable.cpp b/js/src/threading/windows/ConditionVariable.cpp
index 868c35141..3c75a0f27 100644
--- a/js/src/threading/windows/ConditionVariable.cpp
+++ b/js/src/threading/windows/ConditionVariable.cpp
@@ -28,342 +28,34 @@
_InterlockedIncrement((volatile long*)(addend))
#endif
-// Windows XP and Server 2003 don't support condition variables natively. The
-// NativeImports class is responsible for detecting native support and
-// retrieving the appropriate function pointers. It gets instantiated once,
-// using a static initializer.
-class ConditionVariableNativeImports
-{
-public:
- ConditionVariableNativeImports() {
- HMODULE kernel32_dll = GetModuleHandle("kernel32.dll");
- MOZ_RELEASE_ASSERT(kernel32_dll != NULL);
-
-#define LOAD_SYMBOL(symbol) loadSymbol(kernel32_dll, #symbol, symbol)
- supported_ = LOAD_SYMBOL(InitializeConditionVariable) &&
- LOAD_SYMBOL(WakeConditionVariable) &&
- LOAD_SYMBOL(WakeAllConditionVariable) &&
- LOAD_SYMBOL(SleepConditionVariableCS);
-#undef LOAD_SYMBOL
- }
-
- inline bool supported() const {
- return supported_;
- }
-
- void(WINAPI* InitializeConditionVariable)(CONDITION_VARIABLE* ConditionVariable);
- void(WINAPI* WakeAllConditionVariable)(PCONDITION_VARIABLE ConditionVariable);
- void(WINAPI* WakeConditionVariable)(CONDITION_VARIABLE* ConditionVariable);
- BOOL(WINAPI* SleepConditionVariableCS)(CONDITION_VARIABLE* ConditionVariable,
- CRITICAL_SECTION* CriticalSection,
- DWORD dwMilliseconds);
-
-private:
- template <typename T>
- inline bool loadSymbol(HMODULE module, const char* name, T& fn) {
- FARPROC ptr = GetProcAddress(module, name);
- if (!ptr)
- return false;
-
- fn = reinterpret_cast<T>(ptr);
- return true;
- }
-
- bool supported_;
-};
-
-static ConditionVariableNativeImports sNativeImports;
-
// Wrapper for native condition variable APIs.
-struct ConditionVariableNative
-{
- inline void initialize() {
- sNativeImports.InitializeConditionVariable(&cv_);
- }
-
- inline void destroy() {
- // Native condition variables don't require cleanup.
- }
-
- inline void notify_one() { sNativeImports.WakeConditionVariable(&cv_); }
-
- inline void notify_all() { sNativeImports.WakeAllConditionVariable(&cv_); }
-
- inline bool wait(CRITICAL_SECTION* cs, DWORD msec) {
- return sNativeImports.SleepConditionVariableCS(&cv_, cs, msec);
- }
-
-private:
- CONDITION_VARIABLE cv_;
-};
-
-// Fallback condition variable support for Windows XP and Server 2003. Given the
-// difficulty of testing on these antiquated platforms and their rapidly
-// diminishing market share, this implementation trades performance for
-// predictable behavior.
-struct ConditionVariableFallback
-{
- static const uint32_t WAKEUP_MODE_NONE = 0;
- static const uint32_t WAKEUP_MODE_ONE = 0x40000000;
- static const uint32_t WAKEUP_MODE_ALL = 0x80000000;
-
- static const uint32_t WAKEUP_MODE_MASK = WAKEUP_MODE_ONE | WAKEUP_MODE_ALL;
- static const uint32_t SLEEPERS_COUNT_MASK = ~WAKEUP_MODE_MASK;
-
- void initialize()
- {
- // Initialize the state variable to 0 sleepers, no wakeup.
- sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
-
- // Create a semaphore that prevents threads from entering sleep,
- // or waking other threads while a wakeup is ongoing.
- sleepWakeupSemaphore_ = CreateSemaphoreW(NULL, 1, 1, NULL);
- MOZ_RELEASE_ASSERT(sleepWakeupSemaphore_);
-
- // Use an auto-reset event for waking up a single sleeper.
- wakeOneEvent_ = CreateEventW(NULL, FALSE, FALSE, NULL);
- MOZ_RELEASE_ASSERT(wakeOneEvent_);
-
- // Use a manual-reset event for waking up all sleepers.
- wakeAllEvent_ = CreateEventW(NULL, TRUE, FALSE, NULL);
- MOZ_RELEASE_ASSERT(wakeAllEvent_);
- }
-
- void destroy()
- {
- BOOL r;
-
- MOZ_RELEASE_ASSERT(sleepersCountAndWakeupMode_ == (0 | WAKEUP_MODE_NONE));
-
- r = CloseHandle(sleepWakeupSemaphore_);
- MOZ_RELEASE_ASSERT(r);
-
- r = CloseHandle(wakeOneEvent_);
- MOZ_RELEASE_ASSERT(r);
-
- r = CloseHandle(wakeAllEvent_);
- MOZ_RELEASE_ASSERT(r);
- }
-
-private:
- void wakeup(uint32_t wakeupMode, HANDLE wakeEvent)
- {
- // Ensure that only one thread at a time can wake up others.
- BOOL result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE);
- MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0);
-
- // Atomically set the wakeup mode and retrieve the number of sleepers.
- uint32_t wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_,
- wakeupMode);
- uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK;
- MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE);
-
- if (sleepersCount > 0) {
- // If there are any sleepers, set the wake event. The (last) woken
- // up thread is responsible for releasing the semaphore.
- BOOL success = SetEvent(wakeEvent);
- MOZ_RELEASE_ASSERT(success);
-
- } else {
- // If there are no sleepers, set the wakeup mode back to 'none'
- // and release the semaphore ourselves.
- sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
-
- BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
- MOZ_RELEASE_ASSERT(success);
- }
- }
-
-public:
- void notify_one() { wakeup(WAKEUP_MODE_ONE, wakeOneEvent_); }
-
- void notify_all() { wakeup(WAKEUP_MODE_ALL, wakeAllEvent_); }
-
- bool wait(CRITICAL_SECTION* userLock, DWORD msec)
- {
- // Make sure that we can't enter sleep when there are other threads
- // that still need to wake up on either of the wake events being set.
- DWORD result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE);
- MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0);
-
- // Register ourselves as a sleeper. Use an atomic operation, because
- // if another thread times out at the same time, it will decrement the
- // sleepers count without acquiring the semaphore.
- uint32_t wcwm = InterlockedIncrement(&sleepersCountAndWakeupMode_);
- MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE);
-
- // Now that that this thread has been enlisted as a sleeper, it is safe
- // again for other threads to do a wakeup.
- BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
- MOZ_RELEASE_ASSERT(success);
-
- // Release the caller's mutex.
- LeaveCriticalSection(userLock);
-
- // Wait for either event to become signaled, which happens when
- // notify_one() or notify_all() is called, or for a timeout.
- HANDLE handles[2] = { wakeOneEvent_, wakeAllEvent_ };
- DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, msec);
- MOZ_RELEASE_ASSERT(waitResult == WAIT_OBJECT_0 ||
- waitResult == WAIT_OBJECT_0 + 1 ||
- (waitResult == WAIT_TIMEOUT && msec != INFINITE));
-
- // Atomically decrease the sleepers count and retrieve the wakeup mode
- // and new sleepers count.
- // If the wait returned because wakeOneEvent_ was set, we are certain
- // that the wakeup mode will be WAKEUP_MODE_ONE. In that case,
- // atomically reset the wakeup mode to 'none', because if another
- // thread's sleep times out at same time and it finds that it was the
- // last sleeper, it decides whether or not to reset the wakeOneEvent_
- // based on the current wakeup mode.
- uint32_t sub;
- if (waitResult == WAIT_OBJECT_0)
- sub = 1 | WAKEUP_MODE_ONE;
- else
- sub = 1;
- // Note that InterlockedExchangeAdd returns the old value, but it's
- // easier to work with the new value.
- wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_, -sub) - sub;
-
- uint32_t wakeupMode = wcwm & WAKEUP_MODE_MASK;
- uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK;
-
- bool releaseSleepWakeupSemaphore = false;
-
- if (waitResult == WAIT_OBJECT_0) {
- // The wake-one event is an auto-reset event so if we're woken by
- // it, it should already have been reset. We also already removed
- // the WAKEUP_MODE_ONE bit so the wakeup mode should now be 'none'
- // again.
- MOZ_RELEASE_ASSERT(wakeupMode == WAKEUP_MODE_NONE);
-
- // The signaling thread has acquired the enter-wakeup semaphore and
- // expects the woken (this) thread to release it again.
- releaseSleepWakeupSemaphore = true;
-
- } else if (waitResult == WAIT_TIMEOUT && wakeupMode == WAKEUP_MODE_ONE &&
- sleepersCount == 0) {
- // In theory a race condition is possible where the last sleeper
- // times out right at the moment that another thread signals it.
- // If that just happened we now have a dangling signal event and
- // mode, but no threads to be woken up by it, and we need to clean
- // that up.
- BOOL success = ResetEvent(wakeOneEvent_);
- MOZ_RELEASE_ASSERT(success);
-
- // This is safe - we are certain there are no other sleepers that
- // could wake up right now, and the semaphore ensures that no
- // non-sleeping threads are messing with
- // sleepersCountAndWakeupMode_.
- sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
-
- // The signaling thread has acquired the sleep-wakeup semaphore and
- // expects the woken thread to release it. But since there are no
- // sleeping threads left this thread will do it instead.
- releaseSleepWakeupSemaphore = true;
-
- } else if (wakeupMode == WAKEUP_MODE_ALL && sleepersCount == 0) {
- // If this was the last thread waking up in response to a
- // notify_all, clear the wakeup mode and reset the wake-all event.
- // A race condition similar to the case described above could
- // occur, so waitResult could be WAIT_TIMEOUT, but that doesn't
- // matter for the actions that need to be taken.
- MOZ_RELEASE_ASSERT(waitResult = WAIT_OBJECT_0 + 1 ||
- waitResult == WAIT_TIMEOUT);
-
- BOOL success = ResetEvent(wakeAllEvent_);
- MOZ_RELEASE_ASSERT(success);
-
- sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
-
- // The broadcasting thread has acquired the enter-wakeup semaphore
- // and expects the last thread that wakes up to release it.
- releaseSleepWakeupSemaphore = true;
-
- } else if ((waitResult == WAIT_TIMEOUT && msec != INFINITE) ||
- (waitResult == WAIT_OBJECT_0 + 1 &&
- wakeupMode == WAKEUP_MODE_ALL)) {
- // Either:
- // * The wait timed out but found no active notify_one or notify_all
- // the moment it decreased the wait count.
- // * A notify_all woke up this thread but there are more threads
- // that need to be woken up by the wake-all event.
- // These are ordinary conditions in which we don't have to do
- // anything.
-
- } else {
- MOZ_CRASH("invalid wakeup condition");
- }
-
- // Release the enter-wakeup semaphore if the wakeup condition requires
- // us to do it.
- if (releaseSleepWakeupSemaphore) {
- BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
- MOZ_RELEASE_ASSERT(success);
- }
-
- // Reacquire the user mutex.
- EnterCriticalSection(userLock);
-
- // Return true if woken up, false when timed out.
- if (waitResult == WAIT_TIMEOUT) {
- SetLastError(ERROR_TIMEOUT);
- return false;
- }
- return true;
- }
-
-private:
- uint32_t sleepersCountAndWakeupMode_;
- HANDLE sleepWakeupSemaphore_;
- HANDLE wakeOneEvent_;
- HANDLE wakeAllEvent_;
-};
-
struct js::ConditionVariable::PlatformData
{
- union
- {
- ConditionVariableNative native;
- ConditionVariableFallback fallback;
- };
+ CONDITION_VARIABLE cv_;
};
js::ConditionVariable::ConditionVariable()
{
- if (sNativeImports.supported())
- platformData()->native.initialize();
- else
- platformData()->fallback.initialize();
+ InitializeConditionVariable(&platformData()->cv_);
}
void
js::ConditionVariable::notify_one()
{
- if (sNativeImports.supported())
- platformData()->native.notify_one();
- else
- platformData()->fallback.notify_one();
+ WakeConditionVariable(&platformData()->cv_);
}
void
js::ConditionVariable::notify_all()
{
- if (sNativeImports.supported())
- platformData()->native.notify_all();
- else
- platformData()->fallback.notify_all();
+ WakeAllConditionVariable(&platformData()->cv_);
}
void
js::ConditionVariable::wait(UniqueLock<Mutex>& lock)
{
CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
- bool r;
- if (sNativeImports.supported())
- r = platformData()->native.wait(cs, INFINITE);
- else
- r = platformData()->fallback.wait(cs, INFINITE);
+ bool r = SleepConditionVariableCS(&platformData()->cv_, cs, INFINITE);
MOZ_RELEASE_ASSERT(r);
}
@@ -390,11 +82,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
? INFINITE
: static_cast<DWORD>(msecd);
- BOOL r;
- if (sNativeImports.supported())
- r = platformData()->native.wait(cs, msec);
- else
- r = platformData()->fallback.wait(cs, msec);
+ BOOL r = SleepConditionVariableCS(&platformData()->cv_, cs, msec);
if (r)
return CVStatus::NoTimeout;
MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT);
@@ -403,10 +91,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
js::ConditionVariable::~ConditionVariable()
{
- if (sNativeImports.supported())
- platformData()->native.destroy();
- else
- platformData()->fallback.destroy();
+ // Native condition variables don't require cleanup.
}
inline js::ConditionVariable::PlatformData*
diff --git a/js/src/vm/Stopwatch.h b/js/src/vm/Stopwatch.h
index a1b8bbbcb..38a3eb801 100644
--- a/js/src/vm/Stopwatch.h
+++ b/js/src/vm/Stopwatch.h
@@ -301,9 +301,9 @@ struct PerformanceMonitoring {
#if WINVER >= 0x0600
struct cpuid_t {
- WORD group_;
- BYTE number_;
- cpuid_t(WORD group, BYTE number)
+ uint16_t group_;
+ uint8_t number_;
+ cpuid_t(uint16_t group, uint8_t number)
: group_(group),
number_(number)
{ }
diff --git a/js/xpconnect/shell/xpcshell.exe.manifest b/js/xpconnect/shell/xpcshell.exe.manifest
index db2aa0861..c35b77af4 100644
--- a/js/xpconnect/shell/xpcshell.exe.manifest
+++ b/js/xpconnect/shell/xpcshell.exe.manifest
@@ -25,7 +25,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/layout/forms/test/test_bug562447.html b/layout/forms/test/test_bug562447.html
index 53f84428e..99f691871 100644
--- a/layout/forms/test/test_bug562447.html
+++ b/layout/forms/test/test_bug562447.html
@@ -23,7 +23,7 @@ addLoadEvent(function() {
setTimeout(function() {
// Make sure that we're scrolled by 5000px
- is(window.pageYOffset, 5000, "Make sure we're scrolled correctly");
+ is(Math.round(window.pageYOffset), 5000, "Make sure we're scrolled correctly");
// Scroll back up, and mess with the input box along the way
var input = document.getElementById("WhyDoYouFocusMe");
@@ -38,14 +38,14 @@ addLoadEvent(function() {
window.scrollTo(0, 5000);
setTimeout(function() {
- is(window.pageYOffset, 5000, "Sanity check");
+ is(Math.round(window.pageYOffset), 5000, "Sanity check");
window.scrollTo(0, 0);
input.focus();
input.blur();
setTimeout(function() {
- isnot(window.pageYOffset, 0, "This time we shouldn't be scrolled up");
+ isnot(Math.round(window.pageYOffset), 0, "This time we shouldn't be scrolled up");
SimpleTest.finish();
}, 0);
diff --git a/layout/forms/test/test_bug564115.html b/layout/forms/test/test_bug564115.html
index 5723b55d5..ffd4222ca 100644
--- a/layout/forms/test/test_bug564115.html
+++ b/layout/forms/test/test_bug564115.html
@@ -30,12 +30,12 @@ addLoadEvent(function() {
win.scrollTo(0, 5000);
setTimeout(function() {
- is(win.pageYOffset, 5000, "Page should be scrolled correctly");
+ is(Math.round(win.pageYOffset), 5000, "Page should be scrolled correctly");
// Refocus the window
SimpleTest.waitForFocus(function() {
SimpleTest.waitForFocus(function() {
- is(win.pageYOffset, 5000,
+ is(Math.round(win.pageYOffset), 5000,
"The page's scroll offset should not have been changed");
win.close();
diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp
index 052ce58e8..5a54d5455 100644
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -70,8 +70,6 @@ struct OperatingSystemVersionInfo {
// Os version identities used in the -moz-os-version media query.
const OperatingSystemVersionInfo osVersionStrings[] = {
- { LookAndFeel::eOperatingSystemVersion_WindowsXP, L"windows-xp" },
- { LookAndFeel::eOperatingSystemVersion_WindowsVista, L"windows-vista" },
{ LookAndFeel::eOperatingSystemVersion_Windows7, L"windows-win7" },
{ LookAndFeel::eOperatingSystemVersion_Windows8, L"windows-win8" },
{ LookAndFeel::eOperatingSystemVersion_Windows10, L"windows-win10" }
diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp
index b22002d87..fa29fe0f1 100644
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4686,7 +4686,8 @@ nsRuleNode::ComputeTextData(void* aStartStruct,
if (font->mSize != 0) {
lh = nscoord(float(lh) * float(font->mFont.size) / float(font->mSize));
} else {
- lh = minimumFontSize;
+ // Never shrink line heights as a result of minFontSize
+ lh = std::max(lh, minimumFontSize);
}
}
text->mLineHeight.SetCoordValue(lh);
diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js
index 4336f4abd..b083f48ee 100644
--- a/layout/style/test/chrome/bug418986-2.js
+++ b/layout/style/test/chrome/bug418986-2.js
@@ -53,8 +53,6 @@ var suppressed_toggles = [
// Possible values for '-moz-os-version'
var windows_versions = [
- "windows-xp",
- "windows-vista",
"windows-win7",
"windows-win8",
"windows-win10",
diff --git a/layout/style/test/test_media_queries.html b/layout/style/test/test_media_queries.html
index 479306a55..d503fad0b 100644
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -713,8 +713,6 @@ function run() {
expression_should_not_be_parseable("-moz-windows-theme: ");
// os version media queries (currently windows only)
- expression_should_be_parseable("-moz-os-version: windows-xp");
- expression_should_be_parseable("-moz-os-version: windows-vista");
expression_should_be_parseable("-moz-os-version: windows-win7");
expression_should_be_parseable("-moz-os-version: windows-win8");
expression_should_be_parseable("-moz-os-version: windows-win10");
diff --git a/media/mtransport/third_party/nrappkit/src/util/util.c b/media/mtransport/third_party/nrappkit/src/util/util.c
index 31a245f45..1e2801259 100644
--- a/media/mtransport/third_party/nrappkit/src/util/util.c
+++ b/media/mtransport/third_party/nrappkit/src/util/util.c
@@ -506,7 +506,7 @@ strlcat(dst, src, siz)
#endif /* LINUX or WIN32 */
-#if defined(USE_OWN_INET_NTOP) || defined(WIN32)
+#if defined(USE_OWN_INET_NTOP) || (defined(WIN32) && WINVER < 0x0600)
#include <errno.h>
#ifdef WIN32
#include <Ws2ipdef.h>
@@ -775,18 +775,5 @@ int gettimeofday(struct timeval *tv, void *tz)
return 0;
}
-#if _MSC_VER < 1900
-int snprintf(char *buffer, size_t n, const char *format, ...)
-{
- va_list argp;
- int ret;
- va_start(argp, format);
- ret = _vscprintf(format, argp);
- vsnprintf_s(buffer, n, _TRUNCATE, format, argp);
- va_end(argp);
- return ret;
-}
-#endif
-
#endif
diff --git a/media/mtransport/third_party/nrappkit/src/util/util.h b/media/mtransport/third_party/nrappkit/src/util/util.h
index 869b72863..975baa4aa 100644
--- a/media/mtransport/third_party/nrappkit/src/util/util.h
+++ b/media/mtransport/third_party/nrappkit/src/util/util.h
@@ -64,8 +64,7 @@ int nr_write_pid_file(char *pid_filename);
int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val);
int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val);
-#ifdef WIN32
-int snprintf(char *buffer, size_t n, const char *format, ...);
+#if defined(WIN32) && WINVER < 0x0600
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
int inet_pton(int af, const char *src, void *dst);
#endif
diff --git a/mfbt/ThreadLocal.h b/mfbt/ThreadLocal.h
index 880e67735..391e748ad 100644
--- a/mfbt/ThreadLocal.h
+++ b/mfbt/ThreadLocal.h
@@ -9,20 +9,7 @@
#ifndef mozilla_ThreadLocal_h
#define mozilla_ThreadLocal_h
-#if defined(XP_WIN)
-// This file will get included in any file that wants to add a profiler mark.
-// In order to not bring <windows.h> together we could include windef.h and
-// winbase.h which are sufficient to get the prototypes for the Tls* functions.
-// # include <windef.h>
-// # include <winbase.h>
-// Unfortunately, even including these headers causes us to add a bunch of ugly
-// stuff to our namespace e.g #define CreateEvent CreateEventW
-extern "C" {
-__declspec(dllimport) void* __stdcall TlsGetValue(unsigned long);
-__declspec(dllimport) int __stdcall TlsSetValue(unsigned long, void*);
-__declspec(dllimport) unsigned long __stdcall TlsAlloc();
-}
-#else
+#if !defined(XP_WIN)
# include <pthread.h>
# include <signal.h>
#endif
@@ -44,7 +31,7 @@ typedef sig_atomic_t sig_safe_t;
namespace detail {
-#if defined(HAVE_THREAD_TLS_KEYWORD)
+#if defined(HAVE_THREAD_TLS_KEYWORD) || defined(XP_WIN) || defined(XP_MACOSX)
#define MOZ_HAS_THREAD_LOCAL
#endif
@@ -91,11 +78,7 @@ template<typename T>
class ThreadLocal
{
#ifndef MOZ_HAS_THREAD_LOCAL
-#if defined(XP_WIN)
- typedef unsigned long key_t;
-#else
typedef pthread_key_t key_t;
-#endif
// Integral types narrower than void* must be extended to avoid
// warnings from valgrind on some platforms. This helper type
@@ -161,12 +144,7 @@ ThreadLocal<T>::init()
return true;
#else
if (!initialized()) {
-#ifdef XP_WIN
- mKey = TlsAlloc();
- mInited = mKey != 0xFFFFFFFFUL; // TLS_OUT_OF_INDEXES
-#else
mInited = !pthread_key_create(&mKey, nullptr);
-#endif
}
return mInited;
#endif
@@ -181,11 +159,7 @@ ThreadLocal<T>::get() const
#else
MOZ_ASSERT(initialized());
void* h;
-#ifdef XP_WIN
- h = TlsGetValue(mKey);
-#else
h = pthread_getspecific(mKey);
-#endif
return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
#endif
}
@@ -199,11 +173,7 @@ ThreadLocal<T>::set(const T aValue)
#else
MOZ_ASSERT(initialized());
void* h = reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
-#ifdef XP_WIN
- bool succeeded = TlsSetValue(mKey, h);
-#else
bool succeeded = !pthread_setspecific(mKey, h);
-#endif
if (!succeeded) {
MOZ_CRASH();
}
@@ -211,7 +181,11 @@ ThreadLocal<T>::set(const T aValue)
}
#ifdef MOZ_HAS_THREAD_LOCAL
+#if defined(XP_WIN) || defined(XP_MACOSX)
+#define MOZ_THREAD_LOCAL(TYPE) thread_local mozilla::detail::ThreadLocal<TYPE>
+#else
#define MOZ_THREAD_LOCAL(TYPE) __thread mozilla::detail::ThreadLocal<TYPE>
+#endif
#else
#define MOZ_THREAD_LOCAL(TYPE) mozilla::detail::ThreadLocal<TYPE>
#endif
diff --git a/mfbt/WindowsVersion.h b/mfbt/WindowsVersion.h
index f8b071861..4d0cb374b 100644
--- a/mfbt/WindowsVersion.h
+++ b/mfbt/WindowsVersion.h
@@ -84,45 +84,12 @@ IsWindowsBuildOrLater(uint32_t aBuild)
return false;
}
-#if defined(_M_X64) || defined(_M_AMD64)
-// We support only Win7 or later on Win64.
-MOZ_ALWAYS_INLINE bool
-IsXPSP3OrLater()
-{
- return true;
-}
-
-MOZ_ALWAYS_INLINE bool
-IsWin2003OrLater()
-{
- return true;
-}
-
-MOZ_ALWAYS_INLINE bool
-IsWin2003SP2OrLater()
-{
- return true;
-}
-
-MOZ_ALWAYS_INLINE bool
-IsVistaOrLater()
-{
- return true;
-}
-
-MOZ_ALWAYS_INLINE bool
-IsVistaSP1OrLater()
-{
- return true;
-}
+// Although many of the older versions are not supported, we should keep
+// these entries for completeness (since they don't take any resources of
+// note), and to cater to corner cases for applications running e.g. in
+// Windows' compatibility mode.
MOZ_ALWAYS_INLINE bool
-IsWin7OrLater()
-{
- return true;
-}
-#else
-MOZ_ALWAYS_INLINE bool
IsXPSP3OrLater()
{
return IsWindowsVersionOrLater(0x05010300ul);
@@ -157,7 +124,6 @@ IsWin7OrLater()
{
return IsWindowsVersionOrLater(0x06010000ul);
}
-#endif
MOZ_ALWAYS_INLINE bool
IsWin7SP1OrLater()
@@ -190,41 +156,6 @@ IsNotWin7PreRTM()
IsWindowsBuildOrLater(7600);
}
-MOZ_ALWAYS_INLINE bool
-IsWin7AndPre2000Compatible() {
- /*
- * See Bug 1279171.
- * We'd like to avoid using WMF on specific OS version when compatibility
- * mode is in effect. The purpose of this function is to check if FF runs on
- * Win7 OS with application compatibility mode being set to 95/98/ME.
- * Those compatibility mode options (95/98/ME) can only display and
- * be selected for 32-bit application.
- * If the compatibility mode is in effect, the GetVersionEx function will
- * report the OS as it identifies itself, which may not be the OS that is
- * installed.
- * Note : 1) We only target for Win7 build number greater than 7600.
- * 2) GetVersionEx may be altered or unavailable for release after
- * Win8.1. Set pragma to avoid build warning as error.
- */
- bool isWin7 = IsNotWin7PreRTM() && !IsWin8OrLater();
- if (!isWin7) {
- return false;
- }
-
- OSVERSIONINFOEX info;
- ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
-
- info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-#pragma warning(push)
-#pragma warning(disable:4996)
- bool success = GetVersionEx((LPOSVERSIONINFO) &info);
-#pragma warning(pop)
- if (!success) {
- return false;
- }
- return info.dwMajorVersion < 5;
-}
-
} // namespace mozilla
#endif /* mozilla_WindowsVersion_h */
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 6feefd943..53891c608 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -21,7 +21,10 @@
pref("keyword.enabled", false);
pref("general.useragent.locale", "chrome://global/locale/intl.properties");
+pref("general.useragent.compatMode.gecko", false);
pref("general.useragent.compatMode.firefox", false);
+pref("general.useragent.compatMode.version", "52.9");
+pref("general.useragent.appVersionIsBuildID", true);
// This pref exists only for testing purposes. In order to disable all
// overrides by default, don't initialize UserAgentOverrides.jsm.
@@ -551,7 +554,7 @@ pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,
// includes Mozilla's test domain: mozilla.github.io (not intended for release)
pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com,bluejeans.com,*.bluejeans.com,*.a.bluejeans.com,*.bbcollab.com");
#endif
-// OS/X 10.6 and XP have screen/window sharing off by default due to various issues - Caveat emptor
+// OS/X 10.6 has screen/window sharing off by default due to various issues - Caveat emptor
pref("media.getusermedia.screensharing.allow_on_old_platforms", false);
pref("media.getusermedia.audiocapture.enabled", false);
@@ -2227,11 +2230,12 @@ pref("clipboard.autocopy", false);
pref("clipboard.plainTextOnly", false);
#ifdef XP_WIN
-// Setting false you can disable 4th button and/or 5th button of your mouse.
-// 4th button is typically mapped to "Back" and 5th button is typically mapped
-// to "Forward" button.
-pref("mousebutton.4th.enabled", true);
-pref("mousebutton.5th.enabled", true);
+// Mouse 4th/5th button handling
+// Setting these to false allows you to disable the 4th and/or 5th button of
+// your mouse. The 4th button is typically mapped to "Back" and the 5th
+// button is typically mapped to "Forward".
+pref("mouse.button4.enabled", true);
+pref("mouse.button5.enabled", true);
#endif
// mouse wheel scroll transaction period of time (in milliseconds)
@@ -2720,8 +2724,8 @@ pref("editor.resizing.preserve_ratio", true);
pref("editor.positioning.offset", 0);
pref("dom.use_watchdog", true);
-pref("dom.max_chrome_script_run_time", 20);
-pref("dom.max_script_run_time", 10);
+pref("dom.max_chrome_script_run_time", 90);
+pref("dom.max_script_run_time", 20);
// Stop all scripts in a compartment when the "stop script" dialog is used.
pref("dom.global_stop_script", true);
@@ -3350,14 +3354,6 @@ pref("font.name.sans-serif.x-math", "Arial");
pref("font.name.monospace.x-math", "Courier New");
pref("font.name.cursive.x-math", "Comic Sans MS");
-// cleartype settings - false implies default system settings
-
-// use cleartype rendering for downloadable fonts (win xp only)
-pref("gfx.font_rendering.cleartype.use_for_downloadable_fonts", true);
-
-// use cleartype rendering for all fonts always (win xp only)
-pref("gfx.font_rendering.cleartype.always_use_for_content", false);
-
// ClearType tuning parameters for directwrite/d2d.
//
// Allows overriding of underlying registry values in:
@@ -4350,7 +4346,7 @@ pref("font.name.monospace.x-unicode", "dt-interface user-ucs2.cjk_japan-0");
// Login Manager prefs
pref("signon.rememberSignons", true);
pref("signon.rememberSignons.visibilityToggle", true);
-pref("signon.autofillForms", true);
+pref("signon.autofillForms", false);
pref("signon.autofillForms.http", false);
pref("signon.autologin.proxy", false);
pref("signon.formlessCapture.enabled", true);
@@ -5336,7 +5332,7 @@ pref("browser.search.official", true);
//pref("media.gmp-manager.url.override", "");
// Update service URL for GMP install/updates:
-pref("media.gmp-manager.url", "https://aus5.mozilla.org/update/3/GMP/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
+pref("media.gmp-manager.url", "https://aus5.mozilla.org/update/3/GMP/55.0/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
// When |media.gmp-manager.cert.requireBuiltIn| is true or not specified the
// final certificate and all certificates the connection is redirected to before
diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/build/WindowsDllBlocklist.cpp
index a3c662723..9b63d6673 100644
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -67,7 +67,6 @@ struct DllBlockInfo {
enum {
FLAGS_DEFAULT = 0,
BLOCK_WIN8PLUS_ONLY = 1,
- BLOCK_XP_ONLY = 2,
USE_TIMESTAMP = 4,
} flags;
};
@@ -156,9 +155,6 @@ static DllBlockInfo sWindowsDllBlocklist[] = {
// Topcrash with Conduit SearchProtect, bug 944542
{ "spvc32.dll", ALL_VERSIONS },
- // XP topcrash with F-Secure, bug 970362
- { "fs_ccf_ni_umh32.dll", MAKE_VERSION(1, 42, 101, 0), DllBlockInfo::BLOCK_XP_ONLY },
-
// Topcrash with V-bates, bug 1002748 and bug 1023239
{ "libinject.dll", UNVERSIONED },
{ "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP },
@@ -676,11 +672,6 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
goto continue_loading;
}
- if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) &&
- IsWin2003OrLater()) {
- goto continue_loading;
- }
-
unsigned long long fVersion = ALL_VERSIONS;
if (info->maxVersion != ALL_VERSIONS) {
@@ -749,7 +740,7 @@ continue_loading:
return STATUS_DLL_NOT_FOUND;
}
- if (IsVistaOrLater() && !CheckASLR(full_fname.get())) {
+ if (!CheckASLR(full_fname.get())) {
printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName);
return STATUS_DLL_NOT_FOUND;
}
diff --git a/mozglue/misc/TimeStamp_windows.cpp b/mozglue/misc/TimeStamp_windows.cpp
index cd519affd..683c2209a 100644
--- a/mozglue/misc/TimeStamp_windows.cpp
+++ b/mozglue/misc/TimeStamp_windows.cpp
@@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with
-// values of GetTickCount().
+// values of GetTickCount64().
#include "mozilla/MathAlgorithms.h"
#include "mozilla/TimeStamp.h"
@@ -69,7 +69,7 @@ static const DWORD kDefaultTimeIncrement = 156001;
* further just referred as [mt], meaning milli-ticks.
*
* This is needed to preserve maximum precision of the performance frequency
- * representation. GetTickCount values in milliseconds are multiplied with
+ * representation. GetTickCount64 values in milliseconds are multiplied with
* frequency per second. Therefor we need to multiply QPC value by 1000 to
* have the same units to allow simple arithmentic with both QPC and GTC.
*/
@@ -156,41 +156,8 @@ static CRITICAL_SECTION sTimeStampLock;
// Kept in [mt]
static ULONGLONG sFaultIntoleranceCheckpoint = 0;
-// Used only when GetTickCount64 is not available on the platform.
-// Last result of GetTickCount call.
-//
-// Kept in [ms]
-static DWORD sLastGTCResult = 0;
-
-// Higher part of the 64-bit value of MozGetTickCount64,
-// incremented atomically.
-static DWORD sLastGTCRollover = 0;
-
namespace mozilla {
-typedef ULONGLONG (WINAPI* GetTickCount64_t)();
-static GetTickCount64_t sGetTickCount64 = nullptr;
-
-// Function protecting GetTickCount result from rolling over,
-// result is in [ms]
-static ULONGLONG WINAPI
-MozGetTickCount64()
-{
- DWORD GTC = ::GetTickCount();
-
- // Cheaper then CMPXCHG8B
- AutoCriticalSection lock(&sTimeStampLock);
-
- // Pull the rollover counter forward only if new value of GTC goes way
- // down under the last saved result
- if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30))) {
- ++sLastGTCRollover;
- }
-
- sLastGTCResult = GTC;
- return ULONGLONG(sLastGTCRollover) << 32 | sLastGTCResult;
-}
-
// Result is in [mt]
static inline ULONGLONG
PerformanceCounter()
@@ -355,7 +322,7 @@ TimeStampValue::CheckQPC(const TimeStampValue& aOther) const
if (duration < sHardFailureLimit) {
// Interval between the two time stamps is very short, consider
// QPC as unstable and record a failure.
- uint64_t now = ms2mt(sGetTickCount64());
+ uint64_t now = ms2mt(GetTickCount64());
AutoCriticalSection lock(&sTimeStampLock);
@@ -485,17 +452,6 @@ TimeStamp::Startup()
gInitialized = true;
- // Decide which implementation to use for the high-performance timer.
-
- HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
- sGetTickCount64 = reinterpret_cast<GetTickCount64_t>(
- GetProcAddress(kernelDLL, "GetTickCount64"));
- if (!sGetTickCount64) {
- // If the platform does not support the GetTickCount64 (Windows XP doesn't),
- // then use our fallback implementation based on GetTickCount.
- sGetTickCount64 = MozGetTickCount64;
- }
-
InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
sHasStableTSC = HasStableTSC();
@@ -504,10 +460,10 @@ TimeStamp::Startup()
LARGE_INTEGER freq;
sUseQPC = ::QueryPerformanceFrequency(&freq);
if (!sUseQPC) {
- // No Performance Counter. Fall back to use GetTickCount.
+ // No Performance Counter. Fall back to use GetTickCount64.
InitResolution();
- LOG(("TimeStamp: using GetTickCount"));
+ LOG(("TimeStamp: using GetTickCount64"));
return;
}
@@ -534,7 +490,7 @@ TimeStamp::Now(bool aHighResolution)
// Both values are in [mt] units.
ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0);
- ULONGLONG GTC = ms2mt(sGetTickCount64());
+ ULONGLONG GTC = ms2mt(GetTickCount64());
return TimeStamp(TimeStampValue(GTC, QPC, useQPC));
}
diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
index 216cf559c..42fdea4a1 100644
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -859,6 +859,12 @@ LoadInfo::SetIsPreflight()
mIsPreflight = true;
}
+void
+LoadInfo::SetUpgradeInsecureRequests()
+{
+ mUpgradeInsecureRequests = true;
+}
+
NS_IMETHODIMP
LoadInfo::GetIsPreflight(bool* aIsPreflight)
{
diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h
index 261f85349..3e1b92ff4 100644
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -78,6 +78,7 @@ public:
already_AddRefed<nsILoadInfo> CloneForNewRequest() const;
void SetIsPreflight();
+ void SetUpgradeInsecureRequests();
private:
// private constructor that is only allowed to be called from within
diff --git a/netwerk/base/NetUtil.jsm b/netwerk/base/NetUtil.jsm
index e970c8ad8..93025e97e 100644
--- a/netwerk/base/NetUtil.jsm
+++ b/netwerk/base/NetUtil.jsm
@@ -363,6 +363,41 @@ this.NetUtil = {
},
/**
+ * @deprecated Use newChannel({ ...options... }) instead.
+ */
+ newChannel2: function NetUtil_newChannel2(aWhatToLoad,
+ aOriginCharset,
+ aBaseURI,
+ aLoadingNode,
+ aLoadingPrincipal,
+ aTriggeringPrincipal,
+ aSecurityFlags,
+ aContentPolicyType)
+ {
+ if (!aWhatToLoad) {
+ let exception = new Components.Exception(
+ "Must have a non-null string spec, nsIURI, or nsIFile object",
+ Cr.NS_ERROR_INVALID_ARG,
+ Components.stack.caller
+ );
+ throw exception;
+ }
+
+ let uri = aWhatToLoad;
+ if (!(aWhatToLoad instanceof Ci.nsIURI)) {
+ // We either have a string or an nsIFile that we'll need a URI for.
+ uri = this.newURI(aWhatToLoad, aOriginCharset, aBaseURI);
+ }
+
+ return this.ioService.newChannelFromURI2(uri,
+ aLoadingNode,
+ aLoadingPrincipal,
+ aTriggeringPrincipal,
+ aSecurityFlags,
+ aContentPolicyType);
+ },
+
+ /**
* Reads aCount bytes from aInputStream into a string.
*
* @param aInputStream
diff --git a/netwerk/base/nsICryptoHash.idl b/netwerk/base/nsICryptoHash.idl
index cd865a3a9..ddd3103af 100644
--- a/netwerk/base/nsICryptoHash.idl
+++ b/netwerk/base/nsICryptoHash.idl
@@ -10,7 +10,7 @@ interface nsIInputStream;
* This interface provides crytographic hashing algorithms.
*/
-[scriptable, uuid(1e5b7c43-4688-45ce-92e1-77ed931e3bbe)]
+[scriptable, uuid(0a248513-dfa7-4474-8777-8c452d60dd04)]
interface nsICryptoHash : nsISupports
{
/**
@@ -25,6 +25,7 @@ interface nsICryptoHash : nsISupports
const short SHA256 = 4; /* String value: "sha256" */
const short SHA384 = 5; /* String value: "sha384" */
const short SHA512 = 6; /* String value: "sha512" */
+ const short SHA224 = 7; /* String value: "sha224" */
/**
* Initialize the hashing object. This method may be
diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp
index 1bfd1fc91..184757d33 100644
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -40,7 +40,6 @@
#include "xpcpublic.h"
#if defined(XP_WIN)
-#include "mozilla/WindowsVersion.h"
#include "ShutdownLayer.h"
#endif
@@ -1724,21 +1723,6 @@ nsSocketTransport::OnSocketConnected()
NS_ASSERTION(mFDref == 1, "wrong socket ref count");
SetSocketName(mFD);
mFDconnected = true;
-
-#ifdef XP_WIN
- if (!IsWin2003OrLater()) { // windows xp
- PRSocketOptionData opt;
- opt.option = PR_SockOpt_RecvBufferSize;
- if (PR_GetSocketOption(mFD, &opt) == PR_SUCCESS) {
- SOCKET_LOG(("%p checking rwin on xp originally=%u\n",
- this, opt.value.recv_buffer_size));
- if (opt.value.recv_buffer_size < 65535) {
- opt.value.recv_buffer_size = 65535;
- PR_SetSocketOption(mFD, &opt);
- }
- }
- }
-#endif
}
// Ensure keepalive is configured correctly if previously enabled.
diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp
index d2f20651e..068bf0eca 100644
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -28,10 +28,6 @@
#include "nsIWidget.h"
#include "mozilla/dom/FlyWebService.h"
-#if defined(XP_WIN)
-#include "mozilla/WindowsVersion.h"
-#endif
-
namespace mozilla {
namespace net {
@@ -1204,12 +1200,7 @@ nsSocketTransportService::UpdateSendBufferPref(nsIPrefBranch *pref)
}
#if defined(XP_WIN)
- // If the pref is not set but this is windows set it depending on windows version
- if (!IsWin2003OrLater()) { // windows xp
- mSendBufferSize = 131072;
- } else { // vista or later
- mSendBufferSize = 131072 * 4;
- }
+ mSendBufferSize = 131072 * 4;
#endif
}
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index 0e570e8cb..1c9093495 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -8335,9 +8335,31 @@ nsHttpChannel::ResumeInternal()
LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this));
if (--mSuspendCount == 0 && mCallOnResume) {
- nsresult rv = AsyncCall(mCallOnResume);
+ // Resume the interrupted procedure first, then resume
+ // the pump to continue process the input stream.
+ RefPtr<nsRunnableMethod<nsHttpChannel>> callOnResume=
+ NewRunnableMethod(this, mCallOnResume);
+ // Should not resume pump that created after resumption.
+ RefPtr<nsInputStreamPump> transactionPump = mTransactionPump;
+ RefPtr<nsInputStreamPump> cachePump = mCachePump;
+
+ nsresult rv =
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ [callOnResume, transactionPump, cachePump]() {
+ callOnResume->Run();
+
+ if (transactionPump) {
+ transactionPump->Resume();
+ }
+
+ if (cachePump) {
+ cachePump->Resume();
+ }
+ })
+ );
mCallOnResume = nullptr;
NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
}
nsresult rvTransaction = NS_OK;
diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
index 1ddffabff..67e29a029 100644
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -199,8 +199,9 @@ nsHttpHandler::nsHttpHandler()
, mSessionStartTime(0)
, mLegacyAppName("Mozilla")
, mLegacyAppVersion("5.0")
- , mProduct("Gecko")
+ , mProduct("Goanna")
, mCompatFirefoxEnabled(false)
+ , mCompatFirefoxVersion("52.9")
, mUserAgentIsDirty(true)
, mPromptTempRedirect(true)
, mEnablePersistentHttpsCaching(false)
@@ -316,9 +317,13 @@ nsHttpHandler::Init()
nsHttpChannelAuthProvider::InitializePrefs();
- mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);
+ // rv: should have the Firefox/Gecko compatversion for web compatibility
+ mMisc.AssignLiteral("rv:");
+ mMisc += mCompatFirefoxVersion;
- mCompatFirefox.AssignLiteral("Firefox/" MOZILLA_UAVERSION);
+ mCompatGecko.AssignLiteral("Gecko/20100101");
+ mCompatFirefox.AssignLiteral("Firefox/");
+ mCompatFirefox += mCompatFirefoxVersion;
nsCOMPtr<nsIXULAppInfo> appInfo =
do_GetService("@mozilla.org/xre/app-info;1");
@@ -330,12 +335,30 @@ nsHttpHandler::Init()
if (mAppName.Length() == 0) {
appInfo->GetName(mAppName);
}
- appInfo->GetVersion(mAppVersion);
mAppName.StripChars(R"( ()<>@,;:\"/[]?={})");
+ }
+
+ nsCString dynamicBuildID;
+ if (appInfo) {
+ appInfo->GetPlatformBuildID(dynamicBuildID);
+ if (dynamicBuildID.Length() > 8 )
+ dynamicBuildID.Left(dynamicBuildID, 8);
+ }
+
+ if (mAppVersionIsBuildID) {
+ // Override BuildID
+ mAppVersion.AssignLiteral(MOZ_UA_BUILDID);
+ } else if (appInfo) {
+ appInfo->GetVersion(mAppVersion);
} else {
- mAppVersion.AssignLiteral(MOZ_APP_UA_VERSION);
+ // Fall back to platform if appInfo is unavailable
+ mAppVersion.AssignLiteral(MOZILLA_UAVERSION);
}
+ // If there's no override set, set it to the dynamic BuildID
+ if (mAppVersion.IsEmpty())
+ mAppVersion.Assign(dynamicBuildID);
+
mSessionStartTime = NowInSeconds();
mHandlerActive = true;
@@ -351,11 +374,11 @@ nsHttpHandler::Init()
mRequestContextService =
do_GetService("@mozilla.org/network/request-context-service;1");
-#if defined(ANDROID) || defined(MOZ_MULET)
+ // Goanna slice version
mProductSub.AssignLiteral(MOZILLA_UAVERSION);
-#else
- mProductSub.AssignLiteral("20100101");
-#endif
+
+ if (mProductSub.IsEmpty())
+ mProductSub.Assign(dynamicBuildID);
#if DEBUG
// dump user agent prefs
@@ -369,6 +392,7 @@ nsHttpHandler::Init()
LOG(("> app-name = %s\n", mAppName.get()));
LOG(("> app-version = %s\n", mAppVersion.get()));
LOG(("> compat-firefox = %s\n", mCompatFirefox.get()));
+ LOG(("> compat-gecko = %s\n", mCompatGecko.get()));
LOG(("> user-agent = %s\n", UserAgent().get()));
#endif
@@ -678,9 +702,10 @@ nsHttpHandler::BuildUserAgent()
mAppName.Length() +
mAppVersion.Length() +
mCompatFirefox.Length() +
+ mCompatGecko.Length() +
mCompatDevice.Length() +
mDeviceModelId.Length() +
- 13);
+ 14);
// Application portion
mUserAgent.Assign(mLegacyAppName);
@@ -710,6 +735,12 @@ nsHttpHandler::BuildUserAgent()
}
mUserAgent += mMisc;
mUserAgent += ')';
+
+ if(mCompatGeckoEnabled) {
+ // Provide frozen Gecko/20100101 slice
+ mUserAgent += ' ';
+ mUserAgent += mCompatGecko;
+ }
// Product portion
mUserAgent += ' ';
@@ -719,7 +750,7 @@ nsHttpHandler::BuildUserAgent()
bool isFirefox = mAppName.EqualsLiteral("Firefox");
if (isFirefox || mCompatFirefoxEnabled) {
- // "Firefox/x.y" (compatibility) app token
+ // Provide "Firefox/x.y" (compatibility) app token
mUserAgent += ' ';
mUserAgent += mCompatFirefox;
}
@@ -966,16 +997,44 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
bool cVar = false;
+ if (PREF_CHANGED(UA_PREF("appVersionIsBuildID"))) {
+ rv = prefs->GetBoolPref(UA_PREF("appVersionIsBuildID"), &cVar);
+ mAppVersionIsBuildID = (NS_SUCCEEDED(rv) && cVar);
+ mUserAgentIsDirty = true;
+ }
+
+ if (PREF_CHANGED(UA_PREF("compatMode.gecko"))) {
+ rv = prefs->GetBoolPref(UA_PREF("compatMode.gecko"), &cVar);
+ mCompatGeckoEnabled = (NS_SUCCEEDED(rv) && cVar);
+ mUserAgentIsDirty = true;
+ }
+
if (PREF_CHANGED(UA_PREF("compatMode.firefox"))) {
rv = prefs->GetBoolPref(UA_PREF("compatMode.firefox"), &cVar);
mCompatFirefoxEnabled = (NS_SUCCEEDED(rv) && cVar);
mUserAgentIsDirty = true;
}
+ // general.useragent.compatMode.version
+ // This is the version number used in rv: for Gecko compatibility
+ // and in the Firefox/nn.nn slice when compatMode.firefox is enabled.
+ if (PREF_CHANGED(UA_PREF("compatMode.version"))) {
+ prefs->GetCharPref(UA_PREF("compatMode.version"),
+ getter_Copies(mCompatFirefoxVersion));
+
+ // rebuild mMisc and compatMode slice
+ mMisc.AssignLiteral("rv:");
+ mMisc += mCompatFirefoxVersion;
+ mCompatFirefox.AssignLiteral("Firefox/");
+ mCompatFirefox += mCompatFirefoxVersion;
+
+ mUserAgentIsDirty = true;
+ }
+
// general.useragent.override
if (PREF_CHANGED(UA_PREF("override"))) {
prefs->GetCharPref(UA_PREF("override"),
- getter_Copies(mUserAgentOverride));
+ getter_Copies(mUserAgentOverride));
mUserAgentIsDirty = true;
}
diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h
index 13cc72e8e..d51662db9 100644
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -485,7 +485,11 @@ private:
nsXPIDLCString mProductSub;
nsXPIDLCString mAppName;
nsXPIDLCString mAppVersion;
+ bool mAppVersionIsBuildID;
+ nsCString mCompatGecko;
+ bool mCompatGeckoEnabled;
nsCString mCompatFirefox;
+ nsCString mCompatFirefoxVersion;
bool mCompatFirefoxEnabled;
nsXPIDLCString mCompatDevice;
nsCString mDeviceModelId;
diff --git a/netwerk/test/TestUDPSocket.cpp b/netwerk/test/TestUDPSocket.cpp
index 9236d2ea3..0ec43a650 100644
--- a/netwerk/test/TestUDPSocket.cpp
+++ b/netwerk/test/TestUDPSocket.cpp
@@ -13,9 +13,6 @@
#include "nsIScriptSecurityManager.h"
#include "nsITimer.h"
#include "mozilla/net/DNS.h"
-#ifdef XP_WIN
-#include "mozilla/WindowsVersion.h"
-#endif
#include "prerror.h"
#define REQUEST 0x68656c6f
@@ -334,16 +331,6 @@ main(int32_t argc, char *argv[])
}
RefPtr<MulticastTimerCallback> timerCb = new MulticastTimerCallback();
- // The following multicast tests using multiple sockets require a firewall
- // exception on Windows XP (the earliest version of Windows we now support)
- // before they pass. For now, we'll skip them here. Later versions of Windows
- // (Win2003 and onward) don't seem to have this issue.
-#ifdef XP_WIN
- if (!mozilla::IsWin2003OrLater()) { // i.e. if it is WinXP
- goto close;
- }
-#endif
-
// Join multicast group
printf("Joining multicast group\n");
phase = TEST_MULTICAST;
diff --git a/netwerk/test/mochitests/mochitest.ini b/netwerk/test/mochitests/mochitest.ini
index 98d406c80..f8a919031 100644
--- a/netwerk/test/mochitests/mochitest.ini
+++ b/netwerk/test/mochitests/mochitest.ini
@@ -24,3 +24,4 @@ support-files =
[test_user_agent_updates_reset.html]
[test_viewsource_unlinkable.html]
[test_xhr_method_case.html]
+[test_1396395.html]
diff --git a/netwerk/test/mochitests/test_1396395.html b/netwerk/test/mochitests/test_1396395.html
new file mode 100644
index 000000000..193ef219c
--- /dev/null
+++ b/netwerk/test/mochitests/test_1396395.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: -->
+<html>
+ <!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+ <iframe id="f" src="http://example.com"></iframe>
+ <script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var script = SpecialPowers.loadChromeScript(() => {
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+ Cu.import('resource://gre/modules/Services.jsm');
+
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("http://example.org")) {
+ return;
+ }
+ Services.obs.removeObserver(onExamResp, 'http-on-examine-response');
+ channel.suspend();
+ Promise.resolve().then(() => {
+ channel.resume();
+ });
+ }, 'http-on-examine-response');
+
+ sendAsyncMessage('start-test');
+});
+
+script.addMessageListener('start-test', () => {
+ const iframe = document.getElementById('f');
+
+ iframe.contentWindow.onunload = function () {
+ xhr = new XMLHttpRequest();
+ xhr.open('GET', window.location, false);
+ xhr.send(null);
+ ok(true, 'complete without crash');
+ script.destroy();
+ SimpleTest.finish();
+ }
+
+ iframe.src = 'http://example.org';
+});
+ </script>
+</body>
+</html>
+
diff --git a/netwerk/wifi/moz.build b/netwerk/wifi/moz.build
index b6ddd2139..e3edb0842 100644
--- a/netwerk/wifi/moz.build
+++ b/netwerk/wifi/moz.build
@@ -45,7 +45,6 @@ elif CONFIG['OS_ARCH'] == 'WINNT':
'nsWifiScannerWin.cpp',
'win_wifiScanner.cpp',
'win_wlanLibrary.cpp',
- 'win_xp_wifiScanner.cpp'
]
elif CONFIG['OS_ARCH'] == 'SunOS':
CXXFLAGS += CONFIG['GLIB_CFLAGS']
diff --git a/netwerk/wifi/nsWifiMonitor.h b/netwerk/wifi/nsWifiMonitor.h
index 3783d38bd..665798efc 100644
--- a/netwerk/wifi/nsWifiMonitor.h
+++ b/netwerk/wifi/nsWifiMonitor.h
@@ -76,7 +76,7 @@ class nsWifiMonitor final : nsIRunnable, nsIWifiMonitor, nsIObserver
mozilla::ReentrantMonitor mReentrantMonitor;
#ifdef XP_WIN
- nsAutoPtr<WindowsWifiScannerInterface> mWinWifiScanner;
+ nsAutoPtr<WinWifiScanner> mWinWifiScanner;
#endif
};
#else
diff --git a/netwerk/wifi/nsWifiScannerWin.cpp b/netwerk/wifi/nsWifiScannerWin.cpp
index ef18706e4..6089c45c6 100644
--- a/netwerk/wifi/nsWifiScannerWin.cpp
+++ b/netwerk/wifi/nsWifiScannerWin.cpp
@@ -12,8 +12,6 @@
#include "nsServiceManagerUtils.h"
#include "nsWifiAccessPoint.h"
#include "win_wifiScanner.h"
-#include "win_xp_wifiScanner.h"
-#include "mozilla/WindowsVersion.h"
using namespace mozilla;
@@ -31,14 +29,7 @@ nsresult
nsWifiMonitor::DoScan()
{
if (!mWinWifiScanner) {
- if (IsWin2003OrLater()) {
- mWinWifiScanner = new WinWifiScanner();
- LOG(("Using Windows 2003+ wifi scanner."));
- } else {
- mWinWifiScanner = new WinXPWifiScanner();
- LOG(("Using Windows XP wifi scanner."));
- }
-
+ mWinWifiScanner = new WinWifiScanner();
if (!mWinWifiScanner) {
// TODO: Probably return OOM error
return NS_ERROR_FAILURE;
diff --git a/netwerk/wifi/win_wifiScanner.h b/netwerk/wifi/win_wifiScanner.h
index b43c23899..79b1bf132 100644
--- a/netwerk/wifi/win_wifiScanner.h
+++ b/netwerk/wifi/win_wifiScanner.h
@@ -11,19 +11,10 @@
class nsWifiAccessPoint;
-// This class allows the wifi monitor to use WinWifiScanner and WinXPWifiScanner interchangeably.
-class WindowsWifiScannerInterface {
-public:
- virtual ~WindowsWifiScannerInterface() {}
-
- virtual nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints) = 0;
-};
-
-
-class WinWifiScanner : public WindowsWifiScannerInterface {
+class WinWifiScanner final {
public:
WinWifiScanner();
- virtual ~WinWifiScanner();
+ ~WinWifiScanner();
/**
* GetAccessPointsFromWLAN
diff --git a/netwerk/wifi/win_xp_wifiScanner.cpp b/netwerk/wifi/win_xp_wifiScanner.cpp
deleted file mode 100644
index 4dcd34034..000000000
--- a/netwerk/wifi/win_xp_wifiScanner.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Windows Vista uses the Native Wifi (WLAN) API for accessing WiFi cards. See
-// http://msdn.microsoft.com/en-us/library/ms705945(VS.85).aspx. Windows XP
-// Service Pack 3 (and Windows XP Service Pack 2, if upgraded with a hot fix)
-// also support a limited version of the WLAN API. See
-// http://msdn.microsoft.com/en-us/library/bb204766.aspx. The WLAN API uses
-// wlanapi.h, which is not part of the SDK used by Gears, so is replicated
-// locally using data from the MSDN.
-//\
-// Windows XP from Service Pack 2 onwards supports the Wireless Zero
-// Configuration (WZC) programming interface. See
-// http://msdn.microsoft.com/en-us/library/ms706587(VS.85).aspx.
-//
-// The MSDN recommends that one use the WLAN API where available, and WZC
-// otherwise.
-//
-// However, it seems that WZC fails for some wireless cards. Also, WLAN seems
-// not to work on XP SP3. So we use WLAN on Vista, and use NDIS directly
-// otherwise.
-
-// MOZILLA NOTE:
-// This code is ported from chromium:
-// https://chromium.googlesource.com/chromium/src/+/master/content/browser/geolocation/wifi_data_provider_win.cc
-// Based on changeset 42c5878
-
-#include "win_xp_wifiScanner.h"
-#include "nsWifiAccessPoint.h"
-#include <windows.h>
-#include <winioctl.h>
-#include <wlanapi.h>
-#include <string>
-#include <vector>
-
-// Taken from ndis.h for WinCE.
-#define NDIS_STATUS_INVALID_LENGTH ((NDIS_STATUS)0xC0010014L)
-#define NDIS_STATUS_BUFFER_TOO_SHORT ((NDIS_STATUS)0xC0010016L)
-
-namespace {
-// The limits on the size of the buffer used for the OID query.
-const int kInitialBufferSize = 2 << 12; // Good for about 50 APs.
-const int kMaximumBufferSize = 2 << 20; // 2MB
-
-// Length for generic string buffers passed to Win32 APIs.
-const int kStringLength = 512;
-
-// WlanOpenHandle
-typedef DWORD (WINAPI* WlanOpenHandleFunction)(DWORD dwClientVersion,
- PVOID pReserved,
- PDWORD pdwNegotiatedVersion,
- PHANDLE phClientHandle);
-
-// WlanEnumInterfaces
-typedef DWORD (WINAPI* WlanEnumInterfacesFunction)(
- HANDLE hClientHandle,
- PVOID pReserved,
- PWLAN_INTERFACE_INFO_LIST* ppInterfaceList);
-
-// WlanGetNetworkBssList
-typedef DWORD (WINAPI* WlanGetNetworkBssListFunction)(
- HANDLE hClientHandle,
- const GUID* pInterfaceGuid,
- const PDOT11_SSID pDot11Ssid,
- DOT11_BSS_TYPE dot11BssType,
- BOOL bSecurityEnabled,
- PVOID pReserved,
- PWLAN_BSS_LIST* ppWlanBssList
-);
-
-// WlanFreeMemory
-typedef VOID (WINAPI* WlanFreeMemoryFunction)(PVOID pMemory);
-
-// WlanCloseHandle
-typedef DWORD (WINAPI* WlanCloseHandleFunction)(HANDLE hClientHandle,
- PVOID pReserved);
-
-// Extracts data for an access point and converts to Gears format.
-bool UndefineDosDevice(const std::string& device_name);
-bool DefineDosDeviceIfNotExists(const std::string& device_name);
-HANDLE GetFileHandle(const std::string& device_name);
-// Makes the OID query and returns a Win32 error code.
-int PerformQuery(HANDLE adapter_handle, std::vector<char>& buffer, DWORD* bytes_out);
-bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer);
-// Gets the system directory and appends a trailing slash if not already
-// present.
-bool GetSystemDirectory(std::string* path);
-
-bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data);
-int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list,
- int list_size,
- nsCOMArray<nsWifiAccessPoint>& outData);
-} // namespace
-
-class WindowsNdisApi
-{
-public:
- virtual ~WindowsNdisApi();
- static WindowsNdisApi* Create();
- virtual bool GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData);
-
-private:
- static bool GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out);
- // Swaps in content of the vector passed
- explicit WindowsNdisApi(std::vector<std::string>* interface_service_names);
- bool GetInterfaceDataNDIS(HANDLE adapter_handle, nsCOMArray<nsWifiAccessPoint>& outData);
- // NDIS variables.
- std::vector<std::string> interface_service_names_;
- std::vector<char> _buffer;
-};
-
-// WindowsNdisApi
-WindowsNdisApi::WindowsNdisApi(
- std::vector<std::string>* interface_service_names)
- : _buffer(kInitialBufferSize) {
- interface_service_names_.swap(*interface_service_names);
-}
-
-WindowsNdisApi::~WindowsNdisApi() {
-}
-
-WindowsNdisApi* WindowsNdisApi::Create() {
- std::vector<std::string> interface_service_names;
- if (GetInterfacesNDIS(interface_service_names)) {
- return new WindowsNdisApi(&interface_service_names);
- }
- return NULL;
-}
-
-bool WindowsNdisApi::GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData) {
- int interfaces_failed = 0;
- int interfaces_succeeded = 0;
-
- for (int i = 0; i < static_cast<int>(interface_service_names_.size()); ++i) {
- // First, check that we have a DOS device for this adapter.
- if (!DefineDosDeviceIfNotExists(interface_service_names_[i])) {
- continue;
- }
-
- // Get the handle to the device. This will fail if the named device is not
- // valid.
- HANDLE adapter_handle = GetFileHandle(interface_service_names_[i]);
- if (adapter_handle == INVALID_HANDLE_VALUE) {
- continue;
- }
-
- // Get the data.
- if (GetInterfaceDataNDIS(adapter_handle, outData)) {
- ++interfaces_succeeded;
- } else {
- ++interfaces_failed;
- }
-
- // Clean up.
- CloseHandle(adapter_handle);
- UndefineDosDevice(interface_service_names_[i]);
- }
-
- // Return true if at least one interface succeeded, or at the very least none
- // failed.
- return interfaces_succeeded > 0 || interfaces_failed == 0;
-}
-
-bool WindowsNdisApi::GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out) {
- HKEY network_cards_key = NULL;
- if (RegOpenKeyEx(
- HKEY_LOCAL_MACHINE,
- "Software\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards",
- 0,
- KEY_READ,
- &network_cards_key) != ERROR_SUCCESS) {
- return false;
- }
- if (!network_cards_key) {
- return false;
- }
-
- for (int i = 0; ; ++i) {
- TCHAR name[kStringLength];
- DWORD name_size = kStringLength;
- FILETIME time;
- if (RegEnumKeyEx(network_cards_key,
- i,
- name,
- &name_size,
- NULL,
- NULL,
- NULL,
- &time) != ERROR_SUCCESS) {
- break;
- }
- HKEY hardware_key = NULL;
- if (RegOpenKeyEx(network_cards_key, name, 0, KEY_READ, &hardware_key) !=
- ERROR_SUCCESS) {
- break;
- }
- if (!hardware_key) {
- return false;
- }
-
- TCHAR service_name[kStringLength];
- DWORD service_name_size = kStringLength;
- DWORD type = 0;
- if (RegQueryValueEx(hardware_key,
- "ServiceName",
- NULL,
- &type,
- reinterpret_cast<LPBYTE>(service_name),
- &service_name_size) == ERROR_SUCCESS) {
- interface_service_names_out.push_back(service_name);
- }
- RegCloseKey(hardware_key);
- }
-
- RegCloseKey(network_cards_key);
- return true;
-}
-
-bool WindowsNdisApi::GetInterfaceDataNDIS(HANDLE adapter_handle,
- nsCOMArray<nsWifiAccessPoint>& outData) {
- DWORD bytes_out;
- int result;
-
- while (true) {
- bytes_out = 0;
- result = PerformQuery(adapter_handle, _buffer, &bytes_out);
- if (result == ERROR_GEN_FAILURE || // Returned by some Intel cards.
- result == ERROR_INSUFFICIENT_BUFFER ||
- result == ERROR_MORE_DATA ||
- result == NDIS_STATUS_INVALID_LENGTH ||
- result == NDIS_STATUS_BUFFER_TOO_SHORT) {
- // The buffer we supplied is too small, so increase it. bytes_out should
- // provide the required buffer size, but this is not always the case.
- size_t newSize;
- if (bytes_out > static_cast<DWORD>(_buffer.size())) {
- newSize = bytes_out;
- } else {
- newSize = _buffer.size() * 2;
- }
- if (!ResizeBuffer(newSize, _buffer)) {
- return false;
- }
- } else {
- // The buffer is not too small.
- break;
- }
- }
-
- if (result == ERROR_SUCCESS) {
- NDIS_802_11_BSSID_LIST* bssid_list =
- reinterpret_cast<NDIS_802_11_BSSID_LIST*>(&_buffer[0]);
- GetDataFromBssIdList(*bssid_list, _buffer.size(), outData);
- }
-
- return true;
-}
-
-namespace {
-#define uint8 unsigned char
-
-bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data)
-{
- access_point_data->setMac(data.MacAddress);
- access_point_data->setSignal(data.Rssi);
- // Note that _NDIS_802_11_SSID::Ssid::Ssid is not null-terminated.
- const unsigned char* ssid = data.Ssid.Ssid;
- size_t len = data.Ssid.SsidLength;
- access_point_data->setSSID(reinterpret_cast<const char*>(ssid), len);
- return true;
-}
-
-int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list,
- int list_size,
- nsCOMArray<nsWifiAccessPoint>& outData)
-{
- // Walk through the BSS IDs.
- int found = 0;
- const uint8* iterator = reinterpret_cast<const uint8*>(&bss_id_list.Bssid[0]);
- const uint8* end_of_buffer =
- reinterpret_cast<const uint8*>(&bss_id_list) + list_size;
- for (int i = 0; i < static_cast<int>(bss_id_list.NumberOfItems); ++i) {
- const NDIS_WLAN_BSSID *bss_id =
- reinterpret_cast<const NDIS_WLAN_BSSID*>(iterator);
- // Check that the length of this BSS ID is reasonable.
- if (bss_id->Length < sizeof(NDIS_WLAN_BSSID) ||
- iterator + bss_id->Length > end_of_buffer) {
- break;
- }
- nsWifiAccessPoint* ap = new nsWifiAccessPoint();
- if (ConvertToAccessPointData(*bss_id, ap)) {
- outData.AppendObject(ap);
- ++found;
- }
- // Move to the next BSS ID.
- iterator += bss_id->Length;
- }
- return found;
-}
-
-
-bool UndefineDosDevice(const std::string& device_name) {
- // We remove only the mapping we use, that is \Device\<device_name>.
- std::string target_path = "\\Device\\" + device_name;
- return DefineDosDevice(
- DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
- device_name.c_str(),
- target_path.c_str()) == TRUE;
-}
-
-bool DefineDosDeviceIfNotExists(const std::string& device_name) {
- // We create a DOS device name for the device at \Device\<device_name>.
- std::string target_path = "\\Device\\" + device_name;
-
- TCHAR target[kStringLength];
- if (QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 &&
- target_path.compare(target) == 0) {
- // Device already exists.
- return true;
- }
-
- if (GetLastError() != ERROR_FILE_NOT_FOUND) {
- return false;
- }
-
- if (!DefineDosDevice(DDD_RAW_TARGET_PATH,
- device_name.c_str(),
- target_path.c_str())) {
- return false;
- }
-
- // Check that the device is really there.
- return QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 &&
- target_path.compare(target) == 0;
-}
-
-HANDLE GetFileHandle(const std::string& device_name) {
- // We access a device with DOS path \Device\<device_name> at
- // \\.\<device_name>.
- std::string formatted_device_name = "\\\\.\\" + device_name;
-
- return CreateFile(formatted_device_name.c_str(),
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
- 0, // security attributes
- OPEN_EXISTING,
- 0, // flags and attributes
- INVALID_HANDLE_VALUE);
-}
-
-int PerformQuery(HANDLE adapter_handle,
- std::vector<char>& buffer,
- DWORD* bytes_out) {
- DWORD oid = OID_802_11_BSSID_LIST;
- if (!DeviceIoControl(adapter_handle,
- IOCTL_NDIS_QUERY_GLOBAL_STATS,
- &oid,
- sizeof(oid),
- &buffer[0],
- buffer.size(),
- bytes_out,
- NULL)) {
- return GetLastError();
- }
- return ERROR_SUCCESS;
-}
-
-bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer) {
- if (requested_size > kMaximumBufferSize) {
- buffer.resize(kInitialBufferSize);
- return false;
- }
-
- buffer.resize(requested_size);
- return true;
-}
-
-} // namespace
-
-
-nsresult
-WinXPWifiScanner::GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints)
-{
- if (!mImplementation) {
- mImplementation = WindowsNdisApi::Create();
- if (!mImplementation) {
- return NS_ERROR_FAILURE;
- }
- }
-
- accessPoints.Clear();
- bool isOk = mImplementation->GetAccessPointData(accessPoints);
- if (!isOk) {
- mImplementation = 0;
- return NS_ERROR_FAILURE;
- }
-
- return NS_OK;
-}
diff --git a/netwerk/wifi/win_xp_wifiScanner.h b/netwerk/wifi/win_xp_wifiScanner.h
deleted file mode 100644
index 33ae4cae4..000000000
--- a/netwerk/wifi/win_xp_wifiScanner.h
+++ /dev/null
@@ -1,25 +0,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/. */
-
-#ifndef WINXPWIFISCANNER_H_
-#define WINXPWIFISCANNER_H_
-
-#include "nsAutoPtr.h"
-#include "nsCOMArray.h"
-#include "win_wifiScanner.h"
-
-class nsWifiAccessPoint;
-class WindowsNdisApi;
-
-// This class is wrapper into the Chromium WindowNdisApi class for scanning wifis
-// on Windows XP. When Firefox drops XP support, this code can go.
-class WinXPWifiScanner : public WindowsWifiScannerInterface {
-public:
- nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints);
- virtual ~WinXPWifiScanner() {}
-private:
- nsAutoPtr<WindowsNdisApi> mImplementation;
-};
-
-#endif \ No newline at end of file
diff --git a/nsprpub/TAG-INFO b/nsprpub/TAG-INFO
deleted file mode 100644
index ebe7269ae..000000000
--- a/nsprpub/TAG-INFO
+++ /dev/null
@@ -1 +0,0 @@
-NSPR_4_16_RTM
diff --git a/nsprpub/configure b/nsprpub/configure
index b8ad2b4eb..19e9fa60f 100755
--- a/nsprpub/configure
+++ b/nsprpub/configure
@@ -2488,7 +2488,7 @@ test -n "$target_alias" &&
program_prefix=${target_alias}-
MOD_MAJOR_VERSION=4
-MOD_MINOR_VERSION=16
+MOD_MINOR_VERSION=18
MOD_PATCH_VERSION=0
NSPR_MODNAME=nspr20
_HAVE_PTHREADS=
@@ -6560,11 +6560,13 @@ fi
$as_echo "#define HAVE_POINTER_LOCALTIME_R 1" >>confdefs.h
+
HOST_DARWIN_MAJOR=`echo "$build_os" | sed -E -e 's/^darwin([0-9]+).*$/\1/'`
+
if test "$HOST_DARWIN_MAJOR" -ge 15 ; then
$as_echo "#define HAS_CONNECTX 1" >>confdefs.h
- fi
+ fi
AS='$(CC) -x assembler-with-cpp'
CFLAGS="$CFLAGS -Wall -fno-common"
case "${target_cpu}" in
@@ -8472,19 +8474,12 @@ case "$target" in
fi
if test "$USE_PTHREADS"; then
- if echo "$OS_RELEASE" | egrep '^(B.10.10|B.10.20)' >/dev/null; then
- $as_echo "#define _REENTRANT 1" >>confdefs.h
-
- $as_echo "#define _PR_DCETHREADS 1" >>confdefs.h
-
- else
- cat >>confdefs.h <<_ACEOF
+ cat >>confdefs.h <<_ACEOF
#define _POSIX_C_SOURCE 199506L
_ACEOF
- $as_echo "#define _PR_HAVE_THREADSAFE_GETHOST 1" >>confdefs.h
+ $as_echo "#define _PR_HAVE_THREADSAFE_GETHOST 1" >>confdefs.h
- fi
fi
if test "$USE_USER_PTHREADS"; then
cat >>confdefs.h <<_ACEOF
diff --git a/nsprpub/configure.in b/nsprpub/configure.in
index 22b4e7224..7b0b8b395 100644
--- a/nsprpub/configure.in
+++ b/nsprpub/configure.in
@@ -15,7 +15,7 @@ dnl ========================================================
dnl = Defaults
dnl ========================================================
MOD_MAJOR_VERSION=4
-MOD_MINOR_VERSION=16
+MOD_MINOR_VERSION=18
MOD_PATCH_VERSION=0
NSPR_MODNAME=nspr20
_HAVE_PTHREADS=
@@ -2889,13 +2889,8 @@ case "$target" in
AC_DEFINE(_PR_LOCAL_THREADS_ONLY)
fi
if test "$USE_PTHREADS"; then
- if echo "$OS_RELEASE" | egrep '^(B.10.10|B.10.20)' >/dev/null; then
- AC_DEFINE(_REENTRANT)
- AC_DEFINE(_PR_DCETHREADS)
- else
- AC_DEFINE_UNQUOTED(_POSIX_C_SOURCE,199506L)
- AC_DEFINE(_PR_HAVE_THREADSAFE_GETHOST)
- fi
+ AC_DEFINE_UNQUOTED(_POSIX_C_SOURCE,199506L)
+ AC_DEFINE(_PR_HAVE_THREADSAFE_GETHOST)
fi
if test "$USE_USER_PTHREADS"; then
AC_DEFINE_UNQUOTED(_POSIX_C_SOURCE,199506L)
diff --git a/nsprpub/pr/include/md/_pth.h b/nsprpub/pr/include/md/_pth.h
index eeeef0438..5603223a0 100644
--- a/nsprpub/pr/include/md/_pth.h
+++ b/nsprpub/pr/include/md/_pth.h
@@ -14,29 +14,7 @@
#define _PR_MD_DISABLE_CLOCK_INTERRUPTS()
#define _PR_MD_ENABLE_CLOCK_INTERRUPTS()
-/* In good standards fashion, the DCE threads (based on posix-4) are not
- * quite the same as newer posix implementations. These are mostly name
- * changes and small differences, so macros usually do the trick
- */
-#ifdef _PR_DCETHREADS
-#define _PT_PTHREAD_MUTEXATTR_INIT pthread_mutexattr_create
-#define _PT_PTHREAD_MUTEXATTR_DESTROY pthread_mutexattr_delete
-#define _PT_PTHREAD_MUTEX_INIT(m, a) pthread_mutex_init(&(m), a)
-#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) (0 == pthread_mutex_trylock(&(m)))
-#define _PT_PTHREAD_CONDATTR_INIT pthread_condattr_create
-#define _PT_PTHREAD_COND_INIT(m, a) pthread_cond_init(&(m), a)
-#define _PT_PTHREAD_CONDATTR_DESTROY pthread_condattr_delete
-
-/* Notes about differences between DCE threads and pthreads 10:
- * 1. pthread_mutex_trylock returns 1 when it locks the mutex
- * 0 when it does not. The latest pthreads has a set of errno-like
- * return values.
- * 2. return values from pthread_cond_timedwait are different.
- *
- *
- *
- */
-#elif defined(BSDI)
+#if defined(BSDI)
/*
* Mutex and condition attributes are not supported. The attr
* argument to pthread_mutex_init() and pthread_cond_init() must
@@ -106,13 +84,7 @@
* PR_EnterMonitor calls any of these functions, infinite
* recursion ensues.
*/
-#if defined(_PR_DCETHREADS)
-#define _PT_PTHREAD_INVALIDATE_THR_HANDLE(t) \
- memset(&(t), 0, sizeof(pthread_t))
-#define _PT_PTHREAD_THR_HANDLE_IS_INVALID(t) \
- (!memcmp(&(t), &pt_zero_tid, sizeof(pthread_t)))
-#define _PT_PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st)
-#elif defined(IRIX) || defined(OSF1) || defined(AIX) || defined(SOLARIS) \
+#if defined(IRIX) || defined(OSF1) || defined(AIX) || defined(SOLARIS) \
|| defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
|| defined(HPUX) || defined(FREEBSD) \
|| defined(NETBSD) || defined(OPENBSD) || defined(BSDI) \
@@ -125,17 +97,7 @@
#error "pthreads is not supported for this architecture"
#endif
-#if defined(_PR_DCETHREADS)
-#define _PT_PTHREAD_ATTR_INIT pthread_attr_create
-#define _PT_PTHREAD_ATTR_DESTROY pthread_attr_delete
-#define _PT_PTHREAD_CREATE(t, a, f, r) pthread_create(t, a, f, r)
-#define _PT_PTHREAD_KEY_CREATE pthread_keycreate
-#define _PT_PTHREAD_ATTR_SETSCHEDPOLICY pthread_attr_setsched
-#define _PT_PTHREAD_ATTR_GETSTACKSIZE(a, s) \
- (*(s) = pthread_attr_getstacksize(*(a)), 0)
-#define _PT_PTHREAD_GETSPECIFIC(k, r) \
- pthread_getspecific((k), (pthread_addr_t *) &(r))
-#elif defined(_PR_PTHREADS)
+#if defined(_PR_PTHREADS)
#define _PT_PTHREAD_ATTR_INIT pthread_attr_init
#define _PT_PTHREAD_ATTR_DESTROY pthread_attr_destroy
#define _PT_PTHREAD_CREATE(t, a, f, r) pthread_create(t, &a, f, r)
@@ -147,22 +109,6 @@
#error "Cannot determine pthread strategy"
#endif
-#if defined(_PR_DCETHREADS)
-#define _PT_PTHREAD_EXPLICIT_SCHED _PT_PTHREAD_DEFAULT_SCHED
-#endif
-
-/*
- * pthread_mutex_trylock returns different values in DCE threads and
- * pthreads.
- */
-#if defined(_PR_DCETHREADS)
-#define PT_TRYLOCK_SUCCESS 1
-#define PT_TRYLOCK_BUSY 0
-#else
-#define PT_TRYLOCK_SUCCESS 0
-#define PT_TRYLOCK_BUSY EBUSY
-#endif
-
/*
* These platforms don't have sigtimedwait()
*/
@@ -190,16 +136,9 @@
#define PT_PRIO_MIN DEFAULT_PRIO
#define PT_PRIO_MAX DEFAULT_PRIO
#elif defined(HPUX)
-
-#if defined(_PR_DCETHREADS)
-#define PT_PRIO_MIN PRI_OTHER_MIN
-#define PT_PRIO_MAX PRI_OTHER_MAX
-#else /* defined(_PR_DCETHREADS) */
#include <sys/sched.h>
#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER)
#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER)
-#endif /* defined(_PR_DCETHREADS) */
-
#elif defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
|| defined(FREEBSD) || defined(SYMBIAN)
#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER)
@@ -238,9 +177,7 @@
* Needed for garbage collection -- Look at PR_Suspend/PR_Resume
* implementation.
*/
-#if defined(_PR_DCETHREADS)
-#define _PT_PTHREAD_YIELD() pthread_yield()
-#elif defined(OSF1)
+#if defined(OSF1)
/*
* sched_yield can't be called from a signal handler. Must use
* the _np version.
diff --git a/nsprpub/pr/include/prinet.h b/nsprpub/pr/include/prinet.h
index 15d229fe4..fc9f73918 100644
--- a/nsprpub/pr/include/prinet.h
+++ b/nsprpub/pr/include/prinet.h
@@ -54,7 +54,7 @@ struct sockaddr_dl;
#endif /* XP_UNIX */
#include <netdb.h>
-#if defined(FREEBSD) || defined(BSDI) || defined(QNX)
+#if defined(BSDI) || defined(QNX)
#include <rpc/types.h> /* the only place that defines INADDR_LOOPBACK */
#endif
diff --git a/nsprpub/pr/include/prinit.h b/nsprpub/pr/include/prinit.h
index fd935ec30..c55788923 100644
--- a/nsprpub/pr/include/prinit.h
+++ b/nsprpub/pr/include/prinit.h
@@ -31,9 +31,9 @@ PR_BEGIN_EXTERN_C
** The format of the version string is
** "<major version>.<minor version>[.<patch level>] [<Beta>]"
*/
-#define PR_VERSION "4.16"
+#define PR_VERSION "4.18"
#define PR_VMAJOR 4
-#define PR_VMINOR 16
+#define PR_VMINOR 18
#define PR_VPATCH 0
#define PR_BETA PR_FALSE
diff --git a/nsprpub/pr/include/private/primpl.h b/nsprpub/pr/include/private/primpl.h
index dc24a2572..a817c206c 100644
--- a/nsprpub/pr/include/private/primpl.h
+++ b/nsprpub/pr/include/private/primpl.h
@@ -6,14 +6,6 @@
#ifndef primpl_h___
#define primpl_h___
-/*
- * HP-UX 10.10's pthread.h (DCE threads) includes dce/cma.h, which
- * has:
- * #define sigaction _sigaction_sys
- * This macro causes chaos if signal.h gets included before pthread.h.
- * To be safe, we include pthread.h first.
- */
-
#if defined(_PR_PTHREADS)
#include <pthread.h>
#endif
@@ -1877,7 +1869,7 @@ extern PRFileDesc *_pr_stderr;
** and functions with macros that expand to the native thread
** types and functions on each platform.
*/
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#define _PR_ZONE_ALLOCATOR
#endif
@@ -2165,6 +2157,18 @@ extern PRUint32 connectCount;
#endif /* XP_BEOS */
+#if defined(_WIN64) && defined(WIN95)
+typedef struct _PRFileDescList {
+ PRFileDesc *fd;
+ struct _PRFileDescList *next;
+} PRFileDescList;
+
+extern PRLock *_fd_waiting_for_overlapped_done_lock;
+extern PRFileDescList *_fd_waiting_for_overlapped_done;
+extern void CheckOverlappedPendingSocketsAreDone();
+#endif
+
+
PR_END_EXTERN_C
#endif /* primpl_h___ */
diff --git a/nsprpub/pr/src/Makefile.in b/nsprpub/pr/src/Makefile.in
index 48b6faed7..19c5a6987 100644
--- a/nsprpub/pr/src/Makefile.in
+++ b/nsprpub/pr/src/Makefile.in
@@ -138,12 +138,8 @@ endif
ifeq ($(OS_ARCH),HP-UX)
ifeq ($(USE_PTHREADS), 1)
-ifeq (,$(filter-out B.10.10 B.10.20,$(OS_RELEASE)))
-OS_LIBS = -ldce
-else
OS_LIBS = -lpthread -lrt
endif
-endif
ifeq ($(PTHREADS_USER), 1)
OS_LIBS = -lpthread
endif
diff --git a/nsprpub/pr/src/io/prio.c b/nsprpub/pr/src/io/prio.c
index bf9763a2c..10ae5e098 100644
--- a/nsprpub/pr/src/io/prio.c
+++ b/nsprpub/pr/src/io/prio.c
@@ -137,11 +137,82 @@ PR_IMPLEMENT(void) PR_FreeFileDesc(PRFileDesc *fd)
_PR_Putfd(fd);
}
+#if defined(_WIN64) && defined(WIN95)
+
+PRFileDescList *_fd_waiting_for_overlapped_done = NULL;
+PRLock *_fd_waiting_for_overlapped_done_lock = NULL;
+
+void CheckOverlappedPendingSocketsAreDone()
+{
+ if (!_fd_waiting_for_overlapped_done_lock ||
+ !_fd_waiting_for_overlapped_done) {
+ return;
+ }
+
+ PR_Lock(_fd_waiting_for_overlapped_done_lock);
+
+ PRFileDescList *cur = _fd_waiting_for_overlapped_done;
+ PRFileDescList *previous = NULL;
+ while (cur) {
+ PR_ASSERT(cur->fd->secret->overlappedActive);
+ PRFileDesc *fd = cur->fd;
+ DWORD rvSent;
+ if (GetOverlappedResult((HANDLE)fd->secret->md.osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("CheckOverlappedPendingSocketsAreDone GetOverlappedResult succeeded\n"));
+ } else {
+ DWORD err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("CheckOverlappedPendingSocketsAreDone GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ }
+ }
+
+ if (!fd->secret->overlappedActive) {
+
+ _PR_MD_CLOSE_SOCKET(fd->secret->md.osfd);
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if (fd->secret->peekBuffer) {
+ PR_ASSERT(fd->secret->peekBufSize > 0);
+ PR_DELETE(fd->secret->peekBuffer);
+ fd->secret->peekBufSize = 0;
+ fd->secret->peekBytes = 0;
+ }
+#endif
+
+ PR_FreeFileDesc(fd);
+
+ if (previous) {
+ previous->next = cur->next;
+ } else {
+ _fd_waiting_for_overlapped_done = cur->next;
+ }
+ PRFileDescList *del = cur;
+ cur = cur->next;
+ PR_Free(del);
+ } else {
+ previous = cur;
+ cur = cur->next;
+ }
+ }
+
+ PR_Unlock(_fd_waiting_for_overlapped_done_lock);
+}
+#endif
+
/*
** Wait for some i/o to finish on one or more more poll descriptors.
*/
PR_IMPLEMENT(PRInt32) PR_Poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
{
+#if defined(_WIN64) && defined(WIN95)
+ // For each iteration check if TFO overlapped IOs are down.
+ CheckOverlappedPendingSocketsAreDone();
+#endif
+
return(_PR_MD_PR_POLL(pds, npds, timeout));
}
diff --git a/nsprpub/pr/src/io/prsocket.c b/nsprpub/pr/src/io/prsocket.c
index a24b8e1bb..26f7a245d 100644
--- a/nsprpub/pr/src/io/prsocket.c
+++ b/nsprpub/pr/src/io/prsocket.c
@@ -304,7 +304,25 @@ static PRStatus PR_CALLBACK SocketConnectContinue(
if (err != 0) {
_PR_MD_MAP_CONNECT_ERROR(err);
} else {
+#if defined(_WIN64)
+ if (fd->secret->overlappedActive) {
+ PRInt32 rvSent;
+ if (GetOverlappedResult(osfd, &fd->secret->ol, &rvSent, FALSE) == FALSE) {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketConnectContinue GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ fd->secret->overlappedActive = PR_FALSE;
+ }
+ }
+ }
+ if (err == 0) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ }
+#else
PR_SetError(PR_UNKNOWN_ERROR, 0);
+#endif
}
return PR_FAILURE;
}
@@ -719,6 +737,56 @@ static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd)
}
if (fd->secret->state == _PR_FILEDESC_OPEN) {
+#if defined(_WIN64) && defined(WIN95)
+ /* TCP Fast Open on Windows must use ConnectEx, which uses overlapped
+ * input/output. Before closing such a socket we must cancelIO.
+ */
+ if (fd->secret->overlappedActive) {
+ PR_ASSERT(fd->secret->nonblocking);
+ if (CancelIo((HANDLE) fd->secret->md.osfd) == TRUE) {
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose - CancelIo succeeded\n"));
+ } else {
+ DWORD err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose - CancelIo failed err=%x\n", err));
+ }
+
+ DWORD rvSent;
+ if (GetOverlappedResult((HANDLE)fd->secret->md.osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose GetOverlappedResult succeeded\n"));
+ } else {
+ DWORD err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ fd->secret->overlappedActive = PR_FALSE;
+ }
+ }
+ }
+
+ if (fd->secret->overlappedActive &&
+ _fd_waiting_for_overlapped_done_lock) {
+ // Put osfd into the list to be checked later.
+ PRFileDescList *forWaiting = PR_NEW(PRFileDescList);
+ if (!forWaiting) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ forWaiting->fd = fd;
+
+ PR_Lock(_fd_waiting_for_overlapped_done_lock);
+ forWaiting->next = _fd_waiting_for_overlapped_done;
+ _fd_waiting_for_overlapped_done = forWaiting;
+ PR_Unlock(_fd_waiting_for_overlapped_done_lock);
+
+ return PR_SUCCESS;
+ }
+#endif
+
if (_PR_MD_CLOSE_SOCKET(fd->secret->md.osfd) < 0) {
return PR_FAILURE;
}
diff --git a/nsprpub/pr/src/md/unix/unix.c b/nsprpub/pr/src/md/unix/unix.c
index 662f561b6..29e24e574 100644
--- a/nsprpub/pr/src/md/unix/unix.c
+++ b/nsprpub/pr/src/md/unix/unix.c
@@ -2854,28 +2854,11 @@ void _PR_UnixInit(void)
#endif
#endif /* !defined(_PR_PTHREADS) */
- /*
- * Under HP-UX DCE threads, sigaction() installs a per-thread
- * handler, so we use sigvector() to install a process-wide
- * handler.
- */
-#if defined(HPUX) && defined(_PR_DCETHREADS)
- {
- struct sigvec vec;
-
- vec.sv_handler = SIG_IGN;
- vec.sv_mask = 0;
- vec.sv_flags = 0;
- rv = sigvector(SIGPIPE, &vec, NULL);
- PR_ASSERT(0 == rv);
- }
-#else
sigact.sa_handler = SIG_IGN;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
rv = sigaction(SIGPIPE, &sigact, 0);
PR_ASSERT(0 == rv);
-#endif /* HPUX && _PR_DCETHREADS */
_pr_rename_lock = PR_NewLock();
PR_ASSERT(NULL != _pr_rename_lock);
diff --git a/nsprpub/pr/src/md/unix/uxproces.c b/nsprpub/pr/src/md/unix/uxproces.c
index 5286b9e18..18f23fde8 100644
--- a/nsprpub/pr/src/md/unix/uxproces.c
+++ b/nsprpub/pr/src/md/unix/uxproces.c
@@ -685,10 +685,6 @@ static void pr_SigchldHandler(int sig)
static void pr_InstallSigchldHandler()
{
-#if defined(HPUX) && defined(_PR_DCETHREADS)
-#error "HP-UX DCE threads have their own SIGCHLD handler"
-#endif
-
struct sigaction act, oact;
int rv;
diff --git a/nsprpub/pr/src/md/windows/ntthread.c b/nsprpub/pr/src/md/windows/ntthread.c
index fead1236d..1fdf0e93b 100644
--- a/nsprpub/pr/src/md/windows/ntthread.c
+++ b/nsprpub/pr/src/md/windows/ntthread.c
@@ -27,6 +27,9 @@ PRUint32 _nt_idleCount;
extern __declspec(thread) PRThread *_pr_io_restarted_io;
extern DWORD _pr_io_restartedIOIndex;
+typedef HRESULT (WINAPI *SETTHREADDESCRIPTION)(HANDLE, PCWSTR);
+static SETTHREADDESCRIPTION sSetThreadDescription = NULL;
+
/* Must check the restarted_io *before* decrementing no_sched to 0 */
#define POST_SWITCH_WORK() \
PR_BEGIN_MACRO \
@@ -79,6 +82,8 @@ _nt_handle_restarted_io(PRThread *restarted_io)
void
_PR_MD_EARLY_INIT()
{
+ HMODULE hModule;
+
_MD_NEW_LOCK( &_nt_idleLock );
_nt_idleCount = 0;
PR_INIT_CLIST(&_nt_idleList);
@@ -98,6 +103,15 @@ _PR_MD_EARLY_INIT()
_pr_intsOffIndex = TlsAlloc();
_pr_io_restartedIOIndex = TlsAlloc();
}
+
+ // SetThreadDescription is Windows 10 build 1607+
+ hModule = GetModuleHandleW(L"kernel32.dll");
+ if (hModule) {
+ sSetThreadDescription =
+ (SETTHREADDESCRIPTION) GetProcAddress(
+ hModule,
+ "SetThreadDescription");
+ }
}
void _PR_MD_CLEANUP_BEFORE_EXIT(void)
@@ -293,7 +307,16 @@ _PR_MD_SET_CURRENT_THREAD_NAME(const char *name)
{
#ifdef _MSC_VER
THREADNAME_INFO info;
+#endif
+
+ if (sSetThreadDescription) {
+ WCHAR wideName[MAX_PATH];
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wideName, MAX_PATH)) {
+ sSetThreadDescription(GetCurrentThread(), wideName);
+ }
+ }
+#ifdef _MSC_VER
if (!IsDebuggerPresent())
return;
diff --git a/nsprpub/pr/src/md/windows/w95sock.c b/nsprpub/pr/src/md/windows/w95sock.c
index 0429c655a..c6a3ec111 100644
--- a/nsprpub/pr/src/md/windows/w95sock.c
+++ b/nsprpub/pr/src/md/windows/w95sock.c
@@ -382,6 +382,11 @@ PRInt32
_PR_MD_TCPSENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
{
+ if (!_fd_waiting_for_overlapped_done_lock) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+
if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) {
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
diff --git a/nsprpub/pr/src/md/windows/w95thred.c b/nsprpub/pr/src/md/windows/w95thred.c
index c27d982a7..a365411f5 100644
--- a/nsprpub/pr/src/md/windows/w95thred.c
+++ b/nsprpub/pr/src/md/windows/w95thred.c
@@ -1,3 +1,4 @@
+
/* -*- 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
@@ -27,14 +28,32 @@ DWORD _pr_currentCPUIndex;
int _pr_intsOff = 0;
_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+typedef HRESULT (WINAPI *SETTHREADDESCRIPTION)(HANDLE, PCWSTR);
+static SETTHREADDESCRIPTION sSetThreadDescription = NULL;
+
void
_PR_MD_EARLY_INIT()
{
+ HMODULE hModule;
+
#ifndef _PR_USE_STATIC_TLS
_pr_currentThreadIndex = TlsAlloc();
_pr_lastThreadIndex = TlsAlloc();
_pr_currentCPUIndex = TlsAlloc();
#endif
+
+#if defined(_WIN64) && defined(WIN95)
+ _fd_waiting_for_overlapped_done_lock = PR_NewLock();
+#endif
+
+ // SetThreadDescription is Windows 10 build 1607+
+ hModule = GetModuleHandleW(L"kernel32.dll");
+ if (hModule) {
+ sSetThreadDescription =
+ (SETTHREADDESCRIPTION) GetProcAddress(
+ hModule,
+ "SetThreadDescription");
+ }
}
void _PR_MD_CLEANUP_BEFORE_EXIT(void)
@@ -50,6 +69,29 @@ void _PR_MD_CLEANUP_BEFORE_EXIT(void)
TlsFree(_pr_lastThreadIndex);
TlsFree(_pr_currentCPUIndex);
#endif
+
+#if defined(_WIN64) && defined(WIN95)
+ // For each iteration check if TFO overlapped IOs are down.
+ if (_fd_waiting_for_overlapped_done_lock) {
+ PRIntervalTime delay = PR_MillisecondsToInterval(1000);
+ PRFileDescList *cur;
+ do {
+ CheckOverlappedPendingSocketsAreDone();
+
+ PR_Lock(_fd_waiting_for_overlapped_done_lock);
+ cur = _fd_waiting_for_overlapped_done;
+ PR_Unlock(_fd_waiting_for_overlapped_done_lock);
+#if defined(DO_NOT_WAIT_FOR_CONNECT_OVERLAPPED_OPERATIONS)
+ cur = NULL;
+#endif
+ if (cur) {
+ PR_Sleep(delay); // wait another 1s.
+ }
+ } while (cur);
+
+ PR_DestroyLock(_fd_waiting_for_overlapped_done_lock);
+ }
+#endif
}
PRStatus
@@ -190,7 +232,16 @@ _PR_MD_SET_CURRENT_THREAD_NAME(const char *name)
{
#ifdef _MSC_VER
THREADNAME_INFO info;
+#endif
+
+ if (sSetThreadDescription) {
+ WCHAR wideName[MAX_PATH];
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wideName, MAX_PATH)) {
+ sSetThreadDescription(GetCurrentThread(), wideName);
+ }
+ }
+#ifdef _MSC_VER
if (!IsDebuggerPresent())
return;
diff --git a/nsprpub/pr/src/md/windows/win32_errors.c b/nsprpub/pr/src/md/windows/win32_errors.c
index d26820a7a..275792187 100644
--- a/nsprpub/pr/src/md/windows/win32_errors.c
+++ b/nsprpub/pr/src/md/windows/win32_errors.c
@@ -166,21 +166,26 @@ void _MD_win32_map_default_error(PRInt32 err)
prError = PR_ADDRESS_IN_USE_ERROR;
break;
case WSAEADDRNOTAVAIL:
+ case ERROR_INVALID_NETNAME:
prError = PR_ADDRESS_NOT_AVAILABLE_ERROR;
break;
case WSAEAFNOSUPPORT:
+ case ERROR_INCORRECT_ADDRESS:
prError = PR_ADDRESS_NOT_SUPPORTED_ERROR;
break;
case WSAEALREADY:
+ case ERROR_ALREADY_INITIALIZED:
prError = PR_ALREADY_INITIATED_ERROR;
break;
case WSAEBADF:
prError = PR_BAD_DESCRIPTOR_ERROR;
break;
case WSAECONNABORTED:
+ case ERROR_CONNECTION_ABORTED:
prError = PR_CONNECT_ABORTED_ERROR;
break;
case WSAECONNREFUSED:
+ case ERROR_CONNECTION_REFUSED:
prError = PR_CONNECT_REFUSED_ERROR;
break;
case WSAECONNRESET:
@@ -193,6 +198,7 @@ void _MD_win32_map_default_error(PRInt32 err)
prError = PR_ACCESS_FAULT_ERROR;
break;
case WSAEHOSTUNREACH:
+ case ERROR_HOST_UNREACHABLE:
prError = PR_HOST_UNREACHABLE_ERROR;
break;
case WSAEINVAL:
@@ -208,12 +214,14 @@ void _MD_win32_map_default_error(PRInt32 err)
prError = PR_BUFFER_OVERFLOW_ERROR;
break;
case WSAENETDOWN:
+ case ERROR_NO_NETWORK:
prError = PR_NETWORK_DOWN_ERROR;
break;
case WSAENETRESET:
prError = PR_CONNECT_ABORTED_ERROR;
break;
case WSAENETUNREACH:
+ case ERROR_NETWORK_UNREACHABLE:
prError = PR_NETWORK_UNREACHABLE_ERROR;
break;
case WSAENOBUFS:
diff --git a/nsprpub/pr/src/misc/pratom.c b/nsprpub/pr/src/misc/pratom.c
index 95bbee1d5..65e6f3cd8 100644
--- a/nsprpub/pr/src/misc/pratom.c
+++ b/nsprpub/pr/src/misc/pratom.c
@@ -23,7 +23,7 @@
#if !defined(_PR_HAVE_ATOMIC_OPS)
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
/*
* PR_AtomicDecrement() is used in NSPR's thread-specific data
* destructor. Because thread-specific data destructors may be
@@ -190,7 +190,7 @@ _PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
pthread_mutex_unlock(&atomic_locks[idx]);
return rv;
}
-#else /* _PR_PTHREADS && !_PR_DCETHREADS */
+#else /* _PR_PTHREADS */
/*
* We use a single lock for all the emulated atomic operations.
* The lock contention should be acceptable.
@@ -259,7 +259,7 @@ _PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
PR_Unlock(atomic_lock);
return rv;
}
-#endif /* _PR_PTHREADS && !_PR_DCETHREADS */
+#endif /* _PR_PTHREADS */
#endif /* !_PR_HAVE_ATOMIC_OPS */
diff --git a/nsprpub/pr/src/pthreads/ptio.c b/nsprpub/pr/src/pthreads/ptio.c
index 9dde03191..f6aa56741 100644
--- a/nsprpub/pr/src/pthreads/ptio.c
+++ b/nsprpub/pr/src/pthreads/ptio.c
@@ -1895,19 +1895,6 @@ static PRInt32 pt_Send(
PRInt32 tmp_amount = amount;
#endif
- /*
- * Under HP-UX DCE threads, pthread.h includes dce/cma_ux.h,
- * which has the following:
- * # define send cma_send
- * extern int cma_send (int , void *, int, int );
- * So we need to cast away the 'const' of argument #2 for send().
- */
-#if defined (HPUX) && defined(_PR_DCETHREADS)
-#define PT_SENDBUF_CAST (void *)
-#else
-#define PT_SENDBUF_CAST
-#endif
-
if (pt_TestAbort()) return bytes;
/*
@@ -1918,9 +1905,9 @@ static PRInt32 pt_Send(
#if defined(SOLARIS)
PR_ASSERT(0 == flags);
retry:
- bytes = write(fd->secret->md.osfd, PT_SENDBUF_CAST buf, tmp_amount);
+ bytes = write(fd->secret->md.osfd, buf, tmp_amount);
#else
- bytes = send(fd->secret->md.osfd, PT_SENDBUF_CAST buf, amount, flags);
+ bytes = send(fd->secret->md.osfd, buf, amount, flags);
#endif
syserrno = errno;
diff --git a/nsprpub/pr/src/pthreads/ptsynch.c b/nsprpub/pr/src/pthreads/ptsynch.c
index 251205336..a93b74795 100644
--- a/nsprpub/pr/src/pthreads/ptsynch.c
+++ b/nsprpub/pr/src/pthreads/ptsynch.c
@@ -23,11 +23,6 @@ static pthread_condattr_t _pt_cvar_attr;
#if defined(DEBUG)
extern PTDebug pt_debug; /* this is shared between several modules */
-
-#if defined(_PR_DCETHREADS)
-static pthread_t pt_zero_tid; /* a null pthread_t (pthread_t is a struct
- * in DCE threads) to compare with */
-#endif /* defined(_PR_DCETHREADS) */
#endif /* defined(DEBUG) */
#if defined(FREEBSD)
@@ -263,12 +258,7 @@ static PRIntn pt_TimedWait(
rv = pthread_cond_timedwait(cv, ml, &tmo);
/* NSPR doesn't report timeouts */
-#ifdef _PR_DCETHREADS
- if (rv == -1) return (errno == EAGAIN) ? 0 : errno;
- else return rv;
-#else
return (rv == ETIMEDOUT) ? 0 : rv;
-#endif
} /* pt_TimedWait */
@@ -1171,14 +1161,14 @@ PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
{
PRIntn rv = pthread_mutex_trylock(&lock->mutex);
- if (rv == PT_TRYLOCK_SUCCESS)
+ if (rv == 0)
{
PR_ASSERT(PR_FALSE == lock->locked);
lock->locked = PR_TRUE;
lock->owner = pthread_self();
}
/* XXX set error code? */
- return (PT_TRYLOCK_SUCCESS == rv) ? PR_SUCCESS : PR_FAILURE;
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
} /* PRP_TryLock */
PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
diff --git a/nsprpub/pr/src/pthreads/ptthread.c b/nsprpub/pr/src/pthreads/ptthread.c
index 9e12606ea..6046d5d8d 100644
--- a/nsprpub/pr/src/pthreads/ptthread.c
+++ b/nsprpub/pr/src/pthreads/ptthread.c
@@ -9,7 +9,7 @@
** Exports: ptthread.h
*/
-#if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include "prlog.h"
#include "primpl.h"
@@ -58,7 +58,7 @@ static struct _PT_Bookeeping
pthread_key_t key; /* thread private data key */
PRBool keyCreated; /* whether 'key' should be deleted */
PRThread *first, *last; /* list of threads we know about */
-#if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
PRInt32 minPrio, maxPrio; /* range of scheduling priorities */
#endif
} pt_book = {0};
@@ -67,7 +67,7 @@ static void _pt_thread_death(void *arg);
static void _pt_thread_death_internal(void *arg, PRBool callDestructors);
static void init_pthread_gc_support(void);
-#if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
static PRIntn pt_PriorityMap(PRThreadPriority pri)
{
#ifdef NTO
@@ -148,21 +148,6 @@ static void *_pt_root(void *arg)
}
#endif
- /*
- ** DCE Threads can't detach during creation, so do it late.
- ** I would like to do it only here, but that doesn't seem
- ** to work.
- */
-#if defined(_PR_DCETHREADS)
- if (detached)
- {
- /* pthread_detach() modifies its argument, so we must pass a copy */
- pthread_t self = id;
- rv = pthread_detach(&self);
- PR_ASSERT(0 == rv);
- }
-#endif /* defined(_PR_DCETHREADS) */
-
/* Set up the thread stack information */
_PR_InitializeStack(thred->stack);
@@ -329,7 +314,7 @@ static PRThread* _PR_CreateThread(
if (EPERM != pt_schedpriv)
{
-#if !defined(_PR_DCETHREADS) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
struct sched_param schedule;
#endif
@@ -340,10 +325,7 @@ static PRThread* _PR_CreateThread(
/* Use the default scheduling policy */
-#if defined(_PR_DCETHREADS)
- rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority));
- PR_ASSERT(0 == rv);
-#elif _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
rv = pthread_attr_getschedparam(&tattr, &schedule);
PR_ASSERT(0 == rv);
schedule.sched_priority = pt_PriorityMap(priority);
@@ -353,19 +335,13 @@ static PRThread* _PR_CreateThread(
rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */
PR_ASSERT(0 == rv);
#endif
-#endif /* !defined(_PR_DCETHREADS) */
+#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING > 0 */
}
- /*
- * DCE threads can't set detach state before creating the thread.
- * AIX can't set detach late. Why can't we all just get along?
- */
-#if !defined(_PR_DCETHREADS)
rv = pthread_attr_setdetachstate(&tattr,
((PR_JOINABLE_THREAD == state) ?
PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
PR_ASSERT(0 == rv);
-#endif /* !defined(_PR_DCETHREADS) */
/*
* If stackSize is 0, we use the default pthread stack size.
@@ -456,7 +432,6 @@ static PRThread* _PR_CreateThread(
*/
rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
-#if !defined(_PR_DCETHREADS)
if (EPERM == rv)
{
#if defined(IRIX)
@@ -482,15 +457,10 @@ static PRThread* _PR_CreateThread(
#endif /* IRIX */
rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
}
-#endif
if (0 != rv)
{
-#if defined(_PR_DCETHREADS)
- PRIntn oserr = errno;
-#else
PRIntn oserr = rv;
-#endif
PR_Lock(pt_book.ml);
if (thred->state & PT_THREAD_SYSTEM)
pt_book.system -= 1;
@@ -602,10 +572,6 @@ PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred)
PR_ASSERT(rv == 0 && result == NULL);
if (0 == rv)
{
-#ifdef _PR_DCETHREADS
- rv = pthread_detach(&id);
- PR_ASSERT(0 == rv);
-#endif
/*
* PR_FALSE, because the thread already called the TPD
* destructors before exiting _pt_root.
@@ -693,10 +659,7 @@ PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri
else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri)
newPri = PR_PRIORITY_LAST;
-#if defined(_PR_DCETHREADS)
- rv = pthread_setprio(thred->id, pt_PriorityMap(newPri));
- /* pthread_setprio returns the old priority */
-#elif _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
if (EPERM != pt_schedpriv)
{
int policy;
@@ -927,7 +890,7 @@ void _PR_InitThreads(
pthread_init();
#endif
-#if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
#if defined(FREEBSD)
{
pthread_attr_t attr;
@@ -1161,11 +1124,7 @@ PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred)
{
-#if defined(_PR_DCETHREADS)
- return (PRUint32)&thred->id; /* this is really a sham! */
-#else
return (PRUint32)thred->id; /* and I don't know what they will do with it */
-#endif
}
/*
@@ -1197,18 +1156,6 @@ PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
* Garbage collection support follows.
*/
-#if defined(_PR_DCETHREADS)
-
-/*
- * statics for Garbage Collection support. We don't need to protect these
- * signal masks since the garbage collector itself is protected by a lock
- * and multiple threads will not be garbage collecting at the same time.
- */
-static sigset_t javagc_vtalarm_sigmask;
-static sigset_t javagc_intsoff_sigmask;
-
-#else /* defined(_PR_DCETHREADS) */
-
/* a bogus signal mask for forcing a timed wait */
/* Not so bogus in AIX as we really do a sigwait */
static sigset_t sigwait_set;
@@ -1224,8 +1171,6 @@ static void suspend_signal_handler(PRIntn sig);
static void null_signal_handler(PRIntn sig);
#endif
-#endif /* defined(_PR_DCETHREADS) */
-
/*
* Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
* conflict with the use of these two signals in our GC support.
@@ -1236,12 +1181,6 @@ static void init_pthread_gc_support(void)
#ifndef SYMBIAN
PRIntn rv;
-#if defined(_PR_DCETHREADS)
- rv = sigemptyset(&javagc_vtalarm_sigmask);
- PR_ASSERT(0 == rv);
- rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM);
- PR_ASSERT(0 == rv);
-#else /* defined(_PR_DCETHREADS) */
{
struct sigaction sigact_usr2;
@@ -1269,7 +1208,6 @@ static void init_pthread_gc_support(void)
PR_ASSERT(0 ==rv);
}
#endif /* defined(PT_NO_SIGTIMEDWAIT) */
-#endif /* defined(_PR_DCETHREADS) */
#endif /* SYMBIAN */
}
@@ -1300,10 +1238,8 @@ PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
PRThread* thred = pt_book.first;
#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
-#if !defined(_PR_DCETHREADS)
PRThread *me = PR_GetCurrentThread();
#endif
-#endif
PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
/*
@@ -1331,9 +1267,7 @@ PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
if (_PT_IS_GCABLE_THREAD(thred))
{
-#if !defined(_PR_DCETHREADS)
PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
-#endif
PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
("In PR_EnumerateThreads callback thread %p thid = %X\n",
thred, thred->id));
@@ -1361,8 +1295,6 @@ PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
* proceed until the thread is suspended or resumed.
*/
-#if !defined(_PR_DCETHREADS)
-
/*
* In the signal handler, we can not use condition variable notify or wait.
* This does not work consistently across all pthread platforms. We also can not
@@ -1661,78 +1593,6 @@ PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred)
return thred->sp;
} /* PR_GetSP */
-#else /* !defined(_PR_DCETHREADS) */
-
-static pthread_once_t pt_gc_support_control = pthread_once_init;
-
-/*
- * For DCE threads, there is no pthread_kill or a way of suspending or resuming a
- * particular thread. We will just disable the preemption (virtual timer alarm) and
- * let the executing thread finish the garbage collection. This stops all other threads
- * (GC able or not) and is very inefficient but there is no other choice.
- */
-PR_IMPLEMENT(void) PR_SuspendAll()
-{
- PRIntn rv;
-
- rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
- PR_ASSERT(0 == rv); /* returns -1 on failure */
-#ifdef DEBUG
- suspendAllOn = PR_TRUE;
-#endif
- PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
- /*
- * turn off preemption - i.e add virtual alarm signal to the set of
- * blocking signals
- */
- rv = sigprocmask(
- SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask);
- PR_ASSERT(0 == rv);
- suspendAllSuspended = PR_TRUE;
- PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n"));
-} /* PR_SuspendAll */
-
-PR_IMPLEMENT(void) PR_ResumeAll()
-{
- PRIntn rv;
-
- suspendAllSuspended = PR_FALSE;
- PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
- /* turn on preemption - i.e re-enable virtual alarm signal */
-
- rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL);
- PR_ASSERT(0 == rv);
-#ifdef DEBUG
- suspendAllOn = PR_FALSE;
-#endif
-
- PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n"));
-} /* PR_ResumeAll */
-
-/* Return the stack pointer for the given thread- used by the GC */
-PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred)
-{
- pthread_t tid = thred->id;
- char *thread_tcb, *top_sp;
-
- /*
- * For HPUX DCE threads, pthread_t is a struct with the
- * following three fields (see pthread.h, dce/cma.h):
- * cma_t_address field1;
- * short int field2;
- * short int field3;
- * where cma_t_address is typedef'd to be either void*
- * or char*.
- */
- PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n"));
- thread_tcb = (char*)tid.field1;
- top_sp = *(char**)(thread_tcb + 128);
- PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %p \n", top_sp));
- return top_sp;
-} /* PR_GetSP */
-
-#endif /* !defined(_PR_DCETHREADS) */
-
PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name)
{
PRThread *thread;
@@ -1817,6 +1677,6 @@ PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
return thread->name;
}
-#endif /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */
+#endif /* defined(_PR_PTHREADS) */
/* ptthread.c */
diff --git a/nsprpub/pr/src/threads/prrwlock.c b/nsprpub/pr/src/threads/prrwlock.c
index 2e0f9ea4e..1dd9e0a90 100644
--- a/nsprpub/pr/src/threads/prrwlock.c
+++ b/nsprpub/pr/src/threads/prrwlock.c
@@ -7,7 +7,7 @@
#include <string.h>
-#if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(HPUX) && defined(_PR_PTHREADS)
#include <pthread.h>
#define HAVE_UNIX98_RWLOCK
diff --git a/nsprpub/pr/tests/Makefile.in b/nsprpub/pr/tests/Makefile.in
index df1f1f2dd..79a67f09c 100644
--- a/nsprpub/pr/tests/Makefile.in
+++ b/nsprpub/pr/tests/Makefile.in
@@ -373,9 +373,6 @@ endif
ifeq (,$(filter-out FreeBSD OpenBSD BSD_OS QNX Darwin OpenUNIX,$(OS_ARCH)))
LIBPTHREAD =
endif
-ifeq ($(OS_ARCH)$(basename $(OS_RELEASE)),HP-UXB.10)
-LIBPTHREAD = -ldce
-endif
endif
ifeq ($(OS_TARGET),Android)
diff --git a/nsprpub/pr/tests/attach.c b/nsprpub/pr/tests/attach.c
index a5daf9d96..a1493b8ec 100644
--- a/nsprpub/pr/tests/attach.c
+++ b/nsprpub/pr/tests/attach.c
@@ -111,16 +111,6 @@ static int32 threadStartFunc(void *arg)
static void * threadStartFunc(void *arg)
#endif
{
-#ifdef _PR_DCETHREADS
- {
- int rv;
- pthread_t self = pthread_self();
- rv = pthread_detach(&self);
- if (debug_mode) PR_ASSERT(0 == rv);
- else if (0 != rv) failed_already=1;
- }
-#endif
-
Measure(AttachDetach, "Attach/Detach");
#ifndef IRIX
@@ -206,14 +196,12 @@ int main(int argc, char **argv)
goto exit_now;
}
-#ifndef _PR_DCETHREADS
rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (debug_mode) PR_ASSERT(0 == rv);
else if (0 != rv) {
failed_already=1;
goto exit_now;
}
-#endif /* !_PR_DCETHREADS */
rv = _PT_PTHREAD_CREATE(&threadID, attr, threadStartFunc, NULL);
if (rv != 0) {
fprintf(stderr, "thread creation failed: error code %d\n", rv);
diff --git a/nsprpub/pr/tests/dceemu.c b/nsprpub/pr/tests/dceemu.c
index b06b49e3a..99fd6cb7c 100644
--- a/nsprpub/pr/tests/dceemu.c
+++ b/nsprpub/pr/tests/dceemu.c
@@ -29,8 +29,6 @@
#include <stdio.h>
#include <stdlib.h>
-#if defined(_PR_DCETHREADS)
-
PRIntn failed_already=0;
PRIntn debug_mode=0;
@@ -80,20 +78,13 @@ static PRIntn prmain(PRIntn argc, char **argv)
} /* prmain */
-#endif /* #if defined(_PR_DCETHREADS) */
-
int main(int argc, char **argv)
{
-
-#if defined(_PR_DCETHREADS)
PR_Initialize(prmain, argc, argv, 0);
if(failed_already)
return 1;
else
return 0;
-#else
- return 0;
-#endif
} /* main */
diff --git a/nsprpub/pr/tests/foreign.c b/nsprpub/pr/tests/foreign.c
index 637f419d1..cfb2e56ef 100644
--- a/nsprpub/pr/tests/foreign.c
+++ b/nsprpub/pr/tests/foreign.c
@@ -52,7 +52,7 @@ static int _debug_on = 0;
#define DPRINTF(arg) if (_debug_on) PR_fprintf arg
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include <pthread.h>
#include "md/_pth.h"
static void *pthread_start(void *arg)
@@ -63,7 +63,7 @@ static void *pthread_start(void *arg)
start(data);
return NULL;
} /* pthread_start */
-#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+#endif /* defined(_PR_PTHREADS) */
#if defined(IRIX) && !defined(_PR_PTHREADS)
#include <sys/types.h>
@@ -109,7 +109,7 @@ static PRStatus NSPRPUB_TESTS_CreateThread(StartFn start, void *arg)
}
break;
case thread_pthread:
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
{
int rv;
pthread_t id;
@@ -137,7 +137,7 @@ static PRStatus NSPRPUB_TESTS_CreateThread(StartFn start, void *arg)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
rv = PR_FAILURE;
break;
-#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+#endif /* defined(_PR_PTHREADS) */
case thread_sproc:
#if defined(IRIX) && !defined(_PR_PTHREADS)
diff --git a/nsprpub/pr/tests/forktest.c b/nsprpub/pr/tests/forktest.c
index a389fa479..66dc64575 100644
--- a/nsprpub/pr/tests/forktest.c
+++ b/nsprpub/pr/tests/forktest.c
@@ -196,51 +196,6 @@ finish:
return;
}
-#ifdef _PR_DCETHREADS
-
-#include <syscall.h>
-
-pid_t PR_UnixFork1(void)
-{
- pid_t parent = getpid();
- int rv = syscall(SYS_fork);
-
- if (rv == -1) {
- return (pid_t) -1;
- } else {
- /* For each process, rv is the pid of the other process */
- if (rv == parent) {
- /* the child */
- return 0;
- } else {
- /* the parent */
- return rv;
- }
- }
-}
-
-#elif defined(SOLARIS)
-
-/*
- * It seems like that in Solaris 2.4 one must call fork1() if the
- * the child process is going to use thread functions. Solaris 2.5
- * doesn't have this problem. Calling fork() also works.
- */
-
-pid_t PR_UnixFork1(void)
-{
- return fork1();
-}
-
-#else
-
-pid_t PR_UnixFork1(void)
-{
- return fork();
-}
-
-#endif /* PR_DCETHREADS */
-
int main(int argc, char **argv)
{
pid_t pid;
@@ -250,7 +205,7 @@ int main(int argc, char **argv)
DoIO();
- pid = PR_UnixFork1();
+ pid = fork();
if (pid == (pid_t) -1) {
fprintf(stderr, "Fork failed: errno %d\n", errno);
diff --git a/nsprpub/pr/tests/provider.c b/nsprpub/pr/tests/provider.c
index 0e6569d66..932241ec3 100644
--- a/nsprpub/pr/tests/provider.c
+++ b/nsprpub/pr/tests/provider.c
@@ -606,7 +606,7 @@ typedef struct StartObject
void *arg;
} StartObject;
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include "md/_pth.h"
#include <pthread.h>
@@ -619,7 +619,7 @@ static void *pthread_start(void *arg)
start(data);
return NULL;
} /* pthread_start */
-#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+#endif /* defined(_PR_PTHREADS) */
#if defined(IRIX) && !defined(_PR_PTHREADS)
#include <sys/types.h>
@@ -657,10 +657,10 @@ static PRStatus JoinThread(PRThread *thread)
rv = PR_JoinThread(thread);
break;
case thread_pthread:
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
rv = PR_SUCCESS;
break;
-#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+#endif /* defined(_PR_PTHREADS) */
case thread_win32:
#if defined(WIN32)
rv = PR_SUCCESS;
@@ -690,7 +690,7 @@ static PRStatus NewThread(
}
break;
case thread_pthread:
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
{
int rv;
pthread_t id;
@@ -717,7 +717,7 @@ static PRStatus NewThread(
#else
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
rv = PR_FAILURE;
-#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+#endif /* defined(_PR_PTHREADS) */
break;
case thread_sproc:
diff --git a/nsprpub/pr/tests/socket.c b/nsprpub/pr/tests/socket.c
index 5ee2b78f3..92f019d01 100644
--- a/nsprpub/pr/tests/socket.c
+++ b/nsprpub/pr/tests/socket.c
@@ -21,7 +21,7 @@
#ifdef XP_UNIX
#include <sys/mman.h>
#endif
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include <pthread.h>
#endif
@@ -313,7 +313,7 @@ PRThread* create_new_thread(PRThreadType type,
PRInt32 native_thread = 0;
PR_ASSERT(state == PR_UNJOINABLE_THREAD);
-#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WIN32)
+#if defined(_PR_PTHREADS) || defined(WIN32)
switch(index % 4) {
case 0:
scope = (PR_LOCAL_THREAD);
@@ -332,7 +332,7 @@ PRInt32 native_thread = 0;
break;
}
if (native_thread) {
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
pthread_t tid;
if (!pthread_create(&tid, NULL, (void * (*)(void *)) start, arg))
return((PRThread *) tid);
diff --git a/nsprpub/pr/tests/testfile.c b/nsprpub/pr/tests/testfile.c
index 23659876e..1191bfe94 100644
--- a/nsprpub/pr/tests/testfile.c
+++ b/nsprpub/pr/tests/testfile.c
@@ -13,7 +13,7 @@
#include <windows.h>
#include <process.h>
#endif
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include <pthread.h>
#endif
#ifdef SYMBIAN
@@ -104,7 +104,7 @@ PRInt32 native_thread = 0;
PR_ASSERT(state == PR_UNJOINABLE_THREAD);
-#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WIN32) || defined(XP_OS2)
+#if defined(_PR_PTHREADS) || defined(WIN32) || defined(XP_OS2)
switch(index % 4) {
case 0:
@@ -124,7 +124,7 @@ PRInt32 native_thread = 0;
break;
}
if (native_thread) {
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
pthread_t tid;
if (!pthread_create(&tid, NULL, start, arg))
return((PRThread *) tid);
diff --git a/nsprpub/pr/tests/thrpool_client.c b/nsprpub/pr/tests/thrpool_client.c
index a0e1e4c24..7f3df3271 100644
--- a/nsprpub/pr/tests/thrpool_client.c
+++ b/nsprpub/pr/tests/thrpool_client.c
@@ -21,7 +21,7 @@
#ifdef XP_UNIX
#include <sys/mman.h>
#endif
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include <pthread.h>
#endif
diff --git a/nsprpub/pr/tests/thrpool_server.c b/nsprpub/pr/tests/thrpool_server.c
index 9665829a1..ef09f23b5 100644
--- a/nsprpub/pr/tests/thrpool_server.c
+++ b/nsprpub/pr/tests/thrpool_server.c
@@ -21,7 +21,7 @@
#ifdef XP_UNIX
#include <sys/mman.h>
#endif
-#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#if defined(_PR_PTHREADS)
#include <pthread.h>
#endif
diff --git a/nsprpub/pr/tests/vercheck.c b/nsprpub/pr/tests/vercheck.c
index 6170125de..da2f7b1de 100644
--- a/nsprpub/pr/tests/vercheck.c
+++ b/nsprpub/pr/tests/vercheck.c
@@ -39,7 +39,8 @@ static char *compatible_version[] = {
"4.9.6",
"4.10", "4.10.1", "4.10.2", "4.10.3", "4.10.4",
"4.10.5", "4.10.6", "4.10.7", "4.10.8", "4.10.9",
- "4.10.10", "4.11", "4.12", "4.13", "4.14", "4.15"
+ "4.10.10", "4.11", "4.12", "4.13", "4.14", "4.15",
+ "4.16", "4.17",
PR_VERSION
};
@@ -55,8 +56,8 @@ static char *incompatible_version[] = {
"3.0", "3.0.1",
"3.1", "3.1.1", "3.1.2", "3.1.3",
"3.5", "3.5.1",
- "4.16.1",
- "4.17", "4.17.1",
+ "4.18.1",
+ "4.19", "4.19.1",
"10.0", "11.1", "12.14.20"
};
diff --git a/old-configure.in b/old-configure.in
index 76f9d3a4d..17e1a4eca 100644
--- a/old-configure.in
+++ b/old-configure.in
@@ -212,7 +212,7 @@ dnl ========================================================
dnl Special win32 checks
dnl ========================================================
-WINVER=502
+WINVER=601
case "$target" in
*-mingw*)
@@ -842,7 +842,10 @@ fi
AC_DEFINE_UNQUOTED(MOZILLA_VERSION,"$MOZILLA_VERSION")
AC_DEFINE_UNQUOTED(MOZILLA_VERSION_U,$MOZILLA_VERSION)
AC_DEFINE_UNQUOTED(MOZILLA_UAVERSION,"$MOZILLA_UAVERSION")
+AC_DEFINE_UNQUOTED(MOZILLA_UAVERSION_U,$MOZILLA_UAVERSION)
AC_SUBST(MOZILLA_SYMBOLVERSION)
+AC_SUBST(MOZILLA_UAVERSION)
+AC_SUBST(MOZILLA_UAVERSION_U)
MOZ_DOING_LTO(lto_is_enabled)
@@ -1010,12 +1013,7 @@ case "$target" in
IMPORT_LIB_SUFFIX=lib
MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
- dnl Set subsystem version 5 for Windows XP.
- if test "$CPU_ARCH" = "x86"; then
- WIN32_SUBSYSTEM_VERSION=5.01
- else
- WIN32_SUBSYSTEM_VERSION=6.01
- fi
+ WIN32_SUBSYSTEM_VERSION=6.01
WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
DSO_LDOPTS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
@@ -2111,7 +2109,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
_USE_SYSTEM_NSS=1 )
if test -n "$_USE_SYSTEM_NSS"; then
- AM_PATH_NSS(3.28.6, [MOZ_SYSTEM_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
+ AM_PATH_NSS(3.35, [MOZ_SYSTEM_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
fi
if test -n "$MOZ_SYSTEM_NSS"; then
@@ -2309,9 +2307,11 @@ MOZ_TIME_MANAGER=
MOZ_AUDIO_CHANNEL_MANAGER=
MOZ_CONTENT_SANDBOX=
MOZ_GMP_SANDBOX=
-MOZ_SANDBOX=1
+MOZ_SANDBOX=
MOZ_BINARY_EXTENSIONS=
-MOZ_DEVTOOLS=server
+MOZ_JETPACK=1
+MOZ_DEVTOOLS_SERVER=1
+MOZ_DEVTOOLS=
case "$target_os" in
mingw*)
@@ -3210,15 +3210,20 @@ dnl ==================================
dnl = Check alsa availability on Linux
dnl ==================================
+dnl If using Linux, enable the alsa library by default
+if test "$OS_TARGET" = "Linux"; then
+ MOZ_ALSA=1
+fi
+
MOZ_ARG_ENABLE_BOOL(alsa,
-[ --enable-alsa Enable Alsa support],
+[ --enable-alsa Enable Alsa support (default on Linux)],
MOZ_ALSA=1,
MOZ_ALSA=)
if test -n "$MOZ_ALSA"; then
PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
[echo "$MOZ_ALSA_PKG_ERRORS"
- AC_MSG_ERROR([Need alsa for audio output on Linux. (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ AC_MSG_ERROR([You need ALSA for audio output on Linux.])])
fi
AC_SUBST(MOZ_ALSA)
@@ -3246,7 +3251,7 @@ if test -n "$MOZ_PULSEAUDIO"; then
if test -z "$gonkdir"; then
PKG_CHECK_MODULES(MOZ_PULSEAUDIO, libpulse, ,
[echo "$MOZ_PULSEAUDIO_PKG_ERRORS"
- AC_MSG_ERROR([pulseaudio audio backend requires libpulse development package])])
+ AC_MSG_ERROR([Building pulseaudio audio backend requires libpulse development package])])
else
MOZ_PULSEAUDIO_CFLAGS="-I$gonkdir/external/pulseaudio/pulseaudio/src"
fi
@@ -3312,9 +3317,6 @@ MOZ_D3D_CPU_SUFFIX=
MOZ_HAS_WINSDK_WITH_D3D=
MOZ_D3DCOMPILER_VISTA_DLL=
MOZ_D3DCOMPILER_VISTA_DLL_PATH=
-MOZ_DIRECTX_SDK_PATH=
-MOZ_D3DCOMPILER_XP_DLL=
-MOZ_D3DCOMPILER_XP_CAB=
if test "$COMPILE_ENVIRONMENT" ; then
case "$target_os" in
@@ -3333,11 +3335,6 @@ x86_64)
;;
esac
-MOZ_ARG_ENABLE_BOOL(require-all-d3dc-versions,
-[ --enable-require-all-d3dc-versions Require all versions of the D3D compiler needed for supported Windows systems.],
- MOZ_REQUIRE_ALL_D3DCS=1,
- MOZ_REQUIRE_ALL_D3DCS=)
-
# This is potentially set in external mozconfig files; if it's set,
# then the build exposes the "webgl" context name, which is reserved
# for conformant implementations.
@@ -3396,88 +3393,18 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
fi
######################################
- # Find _43 for use by XP.
-
- if test "$HAVE_64BIT_BUILD"; then
- AC_MSG_RESULT([We are building a 64-bit binary, skip checking d3dcompiler_43.])
- else
- # Get the SDK path from the registry.
- # First try to get the June 2010 SDK
- MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK (June 2010)' | head -n 1`
- if test -z "$MOZ_DIRECTX_SDK_REG_KEY" ; then
- # Otherwise just take whatever comes first
- MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK' | head -n 1`
- fi
- MOZ_DIRECTX_SDK_PATH=`reg query "$MOZ_DIRECTX_SDK_REG_KEY" //v InstallPath | grep REG_SZ | sed 's/.*\([[a-zA-Z]]\)\\:\\\\/\\1\\:\\\\/' | sed 's,\\\\,/,g'`
-
- if test -n "$MOZ_DIRECTX_SDK_PATH" &&
- test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/dxguid.lib ; then
- AC_MSG_RESULT([Found DirectX SDK via registry, using $MOZ_DIRECTX_SDK_PATH])
- else
- AC_MSG_RESULT([DirectX SDK not found.])
- MOZ_DIRECTX_SDK_PATH=
- fi
-
- # Check that our DirectX SDK is acceptable.
- if test -n "$MOZ_DIRECTX_SDK_PATH"; then
- if test -n "`echo $MOZ_DIRECTX_SDK_REG_KEY | grep 'February 2010'`" ; then
- AC_MSG_RESULT([Found the February 2010 DirectX SDK, which is unacceptable to ANGLE.])
- MOZ_DIRECTX_SDK_PATH=
- fi
- fi
-
- if test -n "$MOZ_DIRECTX_SDK_PATH"; then
- # Find a D3D compiler DLL in the DirectX SDK, if we didn't find one already.
- # Get the SDK numeric version (e.g. 43) by looking at the dependencies of d3dx9.lib
- MOZ_D3DX9_VERSION=`dumpbin //headers "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/d3dx9.lib | egrep d3dx9_[[0-9]][[0-9]]\.dll | head -n1 | sed 's/.*\([[0-9]][[0-9]]\).*/\\1/g'`
-
- if test -n "$MOZ_D3DX9_VERSION" ; then
- MOZ_D3DCOMPILER_XP_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *D3DCompiler_${MOZ_D3DX9_VERSION}_${MOZ_D3D_CPU_SUFFIX}.cab | head -n1`
-
- if test -n "$MOZ_D3DCOMPILER_XP_CAB"; then
- MOZ_D3DCOMPILER_XP_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
- else
- AC_MSG_RESULT([Couldn't find a CAB containing the D3D compiler DLL.])
- AC_MSG_ERROR([DirectX SDK at "$MOZ_DIRECTX_SDK_PATH" appears broken.])
- MOZ_DIRECTX_SDK_PATH=
- fi
- else
- AC_MSG_RESULT([Couldn't determine the D3DX9 version for the DirectX SDK.])
- MOZ_DIRECTX_SDK_PATH=
- fi
- else
- AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.])
- fi
- fi
-
- ######################################
# Check that we found what we needed.
MOZ_FOUND_A_D3D_COMPILER=
- MOZ_FOUND_BOTH_D3D_COMPILERS=1
if test -n "$MOZ_D3DCOMPILER_VISTA_DLL"; then
MOZ_FOUND_A_D3D_COMPILER=1
AC_MSG_RESULT([Found d3dcompiler DLL for Vista+: $MOZ_D3DCOMPILER_VISTA_DLL])
- else
- MOZ_FOUND_BOTH_D3D_COMPILERS=
- fi
-
- if test -n "$MOZ_D3DCOMPILER_XP_DLL"; then
- MOZ_FOUND_A_D3D_COMPILER=1
- AC_MSG_RESULT([Found d3dcompiler DLL for XP: $MOZ_D3DCOMPILER_XP_DLL])
- else
- MOZ_FOUND_BOTH_D3D_COMPILERS=
fi
if test -z "$CROSS_COMPILE"; then
if test -z "MOZ_FOUND_A_D3D_COMPILER"; then
AC_MSG_ERROR([Couldn't find an acceptable D3D compiler DLL.])
fi
-
- if test -n "$MOZ_REQUIRE_ALL_D3DCS" -a -z "$MOZ_FOUND_BOTH_D3D_COMPILERS"; then
- AC_MSG_ERROR([Both D3D compilers _43 and _46+ are required by --enable-require-d3d-compilers.])
- AC_MSG_ERROR([ Install Windows SDK 8.0+, as well as DirectX SDK (June 2010 version or newer), or reconfigure without this flag.])
- fi
fi
fi # MOZ_ANGLE_RENDERER
@@ -3928,6 +3855,11 @@ AC_SUBST(MOZ_NO_SMART_CARDS)
dnl ========================================================
dnl = Sandboxing support
dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(sandbox,
+[ --enable-sandbox Enable sandboxing support],
+ MOZ_SANDBOX=1,
+ MOZ_SANDBOX=)
+
if test -n "$MOZ_TSAN" -o -n "$MOZ_ASAN"; then
# Bug 1182565: TSan conflicts with sandboxing on Linux.
# Bug 1287971: LSan also conflicts with sandboxing on Linux.
@@ -3938,11 +3870,6 @@ if test -n "$MOZ_TSAN" -o -n "$MOZ_ASAN"; then
esac
fi
-MOZ_ARG_DISABLE_BOOL(sandbox,
-[ --disable-sandbox Disable sandboxing support],
- MOZ_SANDBOX=,
- MOZ_SANDBOX=1)
-
dnl ========================================================
dnl = Content process sandboxing
dnl ========================================================
@@ -4789,6 +4716,58 @@ dnl ========================================================
MOZ_ARG_HEADER(Misc. Options)
dnl ========================================================
+dnl = Disable Jetpack
+dnl ========================================================
+MOZ_ARG_DISABLE_BOOL(jetpack,
+[ --disable-jetpack Disable Jetpack Support Code],
+ MOZ_JETPACK=,
+ MOZ_JETPACK=1)
+
+if test -n "$MOZ_JETPACK"; then
+ AC_DEFINE(MOZ_JETPACK)
+fi
+
+AC_SUBST(MOZ_JETPACK)
+
+dnl ========================================================
+dnl = Disable Mozilla Developer Tools (server)
+dnl ========================================================
+MOZ_ARG_DISABLE_BOOL(devtools-server,
+[ --disable-devtools-server Disable Mozilla Developer Tools (server)],
+ MOZ_DEVTOOLS_SERVER=,
+ MOZ_DEVTOOLS_SERVER=1)
+
+
+
+if test -n "$MOZ_DEVTOOLS_SERVER"; then
+ if test ! -n "$MOZ_JETPACK"; then
+ AC_MSG_ERROR("Jetpack is required to include the Developer Tools Server")
+ fi
+
+ AC_DEFINE(MOZ_DEVTOOLS_SERVER)
+fi
+
+AC_SUBST(MOZ_DEVTOOLS_SERVER)
+
+dnl ========================================================
+dnl = Enable Mozilla Developer Tools (client)
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(devtools,
+[ --enable-devtools Enable Mozilla Developer Tools (client)],
+ MOZ_DEVTOOLS=1,
+ MOZ_DEVTOOLS=)
+
+if test -n "$MOZ_DEVTOOLS"; then
+ if test ! -n "$MOZ_DEVTOOLS_SERVER"; then
+ AC_MSG_ERROR("The Developer Tools Server is required to include the client")
+ fi
+
+ AC_DEFINE(MOZ_DEVTOOLS)
+fi
+
+AC_SUBST(MOZ_DEVTOOLS)
+
+dnl ========================================================
dnl = Define default location for MOZILLA_FIVE_HOME
dnl ========================================================
MOZ_ARG_WITH_STRING(default-mozilla-five-home,
@@ -5285,7 +5264,6 @@ AC_SUBST(MOZ_ANDROID_ANR_REPORTER)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_CRASHREPORTER_INJECTOR)
AC_SUBST(MOZ_MAINTENANCE_SERVICE)
-AC_SUBST(MOZ_STUB_INSTALLER)
AC_SUBST(MOZ_VERIFY_MAR_SIGNATURE)
AC_SUBST(MOZ_ENABLE_SIGNMAR)
AC_SUBST(MOZ_UPDATER)
@@ -5295,9 +5273,6 @@ AC_SUBST(MOZ_D3D_CPU_SUFFIX)
AC_SUBST(MOZ_HAS_WINSDK_WITH_D3D)
AC_SUBST(MOZ_D3DCOMPILER_VISTA_DLL)
AC_SUBST(MOZ_D3DCOMPILER_VISTA_DLL_PATH)
-AC_SUBST(MOZ_DIRECTX_SDK_PATH)
-AC_SUBST(MOZ_D3DCOMPILER_XP_DLL)
-AC_SUBST(MOZ_D3DCOMPILER_XP_CAB)
AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
@@ -5489,6 +5464,9 @@ fi
AC_SUBST(MOZ_APP_STATIC_INI)
+AC_DEFINE_UNQUOTED(MOZ_UA_BUILDID, "$MOZ_UA_BUILDID")
+AC_SUBST(MOZ_UA_BUILDID)
+
AC_SUBST(MOZ_PKG_SPECIAL)
AC_SUBST(MOZ_SIMPLE_PACKAGE_NAME)
@@ -5671,7 +5649,6 @@ AC_SUBST(LIBJPEG_TURBO_USE_YASM)
AC_SUBST_LIST(LIBJPEG_TURBO_ASFLAGS)
AC_SUBST(MOZ_LIBAV_FFT)
AC_SUBST_LIST(LIBAV_FFT_ASFLAGS)
-AC_SUBST(MOZ_DEVTOOLS)
AC_SUBST(MOZ_PACKAGE_JSSHELL)
AC_SUBST(MOZ_FOLD_LIBS)
diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py
index b6802a47c..ae71d0868 100644
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1076,6 +1076,24 @@ class Package(MachCommandBase):
return ret
@CommandProvider
+class Installer(MachCommandBase):
+ """Create the windows installer for the built product."""
+
+ @Command('installer', category='post-build',
+ description='Create the installer for the built product for distribution.')
+ def installer(self):
+ return self._run_make(directory=".", target='installer', ensure_exit_code=False)
+
+@CommandProvider
+class Mar(MachCommandBase):
+ """Create the mar file for the built product."""
+
+ @Command('mar', category='post-build',
+ description='Create the mar file for the built product for distribution.')
+ def mar(self):
+ return self._run_make(directory="./tools/update-packaging/", target='', ensure_exit_code=False)
+
+@CommandProvider
class Install(MachCommandBase):
"""Install a package."""
diff --git a/python/mozbuild/mozbuild/milestone.py b/python/mozbuild/mozbuild/milestone.py
index c2aa78fcd..3ef55d540 100644
--- a/python/mozbuild/mozbuild/milestone.py
+++ b/python/mozbuild/mozbuild/milestone.py
@@ -43,6 +43,12 @@ def get_milestone_major(milestone):
return milestone.split('.')[0]
+def get_milestone_minor(milestone):
+ """
+ Returns the minor (second) part of the milestone.
+ """
+
+ return milestone.split('.')[1]
def main(args):
parser = argparse.ArgumentParser()
@@ -56,9 +62,9 @@ def main(args):
milestone = get_official_milestone(milestone_file)
if options.uaversion:
- # Only expose the major milestone in the UA string, hide the patch
- # level (bugs 572659 and 870868).
- uaversion = "%s.0" % (get_milestone_major(milestone),)
+ # Only expose the major+minor milestone in the UA string.
+ uaversion = "%s.%s" % (get_milestone_major(milestone),
+ get_milestone_minor(milestone))
print(uaversion)
elif options.symbolversion:
diff --git a/security/nss/.taskcluster.yml b/security/nss/.taskcluster.yml
index 9d56c9bcd..494d31a7b 100644
--- a/security/nss/.taskcluster.yml
+++ b/security/nss/.taskcluster.yml
@@ -57,7 +57,7 @@ tasks:
- "tc-treeherder.v2.{{project}}.{{revision}}.{{pushlog_id}}"
payload:
- image: ttaubert/nss-decision:0.0.2
+ image: nssdev/nss-decision:0.0.2
env:
TC_OWNER: {{owner}}
diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO
deleted file mode 100644
index 2ff04f990..000000000
--- a/security/nss/TAG-INFO
+++ /dev/null
@@ -1 +0,0 @@
-NSS_3_32_1_RTM
diff --git a/security/nss/automation/abi-check/expected-report-libnspr4.so.txt b/security/nss/automation/abi-check/expected-report-libnspr4.so.txt
index e69de29bb..44f52325f 100644
--- a/security/nss/automation/abi-check/expected-report-libnspr4.so.txt
+++ b/security/nss/automation/abi-check/expected-report-libnspr4.so.txt
@@ -0,0 +1,8 @@
+Functions changes summary: 1 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 Removed function:
+
+ 'function void PR_EXPERIMENTAL_ONLY_IN_4_17_GetOverlappedIOHandle(void**)' {PR_EXPERIMENTAL_ONLY_IN_4_17_GetOverlappedIOHandle}
+
+
diff --git a/security/nss/automation/abi-check/expected-report-libssl3.so.txt b/security/nss/automation/abi-check/expected-report-libssl3.so.txt
index e69de29bb..2a093094f 100644
--- a/security/nss/automation/abi-check/expected-report-libssl3.so.txt
+++ b/security/nss/automation/abi-check/expected-report-libssl3.so.txt
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed, 0 Changed (5 filtered out), 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
diff --git a/security/nss/automation/abi-check/previous-nss-release b/security/nss/automation/abi-check/previous-nss-release
index b8d28cde0..a91a569f5 100644
--- a/security/nss/automation/abi-check/previous-nss-release
+++ b/security/nss/automation/abi-check/previous-nss-release
@@ -1 +1 @@
-NSS_3_31_BRANCH
+NSS_3_34_BRANCH
diff --git a/security/nss/automation/buildbot-slave/build.sh b/security/nss/automation/buildbot-slave/build.sh
index 3fc914803..844254dae 100755
--- a/security/nss/automation/buildbot-slave/build.sh
+++ b/security/nss/automation/buildbot-slave/build.sh
@@ -236,11 +236,14 @@ check_abi()
BASE_NSPR=NSPR_$(head -1 ${HGDIR}/baseline/nss/automation/release/nspr-version.txt | cut -d . -f 1-2 | tr . _)_BRANCH
hg clone -u "${BASE_NSPR}" "${HGDIR}/nspr" "${HGDIR}/baseline/nspr"
if [ $? -ne 0 ]; then
- echo "invalid tag ${BASE_NSPR} derived from ${BASE_NSS} automation/release/nspr-version.txt"
- return 1
+ echo "nonexisting tag ${BASE_NSPR} derived from ${BASE_NSS} automation/release/nspr-version.txt"
+ # Assume that version hasn't been released yet, fall back to trunk
+ pushd "${HGDIR}/baseline/nspr"
+ hg update default
+ popd
fi
- print_log "######## building older NSPR/NSS ########"
+ print_log "######## building baseline NSPR/NSS ########"
pushd ${HGDIR}/baseline/nss
print_log "$ ${MAKE} ${NSS_BUILD_TARGET}"
@@ -253,26 +256,44 @@ check_abi()
fi
popd
+ ABI_PROBLEM_FOUND=0
ABI_REPORT=${OUTPUTDIR}/abi-diff.txt
rm -f ${ABI_REPORT}
PREVDIST=${HGDIR}/baseline/dist
NEWDIST=${HGDIR}/dist
ALL_SOs="libfreebl3.so libfreeblpriv3.so libnspr4.so libnss3.so libnssckbi.so libnssdbm3.so libnsssysinit.so libnssutil3.so libplc4.so libplds4.so libsmime3.so libsoftokn3.so libssl3.so"
for SO in ${ALL_SOs}; do
- if [ ! -f nss/automation/abi-check/expected-report-$SO.txt ]; then
- touch nss/automation/abi-check/expected-report-$SO.txt
+ if [ ! -f ${HGDIR}/nss/automation/abi-check/expected-report-$SO.txt ]; then
+ touch ${HGDIR}/nss/automation/abi-check/expected-report-$SO.txt
fi
abidiff --hd1 $PREVDIST/public/ --hd2 $NEWDIST/public \
$PREVDIST/*/lib/$SO $NEWDIST/*/lib/$SO \
- > nss/automation/abi-check/new-report-$SO.txt
- diff -u nss/automation/abi-check/expected-report-$SO.txt \
- nss/automation/abi-check/new-report-$SO.txt >> ${ABI_REPORT}
+ > ${HGDIR}/nss/automation/abi-check/new-report-$SO.txt
+ if [ $? -ne 0 ]; then
+ ABI_PROBLEM_FOUND=1
+ print_log "FAILED to run abidiff {$PREVDIST , $NEWDIST} for $SO, or failed writing to ${HGDIR}/nss/automation/abi-check/new-report-$SO.txt"
+ fi
+ if [ ! -f ${HGDIR}/nss/automation/abi-check/expected-report-$SO.txt ]; then
+ ABI_PROBLEM_FOUND=1
+ print_log "FAILED to access report file: ${HGDIR}/nss/automation/abi-check/expected-report-$SO.txt"
+ fi
+
+ diff -wB -u ${HGDIR}/nss/automation/abi-check/expected-report-$SO.txt \
+ ${HGDIR}/nss/automation/abi-check/new-report-$SO.txt >> ${ABI_REPORT}
+ if [ ! -f ${ABI_REPORT} ]; then
+ ABI_PROBLEM_FOUND=1
+ print_log "FAILED to compare exepcted and new report: ${HGDIR}/nss/automation/abi-check/new-report-$SO.txt"
+ fi
done
if [ -s ${ABI_REPORT} ]; then
print_log "FAILED: there are new unexpected ABI changes"
cat ${ABI_REPORT}
return 1
+ elif [ $ABI_PROBLEM_FOUND -ne 0 ]; then
+ print_log "FAILED: failure executing the ABI checks"
+ cat ${ABI_REPORT}
+ return 1
fi
return 0
diff --git a/security/nss/automation/clang-format/run_clang_format.sh b/security/nss/automation/clang-format/run_clang_format.sh
index 2ba5ebeb1..378b00ff0 100644
--- a/security/nss/automation/clang-format/run_clang_format.sh
+++ b/security/nss/automation/clang-format/run_clang_format.sh
@@ -6,6 +6,8 @@ if [[ $(id -u) -eq 0 ]]; then
exec su worker -c "$0 $*"
fi
+set -e
+
# Apply clang-format on the provided folder and verify that this doesn't change any file.
# If any file differs after formatting, the script eventually exits with 1.
# Any differences between formatted and unformatted files is printed to stdout to give a hint what's wrong.
@@ -21,17 +23,16 @@ blacklist=(
"./lib/zlib" \
"./lib/sqlite" \
"./gtests/google_test" \
- "./.hg" \
"./out" \
)
-top="$(dirname $0)/../.."
-cd "$top"
+top=$(cd "$(dirname $0)/../.."; pwd -P)
if [ $# -gt 0 ]; then
dirs=("$@")
else
- dirs=($(find . -maxdepth 2 -mindepth 1 -type d ! -path . \( ! -regex '.*/' \)))
+ cd "$top"
+ dirs=($(find . -maxdepth 2 -mindepth 1 -type d ! -path '*/.*' -print))
fi
format_folder()
@@ -46,20 +47,20 @@ format_folder()
}
for dir in "${dirs[@]}"; do
- if format_folder "$dir" ; then
+ if format_folder "$dir"; then
c="${dir//[^\/]}"
echo "formatting $dir ..."
- depth=""
+ depth=()
if [ "${#c}" == "1" ]; then
- depth="-maxdepth 1"
+ depth+=(-maxdepth 1)
fi
- find "$dir" $depth -type f \( -name '*.[ch]' -o -name '*.cc' \) -exec clang-format -i {} \+
+ find "$dir" "${depth[@]}" -type f \( -name '*.[ch]' -o -name '*.cc' \) -exec clang-format -i {} \+
fi
done
TMPFILE=$(mktemp /tmp/$(basename $0).XXXXXX)
-trap 'rm $TMPFILE' exit
-if (cd $(dirname $0); hg root >/dev/null 2>&1); then
+trap 'rm -f $TMPFILE' exit
+if [[ -d "$top/.hg" ]]; then
hg diff --git "$top" | tee $TMPFILE
else
git -C "$top" diff | tee $TMPFILE
diff --git a/security/nss/automation/clang-format/setup.sh b/security/nss/automation/clang-format/setup.sh
index 9b2480e90..beac9e905 100644
--- a/security/nss/automation/clang-format/setup.sh
+++ b/security/nss/automation/clang-format/setup.sh
@@ -17,8 +17,8 @@ apt_packages+=('locales')
apt-get install -y --no-install-recommends ${apt_packages[@]}
# Download clang.
-curl -L http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz -o clang.tar.xz
-curl -L http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig -o clang.tar.xz.sig
+curl -L https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz -o clang.tar.xz
+curl -L https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig -o clang.tar.xz.sig
# Verify the signature.
gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
gpg --verify clang.tar.xz.sig
diff --git a/security/nss/automation/release/nspr-version.txt b/security/nss/automation/release/nspr-version.txt
index 98783a615..01eeb3615 100644
--- a/security/nss/automation/release/nspr-version.txt
+++ b/security/nss/automation/release/nspr-version.txt
@@ -1,4 +1,4 @@
-4.16
+4.18
# The first line of this file must contain the human readable NSPR
# version number, which is the minimum required version of NSPR
diff --git a/security/nss/automation/taskcluster/docker-clang-3.9/setup.sh b/security/nss/automation/taskcluster/docker-clang-3.9/setup.sh
index 7b7d534e6..3076667a6 100644
--- a/security/nss/automation/taskcluster/docker-clang-3.9/setup.sh
+++ b/security/nss/automation/taskcluster/docker-clang-3.9/setup.sh
@@ -25,8 +25,8 @@ apt-get -y update
apt-get install -y --no-install-recommends ${apt_packages[@]}
# Download clang.
-curl -LO http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
-curl -LO http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
+curl -LO https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+curl -LO https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
# Verify the signature.
gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
gpg --verify *.tar.xz.sig
diff --git a/security/nss/automation/taskcluster/docker-decision/Dockerfile b/security/nss/automation/taskcluster/docker-decision/Dockerfile
index 35777c0b7..473ce64ba 100644
--- a/security/nss/automation/taskcluster/docker-decision/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-decision/Dockerfile
@@ -12,6 +12,9 @@ RUN chmod +x /home/worker/bin/*
ADD setup.sh /tmp/setup.sh
RUN bash /tmp/setup.sh
+# Change user.
+USER worker
+
# Env variables.
ENV HOME /home/worker
ENV SHELL /bin/bash
diff --git a/security/nss/automation/taskcluster/docker-decision/bin/checkout.sh b/security/nss/automation/taskcluster/docker-decision/bin/checkout.sh
index 9167f6bda..0cdd2ac40 100644
--- a/security/nss/automation/taskcluster/docker-decision/bin/checkout.sh
+++ b/security/nss/automation/taskcluster/docker-decision/bin/checkout.sh
@@ -2,11 +2,6 @@
set -v -e -x
-if [ $(id -u) = 0 ]; then
- # Drop privileges by re-running this script.
- exec su worker $0
-fi
-
# Default values for testing.
REVISION=${NSS_HEAD_REVISION:-default}
REPOSITORY=${NSS_HEAD_REPOSITORY:-https://hg.mozilla.org/projects/nss}
diff --git a/security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile b/security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile
new file mode 100644
index 000000000..3330c007f
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile
@@ -0,0 +1,30 @@
+FROM ubuntu:14.04
+MAINTAINER Tim Taubert <ttaubert@mozilla.com>
+
+RUN useradd -d /home/worker -s /bin/bash -m worker
+WORKDIR /home/worker
+
+# Add build and test scripts.
+ADD bin /home/worker/bin
+RUN chmod +x /home/worker/bin/*
+
+# Install dependencies.
+ADD setup.sh /tmp/setup.sh
+RUN bash /tmp/setup.sh
+
+# Change user.
+USER worker
+
+# Env variables.
+ENV HOME /home/worker
+ENV SHELL /bin/bash
+ENV USER worker
+ENV LOGNAME worker
+ENV HOSTNAME taskcluster-worker
+ENV LANG en_US.UTF-8
+ENV LC_ALL en_US.UTF-8
+ENV HOST localhost
+ENV DOMSUF localdomain
+
+# Set a default command for debugging.
+CMD ["/bin/bash", "--login"]
diff --git a/security/nss/automation/taskcluster/docker-gcc-4.4/bin/checkout.sh b/security/nss/automation/taskcluster/docker-gcc-4.4/bin/checkout.sh
new file mode 100644
index 000000000..9167f6bda
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-gcc-4.4/bin/checkout.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+if [ $(id -u) = 0 ]; then
+ # Drop privileges by re-running this script.
+ exec su worker $0
+fi
+
+# Default values for testing.
+REVISION=${NSS_HEAD_REVISION:-default}
+REPOSITORY=${NSS_HEAD_REPOSITORY:-https://hg.mozilla.org/projects/nss}
+
+# Clone NSS.
+for i in 0 2 5; do
+ sleep $i
+ hg clone -r $REVISION $REPOSITORY nss && exit 0
+ rm -rf nss
+done
+exit 1
diff --git a/security/nss/automation/taskcluster/docker-gcc-4.4/setup.sh b/security/nss/automation/taskcluster/docker-gcc-4.4/setup.sh
new file mode 100644
index 000000000..f6325d966
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-gcc-4.4/setup.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+# Update packages.
+export DEBIAN_FRONTEND=noninteractive
+apt-get -y update && apt-get -y upgrade
+
+apt_packages=()
+apt_packages+=('ca-certificates')
+apt_packages+=('g++-4.4')
+apt_packages+=('gcc-4.4')
+apt_packages+=('locales')
+apt_packages+=('make')
+apt_packages+=('mercurial')
+apt_packages+=('zlib1g-dev')
+
+# Install packages.
+apt-get -y update
+apt-get install -y --no-install-recommends ${apt_packages[@]}
+
+locale-gen en_US.UTF-8
+dpkg-reconfigure locales
+
+# Cleanup.
+rm -rf ~/.ccache ~/.cache
+apt-get autoremove -y
+apt-get clean
+apt-get autoclean
+rm $0
diff --git a/security/nss/automation/taskcluster/docker-hacl/Dockerfile b/security/nss/automation/taskcluster/docker-hacl/Dockerfile
new file mode 100644
index 000000000..e8a88f06c
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-hacl/Dockerfile
@@ -0,0 +1,30 @@
+FROM ubuntu:xenial
+
+MAINTAINER Franziskus Kiefer <franziskuskiefer@gmail.com>
+# Based on the HACL* image from Benjamin Beurdouche and
+# the original F* formula with Daniel Fabian
+
+# Pinned versions of HACL* (F* and KreMLin are pinned as submodules)
+ENV haclrepo https://github.com/mitls/hacl-star.git
+
+# Define versions of dependencies
+ENV opamv 4.04.2
+ENV haclversion dcd48329d535727dbde93877b124c5ec4a7a2b20
+
+# Install required packages and set versions
+ADD setup.sh /tmp/setup.sh
+RUN bash /tmp/setup.sh
+
+# Create user, add scripts.
+RUN useradd -ms /bin/bash worker
+WORKDIR /home/worker
+ADD bin /home/worker/bin
+RUN chmod +x /home/worker/bin/*
+USER worker
+
+# Build F*, HACL*, verify. Install a few more dependencies.
+ENV OPAMYES true
+ENV PATH "/home/worker/hacl-star/dependencies/z3/bin:$PATH"
+ADD setup-user.sh /tmp/setup-user.sh
+ADD license.txt /tmp/license.txt
+RUN bash /tmp/setup-user.sh
diff --git a/security/nss/automation/taskcluster/docker-hacl/bin/checkout.sh b/security/nss/automation/taskcluster/docker-hacl/bin/checkout.sh
new file mode 100644
index 000000000..9167f6bda
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-hacl/bin/checkout.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+if [ $(id -u) = 0 ]; then
+ # Drop privileges by re-running this script.
+ exec su worker $0
+fi
+
+# Default values for testing.
+REVISION=${NSS_HEAD_REVISION:-default}
+REPOSITORY=${NSS_HEAD_REPOSITORY:-https://hg.mozilla.org/projects/nss}
+
+# Clone NSS.
+for i in 0 2 5; do
+ sleep $i
+ hg clone -r $REVISION $REPOSITORY nss && exit 0
+ rm -rf nss
+done
+exit 1
diff --git a/security/nss/automation/taskcluster/docker-hacl/license.txt b/security/nss/automation/taskcluster/docker-hacl/license.txt
new file mode 100644
index 000000000..03d25c4d3
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-hacl/license.txt
@@ -0,0 +1,15 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
diff --git a/security/nss/automation/taskcluster/docker-hacl/setup-user.sh b/security/nss/automation/taskcluster/docker-hacl/setup-user.sh
new file mode 100644
index 000000000..b8accaf58
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-hacl/setup-user.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+# Prepare build (OCaml packages)
+opam init
+echo ". /home/worker/.opam/opam-init/init.sh > /dev/null 2> /dev/null || true" >> .bashrc
+opam switch -v ${opamv}
+opam install ocamlfind batteries sqlite3 fileutils yojson ppx_deriving_yojson zarith pprint menhir ulex process fix wasm stdint
+
+# Get the HACL* code
+git clone ${haclrepo} hacl-star
+git -C hacl-star checkout ${haclversion}
+
+# Prepare submodules, and build, verify, test, and extract c code
+# This caches the extracted c code (pins the HACL* version). All we need to do
+# on CI now is comparing the code in this docker image with the one in NSS.
+opam config exec -- make -C hacl-star prepare -j$(nproc)
+make -C hacl-star verify-nss -j$(nproc)
+make -C hacl-star -f Makefile.build snapshots/nss -j$(nproc)
+KOPTS="-funroll-loops 5" make -C hacl-star/code/curve25519 test -j$(nproc)
+make -C hacl-star/code/salsa-family test -j$(nproc)
+make -C hacl-star/code/poly1305 test -j$(nproc)
+
+# Cleanup.
+rm -rf ~/.ccache ~/.cache
diff --git a/security/nss/automation/taskcluster/docker-hacl/setup.sh b/security/nss/automation/taskcluster/docker-hacl/setup.sh
new file mode 100644
index 000000000..f5f8bd7d5
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-hacl/setup.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+# Update packages.
+export DEBIAN_FRONTEND=noninteractive
+apt-get -qq update
+apt-get install --yes libssl-dev libsqlite3-dev g++-5 gcc-5 m4 make opam pkg-config python libgmp3-dev cmake curl libtool-bin autoconf wget locales
+update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 200
+update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-5 200
+
+# Get clang-format-3.9
+curl -LO https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+curl -LO https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
+# Verify the signature.
+gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
+gpg --verify *.tar.xz.sig
+# Install into /usr/local/.
+tar xJvf *.tar.xz -C /usr/local --strip-components=1
+# Cleanup.
+rm *.tar.xz*
+
+locale-gen en_US.UTF-8
+dpkg-reconfigure locales
+
+# Cleanup.
+rm -rf ~/.ccache ~/.cache
+apt-get autoremove -y
+apt-get clean
+apt-get autoclean
diff --git a/security/nss/automation/taskcluster/docker/setup.sh b/security/nss/automation/taskcluster/docker/setup.sh
index 3ba4e854e..01f9c413a 100644
--- a/security/nss/automation/taskcluster/docker/setup.sh
+++ b/security/nss/automation/taskcluster/docker/setup.sh
@@ -48,8 +48,8 @@ apt-get -y update
apt-get install -y --no-install-recommends ${apt_packages[@]}
# Download clang.
-curl -LO http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
-curl -LO http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
+curl -LO https://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+curl -LO https://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
# Verify the signature.
gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
gpg --verify *.tar.xz.sig
diff --git a/security/nss/automation/taskcluster/graph/src/context_hash.js b/security/nss/automation/taskcluster/graph/src/context_hash.js
index f0a2e9a88..0699a0590 100644
--- a/security/nss/automation/taskcluster/graph/src/context_hash.js
+++ b/security/nss/automation/taskcluster/graph/src/context_hash.js
@@ -27,14 +27,24 @@ function collectFilesInDirectory(dir) {
});
}
-// Compute a context hash for the given context path.
-export default function (context_path) {
+// A list of hashes for each file in the given path.
+function collectFileHashes(context_path) {
let root = path.join(__dirname, "../../../..");
let dir = path.join(root, context_path);
let files = collectFilesInDirectory(dir).sort();
- let hashes = files.map(file => {
+
+ return files.map(file => {
return sha256(file + "|" + fs.readFileSync(file, "utf-8"));
});
+}
+
+// Compute a context hash for the given context path.
+export default function (context_path) {
+ // Regenerate all images when the image_builder changes.
+ let hashes = collectFileHashes("automation/taskcluster/image_builder");
+
+ // Regenerate images when the image itself changes.
+ hashes = hashes.concat(collectFileHashes(context_path));
// Generate a new prefix every month to ensure the image stays buildable.
let now = new Date();
diff --git a/security/nss/automation/taskcluster/graph/src/extend.js b/security/nss/automation/taskcluster/graph/src/extend.js
index d541a1a3b..90e23ae60 100644
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -15,15 +15,29 @@ const LINUX_CLANG39_IMAGE = {
path: "automation/taskcluster/docker-clang-3.9"
};
+const LINUX_GCC44_IMAGE = {
+ name: "linux-gcc-4.4",
+ path: "automation/taskcluster/docker-gcc-4.4"
+};
+
const FUZZ_IMAGE = {
name: "fuzz",
path: "automation/taskcluster/docker-fuzz"
};
+const HACL_GEN_IMAGE = {
+ name: "hacl",
+ path: "automation/taskcluster/docker-hacl"
+};
+
const WINDOWS_CHECKOUT_CMD =
"bash -c \"hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss || " +
"(sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || " +
"(sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)\"";
+const MAC_CHECKOUT_CMD = ["bash", "-c",
+ "hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss || " +
+ "(sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || " +
+ "(sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)"];
/*****************************************************************************/
@@ -51,6 +65,15 @@ queue.filter(task => {
if (task.platform == "aarch64") {
return false;
}
+
+ // No mac
+ if (task.platform == "mac") {
+ return false;
+ }
+ }
+
+ if (task.tests == "fips" && task.platform == "mac") {
+ return false;
}
// Only old make builds have -Ddisable_libpkix=0 and can run chain tests.
@@ -59,8 +82,8 @@ queue.filter(task => {
}
if (task.group == "Test") {
- // Don't run test builds on old make platforms
- if (task.collection == "make") {
+ // Don't run test builds on old make platforms, and not for fips gyp.
+ if (task.collection == "make" || task.collection == "fips") {
return false;
}
}
@@ -78,11 +101,19 @@ queue.filter(task => {
queue.map(task => {
if (task.collection == "asan") {
// CRMF and FIPS tests still leak, unfortunately.
- if (task.tests == "crmf" || task.tests == "fips") {
+ if (task.tests == "crmf") {
task.env.ASAN_OPTIONS = "detect_leaks=0";
}
}
+ // We don't run FIPS SSL tests
+ if (task.tests == "ssl") {
+ if (!task.env) {
+ task.env = {};
+ }
+ task.env.NSS_SSL_TESTS = "crl iopr policy";
+ }
+
// Windows is slow.
if (task.platform == "windows2012-64" && task.tests == "chains") {
task.maxRunTime = 7200;
@@ -128,6 +159,18 @@ export default async function main() {
],
});
+ await scheduleLinux("Linux 64 (opt, make)", {
+ env: {USE_64: "1", BUILD_OPT: "1"},
+ platform: "linux64",
+ image: LINUX_IMAGE,
+ collection: "make",
+ command: [
+ "/bin/bash",
+ "-c",
+ "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh"
+ ],
+ });
+
await scheduleLinux("Linux 32 (debug, make)", {
platform: "linux32",
image: LINUX_IMAGE,
@@ -153,6 +196,12 @@ export default async function main() {
features: ["allowPtrace"],
}, "--ubsan --asan");
+ await scheduleLinux("Linux 64 (FIPS opt)", {
+ platform: "linux64",
+ collection: "fips",
+ image: LINUX_IMAGE,
+ }, "--enable-fips --opt");
+
await scheduleWindows("Windows 2012 64 (debug, make)", {
platform: "windows2012-64",
collection: "make",
@@ -216,6 +265,70 @@ export default async function main() {
collection: "opt",
}, aarch64_base)
);
+
+ await scheduleMac("Mac (opt)", {collection: "opt"}, "--opt");
+ await scheduleMac("Mac (debug)", {collection: "debug"});
+}
+
+
+async function scheduleMac(name, base, args = "") {
+ let mac_base = merge(base, {
+ env: {
+ PATH: "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin",
+ NSS_TASKCLUSTER_MAC: "1",
+ DOMSUF: "localdomain",
+ HOST: "localhost",
+ },
+ provisioner: "localprovisioner",
+ workerType: "nss-macos-10-12",
+ platform: "mac"
+ });
+
+ // Build base definition.
+ let build_base = merge({
+ command: [
+ MAC_CHECKOUT_CMD,
+ ["bash", "-c",
+ "nss/automation/taskcluster/scripts/build_gyp.sh", args]
+ ],
+ provisioner: "localprovisioner",
+ workerType: "nss-macos-10-12",
+ platform: "mac",
+ maxRunTime: 7200,
+ artifacts: [{
+ expires: 24 * 7,
+ type: "directory",
+ path: "public"
+ }],
+ kind: "build",
+ symbol: "B"
+ }, mac_base);
+
+ // The task that builds NSPR+NSS.
+ let task_build = queue.scheduleTask(merge(build_base, {name}));
+
+ // The task that generates certificates.
+ let task_cert = queue.scheduleTask(merge(build_base, {
+ name: "Certificates",
+ command: [
+ MAC_CHECKOUT_CMD,
+ ["bash", "-c",
+ "nss/automation/taskcluster/scripts/gen_certs.sh"]
+ ],
+ parent: task_build,
+ symbol: "Certs"
+ }));
+
+ // Schedule tests.
+ scheduleTests(task_build, task_cert, merge(mac_base, {
+ command: [
+ MAC_CHECKOUT_CMD,
+ ["bash", "-c",
+ "nss/automation/taskcluster/scripts/run_tests.sh"]
+ ]
+ }));
+
+ return queue.submit();
}
/*****************************************************************************/
@@ -242,6 +355,45 @@ async function scheduleLinux(name, base, args = "") {
// The task that builds NSPR+NSS.
let task_build = queue.scheduleTask(merge(build_base, {name}));
+ // Make builds run FIPS tests, which need an extra FIPS build.
+ if (base.collection == "make") {
+ let extra_build = queue.scheduleTask(merge(build_base, {
+ env: { NSS_FORCE_FIPS: "1" },
+ group: "FIPS",
+ name: `${name} w/ NSS_FORCE_FIPS`
+ }));
+
+ // The task that generates certificates.
+ let task_cert = queue.scheduleTask(merge(build_base, {
+ name: "Certificates",
+ command: [
+ "/bin/bash",
+ "-c",
+ "bin/checkout.sh && nss/automation/taskcluster/scripts/gen_certs.sh"
+ ],
+ parent: extra_build,
+ symbol: "Certs-F",
+ group: "FIPS",
+ }));
+
+ // Schedule FIPS tests.
+ queue.scheduleTask(merge(base, {
+ parent: task_cert,
+ name: "FIPS",
+ command: [
+ "/bin/bash",
+ "-c",
+ "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
+ ],
+ cycle: "standard",
+ kind: "test",
+ name: "FIPS tests",
+ symbol: "Tests-F",
+ tests: "fips",
+ group: "FIPS"
+ }));
+ }
+
// The task that generates certificates.
let task_cert = queue.scheduleTask(merge(build_base, {
name: "Certificates",
@@ -275,6 +427,26 @@ async function scheduleLinux(name, base, args = "") {
}));
queue.scheduleTask(merge(extra_base, {
+ name: `${name} w/ gcc-4.4`,
+ image: LINUX_GCC44_IMAGE,
+ env: {
+ USE_64: "1",
+ CC: "gcc-4.4",
+ CCC: "g++-4.4",
+ // gcc-4.6 introduced nullptr.
+ NSS_DISABLE_GTESTS: "1",
+ },
+ // Use the old Makefile-based build system, GYP doesn't have a proper GCC
+ // version check for __int128 support. It's mainly meant to cover RHEL6.
+ command: [
+ "/bin/bash",
+ "-c",
+ "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh",
+ ],
+ symbol: "gcc-4.4"
+ }));
+
+ queue.scheduleTask(merge(extra_base, {
name: `${name} w/ gcc-4.8`,
env: {
CC: "gcc-4.8",
@@ -403,12 +575,13 @@ async function scheduleFuzzing() {
// Schedule MPI fuzzing runs.
let mpi_base = merge(run_base, {group: "MPI"});
- let mpi_names = ["add", "addmod", "div", "expmod", "mod", "mulmod", "sqr",
+ let mpi_names = ["add", "addmod", "div", "mod", "mulmod", "sqr",
"sqrmod", "sub", "submod"];
for (let name of mpi_names) {
scheduleFuzzingRun(mpi_base, `MPI (${name})`, `mpi-${name}`, 4096, name);
}
scheduleFuzzingRun(mpi_base, `MPI (invmod)`, `mpi-invmod`, 256, "invmod");
+ scheduleFuzzingRun(mpi_base, `MPI (expmod)`, `mpi-expmod`, 2048, "expmod");
// Schedule TLS fuzzing runs (non-fuzzing mode).
let tls_base = merge(run_base, {group: "TLS"});
@@ -625,6 +798,43 @@ async function scheduleWindows(name, base, build_script) {
symbol: "B"
});
+ // Make builds run FIPS tests, which need an extra FIPS build.
+ if (base.collection == "make") {
+ let extra_build = queue.scheduleTask(merge(build_base, {
+ env: { NSS_FORCE_FIPS: "1" },
+ group: "FIPS",
+ name: `${name} w/ NSS_FORCE_FIPS`
+ }));
+
+ // The task that generates certificates.
+ let task_cert = queue.scheduleTask(merge(build_base, {
+ name: "Certificates",
+ command: [
+ WINDOWS_CHECKOUT_CMD,
+ "bash -c nss/automation/taskcluster/windows/gen_certs.sh"
+ ],
+ parent: extra_build,
+ symbol: "Certs-F",
+ group: "FIPS",
+ }));
+
+ // Schedule FIPS tests.
+ queue.scheduleTask(merge(base, {
+ parent: task_cert,
+ name: "FIPS",
+ command: [
+ WINDOWS_CHECKOUT_CMD,
+ "bash -c nss/automation/taskcluster/windows/run_tests.sh"
+ ],
+ cycle: "standard",
+ kind: "test",
+ name: "FIPS tests",
+ symbol: "Tests-F",
+ tests: "fips",
+ group: "FIPS"
+ }));
+ }
+
// The task that builds NSPR+NSS.
let task_build = queue.scheduleTask(merge(build_base, {name}));
@@ -703,9 +913,6 @@ function scheduleTests(task_build, task_cert, test_base) {
name: "DB tests", symbol: "DB", tests: "dbtests"
}));
queue.scheduleTask(merge(cert_base, {
- name: "FIPS tests", symbol: "FIPS", tests: "fips"
- }));
- queue.scheduleTask(merge(cert_base, {
name: "Merge tests", symbol: "Merge", tests: "merge"
}));
queue.scheduleTask(merge(cert_base, {
@@ -773,5 +980,16 @@ async function scheduleTools() {
]
}));
+ queue.scheduleTask(merge(base, {
+ symbol: "hacl",
+ name: "hacl",
+ image: HACL_GEN_IMAGE,
+ command: [
+ "/bin/bash",
+ "-c",
+ "bin/checkout.sh && nss/automation/taskcluster/scripts/run_hacl.sh"
+ ]
+ }));
+
return queue.submit();
}
diff --git a/security/nss/automation/taskcluster/graph/src/image_builder.js b/security/nss/automation/taskcluster/graph/src/image_builder.js
index bc90e0242..b89b6980c 100644
--- a/security/nss/automation/taskcluster/graph/src/image_builder.js
+++ b/security/nss/automation/taskcluster/graph/src/image_builder.js
@@ -31,13 +31,11 @@ export async function buildTask({name, path}) {
return {
name: "Image Builder",
- image: "taskcluster/image_builder:0.1.5",
+ image: "nssdev/image_builder:0.1.5",
routes: ["index." + ns],
env: {
- HEAD_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
- BASE_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
- HEAD_REV: process.env.NSS_HEAD_REVISION,
- HEAD_REF: process.env.NSS_HEAD_REVISION,
+ NSS_HEAD_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
+ NSS_HEAD_REVISION: process.env.NSS_HEAD_REVISION,
PROJECT: process.env.TC_PROJECT,
CONTEXT_PATH: path,
HASH: hash
@@ -52,10 +50,11 @@ export async function buildTask({name, path}) {
command: [
"/bin/bash",
"-c",
- "/home/worker/bin/build_image.sh"
+ "bin/checkout.sh && nss/automation/taskcluster/scripts/build_image.sh"
],
platform: "nss-decision",
features: ["dind"],
+ maxRunTime: 7200,
kind: "build",
symbol: "I"
};
diff --git a/security/nss/automation/taskcluster/graph/src/try_syntax.js b/security/nss/automation/taskcluster/graph/src/try_syntax.js
index 7748e068a..1f4e12eee 100644
--- a/security/nss/automation/taskcluster/graph/src/try_syntax.js
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -22,10 +22,10 @@ function parseOptions(opts) {
}
// Parse platforms.
- let allPlatforms = ["linux", "linux64", "linux64-asan",
+ let allPlatforms = ["linux", "linux64", "linux64-asan", "linux64-fips",
"win", "win64", "win-make", "win64-make",
"linux64-make", "linux-make", "linux-fuzz",
- "linux64-fuzz", "aarch64"];
+ "linux64-fuzz", "aarch64", "mac"];
let platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms);
// If the given value is nonsense or "none" default to all platforms.
@@ -51,7 +51,7 @@ function parseOptions(opts) {
}
// Parse tools.
- let allTools = ["clang-format", "scan-build"];
+ let allTools = ["clang-format", "scan-build", "hacl"];
let tools = intersect(opts.tools.split(/\s*,\s*/), allTools);
// If the given value is "all" run all tools.
@@ -111,6 +111,7 @@ function filter(opts) {
"linux": "linux32",
"linux-fuzz": "linux32",
"linux64-asan": "linux64",
+ "linux64-fips": "linux64",
"linux64-fuzz": "linux64",
"linux64-make": "linux64",
"linux-make": "linux32",
@@ -126,6 +127,8 @@ function filter(opts) {
// Additional checks.
if (platform == "linux64-asan") {
keep &= coll("asan");
+ } else if (platform == "linux64-fips") {
+ keep &= coll("fips");
} else if (platform == "linux64-make" || platform == "linux-make" ||
platform == "win64-make" || platform == "win-make") {
keep &= coll("make");
diff --git a/security/nss/automation/taskcluster/image_builder/Dockerfile b/security/nss/automation/taskcluster/image_builder/Dockerfile
new file mode 100644
index 000000000..f8b4edcc5
--- /dev/null
+++ b/security/nss/automation/taskcluster/image_builder/Dockerfile
@@ -0,0 +1,23 @@
+FROM ubuntu:16.04
+MAINTAINER Tim Taubert <ttaubert@mozilla.com>
+
+WORKDIR /home/worker
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apt-get update && apt-get install -y apt-transport-https apt-utils
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 && \
+ sh -c "echo deb https://get.docker.io/ubuntu docker main \
+ > /etc/apt/sources.list.d/docker.list"
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE && \
+ sh -c "echo deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main \
+ > /etc/apt/sources.list.d/mercurial.list"
+RUN apt-get update && apt-get install -y \
+ lxc-docker-1.6.1 \
+ mercurial
+
+ADD bin /home/worker/bin
+RUN chmod +x /home/worker/bin/*
+
+# Set a default command useful for debugging
+CMD ["/bin/bash", "--login"]
diff --git a/security/nss/automation/taskcluster/image_builder/VERSION b/security/nss/automation/taskcluster/image_builder/VERSION
new file mode 100644
index 000000000..9faa1b7a7
--- /dev/null
+++ b/security/nss/automation/taskcluster/image_builder/VERSION
@@ -0,0 +1 @@
+0.1.5
diff --git a/security/nss/automation/taskcluster/image_builder/bin/checkout.sh b/security/nss/automation/taskcluster/image_builder/bin/checkout.sh
new file mode 100644
index 000000000..0cdd2ac40
--- /dev/null
+++ b/security/nss/automation/taskcluster/image_builder/bin/checkout.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+# Default values for testing.
+REVISION=${NSS_HEAD_REVISION:-default}
+REPOSITORY=${NSS_HEAD_REPOSITORY:-https://hg.mozilla.org/projects/nss}
+
+# Clone NSS.
+for i in 0 2 5; do
+ sleep $i
+ hg clone -r $REVISION $REPOSITORY nss && exit 0
+ rm -rf nss
+done
+exit 1
diff --git a/security/nss/automation/taskcluster/scripts/build_gyp.sh b/security/nss/automation/taskcluster/scripts/build_gyp.sh
index 7190bd5c4..fb3a33a52 100755
--- a/security/nss/automation/taskcluster/scripts/build_gyp.sh
+++ b/security/nss/automation/taskcluster/scripts/build_gyp.sh
@@ -9,5 +9,10 @@ hg_clone https://hg.mozilla.org/projects/nspr ./nspr default
nss/build.sh -g -v "$@"
# Package.
-mkdir artifacts
-tar cvfjh artifacts/dist.tar.bz2 dist
+if [[ $(uname) = "Darwin" ]]; then
+ mkdir -p public
+ tar cvfjh public/dist.tar.bz2 dist
+else
+ mkdir artifacts
+ tar cvfjh artifacts/dist.tar.bz2 dist
+fi
diff --git a/security/nss/automation/taskcluster/scripts/build_image.sh b/security/nss/automation/taskcluster/scripts/build_image.sh
new file mode 100644
index 000000000..b422214e7
--- /dev/null
+++ b/security/nss/automation/taskcluster/scripts/build_image.sh
@@ -0,0 +1,24 @@
+#!/bin/bash -vex
+
+set -x -e -v
+
+# Prefix errors with taskcluster error prefix so that they are parsed by Treeherder
+raise_error() {
+ echo
+ echo "[taskcluster-image-build:error] $1"
+ exit 1
+}
+
+# Ensure that the PROJECT is specified so the image can be indexed
+test -n "$PROJECT" || raise_error "Project must be provided."
+test -n "$HASH" || raise_error "Context Hash must be provided."
+
+CONTEXT_PATH=/home/worker/nss/$CONTEXT_PATH
+
+test -d $CONTEXT_PATH || raise_error "Context Path $CONTEXT_PATH does not exist."
+test -f "$CONTEXT_PATH/Dockerfile" || raise_error "Dockerfile must be present in $CONTEXT_PATH."
+
+docker build -t $PROJECT:$HASH $CONTEXT_PATH
+
+mkdir /artifacts
+docker save $PROJECT:$HASH > /artifacts/image.tar
diff --git a/security/nss/automation/taskcluster/scripts/gen_certs.sh b/security/nss/automation/taskcluster/scripts/gen_certs.sh
index b8d4f60ba..c03db7e9c 100755
--- a/security/nss/automation/taskcluster/scripts/gen_certs.sh
+++ b/security/nss/automation/taskcluster/scripts/gen_certs.sh
@@ -12,5 +12,10 @@ NSS_TESTS=cert NSS_CYCLES="standard pkix sharedb" $(dirname $0)/run_tests.sh
echo 1 > tests_results/security/localhost
# Package.
-mkdir artifacts
-tar cvfjh artifacts/dist.tar.bz2 dist tests_results
+if [[ $(uname) = "Darwin" ]]; then
+ mkdir -p public
+ tar cvfjh public/dist.tar.bz2 dist tests_results
+else
+ mkdir artifacts
+ tar cvfjh artifacts/dist.tar.bz2 dist tests_results
+fi
diff --git a/security/nss/automation/taskcluster/scripts/run_hacl.sh b/security/nss/automation/taskcluster/scripts/run_hacl.sh
new file mode 100644
index 000000000..281075eef
--- /dev/null
+++ b/security/nss/automation/taskcluster/scripts/run_hacl.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+if [[ $(id -u) -eq 0 ]]; then
+ # Drop privileges by re-running this script.
+ # Note: this mangles arguments, better to avoid running scripts as root.
+ exec su worker -c "$0 $*"
+fi
+
+set -e -x -v
+
+# The docker image this is running in has the HACL* and NSS sources.
+# The extracted C code from HACL* is already generated and the HACL* tests were
+# successfully executed.
+
+# Verify Poly1305 (doesn't work in docker image build)
+make verify -C ~/hacl-star/code/poly1305 -j$(nproc)
+
+# Add license header to specs
+spec_files=($(find ~/hacl-star/specs -type f -name '*.fst'))
+for f in "${spec_files[@]}"; do
+ cat /tmp/license.txt "$f" > /tmp/tmpfile && mv /tmp/tmpfile "$f"
+done
+
+# Format the extracted C code.
+cd ~/hacl-star/snapshots/nss
+cp ~/nss/.clang-format .
+find . -type f -name '*.[ch]' -exec clang-format -i {} \+
+
+# These diff commands will return 1 if there are differences and stop the script.
+files=($(find ~/nss/lib/freebl/verified/ -type f -name '*.[ch]'))
+for f in "${files[@]}"; do
+ diff $f $(basename "$f")
+done
+
+# Check that the specs didn't change either.
+cd ~/hacl-star/specs
+files=($(find ~/nss/lib/freebl/verified/specs -type f))
+for f in "${files[@]}"; do
+ diff $f $(basename "$f")
+done
diff --git a/security/nss/automation/taskcluster/scripts/split.sh b/security/nss/automation/taskcluster/scripts/split.sh
index 4d18385ec..fded64e1b 100644
--- a/security/nss/automation/taskcluster/scripts/split.sh
+++ b/security/nss/automation/taskcluster/scripts/split.sh
@@ -23,16 +23,10 @@ split_util() {
# Copy everything.
cp -R $nssdir $dstdir
- # Skip gtests when building.
- sed '/^DIRS = /s/ cpputil gtests$//' $nssdir/manifest.mn > $dstdir/manifest.mn-t && mv $dstdir/manifest.mn-t $dstdir/manifest.mn
-
# Remove subdirectories that we don't want.
rm -rf $dstdir/cmd
- rm -rf $dstdir/tests
rm -rf $dstdir/lib
rm -rf $dstdir/automation
- rm -rf $dstdir/gtests
- rm -rf $dstdir/cpputil
rm -rf $dstdir/doc
# Start with an empty cmd lib directories to be filled selectively.
diff --git a/security/nss/automation/taskcluster/windows/releng.manifest b/security/nss/automation/taskcluster/windows/releng.manifest
index 68d2c1d9e..d571c544d 100644
--- a/security/nss/automation/taskcluster/windows/releng.manifest
+++ b/security/nss/automation/taskcluster/windows/releng.manifest
@@ -1,10 +1,10 @@
[
{
- "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
- "size": 326656969,
- "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+ "version": "Visual Studio 2017 15.4.2 / SDK 10.0.15063.0",
+ "size": 303146863,
+ "digest": "18700889e6b5e81613b9cf57ce4e0d46a6ee45bb4c5c33bae2604a5275326128775b8a032a1eb178c5db973746d565340c4e36d98375789e1d5bd836ab16ba58",
"algorithm": "sha512",
- "filename": "vs2015u3.zip",
+ "filename": "vs2017_15.4.2.zip",
"unpack": true
},
{
diff --git a/security/nss/automation/taskcluster/windows/setup.sh b/security/nss/automation/taskcluster/windows/setup.sh
index 7def50db4..36a040ba1 100644
--- a/security/nss/automation/taskcluster/windows/setup.sh
+++ b/security/nss/automation/taskcluster/windows/setup.sh
@@ -2,12 +2,12 @@
set -v -e -x
-export VSPATH="$(pwd)/vs2015u3"
+export VSPATH="$(pwd)/vs2017_15.4.2"
export NINJA_PATH="$(pwd)/ninja/bin"
export WINDOWSSDKDIR="${VSPATH}/SDK"
export VS90COMNTOOLS="${VSPATH}/VC"
-export INCLUDE="${VSPATH}/VC/include:${VSPATH}/SDK/Include/10.0.14393.0/ucrt:${VSPATH}/SDK/Include/10.0.14393.0/shared:${VSPATH}/SDK/Include/10.0.14393.0/um"
+export INCLUDE="${VSPATH}/VC/include:${VSPATH}/SDK/Include/10.0.15063.0/ucrt:${VSPATH}/SDK/Include/10.0.15063.0/shared:${VSPATH}/SDK/Include/10.0.15063.0/um"
# Usage: hg_clone repo dir [revision=@]
hg_clone() {
@@ -23,4 +23,4 @@ hg_clone() {
}
hg_clone https://hg.mozilla.org/build/tools tools default
-tools/scripts/tooltool/tooltool_wrapper.sh $(dirname $0)/releng.manifest https://api.pub.build.mozilla.org/tooltool/ non-existant-file.sh /c/mozilla-build/python/python.exe /c/builds/tooltool.py --authentication-file /c/builds/relengapi.tok -c /c/builds/tooltool_cache
+tools/scripts/tooltool/tooltool_wrapper.sh $(dirname $0)/releng.manifest https://tooltool.mozilla-releng.net/ non-existant-file.sh /c/mozilla-build/python/python.exe /c/builds/tooltool.py --authentication-file /c/builds/relengapi.tok -c /c/builds/tooltool_cache
diff --git a/security/nss/automation/taskcluster/windows/setup32.sh b/security/nss/automation/taskcluster/windows/setup32.sh
index bcddabfa3..19bed284d 100644
--- a/security/nss/automation/taskcluster/windows/setup32.sh
+++ b/security/nss/automation/taskcluster/windows/setup32.sh
@@ -4,7 +4,7 @@ set -v -e -x
source $(dirname $0)/setup.sh
-export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT"
+export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC141.CRT"
export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86"
-export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
-export LIB="${VSPATH}/VC/lib:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.14393.0/um/x86"
+export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/Hostx64/x86:${VSPATH}/VC/bin/Hostx64/x64:${VSPATH}/VC/Hostx86/x86:${VSPATH}/SDK/bin/10.0.15063.0/x64:${VSPATH}/VC/redist/x86/Microsoft.VC141.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${PATH}"
+export LIB="${VSPATH}/VC/lib/x86:${VSPATH}/SDK/lib/10.0.15063.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.15063.0/um/x86"
diff --git a/security/nss/automation/taskcluster/windows/setup64.sh b/security/nss/automation/taskcluster/windows/setup64.sh
index f308298c1..d16cb0ec9 100644
--- a/security/nss/automation/taskcluster/windows/setup64.sh
+++ b/security/nss/automation/taskcluster/windows/setup64.sh
@@ -4,7 +4,7 @@ set -v -e -x
source $(dirname $0)/setup.sh
-export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT"
+export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x64/Microsoft.VC141.CRT"
export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64"
-export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
-export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.14393.0/um/x64"
+export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/Hostx64/x64:${VSPATH}/VC/bin/Hostx86/x86:${VSPATH}/SDK/bin/10.0.15063.0/x64:${VSPATH}/VC/redist/x64/Microsoft.VC141.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
+export LIB="${VSPATH}/VC/lib/x64:${VSPATH}/SDK/lib/10.0.15063.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.15063.0/um/x64"
diff --git a/security/nss/build.sh b/security/nss/build.sh
index 2b377dec5..2db8256d8 100755
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -68,11 +68,14 @@ fi
while [ $# -gt 0 ]; do
case $1 in
-c) clean=1 ;;
+ -cc) clean_only=1 ;;
--gyp|-g) rebuild_gyp=1 ;;
--nspr) nspr_clean; rebuild_nspr=1 ;;
-j) ninja_params+=(-j "$2"); shift ;;
-v) ninja_params+=(-v); verbose=1 ;;
--test) gyp_params+=(-Dtest_build=1) ;;
+ --clang) export CC=clang; export CCC=clang++; export CXX=clang++ ;;
+ --gcc) export CC=gcc; export CCC=g++; export CXX=g++ ;;
--fuzz) fuzz=1 ;;
--fuzz=oss) fuzz=1; fuzz_oss=1 ;;
--fuzz=tls) fuzz=1; fuzz_tls=1 ;;
@@ -94,6 +97,7 @@ while [ $# -gt 0 ]; do
--with-nspr=?*) set_nspr_path "${1#*=}"; no_local_nspr=1 ;;
--system-nspr) set_nspr_path "/usr/include/nspr/:"; no_local_nspr=1 ;;
--enable-libpkix) gyp_params+=(-Ddisable_libpkix=0) ;;
+ --enable-fips) gyp_params+=(-Ddisable_fips=0) ;;
*) show_help; exit 2 ;;
esac
shift
@@ -121,10 +125,15 @@ dist_dir=$(mkdir -p "$dist_dir"; cd "$dist_dir"; pwd -P)
gyp_params+=(-Dnss_dist_dir="$dist_dir")
# -c = clean first
-if [ "$clean" = 1 ]; then
+if [ "$clean" = 1 -o "$clean_only" = 1 ]; then
nspr_clean
rm -rf "$cwd"/out
rm -rf "$dist_dir"
+ # -cc = only clean, don't build
+ if [ "$clean_only" = 1 ]; then
+ echo "Cleaned"
+ exit 0
+ fi
fi
# This saves a canonical representation of arguments that we are passing to gyp
diff --git a/security/nss/cmd/bltest/blapitest.c b/security/nss/cmd/bltest/blapitest.c
index a3a162da1..ca3d6f314 100644
--- a/security/nss/cmd/bltest/blapitest.c
+++ b/security/nss/cmd/bltest/blapitest.c
@@ -20,16 +20,14 @@
#include "secport.h"
#include "secoid.h"
#include "nssutil.h"
+#include "ecl-curve.h"
#include "pkcs1_vectors.h"
-#ifndef NSS_DISABLE_ECC
-#include "ecl-curve.h"
SECStatus EC_DecodeParams(const SECItem *encodedParams,
ECParams **ecparams);
SECStatus EC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
const ECParams *srcParams);
-#endif
char *progName;
char *testdir = NULL;
@@ -135,18 +133,14 @@ Usage()
PRINTUSAGE(progName, "-S -m mode", "Sign a buffer");
PRINTUSAGE("", "", "[-i plaintext] [-o signature] [-k key]");
PRINTUSAGE("", "", "[-b bufsize]");
-#ifndef NSS_DISABLE_ECC
PRINTUSAGE("", "", "[-n curvename]");
-#endif
PRINTUSAGE("", "", "[-p repetitions | -5 time_interval] [-4 th_num]");
PRINTUSAGE("", "-m", "cipher mode to use");
PRINTUSAGE("", "-i", "file which contains input buffer");
PRINTUSAGE("", "-o", "file for signature");
PRINTUSAGE("", "-k", "file which contains key");
-#ifndef NSS_DISABLE_ECC
PRINTUSAGE("", "-n", "name of curve for EC key generation; one of:");
PRINTUSAGE("", "", " nistp256, nistp384, nistp521");
-#endif
PRINTUSAGE("", "-p", "do performance test");
PRINTUSAGE("", "-4", "run test in multithread mode. th_num number of parallel threads");
PRINTUSAGE("", "-5", "run test for specified time interval(in seconds)");
@@ -369,7 +363,6 @@ dsakey_from_filedata(PLArenaPool *arena, SECItem *filedata)
return key;
}
-#ifndef NSS_DISABLE_ECC
static ECPrivateKey *
eckey_from_filedata(PLArenaPool *arena, SECItem *filedata)
{
@@ -519,7 +512,6 @@ getECParams(const char *curve)
return ecparams;
}
-#endif /* NSS_DISABLE_ECC */
static void
dump_pqg(PQGParams *pqg)
@@ -537,7 +529,6 @@ dump_dsakey(DSAPrivateKey *key)
SECU_PrintInteger(stdout, &key->privateValue, "PRIVATE VALUE:", 0);
}
-#ifndef NSS_DISABLE_ECC
static void
dump_ecp(ECParams *ecp)
{
@@ -552,7 +543,6 @@ dump_eckey(ECPrivateKey *key)
SECU_PrintInteger(stdout, &key->publicValue, "PUBLIC VALUE:", 0);
SECU_PrintInteger(stdout, &key->privateValue, "PRIVATE VALUE:", 0);
}
-#endif
static void
dump_rsakey(RSAPrivateKey *key)
@@ -638,17 +628,15 @@ typedef enum {
bltestRSA, /* Public Key Ciphers */
bltestRSA_OAEP, /* . (Public Key Enc.) */
bltestRSA_PSS, /* . (Public Key Sig.) */
-#ifndef NSS_DISABLE_ECC
- bltestECDSA, /* . (Public Key Sig.) */
-#endif
- bltestDSA, /* . (Public Key Sig.) */
- bltestMD2, /* Hash algorithms */
- bltestMD5, /* . */
- bltestSHA1, /* . */
- bltestSHA224, /* . */
- bltestSHA256, /* . */
- bltestSHA384, /* . */
- bltestSHA512, /* . */
+ bltestECDSA, /* . (Public Key Sig.) */
+ bltestDSA, /* . (Public Key Sig.) */
+ bltestMD2, /* Hash algorithms */
+ bltestMD5, /* . */
+ bltestSHA1, /* . */
+ bltestSHA224, /* . */
+ bltestSHA256, /* . */
+ bltestSHA384, /* . */
+ bltestSHA512, /* . */
NUMMODES
} bltestCipherMode;
@@ -678,9 +666,7 @@ static char *mode_strings[] =
"rsa",
"rsa_oaep",
"rsa_pss",
-#ifndef NSS_DISABLE_ECC
"ecdsa",
-#endif
/*"pqg",*/
"dsa",
"md2",
@@ -732,13 +718,11 @@ typedef struct
PQGParams *pqg;
} bltestDSAParams;
-#ifndef NSS_DISABLE_ECC
typedef struct
{
char *curveName;
bltestIO sigseed;
} bltestECDSAParams;
-#endif
typedef struct
{
@@ -751,9 +735,7 @@ typedef struct
union {
bltestRSAParams rsa;
bltestDSAParams dsa;
-#ifndef NSS_DISABLE_ECC
bltestECDSAParams ecdsa;
-#endif
} cipherParams;
} bltestAsymKeyParams;
@@ -1310,7 +1292,6 @@ dsa_verifyDigest(void *cx, SECItem *output, const SECItem *input)
return DSA_VerifyDigest((DSAPublicKey *)params->pubKey, output, input);
}
-#ifndef NSS_DISABLE_ECC
SECStatus
ecdsa_signDigest(void *cx, SECItem *output, const SECItem *input)
{
@@ -1331,7 +1312,6 @@ ecdsa_verifyDigest(void *cx, SECItem *output, const SECItem *input)
bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
return ECDSA_VerifyDigest((ECPublicKey *)params->pubKey, output, input);
}
-#endif
SECStatus
bltest_des_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
@@ -1811,7 +1791,6 @@ bltest_dsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
return SECSuccess;
}
-#ifndef NSS_DISABLE_ECC
SECStatus
bltest_ecdsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
@@ -1877,7 +1856,6 @@ bltest_ecdsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
}
return SECSuccess;
}
-#endif
/* XXX unfortunately, this is not defined in blapi.h */
SECStatus
@@ -2169,11 +2147,7 @@ finish:
SECStatus
pubkeyInitKey(bltestCipherInfo *cipherInfo, PRFileDesc *file,
-#ifndef NSS_DISABLE_ECC
int keysize, int exponent, char *curveName)
-#else
- int keysize, int exponent)
-#endif
{
int i;
SECStatus rv = SECSuccess;
@@ -2182,12 +2156,10 @@ pubkeyInitKey(bltestCipherInfo *cipherInfo, PRFileDesc *file,
RSAPrivateKey **rsaKey = NULL;
bltestDSAParams *dsap;
DSAPrivateKey **dsaKey = NULL;
-#ifndef NSS_DISABLE_ECC
SECItem *tmpECParamsDER;
ECParams *tmpECParams = NULL;
SECItem ecSerialize[3];
ECPrivateKey **ecKey = NULL;
-#endif
switch (cipherInfo->mode) {
case bltestRSA:
case bltestRSA_PSS:
@@ -2224,7 +2196,6 @@ pubkeyInitKey(bltestCipherInfo *cipherInfo, PRFileDesc *file,
dsap->keysize = (*dsaKey)->params.prime.len * 8;
}
break;
-#ifndef NSS_DISABLE_ECC
case bltestECDSA:
ecKey = (ECPrivateKey **)&asymk->privKey;
if (curveName != NULL) {
@@ -2254,7 +2225,6 @@ pubkeyInitKey(bltestCipherInfo *cipherInfo, PRFileDesc *file,
*ecKey = eckey_from_filedata(cipherInfo->arena, &asymk->key.buf);
}
break;
-#endif
default:
return SECFailure;
}
@@ -2341,7 +2311,6 @@ cipherInit(bltestCipherInfo *cipherInfo, PRBool encrypt)
}
return bltest_dsa_init(cipherInfo, encrypt);
break;
-#ifndef NSS_DISABLE_ECC
case bltestECDSA:
if (encrypt) {
SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
@@ -2349,7 +2318,6 @@ cipherInit(bltestCipherInfo *cipherInfo, PRBool encrypt)
}
return bltest_ecdsa_init(cipherInfo, encrypt);
break;
-#endif
case bltestMD2:
restart = cipherInfo->params.hash.restart;
SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
@@ -2644,9 +2612,7 @@ cipherFinish(bltestCipherInfo *cipherInfo)
case bltestRSA_PSS: /* will be freed with it. */
case bltestRSA_OAEP:
case bltestDSA:
-#ifndef NSS_DISABLE_ECC
case bltestECDSA:
-#endif
case bltestMD2: /* hash contexts are ephemeral */
case bltestMD5:
case bltestSHA1:
@@ -2822,7 +2788,6 @@ print_td:
fprintf(stdout, "%8d", info->params.asymk.cipherParams.dsa.keysize);
}
break;
-#ifndef NSS_DISABLE_ECC
case bltestECDSA:
if (td) {
fprintf(stdout, "%12s", "ec_curve");
@@ -2833,7 +2798,6 @@ print_td:
ecCurve_map[curveName] ? ecCurve_map[curveName]->text : "Unsupported curve");
}
break;
-#endif
case bltestMD2:
case bltestMD5:
case bltestSHA1:
@@ -3063,7 +3027,6 @@ get_params(PLArenaPool *arena, bltestParams *params,
sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext", j);
load_file_data(arena, &params->asymk.sig, filename, bltestBase64Encoded);
break;
-#ifndef NSS_DISABLE_ECC
case bltestECDSA:
sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
load_file_data(arena, &params->asymk.key, filename, bltestBase64Encoded);
@@ -3075,7 +3038,6 @@ get_params(PLArenaPool *arena, bltestParams *params,
sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext", j);
load_file_data(arena, &params->asymk.sig, filename, bltestBase64Encoded);
break;
-#endif
case bltestMD2:
case bltestMD5:
case bltestSHA1:
@@ -3297,13 +3259,11 @@ dump_file(bltestCipherMode mode, char *filename)
load_file_data(arena, &keydata, filename, bltestBase64Encoded);
key = dsakey_from_filedata(arena, &keydata.buf);
dump_dsakey(key);
-#ifndef NSS_DISABLE_ECC
} else if (mode == bltestECDSA) {
ECPrivateKey *key;
load_file_data(arena, &keydata, filename, bltestBase64Encoded);
key = eckey_from_filedata(arena, &keydata.buf);
dump_eckey(key);
-#endif
}
PORT_FreeArena(arena, PR_FALSE);
return SECFailure;
@@ -3590,9 +3550,7 @@ enum {
opt_Key,
opt_HexWSpc,
opt_Mode,
-#ifndef NSS_DISABLE_ECC
opt_CurveName,
-#endif
opt_Output,
opt_Repetitions,
opt_ZeroBuf,
@@ -3644,9 +3602,7 @@ static secuCommandFlag bltest_options[] =
{ /* opt_Key */ 'k', PR_TRUE, 0, PR_FALSE },
{ /* opt_HexWSpc */ 'l', PR_FALSE, 0, PR_FALSE },
{ /* opt_Mode */ 'm', PR_TRUE, 0, PR_FALSE },
-#ifndef NSS_DISABLE_ECC
{ /* opt_CurveName */ 'n', PR_TRUE, 0, PR_FALSE },
-#endif
{ /* opt_Output */ 'o', PR_TRUE, 0, PR_FALSE },
{ /* opt_Repetitions */ 'p', PR_TRUE, 0, PR_FALSE },
{ /* opt_ZeroBuf */ 'q', PR_FALSE, 0, PR_FALSE },
@@ -3679,9 +3635,7 @@ main(int argc, char **argv)
bltestCipherInfo *cipherInfoListHead, *cipherInfo = NULL;
bltestIOMode ioMode;
int bufsize, exponent, curThrdNum;
-#ifndef NSS_DISABLE_ECC
char *curveName = NULL;
-#endif
int i, commandsEntered;
int inoff, outoff;
int threads = 1;
@@ -3917,12 +3871,10 @@ main(int argc, char **argv)
else
exponent = 65537;
-#ifndef NSS_DISABLE_ECC
if (bltest.options[opt_CurveName].activated)
curveName = PORT_Strdup(bltest.options[opt_CurveName].arg);
else
curveName = NULL;
-#endif
if (bltest.commands[cmd_Verify].activated &&
!bltest.options[opt_SigFile].activated) {
@@ -4008,11 +3960,7 @@ main(int argc, char **argv)
file = PR_Open("tmp.key", PR_WRONLY | PR_CREATE_FILE, 00660);
}
params->key.mode = bltestBase64Encoded;
-#ifndef NSS_DISABLE_ECC
pubkeyInitKey(cipherInfo, file, keysize, exponent, curveName);
-#else
- pubkeyInitKey(cipherInfo, file, keysize, exponent);
-#endif
PR_Close(file);
}
diff --git a/security/nss/cmd/certcgi/certcgi.c b/security/nss/cmd/certcgi/certcgi.c
index 35409e250..4d1a1061a 100644
--- a/security/nss/cmd/certcgi/certcgi.c
+++ b/security/nss/cmd/certcgi/certcgi.c
@@ -233,8 +233,7 @@ make_datastruct(char *data, int len)
if (remaining == 1) {
remaining += fields;
fields = fields * 2;
- datastruct = (Pair *)PORT_Realloc(datastruct, fields *
- sizeof(Pair));
+ datastruct = (Pair *)PORT_Realloc(datastruct, fields * sizeof(Pair));
if (datastruct == NULL) {
error_allocate();
}
diff --git a/security/nss/cmd/certutil/certutil.c b/security/nss/cmd/certutil/certutil.c
index fbc752c1b..03f4478b7 100644
--- a/security/nss/cmd/certutil/certutil.c
+++ b/security/nss/cmd/certutil/certutil.c
@@ -194,6 +194,8 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType,
PLArenaPool *arena;
void *extHandle;
SECItem signedReq = { siBuffer, NULL, 0 };
+ SECAlgorithmID signAlg;
+ SECItem *params = NULL;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
@@ -211,11 +213,26 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType,
/* Change cert type to RSA-PSS, if desired. */
if (pssCertificate) {
+ params = SEC_CreateSignatureAlgorithmParameters(arena,
+ NULL,
+ SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+ hashAlgTag,
+ NULL,
+ privk);
+ if (!params) {
+ PORT_FreeArena(arena, PR_FALSE);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ SECU_PrintError(progName, "unable to create RSA-PSS parameters");
+ return SECFailure;
+ }
+
spki->algorithm.parameters.data = NULL;
rv = SECOID_SetAlgorithmID(arena, &spki->algorithm,
- SEC_OID_PKCS1_RSA_PSS_SIGNATURE, 0);
+ SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+ hashAlgTag == SEC_OID_UNKNOWN ? NULL : params);
if (rv != SECSuccess) {
PORT_FreeArena(arena, PR_FALSE);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
SECU_PrintError(progName, "unable to set algorithm ID");
return SECFailure;
}
@@ -256,16 +273,34 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType,
return SECFailure;
}
- /* Sign the request */
- signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
- if (signAlgTag == SEC_OID_UNKNOWN) {
- PORT_FreeArena(arena, PR_FALSE);
- SECU_PrintError(progName, "unknown Key or Hash type");
- return SECFailure;
+ PORT_Memset(&signAlg, 0, sizeof(signAlg));
+ if (pssCertificate) {
+ rv = SECOID_SetAlgorithmID(arena, &signAlg,
+ SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_FALSE);
+ SECU_PrintError(progName, "unable to set algorithm ID");
+ return SECFailure;
+ }
+ } else {
+ signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
+ if (signAlgTag == SEC_OID_UNKNOWN) {
+ PORT_FreeArena(arena, PR_FALSE);
+ SECU_PrintError(progName, "unknown Key or Hash type");
+ return SECFailure;
+ }
+ rv = SECOID_SetAlgorithmID(arena, &signAlg, signAlgTag, 0);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_FALSE);
+ SECU_PrintError(progName, "unable to set algorithm ID");
+ return SECFailure;
+ }
}
- rv = SEC_DerSignData(arena, &signedReq, encoding->data, encoding->len,
- privk, signAlgTag);
+ /* Sign the request */
+ rv = SEC_DerSignDataWithAlgorithmID(arena, &signedReq,
+ encoding->data, encoding->len,
+ privk, &signAlg);
if (rv) {
PORT_FreeArena(arena, PR_FALSE);
SECU_PrintError(progName, "signing of data failed");
@@ -365,7 +400,7 @@ ChangeTrustAttributes(CERTCertDBHandle *handle, PK11SlotInfo *slot,
CERTCertificate *cert;
CERTCertTrust *trust;
- cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
+ cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata);
if (!cert) {
SECU_PrintError(progName, "could not find certificate named \"%s\"",
name);
@@ -591,6 +626,10 @@ ListCerts(CERTCertDBHandle *handle, char *nickname, char *email,
{
SECStatus rv;
+ if (slot && PK11_NeedUserInit(slot)) {
+ printf("\nDatabase needs user init\n");
+ }
+
if (!ascii && !raw && !nickname && !email) {
PR_fprintf(outfile, "\n%-60s %-5s\n%-60s %-5s\n\n",
"Certificate Nickname", "Trust Attributes", "",
@@ -614,12 +653,12 @@ ListCerts(CERTCertDBHandle *handle, char *nickname, char *email,
}
static SECStatus
-DeleteCert(CERTCertDBHandle *handle, char *name)
+DeleteCert(CERTCertDBHandle *handle, char *name, void *pwdata)
{
SECStatus rv;
CERTCertificate *cert;
- cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
+ cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata);
if (!cert) {
SECU_PrintError(progName, "could not find certificate named \"%s\"",
name);
@@ -635,12 +674,12 @@ DeleteCert(CERTCertDBHandle *handle, char *name)
}
static SECStatus
-RenameCert(CERTCertDBHandle *handle, char *name, char *newName)
+RenameCert(CERTCertDBHandle *handle, char *name, char *newName, void *pwdata)
{
SECStatus rv;
CERTCertificate *cert;
- cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
+ cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata);
if (!cert) {
SECU_PrintError(progName, "could not find certificate named \"%s\"",
name);
@@ -1015,6 +1054,18 @@ ListModules(void)
}
static void
+PrintBuildFlags()
+{
+#ifdef NSS_FIPS_DISABLED
+ PR_fprintf(PR_STDOUT, "NSS_FIPS_DISABLED\n");
+#endif
+#ifdef NSS_NO_INIT_SUPPORT
+ PR_fprintf(PR_STDOUT, "NSS_NO_INIT_SUPPORT\n");
+#endif
+ exit(0);
+}
+
+static void
PrintSyntax(char *progName)
{
#define FPS fprintf(stderr,
@@ -1044,15 +1095,10 @@ PrintSyntax(char *progName)
"\t\t [-f pwfile] [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
FPS "\t%s -G [-h token-name] -k dsa [-q pqgfile -g key-size] [-f pwfile]\n"
"\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
-#ifndef NSS_DISABLE_ECC
FPS "\t%s -G [-h token-name] -k ec -q curve [-f pwfile]\n"
"\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|ec|rsa|all]\n",
progName);
-#else
- FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|rsa|all]\n",
- progName);
-#endif /* NSS_DISABLE_ECC */
FPS "\t\t [-f pwfile] [-X] [-d certdir] [-P dbprefix]\n");
FPS "\t%s --upgrade-merge --source-dir upgradeDir --upgrade-id uniqueID\n",
progName);
@@ -1066,6 +1112,7 @@ PrintSyntax(char *progName)
FPS "\t%s -L [-n cert-name] [-h token-name] [--email email-address]\n",
progName);
FPS "\t\t [-X] [-r] [-a] [--dump-ext-val OID] [-d certdir] [-P dbprefix]\n");
+ FPS "\t%s --build-flags\n", progName);
FPS "\t%s -M -n cert-name -t trustargs [-d certdir] [-P dbprefix]\n",
progName);
FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n", progName);
@@ -1184,6 +1231,8 @@ luC(enum usage_level ul, const char *command)
" -o output-cert");
FPS "%-20s Self sign\n",
" -x");
+ FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n",
+ " --pss-sign");
FPS "%-20s Cert serial number\n",
" -m serial-number");
FPS "%-20s Time Warp\n",
@@ -1244,17 +1293,10 @@ luG(enum usage_level ul, const char *command)
return;
FPS "%-20s Name of token in which to generate key (default is internal)\n",
" -h token-name");
-#ifndef NSS_DISABLE_ECC
FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
" -k key-type");
FPS "%-20s Key size in bits, (min %d, max %d, default %d) (not for ec)\n",
" -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
-#else
- FPS "%-20s Type of key pair to generate (\"dsa\", \"rsa\" (default))\n",
- " -k key-type");
- FPS "%-20s Key size in bits, (min %d, max %d, default %d)\n",
- " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
-#endif /* NSS_DISABLE_ECC */
FPS "%-20s Set the public exponent value (3, 17, 65537) (rsa only)\n",
" -y exp");
FPS "%-20s Specify the password file\n",
@@ -1263,7 +1305,6 @@ luG(enum usage_level ul, const char *command)
" -z noisefile");
FPS "%-20s read PQG value from pqgfile (dsa only)\n",
" -q pqgfile");
-#ifndef NSS_DISABLE_ECC
FPS "%-20s Elliptic curve name (ec only)\n",
" -q curve-name");
FPS "%-20s One of nistp256, nistp384, nistp521, curve25519.\n", "");
@@ -1285,7 +1326,6 @@ luG(enum usage_level ul, const char *command)
FPS "%-20s c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, \n", "");
FPS "%-20s secp112r2, secp128r1, secp128r2, sect113r1, sect113r2\n", "");
FPS "%-20s sect131r1, sect131r2\n", "");
-#endif
FPS "%-20s Key database directory (default is ~/.netscape)\n",
" -d keydir");
FPS "%-20s Cert & Key database prefix\n",
@@ -1375,9 +1415,7 @@ luK(enum usage_level ul, const char *command)
" -h token-name ");
FPS "%-20s Key type (\"all\" (default), \"dsa\","
-#ifndef NSS_DISABLE_ECC
" \"ec\","
-#endif
" \"rsa\")\n",
" -k key-type");
FPS "%-20s The nickname of the key or associated certificate\n",
@@ -1520,11 +1558,7 @@ luR(enum usage_level ul, const char *command)
" -s subject");
FPS "%-20s Output the cert request to this file\n",
" -o output-req");
-#ifndef NSS_DISABLE_ECC
FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
-#else
- FPS "%-20s Type of key pair to generate (\"dsa\", \"rsa\" (default))\n",
-#endif /* NSS_DISABLE_ECC */
" -k key-type-or-id");
FPS "%-20s or nickname of the cert key to use \n",
"");
@@ -1532,14 +1566,14 @@ luR(enum usage_level ul, const char *command)
" -h token-name");
FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n",
" -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
+ FPS "%-20s Create a certificate request restricted to RSA-PSS (rsa only)\n",
+ " --pss");
FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
" -q pqgfile");
-#ifndef NSS_DISABLE_ECC
FPS "%-20s Elliptic curve name (ec only)\n",
" -q curve-name");
FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
"");
-#endif /* NSS_DISABLE_ECC */
FPS "%-20s Specify the password file\n",
" -f pwfile");
FPS "%-20s Key database directory (default is ~/.netscape)\n",
@@ -1705,26 +1739,24 @@ luS(enum usage_level ul, const char *command)
" -c issuer-name");
FPS "%-20s Set the certificate trust attributes (see -A above)\n",
" -t trustargs");
-#ifndef NSS_DISABLE_ECC
FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
-#else
- FPS "%-20s Type of key pair to generate (\"dsa\", \"rsa\" (default))\n",
-#endif /* NSS_DISABLE_ECC */
" -k key-type-or-id");
FPS "%-20s Name of token in which to generate key (default is internal)\n",
" -h token-name");
FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n",
" -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
+ FPS "%-20s Create a certificate restricted to RSA-PSS (rsa only)\n",
+ " --pss");
FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
" -q pqgfile");
-#ifndef NSS_DISABLE_ECC
FPS "%-20s Elliptic curve name (ec only)\n",
" -q curve-name");
FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
"");
-#endif /* NSS_DISABLE_ECC */
FPS "%-20s Self sign\n",
" -x");
+ FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n",
+ " --pss-sign");
FPS "%-20s Cert serial number\n",
" -m serial-number");
FPS "%-20s Time Warp\n",
@@ -1794,6 +1826,18 @@ luS(enum usage_level ul, const char *command)
}
static void
+luBuildFlags(enum usage_level ul, const char *command)
+{
+ int is_my_command = (command && 0 == strcmp(command, "build-flags"));
+ if (ul == usage_all || !command || is_my_command)
+ FPS "%-15s Print enabled build flags relevant for NSS test execution\n",
+ "--build-flags");
+ if (ul == usage_selected && !is_my_command)
+ return;
+ FPS "\n");
+}
+
+static void
LongUsage(char *progName, enum usage_level ul, const char *command)
{
luA(ul, command);
@@ -1807,6 +1851,7 @@ LongUsage(char *progName, enum usage_level ul, const char *command)
luU(ul, command);
luK(ul, command);
luL(ul, command);
+ luBuildFlags(ul, command);
luM(ul, command);
luN(ul, command);
luT(ul, command);
@@ -1889,46 +1934,119 @@ MakeV1Cert(CERTCertDBHandle *handle,
}
static SECStatus
+SetSignatureAlgorithm(PLArenaPool *arena,
+ SECAlgorithmID *signAlg,
+ SECAlgorithmID *spkiAlg,
+ SECOidTag hashAlgTag,
+ SECKEYPrivateKey *privKey,
+ PRBool pssSign)
+{
+ SECStatus rv;
+
+ if (pssSign ||
+ SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+ SECItem *srcParams;
+ SECItem *params;
+
+ if (SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+ srcParams = &spkiAlg->parameters;
+ } else {
+ /* If the issuer's public key is RSA, the parameter field
+ * of the SPKI should be NULL, which can't be used as a
+ * basis of RSA-PSS parameters. */
+ srcParams = NULL;
+ }
+ params = SEC_CreateSignatureAlgorithmParameters(arena,
+ NULL,
+ SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+ hashAlgTag,
+ srcParams,
+ privKey);
+ if (!params) {
+ SECU_PrintError(progName, "Could not create RSA-PSS parameters");
+ return SECFailure;
+ }
+ rv = SECOID_SetAlgorithmID(arena, signAlg,
+ SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+ params);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "Could not set signature algorithm id.");
+ return rv;
+ }
+ } else {
+ KeyType keyType = SECKEY_GetPrivateKeyType(privKey);
+ SECOidTag algID;
+
+ algID = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
+ if (algID == SEC_OID_UNKNOWN) {
+ SECU_PrintError(progName, "Unknown key or hash type for issuer.");
+ return SECFailure;
+ }
+ rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "Could not set signature algorithm id.");
+ return rv;
+ }
+ }
+ return SECSuccess;
+}
+
+static SECStatus
SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign,
SECOidTag hashAlgTag,
SECKEYPrivateKey *privKey, char *issuerNickName,
- int certVersion, void *pwarg)
+ int certVersion, PRBool pssSign, void *pwarg)
{
SECItem der;
SECKEYPrivateKey *caPrivateKey = NULL;
SECStatus rv;
PLArenaPool *arena;
- SECOidTag algID;
+ CERTCertificate *issuer;
void *dummy;
- if (!selfsign) {
- CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg);
+ arena = cert->arena;
+
+ if (selfsign) {
+ issuer = cert;
+ } else {
+ issuer = PK11_FindCertFromNickname(issuerNickName, pwarg);
if ((CERTCertificate *)NULL == issuer) {
SECU_PrintError(progName, "unable to find issuer with nickname %s",
issuerNickName);
- return SECFailure;
+ rv = SECFailure;
+ goto done;
}
-
privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg);
- CERT_DestroyCertificate(issuer);
if (caPrivateKey == NULL) {
SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName);
- return SECFailure;
+ rv = SECFailure;
+ CERT_DestroyCertificate(issuer);
+ goto done;
}
}
- arena = cert->arena;
-
- algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag);
- if (algID == SEC_OID_UNKNOWN) {
- fprintf(stderr, "Unknown key or hash type for issuer.");
+ if (pssSign &&
+ (SECKEY_GetPrivateKeyType(privKey) != rsaKey &&
+ SECKEY_GetPrivateKeyType(privKey) != rsaPssKey)) {
+ SECU_PrintError(progName, "unable to create RSA-PSS signature with key %s",
+ issuerNickName);
rv = SECFailure;
+ if (!selfsign) {
+ CERT_DestroyCertificate(issuer);
+ }
goto done;
}
- rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0);
+ rv = SetSignatureAlgorithm(arena,
+ &cert->signature,
+ &issuer->subjectPublicKeyInfo.algorithm,
+ hashAlgTag,
+ privKey,
+ pssSign);
+ if (!selfsign) {
+ CERT_DestroyCertificate(issuer);
+ }
if (rv != SECSuccess) {
- fprintf(stderr, "Could not set signature algorithm id.");
goto done;
}
@@ -1947,7 +2065,8 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign,
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ rv = SECFailure;
+ goto done;
}
der.len = 0;
@@ -1960,7 +2079,8 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign,
goto done;
}
- rv = SEC_DerSignData(arena, &cert->derCert, der.data, der.len, privKey, algID);
+ rv = SEC_DerSignDataWithAlgorithmID(arena, &cert->derCert, der.data, der.len,
+ privKey, &cert->signature);
if (rv != SECSuccess) {
fprintf(stderr, "Could not sign encoded certificate data.\n");
/* result allocated out of the arena, it will be freed
@@ -1993,6 +2113,7 @@ CreateCert(
certutilExtnList extnList,
const char *extGeneric,
int certVersion,
+ PRBool pssSign,
SECItem *certDER)
{
void *extHandle = NULL;
@@ -2053,7 +2174,7 @@ CreateCert(
rv = SignCert(handle, subjectCert, selfsign, hashAlgTag,
*selfsignprivkey, issuerNickName,
- certVersion, pwarg);
+ certVersion, pssSign, pwarg);
if (rv != SECSuccess)
break;
@@ -2306,6 +2427,7 @@ enum {
cmd_Merge,
cmd_UpgradeMerge, /* test only */
cmd_Rename,
+ cmd_BuildFlags,
max_cmd
};
@@ -2376,6 +2498,7 @@ enum certutilOpts {
opt_GenericExtensions,
opt_NewNickname,
opt_Pss,
+ opt_PssSign,
opt_Help
};
@@ -2407,7 +2530,9 @@ static const secuCommandFlag commands_init[] =
{ /* cmd_UpgradeMerge */ 0, PR_FALSE, 0, PR_FALSE,
"upgrade-merge" },
{ /* cmd_Rename */ 0, PR_FALSE, 0, PR_FALSE,
- "rename" }
+ "rename" },
+ { /* cmd_BuildFlags */ 0, PR_FALSE, 0, PR_FALSE,
+ "build-flags" }
};
#define NUM_COMMANDS ((sizeof commands_init) / (sizeof commands_init[0]))
@@ -2496,6 +2621,8 @@ static const secuCommandFlag options_init[] =
"new-n" },
{ /* opt_Pss */ 0, PR_FALSE, 0, PR_FALSE,
"pss" },
+ { /* opt_PssSign */ 0, PR_FALSE, 0, PR_FALSE,
+ "pss-sign" },
};
#define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0]))
@@ -2592,6 +2719,10 @@ certutil_main(int argc, char **argv, PRBool initialize)
exit(1);
}
+ if (certutil.commands[cmd_BuildFlags].activated) {
+ PrintBuildFlags();
+ }
+
if (certutil.options[opt_PasswordFile].arg) {
pwdata.source = PW_FROMFILE;
pwdata.data = certutil.options[opt_PasswordFile].arg;
@@ -2621,12 +2752,10 @@ certutil_main(int argc, char **argv, PRBool initialize)
progName, MIN_KEY_BITS, MAX_KEY_BITS);
return 255;
}
-#ifndef NSS_DISABLE_ECC
if (keytype == ecKey) {
PR_fprintf(PR_STDERR, "%s -g: Not for ec keys.\n", progName);
return 255;
}
-#endif /* NSS_DISABLE_ECC */
}
/* -h specify token name */
@@ -2655,10 +2784,8 @@ certutil_main(int argc, char **argv, PRBool initialize)
keytype = rsaKey;
} else if (PL_strcmp(arg, "dsa") == 0) {
keytype = dsaKey;
-#ifndef NSS_DISABLE_ECC
} else if (PL_strcmp(arg, "ec") == 0) {
keytype = ecKey;
-#endif /* NSS_DISABLE_ECC */
} else if (PL_strcmp(arg, "all") == 0) {
keytype = nullKey;
} else {
@@ -2711,16 +2838,10 @@ certutil_main(int argc, char **argv, PRBool initialize)
/* -q PQG file or curve name */
if (certutil.options[opt_PQGFile].activated) {
-#ifndef NSS_DISABLE_ECC
if ((keytype != dsaKey) && (keytype != ecKey)) {
PR_fprintf(PR_STDERR, "%s -q: specifies a PQG file for DSA keys"
" (-k dsa) or a named curve for EC keys (-k ec)\n)",
progName);
-#else /* } */
- if (keytype != dsaKey) {
- PR_fprintf(PR_STDERR, "%s -q: PQG file is for DSA key (-k dsa).\n)",
- progName);
-#endif /* NSS_DISABLE_ECC */
return 255;
}
}
@@ -3032,11 +3153,43 @@ certutil_main(int argc, char **argv, PRBool initialize)
/* If creating new database, initialize the password. */
if (certutil.commands[cmd_NewDBs].activated) {
- if (certutil.options[opt_EmptyPassword].activated && (PK11_NeedUserInit(slot)))
- PK11_InitPin(slot, (char *)NULL, "");
- else
- SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg,
- certutil.options[opt_NewPasswordFile].arg);
+ if (certutil.options[opt_EmptyPassword].activated && (PK11_NeedUserInit(slot))) {
+ rv = PK11_InitPin(slot, (char *)NULL, "");
+ } else {
+ rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg,
+ certutil.options[opt_NewPasswordFile].arg);
+ }
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "Could not set password for the slot");
+ goto shutdown;
+ }
+ }
+
+ /* if we are going to modify the cert database,
+ * make sure it's initialized */
+ if (certutil.commands[cmd_ModifyCertTrust].activated ||
+ certutil.commands[cmd_CreateAndAddCert].activated ||
+ certutil.commands[cmd_AddCert].activated ||
+ certutil.commands[cmd_AddEmailCert].activated) {
+ if (PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) {
+ char *password = NULL;
+ /* fetch the password from the command line or the file
+ * if no password is supplied, initialize the password to NULL */
+ if (pwdata.source == PW_FROMFILE) {
+ password = SECU_FilePasswd(slot, PR_FALSE, pwdata.data);
+ } else if (pwdata.source == PW_PLAINTEXT) {
+ password = PL_strdup(pwdata.data);
+ }
+ rv = PK11_InitPin(slot, (char *)NULL, password ? password : "");
+ if (password) {
+ PORT_Memset(password, 0, PL_strlen(password));
+ PORT_Free(password);
+ }
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "Could not set password for the slot");
+ goto shutdown;
+ }
+ }
}
/* walk through the upgrade merge if necessary.
@@ -3214,12 +3367,12 @@ certutil_main(int argc, char **argv, PRBool initialize)
}
/* Delete cert (-D) */
if (certutil.commands[cmd_DeleteCert].activated) {
- rv = DeleteCert(certHandle, name);
+ rv = DeleteCert(certHandle, name, &pwdata);
goto shutdown;
}
/* Rename cert (--rename) */
if (certutil.commands[cmd_Rename].activated) {
- rv = RenameCert(certHandle, name, newName);
+ rv = RenameCert(certHandle, name, newName, &pwdata);
goto shutdown;
}
/* Delete key (-F) */
@@ -3237,7 +3390,10 @@ certutil_main(int argc, char **argv, PRBool initialize)
if (certutil.commands[cmd_ChangePassword].activated) {
rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg,
certutil.options[opt_NewPasswordFile].arg);
- goto shutdown;
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "Could not set password for the slot");
+ goto shutdown;
+ }
}
/* Reset the a token */
if (certutil.commands[cmd_TokenReset].activated) {
@@ -3362,6 +3518,25 @@ certutil_main(int argc, char **argv, PRBool initialize)
}
}
+ /* --pss-sign is to sign a certificate with RSA-PSS, even if the
+ * issuer's key is an RSA key. If the key is an RSA-PSS key, the
+ * generated signature is always RSA-PSS. */
+ if (certutil.options[opt_PssSign].activated) {
+ if (!certutil.commands[cmd_CreateNewCert].activated &&
+ !certutil.commands[cmd_CreateAndAddCert].activated) {
+ PR_fprintf(PR_STDERR,
+ "%s -%c: --pss-sign only works with -C or -S.\n",
+ progName, commandToRun);
+ return 255;
+ }
+ if (keytype != rsaKey) {
+ PR_fprintf(PR_STDERR,
+ "%s -%c: --pss-sign only works with RSA keys.\n",
+ progName, commandToRun);
+ return 255;
+ }
+ }
+
/* If we need a list of extensions convert the flags into list format */
if (certutil.commands[cmd_CertReq].activated ||
certutil.commands[cmd_CreateAndAddCert].activated ||
@@ -3499,6 +3674,7 @@ certutil_main(int argc, char **argv, PRBool initialize)
(certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg
: NULL),
certVersion,
+ certutil.options[opt_PssSign].activated,
&certDER);
if (rv)
goto shutdown;
diff --git a/security/nss/cmd/certutil/keystuff.c b/security/nss/cmd/certutil/keystuff.c
index 2878e3765..330284c61 100644
--- a/security/nss/cmd/certutil/keystuff.c
+++ b/security/nss/cmd/certutil/keystuff.c
@@ -380,7 +380,6 @@ CERTUTIL_FileForRNG(const char *noise)
return SECSuccess;
}
-#ifndef NSS_DISABLE_ECC
typedef struct curveNameTagPairStr {
char *curveName;
SECOidTag curveOidTag;
@@ -495,9 +494,9 @@ getECParams(const char *curve)
ecparams = SECITEM_AllocItem(NULL, NULL, (2 + oidData->oid.len));
- /*
+ /*
* ecparams->data needs to contain the ASN encoding of an object ID (OID)
- * representing the named curve. The actual OID is in
+ * representing the named curve. The actual OID is in
* oidData->oid.data so we simply prepend 0x06 and OID length
*/
ecparams->data[0] = SEC_ASN1_OBJECT_ID;
@@ -506,7 +505,6 @@ getECParams(const char *curve)
return ecparams;
}
-#endif /* NSS_DISABLE_ECC */
SECKEYPrivateKey *
CERTUTIL_GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
@@ -564,14 +562,12 @@ CERTUTIL_GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
params = (void *)&default_pqg_params;
}
break;
-#ifndef NSS_DISABLE_ECC
case ecKey:
mechanism = CKM_EC_KEY_PAIR_GEN;
/* For EC keys, PQGFile determines EC parameters */
if ((params = (void *)getECParams(pqgFile)) == NULL)
return NULL;
break;
-#endif /* NSS_DISABLE_ECC */
default:
return NULL;
}
@@ -580,8 +576,7 @@ CERTUTIL_GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
fprintf(stderr, "Generating key. This may take a few moments...\n\n");
privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, params, pubkeyp,
- attrFlags, opFlagsOn, opFlagsOn |
- opFlagsOff,
+ attrFlags, opFlagsOn, opFlagsOn | opFlagsOff,
pwdata /*wincx*/);
/* free up the params */
switch (keytype) {
@@ -589,11 +584,9 @@ CERTUTIL_GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
if (dsaparams)
CERTUTIL_DestroyParamsPQG(dsaparams);
break;
-#ifndef NSS_DISABLE_ECC
case ecKey:
SECITEM_FreeItem((SECItem *)params, PR_TRUE);
break;
-#endif
default: /* nothing to free */
break;
}
diff --git a/security/nss/cmd/crlutil/crlgen.c b/security/nss/cmd/crlutil/crlgen.c
index 1f9dc4b43..fce5e2a60 100644
--- a/security/nss/cmd/crlutil/crlgen.c
+++ b/security/nss/cmd/crlutil/crlgen.c
@@ -616,8 +616,7 @@ crlgen_CreateInvalidityDate(PLArenaPool *arena, const char **dataArr,
goto loser;
}
- PORT_Memcpy(encodedItem->data, dataArr[2], (encodedItem->len = length) *
- sizeof(char));
+ PORT_Memcpy(encodedItem->data, dataArr[2], (encodedItem->len = length) * sizeof(char));
*extCode = SEC_OID_X509_INVALID_DATE;
return encodedItem;
diff --git a/security/nss/cmd/fipstest/fipstest.c b/security/nss/cmd/fipstest/fipstest.c
index ab73e42a5..061f3dde0 100644
--- a/security/nss/cmd/fipstest/fipstest.c
+++ b/security/nss/cmd/fipstest/fipstest.c
@@ -35,13 +35,11 @@
#include "../../lib/freebl/mpi/mpi.h"
#endif
-#ifndef NSS_DISABLE_ECC
extern SECStatus
EC_DecodeParams(const SECItem *encodedParams, ECParams **ecparams);
extern SECStatus
EC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
const ECParams *srcParams);
-#endif
#define ENCRYPT 1
#define DECRYPT 0
@@ -2094,7 +2092,6 @@ get_next_line(FILE *req, char *key, char *val, FILE *rsp)
return (c == EOF) ? -1 : ignore;
}
-#ifndef NSS_DISABLE_ECC
typedef struct curveNameTagPairStr {
char *curveName;
SECOidTag curveOidTag;
@@ -2958,7 +2955,6 @@ loser:
}
fclose(ecdsareq);
}
-#endif /* NSS_DISABLE_ECC */
PRBool
isblankline(char *b)
@@ -5926,8 +5922,7 @@ tls(char *reqfn)
goto loser;
}
crv = NSC_DeriveKey(session, &master_mech, pms_handle,
- derive_template, derive_template_count -
- 1,
+ derive_template, derive_template_count - 1,
&master_handle);
if (crv != CKR_OK) {
fprintf(stderr, "NSC_DeriveKey(master) failed crv=0x%x\n",
@@ -6094,7 +6089,6 @@ main(int argc, char **argv)
/* Signature Verification Test */
dsa_sigver_test(argv[3]);
}
-#ifndef NSS_DISABLE_ECC
/*************/
/* ECDSA */
/*************/
@@ -6113,7 +6107,6 @@ main(int argc, char **argv)
/* Signature Verification Test */
ecdsa_sigver_test(argv[3]);
}
-#endif /* NSS_DISABLE_ECC */
/*************/
/* RNG */
/*************/
diff --git a/security/nss/cmd/fipstest/runtest.sh b/security/nss/cmd/fipstest/runtest.sh
index 99cefed77..5f8e66a08 100644
--- a/security/nss/cmd/fipstest/runtest.sh
+++ b/security/nss/cmd/fipstest/runtest.sh
@@ -7,9 +7,6 @@
TESTDIR=${1-.}
COMMAND=${2-run}
TESTS="aes aesgcm dsa ecdsa hmac tls rng rsa sha tdea"
-if [ ${NSS_ENABLE_ECC}x = 1x ]; then
- TESTS=${TESTS} ecdsa
-fi
for i in $TESTS
do
echo "********************Running $i tests"
diff --git a/security/nss/cmd/lib/secutil.c b/security/nss/cmd/lib/secutil.c
index cb4752df9..2b33f8963 100644
--- a/security/nss/cmd/lib/secutil.c
+++ b/security/nss/cmd/lib/secutil.c
@@ -54,6 +54,10 @@ static char consoleName[] = {
static PRBool utf8DisplayEnabled = PR_FALSE;
+/* The minimum password/pin length (in Unicode characters) in FIPS mode,
+ * defined in lib/softoken/pkcs11i.h. */
+#define FIPS_MIN_PIN 7
+
void
SECU_EnableUtf8Display(PRBool enable)
{
@@ -236,7 +240,8 @@ SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
sprintf(prompt,
"Press Enter, then enter PIN for \"%s\" on external device.\n",
PK11_GetTokenName(slot));
- (void)SECU_GetPasswordString(NULL, prompt);
+ char *pw = SECU_GetPasswordString(NULL, prompt);
+ PORT_Free(pw);
/* Fall Through */
case PW_PLAINTEXT:
return PL_strdup(pwdata->data);
@@ -276,10 +281,25 @@ secu_InitSlotPassword(PK11SlotInfo *slot, PRBool retry, void *arg)
}
/* we have no password, so initialize database with one */
- PR_fprintf(PR_STDERR,
- "Enter a password which will be used to encrypt your keys.\n"
- "The password should be at least 8 characters long,\n"
- "and should contain at least one non-alphabetic character.\n\n");
+ if (PK11_IsFIPS()) {
+ PR_fprintf(PR_STDERR,
+ "Enter a password which will be used to encrypt your keys.\n"
+ "The password should be at least %d characters long,\n"
+ "and should consist of at least three character classes.\n"
+ "The available character classes are: digits (0-9), ASCII\n"
+ "lowercase letters, ASCII uppercase letters, ASCII\n"
+ "non-alphanumeric characters, and non-ASCII characters.\n\n"
+ "If an ASCII uppercase letter appears at the beginning of\n"
+ "the password, it is not counted toward its character class.\n"
+ "Similarly, if a digit appears at the end of the password,\n"
+ "it is not counted toward its character class.\n\n",
+ FIPS_MIN_PIN);
+ } else {
+ PR_fprintf(PR_STDERR,
+ "Enter a password which will be used to encrypt your keys.\n"
+ "The password should be at least 8 characters long,\n"
+ "and should contain at least one non-alphabetic character.\n\n");
+ }
output = fopen(consoleName, "w");
if (output == NULL) {
@@ -465,48 +485,6 @@ SECU_ConfigDirectory(const char *base)
return buf;
}
-/*Turn off SSL for now */
-/* This gets called by SSL when server wants our cert & key */
-int
-SECU_GetClientAuthData(void *arg, PRFileDesc *fd,
- struct CERTDistNamesStr *caNames,
- struct CERTCertificateStr **pRetCert,
- struct SECKEYPrivateKeyStr **pRetKey)
-{
- SECKEYPrivateKey *key;
- CERTCertificate *cert;
- int errsave;
-
- if (arg == NULL) {
- fprintf(stderr, "no key/cert name specified for client auth\n");
- return -1;
- }
- cert = PK11_FindCertFromNickname(arg, NULL);
- errsave = PORT_GetError();
- if (!cert) {
- if (errsave == SEC_ERROR_BAD_PASSWORD)
- fprintf(stderr, "Bad password\n");
- else if (errsave > 0)
- fprintf(stderr, "Unable to read cert (error %d)\n", errsave);
- else if (errsave == SEC_ERROR_BAD_DATABASE)
- fprintf(stderr, "Unable to get cert from database (%d)\n", errsave);
- else
- fprintf(stderr, "SECKEY_FindKeyByName: internal error %d\n", errsave);
- return -1;
- }
-
- key = PK11_FindKeyByAnyCert(arg, NULL);
- if (!key) {
- fprintf(stderr, "Unable to get key (%d)\n", PORT_GetError());
- return -1;
- }
-
- *pRetCert = cert;
- *pRetKey = key;
-
- return 0;
-}
-
SECStatus
SECU_ReadDERFromFile(SECItem *der, PRFileDesc *inFile, PRBool ascii,
PRBool warnOnPrivateKeyInAsciiFile)
@@ -991,7 +969,7 @@ secu_PrintUniversalString(FILE *out, const SECItem *i, const char *m, int level)
for (s = my.data, d = tmp.data; len > 0; len--) {
PRUint32 bmpChar = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
s += 4;
- if (!isprint(bmpChar))
+ if (!isprint(bmpChar & 0xFF))
goto loser;
*d++ = (unsigned char)bmpChar;
}
@@ -1215,7 +1193,7 @@ secu_PrintRSAPSSParams(FILE *out, SECItem *value, char *m, int level)
SECU_Indent(out, level + 1);
fprintf(out, "Salt length: default, %i (0x%2X)\n", 20, 20);
} else {
- SECU_PrintInteger(out, &param.saltLength, "Salt Length", level + 1);
+ SECU_PrintInteger(out, &param.saltLength, "Salt length", level + 1);
}
} else {
SECU_Indent(out, level + 1);
@@ -1335,15 +1313,12 @@ SECU_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m, int level)
return;
}
- if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
- secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1);
- return;
- }
-
if (a->parameters.len == 0 ||
(a->parameters.len == 2 &&
PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) {
/* No arguments or NULL argument */
+ } else if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+ secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1);
} else {
/* Print args to algorithm */
SECU_PrintAsHex(out, &a->parameters, "Args", level + 1);
@@ -1390,7 +1365,6 @@ secu_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m, int level)
}
}
-#ifndef NSS_DISABLE_ECC
static void
secu_PrintECPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
{
@@ -1409,7 +1383,6 @@ secu_PrintECPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
SECU_PrintObjectID(out, &curveOID, "Curve", level + 1);
}
}
-#endif /* NSS_DISABLE_ECC */
void
SECU_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
@@ -1457,11 +1430,9 @@ secu_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena,
SECU_PrintDSAPublicKey(out, pk, "DSA Public Key", level + 1);
break;
-#ifndef NSS_DISABLE_ECC
case ecKey:
secu_PrintECPublicKey(out, pk, "EC Public Key", level + 1);
break;
-#endif
case dhKey:
case fortezzaKey:
@@ -3614,44 +3585,6 @@ loser:
return rv;
}
-#if 0
-
-/* we need access to the private function cert_FindExtension for this code to work */
-
-CERTAuthKeyID *
-SECU_FindCRLAuthKeyIDExten (PLArenaPool *arena, CERTSignedCrl *scrl)
-{
- SECItem encodedExtenValue;
- SECStatus rv;
- CERTAuthKeyID *ret;
- CERTCrl* crl;
-
- if (!scrl) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return NULL;
- }
-
- crl = &scrl->crl;
-
- encodedExtenValue.data = NULL;
- encodedExtenValue.len = 0;
-
- rv = cert_FindExtension(crl->extensions, SEC_OID_X509_AUTH_KEY_ID,
- &encodedExtenValue);
- if ( rv != SECSuccess ) {
- return (NULL);
- }
-
- ret = CERT_DecodeAuthKeyID (arena, &encodedExtenValue);
-
- PORT_Free(encodedExtenValue.data);
- encodedExtenValue.data = NULL;
-
- return(ret);
-}
-
-#endif
-
/*
* Find the issuer of a Crl. Use the authorityKeyID if it exists.
*/
@@ -3725,7 +3658,7 @@ SECU_FindCertByNicknameOrFilename(CERTCertDBHandle *handle,
void *pwarg)
{
CERTCertificate *the_cert;
- the_cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
+ the_cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwarg);
if (the_cert) {
return the_cert;
}
diff --git a/security/nss/cmd/libpkix/pkix/util/test_list2.c b/security/nss/cmd/libpkix/pkix/util/test_list2.c
index 7e4114e52..b802ff0e6 100644
--- a/security/nss/cmd/libpkix/pkix/util/test_list2.c
+++ b/security/nss/cmd/libpkix/pkix/util/test_list2.c
@@ -78,16 +78,14 @@ test_list2(int argc, char *argv[])
for (i = 0; i < size; i++)
for (j = 9; j > i; j--) {
PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_GetItem(list, j, &obj, plContext));
- PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_GetItem(list, j -
- 1,
+ PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_GetItem(list, j - 1,
&obj2, plContext));
PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_Compare(obj, obj2, &cmpResult, plContext));
if (cmpResult < 0) {
/* Exchange the items */
PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_SetItem(list, j, obj2, plContext));
- PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_SetItem(list, j -
- 1,
+ PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_SetItem(list, j - 1,
obj, plContext));
}
/* DecRef objects */
diff --git a/security/nss/cmd/listsuites/listsuites.c b/security/nss/cmd/listsuites/listsuites.c
index 458130e5e..8eb2c3553 100644
--- a/security/nss/cmd/listsuites/listsuites.c
+++ b/security/nss/cmd/listsuites/listsuites.c
@@ -10,7 +10,9 @@
#include <errno.h>
#include <stdio.h>
+#include "nss.h"
#include "secport.h"
+#include "secutil.h"
#include "ssl.h"
int
@@ -19,6 +21,43 @@ main(int argc, char **argv)
const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
int i;
int errCount = 0;
+ SECStatus rv;
+ PRErrorCode err;
+ char *certDir = NULL;
+
+ /* load policy from $SSL_DIR/pkcs11.txt, for testing */
+ certDir = SECU_DefaultSSLDir();
+ if (certDir) {
+ rv = NSS_Init(certDir);
+ } else {
+ rv = NSS_NoDB_Init(NULL);
+ }
+ if (rv != SECSuccess) {
+ err = PR_GetError();
+ ++errCount;
+ fprintf(stderr, "NSS_Init failed: %s\n", PORT_ErrorToString(err));
+ goto out;
+ }
+
+ /* apply policy */
+ rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, 0);
+ if (rv != SECSuccess) {
+ err = PR_GetError();
+ ++errCount;
+ fprintf(stderr, "NSS_SetAlgorithmPolicy failed: %s\n",
+ PORT_ErrorToString(err));
+ goto out;
+ }
+
+ /* update the default cipher suites according to the policy */
+ rv = SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE);
+ if (rv != SECSuccess) {
+ err = PR_GetError();
+ ++errCount;
+ fprintf(stderr, "SSL_OptionSetDefault failed: %s\n",
+ PORT_ErrorToString(err));
+ goto out;
+ }
fputs("This version of libSSL supports these cipher suites:\n\n", stdout);
@@ -58,5 +97,14 @@ main(int argc, char **argv)
info.isFIPS ? "FIPS" : "",
info.nonStandard ? "nonStandard" : "");
}
+
+out:
+ rv = NSS_Shutdown();
+ if (rv != SECSuccess) {
+ err = PR_GetError();
+ ++errCount;
+ fprintf(stderr, "NSS_Shutdown failed: %s\n", PORT_ErrorToString(err));
+ }
+
return errCount;
}
diff --git a/security/nss/cmd/manifest.mn b/security/nss/cmd/manifest.mn
index 153384ce1..f5e6bc236 100644
--- a/security/nss/cmd/manifest.mn
+++ b/security/nss/cmd/manifest.mn
@@ -63,6 +63,7 @@ NSS_SRCDIRS = \
pp \
pwdecrypt \
rsaperf \
+ rsapoptst \
sdrtest \
selfserv \
signtool \
diff --git a/security/nss/cmd/modutil/error.h b/security/nss/cmd/modutil/error.h
index b328afebc..d9f06592f 100644
--- a/security/nss/cmd/modutil/error.h
+++ b/security/nss/cmd/modutil/error.h
@@ -57,6 +57,7 @@ typedef enum {
UNSPECIFIED_ERR,
NOCERTDB_MISUSE_ERR,
NSS_INITIALIZE_FAILED_ERR,
+ INITPW_FAILED_ERR,
LAST_ERR /* must be last */
} Error;
@@ -109,8 +110,9 @@ static char *errStrings[] = {
"ERROR: Failed to change default.\n",
"ERROR: Unable to read from standard input.\n",
"ERROR: Unknown error occurred.\n",
- "ERROR: -nocertdb option can only be used with the -jar command.\n"
- "ERROR: NSS_Initialize() failed.\n"
+ "ERROR: -nocertdb option can only be used with the -jar command.\n",
+ "ERROR: NSS_Initialize() failed.\n",
+ "ERROR: Unable to set initial password on the database.\n"
};
typedef enum {
diff --git a/security/nss/cmd/modutil/install-ds.c b/security/nss/cmd/modutil/install-ds.c
index c8fef7897..030568762 100644
--- a/security/nss/cmd/modutil/install-ds.c
+++ b/security/nss/cmd/modutil/install-ds.c
@@ -975,8 +975,7 @@ Pk11Install_Platform_Print(Pk11Install_Platform* _this, int pad)
printf("Doesn't use equiv\n");
}
PAD(pad);
- printf("Module File: %s\n", _this->moduleFile ? _this->moduleFile
- : "<NULL>");
+ printf("Module File: %s\n", _this->moduleFile ? _this->moduleFile : "<NULL>");
PAD(pad);
printf("mechFlags: %lx\n", _this->mechFlags);
PAD(pad);
diff --git a/security/nss/cmd/modutil/modutil.c b/security/nss/cmd/modutil/modutil.c
index 02972f7b4..c1b44be53 100644
--- a/security/nss/cmd/modutil/modutil.c
+++ b/security/nss/cmd/modutil/modutil.c
@@ -865,7 +865,7 @@ main(int argc, char* argv[])
errcode = ChangePW(tokenName, pwFile, newpwFile);
break;
case CREATE_COMMAND:
- /* The work was already done in init_crypto() */
+ errcode = InitPW();
break;
case DEFAULT_COMMAND:
errcode = SetDefaultModule(moduleName, slotName, mechanisms);
diff --git a/security/nss/cmd/modutil/modutil.h b/security/nss/cmd/modutil/modutil.h
index 127d0d0da..04aa908c8 100644
--- a/security/nss/cmd/modutil/modutil.h
+++ b/security/nss/cmd/modutil/modutil.h
@@ -29,6 +29,7 @@ Error AddModule(char *moduleName, char *libFile, char *ciphers,
Error DeleteModule(char *moduleName);
Error ListModule(char *moduleName);
Error ListModules();
+Error InitPW(void);
Error ChangePW(char *tokenName, char *pwFile, char *newpwFile);
Error EnableModule(char *moduleName, char *slotName, PRBool enable);
Error RawAddModule(char *dbmodulespec, char *modulespec);
diff --git a/security/nss/cmd/modutil/pk11.c b/security/nss/cmd/modutil/pk11.c
index 834469af1..1efc1895c 100644
--- a/security/nss/cmd/modutil/pk11.c
+++ b/security/nss/cmd/modutil/pk11.c
@@ -670,6 +670,39 @@ loser:
/************************************************************************
*
+ * I n i t P W
+ */
+Error
+InitPW(void)
+{
+ PK11SlotInfo *slot;
+ Error ret = UNSPECIFIED_ERR;
+
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) {
+ PR_fprintf(PR_STDERR, errStrings[NO_SUCH_TOKEN_ERR], "internal");
+ return NO_SUCH_TOKEN_ERR;
+ }
+
+ /* Set the initial password to empty */
+ if (PK11_NeedUserInit(slot)) {
+ if (PK11_InitPin(slot, NULL, "") != SECSuccess) {
+ PR_fprintf(PR_STDERR, errStrings[INITPW_FAILED_ERR]);
+ ret = INITPW_FAILED_ERR;
+ goto loser;
+ }
+ }
+
+ ret = SUCCESS;
+
+loser:
+ PK11_FreeSlot(slot);
+
+ return ret;
+}
+
+/************************************************************************
+ *
* C h a n g e P W
*/
Error
@@ -695,7 +728,7 @@ ChangePW(char *tokenName, char *pwFile, char *newpwFile)
ret = BAD_PW_ERR;
goto loser;
}
- } else {
+ } else if (PK11_NeedLogin(slot)) {
for (matching = PR_FALSE; !matching;) {
oldpw = SECU_GetPasswordString(NULL, "Enter old password: ");
if (PK11_CheckUserPassword(slot, oldpw) == SECSuccess) {
diff --git a/security/nss/cmd/multinit/multinit.c b/security/nss/cmd/multinit/multinit.c
index a57c4819f..874263e56 100644
--- a/security/nss/cmd/multinit/multinit.c
+++ b/security/nss/cmd/multinit/multinit.c
@@ -502,8 +502,7 @@ do_list_certs(const char *progName, int log)
SECU_PrintCertNickname(node, stderr);
if (log) {
- fprintf(stderr, "* Slot=%s*\n", cert->slot ? PK11_GetTokenName(cert->slot)
- : "none");
+ fprintf(stderr, "* Slot=%s*\n", cert->slot ? PK11_GetTokenName(cert->slot) : "none");
fprintf(stderr, "* Nickname=%s*\n", cert->nickname);
fprintf(stderr, "* Subject=<%s>*\n", cert->subjectName);
fprintf(stderr, "* Issuer=<%s>*\n", cert->issuerName);
diff --git a/security/nss/cmd/pk11mode/pk11mode.c b/security/nss/cmd/pk11mode/pk11mode.c
index 2f1fa374e..99891096c 100644
--- a/security/nss/cmd/pk11mode/pk11mode.c
+++ b/security/nss/cmd/pk11mode/pk11mode.c
@@ -2169,36 +2169,22 @@ PKM_Mechanism(CK_FUNCTION_LIST_PTR pFunctionList,
PKM_LogIt(" ulMinKeySize = %lu\n", minfo.ulMinKeySize);
PKM_LogIt(" ulMaxKeySize = %lu\n", minfo.ulMaxKeySize);
PKM_LogIt(" flags = 0x%08x\n", minfo.flags);
- PKM_LogIt(" -> HW = %s\n", minfo.flags & CKF_HW ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> ENCRYPT = %s\n", minfo.flags & CKF_ENCRYPT ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> DECRYPT = %s\n", minfo.flags & CKF_DECRYPT ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> DIGEST = %s\n", minfo.flags & CKF_DIGEST ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> SIGN = %s\n", minfo.flags & CKF_SIGN ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> SIGN_RECOVER = %s\n", minfo.flags &
- CKF_SIGN_RECOVER
- ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> VERIFY = %s\n", minfo.flags & CKF_VERIFY ? "TRUE"
- : "FALSE");
+ PKM_LogIt(" -> HW = %s\n", minfo.flags & CKF_HW ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> ENCRYPT = %s\n", minfo.flags & CKF_ENCRYPT ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> DECRYPT = %s\n", minfo.flags & CKF_DECRYPT ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> DIGEST = %s\n", minfo.flags & CKF_DIGEST ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> SIGN = %s\n", minfo.flags & CKF_SIGN ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> SIGN_RECOVER = %s\n", minfo.flags & CKF_SIGN_RECOVER ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> VERIFY = %s\n", minfo.flags & CKF_VERIFY ? "TRUE" : "FALSE");
PKM_LogIt(" -> VERIFY_RECOVER = %s\n",
minfo.flags & CKF_VERIFY_RECOVER ? "TRUE" : "FALSE");
- PKM_LogIt(" -> GENERATE = %s\n", minfo.flags & CKF_GENERATE ? "TRUE"
- : "FALSE");
+ PKM_LogIt(" -> GENERATE = %s\n", minfo.flags & CKF_GENERATE ? "TRUE" : "FALSE");
PKM_LogIt(" -> GENERATE_KEY_PAIR = %s\n",
minfo.flags & CKF_GENERATE_KEY_PAIR ? "TRUE" : "FALSE");
- PKM_LogIt(" -> WRAP = %s\n", minfo.flags & CKF_WRAP ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> UNWRAP = %s\n", minfo.flags & CKF_UNWRAP ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> DERIVE = %s\n", minfo.flags & CKF_DERIVE ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> EXTENSION = %s\n", minfo.flags & CKF_EXTENSION ? "TRUE"
- : "FALSE");
+ PKM_LogIt(" -> WRAP = %s\n", minfo.flags & CKF_WRAP ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> UNWRAP = %s\n", minfo.flags & CKF_UNWRAP ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> DERIVE = %s\n", minfo.flags & CKF_DERIVE ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> EXTENSION = %s\n", minfo.flags & CKF_EXTENSION ? "TRUE" : "FALSE");
PKM_LogIt("\n");
}
@@ -3604,24 +3590,12 @@ PKM_FindAllObjects(CK_FUNCTION_LIST_PTR pFunctionList,
PKM_LogIt(" state = %lu\n", sinfo.state);
PKM_LogIt(" flags = 0x%08x\n", sinfo.flags);
#ifdef CKF_EXCLUSIVE_SESSION
- PKM_LogIt(" -> EXCLUSIVE SESSION = %s\n", sinfo.flags &
- CKF_EXCLUSIVE_SESSION
- ? "TRUE"
- : "FALSE");
+ PKM_LogIt(" -> EXCLUSIVE SESSION = %s\n", sinfo.flags & CKF_EXCLUSIVE_SESSION ? "TRUE" : "FALSE");
#endif /* CKF_EXCLUSIVE_SESSION */
- PKM_LogIt(" -> RW SESSION = %s\n", sinfo.flags &
- CKF_RW_SESSION
- ? "TRUE"
- : "FALSE");
- PKM_LogIt(" -> SERIAL SESSION = %s\n", sinfo.flags &
- CKF_SERIAL_SESSION
- ? "TRUE"
- : "FALSE");
+ PKM_LogIt(" -> RW SESSION = %s\n", sinfo.flags & CKF_RW_SESSION ? "TRUE" : "FALSE");
+ PKM_LogIt(" -> SERIAL SESSION = %s\n", sinfo.flags & CKF_SERIAL_SESSION ? "TRUE" : "FALSE");
#ifdef CKF_INSERTION_CALLBACK
- PKM_LogIt(" -> INSERTION CALLBACK = %s\n", sinfo.flags &
- CKF_INSERTION_CALLBACK
- ? "TRUE"
- : "FALSE");
+ PKM_LogIt(" -> INSERTION CALLBACK = %s\n", sinfo.flags & CKF_INSERTION_CALLBACK ? "TRUE" : "FALSE");
#endif /* CKF_INSERTION_CALLBACK */
PKM_LogIt(" ulDeviceError = %lu\n", sinfo.ulDeviceError);
PKM_LogIt("\n");
diff --git a/security/nss/cmd/pk12util/pk12util.c b/security/nss/cmd/pk12util/pk12util.c
index 0ac1ba00e..70454a0d8 100644
--- a/security/nss/cmd/pk12util/pk12util.c
+++ b/security/nss/cmd/pk12util/pk12util.c
@@ -23,6 +23,7 @@
static char *progName;
PRBool pk12_debugging = PR_FALSE;
PRBool dumpRawFile;
+static PRBool pk12uForceUnicode;
PRIntn pk12uErrno = 0;
@@ -357,6 +358,7 @@ p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
SECItem p12file = { 0 };
SECStatus rv = SECFailure;
PRBool swapUnicode = PR_FALSE;
+ PRBool forceUnicode = pk12uForceUnicode;
PRBool trypw;
int error;
@@ -424,6 +426,18 @@ p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
SEC_PKCS12DecoderFinish(p12dcx);
uniPwp->len = 0;
trypw = PR_TRUE;
+ } else if (forceUnicode == pk12uForceUnicode) {
+ /* try again with a different password encoding */
+ forceUnicode = !pk12uForceUnicode;
+ rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE,
+ forceUnicode);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "PKCS12 decoding failed to set option");
+ pk12uErrno = PK12UERR_DECODEVERIFY;
+ break;
+ }
+ SEC_PKCS12DecoderFinish(p12dcx);
+ trypw = PR_TRUE;
} else {
SECU_PrintError(progName, "PKCS12 decode not verified");
pk12uErrno = PK12UERR_DECODEVERIFY;
@@ -431,6 +445,15 @@ p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
}
}
} while (trypw == PR_TRUE);
+
+ /* revert the option setting */
+ if (forceUnicode != pk12uForceUnicode) {
+ rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "PKCS12 decoding failed to set option");
+ pk12uErrno = PK12UERR_DECODEVERIFY;
+ }
+ }
/* rv has been set at this point */
done:
@@ -470,6 +493,8 @@ P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot,
{
SEC_PKCS12DecoderContext *p12dcx = NULL;
SECItem uniPwitem = { 0 };
+ PRBool forceUnicode = pk12uForceUnicode;
+ PRBool trypw;
SECStatus rv = SECFailure;
rv = P12U_InitSlot(slot, slotPw);
@@ -480,31 +505,62 @@ P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot,
return rv;
}
- rv = SECFailure;
- p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
+ do {
+ trypw = PR_FALSE; /* normally we do this once */
+ rv = SECFailure;
+ p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
- if (p12dcx == NULL) {
- goto loser;
- }
+ if (p12dcx == NULL) {
+ goto loser;
+ }
- /* make sure the bags are okey dokey -- nicknames correct, etc. */
- rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback);
- if (rv != SECSuccess) {
- if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
- pk12uErrno = PK12UERR_CERTALREADYEXISTS;
- } else {
- pk12uErrno = PK12UERR_DECODEVALIBAGS;
+ /* make sure the bags are okey dokey -- nicknames correct, etc. */
+ rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
+ pk12uErrno = PK12UERR_CERTALREADYEXISTS;
+ } else {
+ pk12uErrno = PK12UERR_DECODEVALIBAGS;
+ }
+ SECU_PrintError(progName, "PKCS12 decode validate bags failed");
+ goto loser;
}
- SECU_PrintError(progName, "PKCS12 decode validate bags failed");
- goto loser;
- }
- /* stuff 'em in */
- rv = SEC_PKCS12DecoderImportBags(p12dcx);
- if (rv != SECSuccess) {
- SECU_PrintError(progName, "PKCS12 decode import bags failed");
- pk12uErrno = PK12UERR_DECODEIMPTBAGS;
- goto loser;
+ /* stuff 'em in */
+ if (forceUnicode != pk12uForceUnicode) {
+ rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE,
+ forceUnicode);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "PKCS12 decode set option failed");
+ pk12uErrno = PK12UERR_DECODEIMPTBAGS;
+ goto loser;
+ }
+ }
+ rv = SEC_PKCS12DecoderImportBags(p12dcx);
+ if (rv != SECSuccess) {
+ if (PR_GetError() == SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY &&
+ forceUnicode == pk12uForceUnicode) {
+ /* try again with a different password encoding */
+ forceUnicode = !pk12uForceUnicode;
+ SEC_PKCS12DecoderFinish(p12dcx);
+ SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
+ trypw = PR_TRUE;
+ } else {
+ SECU_PrintError(progName, "PKCS12 decode import bags failed");
+ pk12uErrno = PK12UERR_DECODEIMPTBAGS;
+ goto loser;
+ }
+ }
+ } while (trypw);
+
+ /* revert the option setting */
+ if (forceUnicode != pk12uForceUnicode) {
+ rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "PKCS12 decode set option failed");
+ pk12uErrno = PK12UERR_DECODEIMPTBAGS;
+ goto loser;
+ }
}
fprintf(stdout, "%s: PKCS12 IMPORT SUCCESSFUL\n", progName);
@@ -947,6 +1003,7 @@ main(int argc, char **argv)
int keyLen = 0;
int certKeyLen = 0;
secuCommand pk12util;
+ PRInt32 forceUnicode;
#ifdef _CRTDBG_MAP_ALLOC
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
@@ -978,6 +1035,14 @@ main(int argc, char **argv)
Usage(progName);
}
+ rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName,
+ "Failed to get NSS_PKCS12_DECODE_FORCE_UNICODE option");
+ Usage(progName);
+ }
+ pk12uForceUnicode = forceUnicode;
+
slotname = SECU_GetOptionArg(&pk12util, opt_TokenName);
import_file = (pk12util.options[opt_List].activated) ? SECU_GetOptionArg(&pk12util, opt_List)
diff --git a/security/nss/cmd/pp/pp.c b/security/nss/cmd/pp/pp.c
index 9f33d10a4..d6e276834 100644
--- a/security/nss/cmd/pp/pp.c
+++ b/security/nss/cmd/pp/pp.c
@@ -84,6 +84,8 @@ main(int argc, char **argv)
if (!inFile) {
fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
progName, optstate->value);
+ PORT_Free(typeTag);
+ PL_DestroyOptState(optstate);
return -1;
}
break;
@@ -93,6 +95,8 @@ main(int argc, char **argv)
if (!outFile) {
fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
progName, optstate->value);
+ PORT_Free(typeTag);
+ PL_DestroyOptState(optstate);
return -1;
}
break;
diff --git a/security/nss/cmd/rsaperf/rsaperf.c b/security/nss/cmd/rsaperf/rsaperf.c
index 556030f6a..2bb23856e 100644
--- a/security/nss/cmd/rsaperf/rsaperf.c
+++ b/security/nss/cmd/rsaperf/rsaperf.c
@@ -671,8 +671,7 @@ main(int argc, char **argv)
printf("%ld iterations in %s\n",
iters, TimingGenerateString(timeCtx));
- printf("%.2f operations/s .\n", ((double)(iters) * (double)1000000.0) /
- (double)timeCtx->interval);
+ printf("%.2f operations/s .\n", ((double)(iters) * (double)1000000.0) / (double)timeCtx->interval);
TimingDivide(timeCtx, iters);
printf("one operation every %s\n", TimingGenerateString(timeCtx));
diff --git a/security/nss/cmd/rsapoptst/rsapoptst.c b/security/nss/cmd/rsapoptst/rsapoptst.c
index 81ddcd6c4..d9468e6d6 100644
--- a/security/nss/cmd/rsapoptst/rsapoptst.c
+++ b/security/nss/cmd/rsapoptst/rsapoptst.c
@@ -23,7 +23,7 @@ static const struct test_args test_array[] = {
{ "d_n_q", 0x02, "private exponent, modulus, prime2" },
{ "d_p_q", 0x04, "private exponent, prime1, prime2" },
{ "e_d_q", 0x08, "public exponent, private exponent, prime2" },
- { "e_d_n", 0x10, "public exponent, private exponent, moduls" }
+ { "e_d_n", 0x10, "public exponent, private exponent, modulus" }
};
static const int test_array_size =
(sizeof(test_array) / sizeof(struct test_args));
@@ -58,6 +58,7 @@ const static CK_ATTRIBUTE rsaTemplate[] = {
{ CKA_TOKEN, NULL, 0 },
{ CKA_SENSITIVE, NULL, 0 },
{ CKA_PRIVATE, NULL, 0 },
+ { CKA_ID, NULL, 0 },
{ CKA_MODULUS, NULL, 0 },
{ CKA_PUBLIC_EXPONENT, NULL, 0 },
{ CKA_PRIVATE_EXPONENT, NULL, 0 },
@@ -123,46 +124,77 @@ fail:
#define ATTR_STRING(x) getNameFromAttribute(x)
+static void
+dumphex(FILE *file, const unsigned char *cpval, int start, int end)
+{
+ int i;
+ for (i = start; i < end; i++) {
+ if ((i % 16) == 0)
+ fprintf(file, "\n ");
+ fprintf(file, " %02x", cpval[i]);
+ }
+ return;
+}
+
void
-dumpTemplate(CK_ATTRIBUTE *template, int start, int end)
+dumpTemplate(FILE *file, const CK_ATTRIBUTE *template, int start, int end)
{
- int i, j;
- for (i = 0; i < end; i++) {
+ int i;
+ for (i = start; i < end; i++) {
unsigned char cval;
CK_ULONG ulval;
- unsigned char *cpval;
+ const unsigned char *cpval;
- fprintf(stderr, "%s:", ATTR_STRING(template[i].type));
+ fprintf(file, "%s:", ATTR_STRING(template[i].type));
switch (template[i].ulValueLen) {
case 1:
cval = *(unsigned char *)template[i].pValue;
switch (cval) {
case 0:
- fprintf(stderr, " false");
+ fprintf(file, " false");
break;
case 1:
- fprintf(stderr, " true");
+ fprintf(file, " true");
break;
default:
- fprintf(stderr, " %d (=0x%02x,'%c')", cval, cval, cval);
+ fprintf(file, " %d (=0x%02x,'%c')", cval, cval, cval);
break;
}
break;
case sizeof(CK_ULONG):
ulval = *(CK_ULONG *)template[i].pValue;
- fprintf(stderr, " %ld (=0x%04lx)", ulval, ulval);
+ fprintf(file, " %ld (=0x%04lx)", ulval, ulval);
break;
default:
- cpval = (unsigned char *)template[i].pValue;
- for (j = 0; j < template[i].ulValueLen; j++) {
- if ((j % 16) == 0)
- fprintf(stderr, "\n ");
- fprintf(stderr, " %02x", cpval[j]);
- }
+ cpval = (const unsigned char *)template[i].pValue;
+ dumphex(file, cpval, 0, template[i].ulValueLen);
break;
}
- fprintf(stderr, "\n");
+ fprintf(file, "\n");
+ }
+}
+
+void
+dumpItem(FILE *file, const SECItem *item)
+{
+ const unsigned char *cpval;
+
+ if (item == NULL) {
+ fprintf(file, " pNULL ");
+ return;
+ }
+ if (item->data == NULL) {
+ fprintf(file, " NULL ");
+ return;
}
+ if (item->len == 0) {
+ fprintf(file, " Empty ");
+ return;
+ }
+ cpval = item->data;
+ dumphex(file, cpval, 0, item->len);
+ fprintf(file, " ");
+ return;
}
PRBool
@@ -184,13 +216,16 @@ rsaKeysAreEqual(PK11ObjectType srcType, void *src,
printf("Could read source key\n");
return PR_FALSE;
}
- readKey(destType, dest, destTemplate, 0, RSA_ATTRIBUTES);
+ rv = readKey(destType, dest, destTemplate, 0, RSA_ATTRIBUTES);
if (rv != SECSuccess) {
printf("Could read dest key\n");
return PR_FALSE;
}
for (i = 0; i < RSA_ATTRIBUTES; i++) {
+ if (srcTemplate[i].type == CKA_ID) {
+ continue; /* we purposefully make the CKA_ID different */
+ }
if (srcTemplate[i].ulValueLen != destTemplate[i].ulValueLen) {
printf("key->%s not equal src_len = %ld, dest_len=%ld\n",
ATTR_STRING(srcTemplate[i].type),
@@ -204,18 +239,22 @@ rsaKeysAreEqual(PK11ObjectType srcType, void *src,
}
if (!areEqual) {
fprintf(stderr, "original key:\n");
- dumpTemplate(srcTemplate, 0, RSA_ATTRIBUTES);
+ dumpTemplate(stderr, srcTemplate, 0, RSA_ATTRIBUTES);
fprintf(stderr, "created key:\n");
- dumpTemplate(destTemplate, 0, RSA_ATTRIBUTES);
+ dumpTemplate(stderr, destTemplate, 0, RSA_ATTRIBUTES);
}
+ resetTemplate(srcTemplate, 0, RSA_ATTRIBUTES);
+ resetTemplate(destTemplate, 0, RSA_ATTRIBUTES);
return areEqual;
}
static int exp_exp_prime_fail_count = 0;
+#define LEAK_ID 0xf
+
static int
doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
- int mask, void *pwarg)
+ int mask, int round, void *pwarg)
{
SECKEYPrivateKey *rsaPrivKey;
SECKEYPublicKey *rsaPubKey;
@@ -227,7 +266,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY;
CK_KEY_TYPE key_type = CKK_RSA;
CK_BBOOL ck_false = CK_FALSE;
+ CK_BYTE cka_id[2] = { 0, 0 };
int failed = 0;
+ int leak_found; /* did we find the expected leak */
+ int expect_leak = 0; /* are we expecting a leak? */
rsaParams.pe = exponent;
rsaParams.keySizeInBits = keySize;
@@ -259,11 +301,15 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
tstTemplate[3].ulValueLen = sizeof(ck_false);
tstTemplate[4].pValue = &ck_false;
tstTemplate[4].ulValueLen = sizeof(ck_false);
- tstHeaderCount = 5;
+ tstTemplate[5].pValue = &cka_id[0];
+ tstTemplate[5].ulValueLen = sizeof(cka_id);
+ tstHeaderCount = 6;
+ cka_id[0] = round;
if (mask & 1) {
printf("%s\n", test_array[1].description);
resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES);
+ cka_id[1] = 0;
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount, CKA_PUBLIC_EXPONENT);
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
@@ -271,10 +317,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount + 2, CKA_PRIME_1);
- tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate,
- tstHeaderCount +
- 3,
- PR_FALSE);
+ tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate,
+ tstHeaderCount +
+ 3,
+ PR_FALSE);
if (tstPrivKey == NULL) {
fprintf(stderr, "RSA Populate failed: pubExp mod p\n");
failed = 1;
@@ -290,6 +336,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
printf("%s\n", test_array[2].description);
/* test the basic2 case, public exponent, modulus, prime2 */
resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES);
+ cka_id[1] = 1;
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount, CKA_PUBLIC_EXPONENT);
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
@@ -299,10 +346,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
/* test with q in the prime1 position */
tstTemplate[tstHeaderCount + 2].type = CKA_PRIME_1;
- tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate,
- tstHeaderCount +
- 3,
- PR_FALSE);
+ tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate,
+ tstHeaderCount +
+ 3,
+ PR_FALSE);
if (tstPrivKey == NULL) {
fprintf(stderr, "RSA Populate failed: pubExp mod q\n");
failed = 1;
@@ -318,6 +365,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
printf("%s\n", test_array[3].description);
/* test the medium case, private exponent, prime1, prime2 */
resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES);
+ cka_id[1] = 2;
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount, CKA_PRIVATE_EXPONENT);
@@ -329,10 +377,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
tstTemplate[tstHeaderCount + 2].type = CKA_PRIME_1;
tstTemplate[tstHeaderCount + 1].type = CKA_PRIME_2;
- tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate,
- tstHeaderCount +
- 3,
- PR_FALSE);
+ tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate,
+ tstHeaderCount +
+ 3,
+ PR_FALSE);
if (tstPrivKey == NULL) {
fprintf(stderr, "RSA Populate failed: privExp p q\n");
failed = 1;
@@ -348,6 +396,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
printf("%s\n", test_array[4].description);
/* test the advanced case, public exponent, private exponent, prime2 */
resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES);
+ cka_id[1] = 3;
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount, CKA_PRIVATE_EXPONENT);
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
@@ -355,10 +404,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount + 2, CKA_PRIME_2);
- tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate,
- tstHeaderCount +
- 3,
- PR_FALSE);
+ tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate,
+ tstHeaderCount +
+ 3,
+ PR_FALSE);
if (tstPrivKey == NULL) {
fprintf(stderr, "RSA Populate failed: pubExp privExp q\n");
fprintf(stderr, " this is expected periodically. It means we\n");
@@ -373,11 +422,12 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
if (tstPrivKey)
PK11_DestroyGenericObject(tstPrivKey);
}
- if (mask & 16) {
+ if (mask & 0x10) {
printf("%s\n", test_array[5].description);
/* test the advanced case2, public exponent, private exponent, modulus
*/
resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES);
+ cka_id[1] = LEAK_ID;
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount, CKA_PRIVATE_EXPONENT);
@@ -386,6 +436,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate,
tstHeaderCount + 2, CKA_MODULUS);
+ /* purposefully use the old version. This will create a leak */
tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate,
tstHeaderCount +
3,
@@ -398,9 +449,59 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent,
fprintf(stderr, "RSA Populate key mismatch: pubExp privExp mod\n");
failed = 1;
}
+ expect_leak = 1;
if (tstPrivKey)
PK11_DestroyGenericObject(tstPrivKey);
}
+ resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES);
+ SECKEY_DestroyPrivateKey(rsaPrivKey);
+ SECKEY_DestroyPublicKey(rsaPubKey);
+
+ /* make sure we didn't leak */
+ leak_found = 0;
+ tstPrivKey = PK11_FindGenericObjects(slot, CKO_PRIVATE_KEY);
+ if (tstPrivKey) {
+ SECStatus rv;
+ PK11GenericObject *thisKey;
+ int i;
+
+ fprintf(stderr, "Leaking keys...\n");
+ for (i = 0, thisKey = tstPrivKey; thisKey; i++,
+ thisKey = PK11_GetNextGenericObject(thisKey)) {
+ SECItem id = { 0, NULL, 0 };
+
+ rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisKey,
+ CKA_ID, &id);
+ if (rv != SECSuccess) {
+ fprintf(stderr, "Key %d: couldn't read CKA_ID: %s\n",
+ i, PORT_ErrorToString(PORT_GetError()));
+ continue;
+ }
+ fprintf(stderr, "id = { ");
+ dumpItem(stderr, &id);
+ fprintf(stderr, "};");
+ if (id.data[1] == LEAK_ID) {
+ fprintf(stderr, " ---> leak expected\n");
+ if (id.data[0] == round)
+ leak_found = 1;
+ } else {
+ if (id.len != sizeof(cka_id)) {
+ fprintf(stderr,
+ " ---> ERROR unexpected leak in generated key\n");
+ } else {
+ fprintf(stderr,
+ " ---> ERROR unexpected leak in constructed key\n");
+ }
+ failed = 1;
+ }
+ SECITEM_FreeItem(&id, PR_FALSE);
+ }
+ PK11_DestroyGenericObjects(tstPrivKey);
+ }
+ if (expect_leak && !leak_found) {
+ fprintf(stderr, "ERROR expected leak not found\n");
+ failed = 1;
+ }
PK11_FreeSlot(slot);
return failed ? -1 : 0;
@@ -517,7 +618,7 @@ main(int argc, char **argv)
exp_exp_prime_fail_count = 0;
for (i = 0; i < repeat; i++) {
printf("Running RSA Populate test run %d\n", i);
- ret = doRSAPopulateTest(keySize, exponent, mask, NULL);
+ ret = doRSAPopulateTest(keySize, exponent, mask, i, NULL);
if (ret != 0) {
i++;
break;
@@ -531,5 +632,9 @@ main(int argc, char **argv)
exp_exp_prime_fail_count, i,
(((double)exp_exp_prime_fail_count) * 100.0) / (double)i);
}
+ if (NSS_Shutdown() != SECSuccess) {
+ fprintf(stderr, "Shutdown failed\n");
+ ret = -1;
+ }
return ret;
}
diff --git a/security/nss/cmd/rsapoptst/rsapoptst.gyp b/security/nss/cmd/rsapoptst/rsapoptst.gyp
new file mode 100644
index 000000000..325a10909
--- /dev/null
+++ b/security/nss/cmd/rsapoptst/rsapoptst.gyp
@@ -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/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi',
+ '../../cmd/platlibs.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'rsapoptst',
+ 'type': 'executable',
+ 'sources': [
+ 'rsapoptst.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:dbm_exports',
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss',
+ }
+}
diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c
index 65b1ee304..fac428e10 100644
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -38,6 +38,7 @@
#include "nss.h"
#include "ssl.h"
#include "sslproto.h"
+#include "sslexp.h"
#include "cert.h"
#include "certt.h"
#include "ocsp.h"
@@ -165,9 +166,7 @@ PrintUsageHeader(const char *progName)
" [-V [min-version]:[max-version]] [-a sni_name]\n"
" [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n"
" [-C SSLCacheEntries] [-S dsa_nickname] -Q [-I groups]"
-#ifndef NSS_DISABLE_ECC
" [-e ec_nickname]"
-#endif /* NSS_DISABLE_ECC */
"\n"
" -U [0|1] -H [0|1|2] -W [0|1]\n"
"\n",
@@ -1955,6 +1954,10 @@ server_main(
if (enabledVersions.max < SSL_LIBRARY_VERSION_TLS_1_3) {
errExit("You tried enabling 0RTT without enabling TLS 1.3!");
}
+ rv = SSL_SetupAntiReplay(10 * PR_USEC_PER_SEC, 7, 14);
+ if (rv != SECSuccess) {
+ errExit("error configuring anti-replay ");
+ }
rv = SSL_OptionSet(model_sock, SSL_ENABLE_0RTT_DATA, PR_TRUE);
if (rv != SECSuccess) {
errExit("error enabling 0RTT ");
@@ -2343,7 +2346,6 @@ main(int argc, char **argv)
dir = optstate->value;
break;
-#ifndef NSS_DISABLE_ECC
case 'e':
if (certNicknameIndex >= MAX_CERT_NICKNAME_ARRAY_INDEX) {
Usage(progName);
@@ -2351,7 +2353,6 @@ main(int argc, char **argv)
}
certNicknameArray[certNicknameIndex++] = PORT_Strdup(optstate->value);
break;
-#endif /* NSS_DISABLE_ECC */
case 'f':
pwdata.source = PW_FROMFILE;
@@ -2553,6 +2554,14 @@ main(int argc, char **argv)
tmp = PR_GetEnvSecure("TMPDIR");
if (!tmp)
tmp = PR_GetEnvSecure("TEMP");
+
+ /* Call the NSS initialization routines */
+ rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
+ if (rv != SECSuccess) {
+ fputs("NSS_Init failed.\n", stderr);
+ exit(8);
+ }
+
if (envString) {
/* we're one of the children in a multi-process server. */
listen_sock = PR_GetInheritedFD(inheritableSockName);
@@ -2607,13 +2616,6 @@ main(int argc, char **argv)
/* set our password function */
PK11_SetPasswordFunc(SECU_GetModulePassword);
- /* Call the NSS initialization routines */
- rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
- if (rv != SECSuccess) {
- fputs("NSS_Init failed.\n", stderr);
- exit(8);
- }
-
/* all SSL3 cipher suites are enabled by default. */
if (cipherString) {
char *cstringSaved = cipherString;
@@ -2681,9 +2683,7 @@ main(int argc, char **argv)
certNicknameArray[i]);
exit(11);
}
-#ifdef NSS_DISABLE_ECC
if (privKey[i]->keyType != ecKey)
-#endif
setupCertStatus(certStatusArena, ocspStaplingMode, cert[i], i, &pwdata);
}
diff --git a/security/nss/cmd/signtool/javascript.c b/security/nss/cmd/signtool/javascript.c
index 746f724f8..ffff2db59 100644
--- a/security/nss/cmd/signtool/javascript.c
+++ b/security/nss/cmd/signtool/javascript.c
@@ -1115,8 +1115,7 @@ extract_js(char *filename)
textStart = 0;
startLine = 0;
- while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) !=
- EOF) {
+ while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) != EOF) {
switch (state) {
case TEXT_HTML_STATE:
if (curchar == '<') {
diff --git a/security/nss/cmd/signtool/signtool.c b/security/nss/cmd/signtool/signtool.c
index 51857d638..915a00fbc 100644
--- a/security/nss/cmd/signtool/signtool.c
+++ b/security/nss/cmd/signtool/signtool.c
@@ -1033,9 +1033,7 @@ main(int argc, char *argv[])
if (errorCount > 0 || warningCount > 0) {
PR_fprintf(outputFD, "%d error%s, %d warning%s.\n",
errorCount,
- errorCount == 1 ? "" : "s", warningCount, warningCount == 1
- ? ""
- : "s");
+ errorCount == 1 ? "" : "s", warningCount, warningCount == 1 ? "" : "s");
} else {
PR_fprintf(outputFD, "Directory %s signed successfully.\n",
jartree);
diff --git a/security/nss/cmd/smimetools/cmsutil.c b/security/nss/cmd/smimetools/cmsutil.c
index 10e743c6b..fe17f26a4 100644
--- a/security/nss/cmd/smimetools/cmsutil.c
+++ b/security/nss/cmd/smimetools/cmsutil.c
@@ -1572,10 +1572,7 @@ main(int argc, char **argv)
{
unsigned int j;
for (j = 0; j < input.len; j++)
- fprintf(stderr, "%2x%c", input.data[j], (j > 0 &&
- j % 35 == 0)
- ? '\n'
- : ' ');
+ fprintf(stderr, "%2x%c", input.data[j], (j > 0 && j % 35 == 0) ? '\n' : ' ');
}
}
if (input.len > 0) { /* skip if certs-only (or other zero content) */
diff --git a/security/nss/cmd/ssltap/ssltap.c b/security/nss/cmd/ssltap/ssltap.c
index 197b1942d..a2471884e 100644
--- a/security/nss/cmd/ssltap/ssltap.c
+++ b/security/nss/cmd/ssltap/ssltap.c
@@ -1637,8 +1637,7 @@ print_ssl3_handshake(unsigned char *recordBuf,
PR_snprintf(certFileName, sizeof certFileName, "cert.%03d",
++certFileNumber);
cfd =
- PR_Open(certFileName, PR_WRONLY |
- PR_CREATE_FILE | PR_TRUNCATE,
+ PR_Open(certFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0664);
if (!cfd) {
PR_fprintf(PR_STDOUT,
@@ -1722,8 +1721,7 @@ print_ssl3_handshake(unsigned char *recordBuf,
0 &&
sslhexparse) {
PR_fprintf(PR_STDOUT, " = {\n");
- print_hex(dnLen, hsdata +
- pos);
+ print_hex(dnLen, hsdata + pos);
PR_fprintf(PR_STDOUT, " }\n");
} else {
PR_fprintf(PR_STDOUT, "\n");
@@ -1796,8 +1794,7 @@ print_ssl3_handshake(unsigned char *recordBuf,
PR_snprintf(ocspFileName, sizeof ocspFileName, "ocsp.%03d",
++ocspFileNumber);
- ofd = PR_Open(ocspFileName, PR_WRONLY |
- PR_CREATE_FILE | PR_TRUNCATE,
+ ofd = PR_Open(ocspFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0664);
if (!ofd) {
PR_fprintf(PR_STDOUT,
@@ -2167,8 +2164,7 @@ print_ssl(DataBufferList *s, int length, unsigned char *buffer)
break;
case 22: /* handshake */
- print_ssl3_handshake(recordBuf, recordLen -
- s->hMACsize,
+ print_ssl3_handshake(recordBuf, recordLen - s->hMACsize,
&sr, s);
break;
diff --git a/security/nss/cmd/strsclnt/strsclnt.c b/security/nss/cmd/strsclnt/strsclnt.c
index f65e31913..7d259bd0a 100644
--- a/security/nss/cmd/strsclnt/strsclnt.c
+++ b/security/nss/cmd/strsclnt/strsclnt.c
@@ -886,8 +886,10 @@ PRBool
LoggedIn(CERTCertificate *cert, SECKEYPrivateKey *key)
{
if ((cert->slot) && (key->pkcs11Slot) &&
- (PR_TRUE == PK11_IsLoggedIn(cert->slot, NULL)) &&
- (PR_TRUE == PK11_IsLoggedIn(key->pkcs11Slot, NULL))) {
+ (!PK11_NeedLogin(cert->slot) ||
+ PR_TRUE == PK11_IsLoggedIn(cert->slot, NULL)) &&
+ (!PK11_NeedLogin(key->pkcs11Slot) ||
+ PR_TRUE == PK11_IsLoggedIn(key->pkcs11Slot, NULL))) {
return PR_TRUE;
}
diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c
index 959afec59..1ad99502b 100644
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -31,6 +31,7 @@
#include "ocsp.h"
#include "ssl.h"
#include "sslproto.h"
+#include "sslexp.h"
#include "pk11func.h"
#include "secmod.h"
#include "plgetopt.h"
@@ -95,6 +96,7 @@ PRBool verbose;
int dumpServerChain = 0;
int renegotiationsToDo = 0;
int renegotiationsDone = 0;
+PRBool initializedServerSessionCache = PR_FALSE;
static char *progName;
@@ -178,7 +180,7 @@ PrintUsageHeader(const char *progName)
"[-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z]\n"
"[-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
"[-r N] [-w passwd] [-W pwfile] [-q [-t seconds]] [-I groups]\n"
- "[-A requestfile] [-L totalconnections]\n"
+ "[-A requestfile] [-L totalconnections] [-P {client,server}] [-Q]\n"
"\n",
progName);
}
@@ -202,7 +204,7 @@ PrintParameterUsage(void)
fprintf(stderr, "%-20s Print certificate chain information\n", "-C");
fprintf(stderr, "%-20s (use -C twice to print more certificate details)\n", "");
fprintf(stderr, "%-20s (use -C three times to include PEM format certificate dumps)\n", "");
- fprintf(stderr, "%-20s Nickname of key and cert for client auth\n",
+ fprintf(stderr, "%-20s Nickname of key and cert\n",
"-n nickname");
fprintf(stderr,
"%-20s Restricts the set of enabled SSL/TLS protocols versions.\n"
@@ -251,6 +253,9 @@ PrintParameterUsage(void)
"%-20s The following values are valid:\n"
"%-20s P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n",
"-I", "", "");
+ fprintf(stderr, "%-20s Enable alternative TLS 1.3 handshake\n", "-X alt-server-hello");
+ fprintf(stderr, "%-20s Use DTLS\n", "-P {client, server}");
+ fprintf(stderr, "%-20s Exit after handshake\n", "-Q");
}
static void
@@ -914,6 +919,12 @@ char *requestString = NULL;
PRInt32 requestStringLen = 0;
PRBool requestSent = PR_FALSE;
PRBool enableZeroRtt = PR_FALSE;
+PRBool enableAltServerHello = PR_FALSE;
+PRBool useDTLS = PR_FALSE;
+PRBool actAsServer = PR_FALSE;
+PRBool stopAfterHandshake = PR_FALSE;
+PRBool requestToExit = PR_FALSE;
+char *versionString = NULL;
static int
writeBytesToServer(PRFileDesc *s, const char *buf, int nb)
@@ -996,12 +1007,129 @@ handshakeCallback(PRFileDesc *fd, void *client_data)
writeBytesToServer(fd, requestString, requestStringLen);
}
}
+ if (stopAfterHandshake) {
+ requestToExit = PR_TRUE;
+ }
}
#define REQUEST_WAITING (requestString && !requestSent)
+static SECStatus
+installServerCertificate(PRFileDesc *s, char *nickname)
+{
+ CERTCertificate *cert;
+ SECKEYPrivateKey *privKey = NULL;
+
+ if (!nickname) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ cert = PK11_FindCertFromNickname(nickname, &pwdata);
+ if (cert == NULL) {
+ return SECFailure;
+ }
+
+ privKey = PK11_FindKeyByAnyCert(cert, &pwdata);
+ if (privKey == NULL) {
+ return SECFailure;
+ }
+ if (SSL_ConfigServerCert(s, cert, privKey, NULL, 0) != SECSuccess) {
+ return SECFailure;
+ }
+ SECKEY_DestroyPrivateKey(privKey);
+ CERT_DestroyCertificate(cert);
+
+ return SECSuccess;
+}
+
+static SECStatus
+bindToClient(PRFileDesc *s)
+{
+ PRStatus status;
+ status = PR_Bind(s, &addr);
+ if (status != PR_SUCCESS) {
+ return SECFailure;
+ }
+
+ for (;;) {
+ /* Bind the remote address on first packet. This must happen
+ * before we SSL-ize the socket because we need to get the
+ * peer's address before SSLizing. Recvfrom gives us that
+ * while not consuming any data. */
+ unsigned char tmp;
+ PRNetAddr remote;
+ int nb;
+
+ nb = PR_RecvFrom(s, &tmp, 1, PR_MSG_PEEK,
+ &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (nb != 1)
+ continue;
+
+ status = PR_Connect(s, &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (status != PR_SUCCESS) {
+ SECU_PrintError(progName, "server bind to remote end failed");
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+
+ /* Unreachable. */
+}
+
+static SECStatus
+connectToServer(PRFileDesc *s, PRPollDesc *pollset)
+{
+ PRStatus status;
+ PRInt32 filesReady;
+
+ status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (status != PR_SUCCESS) {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ if (verbose)
+ SECU_PrintError(progName, "connect");
+ milliPause(50 * multiplier);
+ pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ pollset[SSOCK_FD].out_flags = 0;
+ pollset[SSOCK_FD].fd = s;
+ while (1) {
+ FPRINTF(stderr,
+ "%s: about to call PR_Poll for connect completion!\n",
+ progName);
+ filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (filesReady < 0) {
+ SECU_PrintError(progName, "unable to connect (poll)");
+ return SECFailure;
+ }
+ FPRINTF(stderr,
+ "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
+ progName, pollset[SSOCK_FD].out_flags);
+ if (filesReady == 0) { /* shouldn't happen! */
+ SECU_PrintError(progName, "%s: PR_Poll returned zero!\n");
+ return SECFailure;
+ }
+ status = PR_GetConnectStatus(pollset);
+ if (status == PR_SUCCESS) {
+ break;
+ }
+ if (PR_GetError() != PR_IN_PROGRESS_ERROR) {
+ SECU_PrintError(progName, "unable to connect (poll)");
+ return SECFailure;
+ }
+ SECU_PrintError(progName, "poll");
+ milliPause(50 * multiplier);
+ }
+ } else {
+ SECU_PrintError(progName, "unable to connect");
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
static int
-run_client(void)
+run(void)
{
int headerSeparatorPtrnId = 0;
int error = 0;
@@ -1017,13 +1145,23 @@ run_client(void)
requestSent = PR_FALSE;
/* Create socket */
- s = PR_OpenTCPSocket(addr.raw.family);
+ if (useDTLS) {
+ s = PR_OpenUDPSocket(addr.raw.family);
+ } else {
+ s = PR_OpenTCPSocket(addr.raw.family);
+ }
+
if (s == NULL) {
SECU_PrintError(progName, "error creating socket");
error = 1;
goto done;
}
+ if (actAsServer) {
+ if (bindToClient(s) != SECSuccess) {
+ return 1;
+ }
+ }
opt.option = PR_SockOpt_Nonblocking;
opt.value.non_blocking = PR_TRUE; /* default */
if (serverCertAuth.testFreshStatusFromSideChannel) {
@@ -1036,13 +1174,16 @@ run_client(void)
goto done;
}
- s = SSL_ImportFD(NULL, s);
+ if (useDTLS) {
+ s = DTLS_ImportFD(NULL, s);
+ } else {
+ s = SSL_ImportFD(NULL, s);
+ }
if (s == NULL) {
SECU_PrintError(progName, "error importing socket");
error = 1;
goto done;
}
-
SSL_SetPKCS11PinArg(s, &pwdata);
rv = SSL_OptionSet(s, SSL_SECURITY, 1);
@@ -1052,7 +1193,7 @@ run_client(void)
goto done;
}
- rv = SSL_OptionSet(s, SSL_HANDSHAKE_AS_CLIENT, 1);
+ rv = SSL_OptionSet(s, actAsServer ? SSL_HANDSHAKE_AS_SERVER : SSL_HANDSHAKE_AS_CLIENT, 1);
if (rv != SECSuccess) {
SECU_PrintError(progName, "error enabling client handshake");
error = 1;
@@ -1178,6 +1319,16 @@ run_client(void)
}
}
+ /* Alternate ServerHello content type (TLS 1.3 only) */
+ if (enableAltServerHello) {
+ rv = SSL_UseAltServerHelloType(s, PR_TRUE);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error enabling alternate ServerHello type");
+ error = 1;
+ goto done;
+ }
+ }
+
/* require the use of fixed finite-field DH groups */
if (requireDHNamedGroups) {
rv = SSL_OptionSet(s, SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
@@ -1212,7 +1363,21 @@ run_client(void)
if (override) {
SSL_BadCertHook(s, ownBadCertHandler, NULL);
}
- SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
+ if (actAsServer) {
+ rv = installServerCertificate(s, nickname);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error installing server cert");
+ return 1;
+ }
+ rv = SSL_ConfigServerSessionIDCache(1024, 0, 0, ".");
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error configuring session cache");
+ return 1;
+ }
+ initializedServerSessionCache = PR_TRUE;
+ } else {
+ SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
+ }
SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
if (hs1SniHostName) {
SSL_SetURL(s, hs1SniHostName);
@@ -1220,56 +1385,27 @@ run_client(void)
SSL_SetURL(s, host);
}
- /* Try to connect to the server */
- status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT);
- if (status != PR_SUCCESS) {
- if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
- if (verbose)
- SECU_PrintError(progName, "connect");
- milliPause(50 * multiplier);
- pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
- pollset[SSOCK_FD].out_flags = 0;
- pollset[SSOCK_FD].fd = s;
- while (1) {
- FPRINTF(stderr,
- "%s: about to call PR_Poll for connect completion!\n",
- progName);
- filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
- if (filesReady < 0) {
- SECU_PrintError(progName, "unable to connect (poll)");
- error = 1;
- goto done;
- }
- FPRINTF(stderr,
- "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
- progName, pollset[SSOCK_FD].out_flags);
- if (filesReady == 0) { /* shouldn't happen! */
- FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName);
- error = 1;
- goto done;
- }
- status = PR_GetConnectStatus(pollset);
- if (status == PR_SUCCESS) {
- break;
- }
- if (PR_GetError() != PR_IN_PROGRESS_ERROR) {
- SECU_PrintError(progName, "unable to connect (poll)");
- error = 1;
- goto done;
- }
- SECU_PrintError(progName, "poll");
- milliPause(50 * multiplier);
- }
- } else {
- SECU_PrintError(progName, "unable to connect");
+ if (actAsServer) {
+ rv = SSL_ResetHandshake(s, PR_TRUE /* server */);
+ if (rv != SECSuccess) {
+ return 1;
+ }
+ } else {
+ /* Try to connect to the server */
+ rv = connectToServer(s, pollset);
+ if (rv != SECSuccess) {
+ ;
error = 1;
goto done;
}
}
pollset[SSOCK_FD].fd = s;
- pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT |
- (clientSpeaksFirst ? 0 : PR_POLL_READ);
+ pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT;
+ if (!actAsServer)
+ pollset[SSOCK_FD].in_flags |= (clientSpeaksFirst ? 0 : PR_POLL_READ);
+ else
+ pollset[SSOCK_FD].in_flags |= PR_POLL_READ;
pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
if (!REQUEST_WAITING) {
pollset[STDIN_FD].in_flags = PR_POLL_READ;
@@ -1319,9 +1455,11 @@ run_client(void)
** Select on stdin and on the socket. Write data from stdin to
** socket, read data from socket and write to stdout.
*/
+ requestToExit = PR_FALSE;
FPRINTF(stderr, "%s: ready...\n", progName);
- while ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
- REQUEST_WAITING) {
+ while (!requestToExit &&
+ ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
+ REQUEST_WAITING)) {
char buf[4000]; /* buffer for stdin */
int nb; /* num bytes read from stdin. */
@@ -1507,12 +1645,10 @@ main(int argc, char **argv)
}
}
- SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions);
-
/* XXX: 'B' was used in the past but removed in 3.28,
* please leave some time before resuing it. */
optstate = PL_CreateOptState(argc, argv,
- "46A:CDFGHI:KL:M:OR:STUV:W:YZa:bc:d:fgh:m:n:op:qr:st:uvw:z");
+ "46A:CDFGHI:KL:M:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:z");
while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case '?':
@@ -1593,6 +1729,21 @@ main(int argc, char **argv)
};
break;
+ case 'P':
+ useDTLS = PR_TRUE;
+ if (!strcmp(optstate->value, "server")) {
+ actAsServer = 1;
+ } else {
+ if (strcmp(optstate->value, "client")) {
+ Usage(progName);
+ }
+ }
+ break;
+
+ case 'Q':
+ stopAfterHandshake = PR_TRUE;
+ break;
+
case 'R':
rootModule = PORT_Strdup(optstate->value);
break;
@@ -1610,14 +1761,16 @@ main(int argc, char **argv)
break;
case 'V':
- if (SECU_ParseSSLVersionRangeString(optstate->value,
- enabledVersions, &enabledVersions) !=
- SECSuccess) {
- fprintf(stderr, "Bad version specified.\n");
+ versionString = PORT_Strdup(optstate->value);
+ break;
+
+ case 'X':
+ if (!strcmp(optstate->value, "alt-server-hello")) {
+ enableAltServerHello = PR_TRUE;
+ } else {
Usage(progName);
}
break;
-
case 'Y':
PrintCipherUsage(progName);
exit(0);
@@ -1727,9 +1880,20 @@ main(int argc, char **argv)
break;
}
}
-
PL_DestroyOptState(optstate);
+ SSL_VersionRangeGetSupported(useDTLS ? ssl_variant_datagram : ssl_variant_stream, &enabledVersions);
+
+ if (versionString) {
+ if (SECU_ParseSSLVersionRangeString(versionString,
+ enabledVersions, &enabledVersions) !=
+ SECSuccess) {
+ fprintf(stderr, "Bad version specified.\n");
+ Usage(progName);
+ }
+ PORT_Free(versionString);
+ }
+
if (optstatus == PL_OPT_BAD) {
Usage(progName);
}
@@ -1758,7 +1922,7 @@ main(int argc, char **argv)
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
PK11_SetPasswordFunc(SECU_GetModulePassword);
-
+ memset(&addr, 0, sizeof(addr));
status = PR_StringToNetAddr(host, &addr);
if (status == PR_SUCCESS) {
addr.inet.port = PR_htons(portno);
@@ -1770,6 +1934,7 @@ main(int argc, char **argv)
addrInfo = PR_GetAddrInfoByName(host, PR_AF_UNSPEC,
PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME);
if (!addrInfo) {
+ fprintf(stderr, "HOSTNAME=%s\n", host);
SECU_PrintError(progName, "error looking up host");
error = 1;
goto done;
@@ -1884,7 +2049,7 @@ main(int argc, char **argv)
}
while (numConnections--) {
- error = run_client();
+ error = run();
if (error) {
goto done;
}
@@ -1915,6 +2080,12 @@ done:
}
if (NSS_IsInitialized()) {
SSL_ClearSessionCache();
+ if (initializedServerSessionCache) {
+ if (SSL_ShutdownServerSessionIDCache() != SECSuccess) {
+ error = 1;
+ }
+ }
+
if (NSS_Shutdown() != SECSuccess) {
error = 1;
}
diff --git a/security/nss/coreconf/config.gypi b/security/nss/coreconf/config.gypi
index 9ea528ae4..f4c3fbd0f 100644
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -96,7 +96,6 @@
'mozilla_client%': 0,
'moz_fold_libs%': 0,
'moz_folded_library_name%': '',
- 'ssl_enable_zlib%': 1,
'sanitizer_flags%': 0,
'test_build%': 0,
'no_zdefs%': 0,
@@ -109,6 +108,7 @@
'nss_public_dist_dir%': '<(nss_dist_dir)/public',
'nss_private_dist_dir%': '<(nss_dist_dir)/private',
'only_dev_random%': 1,
+ 'disable_fips%': 1,
},
'target_defaults': {
# Settings specific to targets should go here.
@@ -125,6 +125,12 @@
'<(nss_dist_dir)/private/<(module)',
],
'conditions': [
+ [ 'disable_fips==1', {
+ 'defines': [
+ 'NSS_FIPS_DISABLED',
+ 'NSS_NO_INIT_SUPPORT',
+ ],
+ }],
[ 'OS!="android" and OS!="mac" and OS!="win"', {
'libraries': [
'-lpthread',
@@ -167,7 +173,7 @@
},
},
}],
- [ 'target_arch=="arm64" or target_arch=="aarch64"', {
+ [ 'target_arch=="arm64" or target_arch=="aarch64" or target_arch=="sparc64" or target_arch=="ppc64" or target_arch=="ppc64le" or target_arch=="s390x" or target_arch=="mips64"', {
'defines': [
'NSS_USE_64',
],
@@ -294,7 +300,6 @@
'Common': {
'abstract': 1,
'defines': [
- 'NSS_NO_INIT_SUPPORT',
'USE_UTIL_DIRECTLY',
'NO_NSPR_10_SUPPORT',
'SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES',
diff --git a/security/nss/coreconf/config.mk b/security/nss/coreconf/config.mk
index 09b733d5c..55d95c30e 100644
--- a/security/nss/coreconf/config.mk
+++ b/security/nss/coreconf/config.mk
@@ -146,10 +146,6 @@ endif
# [16.0] Global environ ment defines
#######################################################################
-ifdef NSS_DISABLE_ECC
-DEFINES += -DNSS_DISABLE_ECC
-endif
-
ifdef NSS_ALLOW_UNSUPPORTED_CRITICAL
DEFINES += -DNSS_ALLOW_UNSUPPORTED_CRITICAL
endif
@@ -176,7 +172,7 @@ endif
# FIPS support requires startup tests to be executed at load time of shared modules.
# For performance reasons, these tests are disabled by default.
-# When compiling binaries that must support FIPS mode,
+# When compiling binaries that must support FIPS mode,
# you should define NSS_FORCE_FIPS
#
# NSS_NO_INIT_SUPPORT is always defined on platforms that don't support
@@ -203,8 +199,3 @@ DEFINES += -DNO_NSPR_10_SUPPORT
# Hide old, deprecated, TLS cipher suite names when building NSS
DEFINES += -DSSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES
-
-# Mozilla's mozilla/modules/zlib/src/zconf.h adds the MOZ_Z_ prefix to zlib
-# exported symbols, which causes problem when NSS is built as part of Mozilla.
-# So we add a NSS_SSL_ENABLE_ZLIB variable to allow Mozilla to turn this off.
-NSS_SSL_ENABLE_ZLIB = 1
diff --git a/security/nss/coreconf/coreconf.dep b/security/nss/coreconf/coreconf.dep
index 590d1bfae..5182f7555 100644
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -10,4 +10,3 @@
*/
#error "Do not include this header file."
-
diff --git a/security/nss/coreconf/werror.py b/security/nss/coreconf/werror.py
index 0d3843f64..c469c4002 100644
--- a/security/nss/coreconf/werror.py
+++ b/security/nss/coreconf/werror.py
@@ -24,7 +24,7 @@ def main():
# If we aren't clang, make sure we have gcc 4.8 at least
if not cc_is_clang:
try:
- v = subprocess.check_output([cc, '-dumpversion'], stderr=sink)
+ v = subprocess.check_output([cc, '-dumpversion'], stderr=sink).decode("utf-8")
v = v.strip(' \r\n').split('.')
v = list(map(int, v))
if v[0] < 4 or (v[0] == 4 and v[1] < 8):
diff --git a/security/nss/cpputil/.clang-format b/security/nss/cpputil/.clang-format
new file mode 100644
index 000000000..06e3c5115
--- /dev/null
+++ b/security/nss/cpputil/.clang-format
@@ -0,0 +1,4 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+...
diff --git a/security/nss/cpputil/Makefile b/security/nss/cpputil/Makefile
new file mode 100644
index 000000000..7adfc6117
--- /dev/null
+++ b/security/nss/cpputil/Makefile
@@ -0,0 +1,49 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+ifeq (WINNT,$(OS_ARCH))
+OS_CFLAGS += -EHsc
+else
+CXXFLAGS += -std=c++0x
+endif
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
diff --git a/security/nss/cpputil/README b/security/nss/cpputil/README
new file mode 100644
index 000000000..22297dd33
--- /dev/null
+++ b/security/nss/cpputil/README
@@ -0,0 +1,11 @@
+######################################
+## PLEASE READ BEFORE USING CPPUTIL ##
+######################################
+
+This is a static library supposed to be mainly used by NSS internally. We use
+it for testing, fuzzing, and a few new tools written in C++ that we're
+experimenting with.
+
+You might find it handy to use for your own projects but please be aware that
+we will make no promises your application won't break in the future. We will
+provide no support if you decide to link against it.
diff --git a/security/nss/cpputil/config.mk b/security/nss/cpputil/config.mk
new file mode 100644
index 000000000..b8c03de79
--- /dev/null
+++ b/security/nss/cpputil/config.mk
@@ -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/.
+
+#
+# Override TARGETS variable so that only static libraries
+# are specifed as dependencies within rules.mk.
+#
+
+TARGETS = $(LIBRARY)
+SHARED_LIBRARY =
+IMPORT_LIBRARY =
+PROGRAM =
+
diff --git a/security/nss/cpputil/cpputil.gyp b/security/nss/cpputil/cpputil.gyp
new file mode 100644
index 000000000..5042acf5c
--- /dev/null
+++ b/security/nss/cpputil/cpputil.gyp
@@ -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/.
+{
+ 'includes': [
+ '../coreconf/config.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'cpputil',
+ 'type': 'static_library',
+ 'sources': [
+ 'databuffer.cc',
+ 'dummy_io.cc',
+ 'dummy_io_fwd.cc',
+ 'tls_parser.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(DEPTH)/cpputil',
+ ],
+ },
+ },
+ ],
+}
+
diff --git a/security/nss/cpputil/cpputil.h b/security/nss/cpputil/cpputil.h
new file mode 100644
index 000000000..017ce9bfc
--- /dev/null
+++ b/security/nss/cpputil/cpputil.h
@@ -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/. */
+
+#ifndef cpputil_h__
+#define cpputil_h__
+
+static unsigned char* toUcharPtr(const uint8_t* v) {
+ return const_cast<unsigned char*>(static_cast<const unsigned char*>(v));
+}
+
+#endif // cpputil_h__
diff --git a/security/nss/cpputil/databuffer.cc b/security/nss/cpputil/databuffer.cc
new file mode 100644
index 000000000..d60ebccb3
--- /dev/null
+++ b/security/nss/cpputil/databuffer.cc
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "databuffer.h"
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+namespace nss_test {
+
+void DataBuffer::Assign(const uint8_t* data, size_t len) {
+ if (data) {
+ Allocate(len);
+ memcpy(static_cast<void*>(data_), static_cast<const void*>(data), len);
+ } else {
+ assert(len == 0);
+ data_ = nullptr;
+ len_ = 0;
+ }
+}
+
+// Write will do a new allocation and expand the size of the buffer if needed.
+// Returns the offset of the end of the write.
+size_t DataBuffer::Write(size_t index, const uint8_t* val, size_t count) {
+ assert(val);
+ if (index + count > len_) {
+ size_t newlen = index + count;
+ uint8_t* tmp = new uint8_t[newlen]; // Always > 0.
+ if (data_) {
+ memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_);
+ }
+ if (index > len_) {
+ memset(static_cast<void*>(tmp + len_), 0, index - len_);
+ }
+ delete[] data_;
+ data_ = tmp;
+ len_ = newlen;
+ }
+ if (data_) {
+ memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val),
+ count);
+ }
+ return index + count;
+}
+
+// Write an integer, also performing host-to-network order conversion.
+// Returns the offset of the end of the write.
+size_t DataBuffer::Write(size_t index, uint32_t val, size_t count) {
+ assert(count <= sizeof(uint32_t));
+ uint32_t nvalue = htonl(val);
+ auto* addr = reinterpret_cast<const uint8_t*>(&nvalue);
+ return Write(index, addr + sizeof(uint32_t) - count, count);
+}
+
+void DataBuffer::Splice(const uint8_t* ins, size_t ins_len, size_t index,
+ size_t remove) {
+ assert(ins);
+ uint8_t* old_value = data_;
+ size_t old_len = len_;
+
+ // The amount of stuff remaining from the tail of the old.
+ size_t tail_len = old_len - (std::min)(old_len, index + remove);
+ // The new length: the head of the old, the new, and the tail of the old.
+ len_ = index + ins_len + tail_len;
+ data_ = new uint8_t[len_ ? len_ : 1];
+
+ // The head of the old.
+ if (old_value) {
+ Write(0, old_value, (std::min)(old_len, index));
+ }
+ // Maybe a gap.
+ if (old_value && index > old_len) {
+ memset(old_value + index, 0, index - old_len);
+ }
+ // The new.
+ Write(index, ins, ins_len);
+ // The tail of the old.
+ if (tail_len > 0) {
+ Write(index + ins_len, old_value + index + remove, tail_len);
+ }
+
+ delete[] old_value;
+}
+
+// This can't use the same trick as Write(), since we might be reading from a
+// smaller data source.
+bool DataBuffer::Read(size_t index, size_t count, uint64_t* val) const {
+ assert(count <= sizeof(uint64_t));
+ assert(val);
+ if ((index > len()) || (count > (len() - index))) {
+ return false;
+ }
+ *val = 0;
+ for (size_t i = 0; i < count; ++i) {
+ *val = (*val << 8) | data()[index + i];
+ }
+ return true;
+}
+
+bool DataBuffer::Read(size_t index, size_t count, uint32_t* val) const {
+ assert(count <= sizeof(uint32_t));
+ uint64_t tmp;
+
+ if (!Read(index, count, &tmp)) {
+ return false;
+ }
+ *val = tmp & 0xffffffff;
+ return true;
+}
+
+size_t DataBuffer::logging_limit = 32;
+
+/* static */ void DataBuffer::SetLogLimit(size_t limit) {
+ DataBuffer::logging_limit = limit;
+}
+
+} // namespace nss_test
diff --git a/security/nss/cpputil/databuffer.h b/security/nss/cpputil/databuffer.h
new file mode 100644
index 000000000..58e07efe1
--- /dev/null
+++ b/security/nss/cpputil/databuffer.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef databuffer_h__
+#define databuffer_h__
+
+#include <algorithm>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+
+namespace nss_test {
+
+class DataBuffer {
+ public:
+ DataBuffer() : data_(nullptr), len_(0) {}
+ DataBuffer(const uint8_t* data, size_t len) : data_(nullptr), len_(0) {
+ Assign(data, len);
+ }
+ DataBuffer(const DataBuffer& other) : data_(nullptr), len_(0) {
+ Assign(other);
+ }
+ ~DataBuffer() { delete[] data_; }
+
+ DataBuffer& operator=(const DataBuffer& other) {
+ if (&other != this) {
+ Assign(other);
+ }
+ return *this;
+ }
+
+ void Allocate(size_t len) {
+ delete[] data_;
+ data_ = new uint8_t[len ? len : 1]; // Don't depend on new [0].
+ len_ = len;
+ }
+
+ void Truncate(size_t len) { len_ = (std::min)(len_, len); }
+
+ void Assign(const DataBuffer& other) { Assign(other.data(), other.len()); }
+
+ void Assign(const uint8_t* data, size_t len);
+
+ // Write will do a new allocation and expand the size of the buffer if needed.
+ // Returns the offset of the end of the write.
+ size_t Write(size_t index, const uint8_t* val, size_t count);
+ size_t Write(size_t index, const DataBuffer& buf) {
+ return Write(index, buf.data(), buf.len());
+ }
+
+ // Write an integer, also performing host-to-network order conversion.
+ // Returns the offset of the end of the write.
+ size_t Write(size_t index, uint32_t val, size_t count);
+
+ // Starting at |index|, remove |remove| bytes and replace them with the
+ // contents of |buf|.
+ void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) {
+ Splice(buf.data(), buf.len(), index, remove);
+ }
+
+ void Splice(const uint8_t* ins, size_t ins_len, size_t index,
+ size_t remove = 0);
+ void Append(const DataBuffer& buf) { Splice(buf, len_); }
+
+ bool Read(size_t index, size_t count, uint64_t* val) const;
+ bool Read(size_t index, size_t count, uint32_t* val) const;
+
+ const uint8_t* data() const { return data_; }
+ uint8_t* data() { return data_; }
+ size_t len() const { return len_; }
+ bool empty() const { return len_ == 0; }
+
+ static void SetLogLimit(size_t limit);
+ friend std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf);
+
+ private:
+ static size_t logging_limit;
+ uint8_t* data_;
+ size_t len_;
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf) {
+ stream << "[" << buf.len() << "] ";
+ for (size_t i = 0; i < buf.len(); ++i) {
+ if (i >= DataBuffer::logging_limit) {
+ stream << "...";
+ break;
+ }
+ stream << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<unsigned>(buf.data()[i]);
+ }
+ stream << std::dec;
+ return stream;
+}
+
+inline bool operator==(const DataBuffer& a, const DataBuffer& b) {
+ return (a.empty() && b.empty()) ||
+ (a.len() == b.len() && 0 == memcmp(a.data(), b.data(), a.len()));
+}
+
+inline bool operator!=(const DataBuffer& a, const DataBuffer& b) {
+ return !(a == b);
+}
+
+} // namespace nss_test
+
+#endif
diff --git a/security/nss/cpputil/dummy_io.cc b/security/nss/cpputil/dummy_io.cc
new file mode 100644
index 000000000..ef45db833
--- /dev/null
+++ b/security/nss/cpputil/dummy_io.cc
@@ -0,0 +1,225 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <assert.h>
+#include <iostream>
+
+#include "prerror.h"
+#include "prio.h"
+
+#include "dummy_io.h"
+
+#define UNIMPLEMENTED() \
+ std::cerr << "Unimplemented: " << __FUNCTION__ << std::endl; \
+ assert(false);
+
+extern const struct PRIOMethods DummyMethodsForward;
+
+ScopedPRFileDesc DummyIOLayerMethods::CreateFD(PRDescIdentity id,
+ DummyIOLayerMethods *methods) {
+ ScopedPRFileDesc fd(PR_CreateIOLayerStub(id, &DummyMethodsForward));
+ assert(fd);
+ if (!fd) {
+ return nullptr;
+ }
+ fd->secret = reinterpret_cast<PRFilePrivate *>(methods);
+ return fd;
+}
+
+PRStatus DummyIOLayerMethods::Close(PRFileDesc *f) {
+ f->secret = nullptr;
+ f->dtor(f);
+ return PR_SUCCESS;
+}
+
+int32_t DummyIOLayerMethods::Read(PRFileDesc *f, void *buf, int32_t length) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int32_t DummyIOLayerMethods::Write(PRFileDesc *f, const void *buf,
+ int32_t length) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int32_t DummyIOLayerMethods::Available(PRFileDesc *f) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int64_t DummyIOLayerMethods::Available64(PRFileDesc *f) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+PRStatus DummyIOLayerMethods::Sync(PRFileDesc *f) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+int32_t DummyIOLayerMethods::Seek(PRFileDesc *f, int32_t offset,
+ PRSeekWhence how) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int64_t DummyIOLayerMethods::Seek64(PRFileDesc *f, int64_t offset,
+ PRSeekWhence how) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+PRStatus DummyIOLayerMethods::FileInfo(PRFileDesc *f, PRFileInfo *info) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+PRStatus DummyIOLayerMethods::FileInfo64(PRFileDesc *f, PRFileInfo64 *info) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+int32_t DummyIOLayerMethods::Writev(PRFileDesc *f, const PRIOVec *iov,
+ int32_t iov_size, PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+PRStatus DummyIOLayerMethods::Connect(PRFileDesc *f, const PRNetAddr *addr,
+ PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+PRFileDesc *DummyIOLayerMethods::Accept(PRFileDesc *sd, PRNetAddr *addr,
+ PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return nullptr;
+}
+
+PRStatus DummyIOLayerMethods::Bind(PRFileDesc *f, const PRNetAddr *addr) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+PRStatus DummyIOLayerMethods::Listen(PRFileDesc *f, int32_t depth) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+PRStatus DummyIOLayerMethods::Shutdown(PRFileDesc *f, int32_t how) {
+ return PR_SUCCESS;
+}
+
+int32_t DummyIOLayerMethods::Recv(PRFileDesc *f, void *buf, int32_t buflen,
+ int32_t flags, PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+// Note: this is always nonblocking and assumes a zero timeout.
+int32_t DummyIOLayerMethods::Send(PRFileDesc *f, const void *buf,
+ int32_t amount, int32_t flags,
+ PRIntervalTime to) {
+ return Write(f, buf, amount);
+}
+
+int32_t DummyIOLayerMethods::Recvfrom(PRFileDesc *f, void *buf, int32_t amount,
+ int32_t flags, PRNetAddr *addr,
+ PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int32_t DummyIOLayerMethods::Sendto(PRFileDesc *f, const void *buf,
+ int32_t amount, int32_t flags,
+ const PRNetAddr *addr, PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int16_t DummyIOLayerMethods::Poll(PRFileDesc *f, int16_t in_flags,
+ int16_t *out_flags) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int32_t DummyIOLayerMethods::AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf,
+ int32_t amount, PRIntervalTime t) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+int32_t DummyIOLayerMethods::TransmitFile(PRFileDesc *sd, PRFileDesc *f,
+ const void *headers, int32_t hlen,
+ PRTransmitFileFlags flags,
+ PRIntervalTime t) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+// TODO: Modify to return unique names for each channel
+// somehow, as opposed to always the same static address. The current
+// implementation messes up the session cache, which is why it's off
+// elsewhere
+PRStatus DummyIOLayerMethods::Getpeername(PRFileDesc *f, PRNetAddr *addr) {
+ addr->inet.family = PR_AF_INET;
+ addr->inet.port = 0;
+ addr->inet.ip = 0;
+
+ return PR_SUCCESS;
+}
+
+PRStatus DummyIOLayerMethods::Getsockname(PRFileDesc *f, PRNetAddr *addr) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+PRStatus DummyIOLayerMethods::Getsockoption(PRFileDesc *f,
+ PRSocketOptionData *opt) {
+ switch (opt->option) {
+ case PR_SockOpt_Nonblocking:
+ opt->value.non_blocking = PR_TRUE;
+ return PR_SUCCESS;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+
+ return PR_FAILURE;
+}
+
+PRStatus DummyIOLayerMethods::Setsockoption(PRFileDesc *f,
+ const PRSocketOptionData *opt) {
+ switch (opt->option) {
+ case PR_SockOpt_Nonblocking:
+ return PR_SUCCESS;
+ case PR_SockOpt_NoDelay:
+ return PR_SUCCESS;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+
+ return PR_FAILURE;
+}
+
+int32_t DummyIOLayerMethods::Sendfile(PRFileDesc *out, PRSendFileData *in,
+ PRTransmitFileFlags flags,
+ PRIntervalTime to) {
+ UNIMPLEMENTED();
+ return -1;
+}
+
+PRStatus DummyIOLayerMethods::ConnectContinue(PRFileDesc *f, int16_t flags) {
+ UNIMPLEMENTED();
+ return PR_FAILURE;
+}
+
+int32_t DummyIOLayerMethods::Reserved(PRFileDesc *f) {
+ UNIMPLEMENTED();
+ return -1;
+}
diff --git a/security/nss/cpputil/dummy_io.h b/security/nss/cpputil/dummy_io.h
new file mode 100644
index 000000000..797ac6113
--- /dev/null
+++ b/security/nss/cpputil/dummy_io.h
@@ -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/. */
+
+#ifndef dummy_io_h__
+#define dummy_io_h__
+
+#include "prerror.h"
+#include "prio.h"
+
+#include "scoped_ptrs.h"
+
+class DummyIOLayerMethods {
+ public:
+ static ScopedPRFileDesc CreateFD(PRDescIdentity id,
+ DummyIOLayerMethods *methods);
+
+ virtual PRStatus Close(PRFileDesc *f);
+ virtual int32_t Read(PRFileDesc *f, void *buf, int32_t length);
+ virtual int32_t Write(PRFileDesc *f, const void *buf, int32_t length);
+ virtual int32_t Available(PRFileDesc *f);
+ virtual int64_t Available64(PRFileDesc *f);
+ virtual PRStatus Sync(PRFileDesc *f);
+ virtual int32_t Seek(PRFileDesc *f, int32_t offset, PRSeekWhence how);
+ virtual int64_t Seek64(PRFileDesc *f, int64_t offset, PRSeekWhence how);
+ virtual PRStatus FileInfo(PRFileDesc *f, PRFileInfo *info);
+ virtual PRStatus FileInfo64(PRFileDesc *f, PRFileInfo64 *info);
+ virtual int32_t Writev(PRFileDesc *f, const PRIOVec *iov, int32_t iov_size,
+ PRIntervalTime to);
+ virtual PRStatus Connect(PRFileDesc *f, const PRNetAddr *addr,
+ PRIntervalTime to);
+ virtual PRFileDesc *Accept(PRFileDesc *sd, PRNetAddr *addr,
+ PRIntervalTime to);
+ virtual PRStatus Bind(PRFileDesc *f, const PRNetAddr *addr);
+ virtual PRStatus Listen(PRFileDesc *f, int32_t depth);
+ virtual PRStatus Shutdown(PRFileDesc *f, int32_t how);
+ virtual int32_t Recv(PRFileDesc *f, void *buf, int32_t buflen, int32_t flags,
+ PRIntervalTime to);
+ virtual int32_t Send(PRFileDesc *f, const void *buf, int32_t amount,
+ int32_t flags, PRIntervalTime to);
+ virtual int32_t Recvfrom(PRFileDesc *f, void *buf, int32_t amount,
+ int32_t flags, PRNetAddr *addr, PRIntervalTime to);
+ virtual int32_t Sendto(PRFileDesc *f, const void *buf, int32_t amount,
+ int32_t flags, const PRNetAddr *addr,
+ PRIntervalTime to);
+ virtual int16_t Poll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags);
+ virtual int32_t AcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, int32_t amount, PRIntervalTime t);
+ virtual int32_t TransmitFile(PRFileDesc *sd, PRFileDesc *f,
+ const void *headers, int32_t hlen,
+ PRTransmitFileFlags flags, PRIntervalTime t);
+ virtual PRStatus Getpeername(PRFileDesc *f, PRNetAddr *addr);
+ virtual PRStatus Getsockname(PRFileDesc *f, PRNetAddr *addr);
+ virtual PRStatus Getsockoption(PRFileDesc *f, PRSocketOptionData *opt);
+ virtual PRStatus Setsockoption(PRFileDesc *f, const PRSocketOptionData *opt);
+ virtual int32_t Sendfile(PRFileDesc *out, PRSendFileData *in,
+ PRTransmitFileFlags flags, PRIntervalTime to);
+ virtual PRStatus ConnectContinue(PRFileDesc *f, int16_t flags);
+ virtual int32_t Reserved(PRFileDesc *f);
+};
+
+#endif // dummy_io_h__
diff --git a/security/nss/cpputil/dummy_io_fwd.cc b/security/nss/cpputil/dummy_io_fwd.cc
new file mode 100644
index 000000000..5e53d9e1b
--- /dev/null
+++ b/security/nss/cpputil/dummy_io_fwd.cc
@@ -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/. */
+
+#include "prio.h"
+
+#include "dummy_io.h"
+
+static DummyIOLayerMethods *ToMethods(PRFileDesc *f) {
+ return reinterpret_cast<DummyIOLayerMethods *>(f->secret);
+}
+
+static PRStatus DummyClose(PRFileDesc *f) { return ToMethods(f)->Close(f); }
+
+static int32_t DummyRead(PRFileDesc *f, void *buf, int32_t length) {
+ return ToMethods(f)->Read(f, buf, length);
+}
+
+static int32_t DummyWrite(PRFileDesc *f, const void *buf, int32_t length) {
+ return ToMethods(f)->Write(f, buf, length);
+}
+
+static int32_t DummyAvailable(PRFileDesc *f) {
+ return ToMethods(f)->Available(f);
+}
+
+static int64_t DummyAvailable64(PRFileDesc *f) {
+ return ToMethods(f)->Available64(f);
+}
+
+static PRStatus DummySync(PRFileDesc *f) { return ToMethods(f)->Sync(f); }
+
+static int32_t DummySeek(PRFileDesc *f, int32_t offset, PRSeekWhence how) {
+ return ToMethods(f)->Seek(f, offset, how);
+}
+
+static int64_t DummySeek64(PRFileDesc *f, int64_t offset, PRSeekWhence how) {
+ return ToMethods(f)->Seek64(f, offset, how);
+}
+
+static PRStatus DummyFileInfo(PRFileDesc *f, PRFileInfo *info) {
+ return ToMethods(f)->FileInfo(f, info);
+}
+
+static PRStatus DummyFileInfo64(PRFileDesc *f, PRFileInfo64 *info) {
+ return ToMethods(f)->FileInfo64(f, info);
+}
+
+static int32_t DummyWritev(PRFileDesc *f, const PRIOVec *iov, int32_t iov_size,
+ PRIntervalTime to) {
+ return ToMethods(f)->Writev(f, iov, iov_size, to);
+}
+
+static PRStatus DummyConnect(PRFileDesc *f, const PRNetAddr *addr,
+ PRIntervalTime to) {
+ return ToMethods(f)->Connect(f, addr, to);
+}
+
+static PRFileDesc *DummyAccept(PRFileDesc *f, PRNetAddr *addr,
+ PRIntervalTime to) {
+ return ToMethods(f)->Accept(f, addr, to);
+}
+
+static PRStatus DummyBind(PRFileDesc *f, const PRNetAddr *addr) {
+ return ToMethods(f)->Bind(f, addr);
+}
+
+static PRStatus DummyListen(PRFileDesc *f, int32_t depth) {
+ return ToMethods(f)->Listen(f, depth);
+}
+
+static PRStatus DummyShutdown(PRFileDesc *f, int32_t how) {
+ return ToMethods(f)->Shutdown(f, how);
+}
+
+static int32_t DummyRecv(PRFileDesc *f, void *buf, int32_t buflen,
+ int32_t flags, PRIntervalTime to) {
+ return ToMethods(f)->Recv(f, buf, buflen, flags, to);
+}
+
+static int32_t DummySend(PRFileDesc *f, const void *buf, int32_t amount,
+ int32_t flags, PRIntervalTime to) {
+ return ToMethods(f)->Send(f, buf, amount, flags, to);
+}
+
+static int32_t DummyRecvfrom(PRFileDesc *f, void *buf, int32_t amount,
+ int32_t flags, PRNetAddr *addr,
+ PRIntervalTime to) {
+ return ToMethods(f)->Recvfrom(f, buf, amount, flags, addr, to);
+}
+
+static int32_t DummySendto(PRFileDesc *f, const void *buf, int32_t amount,
+ int32_t flags, const PRNetAddr *addr,
+ PRIntervalTime to) {
+ return ToMethods(f)->Sendto(f, buf, amount, flags, addr, to);
+}
+
+static int16_t DummyPoll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags) {
+ return ToMethods(f)->Poll(f, in_flags, out_flags);
+}
+
+static int32_t DummyAcceptRead(PRFileDesc *f, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, int32_t amount,
+ PRIntervalTime t) {
+ return ToMethods(f)->AcceptRead(f, nd, raddr, buf, amount, t);
+}
+
+static int32_t DummyTransmitFile(PRFileDesc *sd, PRFileDesc *f,
+ const void *headers, int32_t hlen,
+ PRTransmitFileFlags flags, PRIntervalTime t) {
+ return ToMethods(f)->TransmitFile(sd, f, headers, hlen, flags, t);
+}
+
+static PRStatus DummyGetpeername(PRFileDesc *f, PRNetAddr *addr) {
+ return ToMethods(f)->Getpeername(f, addr);
+}
+
+static PRStatus DummyGetsockname(PRFileDesc *f, PRNetAddr *addr) {
+ return ToMethods(f)->Getsockname(f, addr);
+}
+
+static PRStatus DummyGetsockoption(PRFileDesc *f, PRSocketOptionData *opt) {
+ return ToMethods(f)->Getsockoption(f, opt);
+}
+
+static PRStatus DummySetsockoption(PRFileDesc *f,
+ const PRSocketOptionData *opt) {
+ return ToMethods(f)->Setsockoption(f, opt);
+}
+
+static int32_t DummySendfile(PRFileDesc *f, PRSendFileData *in,
+ PRTransmitFileFlags flags, PRIntervalTime to) {
+ return ToMethods(f)->Sendfile(f, in, flags, to);
+}
+
+static PRStatus DummyConnectContinue(PRFileDesc *f, int16_t flags) {
+ return ToMethods(f)->ConnectContinue(f, flags);
+}
+
+static int32_t DummyReserved(PRFileDesc *f) {
+ return ToMethods(f)->Reserved(f);
+}
+
+extern const struct PRIOMethods DummyMethodsForward = {
+ PR_DESC_LAYERED, DummyClose,
+ DummyRead, DummyWrite,
+ DummyAvailable, DummyAvailable64,
+ DummySync, DummySeek,
+ DummySeek64, DummyFileInfo,
+ DummyFileInfo64, DummyWritev,
+ DummyConnect, DummyAccept,
+ DummyBind, DummyListen,
+ DummyShutdown, DummyRecv,
+ DummySend, DummyRecvfrom,
+ DummySendto, DummyPoll,
+ DummyAcceptRead, DummyTransmitFile,
+ DummyGetsockname, DummyGetpeername,
+ DummyReserved, DummyReserved,
+ DummyGetsockoption, DummySetsockoption,
+ DummySendfile, DummyConnectContinue,
+ DummyReserved, DummyReserved,
+ DummyReserved, DummyReserved};
diff --git a/security/nss/cpputil/manifest.mn b/security/nss/cpputil/manifest.mn
new file mode 100644
index 000000000..b3ccad8b5
--- /dev/null
+++ b/security/nss/cpputil/manifest.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/.
+CORE_DEPTH = ..
+DEPTH = ..
+
+MODULE = nss
+LIBRARY_NAME = cpputil
+
+ifeq ($(NSS_BUILD_UTIL_ONLY),1)
+CPPSRCS = \
+ $(NULL)
+else
+CPPSRCS = \
+ databuffer.cc \
+ dummy_io.cc \
+ dummy_io_fwd.cc \
+ tls_parser.cc \
+ $(NULL)
+endif
+
+EXPORTS = \
+ $(NULL)
diff --git a/security/nss/cpputil/scoped_ptrs.h b/security/nss/cpputil/scoped_ptrs.h
new file mode 100644
index 000000000..b92b8132b
--- /dev/null
+++ b/security/nss/cpputil/scoped_ptrs.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef scoped_ptrs_h__
+#define scoped_ptrs_h__
+
+#include <memory>
+#include "cert.h"
+#include "keyhi.h"
+#include "pk11pub.h"
+#include "pkcs11uri.h"
+
+struct ScopedDelete {
+ void operator()(CERTCertificate* cert) { CERT_DestroyCertificate(cert); }
+ void operator()(CERTCertificateList* list) {
+ CERT_DestroyCertificateList(list);
+ }
+ void operator()(CERTName* name) { CERT_DestroyName(name); }
+ void operator()(CERTCertList* list) { CERT_DestroyCertList(list); }
+ void operator()(CERTSubjectPublicKeyInfo* spki) {
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ }
+ void operator()(PK11SlotInfo* slot) { PK11_FreeSlot(slot); }
+ void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); }
+ void operator()(PRFileDesc* fd) { PR_Close(fd); }
+ void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
+ void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
+ void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
+ void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
+ void operator()(SECKEYPrivateKeyList* list) {
+ SECKEY_DestroyPrivateKeyList(list);
+ }
+ void operator()(PK11URI* uri) { PK11URI_DestroyURI(uri); }
+ void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); }
+ void operator()(PK11Context* context) { PK11_DestroyContext(context, true); }
+ void operator()(PK11GenericObject* obj) { PK11_DestroyGenericObject(obj); }
+};
+
+template <class T>
+struct ScopedMaybeDelete {
+ void operator()(T* ptr) {
+ if (ptr) {
+ ScopedDelete del;
+ del(ptr);
+ }
+ }
+};
+
+#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x
+
+SCOPED(CERTCertificate);
+SCOPED(CERTCertificateList);
+SCOPED(CERTCertList);
+SCOPED(CERTName);
+SCOPED(CERTSubjectPublicKeyInfo);
+SCOPED(PK11SlotInfo);
+SCOPED(PK11SymKey);
+SCOPED(PRFileDesc);
+SCOPED(SECAlgorithmID);
+SCOPED(SECItem);
+SCOPED(SECKEYPublicKey);
+SCOPED(SECKEYPrivateKey);
+SCOPED(SECKEYPrivateKeyList);
+SCOPED(PK11URI);
+SCOPED(PLArenaPool);
+SCOPED(PK11Context);
+SCOPED(PK11GenericObject);
+
+#undef SCOPED
+
+#endif // scoped_ptrs_h__
diff --git a/security/nss/cpputil/scoped_ptrs_util.h b/security/nss/cpputil/scoped_ptrs_util.h
new file mode 100644
index 000000000..2dbf34e1d
--- /dev/null
+++ b/security/nss/cpputil/scoped_ptrs_util.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef scoped_ptrs_util_h__
+#define scoped_ptrs_util_h__
+
+#include <memory>
+#include "pkcs11uri.h"
+#include "secoid.h"
+
+struct ScopedDelete {
+ void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
+ void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
+ void operator()(PK11URI* uri) { PK11URI_DestroyURI(uri); }
+ void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); }
+};
+
+template <class T>
+struct ScopedMaybeDelete {
+ void operator()(T* ptr) {
+ if (ptr) {
+ ScopedDelete del;
+ del(ptr);
+ }
+ }
+};
+
+#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x
+
+SCOPED(SECAlgorithmID);
+SCOPED(SECItem);
+SCOPED(PK11URI);
+
+#undef SCOPED
+
+#endif // scoped_ptrs_util_h__
diff --git a/security/nss/cpputil/tls_parser.cc b/security/nss/cpputil/tls_parser.cc
new file mode 100644
index 000000000..e4c06aa91
--- /dev/null
+++ b/security/nss/cpputil/tls_parser.cc
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "tls_parser.h"
+
+namespace nss_test {
+
+bool TlsParser::Read(uint8_t* val) {
+ if (remaining() < 1) {
+ return false;
+ }
+ *val = *ptr();
+ consume(1);
+ return true;
+}
+
+bool TlsParser::Read(uint32_t* val, size_t size) {
+ if (size > sizeof(uint32_t)) {
+ return false;
+ }
+
+ uint32_t v = 0;
+ for (size_t i = 0; i < size; ++i) {
+ uint8_t tmp;
+ if (!Read(&tmp)) {
+ return false;
+ }
+
+ v = (v << 8) | tmp;
+ }
+
+ *val = v;
+ return true;
+}
+
+bool TlsParser::Read(DataBuffer* val, size_t len) {
+ if (remaining() < len) {
+ return false;
+ }
+
+ val->Assign(ptr(), len);
+ consume(len);
+ return true;
+}
+
+bool TlsParser::ReadVariable(DataBuffer* val, size_t len_size) {
+ uint32_t len;
+ if (!Read(&len, len_size)) {
+ return false;
+ }
+ return Read(val, len);
+}
+
+bool TlsParser::Skip(size_t len) {
+ if (len > remaining()) {
+ return false;
+ }
+ consume(len);
+ return true;
+}
+
+bool TlsParser::SkipVariable(size_t len_size) {
+ uint32_t len;
+ if (!Read(&len, len_size)) {
+ return false;
+ }
+ return Skip(len);
+}
+
+} // namespace nss_test
diff --git a/security/nss/cpputil/tls_parser.h b/security/nss/cpputil/tls_parser.h
new file mode 100644
index 000000000..a5f5771d5
--- /dev/null
+++ b/security/nss/cpputil/tls_parser.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef tls_parser_h_
+#define tls_parser_h_
+
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include "databuffer.h"
+#include "sslt.h"
+
+namespace nss_test {
+
+const uint8_t kTlsChangeCipherSpecType = 20;
+const uint8_t kTlsAlertType = 21;
+const uint8_t kTlsHandshakeType = 22;
+const uint8_t kTlsApplicationDataType = 23;
+const uint8_t kTlsAltHandshakeType = 24;
+const uint8_t kTlsAckType = 25;
+
+const uint8_t kTlsHandshakeClientHello = 1;
+const uint8_t kTlsHandshakeServerHello = 2;
+const uint8_t kTlsHandshakeNewSessionTicket = 4;
+const uint8_t kTlsHandshakeHelloRetryRequest = 6;
+const uint8_t kTlsHandshakeEncryptedExtensions = 8;
+const uint8_t kTlsHandshakeCertificate = 11;
+const uint8_t kTlsHandshakeServerKeyExchange = 12;
+const uint8_t kTlsHandshakeCertificateRequest = 13;
+const uint8_t kTlsHandshakeCertificateVerify = 15;
+const uint8_t kTlsHandshakeClientKeyExchange = 16;
+const uint8_t kTlsHandshakeFinished = 20;
+
+const uint8_t kTlsAlertWarning = 1;
+const uint8_t kTlsAlertFatal = 2;
+
+const uint8_t kTlsAlertCloseNotify = 0;
+const uint8_t kTlsAlertUnexpectedMessage = 10;
+const uint8_t kTlsAlertBadRecordMac = 20;
+const uint8_t kTlsAlertRecordOverflow = 22;
+const uint8_t kTlsAlertHandshakeFailure = 40;
+const uint8_t kTlsAlertIllegalParameter = 47;
+const uint8_t kTlsAlertDecodeError = 50;
+const uint8_t kTlsAlertDecryptError = 51;
+const uint8_t kTlsAlertProtocolVersion = 70;
+const uint8_t kTlsAlertInternalError = 80;
+const uint8_t kTlsAlertInappropriateFallback = 86;
+const uint8_t kTlsAlertMissingExtension = 109;
+const uint8_t kTlsAlertUnsupportedExtension = 110;
+const uint8_t kTlsAlertUnrecognizedName = 112;
+const uint8_t kTlsAlertNoApplicationProtocol = 120;
+
+const uint8_t kTlsFakeChangeCipherSpec[] = {
+ kTlsChangeCipherSpecType, // Type
+ 0xfe,
+ 0xff, // Version
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x10, // Fictitious sequence #
+ 0x00,
+ 0x01, // Length
+ 0x01 // Value
+};
+
+static const uint8_t kTls13PskKe = 0;
+static const uint8_t kTls13PskDhKe = 1;
+static const uint8_t kTls13PskAuth = 0;
+static const uint8_t kTls13PskSignAuth = 1;
+
+inline std::ostream& operator<<(std::ostream& os, SSLProtocolVariant v) {
+ return os << ((v == ssl_variant_stream) ? "TLS" : "DTLS");
+}
+
+inline bool IsDtls(uint16_t version) { return (version & 0x8000) == 0x8000; }
+
+inline uint16_t NormalizeTlsVersion(uint16_t version) {
+ if (version == 0xfeff) {
+ return 0x0302; // special: DTLS 1.0 == TLS 1.1
+ }
+ if (IsDtls(version)) {
+ return (version ^ 0xffff) + 0x0201;
+ }
+ return version;
+}
+
+inline uint16_t TlsVersionToDtlsVersion(uint16_t version) {
+ if (version == 0x0302) {
+ return 0xfeff;
+ }
+ if (version == 0x0304) {
+ return version;
+ }
+ return 0xffff - version + 0x0201;
+}
+
+inline size_t WriteVariable(DataBuffer* target, size_t index,
+ const DataBuffer& buf, size_t len_size) {
+ index = target->Write(index, static_cast<uint32_t>(buf.len()), len_size);
+ return target->Write(index, buf.data(), buf.len());
+}
+
+class TlsParser {
+ public:
+ TlsParser(const uint8_t* data, size_t len) : buffer_(data, len), offset_(0) {}
+ explicit TlsParser(const DataBuffer& buf) : buffer_(buf), offset_(0) {}
+
+ bool Read(uint8_t* val);
+ // Read an integral type of specified width.
+ bool Read(uint32_t* val, size_t size);
+ // Reads len bytes into dest buffer, overwriting it.
+ bool Read(DataBuffer* dest, size_t len);
+ // Reads bytes into dest buffer, overwriting it. The number of bytes is
+ // determined by reading from len_size bytes from the stream first.
+ bool ReadVariable(DataBuffer* dest, size_t len_size);
+
+ bool Skip(size_t len);
+ bool SkipVariable(size_t len_size);
+
+ size_t consumed() const { return offset_; }
+ size_t remaining() const { return buffer_.len() - offset_; }
+
+ private:
+ void consume(size_t len) { offset_ += len; }
+ const uint8_t* ptr() const { return buffer_.data() + offset_; }
+
+ DataBuffer buffer_;
+ size_t offset_;
+};
+
+} // namespace nss_test
+
+#endif
diff --git a/security/nss/doc/certutil.xml b/security/nss/doc/certutil.xml
index 461b21389..d5062bd5e 100644
--- a/security/nss/doc/certutil.xml
+++ b/security/nss/doc/certutil.xml
@@ -456,6 +456,16 @@ of the attribute codes:
</varlistentry>
<varlistentry>
+ <term>--pss</term>
+ <listitem><para>Restrict the generated certificate (with the <option>-S</option> option) or certificate request (with the <option>-R</option> option) to be used with the RSA-PSS signature scheme. This only works when the private key of the certificate or certificate request is RSA.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--pss-sign</term>
+ <listitem><para>Sign the generated certificate with the RSA-PSS signature scheme (with the <option>-C</option> or <option>-S</option> option). This only works when the private key of the signer's certificate is RSA. If the signer's certificate is restricted to RSA-PSS, it is not necessary to specify this option.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>-z noise-file</term>
<listitem><para>Read a seed value from the specified file to generate a new private and public key pair. This argument makes it possible to use hardware-generated seed values or manually create a value from the keyboard. The minimum file size is 20 bytes.</para></listitem>
</varlistentry>
diff --git a/security/nss/doc/html/certutil.html b/security/nss/doc/html/certutil.html
index eb2e94322..902d1309a 100644
--- a/security/nss/doc/html/certutil.html
+++ b/security/nss/doc/html/certutil.html
@@ -1,4 +1,4 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>CERTUTIL</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="CERTUTIL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">CERTUTIL</th></tr></table><hr></div><div class="refentry"><a name="certutil"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>certutil — Manage keys and certificate in both NSS databases and other NSS tokens</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><code class="command">certutil</code> [<em class="replaceable"><code>options</code></em>] [[<em class="replaceable"><code>arguments</code></em>]]</p></div></div><div class="refsection"><a name="idm139774553663312"></a><h2>STATUS</h2><p>This documentation is still work in progress. Please contribute to the initial review in <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836477" target="_top">Mozilla NSS bug 836477</a>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>CERTUTIL</title><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot"><link rel="home" href="index.html" title="CERTUTIL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">CERTUTIL</th></tr></table><hr></div><div class="refentry"><a name="certutil"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>certutil — Manage keys and certificate in both NSS databases and other NSS tokens</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><code class="command">certutil</code> [<em class="replaceable"><code>options</code></em>] [[<em class="replaceable"><code>arguments</code></em>]]</p></div></div><div class="refsection"><a name="idm140440587239488"></a><h2>STATUS</h2><p>This documentation is still work in progress. Please contribute to the initial review in <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836477" target="_top">Mozilla NSS bug 836477</a>
</p></div><div class="refsection"><a name="description"></a><h2>Description</h2><p>The Certificate Database Tool, <span class="command"><strong>certutil</strong></span>, is a command-line utility that can create and modify certificate and key databases. It can specifically list, generate, modify, or delete certificates, create or change the password, generate new public and private key pairs, display the contents of the key database, or delete key pairs within the key database.</p><p>Certificate issuance, part of the key and certificate management process, requires that keys and certificates be created in the key database. This document discusses certificate and key database management. For information on the security module database management, see the <span class="command"><strong>modutil</strong></span> manpage.</p></div><div class="refsection"><a name="options"></a><h2>Command Options and Arguments</h2><p>Running <span class="command"><strong>certutil</strong></span> always requires one and only one command option to specify the type of certificate operation. Each command option may take zero or more arguments. The command option <code class="option">-H</code> will list all the command options and their relevant arguments.</p><p><span class="command"><strong>Command Options</strong></span></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-A </span></dt><dd><p>Add an existing certificate to a certificate database. The certificate database should already exist; if one is not present, this command option will initialize one by default.</p></dd><dt><span class="term">-B</span></dt><dd><p>Run a series of commands from the specified batch file. This requires the <code class="option">-i</code> argument.</p></dd><dt><span class="term">-C </span></dt><dd><p>Create a new binary certificate file from a binary certificate request file. Use the <code class="option">-i</code> argument to specify the certificate request file. If this argument is not used, <span class="command"><strong>certutil</strong></span> prompts for a filename. </p></dd><dt><span class="term">-D </span></dt><dd><p>Delete a certificate from the certificate database.</p></dd><dt><span class="term">--rename </span></dt><dd><p>Change the database nickname of a certificate.</p></dd><dt><span class="term">-E </span></dt><dd><p>Add an email certificate to the certificate database.</p></dd><dt><span class="term">-F</span></dt><dd><p>Delete a private key from a key database. Specify the key to delete with the -n argument. Specify the database from which to delete the key with the
<code class="option">-d</code> argument. Use the <code class="option">-k</code> argument to specify explicitly whether to delete a DSA, RSA, or ECC key. If you don't use the <code class="option">-k</code> argument, the option looks for an RSA key matching the specified nickname.
</p><p>
@@ -20,25 +20,26 @@ Add one or multiple extensions that certutil cannot encode yet, by loading their
duplicate nicknames. Giving a key type generates a new key pair;
giving the ID of an existing key reuses that key pair (which is
required to renew certificates).
- </p></dd><dt><span class="term">-l </span></dt><dd><p>Display detailed information when validating a certificate with the -V option.</p></dd><dt><span class="term">-m serial-number</span></dt><dd><p>Assign a unique serial number to a certificate being created. This operation should be performed by a CA. If no serial number is provided a default serial number is made from the current time. Serial numbers are limited to integers </p></dd><dt><span class="term">-n nickname</span></dt><dd><p>Specify the nickname of a certificate or key to list, create, add to a database, modify, or validate. Bracket the nickname string with quotation marks if it contains spaces.</p></dd><dt><span class="term">-o output-file</span></dt><dd><p>Specify the output file name for new certificates or binary certificate requests. Bracket the output-file string with quotation marks if it contains spaces. If this argument is not used the output destination defaults to standard output.</p></dd><dt><span class="term">-P dbPrefix</span></dt><dd><p>Specify the prefix used on the certificate and key database file. This argument is provided to support legacy servers. Most applications do not use a database prefix.</p></dd><dt><span class="term">-p phone</span></dt><dd><p>Specify a contact telephone number to include in new certificates or certificate requests. Bracket this string with quotation marks if it contains spaces.</p></dd><dt><span class="term">-q pqgfile or curve-name</span></dt><dd><p>Read an alternate PQG value from the specified file when generating DSA key pairs. If this argument is not used, <span class="command"><strong>certutil</strong></span> generates its own PQG value. PQG files are created with a separate DSA utility.</p><p>Elliptic curve name is one of the ones from nistp256, nistp384, nistp521, curve25519.</p><p>If a token is available that supports more curves, the foolowing curves are supported as well:
- sect163k1, nistk163, sect163r1, sect163r2,
- nistb163, sect193r1, sect193r2, sect233k1, nistk233,
- sect233r1, nistb233, sect239k1, sect283k1, nistk283,
- sect283r1, nistb283, sect409k1, nistk409, sect409r1,
- nistb409, sect571k1, nistk571, sect571r1, nistb571,
- secp160k1, secp160r1, secp160r2, secp192k1, secp192r1,
- nistp192, secp224k1, secp224r1, nistp224, secp256k1,
- secp256r1, secp384r1, secp521r1,
- prime192v1, prime192v2, prime192v3,
- prime239v1, prime239v2, prime239v3, c2pnb163v1,
- c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1,
- c2tnb191v2, c2tnb191v3,
- c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3,
- c2pnb272w1, c2pnb304w1,
- c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1,
- secp112r2, secp128r1, secp128r2, sect113r1, sect113r2,
- sect131r1, sect131r2</p>
- </dd><dt><span class="term">-r </span></dt><dd><p>Display a certificate's binary DER encoding when listing information about that certificate with the -L option.</p></dd><dt><span class="term">-s subject</span></dt><dd><p>Identify a particular certificate owner for new certificates or certificate requests. Bracket this string with quotation marks if it contains spaces. The subject identification format follows RFC #1485.</p></dd><dt><span class="term">-t trustargs</span></dt><dd><p>Specify the trust attributes to modify in an existing certificate or to apply to a certificate when creating it or adding it to a database. There are three available trust categories for each certificate, expressed in the order <span class="emphasis"><em>SSL, email, object signing</em></span> for each trust setting. In each category position, use none, any, or all
+ </p></dd><dt><span class="term">-l </span></dt><dd><p>Display detailed information when validating a certificate with the -V option.</p></dd><dt><span class="term">-m serial-number</span></dt><dd><p>Assign a unique serial number to a certificate being created. This operation should be performed by a CA. If no serial number is provided a default serial number is made from the current time. Serial numbers are limited to integers </p></dd><dt><span class="term">-n nickname</span></dt><dd><p>Specify the nickname of a certificate or key to list, create, add to a database, modify, or validate. Bracket the nickname string with quotation marks if it contains spaces.</p></dd><dt><span class="term">-o output-file</span></dt><dd><p>Specify the output file name for new certificates or binary certificate requests. Bracket the output-file string with quotation marks if it contains spaces. If this argument is not used the output destination defaults to standard output.</p></dd><dt><span class="term">-P dbPrefix</span></dt><dd><p>Specify the prefix used on the certificate and key database file. This argument is provided to support legacy servers. Most applications do not use a database prefix.</p></dd><dt><span class="term">-p phone</span></dt><dd><p>Specify a contact telephone number to include in new certificates or certificate requests. Bracket this string with quotation marks if it contains spaces.</p></dd><dt><span class="term">-q pqgfile or curve-name</span></dt><dd><p>Read an alternate PQG value from the specified file when generating DSA key pairs. If this argument is not used, <span class="command"><strong>certutil</strong></span> generates its own PQG value. PQG files are created with a separate DSA utility.</p><p>Elliptic curve name is one of the ones from nistp256, nistp384, nistp521, curve25519.</p><p>
+ If a token is available that supports more curves, the foolowing curves are supported as well:
+ sect163k1, nistk163, sect163r1, sect163r2,
+ nistb163, sect193r1, sect193r2, sect233k1, nistk233,
+ sect233r1, nistb233, sect239k1, sect283k1, nistk283,
+ sect283r1, nistb283, sect409k1, nistk409, sect409r1,
+ nistb409, sect571k1, nistk571, sect571r1, nistb571,
+ secp160k1, secp160r1, secp160r2, secp192k1, secp192r1,
+ nistp192, secp224k1, secp224r1, nistp224, secp256k1,
+ secp256r1, secp384r1, secp521r1,
+ prime192v1, prime192v2, prime192v3,
+ prime239v1, prime239v2, prime239v3, c2pnb163v1,
+ c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1,
+ c2tnb191v2, c2tnb191v3,
+ c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3,
+ c2pnb272w1, c2pnb304w1,
+ c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1,
+ secp112r2, secp128r1, secp128r2, sect113r1, sect113r2,
+ sect131r1, sect131r2
+ </p></dd><dt><span class="term">-r </span></dt><dd><p>Display a certificate's binary DER encoding when listing information about that certificate with the -L option.</p></dd><dt><span class="term">-s subject</span></dt><dd><p>Identify a particular certificate owner for new certificates or certificate requests. Bracket this string with quotation marks if it contains spaces. The subject identification format follows RFC #1485.</p></dd><dt><span class="term">-t trustargs</span></dt><dd><p>Specify the trust attributes to modify in an existing certificate or to apply to a certificate when creating it or adding it to a database. There are three available trust categories for each certificate, expressed in the order <span class="emphasis"><em>SSL, email, object signing</em></span> for each trust setting. In each category position, use none, any, or all
of the attribute codes:
</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
<span class="command"><strong>p</strong></span> - Valid peer
@@ -59,7 +60,7 @@ of the attribute codes:
the certificate or adding it to a database. Express the offset in integers,
using a minus sign (-) to indicate a negative offset. If this argument is
not used, the validity period begins at the current system time. The length
- of the validity period is set with the -v argument. </p></dd><dt><span class="term">-X </span></dt><dd><p>Force the key and certificate database to open in read-write mode. This is used with the <code class="option">-U</code> and <code class="option">-L</code> command options.</p></dd><dt><span class="term">-x </span></dt><dd><p>Use <span class="command"><strong>certutil</strong></span> to generate the signature for a certificate being created or added to a database, rather than obtaining a signature from a separate CA.</p></dd><dt><span class="term">-y exp</span></dt><dd><p>Set an alternate exponent value to use in generating a new RSA public key for the database, instead of the default value of 65537. The available alternate values are 3 and 17.</p></dd><dt><span class="term">-z noise-file</span></dt><dd><p>Read a seed value from the specified file to generate a new private and public key pair. This argument makes it possible to use hardware-generated seed values or manually create a value from the keyboard. The minimum file size is 20 bytes.</p></dd><dt><span class="term">-Z hashAlg</span></dt><dd><p>Specify the hash algorithm to use with the -C, -S or -R command options. Possible keywords:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>MD2</p></li><li class="listitem"><p>MD4</p></li><li class="listitem"><p>MD5</p></li><li class="listitem"><p>SHA1</p></li><li class="listitem"><p>SHA224</p></li><li class="listitem"><p>SHA256</p></li><li class="listitem"><p>SHA384</p></li><li class="listitem"><p>SHA512</p></li></ul></div></dd><dt><span class="term">-0 SSO_password</span></dt><dd><p>Set a site security officer password on a token.</p></dd><dt><span class="term">-1 | --keyUsage keyword,keyword</span></dt><dd><p>Set an X.509 V3 Certificate Type Extension in the certificate. There are several available keywords:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
+ of the validity period is set with the -v argument. </p></dd><dt><span class="term">-X </span></dt><dd><p>Force the key and certificate database to open in read-write mode. This is used with the <code class="option">-U</code> and <code class="option">-L</code> command options.</p></dd><dt><span class="term">-x </span></dt><dd><p>Use <span class="command"><strong>certutil</strong></span> to generate the signature for a certificate being created or added to a database, rather than obtaining a signature from a separate CA.</p></dd><dt><span class="term">-y exp</span></dt><dd><p>Set an alternate exponent value to use in generating a new RSA public key for the database, instead of the default value of 65537. The available alternate values are 3 and 17.</p></dd><dt><span class="term">--pss</span></dt><dd><p>Restrict the generated certificate (with the <code class="option">-S</code> option) or certificate request (with the <code class="option">-R</code> option) to be used with the RSA-PSS signature scheme. This only works when the private key of the certificate or certificate request is RSA.</p></dd><dt><span class="term">--pss-sign</span></dt><dd><p>Sign the generated certificate with the RSA-PSS signature scheme (with the <code class="option">-C</code> or <code class="option">-S</code> option). This only works when the private key of the signer's certificate is RSA. If the signer's certificate is restricted to RSA-PSS, it is not necessary to specify this option.</p></dd><dt><span class="term">-z noise-file</span></dt><dd><p>Read a seed value from the specified file to generate a new private and public key pair. This argument makes it possible to use hardware-generated seed values or manually create a value from the keyboard. The minimum file size is 20 bytes.</p></dd><dt><span class="term">-Z hashAlg</span></dt><dd><p>Specify the hash algorithm to use with the -C, -S or -R command options. Possible keywords:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>MD2</p></li><li class="listitem"><p>MD4</p></li><li class="listitem"><p>MD5</p></li><li class="listitem"><p>SHA1</p></li><li class="listitem"><p>SHA224</p></li><li class="listitem"><p>SHA256</p></li><li class="listitem"><p>SHA384</p></li><li class="listitem"><p>SHA512</p></li></ul></div></dd><dt><span class="term">-0 SSO_password</span></dt><dd><p>Set a site security officer password on a token.</p></dd><dt><span class="term">-1 | --keyUsage keyword,keyword</span></dt><dd><p>Set an X.509 V3 Certificate Type Extension in the certificate. There are several available keywords:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
digitalSignature
</p></li><li class="listitem"><p>
nonRepudiation
diff --git a/security/nss/doc/html/pk12util.html b/security/nss/doc/html/pk12util.html
index fe516dd83..94dbf51e9 100644
--- a/security/nss/doc/html/pk12util.html
+++ b/security/nss/doc/html/pk12util.html
@@ -1,6 +1,6 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>PK12UTIL</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="PK12UTIL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">PK12UTIL</th></tr></table><hr></div><div class="refentry"><a name="pk12util"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>pk12util — Export and import keys and certificate to or from a PKCS #12 file and the NSS database</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><code class="command">pk12util</code> [-i p12File|-l p12File|-o p12File] [-d [sql:]directory] [-h tokenname] [-P dbprefix] [-r] [-v] [-k slotPasswordFile|-K slotPassword] [-w p12filePasswordFile|-W p12filePassword]</p></div></div><div class="refsection"><a name="idm233250345408"></a><h2>STATUS</h2><p>This documentation is still work in progress. Please contribute to the initial review in <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836477" target="_top">Mozilla NSS bug 836477</a>
- </p></div><div class="refsection"><a name="description"></a><h2>Description</h2><p>The PKCS #12 utility, <span class="command"><strong>pk12util</strong></span>, enables sharing certificates among any server that supports PKCS#12. The tool can import certificates and keys from PKCS#12 files into security databases, export certificates, and list certificates and keys.</p></div><div class="refsection"><a name="options"></a><h2>Options and Arguments</h2><p><span class="command"><strong>Options</strong></span></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-i p12file</span></dt><dd><p>Import keys and certificates from a PKCS#12 file into a security database.</p></dd><dt><span class="term">-l p12file</span></dt><dd><p>List the keys and certificates in PKCS#12 file.</p></dd><dt><span class="term">-o p12file</span></dt><dd><p>Export keys and certificates from the security database to a PKCS#12 file.</p></dd></dl></div><p><span class="command"><strong>Arguments</strong></span></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-c keyCipher</span></dt><dd><p>Specify the key encryption algorithm.</p></dd><dt><span class="term">-C certCipher</span></dt><dd><p>Specify the key cert (overall package) encryption algorithm.</p></dd><dt><span class="term">-d [sql:]directory</span></dt><dd><p>Specify the database directory into which to import to or export from certificates and keys.</p><p><span class="command"><strong>pk12util</strong></span> supports two types of databases: the legacy security databases (<code class="filename">cert8.db</code>, <code class="filename">key3.db</code>, and <code class="filename">secmod.db</code>) and new SQLite databases (<code class="filename">cert9.db</code>, <code class="filename">key4.db</code>, and <code class="filename">pkcs11.txt</code>). If the prefix <span class="command"><strong>sql:</strong></span> is not used, then the tool assumes that the given databases are in the old format.</p></dd><dt><span class="term">-h tokenname</span></dt><dd><p>Specify the name of the token to import into or export from.</p></dd><dt><span class="term">-k slotPasswordFile</span></dt><dd><p>Specify the text file containing the slot's password.</p></dd><dt><span class="term">-K slotPassword</span></dt><dd><p>Specify the slot's password.</p></dd><dt><span class="term">-m | --key-len keyLength</span></dt><dd><p>Specify the desired length of the symmetric key to be used to encrypt the private key.</p></dd><dt><span class="term">-n | --cert-key-len certKeyLength</span></dt><dd><p>Specify the desired length of the symmetric key to be used to encrypt the certificates and other meta-data.</p></dd><dt><span class="term">-n certname</span></dt><dd><p>Specify the nickname of the cert and private key to export.</p></dd><dt><span class="term">-P prefix</span></dt><dd><p>Specify the prefix used on the certificate and key databases. This option is provided as a special case.
- Changing the names of the certificate and key databases is not recommended.</p></dd><dt><span class="term">-r</span></dt><dd><p>Dumps all of the data in raw (binary) form. This must be saved as a DER file. The default is to return information in a pretty-print ASCII format, which displays the information about the certificates and public keys in the p12 file.</p></dd><dt><span class="term">-v </span></dt><dd><p>Enable debug logging when importing.</p></dd><dt><span class="term">-w p12filePasswordFile</span></dt><dd><p>Specify the text file containing the pkcs #12 file password.</p></dd><dt><span class="term">-W p12filePassword</span></dt><dd><p>Specify the pkcs #12 file password.</p></dd></dl></div></div><div class="refsection"><a name="return-codes"></a><h2>Return Codes</h2><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> 0 - No error</p></li><li class="listitem"><p> 1 - User Cancelled</p></li><li class="listitem"><p> 2 - Usage error</p></li><li class="listitem"><p> 6 - NLS init error</p></li><li class="listitem"><p> 8 - Certificate DB open error</p></li><li class="listitem"><p> 9 - Key DB open error</p></li><li class="listitem"><p> 10 - File initialization error</p></li><li class="listitem"><p> 11 - Unicode conversion error</p></li><li class="listitem"><p> 12 - Temporary file creation error</p></li><li class="listitem"><p> 13 - PKCS11 get slot error</p></li><li class="listitem"><p> 14 - PKCS12 decoder start error</p></li><li class="listitem"><p> 15 - error read from import file</p></li><li class="listitem"><p> 16 - pkcs12 decode error</p></li><li class="listitem"><p> 17 - pkcs12 decoder verify error</p></li><li class="listitem"><p> 18 - pkcs12 decoder validate bags error</p></li><li class="listitem"><p> 19 - pkcs12 decoder import bags error</p></li><li class="listitem"><p> 20 - key db conversion version 3 to version 2 error</p></li><li class="listitem"><p> 21 - cert db conversion version 7 to version 5 error</p></li><li class="listitem"><p> 22 - cert and key dbs patch error</p></li><li class="listitem"><p> 23 - get default cert db error</p></li><li class="listitem"><p> 24 - find cert by nickname error</p></li><li class="listitem"><p> 25 - create export context error</p></li><li class="listitem"><p> 26 - PKCS12 add password itegrity error</p></li><li class="listitem"><p> 27 - cert and key Safes creation error</p></li><li class="listitem"><p> 28 - PKCS12 add cert and key error</p></li><li class="listitem"><p> 29 - PKCS12 encode error</p></li></ul></div></div><div class="refsection"><a name="examples"></a><h2>Examples</h2><p><span class="command"><strong>Importing Keys and Certificates</strong></span></p><p>The most basic usage of <span class="command"><strong>pk12util</strong></span> for importing a certificate or key is the PKCS#12 input file (<code class="option">-i</code>) and some way to specify the security database being accessed (either <code class="option">-d</code> for a directory or <code class="option">-h</code> for a token).
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>PK12UTIL</title><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot"><link rel="home" href="index.html" title="PK12UTIL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">PK12UTIL</th></tr></table><hr></div><div class="refentry"><a name="pk12util"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>pk12util — Export and import keys and certificate to or from a PKCS #12 file and the NSS database</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><code class="command">pk12util</code> [-i p12File|-l p12File|-o p12File] [-d [sql:]directory] [-h tokenname] [-P dbprefix] [-r] [-v] [-k slotPasswordFile|-K slotPassword] [-w p12filePasswordFile|-W p12filePassword]</p></div></div><div class="refsection"><a name="idm139975398059856"></a><h2>STATUS</h2><p>This documentation is still work in progress. Please contribute to the initial review in <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836477" target="_top">Mozilla NSS bug 836477</a>
+ </p></div><div class="refsection"><a name="description"></a><h2>Description</h2><p>The PKCS #12 utility, <span class="command"><strong>pk12util</strong></span>, enables sharing certificates among any server that supports PKCS #12. The tool can import certificates and keys from PKCS #12 files into security databases, export certificates, and list certificates and keys.</p></div><div class="refsection"><a name="options"></a><h2>Options and Arguments</h2><p><span class="command"><strong>Options</strong></span></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-i p12file</span></dt><dd><p>Import keys and certificates from a PKCS #12 file into a security database.</p></dd><dt><span class="term">-l p12file</span></dt><dd><p>List the keys and certificates in PKCS #12 file.</p></dd><dt><span class="term">-o p12file</span></dt><dd><p>Export keys and certificates from the security database to a PKCS #12 file.</p></dd></dl></div><p><span class="command"><strong>Arguments</strong></span></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-c keyCipher</span></dt><dd><p>Specify the key encryption algorithm.</p></dd><dt><span class="term">-C certCipher</span></dt><dd><p>Specify the certiticate encryption algorithm.</p></dd><dt><span class="term">-d [sql:]directory</span></dt><dd><p>Specify the database directory into which to import to or export from certificates and keys.</p><p><span class="command"><strong>pk12util</strong></span> supports two types of databases: the legacy security databases (<code class="filename">cert8.db</code>, <code class="filename">key3.db</code>, and <code class="filename">secmod.db</code>) and new SQLite databases (<code class="filename">cert9.db</code>, <code class="filename">key4.db</code>, and <code class="filename">pkcs11.txt</code>). If the prefix <span class="command"><strong>sql:</strong></span> is not used, then the tool assumes that the given databases are in the old format.</p></dd><dt><span class="term">-h tokenname</span></dt><dd><p>Specify the name of the token to import into or export from.</p></dd><dt><span class="term">-k slotPasswordFile</span></dt><dd><p>Specify the text file containing the slot's password.</p></dd><dt><span class="term">-K slotPassword</span></dt><dd><p>Specify the slot's password.</p></dd><dt><span class="term">-m | --key-len keyLength</span></dt><dd><p>Specify the desired length of the symmetric key to be used to encrypt the private key.</p></dd><dt><span class="term">-n | --cert-key-len certKeyLength</span></dt><dd><p>Specify the desired length of the symmetric key to be used to encrypt the certificates and other meta-data.</p></dd><dt><span class="term">-n certname</span></dt><dd><p>Specify the nickname of the cert and private key to export.</p></dd><dt><span class="term">-P prefix</span></dt><dd><p>Specify the prefix used on the certificate and key databases. This option is provided as a special case.
+ Changing the names of the certificate and key databases is not recommended.</p></dd><dt><span class="term">-r</span></dt><dd><p>Dumps all of the data in raw (binary) form. This must be saved as a DER file. The default is to return information in a pretty-print ASCII format, which displays the information about the certificates and public keys in the p12 file.</p></dd><dt><span class="term">-v </span></dt><dd><p>Enable debug logging when importing.</p></dd><dt><span class="term">-w p12filePasswordFile</span></dt><dd><p>Specify the text file containing the pkcs #12 file password.</p></dd><dt><span class="term">-W p12filePassword</span></dt><dd><p>Specify the pkcs #12 file password.</p></dd></dl></div></div><div class="refsection"><a name="return-codes"></a><h2>Return Codes</h2><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> 0 - No error</p></li><li class="listitem"><p> 1 - User Cancelled</p></li><li class="listitem"><p> 2 - Usage error</p></li><li class="listitem"><p> 6 - NLS init error</p></li><li class="listitem"><p> 8 - Certificate DB open error</p></li><li class="listitem"><p> 9 - Key DB open error</p></li><li class="listitem"><p> 10 - File initialization error</p></li><li class="listitem"><p> 11 - Unicode conversion error</p></li><li class="listitem"><p> 12 - Temporary file creation error</p></li><li class="listitem"><p> 13 - PKCS11 get slot error</p></li><li class="listitem"><p> 14 - PKCS12 decoder start error</p></li><li class="listitem"><p> 15 - error read from import file</p></li><li class="listitem"><p> 16 - pkcs12 decode error</p></li><li class="listitem"><p> 17 - pkcs12 decoder verify error</p></li><li class="listitem"><p> 18 - pkcs12 decoder validate bags error</p></li><li class="listitem"><p> 19 - pkcs12 decoder import bags error</p></li><li class="listitem"><p> 20 - key db conversion version 3 to version 2 error</p></li><li class="listitem"><p> 21 - cert db conversion version 7 to version 5 error</p></li><li class="listitem"><p> 22 - cert and key dbs patch error</p></li><li class="listitem"><p> 23 - get default cert db error</p></li><li class="listitem"><p> 24 - find cert by nickname error</p></li><li class="listitem"><p> 25 - create export context error</p></li><li class="listitem"><p> 26 - PKCS12 add password itegrity error</p></li><li class="listitem"><p> 27 - cert and key Safes creation error</p></li><li class="listitem"><p> 28 - PKCS12 add cert and key error</p></li><li class="listitem"><p> 29 - PKCS12 encode error</p></li></ul></div></div><div class="refsection"><a name="examples"></a><h2>Examples</h2><p><span class="command"><strong>Importing Keys and Certificates</strong></span></p><p>The most basic usage of <span class="command"><strong>pk12util</strong></span> for importing a certificate or key is the PKCS #12 input file (<code class="option">-i</code>) and some way to specify the security database being accessed (either <code class="option">-d</code> for a directory or <code class="option">-h</code> for a token).
</p><p>
pk12util -i p12File [-h tokenname] [-v] [-d [sql:]directory] [-P dbprefix] [-k slotPasswordFile|-K slotPassword] [-w p12filePasswordFile|-W p12filePassword]
</p><p>For example:</p><p> </p><pre class="programlisting"># pk12util -i /tmp/cert-files/users.p12 -d sql:/home/my/sharednssdb
@@ -12,7 +12,7 @@ and should contain at least one non-alphabetic character.
Enter new password:
Re-enter password:
Enter password for PKCS12 file:
-pk12util: PKCS12 IMPORT SUCCESSFUL</pre><p><span class="command"><strong>Exporting Keys and Certificates</strong></span></p><p>Using the <span class="command"><strong>pk12util</strong></span> command to export certificates and keys requires both the name of the certificate to extract from the database (<code class="option">-n</code>) and the PKCS#12-formatted output file to write to. There are optional parameters that can be used to encrypt the file to protect the certificate material.
+pk12util: PKCS12 IMPORT SUCCESSFUL</pre><p><span class="command"><strong>Exporting Keys and Certificates</strong></span></p><p>Using the <span class="command"><strong>pk12util</strong></span> command to export certificates and keys requires both the name of the certificate to extract from the database (<code class="option">-n</code>) and the PKCS #12-formatted output file to write to. There are optional parameters that can be used to encrypt the file to protect the certificate material.
</p><p>pk12util -o p12File -n certname [-c keyCipher] [-C certCipher] [-m|--key_len keyLen] [-n|--cert_key_len certKeyLen] [-d [sql:]directory] [-P dbprefix] [-k slotPasswordFile|-K slotPassword] [-w p12filePasswordFile|-W p12filePassword]</p><p>For example:</p><pre class="programlisting"># pk12util -o certs.p12 -n Server-Cert -d sql:/home/my/sharednssdb
Enter password for PKCS12 file:
Re-enter password: </pre><p><span class="command"><strong>Listing Keys and Certificates</strong></span></p><p>The information in a <code class="filename">.p12</code> file are not human-readable. The certificates and keys in the file can be printed (listed) in a human-readable pretty-print format that shows information for every certificate and any public keys in the <code class="filename">.p12</code> file.
@@ -48,7 +48,7 @@ Key(shrouded):
Certificate Friendly Name: Thawte Personal Freemail Issuing CA - Thawte Consulting
Certificate Friendly Name: Thawte Freemail Member's Thawte Consulting (Pty) Ltd. ID
- </pre></div><div class="refsection"><a name="encryption"></a><h2>Password Encryption</h2><p>PKCS#12 provides for not only the protection of the private keys but also the certificate and meta-data associated with the keys. Password-based encryption is used to protect private keys on export to a PKCS#12 file and, optionally, the entire package. If no algorithm is specified, the tool defaults to using <span class="command"><strong>PKCS12 V2 PBE with SHA1 and 3KEY Triple DES-cbc</strong></span> for private key encryption. <span class="command"><strong>PKCS12 V2 PBE with SHA1 and 40 Bit RC4</strong></span> is the default for the overall package encryption when not in FIPS mode. When in FIPS mode, there is no package encryption.</p><p>The private key is always protected with strong encryption by default.</p><p>Several types of ciphers are supported.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">Symmetric CBC ciphers for PKCS#5 V2</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>DES-CBC</p></li><li class="listitem"><p>RC2-CBC</p></li><li class="listitem"><p>RC5-CBCPad</p></li><li class="listitem"><p>DES-EDE3-CBC (the default for key encryption)</p></li><li class="listitem"><p>AES-128-CBC</p></li><li class="listitem"><p>AES-192-CBC</p></li><li class="listitem"><p>AES-256-CBC</p></li><li class="listitem"><p>CAMELLIA-128-CBC</p></li><li class="listitem"><p>CAMELLIA-192-CBC</p></li><li class="listitem"><p>CAMELLIA-256-CBC</p></li></ul></div></dd><dt><span class="term">PKCS#12 PBE ciphers</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>PKCS #12 PBE with Sha1 and 128 Bit RC4</p></li><li class="listitem"><p>PKCS #12 PBE with Sha1 and 40 Bit RC4</p></li><li class="listitem"><p>PKCS #12 PBE with Sha1 and Triple DES CBC</p></li><li class="listitem"><p>PKCS #12 PBE with Sha1 and 128 Bit RC2 CBC</p></li><li class="listitem"><p>PKCS #12 PBE with Sha1 and 40 Bit RC2 CBC</p></li><li class="listitem"><p>PKCS12 V2 PBE with SHA1 and 128 Bit RC4</p></li><li class="listitem"><p>PKCS12 V2 PBE with SHA1 and 40 Bit RC4 (the default for non-FIPS mode)</p></li><li class="listitem"><p>PKCS12 V2 PBE with SHA1 and 3KEY Triple DES-cbc</p></li><li class="listitem"><p>PKCS12 V2 PBE with SHA1 and 2KEY Triple DES-cbc</p></li><li class="listitem"><p>PKCS12 V2 PBE with SHA1 and 128 Bit RC2 CBC</p></li><li class="listitem"><p>PKCS12 V2 PBE with SHA1 and 40 Bit RC2 CBC</p></li></ul></div></dd><dt><span class="term">PKCS#5 PBE ciphers</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>PKCS #5 Password Based Encryption with MD2 and DES CBC</p></li><li class="listitem"><p>PKCS #5 Password Based Encryption with MD5 and DES CBC</p></li><li class="listitem"><p>PKCS #5 Password Based Encryption with SHA1 and DES CBC</p></li></ul></div></dd></dl></div><p>With PKCS#12, the crypto provider may be the soft token module or an external hardware module. If the cryptographic module does not support the requested algorithm, then the next best fit will be selected (usually the default). If no suitable replacement for the desired algorithm can be found, the tool returns the error <span class="emphasis"><em>no security module can perform the requested operation</em></span>.</p></div><div class="refsection"><a name="databases"></a><h2>NSS Database Types</h2><p>NSS originally used BerkeleyDB databases to store security information.
+ </pre></div><div class="refsection"><a name="encryption"></a><h2>Password Encryption</h2><p>PKCS #12 provides for not only the protection of the private keys but also the certificate and meta-data associated with the keys. Password-based encryption is used to protect private keys on export to a PKCS #12 file and, optionally, the associated certificates. If no algorithm is specified, the tool defaults to using PKCS #12 SHA-1 and 3-key triple DES for private key encryption. When not in FIPS mode, PKCS #12 SHA-1 and 40-bit RC4 is used for certificate encryption. When in FIPS mode, there is no certificate encryption. If certificate encryption is not wanted, specify <strong class="userinput"><code>"NONE"</code></strong> as the argument of the <code class="option">-C</code> option.</p><p>The private key is always protected with strong encryption by default.</p><p>Several types of ciphers are supported.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">PKCS #5 password-based encryption</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>PBES2 with AES-CBC-Pad as underlying encryption scheme (<strong class="userinput"><code>"AES-128-CBC"</code></strong>, <strong class="userinput"><code>"AES-192-CBC"</code></strong>, and <strong class="userinput"><code>"AES-256-CBC"</code></strong>)</p></li></ul></div></dd><dt><span class="term">PKCS #12 password-based encryption</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>SHA-1 and 128-bit RC4 (<strong class="userinput"><code>"PKCS #12 V2 PBE With SHA-1 And 128 Bit RC4"</code></strong> or <strong class="userinput"><code>"RC4"</code></strong>)</p></li><li class="listitem"><p>SHA-1 and 40-bit RC4 (<strong class="userinput"><code>"PKCS #12 V2 PBE With SHA-1 And 40 Bit RC4"</code></strong>) (used by default for certificate encryption in non-FIPS mode)</p></li><li class="listitem"><p>SHA-1 and 3-key triple-DES (<strong class="userinput"><code>"PKCS #12 V2 PBE With SHA-1 And 3KEY Triple DES-CBC"</code></strong> or <strong class="userinput"><code>"DES-EDE3-CBC"</code></strong>)</p></li><li class="listitem"><p>SHA-1 and 128-bit RC2 (<strong class="userinput"><code>"PKCS #12 V2 PBE With SHA-1 And 128 Bit RC2 CBC"</code></strong> or <strong class="userinput"><code>"RC2-CBC"</code></strong>)</p></li><li class="listitem"><p>SHA-1 and 40-bit RC2 (<strong class="userinput"><code>"PKCS #12 V2 PBE With SHA-1 And 40 Bit RC2 CBC"</code></strong>)</p></li></ul></div></dd></dl></div><p>With PKCS #12, the crypto provider may be the soft token module or an external hardware module. If the cryptographic module does not support the requested algorithm, then the next best fit will be selected (usually the default). If no suitable replacement for the desired algorithm can be found, the tool returns the error <span class="emphasis"><em>no security module can perform the requested operation</em></span>.</p></div><div class="refsection"><a name="databases"></a><h2>NSS Database Types</h2><p>NSS originally used BerkeleyDB databases to store security information.
The last versions of these <span class="emphasis"><em>legacy</em></span> databases are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
cert8.db for certificates
</p></li><li class="listitem"><p>
@@ -68,7 +68,7 @@ BerkleyDB. These new databases provide more accessibility and performance:</p><d
Using the SQLite databases must be manually specified by using the <span class="command"><strong>sql:</strong></span> prefix with the given security directory. For example:</p><pre class="programlisting"># pk12util -i /tmp/cert-files/users.p12 -d sql:/home/my/sharednssdb</pre><p>To set the shared database type as the default type for the tools, set the <code class="envar">NSS_DEFAULT_DB_TYPE</code> environment variable to <code class="envar">sql</code>:</p><pre class="programlisting">export NSS_DEFAULT_DB_TYPE="sql"</pre><p>This line can be set added to the <code class="filename">~/.bashrc</code> file to make the change permanent.</p><p>Most applications do not use the shared database by default, but they can be configured to use them. For example, this how-to article covers how to configure Firefox and Thunderbird to use the new shared NSS databases:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
https://wiki.mozilla.org/NSS_Shared_DB_Howto</p></li></ul></div><p>For an engineering draft on the changes in the shared NSS databases, see the NSS project wiki:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
https://wiki.mozilla.org/NSS_Shared_DB
- </p></li></ul></div></div><div class="refsection"><a name="seealso"></a><h2>See Also</h2><p>certutil (1)</p><p>modutil (1)</p><p>The NSS wiki has information on the new database design and how to configure applications to use it.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
+ </p></li></ul></div></div><div class="refsection"><a name="compatibility"></a><h2>Compatibility Notes</h2><p>The exporting behavior of <span class="command"><strong>pk12util</strong></span> has changed over time, while importing files exported with older versions of NSS is still supported.</p><p>Until the 3.30 release, <span class="command"><strong>pk12util</strong></span> used the UTF-16 encoding for the PKCS #5 password-based encryption schemes, while the recommendation is to encode passwords in UTF-8 if the used encryption scheme is defined outside of the PKCS #12 standard.</p><p>Until the 3.31 release, even when <strong class="userinput"><code>"AES-128-CBC"</code></strong> or <strong class="userinput"><code>"AES-192-CBC"</code></strong> is given from the command line, <span class="command"><strong>pk12util</strong></span> always used 256-bit AES as the underlying encryption scheme.</p><p>For historical reasons, <span class="command"><strong>pk12util</strong></span> accepts password-based encryption schemes not listed in this document. However, those schemes are not officially supported and may have issues in interoperability with other tools.</p></div><div class="refsection"><a name="seealso"></a><h2>See Also</h2><p>certutil (1)</p><p>modutil (1)</p><p>The NSS wiki has information on the new database design and how to configure applications to use it.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
https://wiki.mozilla.org/NSS_Shared_DB_Howto</p></li><li class="listitem"><p>
https://wiki.mozilla.org/NSS_Shared_DB
</p></li></ul></div></div><div class="refsection"><a name="resources"></a><h2>Additional Resources</h2><p>For information about NSS and other tools related to NSS (like JSS), check out the NSS project wiki at <a class="ulink" href="http://www.mozilla.org/projects/security/pki/nss/" target="_top">http://www.mozilla.org/projects/security/pki/nss/</a>. The NSS site relates directly to NSS code changes and releases.</p><p>Mailing lists: https://lists.mozilla.org/listinfo/dev-tech-crypto</p><p>IRC: Freenode at #dogtag-pki</p></div><div class="refsection"><a name="authors"></a><h2>Authors</h2><p>The NSS tools were written and maintained by developers with Netscape, Red Hat, Sun, Oracle, Mozilla, and Google.</p><p>
diff --git a/security/nss/doc/nroff/certutil.1 b/security/nss/doc/nroff/certutil.1
index b2a8bd2bb..80a02fc27 100644
--- a/security/nss/doc/nroff/certutil.1
+++ b/security/nss/doc/nroff/certutil.1
@@ -1,13 +1,13 @@
'\" t
.\" Title: CERTUTIL
.\" Author: [see the "Authors" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 8 September 2016
+.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
+.\" Date: 27 October 2017
.\" Manual: NSS Security Tools
.\" Source: nss-tools
.\" Language: English
.\"
-.TH "CERTUTIL" "1" "8 September 2016" "nss-tools" "NSS Security Tools"
+.TH "CERTUTIL" "1" "27 October 2017" "nss-tools" "NSS Security Tools"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -371,9 +371,9 @@ Read an alternate PQG value from the specified file when generating DSA key pair
\fBcertutil\fR
generates its own PQG value\&. PQG files are created with a separate DSA utility\&.
.sp
-Elliptic curve name is one of the ones from nistp256, nistp384, nistp521, curve25519.
+Elliptic curve name is one of the ones from nistp256, nistp384, nistp521, curve25519\&.
.sp
-If a token is available that supports more curves, the foolowing curves are supported as well: sect163k1, nistk163, sect163r1, sect163r2, nistb163, sect193r1, sect193r2, sect233k1, nistk233, sect233r1, nistb233, sect239k1, sect283k1, nistk283, sect283r1, nistb283, sect409k1, nistk409, sect409r1, nistb409, sect571k1, nistk571, sect571r1, nistb571, secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, nistp192, secp224k1, secp224r1, nistp224, secp256k1, secp256r1, secp384r1, secp521r1, prime192v1, prime192v2, prime192v3, prime239v1, prime239v2, prime239v3, c2pnb163v1, c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1, c2tnb191v2, c2tnb191v3, c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3, c2pnb272w1, c2pnb304w1, c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, secp112r2, secp128r1, secp128r2, sect113r1, sect113r2, sect131r1, sect131r2
+If a token is available that supports more curves, the foolowing curves are supported as well: sect163k1, nistk163, sect163r1, sect163r2, nistb163, sect193r1, sect193r2, sect233k1, nistk233, sect233r1, nistb233, sect239k1, sect283k1, nistk283, sect283r1, nistb283, sect409k1, nistk409, sect409r1, nistb409, sect571k1, nistk571, sect571r1, nistb571, secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, nistp192, secp224k1, secp224r1, nistp224, secp256k1, secp256r1, secp384r1, secp521r1, prime192v1, prime192v2, prime192v3, prime239v1, prime239v2, prime239v3, c2pnb163v1, c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1, c2tnb191v2, c2tnb191v3, c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3, c2pnb272w1, c2pnb304w1, c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, secp112r2, secp128r1, secp128r2, sect113r1, sect113r2, sect131r1, sect131r2
.RE
.PP
\-r
@@ -609,6 +609,24 @@ to generate the signature for a certificate being created or added to a database
Set an alternate exponent value to use in generating a new RSA public key for the database, instead of the default value of 65537\&. The available alternate values are 3 and 17\&.
.RE
.PP
+\-\-pss
+.RS 4
+Restrict the generated certificate (with the
+\fB\-S\fR
+option) or certificate request (with the
+\fB\-R\fR
+option) to be used with the RSA\-PSS signature scheme\&. This only works when the private key of the certificate or certificate request is RSA\&.
+.RE
+.PP
+\-\-pss\-sign
+.RS 4
+Sign the generated certificate with the RSA\-PSS signature scheme (with the
+\fB\-C\fR
+or
+\fB\-S\fR
+option)\&. This only works when the private key of the signer\*(Aqs certificate is RSA\&. If the signer\*(Aqs certificate is restricted to RSA\-PSS, it is not necessary to specify this option\&.
+.RE
+.PP
\-z noise\-file
.RS 4
Read a seed value from the specified file to generate a new private and public key pair\&. This argument makes it possible to use hardware\-generated seed values or manually create a value from the keyboard\&. The minimum file size is 20 bytes\&.
@@ -1512,7 +1530,8 @@ There are ways to narrow the keys listed in the search results:
.IP \(bu 2.3
.\}
To return a specific key, use the
-\fB\-n\fR\fIname\fR
+\fB\-n\fR
+\fIname\fR
argument with the name of the key\&.
.RE
.sp
@@ -1525,7 +1544,8 @@ argument with the name of the key\&.
.IP \(bu 2.3
.\}
If there are multiple security devices loaded, then the
-\fB\-h\fR\fItokenname\fR
+\fB\-h\fR
+\fItokenname\fR
argument can search a specific token or all tokens\&.
.RE
.sp
@@ -1538,7 +1558,8 @@ argument can search a specific token or all tokens\&.
.IP \(bu 2.3
.\}
If there are multiple key types available, then the
-\fB\-k\fR\fIkey\-type\fR
+\fB\-k\fR
+\fIkey\-type\fR
argument can search a specific type of key, like RSA, DSA, or ECC\&.
.RE
.PP
diff --git a/security/nss/doc/nroff/pk12util.1 b/security/nss/doc/nroff/pk12util.1
index c4fa972c0..e0a8da833 100644
--- a/security/nss/doc/nroff/pk12util.1
+++ b/security/nss/doc/nroff/pk12util.1
@@ -1,13 +1,13 @@
'\" t
.\" Title: PK12UTIL
.\" Author: [see the "Authors" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 5 June 2014
+.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
+.\" Date: 27 October 2017
.\" Manual: NSS Security Tools
.\" Source: nss-tools
.\" Language: English
.\"
-.TH "PK12UTIL" "1" "5 June 2014" "nss-tools" "NSS Security Tools"
+.TH "PK12UTIL" "1" "27 October 2017" "nss-tools" "NSS Security Tools"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -39,24 +39,24 @@ This documentation is still work in progress\&. Please contribute to the initial
.SH "DESCRIPTION"
.PP
The PKCS #12 utility,
-\fBpk12util\fR, enables sharing certificates among any server that supports PKCS#12\&. The tool can import certificates and keys from PKCS#12 files into security databases, export certificates, and list certificates and keys\&.
+\fBpk12util\fR, enables sharing certificates among any server that supports PKCS #12\&. The tool can import certificates and keys from PKCS #12 files into security databases, export certificates, and list certificates and keys\&.
.SH "OPTIONS AND ARGUMENTS"
.PP
\fBOptions\fR
.PP
\-i p12file
.RS 4
-Import keys and certificates from a PKCS#12 file into a security database\&.
+Import keys and certificates from a PKCS #12 file into a security database\&.
.RE
.PP
\-l p12file
.RS 4
-List the keys and certificates in PKCS#12 file\&.
+List the keys and certificates in PKCS #12 file\&.
.RE
.PP
\-o p12file
.RS 4
-Export keys and certificates from the security database to a PKCS#12 file\&.
+Export keys and certificates from the security database to a PKCS #12 file\&.
.RE
.PP
\fBArguments\fR
@@ -68,7 +68,7 @@ Specify the key encryption algorithm\&.
.PP
\-C certCipher
.RS 4
-Specify the key cert (overall package) encryption algorithm\&.
+Specify the certiticate encryption algorithm\&.
.RE
.PP
\-d [sql:]directory
@@ -432,7 +432,7 @@ Specify the pkcs #12 file password\&.
.PP
The most basic usage of
\fBpk12util\fR
-for importing a certificate or key is the PKCS#12 input file (\fB\-i\fR) and some way to specify the security database being accessed (either
+for importing a certificate or key is the PKCS #12 input file (\fB\-i\fR) and some way to specify the security database being accessed (either
\fB\-d\fR
for a directory or
\fB\-h\fR
@@ -467,7 +467,7 @@ pk12util: PKCS12 IMPORT SUCCESSFUL
.PP
Using the
\fBpk12util\fR
-command to export certificates and keys requires both the name of the certificate to extract from the database (\fB\-n\fR) and the PKCS#12\-formatted output file to write to\&. There are optional parameters that can be used to encrypt the file to protect the certificate material\&.
+command to export certificates and keys requires both the name of the certificate to extract from the database (\fB\-n\fR) and the PKCS #12\-formatted output file to write to\&. There are optional parameters that can be used to encrypt the file to protect the certificate material\&.
.PP
pk12util \-o p12File \-n certname [\-c keyCipher] [\-C certCipher] [\-m|\-\-key_len keyLen] [\-n|\-\-cert_key_len certKeyLen] [\-d [sql:]directory] [\-P dbprefix] [\-k slotPasswordFile|\-K slotPassword] [\-w p12filePasswordFile|\-W p12filePassword]
.PP
@@ -559,17 +559,17 @@ Certificate Friendly Name: Thawte Freemail Member\*(Aqs Thawte Consulting (Pt
.\}
.SH "PASSWORD ENCRYPTION"
.PP
-PKCS#12 provides for not only the protection of the private keys but also the certificate and meta\-data associated with the keys\&. Password\-based encryption is used to protect private keys on export to a PKCS#12 file and, optionally, the entire package\&. If no algorithm is specified, the tool defaults to using
-\fBPKCS12 V2 PBE with SHA1 and 3KEY Triple DES\-cbc\fR
-for private key encryption\&.
-\fBPKCS12 V2 PBE with SHA1 and 40 Bit RC4\fR
-is the default for the overall package encryption when not in FIPS mode\&. When in FIPS mode, there is no package encryption\&.
+PKCS #12 provides for not only the protection of the private keys but also the certificate and meta\-data associated with the keys\&. Password\-based encryption is used to protect private keys on export to a PKCS #12 file and, optionally, the associated certificates\&. If no algorithm is specified, the tool defaults to using PKCS #12 SHA\-1 and 3\-key triple DES for private key encryption\&. When not in FIPS mode, PKCS #12 SHA\-1 and 40\-bit RC4 is used for certificate encryption\&. When in FIPS mode, there is no certificate encryption\&. If certificate encryption is not wanted, specify
+\fB"NONE"\fR
+as the argument of the
+\fB\-C\fR
+option\&.
.PP
The private key is always protected with strong encryption by default\&.
.PP
Several types of ciphers are supported\&.
.PP
-Symmetric CBC ciphers for PKCS#5 V2
+PKCS #5 password\-based encryption
.RS 4
.sp
.RS 4
@@ -580,110 +580,13 @@ Symmetric CBC ciphers for PKCS#5 V2
.sp -1
.IP \(bu 2.3
.\}
-DES\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-RC2\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-RC5\-CBCPad
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-DES\-EDE3\-CBC (the default for key encryption)
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-AES\-128\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-AES\-192\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-AES\-256\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-CAMELLIA\-128\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-CAMELLIA\-192\-CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-CAMELLIA\-256\-CBC
+PBES2 with AES\-CBC\-Pad as underlying encryption scheme (\fB"AES\-128\-CBC"\fR,
+\fB"AES\-192\-CBC"\fR, and
+\fB"AES\-256\-CBC"\fR)
.RE
.RE
.PP
-PKCS#12 PBE ciphers
+PKCS #12 password\-based encryption
.RS 4
.sp
.RS 4
@@ -694,7 +597,9 @@ PKCS#12 PBE ciphers
.sp -1
.IP \(bu 2.3
.\}
-PKCS #12 PBE with Sha1 and 128 Bit RC4
+SHA\-1 and 128\-bit RC4 (\fB"PKCS #12 V2 PBE With SHA\-1 And 128 Bit RC4"\fR
+or
+\fB"RC4"\fR)
.RE
.sp
.RS 4
@@ -705,7 +610,7 @@ PKCS #12 PBE with Sha1 and 128 Bit RC4
.sp -1
.IP \(bu 2.3
.\}
-PKCS #12 PBE with Sha1 and 40 Bit RC4
+SHA\-1 and 40\-bit RC4 (\fB"PKCS #12 V2 PBE With SHA\-1 And 40 Bit RC4"\fR) (used by default for certificate encryption in non\-FIPS mode)
.RE
.sp
.RS 4
@@ -716,7 +621,9 @@ PKCS #12 PBE with Sha1 and 40 Bit RC4
.sp -1
.IP \(bu 2.3
.\}
-PKCS #12 PBE with Sha1 and Triple DES CBC
+SHA\-1 and 3\-key triple\-DES (\fB"PKCS #12 V2 PBE With SHA\-1 And 3KEY Triple DES\-CBC"\fR
+or
+\fB"DES\-EDE3\-CBC"\fR)
.RE
.sp
.RS 4
@@ -727,7 +634,9 @@ PKCS #12 PBE with Sha1 and Triple DES CBC
.sp -1
.IP \(bu 2.3
.\}
-PKCS #12 PBE with Sha1 and 128 Bit RC2 CBC
+SHA\-1 and 128\-bit RC2 (\fB"PKCS #12 V2 PBE With SHA\-1 And 128 Bit RC2 CBC"\fR
+or
+\fB"RC2\-CBC"\fR)
.RE
.sp
.RS 4
@@ -738,114 +647,11 @@ PKCS #12 PBE with Sha1 and 128 Bit RC2 CBC
.sp -1
.IP \(bu 2.3
.\}
-PKCS #12 PBE with Sha1 and 40 Bit RC2 CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS12 V2 PBE with SHA1 and 128 Bit RC4
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS12 V2 PBE with SHA1 and 40 Bit RC4 (the default for non\-FIPS mode)
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS12 V2 PBE with SHA1 and 3KEY Triple DES\-cbc
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS12 V2 PBE with SHA1 and 2KEY Triple DES\-cbc
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS12 V2 PBE with SHA1 and 128 Bit RC2 CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS12 V2 PBE with SHA1 and 40 Bit RC2 CBC
+SHA\-1 and 40\-bit RC2 (\fB"PKCS #12 V2 PBE With SHA\-1 And 40 Bit RC2 CBC"\fR)
.RE
.RE
.PP
-PKCS#5 PBE ciphers
-.RS 4
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS #5 Password Based Encryption with MD2 and DES CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS #5 Password Based Encryption with MD5 and DES CBC
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-PKCS #5 Password Based Encryption with SHA1 and DES CBC
-.RE
-.RE
-.PP
-With PKCS#12, the crypto provider may be the soft token module or an external hardware module\&. If the cryptographic module does not support the requested algorithm, then the next best fit will be selected (usually the default)\&. If no suitable replacement for the desired algorithm can be found, the tool returns the error
+With PKCS #12, the crypto provider may be the soft token module or an external hardware module\&. If the cryptographic module does not support the requested algorithm, then the next best fit will be selected (usually the default)\&. If no suitable replacement for the desired algorithm can be found, the tool returns the error
\fIno security module can perform the requested operation\fR\&.
.SH "NSS DATABASE TYPES"
.PP
@@ -987,6 +793,27 @@ For an engineering draft on the changes in the shared NSS databases, see the NSS
.\}
https://wiki\&.mozilla\&.org/NSS_Shared_DB
.RE
+.SH "COMPATIBILITY NOTES"
+.PP
+The exporting behavior of
+\fBpk12util\fR
+has changed over time, while importing files exported with older versions of NSS is still supported\&.
+.PP
+Until the 3\&.30 release,
+\fBpk12util\fR
+used the UTF\-16 encoding for the PKCS #5 password\-based encryption schemes, while the recommendation is to encode passwords in UTF\-8 if the used encryption scheme is defined outside of the PKCS #12 standard\&.
+.PP
+Until the 3\&.31 release, even when
+\fB"AES\-128\-CBC"\fR
+or
+\fB"AES\-192\-CBC"\fR
+is given from the command line,
+\fBpk12util\fR
+always used 256\-bit AES as the underlying encryption scheme\&.
+.PP
+For historical reasons,
+\fBpk12util\fR
+accepts password\-based encryption schemes not listed in this document\&. However, those schemes are not officially supported and may have issues in interoperability with other tools\&.
.SH "SEE ALSO"
.PP
certutil (1)
diff --git a/security/nss/doc/pk12util.xml b/security/nss/doc/pk12util.xml
index 03ee356e6..c26794965 100644
--- a/security/nss/doc/pk12util.xml
+++ b/security/nss/doc/pk12util.xml
@@ -46,7 +46,7 @@
<refsection id="description">
<title>Description</title>
- <para>The PKCS #12 utility, <command>pk12util</command>, enables sharing certificates among any server that supports PKCS#12. The tool can import certificates and keys from PKCS#12 files into security databases, export certificates, and list certificates and keys.</para>
+ <para>The PKCS #12 utility, <command>pk12util</command>, enables sharing certificates among any server that supports PKCS #12. The tool can import certificates and keys from PKCS #12 files into security databases, export certificates, and list certificates and keys.</para>
</refsection>
<refsection id="options">
@@ -55,17 +55,17 @@
<variablelist>
<varlistentry>
<term>-i p12file</term>
- <listitem><para>Import keys and certificates from a PKCS#12 file into a security database.</para></listitem>
+ <listitem><para>Import keys and certificates from a PKCS #12 file into a security database.</para></listitem>
</varlistentry>
<varlistentry>
<term>-l p12file</term>
- <listitem><para>List the keys and certificates in PKCS#12 file.</para></listitem>
+ <listitem><para>List the keys and certificates in PKCS #12 file.</para></listitem>
</varlistentry>
<varlistentry>
<term>-o p12file</term>
- <listitem><para>Export keys and certificates from the security database to a PKCS#12 file.</para></listitem>
+ <listitem><para>Export keys and certificates from the security database to a PKCS #12 file.</para></listitem>
</varlistentry>
</variablelist>
@@ -78,7 +78,7 @@
<varlistentry>
<term>-C certCipher</term>
- <listitem><para>Specify the key cert (overall package) encryption algorithm.</para></listitem>
+ <listitem><para>Specify the certiticate encryption algorithm.</para></listitem>
</varlistentry>
<varlistentry>
@@ -233,7 +233,7 @@
<refsection id="examples">
<title>Examples</title>
<para><command>Importing Keys and Certificates</command></para>
- <para>The most basic usage of <command>pk12util</command> for importing a certificate or key is the PKCS#12 input file (<option>-i</option>) and some way to specify the security database being accessed (either <option>-d</option> for a directory or <option>-h</option> for a token).
+ <para>The most basic usage of <command>pk12util</command> for importing a certificate or key is the PKCS #12 input file (<option>-i</option>) and some way to specify the security database being accessed (either <option>-d</option> for a directory or <option>-h</option> for a token).
</para>
<para>
pk12util -i p12File [-h tokenname] [-v] [-d [sql:]directory] [-P dbprefix] [-k slotPasswordFile|-K slotPassword] [-w p12filePasswordFile|-W p12filePassword]
@@ -252,7 +252,7 @@ Enter password for PKCS12 file:
pk12util: PKCS12 IMPORT SUCCESSFUL</programlisting>
<para><command>Exporting Keys and Certificates</command></para>
- <para>Using the <command>pk12util</command> command to export certificates and keys requires both the name of the certificate to extract from the database (<option>-n</option>) and the PKCS#12-formatted output file to write to. There are optional parameters that can be used to encrypt the file to protect the certificate material.
+ <para>Using the <command>pk12util</command> command to export certificates and keys requires both the name of the certificate to extract from the database (<option>-n</option>) and the PKCS #12-formatted output file to write to. There are optional parameters that can be used to encrypt the file to protect the certificate material.
</para>
<para>pk12util -o p12File -n certname [-c keyCipher] [-C certCipher] [-m|--key_len keyLen] [-n|--cert_key_len certKeyLen] [-d [sql:]directory] [-P dbprefix] [-k slotPasswordFile|-K slotPassword] [-w p12filePasswordFile|-W p12filePassword]</para>
<para>For example:</para>
@@ -304,58 +304,34 @@ Certificate Friendly Name: Thawte Freemail Member's Thawte Consulting (Pty) L
<refsection id="encryption">
<title>Password Encryption</title>
- <para>PKCS#12 provides for not only the protection of the private keys but also the certificate and meta-data associated with the keys. Password-based encryption is used to protect private keys on export to a PKCS#12 file and, optionally, the entire package. If no algorithm is specified, the tool defaults to using <command>PKCS12 V2 PBE with SHA1 and 3KEY Triple DES-cbc</command> for private key encryption. <command>PKCS12 V2 PBE with SHA1 and 40 Bit RC4</command> is the default for the overall package encryption when not in FIPS mode. When in FIPS mode, there is no package encryption.</para>
+ <para>PKCS #12 provides for not only the protection of the private keys but also the certificate and meta-data associated with the keys. Password-based encryption is used to protect private keys on export to a PKCS #12 file and, optionally, the associated certificates. If no algorithm is specified, the tool defaults to using PKCS #12 SHA-1 and 3-key triple DES for private key encryption. When not in FIPS mode, PKCS #12 SHA-1 and 40-bit RC4 is used for certificate encryption. When in FIPS mode, there is no certificate encryption. If certificate encryption is not wanted, specify <userinput>"NONE"</userinput> as the argument of the <option>-C</option> option.</para>
<para>The private key is always protected with strong encryption by default.</para>
<para>Several types of ciphers are supported.</para>
<variablelist>
<varlistentry>
- <term>Symmetric CBC ciphers for PKCS#5 V2</term>
+ <term>PKCS #5 password-based encryption</term>
<listitem>
- <itemizedlist>
- <listitem><para>DES-CBC</para></listitem>
- <listitem><para>RC2-CBC</para></listitem>
- <listitem><para>RC5-CBCPad</para></listitem>
- <listitem><para>DES-EDE3-CBC (the default for key encryption)</para></listitem>
- <listitem><para>AES-128-CBC</para></listitem>
- <listitem><para>AES-192-CBC</para></listitem>
- <listitem><para>AES-256-CBC</para></listitem>
- <listitem><para>CAMELLIA-128-CBC</para></listitem>
- <listitem><para>CAMELLIA-192-CBC</para></listitem>
- <listitem><para>CAMELLIA-256-CBC</para></listitem>
- </itemizedlist>
+ <itemizedlist>
+ <listitem><para>PBES2 with AES-CBC-Pad as underlying encryption scheme (<userinput>"AES-128-CBC"</userinput>, <userinput>"AES-192-CBC"</userinput>, and <userinput>"AES-256-CBC"</userinput>)</para></listitem>
+ </itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
- <term>PKCS#12 PBE ciphers</term>
+ <term>PKCS #12 password-based encryption</term>
<listitem>
- <itemizedlist>
- <listitem><para>PKCS #12 PBE with Sha1 and 128 Bit RC4</para></listitem>
- <listitem><para>PKCS #12 PBE with Sha1 and 40 Bit RC4</para></listitem>
- <listitem><para>PKCS #12 PBE with Sha1 and Triple DES CBC</para></listitem>
- <listitem><para>PKCS #12 PBE with Sha1 and 128 Bit RC2 CBC</para></listitem>
- <listitem><para>PKCS #12 PBE with Sha1 and 40 Bit RC2 CBC</para></listitem>
- <listitem><para>PKCS12 V2 PBE with SHA1 and 128 Bit RC4</para></listitem>
- <listitem><para>PKCS12 V2 PBE with SHA1 and 40 Bit RC4 (the default for non-FIPS mode)</para></listitem>
- <listitem><para>PKCS12 V2 PBE with SHA1 and 3KEY Triple DES-cbc</para></listitem>
- <listitem><para>PKCS12 V2 PBE with SHA1 and 2KEY Triple DES-cbc</para></listitem>
- <listitem><para>PKCS12 V2 PBE with SHA1 and 128 Bit RC2 CBC</para></listitem>
- <listitem><para>PKCS12 V2 PBE with SHA1 and 40 Bit RC2 CBC</para></listitem>
- </itemizedlist>
- </listitem>
- </varlistentry>
- <varlistentry><term>PKCS#5 PBE ciphers</term>
- <listitem>
- <itemizedlist>
- <listitem><para>PKCS #5 Password Based Encryption with MD2 and DES CBC</para></listitem>
- <listitem><para>PKCS #5 Password Based Encryption with MD5 and DES CBC</para></listitem>
- <listitem><para>PKCS #5 Password Based Encryption with SHA1 and DES CBC</para></listitem>
- </itemizedlist>
+ <itemizedlist>
+ <listitem><para>SHA-1 and 128-bit RC4 (<userinput>"PKCS #12 V2 PBE With SHA-1 And 128 Bit RC4"</userinput> or <userinput>"RC4"</userinput>)</para></listitem>
+ <listitem><para>SHA-1 and 40-bit RC4 (<userinput>"PKCS #12 V2 PBE With SHA-1 And 40 Bit RC4"</userinput>) (used by default for certificate encryption in non-FIPS mode)</para></listitem>
+ <listitem><para>SHA-1 and 3-key triple-DES (<userinput>"PKCS #12 V2 PBE With SHA-1 And 3KEY Triple DES-CBC"</userinput> or <userinput>"DES-EDE3-CBC"</userinput>)</para></listitem>
+ <listitem><para>SHA-1 and 128-bit RC2 (<userinput>"PKCS #12 V2 PBE With SHA-1 And 128 Bit RC2 CBC"</userinput> or <userinput>"RC2-CBC"</userinput>)</para></listitem>
+ <listitem><para>SHA-1 and 40-bit RC2 (<userinput>"PKCS #12 V2 PBE With SHA-1 And 40 Bit RC2 CBC"</userinput>)</para></listitem>
+ </itemizedlist>
</listitem>
</varlistentry>
</variablelist>
- <para>With PKCS#12, the crypto provider may be the soft token module or an external hardware module. If the cryptographic module does not support the requested algorithm, then the next best fit will be selected (usually the default). If no suitable replacement for the desired algorithm can be found, the tool returns the error <emphasis>no security module can perform the requested operation</emphasis>.</para>
+ <para>With PKCS #12, the crypto provider may be the soft token module or an external hardware module. If the cryptographic module does not support the requested algorithm, then the next best fit will be selected (usually the default). If no suitable replacement for the desired algorithm can be found, the tool returns the error <emphasis>no security module can perform the requested operation</emphasis>.</para>
</refsection>
<refsection id="databases"><title>NSS Database Types</title>
@@ -432,6 +408,14 @@ Using the SQLite databases must be manually specified by using the <command>sql:
</itemizedlist>
</refsection>
+ <refsection id="compatibility">
+ <title>Compatibility Notes</title>
+ <para>The exporting behavior of <command>pk12util</command> has changed over time, while importing files exported with older versions of NSS is still supported.</para>
+ <para>Until the 3.30 release, <command>pk12util</command> used the UTF-16 encoding for the PKCS #5 password-based encryption schemes, while the recommendation is to encode passwords in UTF-8 if the used encryption scheme is defined outside of the PKCS #12 standard.</para>
+ <para>Until the 3.31 release, even when <userinput>"AES-128-CBC"</userinput> or <userinput>"AES-192-CBC"</userinput> is given from the command line, <command>pk12util</command> always used 256-bit AES as the underlying encryption scheme.</para>
+ <para>For historical reasons, <command>pk12util</command> accepts password-based encryption schemes not listed in this document. However, those schemes are not officially supported and may have issues in interoperability with other tools.</para>
+ </refsection>
+
<refsection id="seealso">
<title>See Also</title>
<para>certutil (1)</para>
diff --git a/security/nss/fuzz/config/clone_libfuzzer.sh b/security/nss/fuzz/config/clone_libfuzzer.sh
index f1dc2e14b..c516057d7 100644
--- a/security/nss/fuzz/config/clone_libfuzzer.sh
+++ b/security/nss/fuzz/config/clone_libfuzzer.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-LIBFUZZER_REVISION=56bd1d43451cca4b6a11d3be316bb77ab159b09d
+LIBFUZZER_REVISION=6937e68f927b6aefe526fcb9db8953f497e6e74d
d=$(dirname $0)
$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer $LIBFUZZER_REVISION $d/../libFuzzer
diff --git a/security/nss/fuzz/config/git-copy.sh b/security/nss/fuzz/config/git-copy.sh
index a5c7d371d..a9e817e2a 100644
--- a/security/nss/fuzz/config/git-copy.sh
+++ b/security/nss/fuzz/config/git-copy.sh
@@ -7,18 +7,18 @@ if [ $# -lt 3 ]; then
exit 2
fi
-REPO=$1
-COMMIT=$2
-DIR=$3
+REPO="$1"
+COMMIT="$2"
+DIR="$3"
echo "Copy '$COMMIT' from '$REPO' to '$DIR'"
-if [ -f $DIR/.git-copy ]; then
- CURRENT=$(cat $DIR/.git-copy)
- if [ $(echo -n $COMMIT | wc -c) != "40" ]; then
+if [ -f "$DIR"/.git-copy ]; then
+ CURRENT=$(cat "$DIR"/.git-copy)
+ if [ $(echo -n "$COMMIT" | wc -c) != "40" ]; then
# On the off chance that $COMMIT is a remote head.
- ACTUAL=$(git ls-remote $REPO $COMMIT | cut -c 1-40 -)
+ ACTUAL=$(git ls-remote "$REPO" "$COMMIT" | cut -c 1-40 -)
else
- ACTUAL=$COMMIT
+ ACTUAL="$COMMIT"
fi
if [ "$CURRENT" = "$ACTUAL" ]; then
echo "Up to date."
@@ -26,8 +26,9 @@ if [ -f $DIR/.git-copy ]; then
fi
fi
-git init -q $DIR
-git -C $DIR fetch -q --depth=1 $REPO $COMMIT:git-copy-tmp
-git -C $DIR reset --hard git-copy-tmp
-git -C $DIR rev-parse --verify HEAD > $DIR/.git-copy
-rm -rf $DIR/.git
+rm -rf "$DIR"
+git init -q "$DIR"
+git -C "$DIR" fetch -q --depth=1 "$REPO" "$COMMIT":git-copy-tmp
+git -C "$DIR" reset --hard git-copy-tmp
+git -C "$DIR" rev-parse --verify HEAD > "$DIR"/.git-copy
+rm -rf "$DIR"/.git
diff --git a/security/nss/fuzz/mpi_expmod_target.cc b/security/nss/fuzz/mpi_expmod_target.cc
index ed31da354..b9be5854f 100644
--- a/security/nss/fuzz/mpi_expmod_target.cc
+++ b/security/nss/fuzz/mpi_expmod_target.cc
@@ -19,6 +19,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
auto modulus = get_modulus(data, size, ctx);
// Compare with OpenSSL exp mod
m1 = &std::get<1>(modulus);
+ // The exponent b (B) can get really big. Make it smaller if necessary.
+ if (MP_USED(&b) > 100) {
+ size_t shift = (MP_USED(&b) - 100) * MP_DIGIT_BIT;
+ mp_div_2d(&b, shift, &b, nullptr);
+ BN_rshift(B, B, shift);
+ }
+ check_equal(A, &a, max_size);
+ check_equal(B, &b, max_size);
+ check_equal(std::get<0>(modulus), m1, 3 * max_size);
assert(mp_exptmod(&a, &b, m1, &c) == MP_OKAY);
(void)BN_mod_exp(C, A, B, std::get<0>(modulus), ctx);
check_equal(C, &c, 2 * max_size);
diff --git a/security/nss/fuzz/mpi_helper.cc b/security/nss/fuzz/mpi_helper.cc
index 65cf4b9cd..d092fdb11 100644
--- a/security/nss/fuzz/mpi_helper.cc
+++ b/security/nss/fuzz/mpi_helper.cc
@@ -12,6 +12,12 @@ char *to_char(const uint8_t *x) {
return reinterpret_cast<char *>(const_cast<unsigned char *>(x));
}
+void print_bn(std::string label, BIGNUM *x) {
+ char *xc = BN_bn2hex(x);
+ std::cout << label << ": " << std::hex << xc << std::endl;
+ OPENSSL_free(xc);
+}
+
// Check that the two numbers are equal.
void check_equal(BIGNUM *b, mp_int *m, size_t max_size) {
char *bnBc = BN_bn2hex(b);
diff --git a/security/nss/fuzz/mpi_helper.h b/security/nss/fuzz/mpi_helper.h
index 17383744b..ef7041b25 100644
--- a/security/nss/fuzz/mpi_helper.h
+++ b/security/nss/fuzz/mpi_helper.h
@@ -23,6 +23,7 @@ void parse_input(const uint8_t *data, size_t size, BIGNUM *A, BIGNUM *B,
void parse_input(const uint8_t *data, size_t size, BIGNUM *A, mp_int *a);
std::tuple<BIGNUM *, mp_int> get_modulus(const uint8_t *data, size_t size,
BN_CTX *ctx);
+void print_bn(std::string label, BIGNUM *x);
// Initialise MPI and BN variables
// XXX: Also silence unused variable warnings for R.
diff --git a/security/nss/fuzz/tls_mutators.cc b/security/nss/fuzz/tls_mutators.cc
index e9770cb39..228bd0bb7 100644
--- a/security/nss/fuzz/tls_mutators.cc
+++ b/security/nss/fuzz/tls_mutators.cc
@@ -2,11 +2,14 @@
* License, v. 2.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 <algorithm>
#include "shared.h"
#include "tls_parser.h"
#include "ssl.h"
+extern "C" {
#include "sslimpl.h"
+}
using namespace nss_test;
@@ -39,7 +42,9 @@ class Record {
void truncate(size_t length) {
assert(length >= 5 + gExtraHeaderBytes);
uint8_t *dest = const_cast<uint8_t *>(data_);
- (void)ssl_EncodeUintX(length - 5 - gExtraHeaderBytes, 2, &dest[3]);
+ size_t l = length - (5 + gExtraHeaderBytes);
+ dest[3] = (l >> 8) & 0xff;
+ dest[4] = l & 0xff;
memmove(dest + length, data_ + size_, remaining_);
}
@@ -222,8 +227,8 @@ size_t FragmentRecord(uint8_t *data, size_t size, size_t max_size,
}
// Pick a record to fragment at random.
- std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
- auto &rec = records.at(dist(rng));
+ std::uniform_int_distribution<size_t> rand_record(0, records.size() - 1);
+ auto &rec = records.at(rand_record(rng));
uint8_t *rdata = const_cast<uint8_t *>(rec->data());
size_t length = rec->size();
size_t content_length = length - 5;
@@ -233,17 +238,21 @@ size_t FragmentRecord(uint8_t *data, size_t size, size_t max_size,
}
// Assign a new length to the first fragment.
- size_t new_length = content_length / 2;
- uint8_t *content = ssl_EncodeUintX(new_length, 2, &rdata[3]);
+ std::uniform_int_distribution<size_t> rand_size(1, content_length - 1);
+ size_t first_length = rand_size(rng);
+ size_t second_length = content_length - first_length;
+ rdata[3] = (first_length >> 8) & 0xff;
+ rdata[4] = first_length & 0xff;
+ uint8_t *second_record = rdata + 5 + first_length;
- // Make room for one more header.
- memmove(content + new_length + 5, content + new_length,
- rec->remaining() + content_length - new_length);
+ // Make room for the header of the second record.
+ memmove(second_record + 5, second_record,
+ rec->remaining() + content_length - first_length);
// Write second header.
- memcpy(content + new_length, rdata, 3);
- (void)ssl_EncodeUintX(content_length - new_length, 2,
- &content[new_length + 3]);
+ memcpy(second_record, rdata, 3);
+ second_record[3] = (second_length >> 8) & 0xff;
+ second_record[4] = second_length & 0xff;
return size + 5;
}
diff --git a/security/nss/fuzz/tls_socket.h b/security/nss/fuzz/tls_socket.h
index 61fa4b3a8..e30f6fa3c 100644
--- a/security/nss/fuzz/tls_socket.h
+++ b/security/nss/fuzz/tls_socket.h
@@ -10,6 +10,7 @@
class DummyPrSocket : public DummyIOLayerMethods {
public:
DummyPrSocket(const uint8_t *buf, size_t len) : buf_(buf), len_(len) {}
+ virtual ~DummyPrSocket() {}
int32_t Read(PRFileDesc *f, void *data, int32_t len) override;
int32_t Write(PRFileDesc *f, const void *buf, int32_t length) override;
diff --git a/security/nss/gtests/certdb_gtest/alg1485_unittest.cc b/security/nss/gtests/certdb_gtest/alg1485_unittest.cc
index b7c659414..ef6733092 100644
--- a/security/nss/gtests/certdb_gtest/alg1485_unittest.cc
+++ b/security/nss/gtests/certdb_gtest/alg1485_unittest.cc
@@ -10,6 +10,7 @@
#include "nss.h"
#include "scoped_ptrs.h"
+#include "prprf.h"
namespace nss_test {
@@ -89,4 +90,23 @@ INSTANTIATE_TEST_CASE_P(ParseAVAStrings, Alg1485ParseTest,
::testing::ValuesIn(kAVATestStrings));
INSTANTIATE_TEST_CASE_P(CompareAVAStrings, Alg1485CompareTest,
::testing::ValuesIn(kAVACompareStrings));
+
+TEST_F(Alg1485Test, ShortOIDTest) {
+ // This is not a valid OID (too short). CERT_GetOidString should return 0.
+ unsigned char data[] = {0x05};
+ const SECItem oid = {siBuffer, data, sizeof(data)};
+ char* result = CERT_GetOidString(&oid);
+ EXPECT_EQ(result, nullptr);
+}
+
+TEST_F(Alg1485Test, BrokenOIDTest) {
+ // This is not a valid OID (first bit of last byte is not set).
+ // CERT_GetOidString should return 0.
+ unsigned char data[] = {0x81, 0x82, 0x83, 0x84};
+ const SECItem oid = {siBuffer, data, sizeof(data)};
+ char* result = CERT_GetOidString(&oid);
+ EXPECT_EQ(15U, strlen(result));
+ EXPECT_EQ(0, strncmp("OID.UNSUPPORTED", result, 15));
+ PR_smprintf_free(result);
+}
}
diff --git a/security/nss/gtests/common/util.h b/security/nss/gtests/common/util.h
index ccab5604e..7ed1fd799 100644
--- a/security/nss/gtests/common/util.h
+++ b/security/nss/gtests/common/util.h
@@ -10,7 +10,7 @@
#include <cassert>
#include <vector>
-std::vector<uint8_t> hex_string_to_bytes(std::string s) {
+static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
std::vector<uint8_t> bytes;
for (size_t i = 0; i < s.length(); i += 2) {
bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16));
diff --git a/security/nss/gtests/cryptohi_gtest/Makefile b/security/nss/gtests/cryptohi_gtest/Makefile
new file mode 100644
index 000000000..0d547e080
--- /dev/null
+++ b/security/nss/gtests/cryptohi_gtest/Makefile
@@ -0,0 +1,43 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include ../common/gtest.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
diff --git a/security/nss/gtests/cryptohi_gtest/cryptohi_gtest.gyp b/security/nss/gtests/cryptohi_gtest/cryptohi_gtest.gyp
new file mode 100644
index 000000000..72c815eca
--- /dev/null
+++ b/security/nss/gtests/cryptohi_gtest/cryptohi_gtest.gyp
@@ -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/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi',
+ '../common/gtest.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'cryptohi_gtest',
+ 'type': 'executable',
+ 'sources': [
+ 'cryptohi_unittest.cc',
+ '<(DEPTH)/gtests/common/gtests.cc'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports',
+ '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
+ '<(DEPTH)/lib/util/util.gyp:nssutil3',
+ '<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
+ '<(DEPTH)/lib/nss/nss.gyp:nss3',
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/gtests/cryptohi_gtest/cryptohi_unittest.cc b/security/nss/gtests/cryptohi_gtest/cryptohi_unittest.cc
new file mode 100644
index 000000000..ab553ee01
--- /dev/null
+++ b/security/nss/gtests/cryptohi_gtest/cryptohi_unittest.cc
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "scoped_ptrs.h"
+#include "cryptohi.h"
+#include "secitem.h"
+#include "secerr.h"
+
+namespace nss_test {
+
+class SignParamsTestF : public ::testing::Test {
+ protected:
+ ScopedPLArenaPool arena_;
+ ScopedSECKEYPrivateKey privk_;
+ ScopedSECKEYPublicKey pubk_;
+ ScopedSECKEYPrivateKey ecPrivk_;
+ ScopedSECKEYPublicKey ecPubk_;
+
+ void SetUp() {
+ arena_.reset(PORT_NewArena(2048));
+
+ SECKEYPublicKey *pubk;
+ SECKEYPrivateKey *privk = SECKEY_CreateRSAPrivateKey(1024, &pubk, NULL);
+ ASSERT_NE(nullptr, pubk);
+ pubk_.reset(pubk);
+ ASSERT_NE(nullptr, privk);
+ privk_.reset(privk);
+
+ SECKEYECParams ecParams = {siBuffer, NULL, 0};
+ SECOidData *oidData;
+ oidData = SECOID_FindOIDByTag(SEC_OID_CURVE25519);
+ ASSERT_NE(nullptr, oidData);
+ ASSERT_NE(nullptr,
+ SECITEM_AllocItem(NULL, &ecParams, (2 + oidData->oid.len)))
+ << "Couldn't allocate memory for OID.";
+ ecParams.data[0] = SEC_ASN1_OBJECT_ID; /* we have to prepend 0x06 */
+ ecParams.data[1] = oidData->oid.len;
+ memcpy(ecParams.data + 2, oidData->oid.data, oidData->oid.len);
+ SECKEYPublicKey *ecPubk;
+ SECKEYPrivateKey *ecPrivk =
+ SECKEY_CreateECPrivateKey(&ecParams, &ecPubk, NULL);
+ ASSERT_NE(nullptr, ecPubk);
+ ecPubk_.reset(ecPubk);
+ ASSERT_NE(nullptr, ecPrivk);
+ ecPrivk_.reset(ecPrivk);
+ }
+
+ void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) {
+ PORT_Memset(params, 0, sizeof(SECKEYRSAPSSParams));
+
+ params->hashAlg = (SECAlgorithmID *)PORT_ArenaZAlloc(
+ arena_.get(), sizeof(SECAlgorithmID));
+ ASSERT_NE(nullptr, params->hashAlg);
+ SECStatus rv =
+ SECOID_SetAlgorithmID(arena_.get(), params->hashAlg, hashAlgTag, NULL);
+ ASSERT_EQ(SECSuccess, rv);
+ }
+
+ void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag,
+ SECOidTag maskHashAlgTag) {
+ CreatePssParams(params, hashAlgTag);
+
+ SECAlgorithmID maskHashAlg;
+ PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg));
+ SECStatus rv =
+ SECOID_SetAlgorithmID(arena_.get(), &maskHashAlg, maskHashAlgTag, NULL);
+ ASSERT_EQ(SECSuccess, rv);
+
+ SECItem *maskHashAlgItem =
+ SEC_ASN1EncodeItem(arena_.get(), NULL, &maskHashAlg,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
+
+ params->maskAlg = (SECAlgorithmID *)PORT_ArenaZAlloc(
+ arena_.get(), sizeof(SECAlgorithmID));
+ ASSERT_NE(nullptr, params->maskAlg);
+
+ rv = SECOID_SetAlgorithmID(arena_.get(), params->maskAlg,
+ SEC_OID_PKCS1_MGF1, maskHashAlgItem);
+ ASSERT_EQ(SECSuccess, rv);
+ }
+
+ void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag,
+ SECOidTag maskHashAlgTag, unsigned long saltLength) {
+ CreatePssParams(params, hashAlgTag, maskHashAlgTag);
+
+ SECItem *saltLengthItem =
+ SEC_ASN1EncodeInteger(arena_.get(), &params->saltLength, saltLength);
+ ASSERT_EQ(&params->saltLength, saltLengthItem);
+ }
+
+ void CheckHashAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) {
+ // If hash algorithm is SHA-1, it must be omitted in the parameters
+ if (hashAlgTag == SEC_OID_SHA1) {
+ EXPECT_EQ(nullptr, params->hashAlg);
+ } else {
+ EXPECT_NE(nullptr, params->hashAlg);
+ EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(params->hashAlg));
+ }
+ }
+
+ void CheckMaskAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) {
+ SECStatus rv;
+
+ // If hash algorithm is SHA-1, it must be omitted in the parameters
+ if (hashAlgTag == SEC_OID_SHA1)
+ EXPECT_EQ(nullptr, params->hashAlg);
+ else {
+ EXPECT_NE(nullptr, params->maskAlg);
+ EXPECT_EQ(SEC_OID_PKCS1_MGF1, SECOID_GetAlgorithmTag(params->maskAlg));
+
+ SECAlgorithmID hashAlg;
+ rv = SEC_QuickDERDecodeItem(arena_.get(), &hashAlg,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+ &params->maskAlg->parameters);
+ ASSERT_EQ(SECSuccess, rv);
+
+ EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(&hashAlg));
+ }
+ }
+
+ void CheckSaltLength(SECKEYRSAPSSParams *params, SECOidTag hashAlg) {
+ // If the salt length parameter is missing, that means it is 20 (default)
+ if (!params->saltLength.data) {
+ return;
+ }
+
+ unsigned long value;
+ SECStatus rv = SEC_ASN1DecodeInteger(&params->saltLength, &value);
+ ASSERT_EQ(SECSuccess, rv);
+
+ // The salt length are usually the same as the hash length,
+ // except for the case where the hash length exceeds the limit
+ // set by the key length
+ switch (hashAlg) {
+ case SEC_OID_SHA1:
+ EXPECT_EQ(20UL, value);
+ break;
+ case SEC_OID_SHA224:
+ EXPECT_EQ(28UL, value);
+ break;
+ case SEC_OID_SHA256:
+ EXPECT_EQ(32UL, value);
+ break;
+ case SEC_OID_SHA384:
+ EXPECT_EQ(48UL, value);
+ break;
+ case SEC_OID_SHA512:
+ // Truncated from 64, because our private key is 1024-bit
+ EXPECT_EQ(62UL, value);
+ break;
+ default:
+ FAIL();
+ }
+ }
+};
+
+class SignParamsTest
+ : public SignParamsTestF,
+ public ::testing::WithParamInterface<std::tuple<SECOidTag, SECOidTag>> {};
+
+class SignParamsSourceTest : public SignParamsTestF,
+ public ::testing::WithParamInterface<SECOidTag> {};
+
+TEST_P(SignParamsTest, CreateRsa) {
+ SECOidTag hashAlg = std::get<0>(GetParam());
+ SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+ SECItem *srcParams;
+ if (srcHashAlg != SEC_OID_UNKNOWN) {
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+ } else {
+ srcParams = NULL;
+ }
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_ENCRYPTION, hashAlg, srcParams,
+ privk_.get());
+
+ // PKCS#1 RSA actually doesn't take any parameters, but if it is
+ // given, return a copy of it
+ if (srcHashAlg != SEC_OID_UNKNOWN) {
+ EXPECT_EQ(srcParams->len, params->len);
+ EXPECT_EQ(0, memcmp(params->data, srcParams->data, srcParams->len));
+ } else {
+ EXPECT_EQ(nullptr, params);
+ }
+}
+
+TEST_P(SignParamsTest, CreateRsaPss) {
+ SECOidTag hashAlg = std::get<0>(GetParam());
+ SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+ SECItem *srcParams;
+ if (srcHashAlg != SEC_OID_UNKNOWN) {
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+ } else {
+ srcParams = NULL;
+ }
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+ srcParams, privk_.get());
+
+ if (hashAlg != SEC_OID_UNKNOWN && srcHashAlg != SEC_OID_UNKNOWN &&
+ hashAlg != srcHashAlg) {
+ EXPECT_EQ(nullptr, params);
+ return;
+ }
+
+ EXPECT_NE(nullptr, params);
+
+ SECKEYRSAPSSParams pssParams;
+ PORT_Memset(&pssParams, 0, sizeof(pssParams));
+ SECStatus rv =
+ SEC_QuickDERDecodeItem(arena_.get(), &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), params);
+ ASSERT_EQ(SECSuccess, rv);
+
+ if (hashAlg == SEC_OID_UNKNOWN) {
+ if (!pssParams.hashAlg) {
+ hashAlg = SEC_OID_SHA1;
+ } else {
+ hashAlg = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+ }
+
+ if (srcHashAlg == SEC_OID_UNKNOWN) {
+ // If both hashAlg and srcHashAlg is unset, NSS will decide the hash
+ // algorithm based on the key length; in this case it's SHA256
+ EXPECT_EQ(SEC_OID_SHA256, hashAlg);
+ } else {
+ EXPECT_EQ(srcHashAlg, hashAlg);
+ }
+ }
+
+ ASSERT_NO_FATAL_FAILURE(CheckHashAlg(&pssParams, hashAlg));
+ ASSERT_NO_FATAL_FAILURE(CheckMaskAlg(&pssParams, hashAlg));
+ ASSERT_NO_FATAL_FAILURE(CheckSaltLength(&pssParams, hashAlg));
+
+ // The default trailer field (1) must be omitted
+ EXPECT_EQ(nullptr, pssParams.trailerField.data);
+}
+
+TEST_P(SignParamsTest, CreateRsaPssWithECPrivateKey) {
+ SECOidTag hashAlg = std::get<0>(GetParam());
+ SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+ SECItem *srcParams;
+ if (srcHashAlg != SEC_OID_UNKNOWN) {
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+ } else {
+ srcParams = NULL;
+ }
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+ srcParams, ecPrivk_.get());
+
+ EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsTest, CreateRsaPssWithInvalidHashAlg) {
+ SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+ SECItem *srcParams;
+ if (srcHashAlg != SEC_OID_UNKNOWN) {
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+ } else {
+ srcParams = NULL;
+ }
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, SEC_OID_MD5,
+ srcParams, privk_.get());
+
+ EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidHashAlg) {
+ SECOidTag hashAlg = GetParam();
+
+ SECItem *srcParams;
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, SEC_OID_MD5, SEC_OID_MD5));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+ srcParams, privk_.get());
+
+ EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidSaltLength) {
+ SECOidTag hashAlg = GetParam();
+
+ SECItem *srcParams;
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, SEC_OID_SHA512, SEC_OID_SHA512, 100));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+ srcParams, privk_.get());
+
+ EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsSourceTest, CreateRsaPssWithHashMismatch) {
+ SECOidTag hashAlg = GetParam();
+
+ SECItem *srcParams;
+ SECKEYRSAPSSParams pssParams;
+ ASSERT_NO_FATAL_FAILURE(
+ CreatePssParams(&pssParams, SEC_OID_SHA256, SEC_OID_SHA512));
+ srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+ SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+ ASSERT_NE(nullptr, srcParams);
+
+ SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+ arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+ srcParams, privk_.get());
+
+ EXPECT_EQ(nullptr, params);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SignParamsTestCases, SignParamsTest,
+ ::testing::Combine(::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1,
+ SEC_OID_SHA224, SEC_OID_SHA256,
+ SEC_OID_SHA384, SEC_OID_SHA512),
+ ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1,
+ SEC_OID_SHA224, SEC_OID_SHA256,
+ SEC_OID_SHA384, SEC_OID_SHA512)));
+
+INSTANTIATE_TEST_CASE_P(SignParamsSourceTestCases, SignParamsSourceTest,
+ ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1,
+ SEC_OID_SHA224, SEC_OID_SHA256,
+ SEC_OID_SHA384, SEC_OID_SHA512));
+
+} // namespace nss_test
diff --git a/security/nss/gtests/cryptohi_gtest/manifest.mn b/security/nss/gtests/cryptohi_gtest/manifest.mn
new file mode 100644
index 000000000..644463aa6
--- /dev/null
+++ b/security/nss/gtests/cryptohi_gtest/manifest.mn
@@ -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/.
+CORE_DEPTH = ../..
+DEPTH = ../..
+MODULE = nss
+
+CPPSRCS = \
+ cryptohi_unittest.cc \
+ $(NULL)
+
+INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
+ -I$(CORE_DEPTH)/gtests/common \
+ -I$(CORE_DEPTH)/cpputil
+
+REQUIRES = nspr gtest
+
+PROGRAM = cryptohi_gtest
+
+EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) $(EXTRA_OBJS) \
+ $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX)
diff --git a/security/nss/gtests/freebl_gtest/blake2b_unittest.cc b/security/nss/gtests/freebl_gtest/blake2b_unittest.cc
new file mode 100644
index 000000000..e6b0c1157
--- /dev/null
+++ b/security/nss/gtests/freebl_gtest/blake2b_unittest.cc
@@ -0,0 +1,277 @@
+/*
+ * blake2b_unittest.cc - unittests for blake2b hash function
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "blapi.h"
+#include "nspr.h"
+#include "nss.h"
+#include "secerr.h"
+
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+#include "kat/blake2b_kat.h"
+
+template <class T>
+struct ScopedDelete {
+ void operator()(T* ptr) {
+ if (ptr) {
+ BLAKE2B_DestroyContext(ptr, PR_TRUE);
+ }
+ }
+};
+
+typedef std::unique_ptr<BLAKE2BContext, ScopedDelete<BLAKE2BContext>>
+ ScopedBLAKE2BContext;
+
+class Blake2BTests : public ::testing::Test {};
+
+class Blake2BKAT
+ : public ::testing::TestWithParam<std::pair<int, std::vector<uint8_t>>> {};
+
+class Blake2BKATUnkeyed : public Blake2BKAT {};
+class Blake2BKATKeyed : public Blake2BKAT {};
+
+TEST_P(Blake2BKATUnkeyed, Unkeyed) {
+ std::vector<uint8_t> values(BLAKE2B512_LENGTH);
+ SECStatus rv =
+ BLAKE2B_HashBuf(values.data(), kat_data.data(), std::get<0>(GetParam()));
+ ASSERT_EQ(SECSuccess, rv);
+ EXPECT_EQ(values, std::get<1>(GetParam()));
+}
+
+TEST_P(Blake2BKATKeyed, Keyed) {
+ std::vector<uint8_t> values(BLAKE2B512_LENGTH);
+ SECStatus rv = BLAKE2B_MAC_HashBuf(values.data(), kat_data.data(),
+ std::get<0>(GetParam()), key.data(),
+ BLAKE2B_KEY_SIZE);
+ ASSERT_EQ(SECSuccess, rv);
+ EXPECT_EQ(values, std::get<1>(GetParam()));
+}
+
+INSTANTIATE_TEST_CASE_P(UnkeyedKAT, Blake2BKATUnkeyed,
+ ::testing::ValuesIn(TestcasesUnkeyed));
+INSTANTIATE_TEST_CASE_P(KeyedKAT, Blake2BKATKeyed,
+ ::testing::ValuesIn(TestcasesKeyed));
+
+TEST_F(Blake2BTests, ContextTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ SECStatus rv = BLAKE2B_Begin(ctx.get());
+ ASSERT_EQ(SECSuccess, rv);
+
+ size_t src_length = 252;
+ const size_t quarter = 63;
+
+ for (int i = 0; i < 4 && src_length > 0; i++) {
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data() + i * quarter,
+ PR_MIN(quarter, src_length));
+ ASSERT_EQ(SECSuccess, rv);
+
+ size_t len = BLAKE2B_FlattenSize(ctx.get());
+ std::vector<unsigned char> ctxbytes(len);
+ rv = BLAKE2B_Flatten(ctx.get(), ctxbytes.data());
+ ASSERT_EQ(SECSuccess, rv);
+ ScopedBLAKE2BContext ctx_cpy(BLAKE2B_Resurrect(ctxbytes.data(), NULL));
+ ASSERT_TRUE(ctx_cpy) << "BLAKE2B_Resurrect failed!";
+ ASSERT_EQ(SECSuccess, PORT_Memcmp(ctx.get(), ctx_cpy.get(), len));
+ src_length -= quarter;
+ }
+ ASSERT_EQ(0U, src_length);
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ rv = BLAKE2B_End(ctx.get(), digest.data(), nullptr, BLAKE2B512_LENGTH);
+ ASSERT_EQ(SECSuccess, rv);
+ ASSERT_EQ(std::get<1>(TestcasesUnkeyed[252]), digest)
+ << "BLAKE2B_End failed!";
+}
+
+TEST_F(Blake2BTests, ContextTest2) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ SECStatus rv = BLAKE2B_Begin(ctx.get());
+ ASSERT_EQ(SECSuccess, rv);
+
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data(), 128);
+ ASSERT_EQ(SECSuccess, rv);
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data() + 128, 127);
+ ASSERT_EQ(SECSuccess, rv);
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ rv = BLAKE2B_End(ctx.get(), digest.data(), nullptr, BLAKE2B512_LENGTH);
+ ASSERT_EQ(SECSuccess, rv);
+ ASSERT_EQ(std::get<1>(TestcasesUnkeyed[255]), digest)
+ << "BLAKE2B_End failed!";
+}
+
+TEST_F(Blake2BTests, CloneTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ScopedBLAKE2BContext cloned_ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+ ASSERT_TRUE(cloned_ctx) << "BLAKE2B_NewContext failed!";
+
+ SECStatus rv = BLAKE2B_Begin(ctx.get());
+ ASSERT_EQ(SECSuccess, rv);
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data(), 255);
+ ASSERT_EQ(SECSuccess, rv);
+ BLAKE2B_Clone(cloned_ctx.get(), ctx.get());
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ rv = BLAKE2B_End(cloned_ctx.get(), digest.data(), nullptr, BLAKE2B512_LENGTH);
+ ASSERT_EQ(SECSuccess, rv);
+ ASSERT_EQ(std::get<1>(TestcasesUnkeyed[255]), digest)
+ << "BLAKE2B_End failed!";
+}
+
+TEST_F(Blake2BTests, NullTest) {
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ SECStatus rv = BLAKE2B_HashBuf(digest.data(), nullptr, 0);
+ ASSERT_EQ(SECSuccess, rv);
+ EXPECT_EQ(std::get<1>(TestcasesUnkeyed[0]), digest);
+
+ digest = std::vector<uint8_t>(BLAKE2B512_LENGTH);
+ rv = BLAKE2B_MAC_HashBuf(digest.data(), nullptr, 0, key.data(),
+ BLAKE2B_KEY_SIZE);
+ ASSERT_EQ(SECSuccess, rv);
+ EXPECT_EQ(std::get<1>(TestcasesKeyed[0]), digest);
+}
+
+TEST_F(Blake2BTests, HashTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ SECStatus rv = BLAKE2B_Hash(digest.data(), "abc");
+ std::vector<uint8_t> expected = {
+ 0xba, 0x80, 0xa5, 0x3f, 0x98, 0x1c, 0x4d, 0x0d, 0x6a, 0x27, 0x97,
+ 0xb6, 0x9f, 0x12, 0xf6, 0xe9, 0x4c, 0x21, 0x2f, 0x14, 0x68, 0x5a,
+ 0xc4, 0xb7, 0x4b, 0x12, 0xbb, 0x6f, 0xdb, 0xff, 0xa2, 0xd1, 0x7d,
+ 0x87, 0xc5, 0x39, 0x2a, 0xab, 0x79, 0x2d, 0xc2, 0x52, 0xd5, 0xde,
+ 0x45, 0x33, 0xcc, 0x95, 0x18, 0xd3, 0x8a, 0xa8, 0xdb, 0xf1, 0x92,
+ 0x5a, 0xb9, 0x23, 0x86, 0xed, 0xd4, 0x00, 0x99, 0x23};
+ ASSERT_EQ(SECSuccess, rv);
+ EXPECT_EQ(expected, digest);
+}
+
+TEST_F(Blake2BTests, LongHashTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ SECStatus rv = BLAKE2B_Hash(
+ digest.data(),
+ "qwertzuiopasdfghjklyxcvbnm123456789qwertzuiopasdfghjklyxcvbnm123456789qw"
+ "ertzuiopasdfghjklyxcvbnm123456789qwertzuiopasdfghjklyxcvbnm123456789qwer"
+ "tzuiopasdfghjklyxcvbnm123456789qwertzuiopasdfghjklyxcvbnm123456789qwertz"
+ "uiopasdfghjklyxcvbnm123456789qwertzuiopasdfghjklyxcvbnm123456789");
+ std::vector<uint8_t> expected = {
+ 0x1f, 0x9e, 0xe6, 0x5a, 0xa0, 0x36, 0x05, 0xfc, 0x41, 0x0e, 0x2f,
+ 0x55, 0x96, 0xfd, 0xb5, 0x9d, 0x85, 0x95, 0x5e, 0x24, 0x37, 0xe7,
+ 0x0d, 0xe4, 0xa0, 0x22, 0x4a, 0xe1, 0x59, 0x1f, 0x97, 0x03, 0x57,
+ 0x54, 0xf0, 0xca, 0x92, 0x75, 0x2f, 0x9e, 0x86, 0xeb, 0x82, 0x4f,
+ 0x9c, 0xf4, 0x02, 0x17, 0x7f, 0x76, 0x56, 0x26, 0x46, 0xf4, 0x07,
+ 0xfd, 0x1f, 0x78, 0xdb, 0x7b, 0x0d, 0x24, 0x43, 0xf0};
+ ASSERT_EQ(SECSuccess, rv);
+ EXPECT_EQ(expected, digest);
+}
+
+TEST_F(Blake2BTests, TruncatedHashTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ SECStatus rv = BLAKE2B_Begin(ctx.get());
+ ASSERT_EQ(SECSuccess, rv);
+
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data(), 128);
+ ASSERT_EQ(SECSuccess, rv);
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data() + 128, 127);
+ ASSERT_EQ(SECSuccess, rv);
+
+ size_t max_digest_len = BLAKE2B512_LENGTH - 5;
+ std::vector<uint8_t> digest(max_digest_len);
+ unsigned int digest_len;
+ rv = BLAKE2B_End(ctx.get(), digest.data(), &digest_len, max_digest_len);
+ ASSERT_EQ(SECSuccess, rv);
+ ASSERT_EQ(digest.size(), digest_len);
+ ASSERT_EQ(0, memcmp(std::get<1>(TestcasesUnkeyed[255]).data(), digest.data(),
+ max_digest_len))
+ << "BLAKE2B_End failed!";
+}
+
+TEST_F(Blake2BTests, TruncatedHashTest2) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ SECStatus rv = BLAKE2B_Begin(ctx.get());
+ ASSERT_EQ(SECSuccess, rv);
+
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data(), 128);
+ ASSERT_EQ(SECSuccess, rv);
+ rv = BLAKE2B_Update(ctx.get(), kat_data.data() + 128, 127);
+ ASSERT_EQ(SECSuccess, rv);
+
+ size_t max_digest_len = BLAKE2B512_LENGTH - 60;
+ std::vector<uint8_t> digest(max_digest_len);
+ unsigned int digest_len;
+ rv = BLAKE2B_End(ctx.get(), digest.data(), &digest_len, max_digest_len);
+ ASSERT_EQ(SECSuccess, rv);
+ ASSERT_EQ(digest.size(), digest_len);
+}
+
+TEST_F(Blake2BTests, OverlongKeyTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ std::vector<uint8_t> key = {
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35};
+ std::vector<uint8_t> data = {0x61, 0x62, 0x63};
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ SECStatus rv =
+ BLAKE2B_MAC_HashBuf(digest.data(), data.data(), 3, key.data(), 65);
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(Blake2BTests, EmptyKeyTest) {
+ ScopedBLAKE2BContext ctx(BLAKE2B_NewContext());
+ ASSERT_TRUE(ctx) << "BLAKE2B_NewContext failed!";
+
+ uint8_t key[1]; // A vector.data() would give us a nullptr.
+ std::vector<uint8_t> data = {0x61, 0x62, 0x63};
+
+ std::vector<uint8_t> digest(BLAKE2B512_LENGTH);
+ SECStatus rv = BLAKE2B_MAC_HashBuf(digest.data(), data.data(), 3, key, 0);
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (NSS_NoDB_Init(nullptr) != SECSuccess) {
+ return 1;
+ }
+
+ int rv = RUN_ALL_TESTS();
+
+ if (NSS_Shutdown() != SECSuccess) {
+ return 1;
+ }
+
+ return rv;
+}
diff --git a/security/nss/gtests/freebl_gtest/freebl_gtest.gyp b/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
index 7e11cd103..21a87c557 100644
--- a/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
+++ b/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
@@ -8,17 +8,10 @@
],
'targets': [
{
- 'target_name': 'freebl_gtest',
- 'type': 'executable',
- 'sources': [
- 'mpi_unittest.cc',
- 'dh_unittest.cc',
- 'ecl_unittest.cc',
- 'ghash_unittest.cc',
- '<(DEPTH)/gtests/common/gtests.cc'
- ],
+ # Dependencies for tests.
+ 'target_name': 'freebl_gtest_deps',
+ 'type': 'none',
'dependencies': [
- '<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
'<(DEPTH)/lib/nss/nss.gyp:nss_static',
@@ -33,42 +26,57 @@
],
},
{
+ 'target_name': 'freebl_gtest',
+ 'type': 'executable',
+ 'sources': [
+ 'mpi_unittest.cc',
+ 'dh_unittest.cc',
+ 'ecl_unittest.cc',
+ 'ghash_unittest.cc',
+ 'rsa_unittest.cc',
+ '<(DEPTH)/gtests/common/gtests.cc'
+ ],
+ 'dependencies': [
+ 'freebl_gtest_deps',
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ],
+ },
+ {
'target_name': 'prng_gtest',
'type': 'executable',
'sources': [
'prng_kat_unittest.cc',
],
'dependencies': [
+ 'freebl_gtest_deps',
'<(DEPTH)/exports.gyp:nss_exports',
- '<(DEPTH)/lib/util/util.gyp:nssutil3',
- '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
- '<(DEPTH)/lib/nss/nss.gyp:nss_static',
- '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
- '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
- '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
- '<(DEPTH)/lib/certdb/certdb.gyp:certdb',
- '<(DEPTH)/lib/base/base.gyp:nssb',
- '<(DEPTH)/lib/dev/dev.gyp:nssdev',
- '<(DEPTH)/lib/pki/pki.gyp:nsspki',
- '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
- '<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix',
],
- 'conditions': [
- [ 'OS=="win"', {
- 'libraries': [
- 'advapi32.lib',
- ],
- }],
+ },
+ {
+ 'target_name': 'blake2b_gtest',
+ 'type': 'executable',
+ 'sources': [
+ 'blake2b_unittest.cc',
],
- 'defines': [
- 'NSS_USE_STATIC_LIBS'
+ 'dependencies': [
+ 'freebl_gtest_deps',
+ '<(DEPTH)/exports.gyp:nss_exports',
],
},
],
'target_defaults': {
'include_dirs': [
+ '<(DEPTH)/lib/freebl/ecl',
'<(DEPTH)/lib/freebl/mpi',
'<(DEPTH)/lib/freebl/',
+ '<(DEPTH)/lib/ssl/',
+ '<(DEPTH)/lib/util/',
+ '<(DEPTH)/lib/certdb/',
+ '<(DEPTH)/lib/cryptohi/',
+ '<(DEPTH)/lib/pk11wrap/',
+ ],
+ 'defines': [
+ 'NSS_USE_STATIC_LIBS',
],
# For test builds we have to set MPI defines.
'conditions': [
@@ -85,6 +93,11 @@
'MP_ASSEMBLY_DIV_2DX1D',
],
}],
+ [ 'OS=="win"', {
+ 'libraries': [
+ 'advapi32.lib',
+ ],
+ }],
],
},
'variables': {
diff --git a/security/nss/gtests/freebl_gtest/kat/blake2b_kat.h b/security/nss/gtests/freebl_gtest/kat/blake2b_kat.h
new file mode 100644
index 000000000..28921cc94
--- /dev/null
+++ b/security/nss/gtests/freebl_gtest/kat/blake2b_kat.h
@@ -0,0 +1,4646 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* https://github.com/BLAKE2/BLAKE2/blob/master/testvectors/blake2b-kat.txt */
+
+#include <vector>
+#include <stdint.h>
+
+const std::vector<uint8_t> key = {
+ 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, 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};
+
+const std::vector<uint8_t> kat_data = {
+ 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, 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};
+
+std::vector<std::pair<int, std::vector<uint8_t>>> TestcasesUnkeyed = {
+ std::make_pair(
+ 0,
+ std::vector<uint8_t>(
+ {0x78, 0x6a, 0x02, 0xf7, 0x42, 0x01, 0x59, 0x03, 0xc6, 0xc6, 0xfd,
+ 0x85, 0x25, 0x52, 0xd2, 0x72, 0x91, 0x2f, 0x47, 0x40, 0xe1, 0x58,
+ 0x47, 0x61, 0x8a, 0x86, 0xe2, 0x17, 0xf7, 0x1f, 0x54, 0x19, 0xd2,
+ 0x5e, 0x10, 0x31, 0xaf, 0xee, 0x58, 0x53, 0x13, 0x89, 0x64, 0x44,
+ 0x93, 0x4e, 0xb0, 0x4b, 0x90, 0x3a, 0x68, 0x5b, 0x14, 0x48, 0xb7,
+ 0x55, 0xd5, 0x6f, 0x70, 0x1a, 0xfe, 0x9b, 0xe2, 0xce})),
+ std::make_pair(
+ 1,
+ std::vector<uint8_t>(
+ {0x2f, 0xa3, 0xf6, 0x86, 0xdf, 0x87, 0x69, 0x95, 0x16, 0x7e, 0x7c,
+ 0x2e, 0x5d, 0x74, 0xc4, 0xc7, 0xb6, 0xe4, 0x8f, 0x80, 0x68, 0xfe,
+ 0x0e, 0x44, 0x20, 0x83, 0x44, 0xd4, 0x80, 0xf7, 0x90, 0x4c, 0x36,
+ 0x96, 0x3e, 0x44, 0x11, 0x5f, 0xe3, 0xeb, 0x2a, 0x3a, 0xc8, 0x69,
+ 0x4c, 0x28, 0xbc, 0xb4, 0xf5, 0xa0, 0xf3, 0x27, 0x6f, 0x2e, 0x79,
+ 0x48, 0x7d, 0x82, 0x19, 0x05, 0x7a, 0x50, 0x6e, 0x4b})),
+ std::make_pair(
+ 2,
+ std::vector<uint8_t>(
+ {0x1c, 0x08, 0x79, 0x8d, 0xc6, 0x41, 0xab, 0xa9, 0xde, 0xe4, 0x35,
+ 0xe2, 0x25, 0x19, 0xa4, 0x72, 0x9a, 0x09, 0xb2, 0xbf, 0xe0, 0xff,
+ 0x00, 0xef, 0x2d, 0xcd, 0x8e, 0xd6, 0xf8, 0xa0, 0x7d, 0x15, 0xea,
+ 0xf4, 0xae, 0xe5, 0x2b, 0xbf, 0x18, 0xab, 0x56, 0x08, 0xa6, 0x19,
+ 0x0f, 0x70, 0xb9, 0x04, 0x86, 0xc8, 0xa7, 0xd4, 0x87, 0x37, 0x10,
+ 0xb1, 0x11, 0x5d, 0x3d, 0xeb, 0xbb, 0x43, 0x27, 0xb5})),
+ std::make_pair(
+ 3,
+ std::vector<uint8_t>(
+ {0x40, 0xa3, 0x74, 0x72, 0x73, 0x02, 0xd9, 0xa4, 0x76, 0x9c, 0x17,
+ 0xb5, 0xf4, 0x09, 0xff, 0x32, 0xf5, 0x8a, 0xa2, 0x4f, 0xf1, 0x22,
+ 0xd7, 0x60, 0x3e, 0x4f, 0xda, 0x15, 0x09, 0xe9, 0x19, 0xd4, 0x10,
+ 0x7a, 0x52, 0xc5, 0x75, 0x70, 0xa6, 0xd9, 0x4e, 0x50, 0x96, 0x7a,
+ 0xea, 0x57, 0x3b, 0x11, 0xf8, 0x6f, 0x47, 0x3f, 0x53, 0x75, 0x65,
+ 0xc6, 0x6f, 0x70, 0x39, 0x83, 0x0a, 0x85, 0xd1, 0x86})),
+ std::make_pair(
+ 4,
+ std::vector<uint8_t>(
+ {0x77, 0xdd, 0xf4, 0xb1, 0x44, 0x25, 0xeb, 0x3d, 0x05, 0x3c, 0x1e,
+ 0x84, 0xe3, 0x46, 0x9d, 0x92, 0xc4, 0xcd, 0x91, 0x0e, 0xd2, 0x0f,
+ 0x92, 0x03, 0x5e, 0x0c, 0x99, 0xd8, 0xa7, 0xa8, 0x6c, 0xec, 0xaf,
+ 0x69, 0xf9, 0x66, 0x3c, 0x20, 0xa7, 0xaa, 0x23, 0x0b, 0xc8, 0x2f,
+ 0x60, 0xd2, 0x2f, 0xb4, 0xa0, 0x0b, 0x09, 0xd3, 0xeb, 0x8f, 0xc6,
+ 0x5e, 0xf5, 0x47, 0xfe, 0x63, 0xc8, 0xd3, 0xdd, 0xce})),
+ std::make_pair(
+ 5,
+ std::vector<uint8_t>(
+ {0xcb, 0xaa, 0x0b, 0xa7, 0xd4, 0x82, 0xb1, 0xf3, 0x01, 0x10, 0x9a,
+ 0xe4, 0x10, 0x51, 0x99, 0x1a, 0x32, 0x89, 0xbc, 0x11, 0x98, 0x00,
+ 0x5a, 0xf2, 0x26, 0xc5, 0xe4, 0xf1, 0x03, 0xb6, 0x65, 0x79, 0xf4,
+ 0x61, 0x36, 0x10, 0x44, 0xc8, 0xba, 0x34, 0x39, 0xff, 0x12, 0xc5,
+ 0x15, 0xfb, 0x29, 0xc5, 0x21, 0x61, 0xb7, 0xeb, 0x9c, 0x28, 0x37,
+ 0xb7, 0x6a, 0x5d, 0xc3, 0x3f, 0x7c, 0xb2, 0xe2, 0xe8})),
+ std::make_pair(
+ 6,
+ std::vector<uint8_t>(
+ {0xf9, 0x5d, 0x45, 0xcf, 0x69, 0xaf, 0x5c, 0x20, 0x23, 0xbd, 0xb5,
+ 0x05, 0x82, 0x1e, 0x62, 0xe8, 0x5d, 0x7c, 0xae, 0xdf, 0x7b, 0xed,
+ 0xa1, 0x2c, 0x02, 0x48, 0x77, 0x5b, 0x0c, 0x88, 0x20, 0x5e, 0xeb,
+ 0x35, 0xaf, 0x3a, 0x90, 0x81, 0x6f, 0x66, 0x08, 0xce, 0x7d, 0xd4,
+ 0x4e, 0xc2, 0x8d, 0xb1, 0x14, 0x06, 0x14, 0xe1, 0xdd, 0xeb, 0xf3,
+ 0xaa, 0x9c, 0xd1, 0x84, 0x3e, 0x0f, 0xad, 0x2c, 0x36})),
+ std::make_pair(
+ 7,
+ std::vector<uint8_t>(
+ {0x8f, 0x94, 0x5b, 0xa7, 0x00, 0xf2, 0x53, 0x0e, 0x5c, 0x2a, 0x7d,
+ 0xf7, 0xd5, 0xdc, 0xe0, 0xf8, 0x3f, 0x9e, 0xfc, 0x78, 0xc0, 0x73,
+ 0xfe, 0x71, 0xae, 0x1f, 0x88, 0x20, 0x4a, 0x4f, 0xd1, 0xcf, 0x70,
+ 0xa0, 0x73, 0xf5, 0xd1, 0xf9, 0x42, 0xed, 0x62, 0x3a, 0xa1, 0x6e,
+ 0x90, 0xa8, 0x71, 0x24, 0x6c, 0x90, 0xc4, 0x5b, 0x62, 0x1b, 0x34,
+ 0x01, 0xa5, 0xdd, 0xbd, 0x9d, 0xf6, 0x26, 0x41, 0x65})),
+ std::make_pair(
+ 8,
+ std::vector<uint8_t>(
+ {0xe9, 0x98, 0xe0, 0xdc, 0x03, 0xec, 0x30, 0xeb, 0x99, 0xbb, 0x6b,
+ 0xfa, 0xaf, 0x66, 0x18, 0xac, 0xc6, 0x20, 0x32, 0x0d, 0x72, 0x20,
+ 0xb3, 0xaf, 0x2b, 0x23, 0xd1, 0x12, 0xd8, 0xe9, 0xcb, 0x12, 0x62,
+ 0xf3, 0xc0, 0xd6, 0x0d, 0x18, 0x3b, 0x1e, 0xe7, 0xf0, 0x96, 0xd1,
+ 0x2d, 0xae, 0x42, 0xc9, 0x58, 0x41, 0x86, 0x00, 0x21, 0x4d, 0x04,
+ 0xf5, 0xed, 0x6f, 0x5e, 0x71, 0x8b, 0xe3, 0x55, 0x66})),
+ std::make_pair(
+ 9,
+ std::vector<uint8_t>(
+ {0x6a, 0x9a, 0x09, 0x0c, 0x61, 0xb3, 0x41, 0x0a, 0xed, 0xe7, 0xec,
+ 0x91, 0x38, 0x14, 0x6c, 0xeb, 0x2c, 0x69, 0x66, 0x2f, 0x46, 0x0c,
+ 0x3d, 0xa5, 0x3c, 0x65, 0x15, 0xc1, 0xeb, 0x31, 0xf4, 0x1c, 0xa3,
+ 0xd2, 0x80, 0xe5, 0x67, 0x88, 0x2f, 0x95, 0xcf, 0x66, 0x4a, 0x94,
+ 0x14, 0x7d, 0x78, 0xf4, 0x2c, 0xfc, 0x71, 0x4a, 0x40, 0xd2, 0x2e,
+ 0xf1, 0x94, 0x70, 0xe0, 0x53, 0x49, 0x35, 0x08, 0xa2})),
+ std::make_pair(
+ 10,
+ std::vector<uint8_t>(
+ {0x29, 0x10, 0x25, 0x11, 0xd7, 0x49, 0xdb, 0x3c, 0xc9, 0xb4, 0xe3,
+ 0x35, 0xfa, 0x1f, 0x5e, 0x8f, 0xac, 0xa8, 0x42, 0x1d, 0x55, 0x8f,
+ 0x6a, 0x3f, 0x33, 0x21, 0xd5, 0x0d, 0x04, 0x4a, 0x24, 0x8b, 0xa5,
+ 0x95, 0xcf, 0xc3, 0xef, 0xd3, 0xd2, 0xad, 0xc9, 0x73, 0x34, 0xda,
+ 0x73, 0x24, 0x13, 0xf5, 0xcb, 0xf4, 0x75, 0x1c, 0x36, 0x2b, 0xa1,
+ 0xd5, 0x38, 0x62, 0xac, 0x1e, 0x8d, 0xab, 0xee, 0xe8})),
+ std::make_pair(
+ 11,
+ std::vector<uint8_t>(
+ {0xc9, 0x7a, 0x47, 0x79, 0xd4, 0x7e, 0x6f, 0x77, 0x72, 0x9b, 0x59,
+ 0x17, 0xd0, 0x13, 0x8a, 0xbb, 0x35, 0x98, 0x0a, 0xb6, 0x41, 0xbd,
+ 0x73, 0xa8, 0x85, 0x9e, 0xb1, 0xac, 0x98, 0xc0, 0x53, 0x62, 0xed,
+ 0x7d, 0x60, 0x8f, 0x2e, 0x95, 0x87, 0xd6, 0xba, 0x9e, 0x27, 0x1d,
+ 0x34, 0x31, 0x25, 0xd4, 0x0d, 0x93, 0x3a, 0x8e, 0xd0, 0x4e, 0xc1,
+ 0xfe, 0x75, 0xec, 0x40, 0x7c, 0x7a, 0x53, 0xc3, 0x4e})),
+ std::make_pair(
+ 12,
+ std::vector<uint8_t>(
+ {0x10, 0xf0, 0xdc, 0x91, 0xb9, 0xf8, 0x45, 0xfb, 0x95, 0xfa, 0xd6,
+ 0x86, 0x0e, 0x6c, 0xe1, 0xad, 0xfa, 0x00, 0x2c, 0x7f, 0xc3, 0x27,
+ 0x11, 0x6d, 0x44, 0xd0, 0x47, 0xcd, 0x7d, 0x58, 0x70, 0xd7, 0x72,
+ 0xbb, 0x12, 0xb5, 0xfa, 0xc0, 0x0e, 0x02, 0xb0, 0x8a, 0xc2, 0xa0,
+ 0x17, 0x4d, 0x04, 0x46, 0xc3, 0x6a, 0xb3, 0x5f, 0x14, 0xca, 0x31,
+ 0x89, 0x4c, 0xd6, 0x1c, 0x78, 0xc8, 0x49, 0xb4, 0x8a})),
+ std::make_pair(
+ 13,
+ std::vector<uint8_t>(
+ {0xde, 0xa9, 0x10, 0x1c, 0xac, 0x62, 0xb8, 0xf6, 0xa3, 0xc6, 0x50,
+ 0xf9, 0x0e, 0xea, 0x5b, 0xfa, 0xe2, 0x65, 0x3a, 0x4e, 0xaf, 0xd6,
+ 0x3a, 0x6d, 0x1f, 0x0f, 0x13, 0x2d, 0xb9, 0xe4, 0xf2, 0xb1, 0xb6,
+ 0x62, 0x43, 0x2e, 0xc8, 0x5b, 0x17, 0xbc, 0xac, 0x41, 0xe7, 0x75,
+ 0x63, 0x78, 0x81, 0xf6, 0xaa, 0xb3, 0x8d, 0xd6, 0x6d, 0xcb, 0xd0,
+ 0x80, 0xf0, 0x99, 0x0a, 0x7a, 0x6e, 0x98, 0x54, 0xfe})),
+ std::make_pair(
+ 14,
+ std::vector<uint8_t>(
+ {0x44, 0x1f, 0xfa, 0xa0, 0x8c, 0xd7, 0x9d, 0xff, 0x4a, 0xfc, 0x9b,
+ 0x9e, 0x5b, 0x56, 0x20, 0xee, 0xc0, 0x86, 0x73, 0x0c, 0x25, 0xf6,
+ 0x61, 0xb1, 0xd6, 0xfb, 0xfb, 0xd1, 0xce, 0xc3, 0x14, 0x8d, 0xd7,
+ 0x22, 0x58, 0xc6, 0x56, 0x41, 0xf2, 0xfc, 0xa5, 0xeb, 0x15, 0x5f,
+ 0xad, 0xbc, 0xab, 0xb1, 0x3c, 0x6e, 0x21, 0xdc, 0x11, 0xfa, 0xf7,
+ 0x2c, 0x2a, 0x28, 0x1b, 0x7d, 0x56, 0x14, 0x5f, 0x19})),
+ std::make_pair(
+ 15,
+ std::vector<uint8_t>(
+ {0x44, 0x4b, 0x24, 0x0f, 0xe3, 0xed, 0x86, 0xd0, 0xe2, 0xef, 0x4c,
+ 0xe7, 0xd8, 0x51, 0xed, 0xde, 0x22, 0x15, 0x55, 0x82, 0xaa, 0x09,
+ 0x14, 0x79, 0x7b, 0x72, 0x6c, 0xd0, 0x58, 0xb6, 0xf4, 0x59, 0x32,
+ 0xe0, 0xe1, 0x29, 0x51, 0x68, 0x76, 0x52, 0x7b, 0x1d, 0xd8, 0x8f,
+ 0xc6, 0x6d, 0x71, 0x19, 0xf4, 0xab, 0x3b, 0xed, 0x93, 0xa6, 0x1a,
+ 0x0e, 0x2d, 0x2d, 0x2a, 0xea, 0xc3, 0x36, 0xd9, 0x58})),
+ std::make_pair(
+ 16,
+ std::vector<uint8_t>(
+ {0xbf, 0xba, 0xbb, 0xef, 0x45, 0x55, 0x4c, 0xcf, 0xa0, 0xdc, 0x83,
+ 0x75, 0x2a, 0x19, 0xcc, 0x35, 0xd5, 0x92, 0x09, 0x56, 0xb3, 0x01,
+ 0xd5, 0x58, 0xd7, 0x72, 0x28, 0x2b, 0xc8, 0x67, 0x00, 0x91, 0x68,
+ 0xe9, 0xe9, 0x86, 0x06, 0xbb, 0x5b, 0xa7, 0x3a, 0x38, 0x5d, 0xe5,
+ 0x74, 0x92, 0x28, 0xc9, 0x25, 0xa8, 0x50, 0x19, 0xb7, 0x1f, 0x72,
+ 0xfe, 0x29, 0xb3, 0xcd, 0x37, 0xca, 0x52, 0xef, 0xe6})),
+ std::make_pair(
+ 17,
+ std::vector<uint8_t>(
+ {0x9c, 0x4d, 0x0c, 0x3e, 0x1c, 0xdb, 0xbf, 0x48, 0x5b, 0xec, 0x86,
+ 0xf4, 0x1c, 0xec, 0x7c, 0x98, 0x37, 0x3f, 0x0e, 0x09, 0xf3, 0x92,
+ 0x84, 0x9a, 0xaa, 0x22, 0x9e, 0xbf, 0xbf, 0x39, 0x7b, 0x22, 0x08,
+ 0x55, 0x29, 0xcb, 0x7e, 0xf3, 0x9f, 0x9c, 0x7c, 0x22, 0x22, 0xa5,
+ 0x14, 0x18, 0x2b, 0x1e, 0xff, 0xaa, 0x17, 0x8c, 0xc3, 0x68, 0x7b,
+ 0x1b, 0x2b, 0x6c, 0xbc, 0xb6, 0xfd, 0xeb, 0x96, 0xf8})),
+ std::make_pair(
+ 18,
+ std::vector<uint8_t>(
+ {0x47, 0x71, 0x76, 0xb3, 0xbf, 0xcb, 0xad, 0xd7, 0x65, 0x7c, 0x23,
+ 0xc2, 0x46, 0x25, 0xe4, 0xd0, 0xd6, 0x74, 0xd1, 0x86, 0x8f, 0x00,
+ 0x60, 0x06, 0x39, 0x8a, 0xf9, 0x7a, 0xa4, 0x18, 0x77, 0xc8, 0xe7,
+ 0x0d, 0x3d, 0x14, 0xc3, 0xbb, 0xc9, 0xbb, 0xcd, 0xce, 0xa8, 0x01,
+ 0xbd, 0x0e, 0x15, 0x99, 0xaf, 0x1f, 0x3e, 0xec, 0x67, 0x40, 0x51,
+ 0x70, 0xf4, 0xe2, 0x6c, 0x96, 0x4a, 0x57, 0xa8, 0xb7})),
+ std::make_pair(
+ 19,
+ std::vector<uint8_t>(
+ {0xa7, 0x8c, 0x49, 0x0e, 0xda, 0x31, 0x73, 0xbb, 0x3f, 0x10, 0xde,
+ 0xe5, 0x2f, 0x11, 0x0f, 0xb1, 0xc0, 0x8e, 0x03, 0x02, 0x23, 0x0b,
+ 0x85, 0xdd, 0xd7, 0xc1, 0x12, 0x57, 0xd9, 0x2d, 0xe1, 0x48, 0x78,
+ 0x5e, 0xf0, 0x0c, 0x03, 0x9c, 0x0b, 0xb8, 0xeb, 0x98, 0x08, 0xa3,
+ 0x5b, 0x2d, 0x8c, 0x08, 0x0f, 0x57, 0x28, 0x59, 0x71, 0x4c, 0x9d,
+ 0x40, 0x69, 0xc5, 0xbc, 0xaf, 0x09, 0x0e, 0x89, 0x8e})),
+ std::make_pair(
+ 20,
+ std::vector<uint8_t>(
+ {0x58, 0xd0, 0x23, 0x39, 0x7b, 0xeb, 0x5b, 0x41, 0x45, 0xcb, 0x22,
+ 0x55, 0xb0, 0x7d, 0x74, 0x29, 0x0b, 0x36, 0xd9, 0xfd, 0x1e, 0x59,
+ 0x4a, 0xfb, 0xd8, 0xee, 0xa4, 0x7c, 0x20, 0x5b, 0x2e, 0xfb, 0xfe,
+ 0x6f, 0x46, 0x19, 0x0f, 0xaf, 0x95, 0xaf, 0x50, 0x4a, 0xb0, 0x72,
+ 0xe3, 0x6f, 0x6c, 0x85, 0xd7, 0x67, 0xa3, 0x21, 0xbf, 0xd7, 0xf2,
+ 0x26, 0x87, 0xa4, 0xab, 0xbf, 0x49, 0x4a, 0x68, 0x9c})),
+ std::make_pair(
+ 21,
+ std::vector<uint8_t>(
+ {0x40, 0x01, 0xec, 0x74, 0xd5, 0xa4, 0x6f, 0xd2, 0x9c, 0x2c, 0x3c,
+ 0xdb, 0xe5, 0xd1, 0xb9, 0xf2, 0x0e, 0x51, 0xa9, 0x41, 0xbe, 0x98,
+ 0xd2, 0xa4, 0xe1, 0xe2, 0xfb, 0xf8, 0x66, 0xa6, 0x72, 0x12, 0x1d,
+ 0xb6, 0xf8, 0x1a, 0x51, 0x4c, 0xfd, 0x10, 0xe7, 0x35, 0x8d, 0x57,
+ 0x1b, 0xdb, 0xa4, 0x8e, 0x4c, 0xe7, 0x08, 0xb9, 0xd1, 0x24, 0x89,
+ 0x4b, 0xc0, 0xb5, 0xed, 0x55, 0x49, 0x35, 0xf7, 0x3a})),
+ std::make_pair(
+ 22,
+ std::vector<uint8_t>(
+ {0xcc, 0xd1, 0xb2, 0x2d, 0xab, 0x65, 0x11, 0x22, 0x5d, 0x24, 0x01,
+ 0xea, 0x2d, 0x86, 0x25, 0xd2, 0x06, 0xa1, 0x24, 0x73, 0xcc, 0x73,
+ 0x2b, 0x61, 0x5e, 0x56, 0x40, 0xce, 0xff, 0xf0, 0xa4, 0xad, 0xf9,
+ 0x71, 0xb0, 0xe8, 0x27, 0xa6, 0x19, 0xe0, 0xa8, 0x0f, 0x5d, 0xb9,
+ 0xcc, 0xd0, 0x96, 0x23, 0x29, 0x01, 0x0d, 0x07, 0xe3, 0x4a, 0x20,
+ 0x64, 0xe7, 0x31, 0xc5, 0x20, 0x81, 0x7b, 0x21, 0x83})),
+ std::make_pair(
+ 23,
+ std::vector<uint8_t>(
+ {0xb4, 0xa0, 0xa9, 0xe3, 0x57, 0x4e, 0xdb, 0x9e, 0x1e, 0x72, 0xaa,
+ 0x31, 0xe3, 0x9c, 0xc5, 0xf3, 0x0d, 0xbf, 0x94, 0x3f, 0x8c, 0xab,
+ 0xc4, 0x08, 0x44, 0x96, 0x54, 0xa3, 0x91, 0x31, 0xe6, 0x6d, 0x71,
+ 0x8a, 0x18, 0x81, 0x91, 0x43, 0xe3, 0xea, 0x96, 0xb4, 0xa1, 0x89,
+ 0x59, 0x88, 0xa1, 0xc0, 0x05, 0x6c, 0xf2, 0xb6, 0xe0, 0x4f, 0x9a,
+ 0xc1, 0x9d, 0x65, 0x73, 0x83, 0xc2, 0x91, 0x0c, 0x44})),
+ std::make_pair(
+ 24,
+ std::vector<uint8_t>(
+ {0x44, 0x7b, 0xec, 0xab, 0x16, 0x63, 0x06, 0x08, 0xd3, 0x9f, 0x4f,
+ 0x05, 0x8b, 0x16, 0xf7, 0xaf, 0x95, 0xb8, 0x5a, 0x76, 0xaa, 0x0f,
+ 0xa7, 0xce, 0xa2, 0xb8, 0x07, 0x55, 0xfb, 0x76, 0xe9, 0xc8, 0x04,
+ 0xf2, 0xca, 0x78, 0xf0, 0x26, 0x43, 0xc9, 0x15, 0xfb, 0xf2, 0xfc,
+ 0xe5, 0xe1, 0x9d, 0xe8, 0x60, 0x00, 0xde, 0x03, 0xb1, 0x88, 0x61,
+ 0x81, 0x5a, 0x83, 0x12, 0x60, 0x71, 0xf8, 0xa3, 0x7b})),
+ std::make_pair(
+ 25,
+ std::vector<uint8_t>(
+ {0x54, 0xe6, 0xda, 0xb9, 0x97, 0x73, 0x80, 0xa5, 0x66, 0x58, 0x22,
+ 0xdb, 0x93, 0x37, 0x4e, 0xda, 0x52, 0x8d, 0x9b, 0xeb, 0x62, 0x6f,
+ 0x9b, 0x94, 0x02, 0x70, 0x71, 0xcb, 0x26, 0x67, 0x5e, 0x11, 0x2b,
+ 0x4a, 0x7f, 0xec, 0x94, 0x1e, 0xe6, 0x0a, 0x81, 0xe4, 0xd2, 0xea,
+ 0x3f, 0xf7, 0xbc, 0x52, 0xcf, 0xc4, 0x5d, 0xfb, 0xfe, 0x73, 0x5a,
+ 0x1c, 0x64, 0x6b, 0x2c, 0xf6, 0xd6, 0xa4, 0x9b, 0x62})),
+ std::make_pair(
+ 26,
+ std::vector<uint8_t>(
+ {0x3e, 0xa6, 0x26, 0x25, 0x94, 0x9e, 0x36, 0x46, 0x70, 0x4d, 0x7e,
+ 0x3c, 0x90, 0x6f, 0x82, 0xf6, 0xc0, 0x28, 0xf5, 0x40, 0xf5, 0xf7,
+ 0x2a, 0x79, 0x4b, 0x0c, 0x57, 0xbf, 0x97, 0xb7, 0x64, 0x9b, 0xfe,
+ 0xb9, 0x0b, 0x01, 0xd3, 0xca, 0x3e, 0x82, 0x9d, 0xe2, 0x1b, 0x38,
+ 0x26, 0xe6, 0xf8, 0x70, 0x14, 0xd3, 0xc7, 0x73, 0x50, 0xcb, 0x5a,
+ 0x15, 0xff, 0x5d, 0x46, 0x8a, 0x81, 0xbe, 0xc1, 0x60})),
+ std::make_pair(
+ 27,
+ std::vector<uint8_t>(
+ {0x21, 0x3c, 0xfe, 0x14, 0x5c, 0x54, 0xa3, 0x36, 0x91, 0x56, 0x99,
+ 0x80, 0xe5, 0x93, 0x8c, 0x88, 0x83, 0xa4, 0x6d, 0x84, 0xd1, 0x49,
+ 0xc8, 0xff, 0x1a, 0x67, 0xcd, 0x28, 0x7b, 0x4d, 0x49, 0xc6, 0xda,
+ 0x69, 0xd3, 0xa0, 0x35, 0x44, 0x3d, 0xb0, 0x85, 0x98, 0x3d, 0x0e,
+ 0xfe, 0x63, 0x70, 0x6b, 0xd5, 0xb6, 0xf1, 0x5a, 0x7d, 0xa4, 0x59,
+ 0xe8, 0xd5, 0x0a, 0x19, 0x09, 0x3d, 0xb5, 0x5e, 0x80})),
+ std::make_pair(
+ 28,
+ std::vector<uint8_t>(
+ {0x57, 0x16, 0xc4, 0xa3, 0x8f, 0x38, 0xdb, 0x10, 0x4e, 0x49, 0x4a,
+ 0x0a, 0x27, 0xcb, 0xe8, 0x9a, 0x26, 0xa6, 0xbb, 0x6f, 0x49, 0x9e,
+ 0xc0, 0x1c, 0x8c, 0x01, 0xaa, 0x7c, 0xb8, 0x84, 0x97, 0xe7, 0x51,
+ 0x48, 0xcd, 0x6e, 0xee, 0x12, 0xa7, 0x16, 0x8b, 0x6f, 0x78, 0xab,
+ 0x74, 0xe4, 0xbe, 0x74, 0x92, 0x51, 0xa1, 0xa7, 0x4c, 0x38, 0xc8,
+ 0x6d, 0x61, 0x29, 0x17, 0x7e, 0x28, 0x89, 0xe0, 0xb6})),
+ std::make_pair(
+ 29,
+ std::vector<uint8_t>(
+ {0x03, 0x04, 0x60, 0xa9, 0x8b, 0xdf, 0x9f, 0xf1, 0x7c, 0xd9, 0x64,
+ 0x04, 0xf2, 0x8f, 0xc3, 0x04, 0xf2, 0xb7, 0xc0, 0x4e, 0xaa, 0xde,
+ 0x53, 0x67, 0x7f, 0xd2, 0x8f, 0x78, 0x8c, 0xa2, 0x21, 0x86, 0xb8,
+ 0xbc, 0x80, 0xdd, 0x21, 0xd1, 0x7f, 0x85, 0x49, 0xc7, 0x11, 0xaf,
+ 0xf0, 0xe5, 0x14, 0xe1, 0x9d, 0x4e, 0x15, 0xf5, 0x99, 0x02, 0x52,
+ 0xa0, 0x3e, 0x08, 0x2f, 0x28, 0xdc, 0x20, 0x52, 0xf6})),
+ std::make_pair(
+ 30,
+ std::vector<uint8_t>(
+ {0x19, 0xe7, 0xf1, 0xcc, 0xee, 0x88, 0xa1, 0x06, 0x72, 0x33, 0x3e,
+ 0x39, 0x0c, 0xf2, 0x20, 0x13, 0xa8, 0xc7, 0x34, 0xc6, 0xcb, 0x9e,
+ 0xab, 0x41, 0xf1, 0x7c, 0x3c, 0x80, 0x32, 0xa2, 0xe4, 0xac, 0xa0,
+ 0x56, 0x9e, 0xa3, 0x6f, 0x08, 0x60, 0xc7, 0xa1, 0xaf, 0x28, 0xfa,
+ 0x47, 0x68, 0x40, 0xd6, 0x60, 0x11, 0x16, 0x88, 0x59, 0x33, 0x4a,
+ 0x9e, 0x4e, 0xf9, 0xcc, 0x2e, 0x61, 0xa0, 0xe2, 0x9e})),
+ std::make_pair(
+ 31,
+ std::vector<uint8_t>(
+ {0x29, 0xf8, 0xb8, 0xc7, 0x8c, 0x80, 0xf2, 0xfc, 0xb4, 0xbd, 0xf7,
+ 0x82, 0x5e, 0xd9, 0x0a, 0x70, 0xd6, 0x25, 0xff, 0x78, 0x5d, 0x26,
+ 0x26, 0x77, 0xe2, 0x50, 0xc0, 0x4f, 0x37, 0x20, 0xc8, 0x88, 0xd0,
+ 0x3f, 0x80, 0x45, 0xe4, 0xed, 0xf3, 0xf5, 0x28, 0x5b, 0xd3, 0x9d,
+ 0x92, 0x8a, 0x10, 0xa7, 0xd0, 0xa5, 0xdf, 0x00, 0xb8, 0x48, 0x4a,
+ 0xc2, 0x86, 0x81, 0x42, 0xa1, 0xe8, 0xbe, 0xa3, 0x51})),
+ std::make_pair(
+ 32,
+ std::vector<uint8_t>(
+ {0x5c, 0x52, 0x92, 0x0a, 0x72, 0x63, 0xe3, 0x9d, 0x57, 0x92, 0x0c,
+ 0xa0, 0xcb, 0x75, 0x2a, 0xc6, 0xd7, 0x9a, 0x04, 0xfe, 0xf8, 0xa7,
+ 0xa2, 0x16, 0xa1, 0xec, 0xb7, 0x11, 0x5c, 0xe0, 0x6d, 0x89, 0xfd,
+ 0x7d, 0x73, 0x5b, 0xd6, 0xf4, 0x27, 0x25, 0x55, 0xdb, 0xa2, 0x2c,
+ 0x2d, 0x1c, 0x96, 0xe6, 0x35, 0x23, 0x22, 0xc6, 0x2c, 0x56, 0x30,
+ 0xfd, 0xe0, 0xf4, 0x77, 0x7a, 0x76, 0xc3, 0xde, 0x2c})),
+ std::make_pair(
+ 33,
+ std::vector<uint8_t>(
+ {0x83, 0xb0, 0x98, 0xf2, 0x62, 0x25, 0x1b, 0xf6, 0x60, 0x06, 0x4a,
+ 0x9d, 0x35, 0x11, 0xce, 0x76, 0x87, 0xa0, 0x9e, 0x6d, 0xfb, 0xb8,
+ 0x78, 0x29, 0x9c, 0x30, 0xe9, 0x3d, 0xfb, 0x43, 0xa9, 0x31, 0x4d,
+ 0xb9, 0xa6, 0x00, 0x33, 0x7d, 0xb2, 0x6e, 0xbe, 0xed, 0xaf, 0x22,
+ 0x56, 0xa9, 0x6d, 0xab, 0xe9, 0xb2, 0x9e, 0x75, 0x73, 0xad, 0x11,
+ 0xc3, 0x52, 0x3d, 0x87, 0x4d, 0xde, 0x5b, 0xe7, 0xed})),
+ std::make_pair(
+ 34,
+ std::vector<uint8_t>(
+ {0x94, 0x47, 0xd9, 0x8a, 0xa5, 0xc9, 0x33, 0x13, 0x52, 0xf4, 0x3d,
+ 0x3e, 0x56, 0xd0, 0xa9, 0xa9, 0xf9, 0x58, 0x18, 0x65, 0x99, 0x8e,
+ 0x28, 0x85, 0xcc, 0x56, 0xdd, 0x0a, 0x0b, 0xd5, 0xa7, 0xb5, 0x05,
+ 0x95, 0xbd, 0x10, 0xf7, 0x52, 0x9b, 0xcd, 0x31, 0xf3, 0x7d, 0xc1,
+ 0x6a, 0x14, 0x65, 0xd5, 0x94, 0x07, 0x96, 0x67, 0xda, 0x2a, 0x3f,
+ 0xcb, 0x70, 0x40, 0x14, 0x98, 0x83, 0x7c, 0xed, 0xeb})),
+ std::make_pair(
+ 35,
+ std::vector<uint8_t>(
+ {0x86, 0x77, 0x32, 0xf2, 0xfe, 0xeb, 0x23, 0x89, 0x30, 0x97, 0x56,
+ 0x1a, 0xc7, 0x10, 0xa4, 0xbf, 0xf4, 0x53, 0xbe, 0x9c, 0xfb, 0xed,
+ 0xba, 0x8b, 0xa3, 0x24, 0xf9, 0xd3, 0x12, 0xa8, 0x2d, 0x73, 0x2e,
+ 0x1b, 0x83, 0xb8, 0x29, 0xfd, 0xcd, 0x17, 0x7b, 0x88, 0x2c, 0xa0,
+ 0xc1, 0xbf, 0x54, 0x4b, 0x22, 0x3b, 0xe5, 0x29, 0x92, 0x4a, 0x24,
+ 0x6a, 0x63, 0xcf, 0x05, 0x9b, 0xfd, 0xc5, 0x0a, 0x1b})),
+ std::make_pair(
+ 36,
+ std::vector<uint8_t>(
+ {0xf1, 0x5a, 0xb2, 0x6d, 0x4c, 0xdf, 0xcf, 0x56, 0xe1, 0x96, 0xbb,
+ 0x6b, 0xa1, 0x70, 0xa8, 0xfc, 0xcc, 0x41, 0x4d, 0xe9, 0x28, 0x5a,
+ 0xfd, 0x98, 0xa3, 0xd3, 0xcf, 0x2f, 0xb8, 0x8f, 0xcb, 0xc0, 0xf1,
+ 0x98, 0x32, 0xac, 0x43, 0x3a, 0x5b, 0x2c, 0xc2, 0x39, 0x2a, 0x4c,
+ 0xe3, 0x43, 0x32, 0x98, 0x7d, 0x8d, 0x2c, 0x2b, 0xef, 0x6c, 0x34,
+ 0x66, 0x13, 0x8d, 0xb0, 0xc6, 0xe4, 0x2f, 0xa4, 0x7b})),
+ std::make_pair(
+ 37,
+ std::vector<uint8_t>(
+ {0x28, 0x13, 0x51, 0x6d, 0x68, 0xed, 0x4a, 0x08, 0xb3, 0x9d, 0x64,
+ 0x8a, 0xa6, 0xaa, 0xcd, 0x81, 0xe9, 0xd6, 0x55, 0xec, 0xd5, 0xf0,
+ 0xc1, 0x35, 0x56, 0xc6, 0x0f, 0xdf, 0x0d, 0x33, 0x3e, 0xa3, 0x84,
+ 0x64, 0xb3, 0x6c, 0x02, 0xba, 0xcc, 0xd7, 0x46, 0xe9, 0x57, 0x5e,
+ 0x96, 0xc6, 0x30, 0x14, 0xf0, 0x74, 0xae, 0x34, 0xa0, 0xa2, 0x5b,
+ 0x32, 0x0f, 0x0f, 0xbe, 0xdd, 0x6a, 0xcf, 0x76, 0x65})),
+ std::make_pair(
+ 38,
+ std::vector<uint8_t>(
+ {0xd3, 0x25, 0x9a, 0xfc, 0xa8, 0xa4, 0x89, 0x62, 0xfa, 0x89, 0x2e,
+ 0x14, 0x5a, 0xcf, 0x54, 0x7f, 0x26, 0x92, 0x3a, 0xe8, 0xd4, 0x92,
+ 0x4c, 0x8a, 0x53, 0x15, 0x81, 0x52, 0x6b, 0x04, 0xb4, 0x4c, 0x7a,
+ 0xf8, 0x3c, 0x64, 0x3e, 0xf5, 0xa0, 0xbc, 0x28, 0x2d, 0x36, 0xf3,
+ 0xfb, 0x04, 0xc8, 0x4e, 0x28, 0xb3, 0x51, 0xf4, 0x0c, 0x74, 0xb6,
+ 0x9d, 0xc7, 0x84, 0x0b, 0xc7, 0x17, 0xb6, 0xf1, 0x5f})),
+ std::make_pair(
+ 39,
+ std::vector<uint8_t>(
+ {0xf1, 0x4b, 0x06, 0x1a, 0xe3, 0x59, 0xfa, 0x31, 0xb9, 0x89, 0xe3,
+ 0x03, 0x32, 0xbf, 0xe8, 0xde, 0x8c, 0xc8, 0xcd, 0xb5, 0x68, 0xe1,
+ 0x4b, 0xe2, 0x14, 0xa2, 0x22, 0x3b, 0x84, 0xca, 0xab, 0x74, 0x19,
+ 0x54, 0x9e, 0xcf, 0xcc, 0x96, 0xce, 0x2a, 0xce, 0xc1, 0x19, 0x48,
+ 0x5d, 0x87, 0xd1, 0x57, 0xd3, 0xa8, 0x73, 0x4f, 0xc4, 0x26, 0x59,
+ 0x7d, 0x64, 0xf3, 0x65, 0x70, 0xce, 0xaf, 0x22, 0x4d})),
+ std::make_pair(
+ 40,
+ std::vector<uint8_t>(
+ {0x55, 0xe7, 0x0b, 0x01, 0xd1, 0xfb, 0xf8, 0xb2, 0x3b, 0x57, 0xfb,
+ 0x62, 0xe2, 0x6c, 0x2c, 0xe5, 0x4f, 0x13, 0xf8, 0xfa, 0x24, 0x64,
+ 0xe6, 0xeb, 0x98, 0xd1, 0x6a, 0x61, 0x17, 0x02, 0x6d, 0x8b, 0x90,
+ 0x81, 0x90, 0x12, 0x49, 0x6d, 0x40, 0x71, 0xeb, 0xe2, 0xe5, 0x95,
+ 0x57, 0xec, 0xe3, 0x51, 0x9a, 0x7a, 0xa4, 0x58, 0x02, 0xf9, 0x61,
+ 0x53, 0x74, 0x87, 0x73, 0x32, 0xb7, 0x34, 0x90, 0xb3})),
+ std::make_pair(
+ 41,
+ std::vector<uint8_t>(
+ {0x25, 0x26, 0x1e, 0xb2, 0x96, 0x97, 0x1d, 0x6e, 0x4a, 0x71, 0xb2,
+ 0x92, 0x8e, 0x64, 0x83, 0x9c, 0x67, 0xd4, 0x22, 0x87, 0x2b, 0xf9,
+ 0xf3, 0xc3, 0x19, 0x93, 0x61, 0x52, 0x22, 0xde, 0x9f, 0x8f, 0x0b,
+ 0x2c, 0x4b, 0xe8, 0x54, 0x85, 0x59, 0xb4, 0xb3, 0x54, 0xe7, 0x36,
+ 0x41, 0x6e, 0x32, 0x18, 0xd4, 0xe8, 0xa1, 0xe2, 0x19, 0xa4, 0xa6,
+ 0xd4, 0x3e, 0x1a, 0x9a, 0x52, 0x1d, 0x0e, 0x75, 0xfc})),
+ std::make_pair(
+ 42,
+ std::vector<uint8_t>(
+ {0x08, 0x30, 0x7f, 0x34, 0x7c, 0x41, 0x29, 0x4e, 0x34, 0xbb, 0x54,
+ 0xcb, 0x42, 0xb1, 0x52, 0x2d, 0x22, 0xf8, 0x24, 0xf7, 0xb6, 0xe5,
+ 0xdb, 0x50, 0xfd, 0xa0, 0x96, 0x79, 0x8e, 0x18, 0x1a, 0x8f, 0x02,
+ 0x6f, 0xa2, 0x7b, 0x4a, 0xe4, 0x5d, 0x52, 0xa6, 0x2c, 0xaf, 0x9d,
+ 0x51, 0x98, 0xe2, 0x4a, 0x49, 0x13, 0xc6, 0x67, 0x17, 0x75, 0xb2,
+ 0xd7, 0x23, 0xc1, 0x23, 0x9b, 0xfb, 0xf0, 0x16, 0xd7})),
+ std::make_pair(
+ 43,
+ std::vector<uint8_t>(
+ {0x1e, 0x5c, 0x62, 0xe7, 0xe9, 0xbf, 0xa1, 0xb1, 0x18, 0x74, 0x7a,
+ 0x2d, 0xe0, 0x8b, 0x3c, 0xa1, 0x01, 0x12, 0xaf, 0x96, 0xa4, 0x6e,
+ 0x4b, 0x22, 0xc3, 0xfc, 0x06, 0xf9, 0xbf, 0xee, 0x4e, 0xb5, 0xc4,
+ 0x9e, 0x05, 0x7a, 0x4a, 0x48, 0x86, 0x23, 0x43, 0x24, 0x57, 0x25,
+ 0x76, 0xbb, 0x9b, 0x5e, 0xcf, 0xde, 0x0d, 0x99, 0xb0, 0xde, 0x4f,
+ 0x98, 0xec, 0x16, 0xe4, 0xd1, 0xb8, 0x5f, 0xa9, 0x47})),
+ std::make_pair(
+ 44,
+ std::vector<uint8_t>(
+ {0xc7, 0x4a, 0x77, 0x39, 0x5f, 0xb8, 0xbc, 0x12, 0x64, 0x47, 0x45,
+ 0x48, 0x38, 0xe5, 0x61, 0xe9, 0x62, 0x85, 0x3d, 0xc7, 0xeb, 0x49,
+ 0xa1, 0xe3, 0xcb, 0x67, 0xc3, 0xd0, 0x85, 0x1f, 0x3e, 0x39, 0x51,
+ 0x7b, 0xe8, 0xc3, 0x50, 0xac, 0x91, 0x09, 0x03, 0xd4, 0x9c, 0xd2,
+ 0xbf, 0xdf, 0x54, 0x5c, 0x99, 0x31, 0x6d, 0x03, 0x46, 0x17, 0x0b,
+ 0x73, 0x9f, 0x0a, 0xdd, 0x5d, 0x53, 0x3c, 0x2c, 0xfc})),
+ std::make_pair(
+ 45,
+ std::vector<uint8_t>(
+ {0x0d, 0xd5, 0x7b, 0x42, 0x3c, 0xc0, 0x1e, 0xb2, 0x86, 0x13, 0x91,
+ 0xeb, 0x88, 0x6a, 0x0d, 0x17, 0x07, 0x9b, 0x93, 0x3f, 0xc7, 0x6e,
+ 0xb3, 0xfc, 0x08, 0xa1, 0x9f, 0x8a, 0x74, 0x95, 0x2c, 0xb6, 0x8f,
+ 0x6b, 0xcd, 0xc6, 0x44, 0xf7, 0x73, 0x70, 0x96, 0x6e, 0x4d, 0x13,
+ 0xe8, 0x05, 0x60, 0xbc, 0xf0, 0x82, 0xef, 0x04, 0x79, 0xd4, 0x8f,
+ 0xbb, 0xab, 0x4d, 0xf0, 0x3b, 0x53, 0xa4, 0xe1, 0x78})),
+ std::make_pair(
+ 46,
+ std::vector<uint8_t>(
+ {0x4d, 0x8d, 0xc3, 0x92, 0x3e, 0xdc, 0xcd, 0xfc, 0xe7, 0x00, 0x72,
+ 0x39, 0x8b, 0x8a, 0x3d, 0xa5, 0xc3, 0x1f, 0xcb, 0x3e, 0xe3, 0xb6,
+ 0x45, 0xc8, 0x5f, 0x71, 0x7c, 0xba, 0xeb, 0x4b, 0x67, 0x3a, 0x19,
+ 0x39, 0x44, 0x25, 0xa5, 0x85, 0xbf, 0xb4, 0x64, 0xd9, 0x2f, 0x15,
+ 0x97, 0xd0, 0xb7, 0x54, 0xd1, 0x63, 0xf9, 0x7c, 0xed, 0x34, 0x3b,
+ 0x25, 0xdb, 0x5a, 0x70, 0xef, 0x48, 0xeb, 0xb3, 0x4f})),
+ std::make_pair(
+ 47,
+ std::vector<uint8_t>(
+ {0xf0, 0xa5, 0x05, 0x53, 0xe4, 0xdf, 0xb0, 0xc4, 0xe3, 0xe3, 0xd3,
+ 0xba, 0x82, 0x03, 0x48, 0x57, 0xe3, 0xb1, 0xe5, 0x09, 0x18, 0xf5,
+ 0xb8, 0xa7, 0xd6, 0x98, 0xe1, 0x0d, 0x24, 0x2b, 0x0f, 0xb5, 0x44,
+ 0xaf, 0x6c, 0x92, 0xd0, 0xc3, 0xaa, 0xf9, 0x93, 0x22, 0x20, 0x41,
+ 0x61, 0x17, 0xb4, 0xe7, 0x8e, 0xcb, 0x8a, 0x8f, 0x43, 0x0e, 0x13,
+ 0xb8, 0x2a, 0x59, 0x15, 0x29, 0x0a, 0x58, 0x19, 0xc5})),
+ std::make_pair(
+ 48,
+ std::vector<uint8_t>(
+ {0xb1, 0x55, 0x43, 0xf3, 0xf7, 0x36, 0x08, 0x66, 0x27, 0xcc, 0x53,
+ 0x65, 0xe7, 0xe8, 0x98, 0x8c, 0x2e, 0xf1, 0x55, 0xc0, 0xfd, 0x4f,
+ 0x42, 0x89, 0x61, 0xb0, 0x0d, 0x15, 0x26, 0xf0, 0x4d, 0x6d, 0x6a,
+ 0x65, 0x8b, 0x4b, 0x8e, 0xd3, 0x2c, 0x5d, 0x86, 0x21, 0xe7, 0xf4,
+ 0xf8, 0xe8, 0xa9, 0x33, 0xd9, 0xec, 0xc9, 0xdd, 0x1b, 0x83, 0x33,
+ 0xcb, 0xe2, 0x8c, 0xfc, 0x37, 0xd9, 0x71, 0x9e, 0x1c})),
+ std::make_pair(
+ 49,
+ std::vector<uint8_t>(
+ {0x7b, 0x4f, 0xa1, 0x58, 0xe4, 0x15, 0xfe, 0xf0, 0x23, 0x24, 0x72,
+ 0x64, 0xcb, 0xbe, 0x15, 0xd1, 0x6d, 0x91, 0xa4, 0x44, 0x24, 0xa8,
+ 0xdb, 0x70, 0x7e, 0xb1, 0xe2, 0x03, 0x3c, 0x30, 0xe9, 0xe1, 0xe7,
+ 0xc8, 0xc0, 0x86, 0x45, 0x95, 0xd2, 0xcb, 0x8c, 0x58, 0x0e, 0xb4,
+ 0x7e, 0x9d, 0x16, 0xab, 0xbd, 0x7e, 0x44, 0xe8, 0x24, 0xf7, 0xce,
+ 0xdb, 0x7d, 0xef, 0x57, 0x13, 0x0e, 0x52, 0xcf, 0xe9})),
+ std::make_pair(
+ 50,
+ std::vector<uint8_t>(
+ {0x60, 0x42, 0x4f, 0xf2, 0x32, 0x34, 0xc3, 0x4d, 0xc9, 0x68, 0x7a,
+ 0xd5, 0x02, 0x86, 0x93, 0x72, 0xcc, 0x31, 0xa5, 0x93, 0x80, 0x18,
+ 0x6b, 0xc2, 0x36, 0x1c, 0x83, 0x5d, 0x97, 0x2f, 0x49, 0x66, 0x6e,
+ 0xb1, 0xac, 0x69, 0x62, 0x9d, 0xe6, 0x46, 0xf0, 0x3f, 0x9b, 0x4d,
+ 0xb9, 0xe2, 0xac, 0xe0, 0x93, 0xfb, 0xfd, 0xf8, 0xf2, 0x0a, 0xb5,
+ 0xf9, 0x85, 0x41, 0x97, 0x8b, 0xe8, 0xef, 0x54, 0x9f})),
+ std::make_pair(
+ 51,
+ std::vector<uint8_t>(
+ {0x74, 0x06, 0x01, 0x8c, 0xe7, 0x04, 0xd8, 0x4f, 0x5e, 0xb9, 0xc7,
+ 0x9f, 0xea, 0x97, 0xda, 0x34, 0x56, 0x99, 0x46, 0x8a, 0x35, 0x0e,
+ 0xe0, 0xb2, 0xd0, 0xf3, 0xa4, 0xbf, 0x20, 0x70, 0x30, 0x4e, 0xa8,
+ 0x62, 0xd7, 0x2a, 0x51, 0xc5, 0x7d, 0x30, 0x64, 0x94, 0x72, 0x86,
+ 0xf5, 0x31, 0xe0, 0xea, 0xf7, 0x56, 0x37, 0x02, 0x26, 0x2e, 0x6c,
+ 0x72, 0x4a, 0xbf, 0x5e, 0xd8, 0xc8, 0x39, 0x8d, 0x17})),
+ std::make_pair(
+ 52,
+ std::vector<uint8_t>(
+ {0x14, 0xef, 0x5c, 0x6d, 0x64, 0x7b, 0x3b, 0xd1, 0xe6, 0xe3, 0x20,
+ 0x06, 0xc2, 0x31, 0x19, 0x98, 0x10, 0xde, 0x5c, 0x4d, 0xc8, 0x8e,
+ 0x70, 0x24, 0x02, 0x73, 0xb0, 0xea, 0x18, 0xe6, 0x51, 0xa3, 0xeb,
+ 0x4f, 0x5c, 0xa3, 0x11, 0x4b, 0x8a, 0x56, 0x71, 0x69, 0x69, 0xc7,
+ 0xcd, 0xa2, 0x7e, 0x0c, 0x8d, 0xb8, 0x32, 0xad, 0x5e, 0x89, 0xa2,
+ 0xdc, 0x6c, 0xb0, 0xad, 0xbe, 0x7d, 0x93, 0xab, 0xd1})),
+ std::make_pair(
+ 53,
+ std::vector<uint8_t>(
+ {0x38, 0xcf, 0x6c, 0x24, 0xe3, 0xe0, 0x8b, 0xcf, 0x1f, 0x6c, 0xf3,
+ 0xd1, 0xb1, 0xf6, 0x5b, 0x90, 0x52, 0x39, 0xa3, 0x11, 0x80, 0x33,
+ 0x24, 0x9e, 0x44, 0x81, 0x13, 0xec, 0x63, 0x2e, 0xa6, 0xdc, 0x34,
+ 0x6f, 0xee, 0xb2, 0x57, 0x1c, 0x38, 0xbd, 0x9a, 0x73, 0x98, 0xb2,
+ 0x22, 0x12, 0x80, 0x32, 0x80, 0x02, 0xb2, 0x3e, 0x1a, 0x45, 0xad,
+ 0xaf, 0xfe, 0x66, 0xd9, 0x3f, 0x65, 0x64, 0xea, 0xa2})),
+ std::make_pair(
+ 54,
+ std::vector<uint8_t>(
+ {0x6c, 0xd7, 0x20, 0x8a, 0x4b, 0xc7, 0xe7, 0xe5, 0x62, 0x01, 0xbb,
+ 0xba, 0x02, 0xa0, 0xf4, 0x89, 0xcd, 0x38, 0x4a, 0xbe, 0x40, 0xaf,
+ 0xd4, 0x22, 0x2f, 0x15, 0x8b, 0x3d, 0x98, 0x6e, 0xe7, 0x2a, 0x54,
+ 0xc5, 0x0f, 0xb6, 0x4f, 0xd4, 0xed, 0x25, 0x30, 0xed, 0xa2, 0xc8,
+ 0xaf, 0x29, 0x28, 0xa0, 0xda, 0x6d, 0x4f, 0x83, 0x0a, 0xe1, 0xc9,
+ 0xdb, 0x46, 0x9d, 0xfd, 0x97, 0x0f, 0x12, 0xa5, 0x6f})),
+ std::make_pair(
+ 55,
+ std::vector<uint8_t>(
+ {0x65, 0x98, 0x58, 0xf0, 0xb5, 0xc9, 0xed, 0xab, 0x5b, 0x94, 0xfd,
+ 0x73, 0x2f, 0x6e, 0x6b, 0x17, 0xc5, 0x1c, 0xc0, 0x96, 0x10, 0x4f,
+ 0x09, 0xbe, 0xb3, 0xaf, 0xc3, 0xaa, 0x46, 0x7c, 0x2e, 0xcf, 0x88,
+ 0x5c, 0x4c, 0x65, 0x41, 0xef, 0xfa, 0x90, 0x23, 0xd3, 0xb5, 0x73,
+ 0x8a, 0xe5, 0xa1, 0x4d, 0x86, 0x7e, 0x15, 0xdb, 0x06, 0xfe, 0x1f,
+ 0x9d, 0x11, 0x27, 0xb7, 0x7e, 0x1a, 0xab, 0xb5, 0x16})),
+ std::make_pair(
+ 56,
+ std::vector<uint8_t>(
+ {0x26, 0xcc, 0xa0, 0x12, 0x6f, 0x5d, 0x1a, 0x81, 0x3c, 0x62, 0xe5,
+ 0xc7, 0x10, 0x01, 0xc0, 0x46, 0xf9, 0xc9, 0x20, 0x95, 0x70, 0x45,
+ 0x50, 0xbe, 0x58, 0x73, 0xa4, 0x95, 0xa9, 0x99, 0xad, 0x01, 0x0a,
+ 0x4f, 0x79, 0x49, 0x1f, 0x24, 0xf2, 0x86, 0x50, 0x0a, 0xdc, 0xe1,
+ 0xa1, 0x37, 0xbc, 0x20, 0x84, 0xe4, 0x94, 0x9f, 0x5b, 0x72, 0x94,
+ 0xce, 0xfe, 0x51, 0xec, 0xaf, 0xf8, 0xe9, 0x5c, 0xba})),
+ std::make_pair(
+ 57,
+ std::vector<uint8_t>(
+ {0x41, 0x47, 0xc1, 0xf5, 0x51, 0x72, 0x78, 0x8c, 0x55, 0x67, 0xc5,
+ 0x61, 0xfe, 0xef, 0x87, 0x6f, 0x62, 0x1f, 0xff, 0x1c, 0xe8, 0x77,
+ 0x86, 0xb8, 0x46, 0x76, 0x37, 0xe7, 0x0d, 0xfb, 0xcd, 0x0d, 0xbd,
+ 0xb6, 0x41, 0x5c, 0xb6, 0x00, 0x95, 0x4a, 0xb9, 0xc0, 0x4c, 0x0e,
+ 0x45, 0x7e, 0x62, 0x5b, 0x40, 0x72, 0x22, 0xc0, 0xfe, 0x1a, 0xe2,
+ 0x1b, 0x21, 0x43, 0x68, 0x8a, 0xda, 0x94, 0xdc, 0x58})),
+ std::make_pair(
+ 58,
+ std::vector<uint8_t>(
+ {0x5b, 0x1b, 0xf1, 0x54, 0xc6, 0x2a, 0x8a, 0xf6, 0xe9, 0x3d, 0x35,
+ 0xf1, 0x8f, 0x7f, 0x90, 0xab, 0xb1, 0x6a, 0x6e, 0xf0, 0xe8, 0xd1,
+ 0xae, 0xcd, 0x11, 0x8b, 0xf7, 0x01, 0x67, 0xba, 0xb2, 0xaf, 0x08,
+ 0x93, 0x5c, 0x6f, 0xdc, 0x06, 0x63, 0xce, 0x74, 0x48, 0x2d, 0x17,
+ 0xa8, 0xe5, 0x4b, 0x54, 0x6d, 0x1c, 0x29, 0x66, 0x31, 0xc6, 0x5f,
+ 0x3b, 0x52, 0x2a, 0x51, 0x58, 0x39, 0xd4, 0x3d, 0x71})),
+ std::make_pair(
+ 59,
+ std::vector<uint8_t>(
+ {0x9f, 0x60, 0x04, 0x19, 0xa4, 0xe8, 0xf4, 0xfb, 0x83, 0x4c, 0x24,
+ 0xb0, 0xf7, 0xfc, 0x13, 0xbf, 0x4e, 0x27, 0x9d, 0x98, 0xe8, 0xa3,
+ 0xc7, 0x65, 0xee, 0x93, 0x49, 0x17, 0x40, 0x3e, 0x3a, 0x66, 0x09,
+ 0x71, 0x82, 0xea, 0x21, 0x45, 0x3c, 0xb6, 0x3e, 0xbb, 0xe8, 0xb7,
+ 0x3a, 0x9c, 0x21, 0x67, 0x59, 0x64, 0x46, 0x43, 0x8c, 0x57, 0x62,
+ 0x7f, 0x33, 0x0b, 0xad, 0xd4, 0xf5, 0x69, 0xf7, 0xd6})),
+ std::make_pair(
+ 60,
+ std::vector<uint8_t>(
+ {0x45, 0x7e, 0xf6, 0x46, 0x6a, 0x89, 0x24, 0xfd, 0x80, 0x11, 0xa3,
+ 0x44, 0x71, 0xa5, 0xa1, 0xac, 0x8c, 0xcd, 0x9b, 0xd0, 0xd0, 0x7a,
+ 0x97, 0x41, 0x4a, 0xc9, 0x43, 0x02, 0x1c, 0xe4, 0xb9, 0xe4, 0xb9,
+ 0xc8, 0xdb, 0x0a, 0x28, 0xf0, 0x16, 0xed, 0x43, 0xb1, 0x54, 0x24,
+ 0x81, 0x99, 0x00, 0x22, 0x14, 0x7b, 0x31, 0x3e, 0x19, 0x46, 0x71,
+ 0x13, 0x1e, 0x70, 0x8d, 0xd4, 0x3a, 0x3e, 0xd7, 0xdc})),
+ std::make_pair(
+ 61,
+ std::vector<uint8_t>(
+ {0x99, 0x97, 0xb2, 0x19, 0x4d, 0x9a, 0xf6, 0xdf, 0xcb, 0x91, 0x43,
+ 0xf4, 0x1c, 0x0e, 0xd8, 0x3d, 0x3a, 0x3f, 0x43, 0x88, 0x36, 0x11,
+ 0x03, 0xd3, 0x8c, 0x2a, 0x49, 0xb2, 0x80, 0xa5, 0x81, 0x21, 0x27,
+ 0x15, 0xfd, 0x90, 0x8d, 0x41, 0xc6, 0x51, 0xf5, 0xc7, 0x15, 0xca,
+ 0x38, 0xc0, 0xce, 0x28, 0x30, 0xa3, 0x7e, 0x00, 0xe5, 0x08, 0xce,
+ 0xd1, 0xbc, 0xdc, 0x32, 0x0e, 0x5e, 0x4d, 0x1e, 0x2e})),
+ std::make_pair(
+ 62,
+ std::vector<uint8_t>(
+ {0x5c, 0x6b, 0xbf, 0x16, 0xba, 0xa1, 0x80, 0xf9, 0x86, 0xbd, 0x40,
+ 0xa1, 0x28, 0x7e, 0xd4, 0xc5, 0x49, 0x77, 0x0e, 0x72, 0x84, 0x85,
+ 0x8f, 0xc4, 0x7b, 0xc2, 0x1a, 0xb9, 0x5e, 0xbb, 0xf3, 0x37, 0x4b,
+ 0x4e, 0xe3, 0xfd, 0x9f, 0x2a, 0xf6, 0x0f, 0x33, 0x95, 0x22, 0x1b,
+ 0x2a, 0xcc, 0x76, 0xf2, 0xd3, 0x4c, 0x13, 0x29, 0x54, 0x04, 0x9f,
+ 0x8a, 0x3a, 0x99, 0x6f, 0x1e, 0x32, 0xec, 0x84, 0xe5})),
+ std::make_pair(
+ 63,
+ std::vector<uint8_t>(
+ {0xd1, 0x0b, 0xf9, 0xa1, 0x5b, 0x1c, 0x9f, 0xc8, 0xd4, 0x1f, 0x89,
+ 0xbb, 0x14, 0x0b, 0xf0, 0xbe, 0x08, 0xd2, 0xf3, 0x66, 0x61, 0x76,
+ 0xd1, 0x3b, 0xaa, 0xc4, 0xd3, 0x81, 0x35, 0x8a, 0xd0, 0x74, 0xc9,
+ 0xd4, 0x74, 0x8c, 0x30, 0x05, 0x20, 0xeb, 0x02, 0x6d, 0xae, 0xae,
+ 0xa7, 0xc5, 0xb1, 0x58, 0x89, 0x2f, 0xde, 0x4e, 0x8e, 0xc1, 0x7d,
+ 0xc9, 0x98, 0xdc, 0xd5, 0x07, 0xdf, 0x26, 0xeb, 0x63})),
+ std::make_pair(
+ 64,
+ std::vector<uint8_t>(
+ {0x2f, 0xc6, 0xe6, 0x9f, 0xa2, 0x6a, 0x89, 0xa5, 0xed, 0x26, 0x90,
+ 0x92, 0xcb, 0x9b, 0x2a, 0x44, 0x9a, 0x44, 0x09, 0xa7, 0xa4, 0x40,
+ 0x11, 0xee, 0xca, 0xd1, 0x3d, 0x7c, 0x4b, 0x04, 0x56, 0x60, 0x2d,
+ 0x40, 0x2f, 0xa5, 0x84, 0x4f, 0x1a, 0x7a, 0x75, 0x81, 0x36, 0xce,
+ 0x3d, 0x5d, 0x8d, 0x0e, 0x8b, 0x86, 0x92, 0x1f, 0xff, 0xf4, 0xf6,
+ 0x92, 0xdd, 0x95, 0xbd, 0xc8, 0xe5, 0xff, 0x00, 0x52})),
+ std::make_pair(
+ 65,
+ std::vector<uint8_t>(
+ {0xfc, 0xbe, 0x8b, 0xe7, 0xdc, 0xb4, 0x9a, 0x32, 0xdb, 0xdf, 0x23,
+ 0x94, 0x59, 0xe2, 0x63, 0x08, 0xb8, 0x4d, 0xff, 0x1e, 0xa4, 0x80,
+ 0xdf, 0x8d, 0x10, 0x4e, 0xef, 0xf3, 0x4b, 0x46, 0xfa, 0xe9, 0x86,
+ 0x27, 0xb4, 0x50, 0xc2, 0x26, 0x7d, 0x48, 0xc0, 0x94, 0x6a, 0x69,
+ 0x7c, 0x5b, 0x59, 0x53, 0x14, 0x52, 0xac, 0x04, 0x84, 0xf1, 0xc8,
+ 0x4e, 0x3a, 0x33, 0xd0, 0xc3, 0x39, 0xbb, 0x2e, 0x28})),
+ std::make_pair(
+ 66,
+ std::vector<uint8_t>(
+ {0xa1, 0x90, 0x93, 0xa6, 0xe3, 0xbc, 0xf5, 0x95, 0x2f, 0x85, 0x0f,
+ 0x20, 0x30, 0xf6, 0x9b, 0x96, 0x06, 0xf1, 0x47, 0xf9, 0x0b, 0x8b,
+ 0xae, 0xe3, 0x36, 0x2d, 0xa7, 0x1d, 0x9f, 0x35, 0xb4, 0x4e, 0xf9,
+ 0xd8, 0xf0, 0xa7, 0x71, 0x2b, 0xa1, 0x87, 0x7f, 0xdd, 0xcd, 0x2d,
+ 0x8e, 0xa8, 0xf1, 0xe5, 0xa7, 0x73, 0xd0, 0xb7, 0x45, 0xd4, 0x72,
+ 0x56, 0x05, 0x98, 0x3a, 0x2d, 0xe9, 0x01, 0xf8, 0x03})),
+ std::make_pair(
+ 67,
+ std::vector<uint8_t>(
+ {0x3c, 0x20, 0x06, 0x42, 0x3f, 0x73, 0xe2, 0x68, 0xfa, 0x59, 0xd2,
+ 0x92, 0x03, 0x77, 0xeb, 0x29, 0xa4, 0xf9, 0xa8, 0xb4, 0x62, 0xbe,
+ 0x15, 0x98, 0x3e, 0xe3, 0xb8, 0x5a, 0xe8, 0xa7, 0x8e, 0x99, 0x26,
+ 0x33, 0x58, 0x1a, 0x90, 0x99, 0x89, 0x3b, 0x63, 0xdb, 0x30, 0x24,
+ 0x1c, 0x34, 0xf6, 0x43, 0x02, 0x7d, 0xc8, 0x78, 0x27, 0x9a, 0xf5,
+ 0x85, 0x0d, 0x7e, 0x2d, 0x4a, 0x26, 0x53, 0x07, 0x3a})),
+ std::make_pair(
+ 68,
+ std::vector<uint8_t>(
+ {0xd0, 0xf2, 0xf2, 0xe3, 0x78, 0x76, 0x53, 0xf7, 0x7c, 0xce, 0x2f,
+ 0xa2, 0x48, 0x35, 0x78, 0x5b, 0xbd, 0x0c, 0x43, 0x3f, 0xc7, 0x79,
+ 0x46, 0x5a, 0x11, 0x51, 0x49, 0x90, 0x5a, 0x9d, 0xd1, 0xcb, 0x82,
+ 0x7a, 0x62, 0x85, 0x06, 0xd4, 0x57, 0xfc, 0xf1, 0x24, 0xa0, 0xc2,
+ 0xae, 0xf9, 0xce, 0x2d, 0x2a, 0x0a, 0x0f, 0x63, 0x54, 0x55, 0x70,
+ 0xd8, 0x66, 0x7f, 0xf9, 0xe2, 0xeb, 0xa0, 0x73, 0x34})),
+ std::make_pair(
+ 69,
+ std::vector<uint8_t>(
+ {0x78, 0xa9, 0xfc, 0x04, 0x8e, 0x25, 0xc6, 0xdc, 0xb5, 0xde, 0x45,
+ 0x66, 0x7d, 0xe8, 0xff, 0xdd, 0x3a, 0x93, 0x71, 0x11, 0x41, 0xd5,
+ 0x94, 0xe9, 0xfa, 0x62, 0xa9, 0x59, 0x47, 0x5d, 0xa6, 0x07, 0x5e,
+ 0xa8, 0xf0, 0x91, 0x6e, 0x84, 0xe4, 0x5a, 0xd9, 0x11, 0xb7, 0x54,
+ 0x67, 0x07, 0x7e, 0xe5, 0x2d, 0x2c, 0x9a, 0xeb, 0xf4, 0xd5, 0x8f,
+ 0x20, 0xce, 0x4a, 0x3a, 0x00, 0x45, 0x8b, 0x05, 0xd4})),
+ std::make_pair(
+ 70,
+ std::vector<uint8_t>(
+ {0x45, 0x81, 0x3f, 0x44, 0x17, 0x69, 0xab, 0x6e, 0xd3, 0x7d, 0x34,
+ 0x9f, 0xf6, 0xe7, 0x22, 0x67, 0xd7, 0x6a, 0xe6, 0xbb, 0x3e, 0x3c,
+ 0x61, 0x2e, 0xc0, 0x5c, 0x6e, 0x02, 0xa1, 0x2a, 0xf5, 0xa3, 0x7c,
+ 0x91, 0x8b, 0x52, 0xbf, 0x74, 0x26, 0x7c, 0x3f, 0x6a, 0x3f, 0x18,
+ 0x3a, 0x80, 0x64, 0xff, 0x84, 0xc0, 0x7b, 0x19, 0x3d, 0x08, 0x06,
+ 0x67, 0x89, 0xa0, 0x1a, 0xcc, 0xdb, 0x6f, 0x93, 0x40})),
+ std::make_pair(
+ 71,
+ std::vector<uint8_t>(
+ {0x95, 0x6d, 0xa1, 0xc6, 0x8d, 0x83, 0xa7, 0xb8, 0x81, 0xe0, 0x1b,
+ 0x9a, 0x96, 0x6c, 0x3c, 0x0b, 0xf2, 0x7f, 0x68, 0x60, 0x6a, 0x8b,
+ 0x71, 0xd4, 0x57, 0xbd, 0x01, 0x6d, 0x4c, 0x41, 0xdd, 0x8a, 0x38,
+ 0x0c, 0x70, 0x9a, 0x29, 0x6c, 0xb4, 0xc6, 0x54, 0x47, 0x92, 0x92,
+ 0x0f, 0xd7, 0x88, 0x83, 0x57, 0x71, 0xa0, 0x7d, 0x4a, 0x16, 0xfb,
+ 0x52, 0xed, 0x48, 0x05, 0x03, 0x31, 0xdc, 0x4c, 0x8b})),
+ std::make_pair(
+ 72,
+ std::vector<uint8_t>(
+ {0xdf, 0x18, 0x6c, 0x2d, 0xc0, 0x9c, 0xaa, 0x48, 0xe1, 0x4e, 0x94,
+ 0x2f, 0x75, 0xde, 0x5a, 0xc1, 0xb7, 0xa2, 0x1e, 0x4f, 0x9f, 0x07,
+ 0x2a, 0x5b, 0x37, 0x1e, 0x09, 0xe0, 0x73, 0x45, 0xb0, 0x74, 0x0c,
+ 0x76, 0x17, 0x7b, 0x01, 0x27, 0x88, 0x08, 0xfe, 0xc0, 0x25, 0xed,
+ 0xed, 0x98, 0x22, 0xc1, 0x22, 0xaf, 0xd1, 0xc6, 0x3e, 0x6f, 0x0c,
+ 0xe2, 0xe3, 0x26, 0x31, 0x04, 0x10, 0x63, 0x14, 0x5c})),
+ std::make_pair(
+ 73,
+ std::vector<uint8_t>(
+ {0x87, 0x47, 0x56, 0x40, 0x96, 0x6a, 0x9f, 0xdc, 0xd6, 0xd3, 0xa3,
+ 0xb5, 0xa2, 0xcc, 0xa5, 0xc0, 0x8f, 0x0d, 0x88, 0x2b, 0x10, 0x24,
+ 0x3c, 0x0e, 0xc1, 0xbf, 0x3c, 0x6b, 0x1c, 0x37, 0xf2, 0xcd, 0x32,
+ 0x12, 0xf1, 0x9a, 0x05, 0x78, 0x64, 0x47, 0x7d, 0x5e, 0xaf, 0x8f,
+ 0xae, 0xd7, 0x3f, 0x29, 0x37, 0xc7, 0x68, 0xa0, 0xaf, 0x41, 0x5e,
+ 0x84, 0xbb, 0xce, 0x6b, 0xd7, 0xde, 0x23, 0xb6, 0x60})),
+ std::make_pair(
+ 74,
+ std::vector<uint8_t>(
+ {0xc3, 0xb5, 0x73, 0xbb, 0xe1, 0x09, 0x49, 0xa0, 0xfb, 0xd4, 0xff,
+ 0x88, 0x4c, 0x44, 0x6f, 0x22, 0x29, 0xb7, 0x69, 0x02, 0xf9, 0xdf,
+ 0xdb, 0xb8, 0xa0, 0x35, 0x3d, 0xa5, 0xc8, 0x3c, 0xa1, 0x4e, 0x81,
+ 0x51, 0xbb, 0xaa, 0xc8, 0x2f, 0xd1, 0x57, 0x6a, 0x00, 0x9a, 0xdc,
+ 0x6f, 0x19, 0x35, 0xcf, 0x26, 0xed, 0xd4, 0xf1, 0xfb, 0x8d, 0xa4,
+ 0x83, 0xe6, 0xc5, 0xcd, 0x9d, 0x89, 0x23, 0xad, 0xc3})),
+ std::make_pair(
+ 75,
+ std::vector<uint8_t>(
+ {0xb0, 0x9d, 0x8d, 0x0b, 0xba, 0x8a, 0x72, 0x86, 0xe4, 0x35, 0x68,
+ 0xf7, 0x90, 0x75, 0x50, 0xe4, 0x20, 0x36, 0xd6, 0x74, 0xe3, 0xc8,
+ 0xfc, 0x34, 0xd8, 0xca, 0x46, 0xf7, 0x71, 0xd6, 0x46, 0x6b, 0x70,
+ 0xfb, 0x60, 0x58, 0x75, 0xf6, 0xa8, 0x63, 0xc8, 0x77, 0xd1, 0x2f,
+ 0x07, 0x06, 0x3f, 0xdc, 0x2e, 0x90, 0xcc, 0xd4, 0x59, 0xb1, 0x91,
+ 0x0d, 0xcd, 0x52, 0xd8, 0xf1, 0x0b, 0x2b, 0x0a, 0x15})),
+ std::make_pair(
+ 76,
+ std::vector<uint8_t>(
+ {0xaf, 0x3a, 0x22, 0xbf, 0x75, 0xb2, 0x1a, 0xbf, 0xb0, 0xac, 0xd5,
+ 0x44, 0x22, 0xba, 0x1b, 0x73, 0x00, 0xa9, 0x52, 0xef, 0xf0, 0x2e,
+ 0xbe, 0xb6, 0x5b, 0x5c, 0x23, 0x44, 0x71, 0xa9, 0x8d, 0xf3, 0x2f,
+ 0x4f, 0x96, 0x43, 0xce, 0x19, 0x04, 0x10, 0x8a, 0x16, 0x87, 0x67,
+ 0x92, 0x42, 0x80, 0xbd, 0x76, 0xc8, 0x3f, 0x8c, 0x82, 0xd9, 0xa7,
+ 0x9d, 0x92, 0x59, 0xb1, 0x95, 0x36, 0x2a, 0x2a, 0x04})),
+ std::make_pair(
+ 77,
+ std::vector<uint8_t>(
+ {0xbf, 0x4f, 0xf2, 0x22, 0x1b, 0x7e, 0x69, 0x57, 0xa7, 0x24, 0xcd,
+ 0x96, 0x4a, 0xa3, 0xd5, 0xd0, 0xd9, 0x94, 0x1f, 0x54, 0x04, 0x13,
+ 0x75, 0x2f, 0x46, 0x99, 0xd8, 0x10, 0x1b, 0x3e, 0x53, 0x75, 0x08,
+ 0xbf, 0x09, 0xf8, 0x50, 0x8b, 0x31, 0x77, 0x36, 0xff, 0xd2, 0x65,
+ 0xf2, 0x84, 0x7a, 0xa7, 0xd8, 0x4b, 0xd2, 0xd9, 0x75, 0x69, 0xc4,
+ 0x9d, 0x63, 0x2a, 0xed, 0x99, 0x45, 0xe5, 0xfa, 0x5e})),
+ std::make_pair(
+ 78,
+ std::vector<uint8_t>(
+ {0x9c, 0x6b, 0x6b, 0x78, 0x19, 0x9b, 0x1b, 0xda, 0xcb, 0x43, 0x00,
+ 0xe3, 0x14, 0x79, 0xfa, 0x62, 0x2a, 0x6b, 0x5b, 0xc8, 0x0d, 0x46,
+ 0x78, 0xa6, 0x07, 0x8f, 0x88, 0xa8, 0x26, 0x8c, 0xd7, 0x20, 0x6a,
+ 0x27, 0x99, 0xe8, 0xd4, 0x62, 0x1a, 0x46, 0x4e, 0xf6, 0xb4, 0x3d,
+ 0xd8, 0xad, 0xff, 0xe9, 0x7c, 0xaf, 0x22, 0x1b, 0x22, 0xb6, 0xb8,
+ 0x77, 0x8b, 0x14, 0x9a, 0x82, 0x2a, 0xef, 0xbb, 0x09})),
+ std::make_pair(
+ 79,
+ std::vector<uint8_t>(
+ {0x89, 0x06, 0x56, 0xf0, 0x9c, 0x99, 0xd2, 0x80, 0xb5, 0xec, 0xb3,
+ 0x81, 0xf5, 0x64, 0x27, 0xb8, 0x13, 0x75, 0x1b, 0xc6, 0x52, 0xc7,
+ 0x82, 0x80, 0x78, 0xb2, 0x3a, 0x4a, 0xf8, 0x3b, 0x4e, 0x3a, 0x61,
+ 0xfd, 0xba, 0xc6, 0x1f, 0x89, 0xbe, 0xe8, 0x4e, 0xa6, 0xbe, 0xe7,
+ 0x60, 0xc0, 0x47, 0xf2, 0x5c, 0x6b, 0x0a, 0x20, 0x1c, 0x69, 0xa3,
+ 0x8f, 0xd6, 0xfd, 0x97, 0x1a, 0xf1, 0x85, 0x88, 0xbb})),
+ std::make_pair(
+ 80,
+ std::vector<uint8_t>(
+ {0x31, 0xa0, 0x46, 0xf7, 0x88, 0x2f, 0xfe, 0x6f, 0x83, 0xce, 0x47,
+ 0x2e, 0x9a, 0x07, 0x01, 0x83, 0x2e, 0xc7, 0xb3, 0xf7, 0x6f, 0xbc,
+ 0xfd, 0x1d, 0xf6, 0x0f, 0xe3, 0xea, 0x48, 0xfd, 0xe1, 0x65, 0x12,
+ 0x54, 0x24, 0x7c, 0x3f, 0xd9, 0x5e, 0x10, 0x0f, 0x91, 0x72, 0x73,
+ 0x1e, 0x17, 0xfd, 0x52, 0x97, 0xc1, 0x1f, 0x4b, 0xb3, 0x28, 0x36,
+ 0x3c, 0xa3, 0x61, 0x62, 0x4a, 0x81, 0xaf, 0x79, 0x7c})),
+ std::make_pair(
+ 81,
+ std::vector<uint8_t>(
+ {0x27, 0xa6, 0x0b, 0x2d, 0x00, 0xe7, 0xa6, 0x71, 0xd4, 0x7d, 0x0a,
+ 0xec, 0x2a, 0x68, 0x6a, 0x0a, 0xc0, 0x4b, 0x52, 0xf4, 0x0a, 0xb6,
+ 0x62, 0x90, 0x28, 0xeb, 0x7d, 0x13, 0xf4, 0xba, 0xa9, 0x9a, 0xc0,
+ 0xfe, 0x46, 0xee, 0x6c, 0x81, 0x49, 0x44, 0xf2, 0xf4, 0xb4, 0xd2,
+ 0x0e, 0x93, 0x78, 0xe4, 0x84, 0x7e, 0xa4, 0x4c, 0x13, 0x17, 0x80,
+ 0x91, 0xe2, 0x77, 0xb8, 0x7e, 0xa7, 0xa5, 0x57, 0x11})),
+ std::make_pair(
+ 82,
+ std::vector<uint8_t>(
+ {0x8b, 0x5c, 0xce, 0xf1, 0x94, 0x16, 0x2c, 0x1f, 0x19, 0xd6, 0x8f,
+ 0x91, 0xe0, 0xb0, 0x92, 0x8f, 0x28, 0x9e, 0xc5, 0x28, 0x37, 0x20,
+ 0x84, 0x0c, 0x2f, 0x73, 0xd2, 0x53, 0x11, 0x12, 0x38, 0xdc, 0xfe,
+ 0x94, 0xaf, 0x2b, 0x59, 0xc2, 0xc1, 0xca, 0x25, 0x91, 0x90, 0x1a,
+ 0x7b, 0xc0, 0x60, 0xe7, 0x45, 0x9b, 0x6c, 0x47, 0xdf, 0x0f, 0x71,
+ 0x70, 0x1a, 0x35, 0xcc, 0x0a, 0xa8, 0x31, 0xb5, 0xb6})),
+ std::make_pair(
+ 83,
+ std::vector<uint8_t>(
+ {0x57, 0xab, 0x6c, 0x4b, 0x22, 0x29, 0xae, 0xb3, 0xb7, 0x04, 0x76,
+ 0xd8, 0x03, 0xcd, 0x63, 0x81, 0x2f, 0x10, 0x7c, 0xe6, 0xda, 0x17,
+ 0xfe, 0xd9, 0xb1, 0x78, 0x75, 0xe8, 0xf8, 0x6c, 0x72, 0x4f, 0x49,
+ 0xe0, 0x24, 0xcb, 0xf3, 0xa1, 0xb8, 0xb1, 0x19, 0xc5, 0x03, 0x57,
+ 0x65, 0x2b, 0x81, 0x87, 0x9d, 0x2a, 0xde, 0x2d, 0x58, 0x8b, 0x9e,
+ 0x4f, 0x7c, 0xed, 0xba, 0x0e, 0x46, 0x44, 0xc9, 0xee})),
+ std::make_pair(
+ 84,
+ std::vector<uint8_t>(
+ {0x01, 0x90, 0xa8, 0xda, 0xc3, 0x20, 0xa7, 0x39, 0xf3, 0x22, 0xe1,
+ 0x57, 0x31, 0xaa, 0x14, 0x0d, 0xda, 0xf5, 0xbe, 0xd2, 0x94, 0xd5,
+ 0xc8, 0x2e, 0x54, 0xfe, 0xf2, 0x9f, 0x21, 0x4e, 0x18, 0xaa, 0xfa,
+ 0xa8, 0x4f, 0x8b, 0xe9, 0x9a, 0xf6, 0x29, 0x50, 0x26, 0x6b, 0x8f,
+ 0x90, 0x1f, 0x15, 0xdd, 0x4c, 0x5d, 0x35, 0x51, 0x6f, 0xc3, 0x5b,
+ 0x4c, 0xab, 0x2e, 0x96, 0xe4, 0x69, 0x5b, 0xbe, 0x1c})),
+ std::make_pair(
+ 85,
+ std::vector<uint8_t>(
+ {0xd1, 0x4d, 0x7c, 0x4c, 0x41, 0x5e, 0xeb, 0x0e, 0x10, 0xb1, 0x59,
+ 0x22, 0x4b, 0xea, 0x12, 0x7e, 0xbd, 0x84, 0xf9, 0x59, 0x1c, 0x70,
+ 0x2a, 0x33, 0x0f, 0x5b, 0xb7, 0xbb, 0x7a, 0xa4, 0x4e, 0xa3, 0x9d,
+ 0xe6, 0xed, 0x01, 0xf1, 0x8d, 0xa7, 0xad, 0xf4, 0x0c, 0xfb, 0x97,
+ 0xc5, 0xd1, 0x52, 0xc2, 0x75, 0x28, 0x82, 0x4b, 0x21, 0xe2, 0x39,
+ 0x52, 0x6a, 0xf8, 0xf3, 0x6b, 0x21, 0x4e, 0x0c, 0xfb})),
+ std::make_pair(
+ 86,
+ std::vector<uint8_t>(
+ {0xbe, 0x28, 0xc4, 0xbe, 0x70, 0x69, 0x70, 0x48, 0x8f, 0xac, 0x7d,
+ 0x29, 0xc3, 0xbd, 0x5c, 0x4e, 0x98, 0x60, 0x85, 0xc4, 0xc3, 0x33,
+ 0x2f, 0x1f, 0x3f, 0xd3, 0x09, 0x73, 0xdb, 0x61, 0x41, 0x64, 0xba,
+ 0x2f, 0x31, 0xa7, 0x88, 0x75, 0xff, 0xdc, 0x15, 0x03, 0x25, 0xc8,
+ 0x83, 0x27, 0xa9, 0x44, 0x3e, 0xd0, 0x4f, 0xdf, 0xe5, 0xbe, 0x93,
+ 0x87, 0x6d, 0x16, 0x28, 0x56, 0x0c, 0x76, 0x4a, 0x80})),
+ std::make_pair(
+ 87,
+ std::vector<uint8_t>(
+ {0x03, 0x1d, 0xa1, 0x06, 0x9e, 0x3a, 0x2e, 0x9c, 0x33, 0x82, 0xe4,
+ 0x36, 0xff, 0xd7, 0x9d, 0xf7, 0x4b, 0x1c, 0xa6, 0xa8, 0xad, 0xb2,
+ 0xde, 0xab, 0xe6, 0x76, 0xab, 0x45, 0x99, 0x4c, 0xbc, 0x05, 0x4f,
+ 0x03, 0x7d, 0x2f, 0x0e, 0xac, 0xe8, 0x58, 0xd3, 0x2c, 0x14, 0xe2,
+ 0xd1, 0xc8, 0xb4, 0x60, 0x77, 0x30, 0x8e, 0x3b, 0xdc, 0x2c, 0x1b,
+ 0x53, 0x17, 0x2e, 0xcf, 0x7a, 0x8c, 0x14, 0xe3, 0x49})),
+ std::make_pair(
+ 88,
+ std::vector<uint8_t>(
+ {0x46, 0x65, 0xce, 0xf8, 0xba, 0x4d, 0xb4, 0xd0, 0xac, 0xb1, 0x18,
+ 0xf2, 0x98, 0x7f, 0x0b, 0xb0, 0x9f, 0x8f, 0x86, 0xaa, 0x44, 0x5a,
+ 0xa3, 0xd5, 0xfc, 0x9a, 0x8b, 0x34, 0x68, 0x64, 0x78, 0x74, 0x89,
+ 0xe8, 0xfc, 0xec, 0xc1, 0x25, 0xd1, 0x7e, 0x9b, 0x56, 0xe1, 0x29,
+ 0x88, 0xea, 0xc5, 0xec, 0xc7, 0x28, 0x68, 0x83, 0xdb, 0x06, 0x61,
+ 0xb8, 0xff, 0x05, 0xda, 0x2a, 0xff, 0xf3, 0x0f, 0xe4})),
+ std::make_pair(
+ 89,
+ std::vector<uint8_t>(
+ {0x63, 0xb7, 0x03, 0x2e, 0x5f, 0x93, 0x0c, 0xc9, 0x93, 0x95, 0x17,
+ 0xf9, 0xe9, 0x86, 0x81, 0x6c, 0xfb, 0xec, 0x2b, 0xe5, 0x9b, 0x95,
+ 0x68, 0xb1, 0x3f, 0x2e, 0xad, 0x05, 0xba, 0xe7, 0x77, 0x7c, 0xab,
+ 0x62, 0x0c, 0x66, 0x59, 0x40, 0x4f, 0x74, 0x09, 0xe4, 0x19, 0x9a,
+ 0x3b, 0xe5, 0xf7, 0x86, 0x5a, 0xa7, 0xcb, 0xdf, 0x8c, 0x42, 0x53,
+ 0xf7, 0xe8, 0x21, 0x9b, 0x1b, 0xd5, 0xf4, 0x6f, 0xea})),
+ std::make_pair(
+ 90,
+ std::vector<uint8_t>(
+ {0x9f, 0x09, 0xbf, 0x09, 0x3a, 0x2b, 0x0f, 0xf8, 0xc2, 0x63, 0x4b,
+ 0x49, 0xe3, 0x7f, 0x1b, 0x21, 0x35, 0xb4, 0x47, 0xaa, 0x91, 0x44,
+ 0xc9, 0x78, 0x7d, 0xbf, 0xd9, 0x21, 0x29, 0x31, 0x6c, 0x99, 0xe8,
+ 0x8a, 0xab, 0x8a, 0x21, 0xfd, 0xef, 0x23, 0x72, 0xd1, 0x18, 0x9a,
+ 0xec, 0x50, 0x0f, 0x95, 0x77, 0x5f, 0x1f, 0x92, 0xbf, 0xb4, 0x55,
+ 0x45, 0xe4, 0x25, 0x9f, 0xb9, 0xb7, 0xb0, 0x2d, 0x14})),
+ std::make_pair(
+ 91,
+ std::vector<uint8_t>(
+ {0xf9, 0xf8, 0x49, 0x3c, 0x68, 0x08, 0x88, 0x07, 0xdf, 0x7f, 0x6a,
+ 0x26, 0x93, 0xd6, 0x4e, 0xa5, 0x9f, 0x03, 0xe9, 0xe0, 0x5a, 0x22,
+ 0x3e, 0x68, 0x52, 0x4c, 0xa3, 0x21, 0x95, 0xa4, 0x73, 0x4b, 0x65,
+ 0x4f, 0xce, 0xa4, 0xd2, 0x73, 0x4c, 0x86, 0x6c, 0xf9, 0x5c, 0x88,
+ 0x9f, 0xb1, 0x0c, 0x49, 0x15, 0x9b, 0xe2, 0xf5, 0x04, 0x3d, 0xc9,
+ 0x8b, 0xb5, 0x5e, 0x02, 0xef, 0x7b, 0xdc, 0xb0, 0x82})),
+ std::make_pair(
+ 92,
+ std::vector<uint8_t>(
+ {0x3c, 0x9a, 0x73, 0x59, 0xab, 0x4f, 0xeb, 0xce, 0x07, 0xb2, 0x0a,
+ 0xc4, 0x47, 0xb0, 0x6a, 0x24, 0x0b, 0x7f, 0xe1, 0xda, 0xe5, 0x43,
+ 0x9c, 0x49, 0xb6, 0x0b, 0x58, 0x19, 0xf7, 0x81, 0x2e, 0x4c, 0x17,
+ 0x24, 0x06, 0xc1, 0xaa, 0xc3, 0x16, 0x71, 0x3c, 0xf0, 0xdd, 0xed,
+ 0x10, 0x38, 0x07, 0x72, 0x58, 0xe2, 0xef, 0xf5, 0xb3, 0x39, 0x13,
+ 0xd9, 0xd9, 0x5c, 0xae, 0xb4, 0xe6, 0xc6, 0xb9, 0x70})),
+ std::make_pair(
+ 93,
+ std::vector<uint8_t>(
+ {0xad, 0x6a, 0xab, 0x80, 0x84, 0x51, 0x0e, 0x82, 0x2c, 0xfc, 0xe8,
+ 0x62, 0x5d, 0x62, 0xcf, 0x4d, 0xe6, 0x55, 0xf4, 0x76, 0x38, 0x84,
+ 0xc7, 0x1e, 0x80, 0xba, 0xb9, 0xac, 0x9d, 0x53, 0x18, 0xdb, 0xa4,
+ 0xa6, 0x03, 0x3e, 0xd2, 0x90, 0x84, 0xe6, 0x52, 0x16, 0xc0, 0x31,
+ 0x60, 0x6c, 0xa1, 0x76, 0x15, 0xdc, 0xfe, 0x3b, 0xa1, 0x1d, 0x26,
+ 0x85, 0x1a, 0xe0, 0x99, 0x9c, 0xa6, 0xe2, 0x32, 0xcf})),
+ std::make_pair(
+ 94,
+ std::vector<uint8_t>(
+ {0x15, 0x6e, 0x9e, 0x62, 0x61, 0x37, 0x4c, 0x9d, 0xc8, 0x84, 0xf3,
+ 0x6e, 0x70, 0xf0, 0xfe, 0x1a, 0xb9, 0x29, 0x79, 0x97, 0xb8, 0x36,
+ 0xfa, 0x7d, 0x17, 0x0a, 0x9c, 0x9e, 0xbf, 0x57, 0x5b, 0x88, 0x1e,
+ 0x7b, 0xce, 0xa4, 0x4d, 0x6c, 0x02, 0x48, 0xd3, 0x55, 0x97, 0x90,
+ 0x71, 0x54, 0x82, 0x89, 0x55, 0xbe, 0x19, 0x13, 0x58, 0x52, 0xf9,
+ 0x22, 0x88, 0x15, 0xec, 0xa0, 0x24, 0xa8, 0xad, 0xfb})),
+ std::make_pair(
+ 95,
+ std::vector<uint8_t>(
+ {0x42, 0x15, 0x40, 0x76, 0x33, 0xf4, 0xcc, 0xa9, 0xb6, 0x78, 0x8b,
+ 0xe9, 0x3e, 0x6a, 0xa3, 0xd9, 0x63, 0xc7, 0xd6, 0xce, 0x4b, 0x14,
+ 0x72, 0x47, 0x09, 0x9f, 0x46, 0xa3, 0xac, 0xb5, 0x00, 0xa3, 0x00,
+ 0x38, 0xcb, 0x3e, 0x78, 0x8c, 0x3d, 0x29, 0xf1, 0x32, 0xad, 0x84,
+ 0x4e, 0x80, 0xe9, 0xe9, 0x92, 0x51, 0xf6, 0xdb, 0x96, 0xac, 0xd8,
+ 0xa0, 0x91, 0xcf, 0xc7, 0x70, 0xaf, 0x53, 0x84, 0x7b})),
+ std::make_pair(
+ 96,
+ std::vector<uint8_t>(
+ {0x1c, 0x07, 0x7e, 0x27, 0x9d, 0xe6, 0x54, 0x85, 0x23, 0x50, 0x2b,
+ 0x6d, 0xf8, 0x00, 0xff, 0xda, 0xb5, 0xe2, 0xc3, 0xe9, 0x44, 0x2e,
+ 0xb8, 0x38, 0xf5, 0x8c, 0x29, 0x5f, 0x3b, 0x14, 0x7c, 0xef, 0x9d,
+ 0x70, 0x1c, 0x41, 0xc3, 0x21, 0x28, 0x3f, 0x00, 0xc7, 0x1a, 0xff,
+ 0xa0, 0x61, 0x93, 0x10, 0x39, 0x91, 0x26, 0x29, 0x5b, 0x78, 0xdd,
+ 0x4d, 0x1a, 0x74, 0x57, 0x2e, 0xf9, 0xed, 0x51, 0x35})),
+ std::make_pair(
+ 97,
+ std::vector<uint8_t>(
+ {0xf0, 0x7a, 0x55, 0x5f, 0x49, 0xfe, 0x48, 0x1c, 0xf4, 0xcd, 0x0a,
+ 0x87, 0xb7, 0x1b, 0x82, 0xe4, 0xa9, 0x50, 0x64, 0xd0, 0x66, 0x77,
+ 0xfd, 0xd9, 0x0a, 0x0e, 0xb5, 0x98, 0x87, 0x7b, 0xa1, 0xc8, 0x3d,
+ 0x46, 0x77, 0xb3, 0x93, 0xc3, 0xa3, 0xb6, 0x66, 0x1c, 0x42, 0x1f,
+ 0x5b, 0x12, 0xcb, 0x99, 0xd2, 0x03, 0x76, 0xba, 0x72, 0x75, 0xc2,
+ 0xf3, 0xa8, 0xf5, 0xa9, 0xb7, 0x82, 0x17, 0x20, 0xda})),
+ std::make_pair(
+ 98,
+ std::vector<uint8_t>(
+ {0xb5, 0x91, 0x1b, 0x38, 0x0d, 0x20, 0xc7, 0xb0, 0x43, 0x23, 0xe4,
+ 0x02, 0x6b, 0x38, 0xe2, 0x00, 0xf5, 0x34, 0x25, 0x92, 0x33, 0xb5,
+ 0x81, 0xe0, 0x2c, 0x1e, 0x3e, 0x2d, 0x84, 0x38, 0xd6, 0xc6, 0x6d,
+ 0x5a, 0x4e, 0xb2, 0x01, 0xd5, 0xa8, 0xb7, 0x50, 0x72, 0xc4, 0xec,
+ 0x29, 0x10, 0x63, 0x34, 0xda, 0x70, 0xbc, 0x79, 0x52, 0x1b, 0x0c,
+ 0xed, 0x2c, 0xfd, 0x53, 0x3f, 0x5f, 0xf8, 0x4f, 0x95})),
+ std::make_pair(
+ 99,
+ std::vector<uint8_t>(
+ {0x01, 0xf0, 0x70, 0xa0, 0x9b, 0xae, 0x91, 0x12, 0x96, 0x36, 0x1f,
+ 0x91, 0xaa, 0x0e, 0x8e, 0x0d, 0x09, 0xa7, 0x72, 0x54, 0x78, 0x53,
+ 0x6d, 0x9d, 0x48, 0xc5, 0xfe, 0x1e, 0x5e, 0x7c, 0x3c, 0x5b, 0x9b,
+ 0x9d, 0x6e, 0xb0, 0x77, 0x96, 0xf6, 0xda, 0x57, 0xae, 0x56, 0x2a,
+ 0x7d, 0x70, 0xe8, 0x82, 0xe3, 0x7a, 0xdf, 0xde, 0x83, 0xf0, 0xc4,
+ 0x33, 0xc2, 0xcd, 0x36, 0x35, 0x36, 0xbb, 0x22, 0xc8})),
+ std::make_pair(
+ 100,
+ std::vector<uint8_t>(
+ {0x6f, 0x79, 0x3e, 0xb4, 0x37, 0x4a, 0x48, 0xb0, 0x77, 0x5a, 0xca,
+ 0xf9, 0xad, 0xcf, 0x8e, 0x45, 0xe5, 0x42, 0x70, 0xc9, 0x47, 0x5f,
+ 0x00, 0x4a, 0xd8, 0xd5, 0x97, 0x3e, 0x2a, 0xca, 0x52, 0x74, 0x7f,
+ 0xf4, 0xed, 0x04, 0xae, 0x96, 0x72, 0x75, 0xb9, 0xf9, 0xeb, 0x0e,
+ 0x1f, 0xf7, 0x5f, 0xb4, 0xf7, 0x94, 0xfa, 0x8b, 0xe9, 0xad, 0xd7,
+ 0xa4, 0x13, 0x04, 0x86, 0x8d, 0x10, 0x3f, 0xab, 0x10})),
+ std::make_pair(
+ 101,
+ std::vector<uint8_t>(
+ {0x96, 0x5f, 0x20, 0xf1, 0x39, 0x76, 0x5f, 0xcc, 0x4c, 0xe4, 0xba,
+ 0x37, 0x94, 0x67, 0x58, 0x63, 0xca, 0xc2, 0x4d, 0xb4, 0x72, 0xcd,
+ 0x2b, 0x79, 0x9d, 0x03, 0x5b, 0xce, 0x3d, 0xbe, 0xa5, 0x02, 0xda,
+ 0x7b, 0x52, 0x48, 0x65, 0xf6, 0xb8, 0x11, 0xd8, 0xc5, 0x82, 0x8d,
+ 0x3a, 0x88, 0x96, 0x46, 0xfe, 0x64, 0xa3, 0x80, 0xda, 0x1a, 0xa7,
+ 0xc7, 0x04, 0x4e, 0x9f, 0x24, 0x5d, 0xce, 0xd1, 0x28})),
+ std::make_pair(
+ 102,
+ std::vector<uint8_t>(
+ {0xec, 0x29, 0x5b, 0x57, 0x83, 0x60, 0x12, 0x44, 0xc3, 0x0e, 0x46,
+ 0x41, 0xe3, 0xb4, 0x5b, 0xe2, 0x22, 0xc4, 0xdc, 0xe7, 0x7a, 0x58,
+ 0x70, 0x0f, 0x53, 0xbc, 0x8e, 0xc5, 0x2a, 0x94, 0x16, 0x90, 0xb4,
+ 0xd0, 0xb0, 0x87, 0xfb, 0x6f, 0xcb, 0x3f, 0x39, 0x83, 0x2b, 0x9d,
+ 0xe8, 0xf7, 0x5e, 0xc2, 0x0b, 0xd4, 0x30, 0x79, 0x81, 0x17, 0x49,
+ 0xcd, 0xc9, 0x07, 0xed, 0xb9, 0x41, 0x57, 0xd1, 0x80})),
+ std::make_pair(
+ 103,
+ std::vector<uint8_t>(
+ {0x61, 0xc7, 0x2f, 0x8c, 0xcc, 0x91, 0xdb, 0xb5, 0x4c, 0xa6, 0x75,
+ 0x0b, 0xc4, 0x89, 0x67, 0x2d, 0xe0, 0x9f, 0xae, 0xdb, 0x8f, 0xdd,
+ 0x4f, 0x94, 0xff, 0x23, 0x20, 0x90, 0x9a, 0x30, 0x3f, 0x5d, 0x5a,
+ 0x98, 0x48, 0x1c, 0x0b, 0xc1, 0xa6, 0x25, 0x41, 0x9f, 0xb4, 0xde,
+ 0xbf, 0xbf, 0x7f, 0x8a, 0x53, 0xbb, 0x07, 0xec, 0x3d, 0x98, 0x5e,
+ 0x8e, 0xa1, 0x1e, 0x72, 0xd5, 0x59, 0x94, 0x07, 0x80})),
+ std::make_pair(
+ 104,
+ std::vector<uint8_t>(
+ {0xaf, 0xd8, 0x14, 0x5b, 0x25, 0x9e, 0xef, 0xc8, 0xd1, 0x26, 0x20,
+ 0xc3, 0xc5, 0xb0, 0x3e, 0x1e, 0xd8, 0xfd, 0x2c, 0xce, 0xfe, 0x03,
+ 0x65, 0x07, 0x8c, 0x80, 0xfd, 0x42, 0xc1, 0x77, 0x0e, 0x28, 0xb4,
+ 0x49, 0x48, 0xf2, 0x7e, 0x65, 0xa1, 0x88, 0x66, 0x90, 0x11, 0x0d,
+ 0xb8, 0x14, 0x39, 0x7b, 0x68, 0xe4, 0x3d, 0x80, 0xd1, 0xba, 0x16,
+ 0xdf, 0xa3, 0x58, 0xe7, 0x39, 0xc8, 0x98, 0xcf, 0xa3})),
+ std::make_pair(
+ 105,
+ std::vector<uint8_t>(
+ {0x55, 0x2f, 0xc7, 0x89, 0x3c, 0xf1, 0xce, 0x93, 0x3a, 0xda, 0x35,
+ 0xc0, 0xda, 0x98, 0x84, 0x4e, 0x41, 0x54, 0x5e, 0x24, 0x4c, 0x31,
+ 0x57, 0xa1, 0x42, 0x8d, 0x7b, 0x4c, 0x21, 0xf9, 0xcd, 0x7e, 0x40,
+ 0x71, 0xae, 0xd7, 0x7b, 0x7c, 0xa9, 0xf1, 0xc3, 0x8f, 0xba, 0x32,
+ 0x23, 0x74, 0x12, 0xef, 0x21, 0xa3, 0x42, 0x74, 0x2e, 0xc8, 0x32,
+ 0x43, 0x78, 0xf2, 0x1e, 0x50, 0x7f, 0xaf, 0xdd, 0x88})),
+ std::make_pair(
+ 106,
+ std::vector<uint8_t>(
+ {0x46, 0x7a, 0x33, 0xfb, 0xad, 0xf5, 0xeb, 0xc5, 0x25, 0x96, 0xef,
+ 0x86, 0xaa, 0xae, 0xfc, 0x6f, 0xab, 0xa8, 0xee, 0x65, 0x1b, 0x1c,
+ 0xe0, 0x4d, 0xe3, 0x68, 0xa0, 0x3a, 0x5a, 0x90, 0x40, 0xef, 0x28,
+ 0x35, 0xe0, 0x0a, 0xdb, 0x09, 0xab, 0xb3, 0xfb, 0xd2, 0xbc, 0xe8,
+ 0x18, 0xa2, 0x41, 0x3d, 0x0b, 0x02, 0x53, 0xb5, 0xbd, 0xa4, 0xfc,
+ 0x5b, 0x2f, 0x6f, 0x85, 0xf3, 0xfd, 0x5b, 0x55, 0xf2})),
+ std::make_pair(
+ 107,
+ std::vector<uint8_t>(
+ {0x22, 0xef, 0xf8, 0xe6, 0xdd, 0x52, 0x36, 0xf5, 0xf5, 0x7d, 0x94,
+ 0xed, 0xe8, 0x74, 0xd6, 0xc9, 0x42, 0x8e, 0x8f, 0x5d, 0x56, 0x6f,
+ 0x17, 0xcd, 0x6d, 0x18, 0x48, 0xcd, 0x75, 0x2f, 0xe1, 0x3c, 0x65,
+ 0x5c, 0xb1, 0x0f, 0xba, 0xaf, 0xf7, 0x68, 0x72, 0xf2, 0xbf, 0x2d,
+ 0xa9, 0x9e, 0x15, 0xdc, 0x62, 0x40, 0x75, 0xe1, 0xec, 0x2f, 0x58,
+ 0xa3, 0xf6, 0x40, 0x72, 0x12, 0x18, 0x38, 0x56, 0x9e})),
+ std::make_pair(
+ 108,
+ std::vector<uint8_t>(
+ {0x9c, 0xec, 0x6b, 0xbf, 0x62, 0xc4, 0xbc, 0xe4, 0x13, 0x8a, 0xba,
+ 0xe1, 0xcb, 0xec, 0x8d, 0xad, 0x31, 0x95, 0x04, 0x44, 0xe9, 0x03,
+ 0x21, 0xb1, 0x34, 0x71, 0x96, 0x83, 0x4c, 0x11, 0x4b, 0x86, 0x4a,
+ 0xf3, 0xf3, 0xcc, 0x35, 0x08, 0xf8, 0x37, 0x51, 0xff, 0xb4, 0xed,
+ 0xa7, 0xc8, 0x4d, 0x14, 0x07, 0x34, 0xbb, 0x42, 0x63, 0xc3, 0x62,
+ 0x5c, 0x00, 0xf0, 0x4f, 0x4c, 0x80, 0x68, 0x98, 0x1b})),
+ std::make_pair(
+ 109,
+ std::vector<uint8_t>(
+ {0xa8, 0xb6, 0x0f, 0xa4, 0xfc, 0x24, 0x42, 0xf6, 0xf1, 0x51, 0x4a,
+ 0xd7, 0x40, 0x26, 0x26, 0x92, 0x0c, 0xc7, 0xc2, 0xc9, 0xf7, 0x21,
+ 0x24, 0xb8, 0xcb, 0xa8, 0xee, 0x2c, 0xb7, 0xc4, 0x58, 0x6f, 0x65,
+ 0x8a, 0x44, 0x10, 0xcf, 0xfc, 0xc0, 0xab, 0x88, 0x34, 0x39, 0x55,
+ 0xe0, 0x94, 0xc6, 0xaf, 0x0d, 0x20, 0xd0, 0xc7, 0x14, 0xfb, 0x0a,
+ 0x98, 0x8f, 0x54, 0x3f, 0x30, 0x0f, 0x58, 0xd3, 0x89})),
+ std::make_pair(
+ 110,
+ std::vector<uint8_t>(
+ {0x82, 0x71, 0xcc, 0x45, 0xdf, 0xa5, 0xe4, 0x17, 0x0e, 0x84, 0x7e,
+ 0x86, 0x30, 0xb9, 0x52, 0xcf, 0x9c, 0x2a, 0xa7, 0x77, 0xd0, 0x6f,
+ 0x26, 0xa7, 0x58, 0x5b, 0x83, 0x81, 0xf1, 0x88, 0xda, 0xcc, 0x73,
+ 0x37, 0x39, 0x1c, 0xfc, 0xc9, 0x4b, 0x05, 0x3d, 0xc4, 0xec, 0x29,
+ 0xcc, 0x17, 0xf0, 0x77, 0x87, 0x04, 0x28, 0xf1, 0xac, 0x23, 0xfd,
+ 0xdd, 0xa1, 0x65, 0xef, 0x5a, 0x3f, 0x15, 0x5f, 0x39})),
+ std::make_pair(
+ 111,
+ std::vector<uint8_t>(
+ {0xbf, 0x23, 0xc0, 0xc2, 0x5c, 0x80, 0x60, 0xe4, 0xf6, 0x99, 0x5f,
+ 0x16, 0x23, 0xa3, 0xbe, 0xbe, 0xca, 0xa9, 0x6e, 0x30, 0x86, 0x80,
+ 0x00, 0x0a, 0x8a, 0xa3, 0xcd, 0x56, 0xbb, 0x1a, 0x6d, 0xa0, 0x99,
+ 0xe1, 0x0d, 0x92, 0x31, 0xb3, 0x7f, 0x45, 0x19, 0xb2, 0xef, 0xd2,
+ 0xc2, 0x4d, 0xe7, 0x2f, 0x31, 0xa5, 0xf1, 0x95, 0x35, 0x24, 0x1b,
+ 0x4a, 0x59, 0xfa, 0x3c, 0x03, 0xce, 0xb7, 0x90, 0xe7})),
+ std::make_pair(
+ 112,
+ std::vector<uint8_t>(
+ {0x87, 0x7f, 0xd6, 0x52, 0xc0, 0x52, 0x81, 0x00, 0x9c, 0x0a, 0x52,
+ 0x50, 0xe7, 0xa3, 0xa6, 0x71, 0xf8, 0xb1, 0x8c, 0x10, 0x88, 0x17,
+ 0xfe, 0x4a, 0x87, 0x4d, 0xe2, 0x2d, 0xa8, 0xe4, 0x5d, 0xb1, 0x19,
+ 0x58, 0xa6, 0x00, 0xc5, 0xf6, 0x2e, 0x67, 0xd3, 0x6c, 0xbf, 0x84,
+ 0x47, 0x4c, 0xf2, 0x44, 0xa9, 0xc2, 0xb0, 0x3a, 0x9f, 0xb9, 0xdc,
+ 0x71, 0x1c, 0xd1, 0xa2, 0xca, 0xb6, 0xf3, 0xfa, 0xe0})),
+ std::make_pair(
+ 113,
+ std::vector<uint8_t>(
+ {0x29, 0xdf, 0x4d, 0x87, 0xea, 0x44, 0x4b, 0xaf, 0x5b, 0xcd, 0xf5,
+ 0xf4, 0xe4, 0x15, 0x79, 0xe2, 0x8a, 0x67, 0xde, 0x84, 0x14, 0x9f,
+ 0x06, 0xc0, 0x3f, 0x11, 0x0e, 0xa8, 0x4f, 0x57, 0x2a, 0x9f, 0x67,
+ 0x6a, 0xdd, 0xd0, 0x4c, 0x48, 0x78, 0xf4, 0x9c, 0x5c, 0x00, 0xac,
+ 0xcd, 0xa4, 0x41, 0xb1, 0xa3, 0x87, 0xca, 0xce, 0xb2, 0xe9, 0x93,
+ 0xbb, 0x7a, 0x10, 0xcd, 0x8c, 0x2d, 0x67, 0x17, 0xe1})),
+ std::make_pair(
+ 114,
+ std::vector<uint8_t>(
+ {0x71, 0x0d, 0xac, 0xb1, 0x66, 0x84, 0x46, 0x39, 0xcd, 0x7b, 0x63,
+ 0x7c, 0x27, 0x42, 0x09, 0x42, 0x4e, 0x24, 0x49, 0xdc, 0x35, 0xd7,
+ 0x90, 0xbb, 0xfa, 0x4f, 0x76, 0x17, 0x70, 0x54, 0xa3, 0x6b, 0x3b,
+ 0x76, 0xfa, 0xc0, 0xca, 0x6e, 0x61, 0xdf, 0x1e, 0x68, 0x70, 0x00,
+ 0x67, 0x8a, 0xc0, 0x74, 0x6d, 0xf7, 0x5d, 0x0a, 0x39, 0x54, 0x89,
+ 0x76, 0x81, 0xfd, 0x39, 0x3a, 0x15, 0x5a, 0x1b, 0xb4})),
+ std::make_pair(
+ 115,
+ std::vector<uint8_t>(
+ {0xc1, 0xd5, 0xf9, 0x3b, 0x8d, 0xea, 0x1f, 0x25, 0x71, 0xba, 0xbc,
+ 0xcb, 0xc0, 0x17, 0x64, 0x54, 0x1a, 0x0c, 0xda, 0x87, 0xe4, 0x44,
+ 0xd6, 0x73, 0xc5, 0x09, 0x66, 0xca, 0x55, 0x9c, 0x33, 0x35, 0x4b,
+ 0x3a, 0xcb, 0x26, 0xe5, 0xd5, 0x78, 0x1f, 0xfb, 0x28, 0x84, 0x7a,
+ 0x4b, 0x47, 0x54, 0xd7, 0x70, 0x08, 0xc6, 0x2a, 0x83, 0x58, 0x35,
+ 0xf5, 0x00, 0xde, 0xa7, 0xc3, 0xb5, 0x8b, 0xda, 0xe2})),
+ std::make_pair(
+ 116,
+ std::vector<uint8_t>(
+ {0xa4, 0x1e, 0x41, 0x27, 0x1c, 0xda, 0xb8, 0xaf, 0x4d, 0x72, 0xb1,
+ 0x04, 0xbf, 0xb2, 0xad, 0x04, 0x1a, 0xc4, 0xdf, 0x14, 0x67, 0x7d,
+ 0xa6, 0x71, 0xd8, 0x56, 0x40, 0xc4, 0xb1, 0x87, 0xf5, 0x0c, 0x2b,
+ 0x66, 0x51, 0x3c, 0x46, 0x19, 0xfb, 0xd5, 0xd5, 0xdc, 0x4f, 0xe6,
+ 0x5d, 0xd3, 0x7b, 0x90, 0x42, 0xe9, 0x84, 0x8d, 0xda, 0x55, 0x6a,
+ 0x50, 0x4c, 0xaa, 0x2b, 0x1c, 0x6a, 0xfe, 0x47, 0x30})),
+ std::make_pair(
+ 117,
+ std::vector<uint8_t>(
+ {0xe7, 0xbc, 0xba, 0xcd, 0xc3, 0x79, 0xc4, 0x3d, 0x81, 0xeb, 0xad,
+ 0xcb, 0x37, 0x78, 0x15, 0x52, 0xfc, 0x1d, 0x75, 0x3e, 0x8c, 0xf3,
+ 0x10, 0xd9, 0x68, 0x39, 0x2d, 0x06, 0xc9, 0x1f, 0x1d, 0x64, 0xcc,
+ 0x9e, 0x90, 0xce, 0x1d, 0x22, 0xc3, 0x2d, 0x27, 0x7f, 0xc6, 0xcd,
+ 0xa4, 0x33, 0xa4, 0xd4, 0x42, 0xc7, 0x62, 0xe9, 0xea, 0xcf, 0x2c,
+ 0x25, 0x9f, 0x32, 0xd6, 0x4c, 0xf9, 0xda, 0x3a, 0x22})),
+ std::make_pair(
+ 118,
+ std::vector<uint8_t>(
+ {0x51, 0x75, 0x5b, 0x4a, 0xc5, 0x45, 0x6b, 0x13, 0x21, 0x8a, 0x19,
+ 0xc5, 0xb9, 0x24, 0x2f, 0x57, 0xc4, 0xa9, 0x81, 0xe4, 0xd4, 0xec,
+ 0xdc, 0xe0, 0x9a, 0x31, 0x93, 0x36, 0x2b, 0x80, 0x8a, 0x57, 0x93,
+ 0x45, 0xd4, 0x88, 0x1c, 0x26, 0x07, 0xa5, 0x65, 0x34, 0xdd, 0x7f,
+ 0x21, 0x95, 0x6a, 0xff, 0x72, 0xc2, 0xf4, 0x17, 0x3a, 0x6e, 0x7b,
+ 0x6c, 0xc2, 0x21, 0x2b, 0xa0, 0xe3, 0xda, 0xee, 0x1f})),
+ std::make_pair(
+ 119,
+ std::vector<uint8_t>(
+ {0xdc, 0xc2, 0xc4, 0xbe, 0xb9, 0xc1, 0xf2, 0x60, 0x7b, 0x78, 0x6c,
+ 0x20, 0xc6, 0x31, 0x97, 0x23, 0x47, 0x03, 0x4c, 0x1c, 0xc0, 0x2f,
+ 0xcc, 0x7d, 0x02, 0xff, 0x01, 0x09, 0x9c, 0xfe, 0x1c, 0x69, 0x89,
+ 0x84, 0x0a, 0xc2, 0x13, 0x92, 0x36, 0x29, 0x11, 0x3a, 0xa8, 0xba,
+ 0xd7, 0x13, 0xcc, 0xf0, 0xfe, 0x4c, 0xe1, 0x32, 0x64, 0xfb, 0x32,
+ 0xb8, 0xb0, 0xfe, 0x37, 0x2d, 0xa3, 0x82, 0x54, 0x4a})),
+ std::make_pair(
+ 120,
+ std::vector<uint8_t>(
+ {0x3d, 0x55, 0x17, 0x6a, 0xce, 0xa4, 0xa7, 0xe3, 0xa6, 0x5f, 0xfa,
+ 0x9f, 0xb1, 0x0a, 0x7a, 0x17, 0x67, 0x19, 0x9c, 0xf0, 0x77, 0xce,
+ 0xe9, 0xf7, 0x15, 0x32, 0xd6, 0x7c, 0xd7, 0xc7, 0x3c, 0x9f, 0x93,
+ 0xcf, 0xc3, 0x7c, 0xcd, 0xcc, 0x1f, 0xde, 0xf5, 0x0a, 0xad, 0x46,
+ 0xa5, 0x04, 0xa6, 0x50, 0xd2, 0x98, 0xd5, 0x97, 0xa3, 0xa9, 0xfa,
+ 0x95, 0xc6, 0xc4, 0x0c, 0xb7, 0x1f, 0xa5, 0xe7, 0x25})),
+ std::make_pair(
+ 121,
+ std::vector<uint8_t>(
+ {0xd0, 0x77, 0x13, 0xc0, 0x05, 0xde, 0x96, 0xdd, 0x21, 0xd2, 0xeb,
+ 0x8b, 0xbe, 0xca, 0x66, 0x74, 0x6e, 0xa5, 0x1a, 0x31, 0xae, 0x92,
+ 0x2a, 0x3e, 0x74, 0x86, 0x48, 0x89, 0x54, 0x0a, 0x48, 0xdb, 0x27,
+ 0xd7, 0xe4, 0xc9, 0x03, 0x11, 0x63, 0x8b, 0x22, 0x4b, 0xf0, 0x20,
+ 0x1b, 0x50, 0x18, 0x91, 0x75, 0x48, 0x48, 0x11, 0x3c, 0x26, 0x61,
+ 0x08, 0xd0, 0xad, 0xb1, 0x3d, 0xb7, 0x19, 0x09, 0xc7})),
+ std::make_pair(
+ 122,
+ std::vector<uint8_t>(
+ {0x58, 0x98, 0x3c, 0x21, 0x43, 0x3d, 0x95, 0x0c, 0xaa, 0x23, 0xe4,
+ 0xbc, 0x18, 0x54, 0x3b, 0x8e, 0x60, 0x1c, 0x20, 0x43, 0x18, 0x53,
+ 0x21, 0x52, 0xda, 0xf5, 0xe1, 0x59, 0xa0, 0xcd, 0x14, 0x80, 0x18,
+ 0x3d, 0x29, 0x28, 0x5c, 0x05, 0xf1, 0x29, 0xcb, 0x0c, 0xc3, 0x16,
+ 0x46, 0x87, 0x92, 0x80, 0x86, 0xff, 0xe3, 0x80, 0x15, 0x8d, 0xf1,
+ 0xd3, 0x94, 0xc6, 0xac, 0x0d, 0x42, 0x88, 0xbc, 0xa8})),
+ std::make_pair(
+ 123,
+ std::vector<uint8_t>(
+ {0x81, 0x00, 0xa8, 0xdc, 0x52, 0x8d, 0x2b, 0x68, 0x2a, 0xb4, 0x25,
+ 0x08, 0x01, 0xba, 0x33, 0xf0, 0x2a, 0x3e, 0x94, 0xc5, 0x4d, 0xac,
+ 0x0a, 0xe1, 0x48, 0x2a, 0xa2, 0x1f, 0x51, 0xef, 0x3a, 0x82, 0xf3,
+ 0x80, 0x7e, 0x6f, 0xac, 0xb0, 0xae, 0xb0, 0x59, 0x47, 0xbf, 0x7a,
+ 0xa2, 0xad, 0xcb, 0x03, 0x43, 0x56, 0xf9, 0x0f, 0xa4, 0x56, 0x0e,
+ 0xde, 0x02, 0x20, 0x1a, 0x37, 0xe4, 0x11, 0xec, 0x1a})),
+ std::make_pair(
+ 124,
+ std::vector<uint8_t>(
+ {0x07, 0x02, 0x5f, 0x1b, 0xb6, 0xc7, 0x84, 0xf3, 0xfe, 0x49, 0xde,
+ 0x5c, 0x14, 0xb9, 0x36, 0xa5, 0xac, 0xac, 0xac, 0xaa, 0xb3, 0x3f,
+ 0x6a, 0xc4, 0xd0, 0xe0, 0x0a, 0xb6, 0xa1, 0x24, 0x83, 0xd6, 0xbe,
+ 0xc0, 0x0b, 0x4f, 0xe6, 0x7c, 0x7c, 0xa5, 0xcc, 0x50, 0x8c, 0x2a,
+ 0x53, 0xef, 0xb5, 0xbf, 0xa5, 0x39, 0x87, 0x69, 0xd8, 0x43, 0xff,
+ 0x0d, 0x9e, 0x8b, 0x14, 0xd3, 0x6a, 0x01, 0xa7, 0x7f})),
+ std::make_pair(
+ 125,
+ std::vector<uint8_t>(
+ {0xba, 0x6a, 0xef, 0xd9, 0x72, 0xb6, 0x18, 0x6e, 0x02, 0x7a, 0x76,
+ 0x27, 0x3a, 0x4a, 0x72, 0x33, 0x21, 0xa3, 0xf5, 0x80, 0xcf, 0xa8,
+ 0x94, 0xda, 0x5a, 0x9c, 0xe8, 0xe7, 0x21, 0xc8, 0x28, 0x55, 0x2c,
+ 0x64, 0xda, 0xce, 0xe3, 0xa7, 0xfd, 0x2d, 0x74, 0x3b, 0x5c, 0x35,
+ 0xad, 0x0c, 0x8e, 0xfa, 0x71, 0xf8, 0xce, 0x99, 0xbf, 0x96, 0x33,
+ 0x47, 0x10, 0xe2, 0xc2, 0x34, 0x6e, 0x8f, 0x3c, 0x52})),
+ std::make_pair(
+ 126,
+ std::vector<uint8_t>(
+ {0xe0, 0x72, 0x1e, 0x02, 0x51, 0x7a, 0xed, 0xfa, 0x4e, 0x7e, 0x9b,
+ 0xa5, 0x03, 0xe0, 0x25, 0xfd, 0x46, 0xe7, 0x14, 0x56, 0x6d, 0xc8,
+ 0x89, 0xa8, 0x4c, 0xbf, 0xe5, 0x6a, 0x55, 0xdf, 0xbe, 0x2f, 0xc4,
+ 0x93, 0x8a, 0xc4, 0x12, 0x05, 0x88, 0x33, 0x5d, 0xea, 0xc8, 0xef,
+ 0x3f, 0xa2, 0x29, 0xad, 0xc9, 0x64, 0x7f, 0x54, 0xad, 0x2e, 0x34,
+ 0x72, 0x23, 0x4f, 0x9b, 0x34, 0xef, 0xc4, 0x65, 0x43})),
+ std::make_pair(
+ 127,
+ std::vector<uint8_t>(
+ {0xb6, 0x29, 0x26, 0x69, 0xcc, 0xd3, 0x8d, 0x5f, 0x01, 0xca, 0xae,
+ 0x96, 0xba, 0x27, 0x2c, 0x76, 0xa8, 0x79, 0xa4, 0x57, 0x43, 0xaf,
+ 0xa0, 0x72, 0x5d, 0x83, 0xb9, 0xeb, 0xb2, 0x66, 0x65, 0xb7, 0x31,
+ 0xf1, 0x84, 0x8c, 0x52, 0xf1, 0x19, 0x72, 0xb6, 0x64, 0x4f, 0x55,
+ 0x4c, 0x06, 0x4f, 0xa9, 0x07, 0x80, 0xdb, 0xbb, 0xf3, 0xa8, 0x9d,
+ 0x4f, 0xc3, 0x1f, 0x67, 0xdf, 0x3e, 0x58, 0x57, 0xef})),
+ std::make_pair(
+ 128,
+ std::vector<uint8_t>(
+ {0x23, 0x19, 0xe3, 0x78, 0x9c, 0x47, 0xe2, 0xda, 0xa5, 0xfe, 0x80,
+ 0x7f, 0x61, 0xbe, 0xc2, 0xa1, 0xa6, 0x53, 0x7f, 0xa0, 0x3f, 0x19,
+ 0xff, 0x32, 0xe8, 0x7e, 0xec, 0xbf, 0xd6, 0x4b, 0x7e, 0x0e, 0x8c,
+ 0xcf, 0xf4, 0x39, 0xac, 0x33, 0x3b, 0x04, 0x0f, 0x19, 0xb0, 0xc4,
+ 0xdd, 0xd1, 0x1a, 0x61, 0xe2, 0x4a, 0xc1, 0xfe, 0x0f, 0x10, 0xa0,
+ 0x39, 0x80, 0x6c, 0x5d, 0xcc, 0x0d, 0xa3, 0xd1, 0x15})),
+ std::make_pair(
+ 129,
+ std::vector<uint8_t>(
+ {0xf5, 0x97, 0x11, 0xd4, 0x4a, 0x03, 0x1d, 0x5f, 0x97, 0xa9, 0x41,
+ 0x3c, 0x06, 0x5d, 0x1e, 0x61, 0x4c, 0x41, 0x7e, 0xde, 0x99, 0x85,
+ 0x90, 0x32, 0x5f, 0x49, 0xba, 0xd2, 0xfd, 0x44, 0x4d, 0x3e, 0x44,
+ 0x18, 0xbe, 0x19, 0xae, 0xc4, 0xe1, 0x14, 0x49, 0xac, 0x1a, 0x57,
+ 0x20, 0x78, 0x98, 0xbc, 0x57, 0xd7, 0x6a, 0x1b, 0xcf, 0x35, 0x66,
+ 0x29, 0x2c, 0x20, 0xc6, 0x83, 0xa5, 0xc4, 0x64, 0x8f})),
+ std::make_pair(
+ 130,
+ std::vector<uint8_t>(
+ {0xdf, 0x0a, 0x9d, 0x0c, 0x21, 0x28, 0x43, 0xa6, 0xa9, 0x34, 0xe3,
+ 0x90, 0x2b, 0x2d, 0xd3, 0x0d, 0x17, 0xfb, 0xa5, 0xf9, 0x69, 0xd2,
+ 0x03, 0x0b, 0x12, 0xa5, 0x46, 0xd8, 0xa6, 0xa4, 0x5e, 0x80, 0xcf,
+ 0x56, 0x35, 0xf0, 0x71, 0xf0, 0x45, 0x2e, 0x9c, 0x91, 0x92, 0x75,
+ 0xda, 0x99, 0xbe, 0xd5, 0x1e, 0xb1, 0x17, 0x3c, 0x1a, 0xf0, 0x51,
+ 0x87, 0x26, 0xb7, 0x5b, 0x0e, 0xc3, 0xba, 0xe2, 0xb5})),
+ std::make_pair(
+ 131,
+ std::vector<uint8_t>(
+ {0xa3, 0xeb, 0x6e, 0x6c, 0x7b, 0xf2, 0xfb, 0x8b, 0x28, 0xbf, 0xe8,
+ 0xb1, 0x5e, 0x15, 0xbb, 0x50, 0x0f, 0x78, 0x1e, 0xcc, 0x86, 0xf7,
+ 0x78, 0xc3, 0xa4, 0xe6, 0x55, 0xfc, 0x58, 0x69, 0xbf, 0x28, 0x46,
+ 0xa2, 0x45, 0xd4, 0xe3, 0x3b, 0x7b, 0x14, 0x43, 0x6a, 0x17, 0xe6,
+ 0x3b, 0xe7, 0x9b, 0x36, 0x65, 0x5c, 0x22, 0x6a, 0x50, 0xff, 0xbc,
+ 0x71, 0x24, 0x20, 0x7b, 0x02, 0x02, 0x34, 0x2d, 0xb5})),
+ std::make_pair(
+ 132,
+ std::vector<uint8_t>(
+ {0x56, 0xd4, 0xcb, 0xcd, 0x07, 0x05, 0x63, 0x42, 0x6a, 0x01, 0x70,
+ 0x69, 0x42, 0x5c, 0x2c, 0xd2, 0xae, 0x54, 0x06, 0x68, 0x28, 0x7a,
+ 0x5f, 0xb9, 0xda, 0xc4, 0x32, 0xeb, 0x8a, 0xb1, 0xa3, 0x53, 0xa3,
+ 0x0f, 0x2f, 0xe1, 0xf4, 0x0d, 0x83, 0x33, 0x3a, 0xfe, 0x69, 0x6a,
+ 0x26, 0x77, 0x95, 0x40, 0x8a, 0x92, 0xfe, 0x7d, 0xa0, 0x7a, 0x0c,
+ 0x18, 0x14, 0xcf, 0x77, 0xf3, 0x6e, 0x10, 0x5e, 0xe8})),
+ std::make_pair(
+ 133,
+ std::vector<uint8_t>(
+ {0xe5, 0x9b, 0x99, 0x87, 0xd4, 0x28, 0xb3, 0xed, 0xa3, 0x7d, 0x80,
+ 0xab, 0xdb, 0x16, 0xcd, 0x2b, 0x0a, 0xef, 0x67, 0x4c, 0x2b, 0x1d,
+ 0xda, 0x44, 0x32, 0xea, 0x91, 0xee, 0x6c, 0x93, 0x5c, 0x68, 0x4b,
+ 0x48, 0xb4, 0x42, 0x8a, 0x8c, 0xc7, 0x40, 0xe5, 0x79, 0xa3, 0x0d,
+ 0xef, 0xf3, 0x5a, 0x80, 0x30, 0x13, 0x82, 0x0d, 0xd2, 0x3f, 0x14,
+ 0xae, 0x1d, 0x84, 0x13, 0xb5, 0xc8, 0x67, 0x2a, 0xec})),
+ std::make_pair(
+ 134,
+ std::vector<uint8_t>(
+ {0xcd, 0x9f, 0xcc, 0x99, 0xf9, 0x9d, 0x4c, 0xc1, 0x6d, 0x03, 0x19,
+ 0x00, 0xb2, 0xa7, 0x36, 0xe1, 0x50, 0x8d, 0xb4, 0xb5, 0x86, 0x81,
+ 0x4e, 0x63, 0x45, 0x85, 0x7f, 0x35, 0x4a, 0x70, 0xcc, 0xec, 0xb1,
+ 0xdf, 0x3b, 0x50, 0xa1, 0x9a, 0xda, 0xf4, 0x3c, 0x27, 0x8e, 0xfa,
+ 0x42, 0x3f, 0xf4, 0xbb, 0x6c, 0x52, 0x3e, 0xc7, 0xfd, 0x78, 0x59,
+ 0xb9, 0x7b, 0x16, 0x8a, 0x7e, 0xbf, 0xf8, 0x46, 0x7c})),
+ std::make_pair(
+ 135,
+ std::vector<uint8_t>(
+ {0x06, 0x02, 0x18, 0x5d, 0x8c, 0x3a, 0x78, 0x73, 0x8b, 0x99, 0x16,
+ 0x4b, 0x8b, 0xc6, 0xff, 0xb2, 0x1c, 0x7d, 0xeb, 0xeb, 0xbf, 0x80,
+ 0x63, 0x72, 0xe0, 0xda, 0x44, 0xd1, 0x21, 0x54, 0x55, 0x97, 0xb9,
+ 0xc6, 0x62, 0xa2, 0x55, 0xdc, 0x31, 0x54, 0x2c, 0xf9, 0x95, 0xec,
+ 0xbe, 0x6a, 0x50, 0xfb, 0x5e, 0x6e, 0x0e, 0xe4, 0xef, 0x24, 0x0f,
+ 0xe5, 0x57, 0xed, 0xed, 0x11, 0x88, 0x08, 0x7e, 0x86})),
+ std::make_pair(
+ 136,
+ std::vector<uint8_t>(
+ {0xc0, 0x8a, 0xfa, 0x5b, 0x92, 0x7b, 0xf0, 0x80, 0x97, 0xaf, 0xc5,
+ 0xff, 0xf9, 0xca, 0x4e, 0x78, 0x00, 0x12, 0x5c, 0x1f, 0x52, 0xf2,
+ 0xaf, 0x35, 0x53, 0xfa, 0x2b, 0x89, 0xe1, 0xe3, 0x01, 0x5c, 0x4f,
+ 0x87, 0xd5, 0xe0, 0xa4, 0x89, 0x56, 0xad, 0x31, 0x45, 0x0b, 0x08,
+ 0x3d, 0xad, 0x14, 0x7f, 0xfb, 0x5e, 0xc0, 0x34, 0x34, 0xa2, 0x68,
+ 0x30, 0xcf, 0x37, 0xd1, 0x03, 0xab, 0x50, 0xc5, 0xda})),
+ std::make_pair(
+ 137,
+ std::vector<uint8_t>(
+ {0x36, 0xf1, 0xe1, 0xc1, 0x1d, 0x6e, 0xf6, 0xbc, 0x3b, 0x53, 0x6d,
+ 0x50, 0x5d, 0x54, 0x4a, 0x87, 0x15, 0x22, 0xc5, 0xc2, 0xa2, 0x53,
+ 0x06, 0x7e, 0xc9, 0x93, 0x3b, 0x6e, 0xc2, 0x54, 0x64, 0xda, 0xf9,
+ 0x85, 0x52, 0x5f, 0x5b, 0x95, 0x60, 0xa1, 0x6d, 0x89, 0x02, 0x59,
+ 0xac, 0x1b, 0xb5, 0xcc, 0x67, 0xc0, 0xc4, 0x69, 0xcd, 0xe1, 0x33,
+ 0xde, 0xf0, 0x00, 0xea, 0x1d, 0x68, 0x6f, 0x4f, 0x5d})),
+ std::make_pair(
+ 138,
+ std::vector<uint8_t>(
+ {0xbf, 0x2a, 0xb2, 0xe2, 0x47, 0x0f, 0x54, 0x38, 0xc3, 0xb6, 0x89,
+ 0xe6, 0x6e, 0x76, 0x86, 0xff, 0xfa, 0x0c, 0xb1, 0xe1, 0x79, 0x8a,
+ 0xd3, 0xa8, 0x6f, 0xf9, 0x90, 0x75, 0xbf, 0x61, 0x38, 0xe3, 0x3d,
+ 0x9c, 0x0c, 0xe5, 0x9a, 0xfb, 0x24, 0xac, 0x67, 0xa0, 0x2a, 0xf3,
+ 0x44, 0x28, 0x19, 0x1a, 0x9a, 0x0a, 0x60, 0x41, 0xc0, 0x74, 0x71,
+ 0xb7, 0xc3, 0xb1, 0xa7, 0x52, 0xd6, 0xfc, 0x0b, 0x8b})),
+ std::make_pair(
+ 139,
+ std::vector<uint8_t>(
+ {0xd4, 0x00, 0x60, 0x1f, 0x97, 0x28, 0xcc, 0xc4, 0xc9, 0x23, 0x42,
+ 0xd9, 0x78, 0x7d, 0x8d, 0x28, 0xab, 0x32, 0x3a, 0xf3, 0x75, 0xca,
+ 0x56, 0x24, 0xb4, 0xbb, 0x91, 0xd1, 0x72, 0x71, 0xfb, 0xae, 0x86,
+ 0x2e, 0x41, 0x3b, 0xe7, 0x3f, 0x1f, 0x68, 0xe6, 0x15, 0xb8, 0xc5,
+ 0xc3, 0x91, 0xbe, 0x0d, 0xbd, 0x91, 0x44, 0x74, 0x6e, 0xb3, 0x39,
+ 0xad, 0x54, 0x15, 0x47, 0xba, 0x9c, 0x46, 0x8a, 0x17})),
+ std::make_pair(
+ 140,
+ std::vector<uint8_t>(
+ {0x79, 0xfe, 0x2f, 0xe1, 0x57, 0xeb, 0x85, 0xa0, 0x38, 0xab, 0xb8,
+ 0xeb, 0xbc, 0x64, 0x77, 0x31, 0xd2, 0xc8, 0x3f, 0x51, 0xb0, 0xac,
+ 0x6e, 0xe1, 0x4a, 0xa2, 0x84, 0xcb, 0x6a, 0x35, 0x49, 0xa4, 0xdc,
+ 0xce, 0xb3, 0x00, 0x74, 0x0a, 0x82, 0x5f, 0x52, 0xf5, 0xfb, 0x30,
+ 0xb0, 0x3b, 0x8c, 0x4d, 0x8b, 0x0f, 0x4a, 0xa6, 0x7a, 0x63, 0xf4,
+ 0xa9, 0x4e, 0x33, 0x03, 0xc4, 0xed, 0xa4, 0xc0, 0x2b})),
+ std::make_pair(
+ 141,
+ std::vector<uint8_t>(
+ {0x75, 0x35, 0x13, 0x13, 0xb5, 0x2a, 0x85, 0x29, 0x29, 0x8d, 0x8c,
+ 0x18, 0x6b, 0x17, 0x68, 0x66, 0x6d, 0xcc, 0xa8, 0x59, 0x53, 0x17,
+ 0xd7, 0xa4, 0x81, 0x6e, 0xb8, 0x8c, 0x06, 0x20, 0x20, 0xc0, 0xc8,
+ 0xef, 0xc5, 0x54, 0xbb, 0x34, 0x1b, 0x64, 0x68, 0x8d, 0xb5, 0xcc,
+ 0xaf, 0xc3, 0x5f, 0x3c, 0x3c, 0xd0, 0x9d, 0x65, 0x64, 0xb3, 0x6d,
+ 0x7b, 0x04, 0xa2, 0x48, 0xe1, 0x46, 0x98, 0x0d, 0x4b})),
+ std::make_pair(
+ 142,
+ std::vector<uint8_t>(
+ {0xe3, 0x12, 0x8b, 0x1d, 0x31, 0x1d, 0x02, 0x17, 0x9d, 0x7f, 0x25,
+ 0xf9, 0x7a, 0x5a, 0x8b, 0xee, 0x2c, 0xc8, 0xc8, 0x63, 0x03, 0x64,
+ 0x4f, 0xcd, 0x66, 0x4e, 0x15, 0x7d, 0x1f, 0xef, 0x00, 0xf2, 0x3e,
+ 0x46, 0xf9, 0xa5, 0xe8, 0xe5, 0xc8, 0x90, 0xce, 0x56, 0x5b, 0xb6,
+ 0xab, 0xd4, 0x30, 0x2c, 0xe0, 0x64, 0x69, 0xd5, 0x2a, 0x5b, 0xd5,
+ 0x3e, 0x1c, 0x5a, 0x54, 0xd0, 0x46, 0x49, 0xdc, 0x03})),
+ std::make_pair(
+ 143,
+ std::vector<uint8_t>(
+ {0xc2, 0x38, 0x2a, 0x72, 0xd2, 0xd3, 0xac, 0xe9, 0xd5, 0x93, 0x3d,
+ 0x00, 0xb6, 0x08, 0x27, 0xed, 0x38, 0x0c, 0xda, 0x08, 0xd0, 0xba,
+ 0x5f, 0x6d, 0xd4, 0x1e, 0x29, 0xee, 0x6d, 0xbe, 0x8e, 0xcb, 0x92,
+ 0x35, 0xf0, 0x6b, 0xe9, 0x5d, 0x83, 0xb6, 0x81, 0x6a, 0x2f, 0xb7,
+ 0xa5, 0xad, 0x47, 0x03, 0x5e, 0x8a, 0x4b, 0x69, 0xa4, 0x88, 0x4b,
+ 0x99, 0xe4, 0xbe, 0xce, 0x58, 0xca, 0xb2, 0x5d, 0x44})),
+ std::make_pair(
+ 144,
+ std::vector<uint8_t>(
+ {0x6b, 0x1c, 0x69, 0x46, 0x0b, 0xbd, 0x50, 0xac, 0x2e, 0xd6, 0xf3,
+ 0x2e, 0x6e, 0x88, 0x7c, 0xfe, 0xd4, 0x07, 0xd4, 0x7d, 0xcf, 0x0a,
+ 0xaa, 0x60, 0x38, 0x7f, 0xe3, 0x20, 0xd7, 0x80, 0xbd, 0x03, 0xea,
+ 0xb6, 0xd7, 0xba, 0xeb, 0x2a, 0x07, 0xd1, 0x0c, 0xd5, 0x52, 0xa3,
+ 0x00, 0x34, 0x13, 0x54, 0xea, 0x9a, 0x5f, 0x03, 0x18, 0x3a, 0x62,
+ 0x3f, 0x92, 0xa2, 0xd4, 0xd9, 0xf0, 0x09, 0x26, 0xaf})),
+ std::make_pair(
+ 145,
+ std::vector<uint8_t>(
+ {0x6c, 0xda, 0x20, 0x6c, 0x80, 0xcd, 0xc9, 0xc4, 0x4b, 0xa9, 0x90,
+ 0xe0, 0x32, 0x8c, 0x31, 0x4f, 0x81, 0x9b, 0x14, 0x2d, 0x00, 0x63,
+ 0x04, 0x04, 0xc4, 0x8c, 0x05, 0xdc, 0x76, 0xd1, 0xb0, 0x0c, 0xe4,
+ 0xd7, 0x2f, 0xc6, 0xa4, 0x8e, 0x14, 0x69, 0xdd, 0xef, 0x60, 0x94,
+ 0x12, 0xc3, 0x64, 0x82, 0x08, 0x54, 0x21, 0x4b, 0x48, 0x69, 0xaf,
+ 0x09, 0x0f, 0x00, 0xd3, 0xc1, 0xba, 0x44, 0x3e, 0x1b})),
+ std::make_pair(
+ 146,
+ std::vector<uint8_t>(
+ {0x7f, 0xfc, 0x8c, 0x26, 0xfb, 0xd6, 0xa0, 0xf7, 0xa6, 0x09, 0xe6,
+ 0xe1, 0x93, 0x9f, 0x6a, 0x9e, 0xdf, 0x1b, 0x0b, 0x06, 0x66, 0x41,
+ 0xfb, 0x76, 0xc4, 0xf9, 0x60, 0x2e, 0xd7, 0x48, 0xd1, 0x16, 0x02,
+ 0x49, 0x6b, 0x35, 0x35, 0x5b, 0x1a, 0xa2, 0x55, 0x85, 0x0a, 0x50,
+ 0x9d, 0x2f, 0x8e, 0xe1, 0x8c, 0x8f, 0x3e, 0x1d, 0x7d, 0xcb, 0xc3,
+ 0x7a, 0x13, 0x65, 0x98, 0xf5, 0x6a, 0x59, 0xed, 0x17})),
+ std::make_pair(
+ 147,
+ std::vector<uint8_t>(
+ {0x70, 0xde, 0x1f, 0x08, 0xdd, 0x4e, 0x09, 0xd5, 0xfc, 0x15, 0x1f,
+ 0x17, 0xfc, 0x99, 0x1a, 0x23, 0xab, 0xfc, 0x05, 0x10, 0x42, 0x90,
+ 0xd5, 0x04, 0x68, 0x88, 0x2e, 0xfa, 0xf5, 0x82, 0xb6, 0xec, 0x2f,
+ 0x14, 0xf5, 0x77, 0xc0, 0xd6, 0x8c, 0x3a, 0xd0, 0x66, 0x26, 0x91,
+ 0x6e, 0x3c, 0x86, 0xe6, 0xda, 0xab, 0x6c, 0x53, 0xe5, 0x16, 0x3e,
+ 0x82, 0xb6, 0xbd, 0x0c, 0xe4, 0x9f, 0xc0, 0xd8, 0xdf})),
+ std::make_pair(
+ 148,
+ std::vector<uint8_t>(
+ {0x4f, 0x81, 0x93, 0x57, 0x56, 0xed, 0x35, 0xee, 0x20, 0x58, 0xee,
+ 0x0c, 0x6a, 0x61, 0x10, 0xd6, 0xfa, 0xc5, 0xcb, 0x6a, 0x4f, 0x46,
+ 0xaa, 0x94, 0x11, 0x60, 0x3f, 0x99, 0x96, 0x58, 0x23, 0xb6, 0xda,
+ 0x48, 0x38, 0x27, 0x6c, 0x5c, 0x06, 0xbc, 0x78, 0x80, 0xe3, 0x76,
+ 0xd9, 0x27, 0x58, 0x36, 0x9e, 0xe7, 0x30, 0x5b, 0xce, 0xc8, 0xd3,
+ 0xcf, 0xd2, 0x8c, 0xca, 0xbb, 0x7b, 0x4f, 0x05, 0x79})),
+ std::make_pair(
+ 149,
+ std::vector<uint8_t>(
+ {0xab, 0xcb, 0x61, 0xcb, 0x36, 0x83, 0xd1, 0x8f, 0x27, 0xad, 0x52,
+ 0x79, 0x08, 0xed, 0x2d, 0x32, 0xa0, 0x42, 0x6c, 0xb7, 0xbb, 0x4b,
+ 0xf1, 0x80, 0x61, 0x90, 0x3a, 0x7d, 0xc4, 0x2e, 0x7e, 0x76, 0xf9,
+ 0x82, 0x38, 0x23, 0x04, 0xd1, 0x8a, 0xf8, 0xc8, 0x0d, 0x91, 0xdd,
+ 0x58, 0xdd, 0x47, 0xaf, 0x76, 0xf8, 0xe2, 0xc3, 0x6e, 0x28, 0xaf,
+ 0x24, 0x76, 0xb4, 0xbc, 0xcf, 0x82, 0xe8, 0x9f, 0xdf})),
+ std::make_pair(
+ 150,
+ std::vector<uint8_t>(
+ {0x02, 0xd2, 0x61, 0xad, 0x56, 0xa5, 0x26, 0x33, 0x1b, 0x64, 0x3d,
+ 0xd2, 0x18, 0x6d, 0xe9, 0xa8, 0x2e, 0x72, 0xa5, 0x82, 0x23, 0xcd,
+ 0x1e, 0x72, 0x36, 0x86, 0xc5, 0x3d, 0x86, 0x9b, 0x83, 0xb9, 0x46,
+ 0x32, 0xb7, 0xb6, 0x47, 0xab, 0x2a, 0xfc, 0x0d, 0x52, 0x2e, 0x29,
+ 0xda, 0x3a, 0x56, 0x15, 0xb7, 0x41, 0xd8, 0x28, 0x52, 0xe0, 0xdf,
+ 0x41, 0xb6, 0x60, 0x07, 0xdb, 0xcb, 0xa9, 0x05, 0x43})),
+ std::make_pair(
+ 151,
+ std::vector<uint8_t>(
+ {0xc5, 0x83, 0x27, 0x41, 0xfa, 0x30, 0xc5, 0x43, 0x68, 0x23, 0x01,
+ 0x53, 0x83, 0xd2, 0x97, 0xff, 0x4c, 0x4a, 0x5d, 0x72, 0x76, 0xc3,
+ 0xf9, 0x02, 0x12, 0x20, 0x66, 0xe0, 0x4b, 0xe5, 0x43, 0x1b, 0x1a,
+ 0x85, 0xfa, 0xf7, 0x3b, 0x91, 0x84, 0x34, 0xf9, 0x30, 0x09, 0x63,
+ 0xd1, 0xde, 0xa9, 0xe8, 0xac, 0x39, 0x24, 0xef, 0x49, 0x02, 0x26,
+ 0xed, 0xee, 0xa5, 0xf7, 0x43, 0xe4, 0x10, 0x66, 0x9f})),
+ std::make_pair(
+ 152,
+ std::vector<uint8_t>(
+ {0xcf, 0xae, 0xab, 0x26, 0x8c, 0xd0, 0x75, 0xa5, 0xa6, 0xae, 0xd5,
+ 0x15, 0x02, 0x3a, 0x03, 0x2d, 0x54, 0xf2, 0xf2, 0xff, 0x73, 0x3c,
+ 0xe0, 0xcb, 0xc7, 0x8d, 0xb5, 0x1d, 0xb4, 0x50, 0x4d, 0x67, 0x59,
+ 0x23, 0xf8, 0x27, 0x46, 0xd6, 0x59, 0x46, 0x06, 0xad, 0x5d, 0x67,
+ 0x73, 0x4b, 0x11, 0xa6, 0x7c, 0xc6, 0xa4, 0x68, 0xc2, 0x03, 0x2e,
+ 0x43, 0xca, 0x1a, 0x94, 0xc6, 0x27, 0x3a, 0x98, 0x5e})),
+ std::make_pair(
+ 153,
+ std::vector<uint8_t>(
+ {0x86, 0x08, 0x50, 0xf9, 0x2e, 0xb2, 0x68, 0x27, 0x2b, 0x67, 0xd1,
+ 0x33, 0x60, 0x9b, 0xd6, 0x4e, 0x34, 0xf6, 0x1b, 0xf0, 0x3f, 0x4c,
+ 0x17, 0x38, 0x64, 0x5c, 0x17, 0xfe, 0xc8, 0x18, 0x46, 0x5d, 0x7e,
+ 0xcd, 0x2b, 0xe2, 0x90, 0x76, 0x41, 0x13, 0x00, 0x25, 0xfd, 0xa7,
+ 0x94, 0x70, 0xab, 0x73, 0x16, 0x46, 0xe7, 0xf6, 0x94, 0x40, 0xe8,
+ 0x36, 0x7e, 0xa7, 0x6a, 0xc4, 0xce, 0xe8, 0xa1, 0xdf})),
+ std::make_pair(
+ 154,
+ std::vector<uint8_t>(
+ {0x84, 0xb1, 0x54, 0xed, 0x29, 0xbb, 0xed, 0xef, 0xa6, 0x48, 0x28,
+ 0x68, 0x39, 0x04, 0x6f, 0x4b, 0x5a, 0xa3, 0x44, 0x30, 0xe2, 0xd6,
+ 0x7f, 0x74, 0x96, 0xe4, 0xc3, 0x9f, 0x2c, 0x7e, 0xa7, 0x89, 0x95,
+ 0xf6, 0x9e, 0x12, 0x92, 0x20, 0x00, 0x16, 0xf1, 0x6a, 0xc3, 0xb3,
+ 0x77, 0x00, 0xe6, 0xc7, 0xe7, 0x86, 0x1a, 0xfc, 0x39, 0x6b, 0x64,
+ 0xa5, 0x9a, 0x1d, 0xbf, 0x47, 0xa5, 0x5c, 0x4b, 0xbc})),
+ std::make_pair(
+ 155,
+ std::vector<uint8_t>(
+ {0xae, 0xee, 0xc2, 0x60, 0xa5, 0xd8, 0xef, 0xf5, 0xcc, 0xab, 0x8b,
+ 0x95, 0xda, 0x43, 0x5a, 0x63, 0xed, 0x7a, 0x21, 0xea, 0x7f, 0xc7,
+ 0x55, 0x94, 0x13, 0xfd, 0x61, 0x7e, 0x33, 0x60, 0x9f, 0x8c, 0x29,
+ 0x0e, 0x64, 0xbb, 0xac, 0xc5, 0x28, 0xf6, 0xc0, 0x80, 0x26, 0x22,
+ 0x88, 0xb0, 0xf0, 0xa3, 0x21, 0x9b, 0xe2, 0x23, 0xc9, 0x91, 0xbe,
+ 0xe9, 0x2e, 0x72, 0x34, 0x95, 0x93, 0xe6, 0x76, 0x38})),
+ std::make_pair(
+ 156,
+ std::vector<uint8_t>(
+ {0x8a, 0xd7, 0x8a, 0x9f, 0x26, 0x60, 0x1d, 0x12, 0x7e, 0x8d, 0x2f,
+ 0x2f, 0x97, 0x6e, 0x63, 0xd1, 0x9a, 0x05, 0x4a, 0x17, 0xdc, 0xf5,
+ 0x9e, 0x0f, 0x01, 0x3a, 0xb5, 0x4a, 0x68, 0x87, 0xbb, 0xdf, 0xfd,
+ 0xe7, 0xaa, 0xae, 0x11, 0x7e, 0x0f, 0xbf, 0x32, 0x71, 0x01, 0x65,
+ 0x95, 0xb9, 0xd9, 0xc7, 0x12, 0xc0, 0x1b, 0x2c, 0x53, 0xe9, 0x65,
+ 0x5a, 0x38, 0x2b, 0xc4, 0x52, 0x2e, 0x61, 0x66, 0x45})),
+ std::make_pair(
+ 157,
+ std::vector<uint8_t>(
+ {0x89, 0x34, 0x15, 0x9d, 0xad, 0xe1, 0xac, 0x74, 0x14, 0x7d, 0xfa,
+ 0x28, 0x2c, 0x75, 0x95, 0x4f, 0xce, 0xf4, 0x43, 0xef, 0x25, 0xf8,
+ 0x0d, 0xfe, 0x9f, 0xb6, 0xea, 0x63, 0x3b, 0x85, 0x45, 0x11, 0x1d,
+ 0x08, 0xb3, 0x4e, 0xf4, 0x3f, 0xff, 0x17, 0x02, 0x6c, 0x79, 0x64,
+ 0xf5, 0xde, 0xac, 0x6d, 0x2b, 0x3c, 0x29, 0xda, 0xcf, 0x27, 0x47,
+ 0xf0, 0x22, 0xdf, 0x59, 0x67, 0xdf, 0xdc, 0x1a, 0x0a})),
+ std::make_pair(
+ 158,
+ std::vector<uint8_t>(
+ {0xcd, 0x36, 0xdd, 0x0b, 0x24, 0x06, 0x14, 0xcf, 0x2f, 0xa2, 0xb9,
+ 0xe9, 0x59, 0x67, 0x9d, 0xcd, 0xd7, 0x2e, 0xc0, 0xcd, 0x58, 0xa4,
+ 0x3d, 0xa3, 0x79, 0x0a, 0x92, 0xf6, 0xcd, 0xeb, 0x9e, 0x1e, 0x79,
+ 0x5e, 0x47, 0x8a, 0x0a, 0x47, 0xd3, 0x71, 0x10, 0x0d, 0x34, 0x0c,
+ 0x5c, 0xed, 0xcd, 0xbb, 0xc9, 0xe6, 0x8b, 0x3f, 0x46, 0x08, 0x18,
+ 0xe5, 0xbd, 0xff, 0x7b, 0x4c, 0xda, 0x4c, 0x27, 0x44})),
+ std::make_pair(
+ 159,
+ std::vector<uint8_t>(
+ {0x00, 0xdf, 0x4e, 0x09, 0x9b, 0x80, 0x71, 0x37, 0xa8, 0x59, 0x90,
+ 0xf4, 0x9d, 0x3a, 0x94, 0x31, 0x5e, 0x5a, 0x5f, 0x7f, 0x7a, 0x60,
+ 0x76, 0xb3, 0x03, 0xe9, 0x6b, 0x05, 0x6f, 0xb9, 0x38, 0x00, 0x11,
+ 0x1f, 0x47, 0x96, 0x28, 0xe2, 0xf8, 0xdb, 0x59, 0xae, 0xb6, 0xac,
+ 0x70, 0xc3, 0xb6, 0x1f, 0x51, 0xf9, 0xb4, 0x6e, 0x80, 0xff, 0xde,
+ 0xae, 0x25, 0xeb, 0xdd, 0xb4, 0xaf, 0x6c, 0xb4, 0xee})),
+ std::make_pair(
+ 160,
+ std::vector<uint8_t>(
+ {0x2b, 0x9c, 0x95, 0x5e, 0x6c, 0xae, 0xd4, 0xb7, 0xc9, 0xe2, 0x46,
+ 0xb8, 0x6f, 0x9a, 0x17, 0x26, 0xe8, 0x10, 0xc5, 0x9d, 0x12, 0x6c,
+ 0xee, 0x66, 0xed, 0x71, 0xbf, 0x01, 0x5b, 0x83, 0x55, 0x8a, 0x4b,
+ 0x6d, 0x84, 0xd1, 0x8d, 0xc3, 0xff, 0x46, 0x20, 0xc2, 0xff, 0xb7,
+ 0x22, 0x35, 0x9f, 0xde, 0xf8, 0x5b, 0xa0, 0xd4, 0xe2, 0xd2, 0x2e,
+ 0xcb, 0xe0, 0xed, 0x78, 0x4f, 0x99, 0xaf, 0xe5, 0x87})),
+ std::make_pair(
+ 161,
+ std::vector<uint8_t>(
+ {0x18, 0x1d, 0xf0, 0xa2, 0x61, 0xa2, 0xf7, 0xd2, 0x9e, 0xa5, 0xa1,
+ 0x57, 0x72, 0x71, 0x51, 0x05, 0xd4, 0x50, 0xa4, 0xb6, 0xc2, 0x36,
+ 0xf6, 0x99, 0xf4, 0x62, 0xd6, 0x0c, 0xa7, 0x64, 0x87, 0xfe, 0xed,
+ 0xfc, 0x9f, 0x5e, 0xb9, 0x2d, 0xf8, 0x38, 0xe8, 0xfb, 0x5d, 0xc3,
+ 0x69, 0x4e, 0x84, 0xc5, 0xe0, 0xf4, 0xa1, 0x0b, 0x76, 0x1f, 0x50,
+ 0x67, 0x62, 0xbe, 0x05, 0x2c, 0x74, 0x5a, 0x6e, 0xe8})),
+ std::make_pair(
+ 162,
+ std::vector<uint8_t>(
+ {0x21, 0xfb, 0x20, 0x34, 0x58, 0xbf, 0x3a, 0x7e, 0x9a, 0x80, 0x43,
+ 0x9f, 0x9a, 0x90, 0x28, 0x99, 0xcd, 0x5d, 0xe0, 0x13, 0x9d, 0xfd,
+ 0x56, 0xf7, 0x11, 0x0c, 0x9d, 0xec, 0x84, 0x37, 0xb2, 0x6b, 0xda,
+ 0x63, 0xde, 0x2f, 0x56, 0x59, 0x26, 0xd8, 0x5e, 0xdb, 0x1d, 0x6c,
+ 0x68, 0x25, 0x66, 0x97, 0x43, 0xdd, 0x99, 0x92, 0x65, 0x3d, 0x13,
+ 0x97, 0x95, 0x44, 0xd5, 0xdc, 0x82, 0x28, 0xbf, 0xaa})),
+ std::make_pair(
+ 163,
+ std::vector<uint8_t>(
+ {0xef, 0x02, 0x1f, 0x29, 0xc5, 0xff, 0xb8, 0x30, 0xe6, 0x4b, 0x9a,
+ 0xa9, 0x05, 0x8d, 0xd6, 0x60, 0xfd, 0x2f, 0xcb, 0x81, 0xc4, 0x97,
+ 0xa7, 0xe6, 0x98, 0xbc, 0xfb, 0xf5, 0x9d, 0xe5, 0xad, 0x4a, 0x86,
+ 0xff, 0x93, 0xc1, 0x0a, 0x4b, 0x9d, 0x1a, 0xe5, 0x77, 0x47, 0x25,
+ 0xf9, 0x07, 0x2d, 0xcd, 0xe9, 0xe1, 0xf1, 0x99, 0xba, 0xb9, 0x1f,
+ 0x8b, 0xff, 0x92, 0x18, 0x64, 0xaa, 0x50, 0x2e, 0xee})),
+ std::make_pair(
+ 164,
+ std::vector<uint8_t>(
+ {0xb3, 0xcf, 0xda, 0x40, 0x52, 0x6b, 0x7f, 0x1d, 0x37, 0x56, 0x9b,
+ 0xdf, 0xcd, 0xf9, 0x11, 0xe5, 0xa6, 0xef, 0xe6, 0xb2, 0xec, 0x90,
+ 0xa0, 0x45, 0x4c, 0x47, 0xb2, 0xc0, 0x46, 0xbf, 0x13, 0x0f, 0xc3,
+ 0xb3, 0x52, 0xb3, 0x4d, 0xf4, 0x81, 0x3d, 0x48, 0xd3, 0x3a, 0xb8,
+ 0xe2, 0x69, 0xb6, 0x9b, 0x07, 0x56, 0x76, 0xcb, 0x6d, 0x00, 0xa8,
+ 0xdc, 0xf9, 0xe1, 0xf9, 0x67, 0xec, 0x19, 0x1b, 0x2c})),
+ std::make_pair(
+ 165,
+ std::vector<uint8_t>(
+ {0xb4, 0xc6, 0xc3, 0xb2, 0x67, 0x07, 0x1e, 0xef, 0xb9, 0xc8, 0xc7,
+ 0x2e, 0x0e, 0x2b, 0x94, 0x12, 0x93, 0x64, 0x1f, 0x86, 0x73, 0xcb,
+ 0x70, 0xc1, 0xcc, 0x26, 0xad, 0x1e, 0x73, 0xcf, 0x14, 0x17, 0x55,
+ 0x86, 0x0a, 0xd1, 0x9b, 0x34, 0xc2, 0xf3, 0x4e, 0xd3, 0x5b, 0xb5,
+ 0x2e, 0xc4, 0x50, 0x7c, 0xc1, 0xfe, 0x59, 0x04, 0x77, 0x43, 0xa5,
+ 0xf0, 0xc6, 0xfe, 0xbd, 0xe6, 0x25, 0xe2, 0x60, 0x91})),
+ std::make_pair(
+ 166,
+ std::vector<uint8_t>(
+ {0x57, 0xa3, 0x4f, 0x2b, 0xcc, 0xa6, 0x0d, 0x4b, 0x85, 0x10, 0x3b,
+ 0x83, 0x0c, 0x9d, 0x79, 0x52, 0xa4, 0x16, 0xbe, 0x52, 0x63, 0xae,
+ 0x42, 0x9c, 0x9e, 0x5e, 0x53, 0xfe, 0x85, 0x90, 0xa8, 0xf7, 0x8e,
+ 0xc6, 0x5a, 0x51, 0x10, 0x9e, 0xa8, 0x5d, 0xcd, 0xf7, 0xb6, 0x22,
+ 0x3f, 0x9f, 0x2b, 0x34, 0x05, 0x39, 0xfa, 0xd8, 0x19, 0x23, 0xdb,
+ 0xf8, 0xed, 0xab, 0xf9, 0x51, 0x29, 0xe4, 0xdf, 0xf6})),
+ std::make_pair(
+ 167,
+ std::vector<uint8_t>(
+ {0x9c, 0xf4, 0x66, 0x62, 0xfc, 0xd6, 0x1a, 0x23, 0x22, 0x77, 0xb6,
+ 0x85, 0x66, 0x3b, 0x8b, 0x5d, 0xa8, 0x32, 0xdf, 0xd9, 0xa3, 0xb8,
+ 0xcc, 0xfe, 0xec, 0x99, 0x3e, 0xc6, 0xac, 0x41, 0x5a, 0xd0, 0x7e,
+ 0x04, 0x8a, 0xdf, 0xe4, 0x14, 0xdf, 0x27, 0x27, 0x70, 0xdb, 0xa8,
+ 0x67, 0xda, 0x5c, 0x12, 0x24, 0xc6, 0xfd, 0x0a, 0xa0, 0xc2, 0x18,
+ 0x7d, 0x42, 0x6a, 0xc6, 0x47, 0xe9, 0x88, 0x73, 0x61})),
+ std::make_pair(
+ 168,
+ std::vector<uint8_t>(
+ {0x5c, 0xe1, 0x04, 0x2a, 0xb4, 0xd5, 0x42, 0xc2, 0xf9, 0xee, 0x9d,
+ 0x17, 0x26, 0x2a, 0xf8, 0x16, 0x40, 0x98, 0x93, 0x5b, 0xef, 0x17,
+ 0x3d, 0x0e, 0x18, 0x48, 0x9b, 0x04, 0x84, 0x17, 0x46, 0xcd, 0x2f,
+ 0x2d, 0xf8, 0x66, 0xbd, 0x7d, 0xa6, 0xe5, 0xef, 0x90, 0x24, 0xc6,
+ 0x48, 0x02, 0x3e, 0xc7, 0x23, 0xab, 0x9c, 0x62, 0xfd, 0x80, 0x28,
+ 0x57, 0x39, 0xd8, 0x4f, 0x15, 0xd2, 0xab, 0x51, 0x5a})),
+ std::make_pair(
+ 169,
+ std::vector<uint8_t>(
+ {0x84, 0x88, 0x39, 0x6b, 0xd4, 0xa8, 0x72, 0x9b, 0x7a, 0x47, 0x31,
+ 0x78, 0xf2, 0x32, 0xda, 0xdf, 0x3f, 0x0f, 0x8e, 0x22, 0x67, 0x8b,
+ 0xa5, 0xa4, 0x3e, 0x04, 0x1e, 0x72, 0xda, 0x1e, 0x2c, 0xf8, 0x21,
+ 0x94, 0xc3, 0x07, 0x20, 0x7a, 0x54, 0xcb, 0x81, 0x56, 0x29, 0x33,
+ 0x39, 0xea, 0xec, 0x69, 0x3f, 0xf6, 0x6b, 0xfc, 0xd5, 0xef, 0xc6,
+ 0x5e, 0x95, 0xe4, 0xec, 0xaf, 0x54, 0x53, 0x0a, 0xbd})),
+ std::make_pair(
+ 170,
+ std::vector<uint8_t>(
+ {0xf5, 0x98, 0xda, 0x90, 0x1c, 0x38, 0x35, 0xbc, 0xa5, 0x60, 0x77,
+ 0x90, 0x37, 0xdf, 0xde, 0x9f, 0x0c, 0x51, 0xdc, 0x61, 0xc0, 0xb7,
+ 0x60, 0xfc, 0x15, 0x22, 0xd7, 0xb4, 0x70, 0xee, 0x63, 0xf5, 0xbd,
+ 0xc6, 0x49, 0x84, 0x76, 0xe8, 0x60, 0x49, 0xad, 0x86, 0xe4, 0xe2,
+ 0x1a, 0xf2, 0x85, 0x4a, 0x98, 0x4c, 0xc9, 0x05, 0x42, 0x7d, 0x2f,
+ 0x17, 0xf6, 0x6b, 0x1f, 0x41, 0xc3, 0xda, 0x6f, 0x61})),
+ std::make_pair(
+ 171,
+ std::vector<uint8_t>(
+ {0x5f, 0x93, 0x26, 0x97, 0x98, 0xcf, 0x02, 0x13, 0x21, 0x07, 0x33,
+ 0x76, 0x60, 0xa8, 0xd7, 0xa1, 0x77, 0x35, 0x4c, 0x02, 0x12, 0xeb,
+ 0x93, 0xe5, 0x55, 0xe7, 0xc3, 0x7a, 0x08, 0xae, 0xf3, 0xd8, 0xdc,
+ 0xe0, 0x12, 0x17, 0x01, 0x1c, 0xd9, 0x65, 0xc0, 0x4d, 0xd2, 0xc1,
+ 0x05, 0xf2, 0xe2, 0xb6, 0xca, 0xe5, 0xe4, 0xe6, 0xbc, 0xaf, 0x09,
+ 0xdf, 0xbe, 0xe3, 0xe0, 0xa6, 0xa6, 0x35, 0x7c, 0x37})),
+ std::make_pair(
+ 172,
+ std::vector<uint8_t>(
+ {0x0e, 0xcf, 0x58, 0x1d, 0x47, 0xba, 0xc9, 0x23, 0x09, 0x86, 0xfa,
+ 0xab, 0xd7, 0x0c, 0x2f, 0x5b, 0x80, 0xe9, 0x10, 0x66, 0xf0, 0xec,
+ 0x55, 0xa8, 0x42, 0x93, 0x78, 0x82, 0x28, 0x6d, 0x2c, 0xa0, 0x07,
+ 0xbb, 0x4e, 0x97, 0x3b, 0x0b, 0x09, 0x1d, 0x52, 0x16, 0x7f, 0xf7,
+ 0xc4, 0x00, 0x9c, 0x7a, 0xb4, 0xad, 0x38, 0xff, 0xf1, 0xdc, 0xea,
+ 0xcd, 0xb7, 0xbe, 0x81, 0xef, 0x4a, 0x45, 0x29, 0x52})),
+ std::make_pair(
+ 173,
+ std::vector<uint8_t>(
+ {0x5a, 0xec, 0xa8, 0xab, 0xe1, 0x52, 0x85, 0x82, 0xb2, 0xa3, 0x07,
+ 0xb4, 0x00, 0x95, 0x85, 0x49, 0x8a, 0x3d, 0x46, 0x7c, 0xa6, 0x10,
+ 0x1c, 0xb0, 0xc5, 0x12, 0x6f, 0x99, 0x76, 0x05, 0x6e, 0x9f, 0xfc,
+ 0x12, 0x3c, 0xc2, 0x0c, 0x30, 0x2b, 0x2a, 0x73, 0x7f, 0x49, 0x2c,
+ 0x75, 0xd2, 0x1f, 0x01, 0x51, 0x2c, 0x90, 0xca, 0x05, 0x41, 0xdf,
+ 0xa5, 0x6e, 0x95, 0x0a, 0x32, 0x1d, 0xcb, 0x28, 0xd8})),
+ std::make_pair(
+ 174,
+ std::vector<uint8_t>(
+ {0x73, 0x2f, 0xbf, 0x8f, 0x1c, 0xb2, 0xb8, 0x32, 0x92, 0x63, 0xed,
+ 0xe2, 0x78, 0x58, 0xfe, 0x46, 0xf8, 0xd3, 0x35, 0x4d, 0x37, 0x6b,
+ 0xcd, 0xa0, 0x54, 0x8e, 0x7c, 0xe1, 0xfa, 0x9d, 0xd1, 0x1f, 0x85,
+ 0xeb, 0x66, 0x1f, 0xe9, 0x50, 0xb5, 0x43, 0xaa, 0x63, 0x5c, 0xa4,
+ 0xd3, 0xf0, 0x4e, 0xde, 0x5b, 0x32, 0xd6, 0xb6, 0x56, 0xe5, 0xce,
+ 0x1c, 0x44, 0xd3, 0x5c, 0x4a, 0x6c, 0x56, 0xcf, 0xf8})),
+ std::make_pair(
+ 175,
+ std::vector<uint8_t>(
+ {0xd5, 0xe9, 0x38, 0x73, 0x5d, 0x63, 0x78, 0x8c, 0x80, 0x10, 0x0a,
+ 0xef, 0xd1, 0x86, 0x48, 0xd1, 0x8c, 0xf2, 0x72, 0xf6, 0x9f, 0x20,
+ 0xff, 0x24, 0xcf, 0xe2, 0x89, 0x5c, 0x08, 0x8a, 0xd0, 0x8b, 0x01,
+ 0x04, 0xda, 0x16, 0x72, 0xa4, 0xeb, 0x26, 0xfc, 0x52, 0x54, 0x5c,
+ 0xc7, 0xd7, 0xa0, 0x1b, 0x26, 0x6c, 0xf5, 0x46, 0xc4, 0x03, 0xc4,
+ 0x5b, 0xd1, 0x29, 0xeb, 0x41, 0xbd, 0xd9, 0x20, 0x0b})),
+ std::make_pair(
+ 176,
+ std::vector<uint8_t>(
+ {0x65, 0xa2, 0x45, 0xb4, 0x93, 0x52, 0xee, 0x29, 0x7d, 0x91, 0xaf,
+ 0x8c, 0x8b, 0xe0, 0x05, 0x28, 0xac, 0x6e, 0x04, 0x6d, 0xd8, 0x3a,
+ 0xc7, 0xbd, 0x46, 0x5a, 0x98, 0x81, 0x6d, 0xd6, 0x8f, 0x3e, 0x00,
+ 0xe1, 0xae, 0x8f, 0x89, 0x53, 0x27, 0xa7, 0xe9, 0xa8, 0xc9, 0x32,
+ 0x65, 0x98, 0x37, 0x9a, 0x29, 0xc9, 0xfc, 0x91, 0xec, 0x0c, 0x6e,
+ 0xef, 0x08, 0xf3, 0xe2, 0xb2, 0x16, 0xc1, 0x10, 0x08})),
+ std::make_pair(
+ 177,
+ std::vector<uint8_t>(
+ {0xc9, 0x56, 0x54, 0xb6, 0x30, 0x19, 0x13, 0x0a, 0xb4, 0x5d, 0xd0,
+ 0xfb, 0x49, 0x41, 0xb9, 0x8a, 0xeb, 0x3a, 0xf2, 0xa1, 0x23, 0x91,
+ 0x3e, 0xca, 0x2c, 0xe9, 0x9b, 0x3e, 0x97, 0x41, 0x0a, 0x7b, 0xf8,
+ 0x66, 0x1c, 0xc7, 0xfb, 0xaa, 0x2b, 0xc1, 0xcf, 0x2b, 0x13, 0x11,
+ 0x3b, 0x1e, 0xd4, 0x0a, 0x01, 0x18, 0xb8, 0x8e, 0x5f, 0xff, 0xc3,
+ 0x54, 0x27, 0x59, 0xea, 0x00, 0x7e, 0xd4, 0xc5, 0x8d})),
+ std::make_pair(
+ 178,
+ std::vector<uint8_t>(
+ {0x1e, 0xb2, 0x62, 0xf3, 0x8f, 0xa4, 0x94, 0x43, 0x1f, 0x01, 0x7d,
+ 0xad, 0x44, 0xc0, 0xdf, 0xb6, 0x93, 0x24, 0xac, 0x03, 0x2f, 0x04,
+ 0xb6, 0x57, 0xfc, 0x91, 0xa8, 0x86, 0x47, 0xbb, 0x74, 0x76, 0x0f,
+ 0x24, 0xe7, 0xc9, 0x56, 0x51, 0x4f, 0x0c, 0xf0, 0x02, 0x99, 0x0b,
+ 0x18, 0x2c, 0x16, 0x42, 0xb9, 0xb2, 0x42, 0x6e, 0x96, 0xa6, 0x11,
+ 0x87, 0xe4, 0xe0, 0x12, 0xf0, 0x0e, 0x21, 0x7d, 0x84})),
+ std::make_pair(
+ 179,
+ std::vector<uint8_t>(
+ {0x3b, 0x95, 0x5a, 0xee, 0xbf, 0xa5, 0x15, 0x1a, 0xc1, 0xab, 0x8e,
+ 0x3f, 0x5c, 0xc1, 0xe3, 0x76, 0x70, 0x84, 0xc8, 0x42, 0xa5, 0x75,
+ 0xd3, 0x62, 0x69, 0x83, 0x6e, 0x97, 0x35, 0x3d, 0x41, 0x62, 0x2b,
+ 0x73, 0x1d, 0xdd, 0xcd, 0x5f, 0x26, 0x95, 0x50, 0xa3, 0xa5, 0xb8,
+ 0x7b, 0xe1, 0xe9, 0x03, 0x26, 0x34, 0x0b, 0x6e, 0x0e, 0x62, 0x55,
+ 0x58, 0x15, 0xd9, 0x60, 0x05, 0x97, 0xac, 0x6e, 0xf9})),
+ std::make_pair(
+ 180,
+ std::vector<uint8_t>(
+ {0x68, 0x28, 0x9f, 0x66, 0x05, 0x47, 0x3b, 0xa0, 0xe4, 0xf2, 0x41,
+ 0xba, 0xf7, 0x47, 0x7a, 0x98, 0x85, 0x42, 0x6a, 0x85, 0x8f, 0x19,
+ 0xef, 0x2a, 0x18, 0xb0, 0xd4, 0x0e, 0xf8, 0xe4, 0x12, 0x82, 0xed,
+ 0x55, 0x26, 0xb5, 0x19, 0x79, 0x9e, 0x27, 0x0f, 0x13, 0x88, 0x13,
+ 0x27, 0x91, 0x82, 0x78, 0x75, 0x57, 0x11, 0x07, 0x1d, 0x85, 0x11,
+ 0xfe, 0x96, 0x3e, 0x3b, 0x56, 0x06, 0xaa, 0x37, 0x16})),
+ std::make_pair(
+ 181,
+ std::vector<uint8_t>(
+ {0x80, 0xa3, 0x37, 0x87, 0x54, 0x26, 0x12, 0xc3, 0x8f, 0x6b, 0xcd,
+ 0x7c, 0xd8, 0x6c, 0xab, 0x46, 0x02, 0x27, 0x50, 0x9b, 0x1c, 0xba,
+ 0xd5, 0xec, 0x40, 0x8a, 0x91, 0x41, 0x3d, 0x51, 0x15, 0x5a, 0x04,
+ 0x76, 0xda, 0xdb, 0xf3, 0xa2, 0x51, 0x8e, 0x4a, 0x6e, 0x77, 0xcc,
+ 0x34, 0x66, 0x22, 0xe3, 0x47, 0xa4, 0x69, 0xbf, 0x8b, 0xaa, 0x5f,
+ 0x04, 0xeb, 0x2d, 0x98, 0x70, 0x53, 0x55, 0xd0, 0x63})),
+ std::make_pair(
+ 182,
+ std::vector<uint8_t>(
+ {0x34, 0x62, 0x9b, 0xc6, 0xd8, 0x31, 0x39, 0x1c, 0x4c, 0xdf, 0x8a,
+ 0xf1, 0xb4, 0xb7, 0xb6, 0xb8, 0xe8, 0xee, 0x17, 0xcf, 0x98, 0xc7,
+ 0x0e, 0x5d, 0xd5, 0x86, 0xcd, 0x99, 0xf1, 0x4b, 0x11, 0xdf, 0x94,
+ 0x51, 0x66, 0x23, 0x6a, 0x95, 0x71, 0xe6, 0xd5, 0x91, 0xbb, 0x83,
+ 0xee, 0x4d, 0x16, 0x4d, 0x46, 0xf6, 0xb9, 0xd8, 0xef, 0x86, 0xff,
+ 0x86, 0x5a, 0x81, 0xbf, 0xb9, 0x1b, 0x00, 0x42, 0x4b})),
+ std::make_pair(
+ 183,
+ std::vector<uint8_t>(
+ {0x8b, 0x7c, 0xc3, 0x39, 0x16, 0x38, 0x63, 0xbb, 0x43, 0x83, 0xe5,
+ 0x42, 0xb0, 0xef, 0x0e, 0x7c, 0xf3, 0x6b, 0x84, 0xad, 0x93, 0x2c,
+ 0xdf, 0x5a, 0x80, 0x41, 0x9e, 0xc9, 0xad, 0x69, 0x2e, 0x7a, 0x7e,
+ 0x78, 0x4d, 0x2c, 0x7c, 0xb3, 0x79, 0x6a, 0x18, 0xb8, 0xf8, 0x00,
+ 0x03, 0x5f, 0x3a, 0xa0, 0x6c, 0x82, 0x41, 0x00, 0x61, 0x11, 0x20,
+ 0xa7, 0xbd, 0xeb, 0x35, 0x61, 0x8c, 0xcb, 0x81, 0xb7})),
+ std::make_pair(
+ 184,
+ std::vector<uint8_t>(
+ {0x4f, 0x08, 0x4e, 0x49, 0x39, 0xdd, 0x5a, 0x7f, 0x5a, 0x65, 0x8f,
+ 0xad, 0x58, 0xa1, 0x8a, 0x15, 0xc2, 0x5c, 0x32, 0xec, 0x1c, 0x7f,
+ 0xd5, 0xc5, 0xc6, 0xc3, 0xe8, 0x92, 0xb3, 0x97, 0x1a, 0xea, 0xac,
+ 0x30, 0x83, 0x04, 0xef, 0x17, 0xb1, 0xc4, 0x72, 0x39, 0xea, 0x4b,
+ 0xb3, 0x98, 0xb3, 0xfd, 0x6d, 0x45, 0x28, 0xd8, 0xde, 0x8e, 0x76,
+ 0x8a, 0xe0, 0xf1, 0xa5, 0xa5, 0xc6, 0xb5, 0xc2, 0x97})),
+ std::make_pair(
+ 185,
+ std::vector<uint8_t>(
+ {0x48, 0xf4, 0x07, 0xa1, 0xaf, 0x5b, 0x80, 0x09, 0xb2, 0x05, 0x17,
+ 0x42, 0xe8, 0xcf, 0x5c, 0xd5, 0x65, 0x66, 0x69, 0xe7, 0xd7, 0x22,
+ 0xee, 0x8e, 0x7b, 0xd2, 0x02, 0x06, 0x08, 0x49, 0x44, 0x21, 0x68,
+ 0xd8, 0xfa, 0xcc, 0x11, 0x7c, 0x01, 0x2b, 0xfb, 0x7b, 0xf4, 0x49,
+ 0xd9, 0x9b, 0xef, 0xff, 0x6a, 0x34, 0xae, 0xa2, 0x03, 0xf1, 0xd8,
+ 0xd3, 0x52, 0x72, 0x2b, 0xe5, 0x01, 0x4e, 0xc8, 0x18})),
+ std::make_pair(
+ 186,
+ std::vector<uint8_t>(
+ {0xa6, 0xaa, 0x82, 0xcd, 0x1e, 0x42, 0x6f, 0x9a, 0x73, 0xbf, 0xa3,
+ 0x9a, 0x29, 0x03, 0x78, 0x76, 0x11, 0x46, 0x55, 0xb8, 0xc2, 0x2d,
+ 0x6d, 0x3f, 0xf8, 0xb6, 0x38, 0xae, 0x7d, 0xea, 0x6b, 0x17, 0x84,
+ 0x3e, 0x09, 0xe5, 0x2e, 0xb6, 0x6f, 0xa1, 0xe4, 0x75, 0xe4, 0xa8,
+ 0xa3, 0xde, 0x42, 0x9b, 0x7d, 0x0f, 0x4a, 0x77, 0x6f, 0xcb, 0x8b,
+ 0xdc, 0x9b, 0x9f, 0xed, 0xe7, 0xd5, 0x2e, 0x81, 0x5f})),
+ std::make_pair(
+ 187,
+ std::vector<uint8_t>(
+ {0x58, 0x17, 0x02, 0x7d, 0x6b, 0xdd, 0x00, 0xc5, 0xdd, 0x10, 0xac,
+ 0x59, 0x3c, 0xd5, 0x60, 0x37, 0x22, 0x70, 0x77, 0x5a, 0x18, 0x52,
+ 0x6d, 0x7e, 0x6f, 0x13, 0x87, 0x2a, 0x2e, 0x20, 0xea, 0xb6, 0x64,
+ 0x62, 0x5b, 0xe7, 0x16, 0x8a, 0xc4, 0xbd, 0x7c, 0x9e, 0x0c, 0xe7,
+ 0xfc, 0x40, 0x99, 0xe0, 0xf4, 0x84, 0x42, 0xe2, 0xc7, 0x67, 0x19,
+ 0x1c, 0x6e, 0x12, 0x84, 0xe9, 0xb2, 0xcc, 0xea, 0x8c})),
+ std::make_pair(
+ 188,
+ std::vector<uint8_t>(
+ {0x08, 0xe4, 0x10, 0x28, 0x34, 0x0a, 0x45, 0xc7, 0x4e, 0x40, 0x52,
+ 0xb3, 0xa8, 0xd6, 0x38, 0x9e, 0x22, 0xe0, 0x43, 0xa1, 0xad, 0xab,
+ 0x5e, 0x28, 0xd9, 0x76, 0x19, 0x45, 0x0d, 0x72, 0x34, 0x69, 0xb6,
+ 0x20, 0xca, 0xa5, 0x19, 0xb8, 0x1c, 0x14, 0x52, 0x38, 0x54, 0xf6,
+ 0x19, 0xfd, 0x30, 0x27, 0xe3, 0x84, 0x7b, 0xd0, 0x32, 0x76, 0xe6,
+ 0x06, 0x04, 0xa8, 0x0d, 0xdb, 0x4d, 0xe8, 0x76, 0xd6})),
+ std::make_pair(
+ 189,
+ std::vector<uint8_t>(
+ {0x13, 0x0b, 0x84, 0x20, 0x53, 0x7e, 0xb0, 0x7d, 0x72, 0xab, 0xda,
+ 0x07, 0xc8, 0x5a, 0xcb, 0xd8, 0xb9, 0xa4, 0x4f, 0x16, 0x32, 0x1d,
+ 0xd0, 0x42, 0x21, 0x45, 0xf8, 0x09, 0x67, 0x3d, 0x30, 0xf2, 0xb5,
+ 0x32, 0x13, 0x26, 0xe2, 0xbf, 0xf3, 0x17, 0xef, 0x3f, 0xef, 0x98,
+ 0x3c, 0x51, 0xc4, 0xf8, 0xab, 0x24, 0xa3, 0x25, 0xd2, 0x98, 0xe3,
+ 0x4a, 0xfc, 0xe5, 0x69, 0xa8, 0x25, 0x55, 0x77, 0x4c})),
+ std::make_pair(
+ 190,
+ std::vector<uint8_t>(
+ {0xac, 0x49, 0xb8, 0x44, 0xaf, 0xaa, 0x01, 0x2e, 0x31, 0xc4, 0x74,
+ 0xca, 0x26, 0x36, 0x48, 0x84, 0x4f, 0xd2, 0xf6, 0x30, 0x79, 0x92,
+ 0xc2, 0xf7, 0x52, 0xac, 0xa0, 0x2c, 0x38, 0x28, 0x96, 0x51, 0x75,
+ 0x79, 0x4d, 0xee, 0xe2, 0xd2, 0xee, 0x95, 0xc6, 0x1c, 0xd2, 0x84,
+ 0xf6, 0xb5, 0xa2, 0xd7, 0x5e, 0x2e, 0xf2, 0xb2, 0x9e, 0xe8, 0x14,
+ 0x9e, 0x77, 0xfb, 0x81, 0x44, 0x7b, 0x2f, 0xd0, 0x4b})),
+ std::make_pair(
+ 191,
+ std::vector<uint8_t>(
+ {0xb9, 0xd7, 0xca, 0x81, 0xcc, 0x60, 0xbb, 0x95, 0x78, 0xe4, 0x40,
+ 0x24, 0xe5, 0xa0, 0xa0, 0xbe, 0x80, 0xf2, 0x73, 0x36, 0xa6, 0xa9,
+ 0xf4, 0xe5, 0x3d, 0xf3, 0x99, 0x9c, 0xb1, 0x91, 0x28, 0x0b, 0x09,
+ 0x0e, 0x2a, 0xc2, 0xd2, 0x9c, 0x5b, 0xaa, 0xd9, 0xd7, 0x14, 0x15,
+ 0xbd, 0xc1, 0x29, 0xe6, 0x9a, 0xa2, 0x66, 0x7a, 0xf6, 0xa7, 0xfd,
+ 0x5e, 0x18, 0x9f, 0xcc, 0xdc, 0xee, 0x81, 0x73, 0x40})),
+ std::make_pair(
+ 192,
+ std::vector<uint8_t>(
+ {0xa7, 0x55, 0xe1, 0x13, 0x38, 0x65, 0x72, 0xc7, 0x5c, 0xed, 0x61,
+ 0xd7, 0x19, 0x70, 0x60, 0x70, 0xb9, 0x14, 0x60, 0x48, 0xe4, 0x2a,
+ 0x9f, 0x8c, 0xd3, 0x56, 0x67, 0xa0, 0x88, 0xb4, 0x2f, 0x08, 0x80,
+ 0x8a, 0xbd, 0xf7, 0x7e, 0x61, 0x8a, 0xbd, 0x95, 0x9a, 0xfc, 0x75,
+ 0x73, 0x79, 0xca, 0x2c, 0x00, 0xbc, 0xc1, 0xa4, 0x83, 0x90, 0xfa,
+ 0x2b, 0xff, 0x61, 0x8b, 0x1e, 0x00, 0x78, 0xa6, 0x13})),
+ std::make_pair(
+ 193,
+ std::vector<uint8_t>(
+ {0xa7, 0x3c, 0x7d, 0xeb, 0xed, 0x32, 0x6f, 0x1c, 0x0d, 0xb0, 0x79,
+ 0x5e, 0xe7, 0xd6, 0xe3, 0x94, 0x68, 0x94, 0xb8, 0x26, 0xb1, 0xf8,
+ 0x10, 0x1c, 0x56, 0xc8, 0x23, 0xba, 0x17, 0x16, 0x83, 0x12, 0xe7,
+ 0xf5, 0x3f, 0xc7, 0xdb, 0xe5, 0x2c, 0x3e, 0x11, 0xe6, 0x98, 0x52,
+ 0xc4, 0x04, 0x85, 0xe2, 0xef, 0x18, 0x24, 0x77, 0x86, 0x2e, 0xa6,
+ 0xa3, 0x4e, 0xc1, 0x36, 0xe2, 0xdf, 0xee, 0xa6, 0xf4})),
+ std::make_pair(
+ 194,
+ std::vector<uint8_t>(
+ {0x6c, 0xb8, 0xf9, 0xd5, 0x2c, 0x56, 0xd8, 0x2c, 0xac, 0x28, 0xf3,
+ 0x9e, 0xa1, 0x59, 0x3e, 0x8b, 0xb2, 0x50, 0x62, 0x93, 0xac, 0x0d,
+ 0x68, 0x37, 0x6a, 0x17, 0x09, 0xb6, 0x2a, 0x46, 0xdf, 0x14, 0xa4,
+ 0xae, 0x64, 0xb2, 0xd8, 0xfa, 0xb7, 0x67, 0x33, 0xa1, 0xce, 0xd2,
+ 0xd5, 0x48, 0xe3, 0xf3, 0xc6, 0xfc, 0xb4, 0x9d, 0x40, 0xc3, 0xd5,
+ 0x80, 0x8e, 0x44, 0x9c, 0xd8, 0x3d, 0x1c, 0x2a, 0xa2})),
+ std::make_pair(
+ 195,
+ std::vector<uint8_t>(
+ {0x68, 0x3f, 0xa2, 0xb2, 0x36, 0x9a, 0x10, 0x16, 0x2c, 0x1c, 0x1c,
+ 0x7b, 0x24, 0xbc, 0x97, 0x0e, 0xe6, 0x7d, 0xa2, 0x20, 0x56, 0x4f,
+ 0x32, 0x20, 0x3f, 0x62, 0x56, 0x96, 0xc0, 0x35, 0x2a, 0x0b, 0x9a,
+ 0xd9, 0x66, 0x24, 0x36, 0x2d, 0x95, 0x2d, 0x84, 0x46, 0x3c, 0x11,
+ 0x06, 0xa2, 0xdb, 0xa7, 0xa0, 0x92, 0x59, 0x98, 0x84, 0xb3, 0x5a,
+ 0x0b, 0x89, 0xc8, 0xf1, 0xb6, 0xa9, 0xb5, 0xa6, 0x1e})),
+ std::make_pair(
+ 196,
+ std::vector<uint8_t>(
+ {0xaa, 0xd9, 0xad, 0x44, 0x61, 0x01, 0x18, 0xb7, 0x7d, 0x50, 0x8a,
+ 0xeb, 0x1b, 0xbc, 0xd1, 0xc1, 0xb7, 0xd0, 0x17, 0x13, 0x97, 0xfb,
+ 0x51, 0x0a, 0x40, 0x1b, 0xbc, 0x0e, 0xc3, 0x46, 0x23, 0x67, 0x0d,
+ 0x86, 0xa2, 0xdc, 0x3c, 0x8f, 0x3a, 0xb5, 0xa2, 0x04, 0x4d, 0xf7,
+ 0x30, 0x25, 0x67, 0x27, 0x54, 0x5f, 0x08, 0x60, 0xce, 0x21, 0xa1,
+ 0xea, 0xc7, 0x17, 0xdf, 0xc4, 0x8f, 0x5d, 0x22, 0x8e})),
+ std::make_pair(
+ 197,
+ std::vector<uint8_t>(
+ {0xc4, 0x25, 0x78, 0xde, 0x23, 0xb4, 0xc9, 0x87, 0xd5, 0xe1, 0xac,
+ 0x4d, 0x68, 0x9e, 0xd5, 0xde, 0x4b, 0x04, 0x17, 0xf9, 0x70, 0x4b,
+ 0xc6, 0xbc, 0xe9, 0x69, 0xfa, 0x13, 0x47, 0x15, 0x85, 0xd6, 0x2c,
+ 0x2c, 0xb1, 0x21, 0x2a, 0x94, 0x4f, 0x39, 0x7f, 0xc9, 0xca, 0x2c,
+ 0x37, 0x47, 0xc3, 0xbe, 0xb6, 0x94, 0xec, 0x4c, 0x5b, 0xe6, 0x88,
+ 0x28, 0xdd, 0xa5, 0x3e, 0xf4, 0x3f, 0xae, 0xc6, 0xc0})),
+ std::make_pair(
+ 198,
+ std::vector<uint8_t>(
+ {0x47, 0x0f, 0x00, 0x84, 0x1e, 0xe8, 0x24, 0x4e, 0x63, 0xed, 0x2c,
+ 0x7e, 0xa3, 0x0e, 0x2e, 0x41, 0x98, 0x97, 0xc1, 0x97, 0x46, 0x2e,
+ 0xcc, 0xce, 0xcf, 0x71, 0x3b, 0x42, 0xa5, 0x06, 0x5f, 0xff, 0x59,
+ 0x14, 0xbc, 0x9b, 0x79, 0xaf, 0xfe, 0x8f, 0x6b, 0x65, 0x78, 0x75,
+ 0xe7, 0x89, 0xae, 0x21, 0x3b, 0xd9, 0x14, 0xcd, 0x35, 0xbd, 0x17,
+ 0x4d, 0x46, 0xe9, 0xd1, 0x8b, 0xd8, 0x43, 0x77, 0x3d})),
+ std::make_pair(
+ 199,
+ std::vector<uint8_t>(
+ {0x34, 0xfc, 0x42, 0x13, 0x73, 0x0f, 0x47, 0xa5, 0xe9, 0xa3, 0x58,
+ 0x0f, 0x64, 0x3e, 0x12, 0x94, 0x5c, 0xfc, 0xb3, 0x1b, 0xf2, 0x06,
+ 0xf6, 0xad, 0x45, 0x0c, 0xe5, 0x28, 0xda, 0x3f, 0xa4, 0x32, 0xe0,
+ 0x05, 0xd6, 0xb0, 0xec, 0xce, 0x10, 0xdc, 0xa7, 0xc5, 0x99, 0x5f,
+ 0x6a, 0xac, 0xc5, 0x15, 0x0e, 0x1b, 0x00, 0x9e, 0x19, 0x75, 0x1e,
+ 0x83, 0x09, 0xf8, 0x85, 0x95, 0x31, 0x84, 0x43, 0x74})),
+ std::make_pair(
+ 200,
+ std::vector<uint8_t>(
+ {0xfb, 0x3c, 0x1f, 0x0f, 0x56, 0xa5, 0x6f, 0x8e, 0x31, 0x6f, 0xdf,
+ 0x5d, 0x85, 0x3c, 0x8c, 0x87, 0x2c, 0x39, 0x63, 0x5d, 0x08, 0x36,
+ 0x34, 0xc3, 0x90, 0x4f, 0xc3, 0xac, 0x07, 0xd1, 0xb5, 0x78, 0xe8,
+ 0x5f, 0xf0, 0xe4, 0x80, 0xe9, 0x2d, 0x44, 0xad, 0xe3, 0x3b, 0x62,
+ 0xe8, 0x93, 0xee, 0x32, 0x34, 0x3e, 0x79, 0xdd, 0xf6, 0xef, 0x29,
+ 0x2e, 0x89, 0xb5, 0x82, 0xd3, 0x12, 0x50, 0x23, 0x14})),
+ std::make_pair(
+ 201,
+ std::vector<uint8_t>(
+ {0xc7, 0xc9, 0x7f, 0xc6, 0x5d, 0xd2, 0xb9, 0xe3, 0xd3, 0xd6, 0x07,
+ 0xd3, 0x15, 0x98, 0xd3, 0xf8, 0x42, 0x61, 0xe9, 0x91, 0x92, 0x51,
+ 0xe9, 0xc8, 0xe5, 0x7b, 0xb5, 0xf8, 0x29, 0x37, 0x7d, 0x5f, 0x73,
+ 0xea, 0xbb, 0xed, 0x55, 0xc6, 0xc3, 0x81, 0x18, 0x0f, 0x29, 0xad,
+ 0x02, 0xe5, 0xbe, 0x79, 0x7f, 0xfe, 0xc7, 0xe5, 0x7b, 0xde, 0xcb,
+ 0xc5, 0x0a, 0xd3, 0xd0, 0x62, 0xf0, 0x99, 0x3a, 0xb0})),
+ std::make_pair(
+ 202,
+ std::vector<uint8_t>(
+ {0xa5, 0x7a, 0x49, 0xcd, 0xbe, 0x67, 0xae, 0x7d, 0x9f, 0x79, 0x7b,
+ 0xb5, 0xcc, 0x7e, 0xfc, 0x2d, 0xf0, 0x7f, 0x4e, 0x1b, 0x15, 0x95,
+ 0x5f, 0x85, 0xda, 0xe7, 0x4b, 0x76, 0xe2, 0xec, 0xb8, 0x5a, 0xfb,
+ 0x6c, 0xd9, 0xee, 0xed, 0x88, 0x88, 0xd5, 0xca, 0x3e, 0xc5, 0xab,
+ 0x65, 0xd2, 0x7a, 0x7b, 0x19, 0xe5, 0x78, 0x47, 0x57, 0x60, 0xa0,
+ 0x45, 0xac, 0x3c, 0x92, 0xe1, 0x3a, 0x93, 0x8e, 0x77})),
+ std::make_pair(
+ 203,
+ std::vector<uint8_t>(
+ {0xc7, 0x14, 0x3f, 0xce, 0x96, 0x14, 0xa1, 0x7f, 0xd6, 0x53, 0xae,
+ 0xb1, 0x40, 0x72, 0x6d, 0xc9, 0xc3, 0xdb, 0xb1, 0xde, 0x6c, 0xc5,
+ 0x81, 0xb2, 0x72, 0x68, 0x97, 0xec, 0x24, 0xb7, 0xa5, 0x03, 0x59,
+ 0xad, 0x49, 0x22, 0x43, 0xbe, 0x66, 0xd9, 0xed, 0xd8, 0xc9, 0x33,
+ 0xb5, 0xb8, 0x0e, 0x0b, 0x91, 0xbb, 0x61, 0xea, 0x98, 0x05, 0x60,
+ 0x06, 0x51, 0x69, 0x76, 0xfa, 0xe8, 0xd9, 0x9a, 0x35})),
+ std::make_pair(
+ 204,
+ std::vector<uint8_t>(
+ {0x65, 0xbb, 0x58, 0xd0, 0x7f, 0x93, 0x7e, 0x2d, 0x3c, 0x7e, 0x65,
+ 0x38, 0x5f, 0x9c, 0x54, 0x73, 0x0b, 0x70, 0x41, 0x05, 0xcc, 0xdb,
+ 0x69, 0x1f, 0x6e, 0x14, 0x6d, 0x4e, 0xe8, 0xf6, 0xc0, 0x86, 0xf4,
+ 0x95, 0x11, 0x03, 0x51, 0x10, 0xa9, 0xad, 0x60, 0x31, 0xfd, 0xce,
+ 0xb9, 0x43, 0xe0, 0xf9, 0x61, 0x3b, 0xcb, 0x27, 0x6d, 0xd4, 0x0f,
+ 0x06, 0x24, 0xef, 0x0f, 0x92, 0x4f, 0x80, 0x97, 0x83})),
+ std::make_pair(
+ 205,
+ std::vector<uint8_t>(
+ {0xe5, 0x40, 0x27, 0x7f, 0x68, 0x3b, 0x11, 0x86, 0xdd, 0x3b, 0x5b,
+ 0x3f, 0x61, 0x43, 0x33, 0x96, 0x58, 0x1a, 0x35, 0xfe, 0xb1, 0x20,
+ 0x02, 0xbe, 0x8c, 0x6a, 0x62, 0x31, 0xfc, 0x40, 0xff, 0xa7, 0x0f,
+ 0x08, 0x08, 0x1b, 0xc5, 0x8b, 0x2d, 0x94, 0xf7, 0x64, 0x95, 0x43,
+ 0x61, 0x4a, 0x43, 0x5f, 0xaa, 0x2d, 0x62, 0x11, 0x0e, 0x13, 0xda,
+ 0xbc, 0x7b, 0x86, 0x62, 0x9b, 0x63, 0xaf, 0x9c, 0x24})),
+ std::make_pair(
+ 206,
+ std::vector<uint8_t>(
+ {0x41, 0x85, 0x00, 0x87, 0x8c, 0x5f, 0xbc, 0xb5, 0x84, 0xc4, 0x32,
+ 0xf4, 0x28, 0x5e, 0x05, 0xe4, 0x9f, 0x2e, 0x3e, 0x07, 0x53, 0x99,
+ 0xa0, 0xdb, 0xfc, 0xf8, 0x74, 0xeb, 0xf8, 0xc0, 0x3d, 0x02, 0xbf,
+ 0x16, 0xbc, 0x69, 0x89, 0xd1, 0x61, 0xc7, 0x7c, 0xa0, 0x78, 0x6b,
+ 0x05, 0x05, 0x3c, 0x6c, 0x70, 0x94, 0x33, 0x71, 0x23, 0x19, 0x19,
+ 0x21, 0x28, 0x83, 0x5c, 0xf0, 0xb6, 0x60, 0x59, 0x5b})),
+ std::make_pair(
+ 207,
+ std::vector<uint8_t>(
+ {0x88, 0x90, 0x90, 0xdb, 0xb1, 0x94, 0x4b, 0xdc, 0x94, 0x33, 0xee,
+ 0x5e, 0xf1, 0x01, 0x0c, 0x7a, 0x4a, 0x24, 0xa8, 0xe7, 0x1e, 0xce,
+ 0xa8, 0xe1, 0x2a, 0x31, 0x31, 0x8c, 0xe4, 0x9d, 0xca, 0xb0, 0xac,
+ 0xa5, 0xc3, 0x80, 0x23, 0x34, 0xaa, 0xb2, 0xcc, 0x84, 0xb1, 0x4c,
+ 0x6b, 0x93, 0x21, 0xfe, 0x58, 0x6b, 0xf3, 0xf8, 0x76, 0xf1, 0x9c,
+ 0xd4, 0x06, 0xeb, 0x11, 0x27, 0xfb, 0x94, 0x48, 0x01})),
+ std::make_pair(
+ 208,
+ std::vector<uint8_t>(
+ {0x53, 0xb6, 0xa2, 0x89, 0x10, 0xaa, 0x92, 0xe2, 0x7e, 0x53, 0x6f,
+ 0xb5, 0x49, 0xcf, 0x9b, 0x99, 0x18, 0x79, 0x10, 0x60, 0x89, 0x8e,
+ 0x0b, 0x9f, 0xe1, 0x83, 0x57, 0x7f, 0xf4, 0x3b, 0x5e, 0x9c, 0x76,
+ 0x89, 0xc7, 0x45, 0xb3, 0x2e, 0x41, 0x22, 0x69, 0x83, 0x7c, 0x31,
+ 0xb8, 0x9e, 0x6c, 0xc1, 0x2b, 0xf7, 0x6e, 0x13, 0xca, 0xd3, 0x66,
+ 0xb7, 0x4e, 0xce, 0x48, 0xbb, 0x85, 0xfd, 0x09, 0xe9})),
+ std::make_pair(
+ 209,
+ std::vector<uint8_t>(
+ {0x7c, 0x09, 0x20, 0x80, 0xc6, 0xa8, 0x0d, 0x67, 0x24, 0x09, 0xd0,
+ 0x81, 0xd3, 0xd1, 0x77, 0x10, 0x6b, 0xcd, 0x63, 0x56, 0x77, 0x85,
+ 0x14, 0x07, 0x19, 0x49, 0x09, 0x50, 0xae, 0x07, 0xae, 0x8f, 0xca,
+ 0xab, 0xba, 0xaa, 0xb3, 0x30, 0xcf, 0xbc, 0xf7, 0x37, 0x44, 0x82,
+ 0xc2, 0x20, 0xaf, 0x2e, 0xad, 0xee, 0xb7, 0x3d, 0xcb, 0xb3, 0x5e,
+ 0xd8, 0x23, 0x34, 0x4e, 0x14, 0x4e, 0x7d, 0x48, 0x99})),
+ std::make_pair(
+ 210,
+ std::vector<uint8_t>(
+ {0x9c, 0xcd, 0xe5, 0x66, 0xd2, 0x40, 0x05, 0x09, 0x18, 0x11, 0x11,
+ 0xf3, 0x2d, 0xde, 0x4c, 0xd6, 0x32, 0x09, 0xfe, 0x59, 0xa3, 0x0c,
+ 0x11, 0x45, 0x46, 0xad, 0x27, 0x76, 0xd8, 0x89, 0xa4, 0x1b, 0xad,
+ 0x8f, 0xa1, 0xbb, 0x46, 0x8c, 0xb2, 0xf9, 0xd4, 0x2c, 0xa9, 0x92,
+ 0x8a, 0x77, 0x70, 0xfe, 0xf8, 0xe8, 0xba, 0x4d, 0x0c, 0x81, 0x2d,
+ 0x9a, 0x1e, 0x75, 0xc3, 0xd8, 0xd2, 0xcc, 0xd7, 0x5a})),
+ std::make_pair(
+ 211,
+ std::vector<uint8_t>(
+ {0x6e, 0x29, 0x3b, 0xf5, 0xd0, 0x3f, 0xe4, 0x39, 0x77, 0xcf, 0xe3,
+ 0xf5, 0x7c, 0xcd, 0xb3, 0xae, 0x28, 0x2a, 0x85, 0x45, 0x5d, 0xca,
+ 0x33, 0xf3, 0x7f, 0x4b, 0x74, 0xf8, 0x39, 0x8c, 0xc6, 0x12, 0x43,
+ 0x3d, 0x75, 0x5c, 0xbe, 0xc4, 0x12, 0xf8, 0xf8, 0x2a, 0x3b, 0xd3,
+ 0xbc, 0x4a, 0x27, 0x8f, 0x7e, 0xcd, 0x0d, 0xfa, 0x9b, 0xbd, 0xc4,
+ 0x0b, 0xe7, 0xa7, 0x87, 0xc8, 0xf1, 0x59, 0xb2, 0xdf})),
+ std::make_pair(
+ 212,
+ std::vector<uint8_t>(
+ {0xc5, 0x65, 0x46, 0xfb, 0x21, 0x78, 0x45, 0x6f, 0x33, 0x61, 0x64,
+ 0xc1, 0x8b, 0x90, 0xde, 0xff, 0xc8, 0x3a, 0xe2, 0xb5, 0xa3, 0xac,
+ 0xa7, 0x7b, 0x68, 0x84, 0xd3, 0x6d, 0x2c, 0x1d, 0xb3, 0x95, 0x01,
+ 0xb3, 0xe6, 0x5e, 0x36, 0xc7, 0x58, 0xc6, 0x6e, 0x31, 0x88, 0x45,
+ 0x1f, 0xdb, 0x35, 0x15, 0xee, 0x16, 0x2c, 0x00, 0x1f, 0x06, 0xc3,
+ 0xe8, 0xcb, 0x57, 0x3a, 0xdf, 0x30, 0xf7, 0xa1, 0x01})),
+ std::make_pair(
+ 213,
+ std::vector<uint8_t>(
+ {0x6f, 0x82, 0xf8, 0x9f, 0x29, 0x9e, 0xbc, 0xa2, 0xfe, 0x01, 0x4b,
+ 0x59, 0xbf, 0xfe, 0x1a, 0xa8, 0x4e, 0x88, 0xb1, 0x91, 0x5f, 0xe2,
+ 0x56, 0xaf, 0xb6, 0x46, 0xfd, 0x84, 0x48, 0xaf, 0x2b, 0x88, 0x91,
+ 0xa7, 0xfa, 0xb3, 0x7a, 0x4e, 0xa6, 0xf9, 0xa5, 0x0e, 0x6c, 0x31,
+ 0x70, 0x39, 0xd8, 0xcf, 0x87, 0x8f, 0x4c, 0x8e, 0x1a, 0x0d, 0xd4,
+ 0x64, 0xf0, 0xb4, 0xd6, 0xff, 0x1c, 0x7e, 0xa8, 0x53})),
+ std::make_pair(
+ 214,
+ std::vector<uint8_t>(
+ {0x2b, 0x85, 0x99, 0xff, 0x9c, 0x3d, 0x61, 0x98, 0x63, 0x7a, 0xd5,
+ 0x1e, 0x57, 0xd1, 0x99, 0x8b, 0x0d, 0x75, 0x31, 0x3f, 0xe2, 0xdd,
+ 0x61, 0xa5, 0x33, 0xc9, 0x64, 0xa6, 0xdd, 0x96, 0x07, 0xc6, 0xf7,
+ 0x23, 0xe9, 0x45, 0x2c, 0xe4, 0x6e, 0x01, 0x4b, 0x1c, 0x1d, 0x6d,
+ 0xe7, 0x7b, 0xa5, 0xb8, 0x8c, 0x91, 0x4d, 0x1c, 0x59, 0x7b, 0xf1,
+ 0xea, 0xe1, 0x34, 0x74, 0xb4, 0x29, 0x0e, 0x89, 0xb2})),
+ std::make_pair(
+ 215,
+ std::vector<uint8_t>(
+ {0x08, 0xbf, 0x34, 0x6d, 0x38, 0xe1, 0xdf, 0x06, 0xc8, 0x26, 0x0e,
+ 0xdb, 0x1d, 0xa7, 0x55, 0x79, 0x27, 0x59, 0x48, 0xd5, 0xc0, 0xa0,
+ 0xaa, 0x9e, 0xd2, 0x88, 0x6f, 0x88, 0x56, 0xde, 0x54, 0x17, 0xa1,
+ 0x56, 0x99, 0x87, 0x58, 0xf5, 0xb1, 0x7e, 0x52, 0xf1, 0x01, 0xca,
+ 0x95, 0x7a, 0x71, 0x13, 0x74, 0x73, 0xdf, 0xd1, 0x8d, 0x7d, 0x20,
+ 0x9c, 0x4c, 0x10, 0xd9, 0x23, 0x3c, 0x93, 0x69, 0x1d})),
+ std::make_pair(
+ 216,
+ std::vector<uint8_t>(
+ {0x6d, 0xf2, 0x15, 0x6d, 0x77, 0x31, 0x14, 0xd3, 0x10, 0xb6, 0x3d,
+ 0xb9, 0xee, 0x53, 0x50, 0xd7, 0x7e, 0x6b, 0xcf, 0x25, 0xb0, 0x5f,
+ 0xcd, 0x91, 0x0f, 0x9b, 0x31, 0xbc, 0x42, 0xbb, 0x13, 0xfe, 0x82,
+ 0x25, 0xeb, 0xcb, 0x2a, 0x23, 0xa6, 0x22, 0x80, 0x77, 0x7b, 0x6b,
+ 0xf7, 0x4e, 0x2c, 0xd0, 0x91, 0x7c, 0x76, 0x40, 0xb4, 0x3d, 0xef,
+ 0xe4, 0x68, 0xcd, 0x1e, 0x18, 0xc9, 0x43, 0xc6, 0x6a})),
+ std::make_pair(
+ 217,
+ std::vector<uint8_t>(
+ {0x7c, 0x70, 0x38, 0xbc, 0x13, 0xa9, 0x11, 0x51, 0x82, 0x8a, 0x5b,
+ 0xa8, 0x2b, 0x4a, 0x96, 0x04, 0x0f, 0x25, 0x8a, 0x4d, 0xfb, 0x1b,
+ 0x13, 0x73, 0xf0, 0xd3, 0x59, 0x16, 0x8a, 0xfb, 0x05, 0x17, 0xa2,
+ 0x0b, 0x28, 0xa1, 0x2d, 0x36, 0x44, 0x04, 0x6b, 0xe6, 0x6b, 0x8d,
+ 0x08, 0xd8, 0xae, 0x7f, 0x6a, 0x92, 0x3e, 0xa1, 0xc0, 0x01, 0x87,
+ 0xc6, 0xd1, 0x1d, 0xc5, 0x02, 0xba, 0xc7, 0x13, 0x05})),
+ std::make_pair(
+ 218,
+ std::vector<uint8_t>(
+ {0xbc, 0xd1, 0xb3, 0x0d, 0x80, 0x8f, 0xb7, 0x39, 0xb9, 0x87, 0xcb,
+ 0xf1, 0x54, 0xbe, 0xa0, 0x0d, 0xa9, 0xd4, 0x03, 0x80, 0xb8, 0x61,
+ 0xd4, 0xc1, 0xd6, 0x37, 0x71, 0x22, 0xda, 0xdd, 0x61, 0xc0, 0xe5,
+ 0x90, 0x18, 0xb7, 0x19, 0x41, 0xcf, 0xb6, 0x2e, 0x00, 0xdc, 0xd7,
+ 0x0a, 0xeb, 0x9a, 0xbf, 0x04, 0x73, 0xe8, 0x0f, 0x0a, 0x7e, 0xca,
+ 0x6b, 0x6d, 0xea, 0x24, 0x6a, 0xb2, 0x29, 0xdd, 0x2b})),
+ std::make_pair(
+ 219,
+ std::vector<uint8_t>(
+ {0x7e, 0xd4, 0x46, 0x8d, 0x96, 0x85, 0x30, 0xfe, 0x7a, 0xb2, 0xc3,
+ 0x35, 0x40, 0xb2, 0x6d, 0x8c, 0x3b, 0xd3, 0xed, 0x44, 0xb3, 0x4f,
+ 0xbe, 0x8c, 0x2a, 0x9d, 0x7f, 0x80, 0x5b, 0x5a, 0xda, 0x0e, 0xa2,
+ 0x52, 0xee, 0xad, 0xe4, 0xfc, 0xe9, 0x7f, 0x89, 0x72, 0x8a, 0xd8,
+ 0x5b, 0xc8, 0xbb, 0x24, 0x30, 0xb1, 0xbe, 0xf2, 0xcd, 0xdd, 0x32,
+ 0xc8, 0x44, 0x6e, 0x59, 0xb8, 0xe8, 0xba, 0x3c, 0x67})),
+ std::make_pair(
+ 220,
+ std::vector<uint8_t>(
+ {0x6d, 0x30, 0xb7, 0xc6, 0xce, 0x8a, 0x32, 0x36, 0xc0, 0xca, 0x2f,
+ 0x8d, 0x72, 0x8b, 0x10, 0x88, 0xca, 0x06, 0x98, 0x3a, 0x80, 0x43,
+ 0xe6, 0x21, 0xd5, 0xdc, 0xf0, 0xc5, 0x37, 0xd1, 0x3b, 0x08, 0x79,
+ 0x1e, 0xde, 0xb0, 0x1a, 0x3c, 0xf0, 0x94, 0x3e, 0xc1, 0xc8, 0x90,
+ 0xab, 0x6e, 0x29, 0xb1, 0x46, 0xa2, 0x36, 0xcd, 0x46, 0xbc, 0xb9,
+ 0xd9, 0x3b, 0xf5, 0x16, 0xfb, 0x67, 0xc6, 0x3f, 0xe5})),
+ std::make_pair(
+ 221,
+ std::vector<uint8_t>(
+ {0x97, 0xfe, 0x03, 0xce, 0xf3, 0x14, 0x38, 0x50, 0x89, 0x11, 0xbd,
+ 0xed, 0x97, 0x59, 0x80, 0xa6, 0x60, 0x29, 0x30, 0x5d, 0xc5, 0xe3,
+ 0xfa, 0x8a, 0xd1, 0xb4, 0xfb, 0x22, 0xfc, 0xdf, 0x5a, 0x19, 0xa7,
+ 0x33, 0x32, 0x03, 0x27, 0xd8, 0xf7, 0x1c, 0xcf, 0x49, 0x6c, 0xb3,
+ 0xa4, 0x4a, 0x77, 0xaf, 0x56, 0xe3, 0xdd, 0xe7, 0x3d, 0x3a, 0x5f,
+ 0x17, 0x68, 0x96, 0xcc, 0x57, 0xc9, 0xa5, 0xad, 0x99})),
+ std::make_pair(
+ 222,
+ std::vector<uint8_t>(
+ {0x78, 0x5a, 0x9d, 0x0f, 0xbd, 0x21, 0x13, 0x6d, 0xbc, 0xe8, 0xfa,
+ 0x7e, 0xaf, 0xd6, 0x3c, 0x9d, 0xad, 0x22, 0x00, 0x52, 0x97, 0x84,
+ 0x16, 0xb3, 0x1d, 0x97, 0x53, 0xea, 0xa1, 0x49, 0x09, 0x78, 0x47,
+ 0xed, 0x9b, 0x30, 0xa6, 0x5c, 0x70, 0x50, 0x7e, 0xff, 0x01, 0x87,
+ 0x91, 0x49, 0xed, 0x5c, 0xf0, 0x47, 0x1d, 0x37, 0x79, 0x8e, 0xdc,
+ 0x05, 0xab, 0xd5, 0x6a, 0xd4, 0xa2, 0xcc, 0xcb, 0x1d})),
+ std::make_pair(
+ 223,
+ std::vector<uint8_t>(
+ {0xad, 0x40, 0x8d, 0x2a, 0xbd, 0xdf, 0xd3, 0x7b, 0x3b, 0xf3, 0x47,
+ 0x94, 0xc1, 0xa3, 0x37, 0x1d, 0x92, 0x8e, 0xd7, 0xfc, 0x8d, 0x96,
+ 0x62, 0x25, 0x33, 0x35, 0x84, 0xc5, 0x66, 0x58, 0x17, 0x83, 0x2a,
+ 0x37, 0xc0, 0x7f, 0x0d, 0xc7, 0xcb, 0x5a, 0xa8, 0x74, 0xcd, 0x7d,
+ 0x20, 0xfe, 0x8f, 0xab, 0x8e, 0xab, 0xcb, 0x9b, 0x33, 0xd2, 0xe0,
+ 0x84, 0x1f, 0x6e, 0x20, 0x09, 0x60, 0x89, 0x9d, 0x95})),
+ std::make_pair(
+ 224,
+ std::vector<uint8_t>(
+ {0x97, 0x66, 0x8f, 0x74, 0x5b, 0x60, 0x32, 0xfc, 0x81, 0x5d, 0x95,
+ 0x79, 0x32, 0x27, 0x69, 0xdc, 0xcd, 0x95, 0x01, 0xa5, 0x08, 0x00,
+ 0x29, 0xb8, 0xae, 0x82, 0x6b, 0xef, 0xb6, 0x74, 0x23, 0x31, 0xbd,
+ 0x9f, 0x76, 0xef, 0xeb, 0x3e, 0x2b, 0x8e, 0x81, 0xa9, 0x78, 0x6b,
+ 0x28, 0x2f, 0x50, 0x68, 0xa3, 0xa2, 0x42, 0x46, 0x97, 0xa7, 0x7c,
+ 0x41, 0x87, 0x6b, 0x7e, 0x75, 0x3f, 0x4c, 0x77, 0x67})),
+ std::make_pair(
+ 225,
+ std::vector<uint8_t>(
+ {0x26, 0xbb, 0x98, 0x5f, 0x47, 0xe7, 0xfe, 0xe0, 0xcf, 0xd2, 0x52,
+ 0xd4, 0xef, 0x96, 0xbe, 0xd4, 0x2b, 0x9c, 0x37, 0x0c, 0x1c, 0x6a,
+ 0x3e, 0x8c, 0x9e, 0xb0, 0x4e, 0xf7, 0xf7, 0x81, 0x8b, 0x83, 0x3a,
+ 0x0d, 0x1f, 0x04, 0x3e, 0xba, 0xfb, 0x91, 0x1d, 0xc7, 0x79, 0xe0,
+ 0x27, 0x40, 0xa0, 0x2a, 0x44, 0xd3, 0xa1, 0xea, 0x45, 0xed, 0x4a,
+ 0xd5, 0x5e, 0x68, 0x6c, 0x92, 0x7c, 0xaf, 0xe9, 0x7e})),
+ std::make_pair(
+ 226,
+ std::vector<uint8_t>(
+ {0x5b, 0xfe, 0x2b, 0x1d, 0xcf, 0x7f, 0xe9, 0xb9, 0x50, 0x88, 0xac,
+ 0xed, 0xb5, 0x75, 0xc1, 0x90, 0x16, 0xc7, 0x43, 0xb2, 0xe7, 0x63,
+ 0xbf, 0x58, 0x51, 0xac, 0x40, 0x7c, 0x9e, 0xda, 0x43, 0x71, 0x5e,
+ 0xdf, 0xa4, 0x8b, 0x48, 0x25, 0x49, 0x2c, 0x51, 0x79, 0x59, 0x3f,
+ 0xff, 0x21, 0x35, 0x1b, 0x76, 0xe8, 0xb7, 0xe0, 0x34, 0xe4, 0xc5,
+ 0x3c, 0x79, 0xf6, 0x1f, 0x29, 0xc4, 0x79, 0xbd, 0x08})),
+ std::make_pair(
+ 227,
+ std::vector<uint8_t>(
+ {0xc7, 0x65, 0x09, 0xef, 0x72, 0xf4, 0xa6, 0xf9, 0xc9, 0xc4, 0x06,
+ 0x18, 0xed, 0x52, 0xb2, 0x08, 0x4f, 0x83, 0x50, 0x22, 0x32, 0xe0,
+ 0xac, 0x8b, 0xda, 0xf3, 0x26, 0x43, 0x68, 0xe4, 0xd0, 0x18, 0x0f,
+ 0x68, 0x54, 0xc4, 0xab, 0xf4, 0xf6, 0x50, 0x9c, 0x79, 0xca, 0xaf,
+ 0xc4, 0x4c, 0xf3, 0x19, 0x4a, 0xfc, 0x57, 0xbd, 0x07, 0x7b, 0xd7,
+ 0xb3, 0xc9, 0xbd, 0xa3, 0xd4, 0xb8, 0x77, 0x58, 0x16})),
+ std::make_pair(
+ 228,
+ std::vector<uint8_t>(
+ {0xd6, 0x6f, 0x2b, 0xea, 0xb9, 0x90, 0xe3, 0x54, 0xcc, 0xb9, 0x10,
+ 0xe4, 0xe9, 0xc7, 0xac, 0x61, 0x8c, 0x7b, 0x63, 0xef, 0x29, 0x2a,
+ 0x96, 0xb5, 0x52, 0x34, 0x1d, 0xe7, 0x8d, 0xc4, 0x6d, 0x3e, 0xc8,
+ 0xcf, 0xab, 0xc6, 0x99, 0xb5, 0x0a, 0xf4, 0x1f, 0xda, 0x39, 0xcf,
+ 0x1b, 0x01, 0x73, 0x66, 0x09, 0x23, 0x51, 0x0a, 0xd6, 0x7f, 0xae,
+ 0xde, 0xf5, 0x20, 0x7c, 0xff, 0xe8, 0x64, 0x1d, 0x20})),
+ std::make_pair(
+ 229,
+ std::vector<uint8_t>(
+ {0x7d, 0x8f, 0x06, 0x72, 0x99, 0x2b, 0x79, 0xbe, 0x3a, 0x36, 0x4d,
+ 0x8e, 0x59, 0x04, 0xf4, 0xab, 0x71, 0x3b, 0xbc, 0x8a, 0xb0, 0x1b,
+ 0x4f, 0x30, 0x9a, 0xd8, 0xcc, 0xf2, 0x23, 0xce, 0x10, 0x34, 0xa8,
+ 0x60, 0xdc, 0xb0, 0xb0, 0x05, 0x50, 0x61, 0x2c, 0xc2, 0xfa, 0x17,
+ 0xf2, 0x96, 0x9e, 0x18, 0xf2, 0x2e, 0x14, 0x27, 0xd2, 0x54, 0xb4,
+ 0xa8, 0x2b, 0x3a, 0x03, 0xa3, 0xeb, 0x39, 0x4a, 0xdf})),
+ std::make_pair(
+ 230,
+ std::vector<uint8_t>(
+ {0xa5, 0x6d, 0x67, 0x25, 0xbf, 0xb3, 0xde, 0x47, 0xc1, 0x41, 0x4a,
+ 0xdf, 0x25, 0xfc, 0x8f, 0x0f, 0xc9, 0x84, 0x6f, 0x69, 0x87, 0x72,
+ 0x2b, 0xc0, 0x63, 0x66, 0xd5, 0xca, 0x4e, 0x89, 0x72, 0x29, 0x25,
+ 0xeb, 0xbc, 0x88, 0x14, 0x18, 0x84, 0x40, 0x75, 0x39, 0x7a, 0x0c,
+ 0xa8, 0x98, 0x42, 0xc7, 0xb9, 0xe9, 0xe0, 0x7e, 0x1d, 0x9d, 0x18,
+ 0x3e, 0xbe, 0xb3, 0x9e, 0x12, 0x0b, 0x48, 0x3b, 0xf7})),
+ std::make_pair(
+ 231,
+ std::vector<uint8_t>(
+ {0xaf, 0x5e, 0x03, 0xd7, 0xfe, 0x60, 0xc6, 0x7e, 0x10, 0x31, 0x33,
+ 0x44, 0x43, 0x4e, 0x79, 0x48, 0x5a, 0x03, 0xa7, 0x58, 0xd6, 0xdc,
+ 0xe9, 0x85, 0x57, 0x47, 0x45, 0x76, 0x3c, 0x1c, 0x5c, 0x77, 0xd4,
+ 0xfb, 0x3e, 0x6f, 0xb1, 0x22, 0x30, 0x36, 0x83, 0x70, 0x99, 0x3b,
+ 0xf9, 0x0f, 0xee, 0xd0, 0xc5, 0xd1, 0x60, 0x75, 0x24, 0x56, 0x2d,
+ 0x7c, 0x09, 0xc0, 0xc2, 0x10, 0xed, 0x39, 0x3d, 0x7c})),
+ std::make_pair(
+ 232,
+ std::vector<uint8_t>(
+ {0x7a, 0x20, 0x54, 0x0c, 0xc0, 0x7b, 0xf7, 0x2b, 0x58, 0x24, 0x21,
+ 0xfc, 0x34, 0x2e, 0x82, 0xf5, 0x21, 0x34, 0xb6, 0x98, 0x41, 0xec,
+ 0x28, 0xed, 0x18, 0x9e, 0x2e, 0xa6, 0xa2, 0x9d, 0xd2, 0xf8, 0x2a,
+ 0x64, 0x03, 0x52, 0xd2, 0x22, 0xb5, 0x2f, 0x29, 0x11, 0xdc, 0x72,
+ 0xa7, 0xda, 0xb3, 0x1c, 0xaa, 0xdd, 0x80, 0xc6, 0x11, 0x8f, 0x13,
+ 0xc5, 0x6b, 0x2a, 0x1e, 0x43, 0x73, 0xbe, 0x0e, 0xa3})),
+ std::make_pair(
+ 233,
+ std::vector<uint8_t>(
+ {0x48, 0x6f, 0x02, 0xc6, 0x3e, 0x54, 0x67, 0xea, 0x1f, 0xdd, 0xe7,
+ 0xe8, 0x2b, 0xfa, 0xcc, 0x2c, 0x1b, 0xa5, 0xd6, 0x36, 0xd9, 0xf3,
+ 0xd0, 0x8b, 0x21, 0x0d, 0xa3, 0xf3, 0x72, 0xf7, 0x06, 0xec, 0x21,
+ 0x8c, 0xc1, 0x7f, 0xf6, 0x0a, 0xef, 0x70, 0x3b, 0xbe, 0x0c, 0x15,
+ 0xc3, 0x8a, 0xe5, 0x5d, 0x28, 0x6a, 0x68, 0x4f, 0x86, 0x4c, 0x78,
+ 0x21, 0x1c, 0xca, 0xb4, 0x17, 0x8c, 0x92, 0xad, 0xba})),
+ std::make_pair(
+ 234,
+ std::vector<uint8_t>(
+ {0x1c, 0x7a, 0x5c, 0x1d, 0xed, 0xcd, 0x04, 0xa9, 0x21, 0x78, 0x8f,
+ 0x7e, 0xb2, 0x33, 0x61, 0xca, 0x19, 0x53, 0xb0, 0x4b, 0x9c, 0x7a,
+ 0xec, 0x35, 0xd6, 0x5e, 0xa3, 0xe4, 0x99, 0x6d, 0xb2, 0x6f, 0x28,
+ 0x12, 0x78, 0xea, 0x4a, 0xe6, 0x66, 0xad, 0x81, 0x02, 0x7d, 0x98,
+ 0xaf, 0x57, 0x26, 0x2c, 0xdb, 0xfa, 0x4c, 0x08, 0x5f, 0x42, 0x10,
+ 0x56, 0x8c, 0x7e, 0x15, 0xee, 0xc7, 0x80, 0x51, 0x14})),
+ std::make_pair(
+ 235,
+ std::vector<uint8_t>(
+ {0x9c, 0xe3, 0xfa, 0x9a, 0x86, 0x0b, 0xdb, 0xd5, 0x37, 0x8f, 0xd6,
+ 0xd7, 0xb8, 0xb6, 0x71, 0xc6, 0xcb, 0x76, 0x92, 0x91, 0x0c, 0xe8,
+ 0xf9, 0xb6, 0xcb, 0x41, 0x22, 0xcb, 0xcb, 0xe6, 0xac, 0x06, 0xca,
+ 0x04, 0x22, 0xce, 0xf1, 0x22, 0x59, 0x35, 0x05, 0x3b, 0x7d, 0x19,
+ 0x3a, 0x81, 0xb9, 0xe9, 0x72, 0xeb, 0x85, 0xa1, 0xd3, 0x07, 0x4f,
+ 0x14, 0xcb, 0xb5, 0xec, 0x9f, 0x05, 0x73, 0x89, 0x2d})),
+ std::make_pair(
+ 236,
+ std::vector<uint8_t>(
+ {0xa9, 0x11, 0x87, 0xbe, 0x5c, 0x37, 0x1c, 0x42, 0x65, 0xc1, 0x74,
+ 0xfd, 0x46, 0x53, 0xb8, 0xab, 0x70, 0x85, 0x51, 0xf8, 0x3d, 0x1f,
+ 0xee, 0x1c, 0xc1, 0x47, 0x95, 0x81, 0xbc, 0x00, 0x6d, 0x6f, 0xb7,
+ 0x8f, 0xcc, 0x9a, 0x5d, 0xee, 0x1d, 0xb3, 0x66, 0x6f, 0x50, 0x8f,
+ 0x97, 0x80, 0xa3, 0x75, 0x93, 0xeb, 0xcc, 0xcf, 0x5f, 0xbe, 0xd3,
+ 0x96, 0x67, 0xdc, 0x63, 0x61, 0xe9, 0x21, 0xf7, 0x79})),
+ std::make_pair(
+ 237,
+ std::vector<uint8_t>(
+ {0x46, 0x25, 0x76, 0x7d, 0x7b, 0x1d, 0x3d, 0x3e, 0xd2, 0xfb, 0xc6,
+ 0x74, 0xaf, 0x14, 0xe0, 0x24, 0x41, 0x52, 0xf2, 0xa4, 0x02, 0x1f,
+ 0xcf, 0x33, 0x11, 0x50, 0x5d, 0x89, 0xbd, 0x81, 0xe2, 0xf9, 0xf9,
+ 0xa5, 0x00, 0xc3, 0xb1, 0x99, 0x91, 0x4d, 0xb4, 0x95, 0x00, 0xb3,
+ 0xc9, 0x8d, 0x03, 0xea, 0x93, 0x28, 0x67, 0x51, 0xa6, 0x86, 0xa3,
+ 0xb8, 0x75, 0xda, 0xab, 0x0c, 0xcd, 0x63, 0xb4, 0x4f})),
+ std::make_pair(
+ 238,
+ std::vector<uint8_t>(
+ {0x43, 0xdf, 0xdf, 0xe1, 0xb0, 0x14, 0xfe, 0xd3, 0xa2, 0xac, 0xab,
+ 0xb7, 0xf3, 0xe9, 0xa1, 0x82, 0xf2, 0xaa, 0x18, 0x01, 0x9d, 0x27,
+ 0xe3, 0xe6, 0xcd, 0xcf, 0x31, 0xa1, 0x5b, 0x42, 0x8e, 0x91, 0xe7,
+ 0xb0, 0x8c, 0xf5, 0xe5, 0xc3, 0x76, 0xfc, 0xe2, 0xd8, 0xa2, 0x8f,
+ 0xf8, 0x5a, 0xb0, 0xa0, 0xa1, 0x65, 0x6e, 0xdb, 0x4a, 0x0a, 0x91,
+ 0x53, 0x26, 0x20, 0x09, 0x6d, 0x9a, 0x5a, 0x65, 0x2d})),
+ std::make_pair(
+ 239,
+ std::vector<uint8_t>(
+ {0x27, 0x9e, 0x32, 0x02, 0xbe, 0x39, 0x89, 0xba, 0x31, 0x12, 0x77,
+ 0x25, 0x85, 0x17, 0x74, 0x87, 0xe4, 0xfe, 0x3e, 0xe3, 0xea, 0xb4,
+ 0x9c, 0x2f, 0x7f, 0xa7, 0xfe, 0x87, 0xcf, 0xe7, 0xb8, 0x0d, 0x3e,
+ 0x03, 0x55, 0xed, 0xff, 0x6d, 0x03, 0x1e, 0x6c, 0x96, 0xc7, 0x95,
+ 0xdb, 0x1c, 0x6f, 0x04, 0x18, 0x80, 0xec, 0x38, 0x24, 0xde, 0xfa,
+ 0xcf, 0x92, 0x63, 0x82, 0x0a, 0x8e, 0x73, 0x27, 0xde})),
+ std::make_pair(
+ 240,
+ std::vector<uint8_t>(
+ {0xea, 0x2d, 0x06, 0x6a, 0xc2, 0x29, 0xd4, 0xd4, 0xb6, 0x16, 0xa8,
+ 0xbe, 0xde, 0xc7, 0x34, 0x32, 0x52, 0x24, 0xe4, 0xb4, 0xe5, 0x8f,
+ 0x1a, 0xe6, 0xda, 0xd7, 0xe4, 0x0c, 0x2d, 0xa2, 0x91, 0x96, 0xc3,
+ 0xb1, 0xea, 0x95, 0x71, 0xda, 0xcc, 0x81, 0xe8, 0x73, 0x28, 0xca,
+ 0xa0, 0x21, 0x1e, 0x09, 0x02, 0x7b, 0x05, 0x24, 0xaa, 0x3f, 0x4a,
+ 0x84, 0x99, 0x17, 0xb3, 0x58, 0x67, 0x47, 0xeb, 0xbb})),
+ std::make_pair(
+ 241,
+ std::vector<uint8_t>(
+ {0x49, 0xf0, 0x14, 0xf5, 0xc6, 0x18, 0x22, 0xc8, 0x99, 0xab, 0x5c,
+ 0xae, 0x51, 0xbe, 0x40, 0x44, 0xa4, 0x49, 0x5e, 0x77, 0x7d, 0xeb,
+ 0x7d, 0xa9, 0xb6, 0xd8, 0x49, 0x0e, 0xfb, 0xb8, 0x75, 0x30, 0xad,
+ 0xf2, 0x93, 0xda, 0xf0, 0x79, 0xf9, 0x4c, 0x33, 0xb7, 0x04, 0x4e,
+ 0xf6, 0x2e, 0x2e, 0x5b, 0xb3, 0xeb, 0x11, 0xe1, 0x73, 0x04, 0xf8,
+ 0x45, 0x3e, 0xe6, 0xce, 0x24, 0xf0, 0x33, 0xdd, 0xb0})),
+ std::make_pair(
+ 242,
+ std::vector<uint8_t>(
+ {0x92, 0x33, 0x49, 0x03, 0x44, 0xe5, 0xb0, 0xdc, 0x59, 0x12, 0x67,
+ 0x1b, 0x7a, 0xe5, 0x4c, 0xee, 0x77, 0x30, 0xdb, 0xe1, 0xf4, 0xc7,
+ 0xd9, 0x2a, 0x4d, 0x3e, 0x3a, 0xab, 0x50, 0x57, 0x17, 0x08, 0xdb,
+ 0x51, 0xdc, 0xf9, 0xc2, 0x94, 0x45, 0x91, 0xdb, 0x65, 0x1d, 0xb3,
+ 0x2d, 0x22, 0x93, 0x5b, 0x86, 0x94, 0x49, 0x69, 0xbe, 0x77, 0xd5,
+ 0xb5, 0xfe, 0xae, 0x6c, 0x38, 0x40, 0xa8, 0xdb, 0x26})),
+ std::make_pair(
+ 243,
+ std::vector<uint8_t>(
+ {0xb6, 0xe7, 0x5e, 0x6f, 0x4c, 0x7f, 0x45, 0x3b, 0x74, 0x65, 0xd2,
+ 0x5b, 0x5a, 0xc8, 0xc7, 0x19, 0x69, 0x02, 0xea, 0xa9, 0x53, 0x87,
+ 0x52, 0x28, 0xc8, 0x63, 0x4e, 0x16, 0xe2, 0xae, 0x1f, 0x38, 0xbc,
+ 0x32, 0x75, 0x30, 0x43, 0x35, 0xf5, 0x98, 0x9e, 0xcc, 0xc1, 0xe3,
+ 0x41, 0x67, 0xd4, 0xe6, 0x8d, 0x77, 0x19, 0x96, 0x8f, 0xba, 0x8e,
+ 0x2f, 0xe6, 0x79, 0x47, 0xc3, 0x5c, 0x48, 0xe8, 0x06})),
+ std::make_pair(
+ 244,
+ std::vector<uint8_t>(
+ {0xcc, 0x14, 0xca, 0x66, 0x5a, 0xf1, 0x48, 0x3e, 0xfb, 0xc3, 0xaf,
+ 0x80, 0x08, 0x0e, 0x65, 0x0d, 0x50, 0x46, 0xa3, 0x93, 0x2f, 0x4f,
+ 0x51, 0xf3, 0xfe, 0x90, 0xa0, 0x70, 0x5e, 0xc2, 0x51, 0x04, 0xad,
+ 0xf0, 0x78, 0x39, 0x26, 0x5d, 0xc5, 0x1d, 0x43, 0x40, 0x14, 0x11,
+ 0x24, 0x6e, 0x47, 0x4f, 0x0d, 0x5e, 0x56, 0x37, 0xaf, 0x94, 0x76,
+ 0x72, 0x83, 0xd5, 0x3e, 0x06, 0x17, 0xe9, 0x81, 0xf4})),
+ std::make_pair(
+ 245,
+ std::vector<uint8_t>(
+ {0x23, 0x0a, 0x1c, 0x85, 0x7c, 0xb2, 0xe7, 0x85, 0x2e, 0x41, 0xb6,
+ 0x47, 0xe9, 0x0e, 0x45, 0x85, 0xd2, 0xd8, 0x81, 0xe1, 0x73, 0x4d,
+ 0xc3, 0x89, 0x55, 0x35, 0x6e, 0x8d, 0xd7, 0xbf, 0xf3, 0x90, 0x53,
+ 0x09, 0x2c, 0x6b, 0x38, 0xe2, 0x36, 0xe1, 0x89, 0x95, 0x25, 0x64,
+ 0x70, 0x73, 0xdd, 0xdf, 0x68, 0x95, 0xd6, 0x42, 0x06, 0x32, 0x5e,
+ 0x76, 0x47, 0xf2, 0x75, 0x56, 0x7b, 0x25, 0x59, 0x09})),
+ std::make_pair(
+ 246,
+ std::vector<uint8_t>(
+ {0xcb, 0xb6, 0x53, 0x21, 0xac, 0x43, 0x6e, 0x2f, 0xfd, 0xab, 0x29,
+ 0x36, 0x35, 0x9c, 0xe4, 0x90, 0x23, 0xf7, 0xde, 0xe7, 0x61, 0x4e,
+ 0xf2, 0x8d, 0x17, 0x3c, 0x3d, 0x27, 0xc5, 0xd1, 0xbf, 0xfa, 0x51,
+ 0x55, 0x3d, 0x43, 0x3f, 0x8e, 0xe3, 0xc9, 0xe4, 0x9c, 0x05, 0xa2,
+ 0xb8, 0x83, 0xcc, 0xe9, 0x54, 0xc9, 0xa8, 0x09, 0x3b, 0x80, 0x61,
+ 0x2a, 0x0c, 0xdd, 0x47, 0x32, 0xe0, 0x41, 0xf9, 0x95})),
+ std::make_pair(
+ 247,
+ std::vector<uint8_t>(
+ {0x3e, 0x7e, 0x57, 0x00, 0x74, 0x33, 0x72, 0x75, 0xef, 0xb5, 0x13,
+ 0x15, 0x58, 0x80, 0x34, 0xc3, 0xcf, 0x0d, 0xdd, 0xca, 0x20, 0xb4,
+ 0x61, 0x2e, 0x0b, 0xd5, 0xb8, 0x81, 0xe7, 0xe5, 0x47, 0x6d, 0x31,
+ 0x9c, 0xe4, 0xfe, 0x9f, 0x19, 0x18, 0x6e, 0x4c, 0x08, 0x26, 0xf4,
+ 0x4f, 0x13, 0x1e, 0xb0, 0x48, 0xe6, 0x5b, 0xe2, 0x42, 0xb1, 0x17,
+ 0x2c, 0x63, 0xba, 0xdb, 0x12, 0x3a, 0xb0, 0xcb, 0xe8})),
+ std::make_pair(
+ 248,
+ std::vector<uint8_t>(
+ {0xd3, 0x2e, 0x9e, 0xc0, 0x2d, 0x38, 0xd4, 0xe1, 0xb8, 0x24, 0x9d,
+ 0xf8, 0xdc, 0xb0, 0x0c, 0x5b, 0x9c, 0x68, 0xeb, 0x89, 0x22, 0x67,
+ 0x2e, 0x35, 0x05, 0x39, 0x3b, 0x6a, 0x21, 0x0b, 0xa5, 0x6f, 0x94,
+ 0x96, 0xe5, 0xee, 0x04, 0x90, 0xef, 0x38, 0x7c, 0x3c, 0xde, 0xc0,
+ 0x61, 0xf0, 0x6b, 0xc0, 0x38, 0x2d, 0x93, 0x04, 0xca, 0xfb, 0xb8,
+ 0xe0, 0xcd, 0x33, 0xd5, 0x70, 0x29, 0xe6, 0x2d, 0xf2})),
+ std::make_pair(
+ 249,
+ std::vector<uint8_t>(
+ {0x8c, 0x15, 0x12, 0x46, 0x60, 0x89, 0xf0, 0x5b, 0x37, 0x75, 0xc2,
+ 0x62, 0xb6, 0x2d, 0x22, 0xb8, 0x38, 0x54, 0xa8, 0x32, 0x18, 0x13,
+ 0x0b, 0x4e, 0xc9, 0x1b, 0x3c, 0xcb, 0xd2, 0x93, 0xd2, 0xa5, 0x43,
+ 0x02, 0xce, 0xca, 0xab, 0x9b, 0x10, 0x0c, 0x68, 0xd1, 0xe6, 0xdd,
+ 0xc8, 0xf0, 0x7c, 0xdd, 0xbd, 0xfe, 0x6f, 0xda, 0xaa, 0xf0, 0x99,
+ 0xcc, 0x09, 0xd6, 0xb7, 0x25, 0x87, 0x9c, 0x63, 0x69})),
+ std::make_pair(
+ 250,
+ std::vector<uint8_t>(
+ {0x91, 0xa7, 0xf6, 0x1c, 0x97, 0xc2, 0x91, 0x1e, 0x4c, 0x81, 0x2e,
+ 0xf7, 0x1d, 0x78, 0x0a, 0xd8, 0xfa, 0x78, 0x87, 0x94, 0x56, 0x1d,
+ 0x08, 0x30, 0x3f, 0xd1, 0xc1, 0xcb, 0x60, 0x8a, 0x46, 0xa1, 0x25,
+ 0x63, 0x08, 0x6e, 0xc5, 0xb3, 0x9d, 0x47, 0x1a, 0xed, 0x94, 0xfb,
+ 0x0f, 0x6c, 0x67, 0x8a, 0x43, 0xb8, 0x79, 0x29, 0x32, 0xf9, 0x02,
+ 0x8d, 0x77, 0x2a, 0x22, 0x76, 0x8e, 0xa2, 0x3a, 0x9b})),
+ std::make_pair(
+ 251,
+ std::vector<uint8_t>(
+ {0x4f, 0x6b, 0xb2, 0x22, 0xa3, 0x95, 0xe8, 0xb1, 0x8f, 0x6b, 0xa1,
+ 0x55, 0x47, 0x7a, 0xed, 0x3f, 0x07, 0x29, 0xac, 0x9e, 0x83, 0xe1,
+ 0x6d, 0x31, 0xa2, 0xa8, 0xbc, 0x65, 0x54, 0x22, 0xb8, 0x37, 0xc8,
+ 0x91, 0xc6, 0x19, 0x9e, 0x6f, 0x0d, 0x75, 0x79, 0x9e, 0x3b, 0x69,
+ 0x15, 0x25, 0xc5, 0x81, 0x95, 0x35, 0x17, 0xf2, 0x52, 0xc4, 0xb9,
+ 0xe3, 0xa2, 0x7a, 0x28, 0xfb, 0xaf, 0x49, 0x64, 0x4c})),
+ std::make_pair(
+ 252,
+ std::vector<uint8_t>(
+ {0x5d, 0x06, 0xc0, 0x7e, 0x7a, 0x64, 0x6c, 0x41, 0x3a, 0x50, 0x1c,
+ 0x3f, 0x4b, 0xb2, 0xfc, 0x38, 0x12, 0x7d, 0xe7, 0x50, 0x9b, 0x70,
+ 0x77, 0xc4, 0xd9, 0xb5, 0x61, 0x32, 0x01, 0xc1, 0xaa, 0x02, 0xfd,
+ 0x5f, 0x79, 0xd2, 0x74, 0x59, 0x15, 0xdd, 0x57, 0xfb, 0xcb, 0x4c,
+ 0xe0, 0x86, 0x95, 0xf6, 0xef, 0xc0, 0xcb, 0x3d, 0x2d, 0x33, 0x0e,
+ 0x19, 0xb4, 0xb0, 0xe6, 0x00, 0x4e, 0xa6, 0x47, 0x1e})),
+ std::make_pair(
+ 253,
+ std::vector<uint8_t>(
+ {0xb9, 0x67, 0x56, 0xe5, 0x79, 0x09, 0x96, 0x8f, 0x14, 0xb7, 0x96,
+ 0xa5, 0xd3, 0x0f, 0x4c, 0x9d, 0x67, 0x14, 0x72, 0xcf, 0x82, 0xc8,
+ 0xcf, 0xb2, 0xca, 0xca, 0x7a, 0xc7, 0xa4, 0x4c, 0xa0, 0xa1, 0x4c,
+ 0x98, 0x42, 0xd0, 0x0c, 0x82, 0xe3, 0x37, 0x50, 0x2c, 0x94, 0xd5,
+ 0x96, 0x0a, 0xca, 0x4c, 0x49, 0x2e, 0xa7, 0xb0, 0xdf, 0x91, 0x9d,
+ 0xdf, 0x1a, 0xad, 0xa2, 0xa2, 0x75, 0xbb, 0x10, 0xd4})),
+ std::make_pair(
+ 254,
+ std::vector<uint8_t>(
+ {0xff, 0x0a, 0x01, 0x5e, 0x98, 0xdb, 0x9c, 0x99, 0xf0, 0x39, 0x77,
+ 0x71, 0x0a, 0xac, 0x3e, 0x65, 0x8c, 0x0d, 0x89, 0x6f, 0x6d, 0x71,
+ 0xd6, 0x18, 0xba, 0x79, 0xdc, 0x6c, 0xf7, 0x2a, 0xc7, 0x5b, 0x7c,
+ 0x03, 0x8e, 0xb6, 0x86, 0x2d, 0xed, 0xe4, 0x54, 0x3e, 0x14, 0x54,
+ 0x13, 0xa6, 0x36, 0x8d, 0x69, 0xf5, 0x72, 0x2c, 0x82, 0x7b, 0xa3,
+ 0xef, 0x25, 0xb6, 0xae, 0x64, 0x40, 0xd3, 0x92, 0x76})),
+ std::make_pair(
+ 255,
+ std::vector<uint8_t>(
+ {0x5b, 0x21, 0xc5, 0xfd, 0x88, 0x68, 0x36, 0x76, 0x12, 0x47, 0x4f,
+ 0xa2, 0xe7, 0x0e, 0x9c, 0xfa, 0x22, 0x01, 0xff, 0xee, 0xe8, 0xfa,
+ 0xfa, 0xb5, 0x79, 0x7a, 0xd5, 0x8f, 0xef, 0xa1, 0x7c, 0x9b, 0x5b,
+ 0x10, 0x7d, 0xa4, 0xa3, 0xdb, 0x63, 0x20, 0xba, 0xaf, 0x2c, 0x86,
+ 0x17, 0xd5, 0xa5, 0x1d, 0xf9, 0x14, 0xae, 0x88, 0xda, 0x38, 0x67,
+ 0xc2, 0xd4, 0x1f, 0x0c, 0xc1, 0x4f, 0xa6, 0x79, 0x28}))};
+
+std::vector<std::pair<int, std::vector<uint8_t>>> TestcasesKeyed = {
+ std::make_pair(
+ 0,
+ std::vector<uint8_t>(
+ {0x10, 0xeb, 0xb6, 0x77, 0x00, 0xb1, 0x86, 0x8e, 0xfb, 0x44, 0x17,
+ 0x98, 0x7a, 0xcf, 0x46, 0x90, 0xae, 0x9d, 0x97, 0x2f, 0xb7, 0xa5,
+ 0x90, 0xc2, 0xf0, 0x28, 0x71, 0x79, 0x9a, 0xaa, 0x47, 0x86, 0xb5,
+ 0xe9, 0x96, 0xe8, 0xf0, 0xf4, 0xeb, 0x98, 0x1f, 0xc2, 0x14, 0xb0,
+ 0x05, 0xf4, 0x2d, 0x2f, 0xf4, 0x23, 0x34, 0x99, 0x39, 0x16, 0x53,
+ 0xdf, 0x7a, 0xef, 0xcb, 0xc1, 0x3f, 0xc5, 0x15, 0x68})),
+ std::make_pair(
+ 1,
+ std::vector<uint8_t>(
+ {0x96, 0x1f, 0x6d, 0xd1, 0xe4, 0xdd, 0x30, 0xf6, 0x39, 0x01, 0x69,
+ 0x0c, 0x51, 0x2e, 0x78, 0xe4, 0xb4, 0x5e, 0x47, 0x42, 0xed, 0x19,
+ 0x7c, 0x3c, 0x5e, 0x45, 0xc5, 0x49, 0xfd, 0x25, 0xf2, 0xe4, 0x18,
+ 0x7b, 0x0b, 0xc9, 0xfe, 0x30, 0x49, 0x2b, 0x16, 0xb0, 0xd0, 0xbc,
+ 0x4e, 0xf9, 0xb0, 0xf3, 0x4c, 0x70, 0x03, 0xfa, 0xc0, 0x9a, 0x5e,
+ 0xf1, 0x53, 0x2e, 0x69, 0x43, 0x02, 0x34, 0xce, 0xbd})),
+ std::make_pair(
+ 2,
+ std::vector<uint8_t>(
+ {0xda, 0x2c, 0xfb, 0xe2, 0xd8, 0x40, 0x9a, 0x0f, 0x38, 0x02, 0x61,
+ 0x13, 0x88, 0x4f, 0x84, 0xb5, 0x01, 0x56, 0x37, 0x1a, 0xe3, 0x04,
+ 0xc4, 0x43, 0x01, 0x73, 0xd0, 0x8a, 0x99, 0xd9, 0xfb, 0x1b, 0x98,
+ 0x31, 0x64, 0xa3, 0x77, 0x07, 0x06, 0xd5, 0x37, 0xf4, 0x9e, 0x0c,
+ 0x91, 0x6d, 0x9f, 0x32, 0xb9, 0x5c, 0xc3, 0x7a, 0x95, 0xb9, 0x9d,
+ 0x85, 0x74, 0x36, 0xf0, 0x23, 0x2c, 0x88, 0xa9, 0x65})),
+ std::make_pair(
+ 3,
+ std::vector<uint8_t>(
+ {0x33, 0xd0, 0x82, 0x5d, 0xdd, 0xf7, 0xad, 0xa9, 0x9b, 0x0e, 0x7e,
+ 0x30, 0x71, 0x04, 0xad, 0x07, 0xca, 0x9c, 0xfd, 0x96, 0x92, 0x21,
+ 0x4f, 0x15, 0x61, 0x35, 0x63, 0x15, 0xe7, 0x84, 0xf3, 0xe5, 0xa1,
+ 0x7e, 0x36, 0x4a, 0xe9, 0xdb, 0xb1, 0x4c, 0xb2, 0x03, 0x6d, 0xf9,
+ 0x32, 0xb7, 0x7f, 0x4b, 0x29, 0x27, 0x61, 0x36, 0x5f, 0xb3, 0x28,
+ 0xde, 0x7a, 0xfd, 0xc6, 0xd8, 0x99, 0x8f, 0x5f, 0xc1})),
+ std::make_pair(
+ 4,
+ std::vector<uint8_t>(
+ {0xbe, 0xaa, 0x5a, 0x3d, 0x08, 0xf3, 0x80, 0x71, 0x43, 0xcf, 0x62,
+ 0x1d, 0x95, 0xcd, 0x69, 0x05, 0x14, 0xd0, 0xb4, 0x9e, 0xff, 0xf9,
+ 0xc9, 0x1d, 0x24, 0xb5, 0x92, 0x41, 0xec, 0x0e, 0xef, 0xa5, 0xf6,
+ 0x01, 0x96, 0xd4, 0x07, 0x04, 0x8b, 0xba, 0x8d, 0x21, 0x46, 0x82,
+ 0x8e, 0xbc, 0xb0, 0x48, 0x8d, 0x88, 0x42, 0xfd, 0x56, 0xbb, 0x4f,
+ 0x6d, 0xf8, 0xe1, 0x9c, 0x4b, 0x4d, 0xaa, 0xb8, 0xac})),
+ std::make_pair(
+ 5,
+ std::vector<uint8_t>(
+ {0x09, 0x80, 0x84, 0xb5, 0x1f, 0xd1, 0x3d, 0xea, 0xe5, 0xf4, 0x32,
+ 0x0d, 0xe9, 0x4a, 0x68, 0x8e, 0xe0, 0x7b, 0xae, 0xa2, 0x80, 0x04,
+ 0x86, 0x68, 0x9a, 0x86, 0x36, 0x11, 0x7b, 0x46, 0xc1, 0xf4, 0xc1,
+ 0xf6, 0xaf, 0x7f, 0x74, 0xae, 0x7c, 0x85, 0x76, 0x00, 0x45, 0x6a,
+ 0x58, 0xa3, 0xaf, 0x25, 0x1d, 0xc4, 0x72, 0x3a, 0x64, 0xcc, 0x7c,
+ 0x0a, 0x5a, 0xb6, 0xd9, 0xca, 0xc9, 0x1c, 0x20, 0xbb})),
+ std::make_pair(
+ 6,
+ std::vector<uint8_t>(
+ {0x60, 0x44, 0x54, 0x0d, 0x56, 0x08, 0x53, 0xeb, 0x1c, 0x57, 0xdf,
+ 0x00, 0x77, 0xdd, 0x38, 0x10, 0x94, 0x78, 0x1c, 0xdb, 0x90, 0x73,
+ 0xe5, 0xb1, 0xb3, 0xd3, 0xf6, 0xc7, 0x82, 0x9e, 0x12, 0x06, 0x6b,
+ 0xba, 0xca, 0x96, 0xd9, 0x89, 0xa6, 0x90, 0xde, 0x72, 0xca, 0x31,
+ 0x33, 0xa8, 0x36, 0x52, 0xba, 0x28, 0x4a, 0x6d, 0x62, 0x94, 0x2b,
+ 0x27, 0x1f, 0xfa, 0x26, 0x20, 0xc9, 0xe7, 0x5b, 0x1f})),
+ std::make_pair(
+ 7,
+ std::vector<uint8_t>(
+ {0x7a, 0x8c, 0xfe, 0x9b, 0x90, 0xf7, 0x5f, 0x7e, 0xcb, 0x3a, 0xcc,
+ 0x05, 0x3a, 0xae, 0xd6, 0x19, 0x31, 0x12, 0xb6, 0xf6, 0xa4, 0xae,
+ 0xeb, 0x3f, 0x65, 0xd3, 0xde, 0x54, 0x19, 0x42, 0xde, 0xb9, 0xe2,
+ 0x22, 0x81, 0x52, 0xa3, 0xc4, 0xbb, 0xbe, 0x72, 0xfc, 0x3b, 0x12,
+ 0x62, 0x95, 0x28, 0xcf, 0xbb, 0x09, 0xfe, 0x63, 0x0f, 0x04, 0x74,
+ 0x33, 0x9f, 0x54, 0xab, 0xf4, 0x53, 0xe2, 0xed, 0x52})),
+ std::make_pair(
+ 8,
+ std::vector<uint8_t>(
+ {0x38, 0x0b, 0xea, 0xf6, 0xea, 0x7c, 0xc9, 0x36, 0x5e, 0x27, 0x0e,
+ 0xf0, 0xe6, 0xf3, 0xa6, 0x4f, 0xb9, 0x02, 0xac, 0xae, 0x51, 0xdd,
+ 0x55, 0x12, 0xf8, 0x42, 0x59, 0xad, 0x2c, 0x91, 0xf4, 0xbc, 0x41,
+ 0x08, 0xdb, 0x73, 0x19, 0x2a, 0x5b, 0xbf, 0xb0, 0xcb, 0xcf, 0x71,
+ 0xe4, 0x6c, 0x3e, 0x21, 0xae, 0xe1, 0xc5, 0xe8, 0x60, 0xdc, 0x96,
+ 0xe8, 0xeb, 0x0b, 0x7b, 0x84, 0x26, 0xe6, 0xab, 0xe9})),
+ std::make_pair(
+ 9,
+ std::vector<uint8_t>(
+ {0x60, 0xfe, 0x3c, 0x45, 0x35, 0xe1, 0xb5, 0x9d, 0x9a, 0x61, 0xea,
+ 0x85, 0x00, 0xbf, 0xac, 0x41, 0xa6, 0x9d, 0xff, 0xb1, 0xce, 0xad,
+ 0xd9, 0xac, 0xa3, 0x23, 0xe9, 0xa6, 0x25, 0xb6, 0x4d, 0xa5, 0x76,
+ 0x3b, 0xad, 0x72, 0x26, 0xda, 0x02, 0xb9, 0xc8, 0xc4, 0xf1, 0xa5,
+ 0xde, 0x14, 0x0a, 0xc5, 0xa6, 0xc1, 0x12, 0x4e, 0x4f, 0x71, 0x8c,
+ 0xe0, 0xb2, 0x8e, 0xa4, 0x73, 0x93, 0xaa, 0x66, 0x37})),
+ std::make_pair(
+ 10,
+ std::vector<uint8_t>(
+ {0x4f, 0xe1, 0x81, 0xf5, 0x4a, 0xd6, 0x3a, 0x29, 0x83, 0xfe, 0xaa,
+ 0xf7, 0x7d, 0x1e, 0x72, 0x35, 0xc2, 0xbe, 0xb1, 0x7f, 0xa3, 0x28,
+ 0xb6, 0xd9, 0x50, 0x5b, 0xda, 0x32, 0x7d, 0xf1, 0x9f, 0xc3, 0x7f,
+ 0x02, 0xc4, 0xb6, 0xf0, 0x36, 0x8c, 0xe2, 0x31, 0x47, 0x31, 0x3a,
+ 0x8e, 0x57, 0x38, 0xb5, 0xfa, 0x2a, 0x95, 0xb2, 0x9d, 0xe1, 0xc7,
+ 0xf8, 0x26, 0x4e, 0xb7, 0x7b, 0x69, 0xf5, 0x85, 0xcd})),
+ std::make_pair(
+ 11,
+ std::vector<uint8_t>(
+ {0xf2, 0x28, 0x77, 0x3c, 0xe3, 0xf3, 0xa4, 0x2b, 0x5f, 0x14, 0x4d,
+ 0x63, 0x23, 0x7a, 0x72, 0xd9, 0x96, 0x93, 0xad, 0xb8, 0x83, 0x7d,
+ 0x0e, 0x11, 0x2a, 0x8a, 0x0f, 0x8f, 0xff, 0xf2, 0xc3, 0x62, 0x85,
+ 0x7a, 0xc4, 0x9c, 0x11, 0xec, 0x74, 0x0d, 0x15, 0x00, 0x74, 0x9d,
+ 0xac, 0x9b, 0x1f, 0x45, 0x48, 0x10, 0x8b, 0xf3, 0x15, 0x57, 0x94,
+ 0xdc, 0xc9, 0xe4, 0x08, 0x28, 0x49, 0xe2, 0xb8, 0x5b})),
+ std::make_pair(
+ 12,
+ std::vector<uint8_t>(
+ {0x96, 0x24, 0x52, 0xa8, 0x45, 0x5c, 0xc5, 0x6c, 0x85, 0x11, 0x31,
+ 0x7e, 0x3b, 0x1f, 0x3b, 0x2c, 0x37, 0xdf, 0x75, 0xf5, 0x88, 0xe9,
+ 0x43, 0x25, 0xfd, 0xd7, 0x70, 0x70, 0x35, 0x9c, 0xf6, 0x3a, 0x9a,
+ 0xe6, 0xe9, 0x30, 0x93, 0x6f, 0xdf, 0x8e, 0x1e, 0x08, 0xff, 0xca,
+ 0x44, 0x0c, 0xfb, 0x72, 0xc2, 0x8f, 0x06, 0xd8, 0x9a, 0x21, 0x51,
+ 0xd1, 0xc4, 0x6c, 0xd5, 0xb2, 0x68, 0xef, 0x85, 0x63})),
+ std::make_pair(
+ 13,
+ std::vector<uint8_t>(
+ {0x43, 0xd4, 0x4b, 0xfa, 0x18, 0x76, 0x8c, 0x59, 0x89, 0x6b, 0xf7,
+ 0xed, 0x17, 0x65, 0xcb, 0x2d, 0x14, 0xaf, 0x8c, 0x26, 0x02, 0x66,
+ 0x03, 0x90, 0x99, 0xb2, 0x5a, 0x60, 0x3e, 0x4d, 0xdc, 0x50, 0x39,
+ 0xd6, 0xef, 0x3a, 0x91, 0x84, 0x7d, 0x10, 0x88, 0xd4, 0x01, 0xc0,
+ 0xc7, 0xe8, 0x47, 0x78, 0x1a, 0x8a, 0x59, 0x0d, 0x33, 0xa3, 0xc6,
+ 0xcb, 0x4d, 0xf0, 0xfa, 0xb1, 0xc2, 0xf2, 0x23, 0x55})),
+ std::make_pair(
+ 14,
+ std::vector<uint8_t>(
+ {0xdc, 0xff, 0xa9, 0xd5, 0x8c, 0x2a, 0x4c, 0xa2, 0xcd, 0xbb, 0x0c,
+ 0x7a, 0xa4, 0xc4, 0xc1, 0xd4, 0x51, 0x65, 0x19, 0x00, 0x89, 0xf4,
+ 0xe9, 0x83, 0xbb, 0x1c, 0x2c, 0xab, 0x4a, 0xae, 0xff, 0x1f, 0xa2,
+ 0xb5, 0xee, 0x51, 0x6f, 0xec, 0xd7, 0x80, 0x54, 0x02, 0x40, 0xbf,
+ 0x37, 0xe5, 0x6c, 0x8b, 0xcc, 0xa7, 0xfa, 0xb9, 0x80, 0xe1, 0xe6,
+ 0x1c, 0x94, 0x00, 0xd8, 0xa9, 0xa5, 0xb1, 0x4a, 0xc6})),
+ std::make_pair(
+ 15,
+ std::vector<uint8_t>(
+ {0x6f, 0xbf, 0x31, 0xb4, 0x5a, 0xb0, 0xc0, 0xb8, 0xda, 0xd1, 0xc0,
+ 0xf5, 0xf4, 0x06, 0x13, 0x79, 0x91, 0x2d, 0xde, 0x5a, 0xa9, 0x22,
+ 0x09, 0x9a, 0x03, 0x0b, 0x72, 0x5c, 0x73, 0x34, 0x6c, 0x52, 0x42,
+ 0x91, 0xad, 0xef, 0x89, 0xd2, 0xf6, 0xfd, 0x8d, 0xfc, 0xda, 0x6d,
+ 0x07, 0xda, 0xd8, 0x11, 0xa9, 0x31, 0x45, 0x36, 0xc2, 0x91, 0x5e,
+ 0xd4, 0x5d, 0xa3, 0x49, 0x47, 0xe8, 0x3d, 0xe3, 0x4e})),
+ std::make_pair(
+ 16,
+ std::vector<uint8_t>(
+ {0xa0, 0xc6, 0x5b, 0xdd, 0xde, 0x8a, 0xde, 0xf5, 0x72, 0x82, 0xb0,
+ 0x4b, 0x11, 0xe7, 0xbc, 0x8a, 0xab, 0x10, 0x5b, 0x99, 0x23, 0x1b,
+ 0x75, 0x0c, 0x02, 0x1f, 0x4a, 0x73, 0x5c, 0xb1, 0xbc, 0xfa, 0xb8,
+ 0x75, 0x53, 0xbb, 0xa3, 0xab, 0xb0, 0xc3, 0xe6, 0x4a, 0x0b, 0x69,
+ 0x55, 0x28, 0x51, 0x85, 0xa0, 0xbd, 0x35, 0xfb, 0x8c, 0xfd, 0xe5,
+ 0x57, 0x32, 0x9b, 0xeb, 0xb1, 0xf6, 0x29, 0xee, 0x93})),
+ std::make_pair(
+ 17,
+ std::vector<uint8_t>(
+ {0xf9, 0x9d, 0x81, 0x55, 0x50, 0x55, 0x8e, 0x81, 0xec, 0xa2, 0xf9,
+ 0x67, 0x18, 0xae, 0xd1, 0x0d, 0x86, 0xf3, 0xf1, 0xcf, 0xb6, 0x75,
+ 0xcc, 0xe0, 0x6b, 0x0e, 0xff, 0x02, 0xf6, 0x17, 0xc5, 0xa4, 0x2c,
+ 0x5a, 0xa7, 0x60, 0x27, 0x0f, 0x26, 0x79, 0xda, 0x26, 0x77, 0xc5,
+ 0xae, 0xb9, 0x4f, 0x11, 0x42, 0x27, 0x7f, 0x21, 0xc7, 0xf7, 0x9f,
+ 0x3c, 0x4f, 0x0c, 0xce, 0x4e, 0xd8, 0xee, 0x62, 0xb1})),
+ std::make_pair(
+ 18,
+ std::vector<uint8_t>(
+ {0x95, 0x39, 0x1d, 0xa8, 0xfc, 0x7b, 0x91, 0x7a, 0x20, 0x44, 0xb3,
+ 0xd6, 0xf5, 0x37, 0x4e, 0x1c, 0xa0, 0x72, 0xb4, 0x14, 0x54, 0xd5,
+ 0x72, 0xc7, 0x35, 0x6c, 0x05, 0xfd, 0x4b, 0xc1, 0xe0, 0xf4, 0x0b,
+ 0x8b, 0xb8, 0xb4, 0xa9, 0xf6, 0xbc, 0xe9, 0xbe, 0x2c, 0x46, 0x23,
+ 0xc3, 0x99, 0xb0, 0xdc, 0xa0, 0xda, 0xb0, 0x5c, 0xb7, 0x28, 0x1b,
+ 0x71, 0xa2, 0x1b, 0x0e, 0xbc, 0xd9, 0xe5, 0x56, 0x70})),
+ std::make_pair(
+ 19,
+ std::vector<uint8_t>(
+ {0x04, 0xb9, 0xcd, 0x3d, 0x20, 0xd2, 0x21, 0xc0, 0x9a, 0xc8, 0x69,
+ 0x13, 0xd3, 0xdc, 0x63, 0x04, 0x19, 0x89, 0xa9, 0xa1, 0xe6, 0x94,
+ 0xf1, 0xe6, 0x39, 0xa3, 0xba, 0x7e, 0x45, 0x18, 0x40, 0xf7, 0x50,
+ 0xc2, 0xfc, 0x19, 0x1d, 0x56, 0xad, 0x61, 0xf2, 0xe7, 0x93, 0x6b,
+ 0xc0, 0xac, 0x8e, 0x09, 0x4b, 0x60, 0xca, 0xee, 0xd8, 0x78, 0xc1,
+ 0x87, 0x99, 0x04, 0x54, 0x02, 0xd6, 0x1c, 0xea, 0xf9})),
+ std::make_pair(
+ 20,
+ std::vector<uint8_t>(
+ {0xec, 0x0e, 0x0e, 0xf7, 0x07, 0xe4, 0xed, 0x6c, 0x0c, 0x66, 0xf9,
+ 0xe0, 0x89, 0xe4, 0x95, 0x4b, 0x05, 0x80, 0x30, 0xd2, 0xdd, 0x86,
+ 0x39, 0x8f, 0xe8, 0x40, 0x59, 0x63, 0x1f, 0x9e, 0xe5, 0x91, 0xd9,
+ 0xd7, 0x73, 0x75, 0x35, 0x51, 0x49, 0x17, 0x8c, 0x0c, 0xf8, 0xf8,
+ 0xe7, 0xc4, 0x9e, 0xd2, 0xa5, 0xe4, 0xf9, 0x54, 0x88, 0xa2, 0x24,
+ 0x70, 0x67, 0xc2, 0x08, 0x51, 0x0f, 0xad, 0xc4, 0x4c})),
+ std::make_pair(
+ 21,
+ std::vector<uint8_t>(
+ {0x9a, 0x37, 0xcc, 0xe2, 0x73, 0xb7, 0x9c, 0x09, 0x91, 0x36, 0x77,
+ 0x51, 0x0e, 0xaf, 0x76, 0x88, 0xe8, 0x9b, 0x33, 0x14, 0xd3, 0x53,
+ 0x2f, 0xd2, 0x76, 0x4c, 0x39, 0xde, 0x02, 0x2a, 0x29, 0x45, 0xb5,
+ 0x71, 0x0d, 0x13, 0x51, 0x7a, 0xf8, 0xdd, 0xc0, 0x31, 0x66, 0x24,
+ 0xe7, 0x3b, 0xec, 0x1c, 0xe6, 0x7d, 0xf1, 0x52, 0x28, 0x30, 0x20,
+ 0x36, 0xf3, 0x30, 0xab, 0x0c, 0xb4, 0xd2, 0x18, 0xdd})),
+ std::make_pair(
+ 22,
+ std::vector<uint8_t>(
+ {0x4c, 0xf9, 0xbb, 0x8f, 0xb3, 0xd4, 0xde, 0x8b, 0x38, 0xb2, 0xf2,
+ 0x62, 0xd3, 0xc4, 0x0f, 0x46, 0xdf, 0xe7, 0x47, 0xe8, 0xfc, 0x0a,
+ 0x41, 0x4c, 0x19, 0x3d, 0x9f, 0xcf, 0x75, 0x31, 0x06, 0xce, 0x47,
+ 0xa1, 0x8f, 0x17, 0x2f, 0x12, 0xe8, 0xa2, 0xf1, 0xc2, 0x67, 0x26,
+ 0x54, 0x53, 0x58, 0xe5, 0xee, 0x28, 0xc9, 0xe2, 0x21, 0x3a, 0x87,
+ 0x87, 0xaa, 0xfb, 0xc5, 0x16, 0xd2, 0x34, 0x31, 0x52})),
+ std::make_pair(
+ 23,
+ std::vector<uint8_t>(
+ {0x64, 0xe0, 0xc6, 0x3a, 0xf9, 0xc8, 0x08, 0xfd, 0x89, 0x31, 0x37,
+ 0x12, 0x98, 0x67, 0xfd, 0x91, 0x93, 0x9d, 0x53, 0xf2, 0xaf, 0x04,
+ 0xbe, 0x4f, 0xa2, 0x68, 0x00, 0x61, 0x00, 0x06, 0x9b, 0x2d, 0x69,
+ 0xda, 0xa5, 0xc5, 0xd8, 0xed, 0x7f, 0xdd, 0xcb, 0x2a, 0x70, 0xee,
+ 0xec, 0xdf, 0x2b, 0x10, 0x5d, 0xd4, 0x6a, 0x1e, 0x3b, 0x73, 0x11,
+ 0x72, 0x8f, 0x63, 0x9a, 0xb4, 0x89, 0x32, 0x6b, 0xc9})),
+ std::make_pair(
+ 24,
+ std::vector<uint8_t>(
+ {0x5e, 0x9c, 0x93, 0x15, 0x8d, 0x65, 0x9b, 0x2d, 0xef, 0x06, 0xb0,
+ 0xc3, 0xc7, 0x56, 0x50, 0x45, 0x54, 0x26, 0x62, 0xd6, 0xee, 0xe8,
+ 0xa9, 0x6a, 0x89, 0xb7, 0x8a, 0xde, 0x09, 0xfe, 0x8b, 0x3d, 0xcc,
+ 0x09, 0x6d, 0x4f, 0xe4, 0x88, 0x15, 0xd8, 0x8d, 0x8f, 0x82, 0x62,
+ 0x01, 0x56, 0x60, 0x2a, 0xf5, 0x41, 0x95, 0x5e, 0x1f, 0x6c, 0xa3,
+ 0x0d, 0xce, 0x14, 0xe2, 0x54, 0xc3, 0x26, 0xb8, 0x8f})),
+ std::make_pair(
+ 25,
+ std::vector<uint8_t>(
+ {0x77, 0x75, 0xdf, 0xf8, 0x89, 0x45, 0x8d, 0xd1, 0x1a, 0xef, 0x41,
+ 0x72, 0x76, 0x85, 0x3e, 0x21, 0x33, 0x5e, 0xb8, 0x8e, 0x4d, 0xec,
+ 0x9c, 0xfb, 0x4e, 0x9e, 0xdb, 0x49, 0x82, 0x00, 0x88, 0x55, 0x1a,
+ 0x2c, 0xa6, 0x03, 0x39, 0xf1, 0x20, 0x66, 0x10, 0x11, 0x69, 0xf0,
+ 0xdf, 0xe8, 0x4b, 0x09, 0x8f, 0xdd, 0xb1, 0x48, 0xd9, 0xda, 0x6b,
+ 0x3d, 0x61, 0x3d, 0xf2, 0x63, 0x88, 0x9a, 0xd6, 0x4b})),
+ std::make_pair(
+ 26,
+ std::vector<uint8_t>(
+ {0xf0, 0xd2, 0x80, 0x5a, 0xfb, 0xb9, 0x1f, 0x74, 0x39, 0x51, 0x35,
+ 0x1a, 0x6d, 0x02, 0x4f, 0x93, 0x53, 0xa2, 0x3c, 0x7c, 0xe1, 0xfc,
+ 0x2b, 0x05, 0x1b, 0x3a, 0x8b, 0x96, 0x8c, 0x23, 0x3f, 0x46, 0xf5,
+ 0x0f, 0x80, 0x6e, 0xcb, 0x15, 0x68, 0xff, 0xaa, 0x0b, 0x60, 0x66,
+ 0x1e, 0x33, 0x4b, 0x21, 0xdd, 0xe0, 0x4f, 0x8f, 0xa1, 0x55, 0xac,
+ 0x74, 0x0e, 0xeb, 0x42, 0xe2, 0x0b, 0x60, 0xd7, 0x64})),
+ std::make_pair(
+ 27,
+ std::vector<uint8_t>(
+ {0x86, 0xa2, 0xaf, 0x31, 0x6e, 0x7d, 0x77, 0x54, 0x20, 0x1b, 0x94,
+ 0x2e, 0x27, 0x53, 0x64, 0xac, 0x12, 0xea, 0x89, 0x62, 0xab, 0x5b,
+ 0xd8, 0xd7, 0xfb, 0x27, 0x6d, 0xc5, 0xfb, 0xff, 0xc8, 0xf9, 0xa2,
+ 0x8c, 0xae, 0x4e, 0x48, 0x67, 0xdf, 0x67, 0x80, 0xd9, 0xb7, 0x25,
+ 0x24, 0x16, 0x09, 0x27, 0xc8, 0x55, 0xda, 0x5b, 0x60, 0x78, 0xe0,
+ 0xb5, 0x54, 0xaa, 0x91, 0xe3, 0x1c, 0xb9, 0xca, 0x1d})),
+ std::make_pair(
+ 28,
+ std::vector<uint8_t>(
+ {0x10, 0xbd, 0xf0, 0xca, 0xa0, 0x80, 0x27, 0x05, 0xe7, 0x06, 0x36,
+ 0x9b, 0xaf, 0x8a, 0x3f, 0x79, 0xd7, 0x2c, 0x0a, 0x03, 0xa8, 0x06,
+ 0x75, 0xa7, 0xbb, 0xb0, 0x0b, 0xe3, 0xa4, 0x5e, 0x51, 0x64, 0x24,
+ 0xd1, 0xee, 0x88, 0xef, 0xb5, 0x6f, 0x6d, 0x57, 0x77, 0x54, 0x5a,
+ 0xe6, 0xe2, 0x77, 0x65, 0xc3, 0xa8, 0xf5, 0xe4, 0x93, 0xfc, 0x30,
+ 0x89, 0x15, 0x63, 0x89, 0x33, 0xa1, 0xdf, 0xee, 0x55})),
+ std::make_pair(
+ 29,
+ std::vector<uint8_t>(
+ {0xb0, 0x17, 0x81, 0x09, 0x2b, 0x17, 0x48, 0x45, 0x9e, 0x2e, 0x4e,
+ 0xc1, 0x78, 0x69, 0x66, 0x27, 0xbf, 0x4e, 0xba, 0xfe, 0xbb, 0xa7,
+ 0x74, 0xec, 0xf0, 0x18, 0xb7, 0x9a, 0x68, 0xae, 0xb8, 0x49, 0x17,
+ 0xbf, 0x0b, 0x84, 0xbb, 0x79, 0xd1, 0x7b, 0x74, 0x31, 0x51, 0x14,
+ 0x4c, 0xd6, 0x6b, 0x7b, 0x33, 0xa4, 0xb9, 0xe5, 0x2c, 0x76, 0xc4,
+ 0xe1, 0x12, 0x05, 0x0f, 0xf5, 0x38, 0x5b, 0x7f, 0x0b})),
+ std::make_pair(
+ 30,
+ std::vector<uint8_t>(
+ {0xc6, 0xdb, 0xc6, 0x1d, 0xec, 0x6e, 0xae, 0xac, 0x81, 0xe3, 0xd5,
+ 0xf7, 0x55, 0x20, 0x3c, 0x8e, 0x22, 0x05, 0x51, 0x53, 0x4a, 0x0b,
+ 0x2f, 0xd1, 0x05, 0xa9, 0x18, 0x89, 0x94, 0x5a, 0x63, 0x85, 0x50,
+ 0x20, 0x4f, 0x44, 0x09, 0x3d, 0xd9, 0x98, 0xc0, 0x76, 0x20, 0x5d,
+ 0xff, 0xad, 0x70, 0x3a, 0x0e, 0x5c, 0xd3, 0xc7, 0xf4, 0x38, 0xa7,
+ 0xe6, 0x34, 0xcd, 0x59, 0xfe, 0xde, 0xdb, 0x53, 0x9e})),
+ std::make_pair(
+ 31,
+ std::vector<uint8_t>(
+ {0xeb, 0xa5, 0x1a, 0xcf, 0xfb, 0x4c, 0xea, 0x31, 0xdb, 0x4b, 0x8d,
+ 0x87, 0xe9, 0xbf, 0x7d, 0xd4, 0x8f, 0xe9, 0x7b, 0x02, 0x53, 0xae,
+ 0x67, 0xaa, 0x58, 0x0f, 0x9a, 0xc4, 0xa9, 0xd9, 0x41, 0xf2, 0xbe,
+ 0xa5, 0x18, 0xee, 0x28, 0x68, 0x18, 0xcc, 0x9f, 0x63, 0x3f, 0x2a,
+ 0x3b, 0x9f, 0xb6, 0x8e, 0x59, 0x4b, 0x48, 0xcd, 0xd6, 0xd5, 0x15,
+ 0xbf, 0x1d, 0x52, 0xba, 0x6c, 0x85, 0xa2, 0x03, 0xa7})),
+ std::make_pair(
+ 32,
+ std::vector<uint8_t>(
+ {0x86, 0x22, 0x1f, 0x3a, 0xda, 0x52, 0x03, 0x7b, 0x72, 0x22, 0x4f,
+ 0x10, 0x5d, 0x79, 0x99, 0x23, 0x1c, 0x5e, 0x55, 0x34, 0xd0, 0x3d,
+ 0xa9, 0xd9, 0xc0, 0xa1, 0x2a, 0xcb, 0x68, 0x46, 0x0c, 0xd3, 0x75,
+ 0xda, 0xf8, 0xe2, 0x43, 0x86, 0x28, 0x6f, 0x96, 0x68, 0xf7, 0x23,
+ 0x26, 0xdb, 0xf9, 0x9b, 0xa0, 0x94, 0x39, 0x24, 0x37, 0xd3, 0x98,
+ 0xe9, 0x5b, 0xb8, 0x16, 0x1d, 0x71, 0x7f, 0x89, 0x91})),
+ std::make_pair(
+ 33,
+ std::vector<uint8_t>(
+ {0x55, 0x95, 0xe0, 0x5c, 0x13, 0xa7, 0xec, 0x4d, 0xc8, 0xf4, 0x1f,
+ 0xb7, 0x0c, 0xb5, 0x0a, 0x71, 0xbc, 0xe1, 0x7c, 0x02, 0x4f, 0xf6,
+ 0xde, 0x7a, 0xf6, 0x18, 0xd0, 0xcc, 0x4e, 0x9c, 0x32, 0xd9, 0x57,
+ 0x0d, 0x6d, 0x3e, 0xa4, 0x5b, 0x86, 0x52, 0x54, 0x91, 0x03, 0x0c,
+ 0x0d, 0x8f, 0x2b, 0x18, 0x36, 0xd5, 0x77, 0x8c, 0x1c, 0xe7, 0x35,
+ 0xc1, 0x77, 0x07, 0xdf, 0x36, 0x4d, 0x05, 0x43, 0x47})),
+ std::make_pair(
+ 34,
+ std::vector<uint8_t>(
+ {0xce, 0x0f, 0x4f, 0x6a, 0xca, 0x89, 0x59, 0x0a, 0x37, 0xfe, 0x03,
+ 0x4d, 0xd7, 0x4d, 0xd5, 0xfa, 0x65, 0xeb, 0x1c, 0xbd, 0x0a, 0x41,
+ 0x50, 0x8a, 0xad, 0xdc, 0x09, 0x35, 0x1a, 0x3c, 0xea, 0x6d, 0x18,
+ 0xcb, 0x21, 0x89, 0xc5, 0x4b, 0x70, 0x0c, 0x00, 0x9f, 0x4c, 0xbf,
+ 0x05, 0x21, 0xc7, 0xea, 0x01, 0xbe, 0x61, 0xc5, 0xae, 0x09, 0xcb,
+ 0x54, 0xf2, 0x7b, 0xc1, 0xb4, 0x4d, 0x65, 0x8c, 0x82})),
+ std::make_pair(
+ 35,
+ std::vector<uint8_t>(
+ {0x7e, 0xe8, 0x0b, 0x06, 0xa2, 0x15, 0xa3, 0xbc, 0xa9, 0x70, 0xc7,
+ 0x7c, 0xda, 0x87, 0x61, 0x82, 0x2b, 0xc1, 0x03, 0xd4, 0x4f, 0xa4,
+ 0xb3, 0x3f, 0x4d, 0x07, 0xdc, 0xb9, 0x97, 0xe3, 0x6d, 0x55, 0x29,
+ 0x8b, 0xce, 0xae, 0x12, 0x24, 0x1b, 0x3f, 0xa0, 0x7f, 0xa6, 0x3b,
+ 0xe5, 0x57, 0x60, 0x68, 0xda, 0x38, 0x7b, 0x8d, 0x58, 0x59, 0xae,
+ 0xab, 0x70, 0x13, 0x69, 0x84, 0x8b, 0x17, 0x6d, 0x42})),
+ std::make_pair(
+ 36,
+ std::vector<uint8_t>(
+ {0x94, 0x0a, 0x84, 0xb6, 0xa8, 0x4d, 0x10, 0x9a, 0xab, 0x20, 0x8c,
+ 0x02, 0x4c, 0x6c, 0xe9, 0x64, 0x76, 0x76, 0xba, 0x0a, 0xaa, 0x11,
+ 0xf8, 0x6d, 0xbb, 0x70, 0x18, 0xf9, 0xfd, 0x22, 0x20, 0xa6, 0xd9,
+ 0x01, 0xa9, 0x02, 0x7f, 0x9a, 0xbc, 0xf9, 0x35, 0x37, 0x27, 0x27,
+ 0xcb, 0xf0, 0x9e, 0xbd, 0x61, 0xa2, 0xa2, 0xee, 0xb8, 0x76, 0x53,
+ 0xe8, 0xec, 0xad, 0x1b, 0xab, 0x85, 0xdc, 0x83, 0x27})),
+ std::make_pair(
+ 37,
+ std::vector<uint8_t>(
+ {0x20, 0x20, 0xb7, 0x82, 0x64, 0xa8, 0x2d, 0x9f, 0x41, 0x51, 0x14,
+ 0x1a, 0xdb, 0xa8, 0xd4, 0x4b, 0xf2, 0x0c, 0x5e, 0xc0, 0x62, 0xee,
+ 0xe9, 0xb5, 0x95, 0xa1, 0x1f, 0x9e, 0x84, 0x90, 0x1b, 0xf1, 0x48,
+ 0xf2, 0x98, 0xe0, 0xc9, 0xf8, 0x77, 0x7d, 0xcd, 0xbc, 0x7c, 0xc4,
+ 0x67, 0x0a, 0xac, 0x35, 0x6c, 0xc2, 0xad, 0x8c, 0xcb, 0x16, 0x29,
+ 0xf1, 0x6f, 0x6a, 0x76, 0xbc, 0xef, 0xbe, 0xe7, 0x60})),
+ std::make_pair(
+ 38,
+ std::vector<uint8_t>(
+ {0xd1, 0xb8, 0x97, 0xb0, 0xe0, 0x75, 0xba, 0x68, 0xab, 0x57, 0x2a,
+ 0xdf, 0x9d, 0x9c, 0x43, 0x66, 0x63, 0xe4, 0x3e, 0xb3, 0xd8, 0xe6,
+ 0x2d, 0x92, 0xfc, 0x49, 0xc9, 0xbe, 0x21, 0x4e, 0x6f, 0x27, 0x87,
+ 0x3f, 0xe2, 0x15, 0xa6, 0x51, 0x70, 0xe6, 0xbe, 0xa9, 0x02, 0x40,
+ 0x8a, 0x25, 0xb4, 0x95, 0x06, 0xf4, 0x7b, 0xab, 0xd0, 0x7c, 0xec,
+ 0xf7, 0x11, 0x3e, 0xc1, 0x0c, 0x5d, 0xd3, 0x12, 0x52})),
+ std::make_pair(
+ 39,
+ std::vector<uint8_t>(
+ {0xb1, 0x4d, 0x0c, 0x62, 0xab, 0xfa, 0x46, 0x9a, 0x35, 0x71, 0x77,
+ 0xe5, 0x94, 0xc1, 0x0c, 0x19, 0x42, 0x43, 0xed, 0x20, 0x25, 0xab,
+ 0x8a, 0xa5, 0xad, 0x2f, 0xa4, 0x1a, 0xd3, 0x18, 0xe0, 0xff, 0x48,
+ 0xcd, 0x5e, 0x60, 0xbe, 0xc0, 0x7b, 0x13, 0x63, 0x4a, 0x71, 0x1d,
+ 0x23, 0x26, 0xe4, 0x88, 0xa9, 0x85, 0xf3, 0x1e, 0x31, 0x15, 0x33,
+ 0x99, 0xe7, 0x30, 0x88, 0xef, 0xc8, 0x6a, 0x5c, 0x55})),
+ std::make_pair(
+ 40,
+ std::vector<uint8_t>(
+ {0x41, 0x69, 0xc5, 0xcc, 0x80, 0x8d, 0x26, 0x97, 0xdc, 0x2a, 0x82,
+ 0x43, 0x0d, 0xc2, 0x3e, 0x3c, 0xd3, 0x56, 0xdc, 0x70, 0xa9, 0x45,
+ 0x66, 0x81, 0x05, 0x02, 0xb8, 0xd6, 0x55, 0xb3, 0x9a, 0xbf, 0x9e,
+ 0x7f, 0x90, 0x2f, 0xe7, 0x17, 0xe0, 0x38, 0x92, 0x19, 0x85, 0x9e,
+ 0x19, 0x45, 0xdf, 0x1a, 0xf6, 0xad, 0xa4, 0x2e, 0x4c, 0xcd, 0xa5,
+ 0x5a, 0x19, 0x7b, 0x71, 0x00, 0xa3, 0x0c, 0x30, 0xa1})),
+ std::make_pair(
+ 41,
+ std::vector<uint8_t>(
+ {0x25, 0x8a, 0x4e, 0xdb, 0x11, 0x3d, 0x66, 0xc8, 0x39, 0xc8, 0xb1,
+ 0xc9, 0x1f, 0x15, 0xf3, 0x5a, 0xde, 0x60, 0x9f, 0x11, 0xcd, 0x7f,
+ 0x86, 0x81, 0xa4, 0x04, 0x5b, 0x9f, 0xef, 0x7b, 0x0b, 0x24, 0xc8,
+ 0x2c, 0xda, 0x06, 0xa5, 0xf2, 0x06, 0x7b, 0x36, 0x88, 0x25, 0xe3,
+ 0x91, 0x4e, 0x53, 0xd6, 0x94, 0x8e, 0xde, 0x92, 0xef, 0xd6, 0xe8,
+ 0x38, 0x7f, 0xa2, 0xe5, 0x37, 0x23, 0x9b, 0x5b, 0xee})),
+ std::make_pair(
+ 42,
+ std::vector<uint8_t>(
+ {0x79, 0xd2, 0xd8, 0x69, 0x6d, 0x30, 0xf3, 0x0f, 0xb3, 0x46, 0x57,
+ 0x76, 0x11, 0x71, 0xa1, 0x1e, 0x6c, 0x3f, 0x1e, 0x64, 0xcb, 0xe7,
+ 0xbe, 0xbe, 0xe1, 0x59, 0xcb, 0x95, 0xbf, 0xaf, 0x81, 0x2b, 0x4f,
+ 0x41, 0x1e, 0x2f, 0x26, 0xd9, 0xc4, 0x21, 0xdc, 0x2c, 0x28, 0x4a,
+ 0x33, 0x42, 0xd8, 0x23, 0xec, 0x29, 0x38, 0x49, 0xe4, 0x2d, 0x1e,
+ 0x46, 0xb0, 0xa4, 0xac, 0x1e, 0x3c, 0x86, 0xab, 0xaa})),
+ std::make_pair(
+ 43,
+ std::vector<uint8_t>(
+ {0x8b, 0x94, 0x36, 0x01, 0x0d, 0xc5, 0xde, 0xe9, 0x92, 0xae, 0x38,
+ 0xae, 0xa9, 0x7f, 0x2c, 0xd6, 0x3b, 0x94, 0x6d, 0x94, 0xfe, 0xdd,
+ 0x2e, 0xc9, 0x67, 0x1d, 0xcd, 0xe3, 0xbd, 0x4c, 0xe9, 0x56, 0x4d,
+ 0x55, 0x5c, 0x66, 0xc1, 0x5b, 0xb2, 0xb9, 0x00, 0xdf, 0x72, 0xed,
+ 0xb6, 0xb8, 0x91, 0xeb, 0xca, 0xdf, 0xef, 0xf6, 0x3c, 0x9e, 0xa4,
+ 0x03, 0x6a, 0x99, 0x8b, 0xe7, 0x97, 0x39, 0x81, 0xe7})),
+ std::make_pair(
+ 44,
+ std::vector<uint8_t>(
+ {0xc8, 0xf6, 0x8e, 0x69, 0x6e, 0xd2, 0x82, 0x42, 0xbf, 0x99, 0x7f,
+ 0x5b, 0x3b, 0x34, 0x95, 0x95, 0x08, 0xe4, 0x2d, 0x61, 0x38, 0x10,
+ 0xf1, 0xe2, 0xa4, 0x35, 0xc9, 0x6e, 0xd2, 0xff, 0x56, 0x0c, 0x70,
+ 0x22, 0xf3, 0x61, 0xa9, 0x23, 0x4b, 0x98, 0x37, 0xfe, 0xee, 0x90,
+ 0xbf, 0x47, 0x92, 0x2e, 0xe0, 0xfd, 0x5f, 0x8d, 0xdf, 0x82, 0x37,
+ 0x18, 0xd8, 0x6d, 0x1e, 0x16, 0xc6, 0x09, 0x00, 0x71})),
+ std::make_pair(
+ 45,
+ std::vector<uint8_t>(
+ {0xb0, 0x2d, 0x3e, 0xee, 0x48, 0x60, 0xd5, 0x86, 0x8b, 0x2c, 0x39,
+ 0xce, 0x39, 0xbf, 0xe8, 0x10, 0x11, 0x29, 0x05, 0x64, 0xdd, 0x67,
+ 0x8c, 0x85, 0xe8, 0x78, 0x3f, 0x29, 0x30, 0x2d, 0xfc, 0x13, 0x99,
+ 0xba, 0x95, 0xb6, 0xb5, 0x3c, 0xd9, 0xeb, 0xbf, 0x40, 0x0c, 0xca,
+ 0x1d, 0xb0, 0xab, 0x67, 0xe1, 0x9a, 0x32, 0x5f, 0x2d, 0x11, 0x58,
+ 0x12, 0xd2, 0x5d, 0x00, 0x97, 0x8a, 0xd1, 0xbc, 0xa4})),
+ std::make_pair(
+ 46,
+ std::vector<uint8_t>(
+ {0x76, 0x93, 0xea, 0x73, 0xaf, 0x3a, 0xc4, 0xda, 0xd2, 0x1c, 0xa0,
+ 0xd8, 0xda, 0x85, 0xb3, 0x11, 0x8a, 0x7d, 0x1c, 0x60, 0x24, 0xcf,
+ 0xaf, 0x55, 0x76, 0x99, 0x86, 0x82, 0x17, 0xbc, 0x0c, 0x2f, 0x44,
+ 0xa1, 0x99, 0xbc, 0x6c, 0x0e, 0xdd, 0x51, 0x97, 0x98, 0xba, 0x05,
+ 0xbd, 0x5b, 0x1b, 0x44, 0x84, 0x34, 0x6a, 0x47, 0xc2, 0xca, 0xdf,
+ 0x6b, 0xf3, 0x0b, 0x78, 0x5c, 0xc8, 0x8b, 0x2b, 0xaf})),
+ std::make_pair(
+ 47,
+ std::vector<uint8_t>(
+ {0xa0, 0xe5, 0xc1, 0xc0, 0x03, 0x1c, 0x02, 0xe4, 0x8b, 0x7f, 0x09,
+ 0xa5, 0xe8, 0x96, 0xee, 0x9a, 0xef, 0x2f, 0x17, 0xfc, 0x9e, 0x18,
+ 0xe9, 0x97, 0xd7, 0xf6, 0xca, 0xc7, 0xae, 0x31, 0x64, 0x22, 0xc2,
+ 0xb1, 0xe7, 0x79, 0x84, 0xe5, 0xf3, 0xa7, 0x3c, 0xb4, 0x5d, 0xee,
+ 0xd5, 0xd3, 0xf8, 0x46, 0x00, 0x10, 0x5e, 0x6e, 0xe3, 0x8f, 0x2d,
+ 0x09, 0x0c, 0x7d, 0x04, 0x42, 0xea, 0x34, 0xc4, 0x6d})),
+ std::make_pair(
+ 48,
+ std::vector<uint8_t>(
+ {0x41, 0xda, 0xa6, 0xad, 0xcf, 0xdb, 0x69, 0xf1, 0x44, 0x0c, 0x37,
+ 0xb5, 0x96, 0x44, 0x01, 0x65, 0xc1, 0x5a, 0xda, 0x59, 0x68, 0x13,
+ 0xe2, 0xe2, 0x2f, 0x06, 0x0f, 0xcd, 0x55, 0x1f, 0x24, 0xde, 0xe8,
+ 0xe0, 0x4b, 0xa6, 0x89, 0x03, 0x87, 0x88, 0x6c, 0xee, 0xc4, 0xa7,
+ 0xa0, 0xd7, 0xfc, 0x6b, 0x44, 0x50, 0x63, 0x92, 0xec, 0x38, 0x22,
+ 0xc0, 0xd8, 0xc1, 0xac, 0xfc, 0x7d, 0x5a, 0xeb, 0xe8})),
+ std::make_pair(
+ 49,
+ std::vector<uint8_t>(
+ {0x14, 0xd4, 0xd4, 0x0d, 0x59, 0x84, 0xd8, 0x4c, 0x5c, 0xf7, 0x52,
+ 0x3b, 0x77, 0x98, 0xb2, 0x54, 0xe2, 0x75, 0xa3, 0xa8, 0xcc, 0x0a,
+ 0x1b, 0xd0, 0x6e, 0xbc, 0x0b, 0xee, 0x72, 0x68, 0x56, 0xac, 0xc3,
+ 0xcb, 0xf5, 0x16, 0xff, 0x66, 0x7c, 0xda, 0x20, 0x58, 0xad, 0x5c,
+ 0x34, 0x12, 0x25, 0x44, 0x60, 0xa8, 0x2c, 0x92, 0x18, 0x70, 0x41,
+ 0x36, 0x3c, 0xc7, 0x7a, 0x4d, 0xc2, 0x15, 0xe4, 0x87})),
+ std::make_pair(
+ 50,
+ std::vector<uint8_t>(
+ {0xd0, 0xe7, 0xa1, 0xe2, 0xb9, 0xa4, 0x47, 0xfe, 0xe8, 0x3e, 0x22,
+ 0x77, 0xe9, 0xff, 0x80, 0x10, 0xc2, 0xf3, 0x75, 0xae, 0x12, 0xfa,
+ 0x7a, 0xaa, 0x8c, 0xa5, 0xa6, 0x31, 0x78, 0x68, 0xa2, 0x6a, 0x36,
+ 0x7a, 0x0b, 0x69, 0xfb, 0xc1, 0xcf, 0x32, 0xa5, 0x5d, 0x34, 0xeb,
+ 0x37, 0x06, 0x63, 0x01, 0x6f, 0x3d, 0x21, 0x10, 0x23, 0x0e, 0xba,
+ 0x75, 0x40, 0x28, 0xa5, 0x6f, 0x54, 0xac, 0xf5, 0x7c})),
+ std::make_pair(
+ 51,
+ std::vector<uint8_t>(
+ {0xe7, 0x71, 0xaa, 0x8d, 0xb5, 0xa3, 0xe0, 0x43, 0xe8, 0x17, 0x8f,
+ 0x39, 0xa0, 0x85, 0x7b, 0xa0, 0x4a, 0x3f, 0x18, 0xe4, 0xaa, 0x05,
+ 0x74, 0x3c, 0xf8, 0xd2, 0x22, 0xb0, 0xb0, 0x95, 0x82, 0x53, 0x50,
+ 0xba, 0x42, 0x2f, 0x63, 0x38, 0x2a, 0x23, 0xd9, 0x2e, 0x41, 0x49,
+ 0x07, 0x4e, 0x81, 0x6a, 0x36, 0xc1, 0xcd, 0x28, 0x28, 0x4d, 0x14,
+ 0x62, 0x67, 0x94, 0x0b, 0x31, 0xf8, 0x81, 0x8e, 0xa2})),
+ std::make_pair(
+ 52,
+ std::vector<uint8_t>(
+ {0xfe, 0xb4, 0xfd, 0x6f, 0x9e, 0x87, 0xa5, 0x6b, 0xef, 0x39, 0x8b,
+ 0x32, 0x84, 0xd2, 0xbd, 0xa5, 0xb5, 0xb0, 0xe1, 0x66, 0x58, 0x3a,
+ 0x66, 0xb6, 0x1e, 0x53, 0x84, 0x57, 0xff, 0x05, 0x84, 0x87, 0x2c,
+ 0x21, 0xa3, 0x29, 0x62, 0xb9, 0x92, 0x8f, 0xfa, 0xb5, 0x8d, 0xe4,
+ 0xaf, 0x2e, 0xdd, 0x4e, 0x15, 0xd8, 0xb3, 0x55, 0x70, 0x52, 0x32,
+ 0x07, 0xff, 0x4e, 0x2a, 0x5a, 0xa7, 0x75, 0x4c, 0xaa})),
+ std::make_pair(
+ 53,
+ std::vector<uint8_t>(
+ {0x46, 0x2f, 0x17, 0xbf, 0x00, 0x5f, 0xb1, 0xc1, 0xb9, 0xe6, 0x71,
+ 0x77, 0x9f, 0x66, 0x52, 0x09, 0xec, 0x28, 0x73, 0xe3, 0xe4, 0x11,
+ 0xf9, 0x8d, 0xab, 0xf2, 0x40, 0xa1, 0xd5, 0xec, 0x3f, 0x95, 0xce,
+ 0x67, 0x96, 0xb6, 0xfc, 0x23, 0xfe, 0x17, 0x19, 0x03, 0xb5, 0x02,
+ 0x02, 0x34, 0x67, 0xde, 0xc7, 0x27, 0x3f, 0xf7, 0x48, 0x79, 0xb9,
+ 0x29, 0x67, 0xa2, 0xa4, 0x3a, 0x5a, 0x18, 0x3d, 0x33})),
+ std::make_pair(
+ 54,
+ std::vector<uint8_t>(
+ {0xd3, 0x33, 0x81, 0x93, 0xb6, 0x45, 0x53, 0xdb, 0xd3, 0x8d, 0x14,
+ 0x4b, 0xea, 0x71, 0xc5, 0x91, 0x5b, 0xb1, 0x10, 0xe2, 0xd8, 0x81,
+ 0x80, 0xdb, 0xc5, 0xdb, 0x36, 0x4f, 0xd6, 0x17, 0x1d, 0xf3, 0x17,
+ 0xfc, 0x72, 0x68, 0x83, 0x1b, 0x5a, 0xef, 0x75, 0xe4, 0x34, 0x2b,
+ 0x2f, 0xad, 0x87, 0x97, 0xba, 0x39, 0xed, 0xdc, 0xef, 0x80, 0xe6,
+ 0xec, 0x08, 0x15, 0x93, 0x50, 0xb1, 0xad, 0x69, 0x6d})),
+ std::make_pair(
+ 55,
+ std::vector<uint8_t>(
+ {0xe1, 0x59, 0x0d, 0x58, 0x5a, 0x3d, 0x39, 0xf7, 0xcb, 0x59, 0x9a,
+ 0xbd, 0x47, 0x90, 0x70, 0x96, 0x64, 0x09, 0xa6, 0x84, 0x6d, 0x43,
+ 0x77, 0xac, 0xf4, 0x47, 0x1d, 0x06, 0x5d, 0x5d, 0xb9, 0x41, 0x29,
+ 0xcc, 0x9b, 0xe9, 0x25, 0x73, 0xb0, 0x5e, 0xd2, 0x26, 0xbe, 0x1e,
+ 0x9b, 0x7c, 0xb0, 0xca, 0xbe, 0x87, 0x91, 0x85, 0x89, 0xf8, 0x0d,
+ 0xad, 0xd4, 0xef, 0x5e, 0xf2, 0x5a, 0x93, 0xd2, 0x8e})),
+ std::make_pair(
+ 56,
+ std::vector<uint8_t>(
+ {0xf8, 0xf3, 0x72, 0x6a, 0xc5, 0xa2, 0x6c, 0xc8, 0x01, 0x32, 0x49,
+ 0x3a, 0x6f, 0xed, 0xcb, 0x0e, 0x60, 0x76, 0x0c, 0x09, 0xcf, 0xc8,
+ 0x4c, 0xad, 0x17, 0x81, 0x75, 0x98, 0x68, 0x19, 0x66, 0x5e, 0x76,
+ 0x84, 0x2d, 0x7b, 0x9f, 0xed, 0xf7, 0x6d, 0xdd, 0xeb, 0xf5, 0xd3,
+ 0xf5, 0x6f, 0xaa, 0xad, 0x44, 0x77, 0x58, 0x7a, 0xf2, 0x16, 0x06,
+ 0xd3, 0x96, 0xae, 0x57, 0x0d, 0x8e, 0x71, 0x9a, 0xf2})),
+ std::make_pair(
+ 57,
+ std::vector<uint8_t>(
+ {0x30, 0x18, 0x60, 0x55, 0xc0, 0x79, 0x49, 0x94, 0x81, 0x83, 0xc8,
+ 0x50, 0xe9, 0xa7, 0x56, 0xcc, 0x09, 0x93, 0x7e, 0x24, 0x7d, 0x9d,
+ 0x92, 0x8e, 0x86, 0x9e, 0x20, 0xba, 0xfc, 0x3c, 0xd9, 0x72, 0x17,
+ 0x19, 0xd3, 0x4e, 0x04, 0xa0, 0x89, 0x9b, 0x92, 0xc7, 0x36, 0x08,
+ 0x45, 0x50, 0x18, 0x68, 0x86, 0xef, 0xba, 0x2e, 0x79, 0x0d, 0x8b,
+ 0xe6, 0xeb, 0xf0, 0x40, 0xb2, 0x09, 0xc4, 0x39, 0xa4})),
+ std::make_pair(
+ 58,
+ std::vector<uint8_t>(
+ {0xf3, 0xc4, 0x27, 0x6c, 0xb8, 0x63, 0x63, 0x77, 0x12, 0xc2, 0x41,
+ 0xc4, 0x44, 0xc5, 0xcc, 0x1e, 0x35, 0x54, 0xe0, 0xfd, 0xdb, 0x17,
+ 0x4d, 0x03, 0x58, 0x19, 0xdd, 0x83, 0xeb, 0x70, 0x0b, 0x4c, 0xe8,
+ 0x8d, 0xf3, 0xab, 0x38, 0x41, 0xba, 0x02, 0x08, 0x5e, 0x1a, 0x99,
+ 0xb4, 0xe1, 0x73, 0x10, 0xc5, 0x34, 0x10, 0x75, 0xc0, 0x45, 0x8b,
+ 0xa3, 0x76, 0xc9, 0x5a, 0x68, 0x18, 0xfb, 0xb3, 0xe2})),
+ std::make_pair(
+ 59,
+ std::vector<uint8_t>(
+ {0x0a, 0xa0, 0x07, 0xc4, 0xdd, 0x9d, 0x58, 0x32, 0x39, 0x30, 0x40,
+ 0xa1, 0x58, 0x3c, 0x93, 0x0b, 0xca, 0x7d, 0xc5, 0xe7, 0x7e, 0xa5,
+ 0x3a, 0xdd, 0x7e, 0x2b, 0x3f, 0x7c, 0x8e, 0x23, 0x13, 0x68, 0x04,
+ 0x35, 0x20, 0xd4, 0xa3, 0xef, 0x53, 0xc9, 0x69, 0xb6, 0xbb, 0xfd,
+ 0x02, 0x59, 0x46, 0xf6, 0x32, 0xbd, 0x7f, 0x76, 0x5d, 0x53, 0xc2,
+ 0x10, 0x03, 0xb8, 0xf9, 0x83, 0xf7, 0x5e, 0x2a, 0x6a})),
+ std::make_pair(
+ 60,
+ std::vector<uint8_t>(
+ {0x08, 0xe9, 0x46, 0x47, 0x20, 0x53, 0x3b, 0x23, 0xa0, 0x4e, 0xc2,
+ 0x4f, 0x7a, 0xe8, 0xc1, 0x03, 0x14, 0x5f, 0x76, 0x53, 0x87, 0xd7,
+ 0x38, 0x77, 0x7d, 0x3d, 0x34, 0x34, 0x77, 0xfd, 0x1c, 0x58, 0xdb,
+ 0x05, 0x21, 0x42, 0xca, 0xb7, 0x54, 0xea, 0x67, 0x43, 0x78, 0xe1,
+ 0x87, 0x66, 0xc5, 0x35, 0x42, 0xf7, 0x19, 0x70, 0x17, 0x1c, 0xc4,
+ 0xf8, 0x16, 0x94, 0x24, 0x6b, 0x71, 0x7d, 0x75, 0x64})),
+ std::make_pair(
+ 61,
+ std::vector<uint8_t>(
+ {0xd3, 0x7f, 0xf7, 0xad, 0x29, 0x79, 0x93, 0xe7, 0xec, 0x21, 0xe0,
+ 0xf1, 0xb4, 0xb5, 0xae, 0x71, 0x9c, 0xdc, 0x83, 0xc5, 0xdb, 0x68,
+ 0x75, 0x27, 0xf2, 0x75, 0x16, 0xcb, 0xff, 0xa8, 0x22, 0x88, 0x8a,
+ 0x68, 0x10, 0xee, 0x5c, 0x1c, 0xa7, 0xbf, 0xe3, 0x32, 0x11, 0x19,
+ 0xbe, 0x1a, 0xb7, 0xbf, 0xa0, 0xa5, 0x02, 0x67, 0x1c, 0x83, 0x29,
+ 0x49, 0x4d, 0xf7, 0xad, 0x6f, 0x52, 0x2d, 0x44, 0x0f})),
+ std::make_pair(
+ 62,
+ std::vector<uint8_t>(
+ {0xdd, 0x90, 0x42, 0xf6, 0xe4, 0x64, 0xdc, 0xf8, 0x6b, 0x12, 0x62,
+ 0xf6, 0xac, 0xcf, 0xaf, 0xbd, 0x8c, 0xfd, 0x90, 0x2e, 0xd3, 0xed,
+ 0x89, 0xab, 0xf7, 0x8f, 0xfa, 0x48, 0x2d, 0xbd, 0xee, 0xb6, 0x96,
+ 0x98, 0x42, 0x39, 0x4c, 0x9a, 0x11, 0x68, 0xae, 0x3d, 0x48, 0x1a,
+ 0x01, 0x78, 0x42, 0xf6, 0x60, 0x00, 0x2d, 0x42, 0x44, 0x7c, 0x6b,
+ 0x22, 0xf7, 0xb7, 0x2f, 0x21, 0xaa, 0xe0, 0x21, 0xc9})),
+ std::make_pair(
+ 63,
+ std::vector<uint8_t>(
+ {0xbd, 0x96, 0x5b, 0xf3, 0x1e, 0x87, 0xd7, 0x03, 0x27, 0x53, 0x6f,
+ 0x2a, 0x34, 0x1c, 0xeb, 0xc4, 0x76, 0x8e, 0xca, 0x27, 0x5f, 0xa0,
+ 0x5e, 0xf9, 0x8f, 0x7f, 0x1b, 0x71, 0xa0, 0x35, 0x12, 0x98, 0xde,
+ 0x00, 0x6f, 0xba, 0x73, 0xfe, 0x67, 0x33, 0xed, 0x01, 0xd7, 0x58,
+ 0x01, 0xb4, 0xa9, 0x28, 0xe5, 0x42, 0x31, 0xb3, 0x8e, 0x38, 0xc5,
+ 0x62, 0xb2, 0xe3, 0x3e, 0xa1, 0x28, 0x49, 0x92, 0xfa})),
+ std::make_pair(
+ 64,
+ std::vector<uint8_t>(
+ {0x65, 0x67, 0x6d, 0x80, 0x06, 0x17, 0x97, 0x2f, 0xbd, 0x87, 0xe4,
+ 0xb9, 0x51, 0x4e, 0x1c, 0x67, 0x40, 0x2b, 0x7a, 0x33, 0x10, 0x96,
+ 0xd3, 0xbf, 0xac, 0x22, 0xf1, 0xab, 0xb9, 0x53, 0x74, 0xab, 0xc9,
+ 0x42, 0xf1, 0x6e, 0x9a, 0xb0, 0xea, 0xd3, 0x3b, 0x87, 0xc9, 0x19,
+ 0x68, 0xa6, 0xe5, 0x09, 0xe1, 0x19, 0xff, 0x07, 0x78, 0x7b, 0x3e,
+ 0xf4, 0x83, 0xe1, 0xdc, 0xdc, 0xcf, 0x6e, 0x30, 0x22})),
+ std::make_pair(
+ 65,
+ std::vector<uint8_t>(
+ {0x93, 0x9f, 0xa1, 0x89, 0x69, 0x9c, 0x5d, 0x2c, 0x81, 0xdd, 0xd1,
+ 0xff, 0xc1, 0xfa, 0x20, 0x7c, 0x97, 0x0b, 0x6a, 0x36, 0x85, 0xbb,
+ 0x29, 0xce, 0x1d, 0x3e, 0x99, 0xd4, 0x2f, 0x2f, 0x74, 0x42, 0xda,
+ 0x53, 0xe9, 0x5a, 0x72, 0x90, 0x73, 0x14, 0xf4, 0x58, 0x83, 0x99,
+ 0xa3, 0xff, 0x5b, 0x0a, 0x92, 0xbe, 0xb3, 0xf6, 0xbe, 0x26, 0x94,
+ 0xf9, 0xf8, 0x6e, 0xcf, 0x29, 0x52, 0xd5, 0xb4, 0x1c})),
+ std::make_pair(
+ 66,
+ std::vector<uint8_t>(
+ {0xc5, 0x16, 0x54, 0x17, 0x01, 0x86, 0x3f, 0x91, 0x00, 0x5f, 0x31,
+ 0x41, 0x08, 0xce, 0xec, 0xe3, 0xc6, 0x43, 0xe0, 0x4f, 0xc8, 0xc4,
+ 0x2f, 0xd2, 0xff, 0x55, 0x62, 0x20, 0xe6, 0x16, 0xaa, 0xa6, 0xa4,
+ 0x8a, 0xeb, 0x97, 0xa8, 0x4b, 0xad, 0x74, 0x78, 0x2e, 0x8d, 0xff,
+ 0x96, 0xa1, 0xa2, 0xfa, 0x94, 0x93, 0x39, 0xd7, 0x22, 0xed, 0xca,
+ 0xa3, 0x2b, 0x57, 0x06, 0x70, 0x41, 0xdf, 0x88, 0xcc})),
+ std::make_pair(
+ 67,
+ std::vector<uint8_t>(
+ {0x98, 0x7f, 0xd6, 0xe0, 0xd6, 0x85, 0x7c, 0x55, 0x3e, 0xae, 0xbb,
+ 0x3d, 0x34, 0x97, 0x0a, 0x2c, 0x2f, 0x6e, 0x89, 0xa3, 0x54, 0x8f,
+ 0x49, 0x25, 0x21, 0x72, 0x2b, 0x80, 0xa1, 0xc2, 0x1a, 0x15, 0x38,
+ 0x92, 0x34, 0x6d, 0x2c, 0xba, 0x64, 0x44, 0x21, 0x2d, 0x56, 0xda,
+ 0x9a, 0x26, 0xe3, 0x24, 0xdc, 0xcb, 0xc0, 0xdc, 0xde, 0x85, 0xd4,
+ 0xd2, 0xee, 0x43, 0x99, 0xee, 0xc5, 0xa6, 0x4e, 0x8f})),
+ std::make_pair(
+ 68,
+ std::vector<uint8_t>(
+ {0xae, 0x56, 0xde, 0xb1, 0xc2, 0x32, 0x8d, 0x9c, 0x40, 0x17, 0x70,
+ 0x6b, 0xce, 0x6e, 0x99, 0xd4, 0x13, 0x49, 0x05, 0x3b, 0xa9, 0xd3,
+ 0x36, 0xd6, 0x77, 0xc4, 0xc2, 0x7d, 0x9f, 0xd5, 0x0a, 0xe6, 0xae,
+ 0xe1, 0x7e, 0x85, 0x31, 0x54, 0xe1, 0xf4, 0xfe, 0x76, 0x72, 0x34,
+ 0x6d, 0xa2, 0xea, 0xa3, 0x1e, 0xea, 0x53, 0xfc, 0xf2, 0x4a, 0x22,
+ 0x80, 0x4f, 0x11, 0xd0, 0x3d, 0xa6, 0xab, 0xfc, 0x2b})),
+ std::make_pair(
+ 69,
+ std::vector<uint8_t>(
+ {0x49, 0xd6, 0xa6, 0x08, 0xc9, 0xbd, 0xe4, 0x49, 0x18, 0x70, 0x49,
+ 0x85, 0x72, 0xac, 0x31, 0xaa, 0xc3, 0xfa, 0x40, 0x93, 0x8b, 0x38,
+ 0xa7, 0x81, 0x8f, 0x72, 0x38, 0x3e, 0xb0, 0x40, 0xad, 0x39, 0x53,
+ 0x2b, 0xc0, 0x65, 0x71, 0xe1, 0x3d, 0x76, 0x7e, 0x69, 0x45, 0xab,
+ 0x77, 0xc0, 0xbd, 0xc3, 0xb0, 0x28, 0x42, 0x53, 0x34, 0x3f, 0x9f,
+ 0x6c, 0x12, 0x44, 0xeb, 0xf2, 0xff, 0x0d, 0xf8, 0x66})),
+ std::make_pair(
+ 70,
+ std::vector<uint8_t>(
+ {0xda, 0x58, 0x2a, 0xd8, 0xc5, 0x37, 0x0b, 0x44, 0x69, 0xaf, 0x86,
+ 0x2a, 0xa6, 0x46, 0x7a, 0x22, 0x93, 0xb2, 0xb2, 0x8b, 0xd8, 0x0a,
+ 0xe0, 0xe9, 0x1f, 0x42, 0x5a, 0xd3, 0xd4, 0x72, 0x49, 0xfd, 0xf9,
+ 0x88, 0x25, 0xcc, 0x86, 0xf1, 0x40, 0x28, 0xc3, 0x30, 0x8c, 0x98,
+ 0x04, 0xc7, 0x8b, 0xfe, 0xee, 0xee, 0x46, 0x14, 0x44, 0xce, 0x24,
+ 0x36, 0x87, 0xe1, 0xa5, 0x05, 0x22, 0x45, 0x6a, 0x1d})),
+ std::make_pair(
+ 71,
+ std::vector<uint8_t>(
+ {0xd5, 0x26, 0x6a, 0xa3, 0x33, 0x11, 0x94, 0xae, 0xf8, 0x52, 0xee,
+ 0xd8, 0x6d, 0x7b, 0x5b, 0x26, 0x33, 0xa0, 0xaf, 0x1c, 0x73, 0x59,
+ 0x06, 0xf2, 0xe1, 0x32, 0x79, 0xf1, 0x49, 0x31, 0xa9, 0xfc, 0x3b,
+ 0x0e, 0xac, 0x5c, 0xe9, 0x24, 0x52, 0x73, 0xbd, 0x1a, 0xa9, 0x29,
+ 0x05, 0xab, 0xe1, 0x62, 0x78, 0xef, 0x7e, 0xfd, 0x47, 0x69, 0x47,
+ 0x89, 0xa7, 0x28, 0x3b, 0x77, 0xda, 0x3c, 0x70, 0xf8})),
+ std::make_pair(
+ 72,
+ std::vector<uint8_t>(
+ {0x29, 0x62, 0x73, 0x4c, 0x28, 0x25, 0x21, 0x86, 0xa9, 0xa1, 0x11,
+ 0x1c, 0x73, 0x2a, 0xd4, 0xde, 0x45, 0x06, 0xd4, 0xb4, 0x48, 0x09,
+ 0x16, 0x30, 0x3e, 0xb7, 0x99, 0x1d, 0x65, 0x9c, 0xcd, 0xa0, 0x7a,
+ 0x99, 0x11, 0x91, 0x4b, 0xc7, 0x5c, 0x41, 0x8a, 0xb7, 0xa4, 0x54,
+ 0x17, 0x57, 0xad, 0x05, 0x47, 0x96, 0xe2, 0x67, 0x97, 0xfe, 0xaf,
+ 0x36, 0xe9, 0xf6, 0xad, 0x43, 0xf1, 0x4b, 0x35, 0xa4})),
+ std::make_pair(
+ 73,
+ std::vector<uint8_t>(
+ {0xe8, 0xb7, 0x9e, 0xc5, 0xd0, 0x6e, 0x11, 0x1b, 0xdf, 0xaf, 0xd7,
+ 0x1e, 0x9f, 0x57, 0x60, 0xf0, 0x0a, 0xc8, 0xac, 0x5d, 0x8b, 0xf7,
+ 0x68, 0xf9, 0xff, 0x6f, 0x08, 0xb8, 0xf0, 0x26, 0x09, 0x6b, 0x1c,
+ 0xc3, 0xa4, 0xc9, 0x73, 0x33, 0x30, 0x19, 0xf1, 0xe3, 0x55, 0x3e,
+ 0x77, 0xda, 0x3f, 0x98, 0xcb, 0x9f, 0x54, 0x2e, 0x0a, 0x90, 0xe5,
+ 0xf8, 0xa9, 0x40, 0xcc, 0x58, 0xe5, 0x98, 0x44, 0xb3})),
+ std::make_pair(
+ 74,
+ std::vector<uint8_t>(
+ {0xdf, 0xb3, 0x20, 0xc4, 0x4f, 0x9d, 0x41, 0xd1, 0xef, 0xdc, 0xc0,
+ 0x15, 0xf0, 0x8d, 0xd5, 0x53, 0x9e, 0x52, 0x6e, 0x39, 0xc8, 0x7d,
+ 0x50, 0x9a, 0xe6, 0x81, 0x2a, 0x96, 0x9e, 0x54, 0x31, 0xbf, 0x4f,
+ 0xa7, 0xd9, 0x1f, 0xfd, 0x03, 0xb9, 0x81, 0xe0, 0xd5, 0x44, 0xcf,
+ 0x72, 0xd7, 0xb1, 0xc0, 0x37, 0x4f, 0x88, 0x01, 0x48, 0x2e, 0x6d,
+ 0xea, 0x2e, 0xf9, 0x03, 0x87, 0x7e, 0xba, 0x67, 0x5e})),
+ std::make_pair(
+ 75,
+ std::vector<uint8_t>(
+ {0xd8, 0x86, 0x75, 0x11, 0x8f, 0xdb, 0x55, 0xa5, 0xfb, 0x36, 0x5a,
+ 0xc2, 0xaf, 0x1d, 0x21, 0x7b, 0xf5, 0x26, 0xce, 0x1e, 0xe9, 0xc9,
+ 0x4b, 0x2f, 0x00, 0x90, 0xb2, 0xc5, 0x8a, 0x06, 0xca, 0x58, 0x18,
+ 0x7d, 0x7f, 0xe5, 0x7c, 0x7b, 0xed, 0x9d, 0x26, 0xfc, 0xa0, 0x67,
+ 0xb4, 0x11, 0x0e, 0xef, 0xcd, 0x9a, 0x0a, 0x34, 0x5d, 0xe8, 0x72,
+ 0xab, 0xe2, 0x0d, 0xe3, 0x68, 0x00, 0x1b, 0x07, 0x45})),
+ std::make_pair(
+ 76,
+ std::vector<uint8_t>(
+ {0xb8, 0x93, 0xf2, 0xfc, 0x41, 0xf7, 0xb0, 0xdd, 0x6e, 0x2f, 0x6a,
+ 0xa2, 0xe0, 0x37, 0x0c, 0x0c, 0xff, 0x7d, 0xf0, 0x9e, 0x3a, 0xcf,
+ 0xcc, 0x0e, 0x92, 0x0b, 0x6e, 0x6f, 0xad, 0x0e, 0xf7, 0x47, 0xc4,
+ 0x06, 0x68, 0x41, 0x7d, 0x34, 0x2b, 0x80, 0xd2, 0x35, 0x1e, 0x8c,
+ 0x17, 0x5f, 0x20, 0x89, 0x7a, 0x06, 0x2e, 0x97, 0x65, 0xe6, 0xc6,
+ 0x7b, 0x53, 0x9b, 0x6b, 0xa8, 0xb9, 0x17, 0x05, 0x45})),
+ std::make_pair(
+ 77,
+ std::vector<uint8_t>(
+ {0x6c, 0x67, 0xec, 0x56, 0x97, 0xac, 0xcd, 0x23, 0x5c, 0x59, 0xb4,
+ 0x86, 0xd7, 0xb7, 0x0b, 0xae, 0xed, 0xcb, 0xd4, 0xaa, 0x64, 0xeb,
+ 0xd4, 0xee, 0xf3, 0xc7, 0xea, 0xc1, 0x89, 0x56, 0x1a, 0x72, 0x62,
+ 0x50, 0xae, 0xc4, 0xd4, 0x8c, 0xad, 0xca, 0xfb, 0xbe, 0x2c, 0xe3,
+ 0xc1, 0x6c, 0xe2, 0xd6, 0x91, 0xa8, 0xcc, 0xe0, 0x6e, 0x88, 0x79,
+ 0x55, 0x6d, 0x44, 0x83, 0xed, 0x71, 0x65, 0xc0, 0x63})),
+ std::make_pair(
+ 78,
+ std::vector<uint8_t>(
+ {0xf1, 0xaa, 0x2b, 0x04, 0x4f, 0x8f, 0x0c, 0x63, 0x8a, 0x3f, 0x36,
+ 0x2e, 0x67, 0x7b, 0x5d, 0x89, 0x1d, 0x6f, 0xd2, 0xab, 0x07, 0x65,
+ 0xf6, 0xee, 0x1e, 0x49, 0x87, 0xde, 0x05, 0x7e, 0xad, 0x35, 0x78,
+ 0x83, 0xd9, 0xb4, 0x05, 0xb9, 0xd6, 0x09, 0xee, 0xa1, 0xb8, 0x69,
+ 0xd9, 0x7f, 0xb1, 0x6d, 0x9b, 0x51, 0x01, 0x7c, 0x55, 0x3f, 0x3b,
+ 0x93, 0xc0, 0xa1, 0xe0, 0xf1, 0x29, 0x6f, 0xed, 0xcd})),
+ std::make_pair(
+ 79,
+ std::vector<uint8_t>(
+ {0xcb, 0xaa, 0x25, 0x95, 0x72, 0xd4, 0xae, 0xbf, 0xc1, 0x91, 0x7a,
+ 0xcd, 0xdc, 0x58, 0x2b, 0x9f, 0x8d, 0xfa, 0xa9, 0x28, 0xa1, 0x98,
+ 0xca, 0x7a, 0xcd, 0x0f, 0x2a, 0xa7, 0x6a, 0x13, 0x4a, 0x90, 0x25,
+ 0x2e, 0x62, 0x98, 0xa6, 0x5b, 0x08, 0x18, 0x6a, 0x35, 0x0d, 0x5b,
+ 0x76, 0x26, 0x69, 0x9f, 0x8c, 0xb7, 0x21, 0xa3, 0xea, 0x59, 0x21,
+ 0xb7, 0x53, 0xae, 0x3a, 0x2d, 0xce, 0x24, 0xba, 0x3a})),
+ std::make_pair(
+ 80,
+ std::vector<uint8_t>(
+ {0xfa, 0x15, 0x49, 0xc9, 0x79, 0x6c, 0xd4, 0xd3, 0x03, 0xdc, 0xf4,
+ 0x52, 0xc1, 0xfb, 0xd5, 0x74, 0x4f, 0xd9, 0xb9, 0xb4, 0x70, 0x03,
+ 0xd9, 0x20, 0xb9, 0x2d, 0xe3, 0x48, 0x39, 0xd0, 0x7e, 0xf2, 0xa2,
+ 0x9d, 0xed, 0x68, 0xf6, 0xfc, 0x9e, 0x6c, 0x45, 0xe0, 0x71, 0xa2,
+ 0xe4, 0x8b, 0xd5, 0x0c, 0x50, 0x84, 0xe9, 0x6b, 0x65, 0x7d, 0xd0,
+ 0x40, 0x40, 0x45, 0xa1, 0xdd, 0xef, 0xe2, 0x82, 0xed})),
+ std::make_pair(
+ 81,
+ std::vector<uint8_t>(
+ {0x5c, 0xf2, 0xac, 0x89, 0x7a, 0xb4, 0x44, 0xdc, 0xb5, 0xc8, 0xd8,
+ 0x7c, 0x49, 0x5d, 0xbd, 0xb3, 0x4e, 0x18, 0x38, 0xb6, 0xb6, 0x29,
+ 0x42, 0x7c, 0xaa, 0x51, 0x70, 0x2a, 0xd0, 0xf9, 0x68, 0x85, 0x25,
+ 0xf1, 0x3b, 0xec, 0x50, 0x3a, 0x3c, 0x3a, 0x2c, 0x80, 0xa6, 0x5e,
+ 0x0b, 0x57, 0x15, 0xe8, 0xaf, 0xab, 0x00, 0xff, 0xa5, 0x6e, 0xc4,
+ 0x55, 0xa4, 0x9a, 0x1a, 0xd3, 0x0a, 0xa2, 0x4f, 0xcd})),
+ std::make_pair(
+ 82,
+ std::vector<uint8_t>(
+ {0x9a, 0xaf, 0x80, 0x20, 0x7b, 0xac, 0xe1, 0x7b, 0xb7, 0xab, 0x14,
+ 0x57, 0x57, 0xd5, 0x69, 0x6b, 0xde, 0x32, 0x40, 0x6e, 0xf2, 0x2b,
+ 0x44, 0x29, 0x2e, 0xf6, 0x5d, 0x45, 0x19, 0xc3, 0xbb, 0x2a, 0xd4,
+ 0x1a, 0x59, 0xb6, 0x2c, 0xc3, 0xe9, 0x4b, 0x6f, 0xa9, 0x6d, 0x32,
+ 0xa7, 0xfa, 0xad, 0xae, 0x28, 0xaf, 0x7d, 0x35, 0x09, 0x72, 0x19,
+ 0xaa, 0x3f, 0xd8, 0xcd, 0xa3, 0x1e, 0x40, 0xc2, 0x75})),
+ std::make_pair(
+ 83,
+ std::vector<uint8_t>(
+ {0xaf, 0x88, 0xb1, 0x63, 0x40, 0x2c, 0x86, 0x74, 0x5c, 0xb6, 0x50,
+ 0xc2, 0x98, 0x8f, 0xb9, 0x52, 0x11, 0xb9, 0x4b, 0x03, 0xef, 0x29,
+ 0x0e, 0xed, 0x96, 0x62, 0x03, 0x42, 0x41, 0xfd, 0x51, 0xcf, 0x39,
+ 0x8f, 0x80, 0x73, 0xe3, 0x69, 0x35, 0x4c, 0x43, 0xea, 0xe1, 0x05,
+ 0x2f, 0x9b, 0x63, 0xb0, 0x81, 0x91, 0xca, 0xa1, 0x38, 0xaa, 0x54,
+ 0xfe, 0xa8, 0x89, 0xcc, 0x70, 0x24, 0x23, 0x68, 0x97})),
+ std::make_pair(
+ 84,
+ std::vector<uint8_t>(
+ {0x48, 0xfa, 0x7d, 0x64, 0xe1, 0xce, 0xee, 0x27, 0xb9, 0x86, 0x4d,
+ 0xb5, 0xad, 0xa4, 0xb5, 0x3d, 0x00, 0xc9, 0xbc, 0x76, 0x26, 0x55,
+ 0x58, 0x13, 0xd3, 0xcd, 0x67, 0x30, 0xab, 0x3c, 0xc0, 0x6f, 0xf3,
+ 0x42, 0xd7, 0x27, 0x90, 0x5e, 0x33, 0x17, 0x1b, 0xde, 0x6e, 0x84,
+ 0x76, 0xe7, 0x7f, 0xb1, 0x72, 0x08, 0x61, 0xe9, 0x4b, 0x73, 0xa2,
+ 0xc5, 0x38, 0xd2, 0x54, 0x74, 0x62, 0x85, 0xf4, 0x30})),
+ std::make_pair(
+ 85,
+ std::vector<uint8_t>(
+ {0x0e, 0x6f, 0xd9, 0x7a, 0x85, 0xe9, 0x04, 0xf8, 0x7b, 0xfe, 0x85,
+ 0xbb, 0xeb, 0x34, 0xf6, 0x9e, 0x1f, 0x18, 0x10, 0x5c, 0xf4, 0xed,
+ 0x4f, 0x87, 0xae, 0xc3, 0x6c, 0x6e, 0x8b, 0x5f, 0x68, 0xbd, 0x2a,
+ 0x6f, 0x3d, 0xc8, 0xa9, 0xec, 0xb2, 0xb6, 0x1d, 0xb4, 0xee, 0xdb,
+ 0x6b, 0x2e, 0xa1, 0x0b, 0xf9, 0xcb, 0x02, 0x51, 0xfb, 0x0f, 0x8b,
+ 0x34, 0x4a, 0xbf, 0x7f, 0x36, 0x6b, 0x6d, 0xe5, 0xab})),
+ std::make_pair(
+ 86,
+ std::vector<uint8_t>(
+ {0x06, 0x62, 0x2d, 0xa5, 0x78, 0x71, 0x76, 0x28, 0x7f, 0xdc, 0x8f,
+ 0xed, 0x44, 0x0b, 0xad, 0x18, 0x7d, 0x83, 0x00, 0x99, 0xc9, 0x4e,
+ 0x6d, 0x04, 0xc8, 0xe9, 0xc9, 0x54, 0xcd, 0xa7, 0x0c, 0x8b, 0xb9,
+ 0xe1, 0xfc, 0x4a, 0x6d, 0x0b, 0xaa, 0x83, 0x1b, 0x9b, 0x78, 0xef,
+ 0x66, 0x48, 0x68, 0x1a, 0x48, 0x67, 0xa1, 0x1d, 0xa9, 0x3e, 0xe3,
+ 0x6e, 0x5e, 0x6a, 0x37, 0xd8, 0x7f, 0xc6, 0x3f, 0x6f})),
+ std::make_pair(
+ 87,
+ std::vector<uint8_t>(
+ {0x1d, 0xa6, 0x77, 0x2b, 0x58, 0xfa, 0xbf, 0x9c, 0x61, 0xf6, 0x8d,
+ 0x41, 0x2c, 0x82, 0xf1, 0x82, 0xc0, 0x23, 0x6d, 0x7d, 0x57, 0x5e,
+ 0xf0, 0xb5, 0x8d, 0xd2, 0x24, 0x58, 0xd6, 0x43, 0xcd, 0x1d, 0xfc,
+ 0x93, 0xb0, 0x38, 0x71, 0xc3, 0x16, 0xd8, 0x43, 0x0d, 0x31, 0x29,
+ 0x95, 0xd4, 0x19, 0x7f, 0x08, 0x74, 0xc9, 0x91, 0x72, 0xba, 0x00,
+ 0x4a, 0x01, 0xee, 0x29, 0x5a, 0xba, 0xc2, 0x4e, 0x46})),
+ std::make_pair(
+ 88,
+ std::vector<uint8_t>(
+ {0x3c, 0xd2, 0xd9, 0x32, 0x0b, 0x7b, 0x1d, 0x5f, 0xb9, 0xaa, 0xb9,
+ 0x51, 0xa7, 0x60, 0x23, 0xfa, 0x66, 0x7b, 0xe1, 0x4a, 0x91, 0x24,
+ 0xe3, 0x94, 0x51, 0x39, 0x18, 0xa3, 0xf4, 0x40, 0x96, 0xae, 0x49,
+ 0x04, 0xba, 0x0f, 0xfc, 0x15, 0x0b, 0x63, 0xbc, 0x7a, 0xb1, 0xee,
+ 0xb9, 0xa6, 0xe2, 0x57, 0xe5, 0xc8, 0xf0, 0x00, 0xa7, 0x03, 0x94,
+ 0xa5, 0xaf, 0xd8, 0x42, 0x71, 0x5d, 0xe1, 0x5f, 0x29})),
+ std::make_pair(
+ 89,
+ std::vector<uint8_t>(
+ {0x04, 0xcd, 0xc1, 0x4f, 0x74, 0x34, 0xe0, 0xb4, 0xbe, 0x70, 0xcb,
+ 0x41, 0xdb, 0x4c, 0x77, 0x9a, 0x88, 0xea, 0xef, 0x6a, 0xcc, 0xeb,
+ 0xcb, 0x41, 0xf2, 0xd4, 0x2f, 0xff, 0xe7, 0xf3, 0x2a, 0x8e, 0x28,
+ 0x1b, 0x5c, 0x10, 0x3a, 0x27, 0x02, 0x1d, 0x0d, 0x08, 0x36, 0x22,
+ 0x50, 0x75, 0x3c, 0xdf, 0x70, 0x29, 0x21, 0x95, 0xa5, 0x3a, 0x48,
+ 0x72, 0x8c, 0xeb, 0x58, 0x44, 0xc2, 0xd9, 0x8b, 0xab})),
+ std::make_pair(
+ 90,
+ std::vector<uint8_t>(
+ {0x90, 0x71, 0xb7, 0xa8, 0xa0, 0x75, 0xd0, 0x09, 0x5b, 0x8f, 0xb3,
+ 0xae, 0x51, 0x13, 0x78, 0x57, 0x35, 0xab, 0x98, 0xe2, 0xb5, 0x2f,
+ 0xaf, 0x91, 0xd5, 0xb8, 0x9e, 0x44, 0xaa, 0xc5, 0xb5, 0xd4, 0xeb,
+ 0xbf, 0x91, 0x22, 0x3b, 0x0f, 0xf4, 0xc7, 0x19, 0x05, 0xda, 0x55,
+ 0x34, 0x2e, 0x64, 0x65, 0x5d, 0x6e, 0xf8, 0xc8, 0x9a, 0x47, 0x68,
+ 0xc3, 0xf9, 0x3a, 0x6d, 0xc0, 0x36, 0x6b, 0x5b, 0xc8})),
+ std::make_pair(
+ 91,
+ std::vector<uint8_t>(
+ {0xeb, 0xb3, 0x02, 0x40, 0xdd, 0x96, 0xc7, 0xbc, 0x8d, 0x0a, 0xbe,
+ 0x49, 0xaa, 0x4e, 0xdc, 0xbb, 0x4a, 0xfd, 0xc5, 0x1f, 0xf9, 0xaa,
+ 0xf7, 0x20, 0xd3, 0xf9, 0xe7, 0xfb, 0xb0, 0xf9, 0xc6, 0xd6, 0x57,
+ 0x13, 0x50, 0x50, 0x17, 0x69, 0xfc, 0x4e, 0xbd, 0x0b, 0x21, 0x41,
+ 0x24, 0x7f, 0xf4, 0x00, 0xd4, 0xfd, 0x4b, 0xe4, 0x14, 0xed, 0xf3,
+ 0x77, 0x57, 0xbb, 0x90, 0xa3, 0x2a, 0xc5, 0xc6, 0x5a})),
+ std::make_pair(
+ 92,
+ std::vector<uint8_t>(
+ {0x85, 0x32, 0xc5, 0x8b, 0xf3, 0xc8, 0x01, 0x5d, 0x9d, 0x1c, 0xbe,
+ 0x00, 0xee, 0xf1, 0xf5, 0x08, 0x2f, 0x8f, 0x36, 0x32, 0xfb, 0xe9,
+ 0xf1, 0xed, 0x4f, 0x9d, 0xfb, 0x1f, 0xa7, 0x9e, 0x82, 0x83, 0x06,
+ 0x6d, 0x77, 0xc4, 0x4c, 0x4a, 0xf9, 0x43, 0xd7, 0x6b, 0x30, 0x03,
+ 0x64, 0xae, 0xcb, 0xd0, 0x64, 0x8c, 0x8a, 0x89, 0x39, 0xbd, 0x20,
+ 0x41, 0x23, 0xf4, 0xb5, 0x62, 0x60, 0x42, 0x2d, 0xec})),
+ std::make_pair(
+ 93,
+ std::vector<uint8_t>(
+ {0xfe, 0x98, 0x46, 0xd6, 0x4f, 0x7c, 0x77, 0x08, 0x69, 0x6f, 0x84,
+ 0x0e, 0x2d, 0x76, 0xcb, 0x44, 0x08, 0xb6, 0x59, 0x5c, 0x2f, 0x81,
+ 0xec, 0x6a, 0x28, 0xa7, 0xf2, 0xf2, 0x0c, 0xb8, 0x8c, 0xfe, 0x6a,
+ 0xc0, 0xb9, 0xe9, 0xb8, 0x24, 0x4f, 0x08, 0xbd, 0x70, 0x95, 0xc3,
+ 0x50, 0xc1, 0xd0, 0x84, 0x2f, 0x64, 0xfb, 0x01, 0xbb, 0x7f, 0x53,
+ 0x2d, 0xfc, 0xd4, 0x73, 0x71, 0xb0, 0xae, 0xeb, 0x79})),
+ std::make_pair(
+ 94,
+ std::vector<uint8_t>(
+ {0x28, 0xf1, 0x7e, 0xa6, 0xfb, 0x6c, 0x42, 0x09, 0x2d, 0xc2, 0x64,
+ 0x25, 0x7e, 0x29, 0x74, 0x63, 0x21, 0xfb, 0x5b, 0xda, 0xea, 0x98,
+ 0x73, 0xc2, 0xa7, 0xfa, 0x9d, 0x8f, 0x53, 0x81, 0x8e, 0x89, 0x9e,
+ 0x16, 0x1b, 0xc7, 0x7d, 0xfe, 0x80, 0x90, 0xaf, 0xd8, 0x2b, 0xf2,
+ 0x26, 0x6c, 0x5c, 0x1b, 0xc9, 0x30, 0xa8, 0xd1, 0x54, 0x76, 0x24,
+ 0x43, 0x9e, 0x66, 0x2e, 0xf6, 0x95, 0xf2, 0x6f, 0x24})),
+ std::make_pair(
+ 95,
+ std::vector<uint8_t>(
+ {0xec, 0x6b, 0x7d, 0x7f, 0x03, 0x0d, 0x48, 0x50, 0xac, 0xae, 0x3c,
+ 0xb6, 0x15, 0xc2, 0x1d, 0xd2, 0x52, 0x06, 0xd6, 0x3e, 0x84, 0xd1,
+ 0xdb, 0x8d, 0x95, 0x73, 0x70, 0x73, 0x7b, 0xa0, 0xe9, 0x84, 0x67,
+ 0xea, 0x0c, 0xe2, 0x74, 0xc6, 0x61, 0x99, 0x90, 0x1e, 0xae, 0xc1,
+ 0x8a, 0x08, 0x52, 0x57, 0x15, 0xf5, 0x3b, 0xfd, 0xb0, 0xaa, 0xcb,
+ 0x61, 0x3d, 0x34, 0x2e, 0xbd, 0xce, 0xed, 0xdc, 0x3b})),
+ std::make_pair(
+ 96,
+ std::vector<uint8_t>(
+ {0xb4, 0x03, 0xd3, 0x69, 0x1c, 0x03, 0xb0, 0xd3, 0x41, 0x8d, 0xf3,
+ 0x27, 0xd5, 0x86, 0x0d, 0x34, 0xbb, 0xfc, 0xc4, 0x51, 0x9b, 0xfb,
+ 0xce, 0x36, 0xbf, 0x33, 0xb2, 0x08, 0x38, 0x5f, 0xad, 0xb9, 0x18,
+ 0x6b, 0xc7, 0x8a, 0x76, 0xc4, 0x89, 0xd8, 0x9f, 0xd5, 0x7e, 0x7d,
+ 0xc7, 0x54, 0x12, 0xd2, 0x3b, 0xcd, 0x1d, 0xae, 0x84, 0x70, 0xce,
+ 0x92, 0x74, 0x75, 0x4b, 0xb8, 0x58, 0x5b, 0x13, 0xc5})),
+ std::make_pair(
+ 97,
+ std::vector<uint8_t>(
+ {0x31, 0xfc, 0x79, 0x73, 0x8b, 0x87, 0x72, 0xb3, 0xf5, 0x5c, 0xd8,
+ 0x17, 0x88, 0x13, 0xb3, 0xb5, 0x2d, 0x0d, 0xb5, 0xa4, 0x19, 0xd3,
+ 0x0b, 0xa9, 0x49, 0x5c, 0x4b, 0x9d, 0xa0, 0x21, 0x9f, 0xac, 0x6d,
+ 0xf8, 0xe7, 0xc2, 0x3a, 0x81, 0x15, 0x51, 0xa6, 0x2b, 0x82, 0x7f,
+ 0x25, 0x6e, 0xcd, 0xb8, 0x12, 0x4a, 0xc8, 0xa6, 0x79, 0x2c, 0xcf,
+ 0xec, 0xc3, 0xb3, 0x01, 0x27, 0x22, 0xe9, 0x44, 0x63})),
+ std::make_pair(
+ 98,
+ std::vector<uint8_t>(
+ {0xbb, 0x20, 0x39, 0xec, 0x28, 0x70, 0x91, 0xbc, 0xc9, 0x64, 0x2f,
+ 0xc9, 0x00, 0x49, 0xe7, 0x37, 0x32, 0xe0, 0x2e, 0x57, 0x7e, 0x28,
+ 0x62, 0xb3, 0x22, 0x16, 0xae, 0x9b, 0xed, 0xcd, 0x73, 0x0c, 0x4c,
+ 0x28, 0x4e, 0xf3, 0x96, 0x8c, 0x36, 0x8b, 0x7d, 0x37, 0x58, 0x4f,
+ 0x97, 0xbd, 0x4b, 0x4d, 0xc6, 0xef, 0x61, 0x27, 0xac, 0xfe, 0x2e,
+ 0x6a, 0xe2, 0x50, 0x91, 0x24, 0xe6, 0x6c, 0x8a, 0xf4})),
+ std::make_pair(
+ 99,
+ std::vector<uint8_t>(
+ {0xf5, 0x3d, 0x68, 0xd1, 0x3f, 0x45, 0xed, 0xfc, 0xb9, 0xbd, 0x41,
+ 0x5e, 0x28, 0x31, 0xe9, 0x38, 0x35, 0x0d, 0x53, 0x80, 0xd3, 0x43,
+ 0x22, 0x78, 0xfc, 0x1c, 0x0c, 0x38, 0x1f, 0xcb, 0x7c, 0x65, 0xc8,
+ 0x2d, 0xaf, 0xe0, 0x51, 0xd8, 0xc8, 0xb0, 0xd4, 0x4e, 0x09, 0x74,
+ 0xa0, 0xe5, 0x9e, 0xc7, 0xbf, 0x7e, 0xd0, 0x45, 0x9f, 0x86, 0xe9,
+ 0x6f, 0x32, 0x9f, 0xc7, 0x97, 0x52, 0x51, 0x0f, 0xd3})),
+ std::make_pair(
+ 100,
+ std::vector<uint8_t>(
+ {0x8d, 0x56, 0x8c, 0x79, 0x84, 0xf0, 0xec, 0xdf, 0x76, 0x40, 0xfb,
+ 0xc4, 0x83, 0xb5, 0xd8, 0xc9, 0xf8, 0x66, 0x34, 0xf6, 0xf4, 0x32,
+ 0x91, 0x84, 0x1b, 0x30, 0x9a, 0x35, 0x0a, 0xb9, 0xc1, 0x13, 0x7d,
+ 0x24, 0x06, 0x6b, 0x09, 0xda, 0x99, 0x44, 0xba, 0xc5, 0x4d, 0x5b,
+ 0xb6, 0x58, 0x0d, 0x83, 0x60, 0x47, 0xaa, 0xc7, 0x4a, 0xb7, 0x24,
+ 0xb8, 0x87, 0xeb, 0xf9, 0x3d, 0x4b, 0x32, 0xec, 0xa9})),
+ std::make_pair(
+ 101,
+ std::vector<uint8_t>(
+ {0xc0, 0xb6, 0x5c, 0xe5, 0xa9, 0x6f, 0xf7, 0x74, 0xc4, 0x56, 0xca,
+ 0xc3, 0xb5, 0xf2, 0xc4, 0xcd, 0x35, 0x9b, 0x4f, 0xf5, 0x3e, 0xf9,
+ 0x3a, 0x3d, 0xa0, 0x77, 0x8b, 0xe4, 0x90, 0x0d, 0x1e, 0x8d, 0xa1,
+ 0x60, 0x1e, 0x76, 0x9e, 0x8f, 0x1b, 0x02, 0xd2, 0xa2, 0xf8, 0xc5,
+ 0xb9, 0xfa, 0x10, 0xb4, 0x4f, 0x1c, 0x18, 0x69, 0x85, 0x46, 0x8f,
+ 0xee, 0xb0, 0x08, 0x73, 0x02, 0x83, 0xa6, 0x65, 0x7d})),
+ std::make_pair(
+ 102,
+ std::vector<uint8_t>(
+ {0x49, 0x00, 0xbb, 0xa6, 0xf5, 0xfb, 0x10, 0x3e, 0xce, 0x8e, 0xc9,
+ 0x6a, 0xda, 0x13, 0xa5, 0xc3, 0xc8, 0x54, 0x88, 0xe0, 0x55, 0x51,
+ 0xda, 0x6b, 0x6b, 0x33, 0xd9, 0x88, 0xe6, 0x11, 0xec, 0x0f, 0xe2,
+ 0xe3, 0xc2, 0xaa, 0x48, 0xea, 0x6a, 0xe8, 0x98, 0x6a, 0x3a, 0x23,
+ 0x1b, 0x22, 0x3c, 0x5d, 0x27, 0xce, 0xc2, 0xea, 0xdd, 0xe9, 0x1c,
+ 0xe0, 0x79, 0x81, 0xee, 0x65, 0x28, 0x62, 0xd1, 0xe4})),
+ std::make_pair(
+ 103,
+ std::vector<uint8_t>(
+ {0xc7, 0xf5, 0xc3, 0x7c, 0x72, 0x85, 0xf9, 0x27, 0xf7, 0x64, 0x43,
+ 0x41, 0x4d, 0x43, 0x57, 0xff, 0x78, 0x96, 0x47, 0xd7, 0xa0, 0x05,
+ 0xa5, 0xa7, 0x87, 0xe0, 0x3c, 0x34, 0x6b, 0x57, 0xf4, 0x9f, 0x21,
+ 0xb6, 0x4f, 0xa9, 0xcf, 0x4b, 0x7e, 0x45, 0x57, 0x3e, 0x23, 0x04,
+ 0x90, 0x17, 0x56, 0x71, 0x21, 0xa9, 0xc3, 0xd4, 0xb2, 0xb7, 0x3e,
+ 0xc5, 0xe9, 0x41, 0x35, 0x77, 0x52, 0x5d, 0xb4, 0x5a})),
+ std::make_pair(
+ 104,
+ std::vector<uint8_t>(
+ {0xec, 0x70, 0x96, 0x33, 0x07, 0x36, 0xfd, 0xb2, 0xd6, 0x4b, 0x56,
+ 0x53, 0xe7, 0x47, 0x5d, 0xa7, 0x46, 0xc2, 0x3a, 0x46, 0x13, 0xa8,
+ 0x26, 0x87, 0xa2, 0x80, 0x62, 0xd3, 0x23, 0x63, 0x64, 0x28, 0x4a,
+ 0xc0, 0x17, 0x20, 0xff, 0xb4, 0x06, 0xcf, 0xe2, 0x65, 0xc0, 0xdf,
+ 0x62, 0x6a, 0x18, 0x8c, 0x9e, 0x59, 0x63, 0xac, 0xe5, 0xd3, 0xd5,
+ 0xbb, 0x36, 0x3e, 0x32, 0xc3, 0x8c, 0x21, 0x90, 0xa6})),
+ std::make_pair(
+ 105,
+ std::vector<uint8_t>(
+ {0x82, 0xe7, 0x44, 0xc7, 0x5f, 0x46, 0x49, 0xec, 0x52, 0xb8, 0x07,
+ 0x71, 0xa7, 0x7d, 0x47, 0x5a, 0x3b, 0xc0, 0x91, 0x98, 0x95, 0x56,
+ 0x96, 0x0e, 0x27, 0x6a, 0x5f, 0x9e, 0xad, 0x92, 0xa0, 0x3f, 0x71,
+ 0x87, 0x42, 0xcd, 0xcf, 0xea, 0xee, 0x5c, 0xb8, 0x5c, 0x44, 0xaf,
+ 0x19, 0x8a, 0xdc, 0x43, 0xa4, 0xa4, 0x28, 0xf5, 0xf0, 0xc2, 0xdd,
+ 0xb0, 0xbe, 0x36, 0x05, 0x9f, 0x06, 0xd7, 0xdf, 0x73})),
+ std::make_pair(
+ 106,
+ std::vector<uint8_t>(
+ {0x28, 0x34, 0xb7, 0xa7, 0x17, 0x0f, 0x1f, 0x5b, 0x68, 0x55, 0x9a,
+ 0xb7, 0x8c, 0x10, 0x50, 0xec, 0x21, 0xc9, 0x19, 0x74, 0x0b, 0x78,
+ 0x4a, 0x90, 0x72, 0xf6, 0xe5, 0xd6, 0x9f, 0x82, 0x8d, 0x70, 0xc9,
+ 0x19, 0xc5, 0x03, 0x9f, 0xb1, 0x48, 0xe3, 0x9e, 0x2c, 0x8a, 0x52,
+ 0x11, 0x83, 0x78, 0xb0, 0x64, 0xca, 0x8d, 0x50, 0x01, 0xcd, 0x10,
+ 0xa5, 0x47, 0x83, 0x87, 0xb9, 0x66, 0x71, 0x5e, 0xd6})),
+ std::make_pair(
+ 107,
+ std::vector<uint8_t>(
+ {0x16, 0xb4, 0xad, 0xa8, 0x83, 0xf7, 0x2f, 0x85, 0x3b, 0xb7, 0xef,
+ 0x25, 0x3e, 0xfc, 0xab, 0x0c, 0x3e, 0x21, 0x61, 0x68, 0x7a, 0xd6,
+ 0x15, 0x43, 0xa0, 0xd2, 0x82, 0x4f, 0x91, 0xc1, 0xf8, 0x13, 0x47,
+ 0xd8, 0x6b, 0xe7, 0x09, 0xb1, 0x69, 0x96, 0xe1, 0x7f, 0x2d, 0xd4,
+ 0x86, 0x92, 0x7b, 0x02, 0x88, 0xad, 0x38, 0xd1, 0x30, 0x63, 0xc4,
+ 0xa9, 0x67, 0x2c, 0x39, 0x39, 0x7d, 0x37, 0x89, 0xb6})),
+ std::make_pair(
+ 108,
+ std::vector<uint8_t>(
+ {0x78, 0xd0, 0x48, 0xf3, 0xa6, 0x9d, 0x8b, 0x54, 0xae, 0x0e, 0xd6,
+ 0x3a, 0x57, 0x3a, 0xe3, 0x50, 0xd8, 0x9f, 0x7c, 0x6c, 0xf1, 0xf3,
+ 0x68, 0x89, 0x30, 0xde, 0x89, 0x9a, 0xfa, 0x03, 0x76, 0x97, 0x62,
+ 0x9b, 0x31, 0x4e, 0x5c, 0xd3, 0x03, 0xaa, 0x62, 0xfe, 0xea, 0x72,
+ 0xa2, 0x5b, 0xf4, 0x2b, 0x30, 0x4b, 0x6c, 0x6b, 0xcb, 0x27, 0xfa,
+ 0xe2, 0x1c, 0x16, 0xd9, 0x25, 0xe1, 0xfb, 0xda, 0xc3})),
+ std::make_pair(
+ 109,
+ std::vector<uint8_t>(
+ {0x0f, 0x74, 0x6a, 0x48, 0x74, 0x92, 0x87, 0xad, 0xa7, 0x7a, 0x82,
+ 0x96, 0x1f, 0x05, 0xa4, 0xda, 0x4a, 0xbd, 0xb7, 0xd7, 0x7b, 0x12,
+ 0x20, 0xf8, 0x36, 0xd0, 0x9e, 0xc8, 0x14, 0x35, 0x9c, 0x0e, 0xc0,
+ 0x23, 0x9b, 0x8c, 0x7b, 0x9f, 0xf9, 0xe0, 0x2f, 0x56, 0x9d, 0x1b,
+ 0x30, 0x1e, 0xf6, 0x7c, 0x46, 0x12, 0xd1, 0xde, 0x4f, 0x73, 0x0f,
+ 0x81, 0xc1, 0x2c, 0x40, 0xcc, 0x06, 0x3c, 0x5c, 0xaa})),
+ std::make_pair(
+ 110,
+ std::vector<uint8_t>(
+ {0xf0, 0xfc, 0x85, 0x9d, 0x3b, 0xd1, 0x95, 0xfb, 0xdc, 0x2d, 0x59,
+ 0x1e, 0x4c, 0xda, 0xc1, 0x51, 0x79, 0xec, 0x0f, 0x1d, 0xc8, 0x21,
+ 0xc1, 0x1d, 0xf1, 0xf0, 0xc1, 0xd2, 0x6e, 0x62, 0x60, 0xaa, 0xa6,
+ 0x5b, 0x79, 0xfa, 0xfa, 0xca, 0xfd, 0x7d, 0x3a, 0xd6, 0x1e, 0x60,
+ 0x0f, 0x25, 0x09, 0x05, 0xf5, 0x87, 0x8c, 0x87, 0x45, 0x28, 0x97,
+ 0x64, 0x7a, 0x35, 0xb9, 0x95, 0xbc, 0xad, 0xc3, 0xa3})),
+ std::make_pair(
+ 111,
+ std::vector<uint8_t>(
+ {0x26, 0x20, 0xf6, 0x87, 0xe8, 0x62, 0x5f, 0x6a, 0x41, 0x24, 0x60,
+ 0xb4, 0x2e, 0x2c, 0xef, 0x67, 0x63, 0x42, 0x08, 0xce, 0x10, 0xa0,
+ 0xcb, 0xd4, 0xdf, 0xf7, 0x04, 0x4a, 0x41, 0xb7, 0x88, 0x00, 0x77,
+ 0xe9, 0xf8, 0xdc, 0x3b, 0x8d, 0x12, 0x16, 0xd3, 0x37, 0x6a, 0x21,
+ 0xe0, 0x15, 0xb5, 0x8f, 0xb2, 0x79, 0xb5, 0x21, 0xd8, 0x3f, 0x93,
+ 0x88, 0xc7, 0x38, 0x2c, 0x85, 0x05, 0x59, 0x0b, 0x9b})),
+ std::make_pair(
+ 112,
+ std::vector<uint8_t>(
+ {0x22, 0x7e, 0x3a, 0xed, 0x8d, 0x2c, 0xb1, 0x0b, 0x91, 0x8f, 0xcb,
+ 0x04, 0xf9, 0xde, 0x3e, 0x6d, 0x0a, 0x57, 0xe0, 0x84, 0x76, 0xd9,
+ 0x37, 0x59, 0xcd, 0x7b, 0x2e, 0xd5, 0x4a, 0x1c, 0xbf, 0x02, 0x39,
+ 0xc5, 0x28, 0xfb, 0x04, 0xbb, 0xf2, 0x88, 0x25, 0x3e, 0x60, 0x1d,
+ 0x3b, 0xc3, 0x8b, 0x21, 0x79, 0x4a, 0xfe, 0xf9, 0x0b, 0x17, 0x09,
+ 0x4a, 0x18, 0x2c, 0xac, 0x55, 0x77, 0x45, 0xe7, 0x5f})),
+ std::make_pair(
+ 113,
+ std::vector<uint8_t>(
+ {0x1a, 0x92, 0x99, 0x01, 0xb0, 0x9c, 0x25, 0xf2, 0x7d, 0x6b, 0x35,
+ 0xbe, 0x7b, 0x2f, 0x1c, 0x47, 0x45, 0x13, 0x1f, 0xde, 0xbc, 0xa7,
+ 0xf3, 0xe2, 0x45, 0x19, 0x26, 0x72, 0x04, 0x34, 0xe0, 0xdb, 0x6e,
+ 0x74, 0xfd, 0x69, 0x3a, 0xd2, 0x9b, 0x77, 0x7d, 0xc3, 0x35, 0x5c,
+ 0x59, 0x2a, 0x36, 0x1c, 0x48, 0x73, 0xb0, 0x11, 0x33, 0xa5, 0x7c,
+ 0x2e, 0x3b, 0x70, 0x75, 0xcb, 0xdb, 0x86, 0xf4, 0xfc})),
+ std::make_pair(
+ 114,
+ std::vector<uint8_t>(
+ {0x5f, 0xd7, 0x96, 0x8b, 0xc2, 0xfe, 0x34, 0xf2, 0x20, 0xb5, 0xe3,
+ 0xdc, 0x5a, 0xf9, 0x57, 0x17, 0x42, 0xd7, 0x3b, 0x7d, 0x60, 0x81,
+ 0x9f, 0x28, 0x88, 0xb6, 0x29, 0x07, 0x2b, 0x96, 0xa9, 0xd8, 0xab,
+ 0x2d, 0x91, 0xb8, 0x2d, 0x0a, 0x9a, 0xab, 0xa6, 0x1b, 0xbd, 0x39,
+ 0x95, 0x81, 0x32, 0xfc, 0xc4, 0x25, 0x70, 0x23, 0xd1, 0xec, 0xa5,
+ 0x91, 0xb3, 0x05, 0x4e, 0x2d, 0xc8, 0x1c, 0x82, 0x00})),
+ std::make_pair(
+ 115,
+ std::vector<uint8_t>(
+ {0xdf, 0xcc, 0xe8, 0xcf, 0x32, 0x87, 0x0c, 0xc6, 0xa5, 0x03, 0xea,
+ 0xda, 0xfc, 0x87, 0xfd, 0x6f, 0x78, 0x91, 0x8b, 0x9b, 0x4d, 0x07,
+ 0x37, 0xdb, 0x68, 0x10, 0xbe, 0x99, 0x6b, 0x54, 0x97, 0xe7, 0xe5,
+ 0xcc, 0x80, 0xe3, 0x12, 0xf6, 0x1e, 0x71, 0xff, 0x3e, 0x96, 0x24,
+ 0x43, 0x60, 0x73, 0x15, 0x64, 0x03, 0xf7, 0x35, 0xf5, 0x6b, 0x0b,
+ 0x01, 0x84, 0x5c, 0x18, 0xf6, 0xca, 0xf7, 0x72, 0xe6})),
+ std::make_pair(
+ 116,
+ std::vector<uint8_t>(
+ {0x02, 0xf7, 0xef, 0x3a, 0x9c, 0xe0, 0xff, 0xf9, 0x60, 0xf6, 0x70,
+ 0x32, 0xb2, 0x96, 0xef, 0xca, 0x30, 0x61, 0xf4, 0x93, 0x4d, 0x69,
+ 0x07, 0x49, 0xf2, 0xd0, 0x1c, 0x35, 0xc8, 0x1c, 0x14, 0xf3, 0x9a,
+ 0x67, 0xfa, 0x35, 0x0b, 0xc8, 0xa0, 0x35, 0x9b, 0xf1, 0x72, 0x4b,
+ 0xff, 0xc3, 0xbc, 0xa6, 0xd7, 0xc7, 0xbb, 0xa4, 0x79, 0x1f, 0xd5,
+ 0x22, 0xa3, 0xad, 0x35, 0x3c, 0x02, 0xec, 0x5a, 0xa8})),
+ std::make_pair(
+ 117,
+ std::vector<uint8_t>(
+ {0x64, 0xbe, 0x5c, 0x6a, 0xba, 0x65, 0xd5, 0x94, 0x84, 0x4a, 0xe7,
+ 0x8b, 0xb0, 0x22, 0xe5, 0xbe, 0xbe, 0x12, 0x7f, 0xd6, 0xb6, 0xff,
+ 0xa5, 0xa1, 0x37, 0x03, 0x85, 0x5a, 0xb6, 0x3b, 0x62, 0x4d, 0xcd,
+ 0x1a, 0x36, 0x3f, 0x99, 0x20, 0x3f, 0x63, 0x2e, 0xc3, 0x86, 0xf3,
+ 0xea, 0x76, 0x7f, 0xc9, 0x92, 0xe8, 0xed, 0x96, 0x86, 0x58, 0x6a,
+ 0xa2, 0x75, 0x55, 0xa8, 0x59, 0x9d, 0x5b, 0x80, 0x8f})),
+ std::make_pair(
+ 118,
+ std::vector<uint8_t>(
+ {0xf7, 0x85, 0x85, 0x50, 0x5c, 0x4e, 0xaa, 0x54, 0xa8, 0xb5, 0xbe,
+ 0x70, 0xa6, 0x1e, 0x73, 0x5e, 0x0f, 0xf9, 0x7a, 0xf9, 0x44, 0xdd,
+ 0xb3, 0x00, 0x1e, 0x35, 0xd8, 0x6c, 0x4e, 0x21, 0x99, 0xd9, 0x76,
+ 0x10, 0x4b, 0x6a, 0xe3, 0x17, 0x50, 0xa3, 0x6a, 0x72, 0x6e, 0xd2,
+ 0x85, 0x06, 0x4f, 0x59, 0x81, 0xb5, 0x03, 0x88, 0x9f, 0xef, 0x82,
+ 0x2f, 0xcd, 0xc2, 0x89, 0x8d, 0xdd, 0xb7, 0x88, 0x9a})),
+ std::make_pair(
+ 119,
+ std::vector<uint8_t>(
+ {0xe4, 0xb5, 0x56, 0x60, 0x33, 0x86, 0x95, 0x72, 0xed, 0xfd, 0x87,
+ 0x47, 0x9a, 0x5b, 0xb7, 0x3c, 0x80, 0xe8, 0x75, 0x9b, 0x91, 0x23,
+ 0x28, 0x79, 0xd9, 0x6b, 0x1d, 0xda, 0x36, 0xc0, 0x12, 0x07, 0x6e,
+ 0xe5, 0xa2, 0xed, 0x7a, 0xe2, 0xde, 0x63, 0xef, 0x84, 0x06, 0xa0,
+ 0x6a, 0xea, 0x82, 0xc1, 0x88, 0x03, 0x1b, 0x56, 0x0b, 0xea, 0xfb,
+ 0x58, 0x3f, 0xb3, 0xde, 0x9e, 0x57, 0x95, 0x2a, 0x7e})),
+ std::make_pair(
+ 120,
+ std::vector<uint8_t>(
+ {0xe1, 0xb3, 0xe7, 0xed, 0x86, 0x7f, 0x6c, 0x94, 0x84, 0xa2, 0xa9,
+ 0x7f, 0x77, 0x15, 0xf2, 0x5e, 0x25, 0x29, 0x4e, 0x99, 0x2e, 0x41,
+ 0xf6, 0xa7, 0xc1, 0x61, 0xff, 0xc2, 0xad, 0xc6, 0xda, 0xae, 0xb7,
+ 0x11, 0x31, 0x02, 0xd5, 0xe6, 0x09, 0x02, 0x87, 0xfe, 0x6a, 0xd9,
+ 0x4c, 0xe5, 0xd6, 0xb7, 0x39, 0xc6, 0xca, 0x24, 0x0b, 0x05, 0xc7,
+ 0x6f, 0xb7, 0x3f, 0x25, 0xdd, 0x02, 0x4b, 0xf9, 0x35})),
+ std::make_pair(
+ 121,
+ std::vector<uint8_t>(
+ {0x85, 0xfd, 0x08, 0x5f, 0xdc, 0x12, 0xa0, 0x80, 0x98, 0x3d, 0xf0,
+ 0x7b, 0xd7, 0x01, 0x2b, 0x0d, 0x40, 0x2a, 0x0f, 0x40, 0x43, 0xfc,
+ 0xb2, 0x77, 0x5a, 0xdf, 0x0b, 0xad, 0x17, 0x4f, 0x9b, 0x08, 0xd1,
+ 0x67, 0x6e, 0x47, 0x69, 0x85, 0x78, 0x5c, 0x0a, 0x5d, 0xcc, 0x41,
+ 0xdb, 0xff, 0x6d, 0x95, 0xef, 0x4d, 0x66, 0xa3, 0xfb, 0xdc, 0x4a,
+ 0x74, 0xb8, 0x2b, 0xa5, 0x2d, 0xa0, 0x51, 0x2b, 0x74})),
+ std::make_pair(
+ 122,
+ std::vector<uint8_t>(
+ {0xae, 0xd8, 0xfa, 0x76, 0x4b, 0x0f, 0xbf, 0xf8, 0x21, 0xe0, 0x52,
+ 0x33, 0xd2, 0xf7, 0xb0, 0x90, 0x0e, 0xc4, 0x4d, 0x82, 0x6f, 0x95,
+ 0xe9, 0x3c, 0x34, 0x3c, 0x1b, 0xc3, 0xba, 0x5a, 0x24, 0x37, 0x4b,
+ 0x1d, 0x61, 0x6e, 0x7e, 0x7a, 0xba, 0x45, 0x3a, 0x0a, 0xda, 0x5e,
+ 0x4f, 0xab, 0x53, 0x82, 0x40, 0x9e, 0x0d, 0x42, 0xce, 0x9c, 0x2b,
+ 0xc7, 0xfb, 0x39, 0xa9, 0x9c, 0x34, 0x0c, 0x20, 0xf0})),
+ std::make_pair(
+ 123,
+ std::vector<uint8_t>(
+ {0x7b, 0xa3, 0xb2, 0xe2, 0x97, 0x23, 0x35, 0x22, 0xee, 0xb3, 0x43,
+ 0xbd, 0x3e, 0xbc, 0xfd, 0x83, 0x5a, 0x04, 0x00, 0x77, 0x35, 0xe8,
+ 0x7f, 0x0c, 0xa3, 0x00, 0xcb, 0xee, 0x6d, 0x41, 0x65, 0x65, 0x16,
+ 0x21, 0x71, 0x58, 0x1e, 0x40, 0x20, 0xff, 0x4c, 0xf1, 0x76, 0x45,
+ 0x0f, 0x12, 0x91, 0xea, 0x22, 0x85, 0xcb, 0x9e, 0xbf, 0xfe, 0x4c,
+ 0x56, 0x66, 0x06, 0x27, 0x68, 0x51, 0x45, 0x05, 0x1c})),
+ std::make_pair(
+ 124,
+ std::vector<uint8_t>(
+ {0xde, 0x74, 0x8b, 0xcf, 0x89, 0xec, 0x88, 0x08, 0x47, 0x21, 0xe1,
+ 0x6b, 0x85, 0xf3, 0x0a, 0xdb, 0x1a, 0x61, 0x34, 0xd6, 0x64, 0xb5,
+ 0x84, 0x35, 0x69, 0xba, 0xbc, 0x5b, 0xbd, 0x1a, 0x15, 0xca, 0x9b,
+ 0x61, 0x80, 0x3c, 0x90, 0x1a, 0x4f, 0xef, 0x32, 0x96, 0x5a, 0x17,
+ 0x49, 0xc9, 0xf3, 0xa4, 0xe2, 0x43, 0xe1, 0x73, 0x93, 0x9d, 0xc5,
+ 0xa8, 0xdc, 0x49, 0x5c, 0x67, 0x1a, 0xb5, 0x21, 0x45})),
+ std::make_pair(
+ 125,
+ std::vector<uint8_t>(
+ {0xaa, 0xf4, 0xd2, 0xbd, 0xf2, 0x00, 0xa9, 0x19, 0x70, 0x6d, 0x98,
+ 0x42, 0xdc, 0xe1, 0x6c, 0x98, 0x14, 0x0d, 0x34, 0xbc, 0x43, 0x3d,
+ 0xf3, 0x20, 0xab, 0xa9, 0xbd, 0x42, 0x9e, 0x54, 0x9a, 0xa7, 0xa3,
+ 0x39, 0x76, 0x52, 0xa4, 0xd7, 0x68, 0x27, 0x77, 0x86, 0xcf, 0x99,
+ 0x3c, 0xde, 0x23, 0x38, 0x67, 0x3e, 0xd2, 0xe6, 0xb6, 0x6c, 0x96,
+ 0x1f, 0xef, 0xb8, 0x2c, 0xd2, 0x0c, 0x93, 0x33, 0x8f})),
+ std::make_pair(
+ 126,
+ std::vector<uint8_t>(
+ {0xc4, 0x08, 0x21, 0x89, 0x68, 0xb7, 0x88, 0xbf, 0x86, 0x4f, 0x09,
+ 0x97, 0xe6, 0xbc, 0x4c, 0x3d, 0xba, 0x68, 0xb2, 0x76, 0xe2, 0x12,
+ 0x5a, 0x48, 0x43, 0x29, 0x60, 0x52, 0xff, 0x93, 0xbf, 0x57, 0x67,
+ 0xb8, 0xcd, 0xce, 0x71, 0x31, 0xf0, 0x87, 0x64, 0x30, 0xc1, 0x16,
+ 0x5f, 0xec, 0x6c, 0x4f, 0x47, 0xad, 0xaa, 0x4f, 0xd8, 0xbc, 0xfa,
+ 0xce, 0xf4, 0x63, 0xb5, 0xd3, 0xd0, 0xfa, 0x61, 0xa0})),
+ std::make_pair(
+ 127,
+ std::vector<uint8_t>(
+ {0x76, 0xd2, 0xd8, 0x19, 0xc9, 0x2b, 0xce, 0x55, 0xfa, 0x8e, 0x09,
+ 0x2a, 0xb1, 0xbf, 0x9b, 0x9e, 0xab, 0x23, 0x7a, 0x25, 0x26, 0x79,
+ 0x86, 0xca, 0xcf, 0x2b, 0x8e, 0xe1, 0x4d, 0x21, 0x4d, 0x73, 0x0d,
+ 0xc9, 0xa5, 0xaa, 0x2d, 0x7b, 0x59, 0x6e, 0x86, 0xa1, 0xfd, 0x8f,
+ 0xa0, 0x80, 0x4c, 0x77, 0x40, 0x2d, 0x2f, 0xcd, 0x45, 0x08, 0x36,
+ 0x88, 0xb2, 0x18, 0xb1, 0xcd, 0xfa, 0x0d, 0xcb, 0xcb})),
+ std::make_pair(
+ 128,
+ std::vector<uint8_t>(
+ {0x72, 0x06, 0x5e, 0xe4, 0xdd, 0x91, 0xc2, 0xd8, 0x50, 0x9f, 0xa1,
+ 0xfc, 0x28, 0xa3, 0x7c, 0x7f, 0xc9, 0xfa, 0x7d, 0x5b, 0x3f, 0x8a,
+ 0xd3, 0xd0, 0xd7, 0xa2, 0x56, 0x26, 0xb5, 0x7b, 0x1b, 0x44, 0x78,
+ 0x8d, 0x4c, 0xaf, 0x80, 0x62, 0x90, 0x42, 0x5f, 0x98, 0x90, 0xa3,
+ 0xa2, 0xa3, 0x5a, 0x90, 0x5a, 0xb4, 0xb3, 0x7a, 0xcf, 0xd0, 0xda,
+ 0x6e, 0x45, 0x17, 0xb2, 0x52, 0x5c, 0x96, 0x51, 0xe4})),
+ std::make_pair(
+ 129,
+ std::vector<uint8_t>(
+ {0x64, 0x47, 0x5d, 0xfe, 0x76, 0x00, 0xd7, 0x17, 0x1b, 0xea, 0x0b,
+ 0x39, 0x4e, 0x27, 0xc9, 0xb0, 0x0d, 0x8e, 0x74, 0xdd, 0x1e, 0x41,
+ 0x6a, 0x79, 0x47, 0x36, 0x82, 0xad, 0x3d, 0xfd, 0xbb, 0x70, 0x66,
+ 0x31, 0x55, 0x80, 0x55, 0xcf, 0xc8, 0xa4, 0x0e, 0x07, 0xbd, 0x01,
+ 0x5a, 0x45, 0x40, 0xdc, 0xde, 0xa1, 0x58, 0x83, 0xcb, 0xbf, 0x31,
+ 0x41, 0x2d, 0xf1, 0xde, 0x1c, 0xd4, 0x15, 0x2b, 0x91})),
+ std::make_pair(
+ 130,
+ std::vector<uint8_t>(
+ {0x12, 0xcd, 0x16, 0x74, 0xa4, 0x48, 0x8a, 0x5d, 0x7c, 0x2b, 0x31,
+ 0x60, 0xd2, 0xe2, 0xc4, 0xb5, 0x83, 0x71, 0xbe, 0xda, 0xd7, 0x93,
+ 0x41, 0x8d, 0x6f, 0x19, 0xc6, 0xee, 0x38, 0x5d, 0x70, 0xb3, 0xe0,
+ 0x67, 0x39, 0x36, 0x9d, 0x4d, 0xf9, 0x10, 0xed, 0xb0, 0xb0, 0xa5,
+ 0x4c, 0xbf, 0xf4, 0x3d, 0x54, 0x54, 0x4c, 0xd3, 0x7a, 0xb3, 0xa0,
+ 0x6c, 0xfa, 0x0a, 0x3d, 0xda, 0xc8, 0xb6, 0x6c, 0x89})),
+ std::make_pair(
+ 131,
+ std::vector<uint8_t>(
+ {0x60, 0x75, 0x69, 0x66, 0x47, 0x9d, 0xed, 0xc6, 0xdd, 0x4b, 0xcf,
+ 0xf8, 0xea, 0x7d, 0x1d, 0x4c, 0xe4, 0xd4, 0xaf, 0x2e, 0x7b, 0x09,
+ 0x7e, 0x32, 0xe3, 0x76, 0x35, 0x18, 0x44, 0x11, 0x47, 0xcc, 0x12,
+ 0xb3, 0xc0, 0xee, 0x6d, 0x2e, 0xca, 0xbf, 0x11, 0x98, 0xce, 0xc9,
+ 0x2e, 0x86, 0xa3, 0x61, 0x6f, 0xba, 0x4f, 0x4e, 0x87, 0x2f, 0x58,
+ 0x25, 0x33, 0x0a, 0xdb, 0xb4, 0xc1, 0xde, 0xe4, 0x44})),
+ std::make_pair(
+ 132,
+ std::vector<uint8_t>(
+ {0xa7, 0x80, 0x3b, 0xcb, 0x71, 0xbc, 0x1d, 0x0f, 0x43, 0x83, 0xdd,
+ 0xe1, 0xe0, 0x61, 0x2e, 0x04, 0xf8, 0x72, 0xb7, 0x15, 0xad, 0x30,
+ 0x81, 0x5c, 0x22, 0x49, 0xcf, 0x34, 0xab, 0xb8, 0xb0, 0x24, 0x91,
+ 0x5c, 0xb2, 0xfc, 0x9f, 0x4e, 0x7c, 0xc4, 0xc8, 0xcf, 0xd4, 0x5b,
+ 0xe2, 0xd5, 0xa9, 0x1e, 0xab, 0x09, 0x41, 0xc7, 0xd2, 0x70, 0xe2,
+ 0xda, 0x4c, 0xa4, 0xa9, 0xf7, 0xac, 0x68, 0x66, 0x3a})),
+ std::make_pair(
+ 133,
+ std::vector<uint8_t>(
+ {0xb8, 0x4e, 0xf6, 0xa7, 0x22, 0x9a, 0x34, 0xa7, 0x50, 0xd9, 0xa9,
+ 0x8e, 0xe2, 0x52, 0x98, 0x71, 0x81, 0x6b, 0x87, 0xfb, 0xe3, 0xbc,
+ 0x45, 0xb4, 0x5f, 0xa5, 0xae, 0x82, 0xd5, 0x14, 0x15, 0x40, 0x21,
+ 0x11, 0x65, 0xc3, 0xc5, 0xd7, 0xa7, 0x47, 0x6b, 0xa5, 0xa4, 0xaa,
+ 0x06, 0xd6, 0x64, 0x76, 0xf0, 0xd9, 0xdc, 0x49, 0xa3, 0xf1, 0xee,
+ 0x72, 0xc3, 0xac, 0xab, 0xd4, 0x98, 0x96, 0x74, 0x14})),
+ std::make_pair(
+ 134,
+ std::vector<uint8_t>(
+ {0xfa, 0xe4, 0xb6, 0xd8, 0xef, 0xc3, 0xf8, 0xc8, 0xe6, 0x4d, 0x00,
+ 0x1d, 0xab, 0xec, 0x3a, 0x21, 0xf5, 0x44, 0xe8, 0x27, 0x14, 0x74,
+ 0x52, 0x51, 0xb2, 0xb4, 0xb3, 0x93, 0xf2, 0xf4, 0x3e, 0x0d, 0xa3,
+ 0xd4, 0x03, 0xc6, 0x4d, 0xb9, 0x5a, 0x2c, 0xb6, 0xe2, 0x3e, 0xbb,
+ 0x7b, 0x9e, 0x94, 0xcd, 0xd5, 0xdd, 0xac, 0x54, 0xf0, 0x7c, 0x4a,
+ 0x61, 0xbd, 0x3c, 0xb1, 0x0a, 0xa6, 0xf9, 0x3b, 0x49})),
+ std::make_pair(
+ 135,
+ std::vector<uint8_t>(
+ {0x34, 0xf7, 0x28, 0x66, 0x05, 0xa1, 0x22, 0x36, 0x95, 0x40, 0x14,
+ 0x1d, 0xed, 0x79, 0xb8, 0x95, 0x72, 0x55, 0xda, 0x2d, 0x41, 0x55,
+ 0xab, 0xbf, 0x5a, 0x8d, 0xbb, 0x89, 0xc8, 0xeb, 0x7e, 0xde, 0x8e,
+ 0xee, 0xf1, 0xda, 0xa4, 0x6d, 0xc2, 0x9d, 0x75, 0x1d, 0x04, 0x5d,
+ 0xc3, 0xb1, 0xd6, 0x58, 0xbb, 0x64, 0xb8, 0x0f, 0xf8, 0x58, 0x9e,
+ 0xdd, 0xb3, 0x82, 0x4b, 0x13, 0xda, 0x23, 0x5a, 0x6b})),
+ std::make_pair(
+ 136,
+ std::vector<uint8_t>(
+ {0x3b, 0x3b, 0x48, 0x43, 0x4b, 0xe2, 0x7b, 0x9e, 0xab, 0xab, 0xba,
+ 0x43, 0xbf, 0x6b, 0x35, 0xf1, 0x4b, 0x30, 0xf6, 0xa8, 0x8d, 0xc2,
+ 0xe7, 0x50, 0xc3, 0x58, 0x47, 0x0d, 0x6b, 0x3a, 0xa3, 0xc1, 0x8e,
+ 0x47, 0xdb, 0x40, 0x17, 0xfa, 0x55, 0x10, 0x6d, 0x82, 0x52, 0xf0,
+ 0x16, 0x37, 0x1a, 0x00, 0xf5, 0xf8, 0xb0, 0x70, 0xb7, 0x4b, 0xa5,
+ 0xf2, 0x3c, 0xff, 0xc5, 0x51, 0x1c, 0x9f, 0x09, 0xf0})),
+ std::make_pair(
+ 137,
+ std::vector<uint8_t>(
+ {0xba, 0x28, 0x9e, 0xbd, 0x65, 0x62, 0xc4, 0x8c, 0x3e, 0x10, 0xa8,
+ 0xad, 0x6c, 0xe0, 0x2e, 0x73, 0x43, 0x3d, 0x1e, 0x93, 0xd7, 0xc9,
+ 0x27, 0x9d, 0x4d, 0x60, 0xa7, 0xe8, 0x79, 0xee, 0x11, 0xf4, 0x41,
+ 0xa0, 0x00, 0xf4, 0x8e, 0xd9, 0xf7, 0xc4, 0xed, 0x87, 0xa4, 0x51,
+ 0x36, 0xd7, 0xdc, 0xcd, 0xca, 0x48, 0x21, 0x09, 0xc7, 0x8a, 0x51,
+ 0x06, 0x2b, 0x3b, 0xa4, 0x04, 0x4a, 0xda, 0x24, 0x69})),
+ std::make_pair(
+ 138,
+ std::vector<uint8_t>(
+ {0x02, 0x29, 0x39, 0xe2, 0x38, 0x6c, 0x5a, 0x37, 0x04, 0x98, 0x56,
+ 0xc8, 0x50, 0xa2, 0xbb, 0x10, 0xa1, 0x3d, 0xfe, 0xa4, 0x21, 0x2b,
+ 0x4c, 0x73, 0x2a, 0x88, 0x40, 0xa9, 0xff, 0xa5, 0xfa, 0xf5, 0x48,
+ 0x75, 0xc5, 0x44, 0x88, 0x16, 0xb2, 0x78, 0x5a, 0x00, 0x7d, 0xa8,
+ 0xa8, 0xd2, 0xbc, 0x7d, 0x71, 0xa5, 0x4e, 0x4e, 0x65, 0x71, 0xf1,
+ 0x0b, 0x60, 0x0c, 0xbd, 0xb2, 0x5d, 0x13, 0xed, 0xe3})),
+ std::make_pair(
+ 139,
+ std::vector<uint8_t>(
+ {0xe6, 0xfe, 0xc1, 0x9d, 0x89, 0xce, 0x87, 0x17, 0xb1, 0xa0, 0x87,
+ 0x02, 0x46, 0x70, 0xfe, 0x02, 0x6f, 0x6c, 0x7c, 0xbd, 0xa1, 0x1c,
+ 0xae, 0xf9, 0x59, 0xbb, 0x2d, 0x35, 0x1b, 0xf8, 0x56, 0xf8, 0x05,
+ 0x5d, 0x1c, 0x0e, 0xbd, 0xaa, 0xa9, 0xd1, 0xb1, 0x78, 0x86, 0xfc,
+ 0x2c, 0x56, 0x2b, 0x5e, 0x99, 0x64, 0x2f, 0xc0, 0x64, 0x71, 0x0c,
+ 0x0d, 0x34, 0x88, 0xa0, 0x2b, 0x5e, 0xd7, 0xf6, 0xfd})),
+ std::make_pair(
+ 140,
+ std::vector<uint8_t>(
+ {0x94, 0xc9, 0x6f, 0x02, 0xa8, 0xf5, 0x76, 0xac, 0xa3, 0x2b, 0xa6,
+ 0x1c, 0x2b, 0x20, 0x6f, 0x90, 0x72, 0x85, 0xd9, 0x29, 0x9b, 0x83,
+ 0xac, 0x17, 0x5c, 0x20, 0x9a, 0x8d, 0x43, 0xd5, 0x3b, 0xfe, 0x68,
+ 0x3d, 0xd1, 0xd8, 0x3e, 0x75, 0x49, 0xcb, 0x90, 0x6c, 0x28, 0xf5,
+ 0x9a, 0xb7, 0xc4, 0x6f, 0x87, 0x51, 0x36, 0x6a, 0x28, 0xc3, 0x9d,
+ 0xd5, 0xfe, 0x26, 0x93, 0xc9, 0x01, 0x96, 0x66, 0xc8})),
+ std::make_pair(
+ 141,
+ std::vector<uint8_t>(
+ {0x31, 0xa0, 0xcd, 0x21, 0x5e, 0xbd, 0x2c, 0xb6, 0x1d, 0xe5, 0xb9,
+ 0xed, 0xc9, 0x1e, 0x61, 0x95, 0xe3, 0x1c, 0x59, 0xa5, 0x64, 0x8d,
+ 0x5c, 0x9f, 0x73, 0x7e, 0x12, 0x5b, 0x26, 0x05, 0x70, 0x8f, 0x2e,
+ 0x32, 0x5a, 0xb3, 0x38, 0x1c, 0x8d, 0xce, 0x1a, 0x3e, 0x95, 0x88,
+ 0x86, 0xf1, 0xec, 0xdc, 0x60, 0x31, 0x8f, 0x88, 0x2c, 0xfe, 0x20,
+ 0xa2, 0x41, 0x91, 0x35, 0x2e, 0x61, 0x7b, 0x0f, 0x21})),
+ std::make_pair(
+ 142,
+ std::vector<uint8_t>(
+ {0x91, 0xab, 0x50, 0x4a, 0x52, 0x2d, 0xce, 0x78, 0x77, 0x9f, 0x4c,
+ 0x6c, 0x6b, 0xa2, 0xe6, 0xb6, 0xdb, 0x55, 0x65, 0xc7, 0x6d, 0x3e,
+ 0x7e, 0x7c, 0x92, 0x0c, 0xaf, 0x7f, 0x75, 0x7e, 0xf9, 0xdb, 0x7c,
+ 0x8f, 0xcf, 0x10, 0xe5, 0x7f, 0x03, 0x37, 0x9e, 0xa9, 0xbf, 0x75,
+ 0xeb, 0x59, 0x89, 0x5d, 0x96, 0xe1, 0x49, 0x80, 0x0b, 0x6a, 0xae,
+ 0x01, 0xdb, 0x77, 0x8b, 0xb9, 0x0a, 0xfb, 0xc9, 0x89})),
+ std::make_pair(
+ 143,
+ std::vector<uint8_t>(
+ {0xd8, 0x5c, 0xab, 0xc6, 0xbd, 0x5b, 0x1a, 0x01, 0xa5, 0xaf, 0xd8,
+ 0xc6, 0x73, 0x47, 0x40, 0xda, 0x9f, 0xd1, 0xc1, 0xac, 0xc6, 0xdb,
+ 0x29, 0xbf, 0xc8, 0xa2, 0xe5, 0xb6, 0x68, 0xb0, 0x28, 0xb6, 0xb3,
+ 0x15, 0x4b, 0xfb, 0x87, 0x03, 0xfa, 0x31, 0x80, 0x25, 0x1d, 0x58,
+ 0x9a, 0xd3, 0x80, 0x40, 0xce, 0xb7, 0x07, 0xc4, 0xba, 0xd1, 0xb5,
+ 0x34, 0x3c, 0xb4, 0x26, 0xb6, 0x1e, 0xaa, 0x49, 0xc1})),
+ std::make_pair(
+ 144,
+ std::vector<uint8_t>(
+ {0xd6, 0x2e, 0xfb, 0xec, 0x2c, 0xa9, 0xc1, 0xf8, 0xbd, 0x66, 0xce,
+ 0x8b, 0x3f, 0x6a, 0x89, 0x8c, 0xb3, 0xf7, 0x56, 0x6b, 0xa6, 0x56,
+ 0x8c, 0x61, 0x8a, 0xd1, 0xfe, 0xb2, 0xb6, 0x5b, 0x76, 0xc3, 0xce,
+ 0x1d, 0xd2, 0x0f, 0x73, 0x95, 0x37, 0x2f, 0xaf, 0x28, 0x42, 0x7f,
+ 0x61, 0xc9, 0x27, 0x80, 0x49, 0xcf, 0x01, 0x40, 0xdf, 0x43, 0x4f,
+ 0x56, 0x33, 0x04, 0x8c, 0x86, 0xb8, 0x1e, 0x03, 0x99})),
+ std::make_pair(
+ 145,
+ std::vector<uint8_t>(
+ {0x7c, 0x8f, 0xdc, 0x61, 0x75, 0x43, 0x9e, 0x2c, 0x3d, 0xb1, 0x5b,
+ 0xaf, 0xa7, 0xfb, 0x06, 0x14, 0x3a, 0x6a, 0x23, 0xbc, 0x90, 0xf4,
+ 0x49, 0xe7, 0x9d, 0xee, 0xf7, 0x3c, 0x3d, 0x49, 0x2a, 0x67, 0x17,
+ 0x15, 0xc1, 0x93, 0xb6, 0xfe, 0xa9, 0xf0, 0x36, 0x05, 0x0b, 0x94,
+ 0x60, 0x69, 0x85, 0x6b, 0x89, 0x7e, 0x08, 0xc0, 0x07, 0x68, 0xf5,
+ 0xee, 0x5d, 0xdc, 0xf7, 0x0b, 0x7c, 0xd6, 0xd0, 0xe0})),
+ std::make_pair(
+ 146,
+ std::vector<uint8_t>(
+ {0x58, 0x60, 0x2e, 0xe7, 0x46, 0x8e, 0x6b, 0xc9, 0xdf, 0x21, 0xbd,
+ 0x51, 0xb2, 0x3c, 0x00, 0x5f, 0x72, 0xd6, 0xcb, 0x01, 0x3f, 0x0a,
+ 0x1b, 0x48, 0xcb, 0xec, 0x5e, 0xca, 0x29, 0x92, 0x99, 0xf9, 0x7f,
+ 0x09, 0xf5, 0x4a, 0x9a, 0x01, 0x48, 0x3e, 0xae, 0xb3, 0x15, 0xa6,
+ 0x47, 0x8b, 0xad, 0x37, 0xba, 0x47, 0xca, 0x13, 0x47, 0xc7, 0xc8,
+ 0xfc, 0x9e, 0x66, 0x95, 0x59, 0x2c, 0x91, 0xd7, 0x23})),
+ std::make_pair(
+ 147,
+ std::vector<uint8_t>(
+ {0x27, 0xf5, 0xb7, 0x9e, 0xd2, 0x56, 0xb0, 0x50, 0x99, 0x3d, 0x79,
+ 0x34, 0x96, 0xed, 0xf4, 0x80, 0x7c, 0x1d, 0x85, 0xa7, 0xb0, 0xa6,
+ 0x7c, 0x9c, 0x4f, 0xa9, 0x98, 0x60, 0x75, 0x0b, 0x0a, 0xe6, 0x69,
+ 0x89, 0x67, 0x0a, 0x8f, 0xfd, 0x78, 0x56, 0xd7, 0xce, 0x41, 0x15,
+ 0x99, 0xe5, 0x8c, 0x4d, 0x77, 0xb2, 0x32, 0xa6, 0x2b, 0xef, 0x64,
+ 0xd1, 0x52, 0x75, 0xbe, 0x46, 0xa6, 0x82, 0x35, 0xff})),
+ std::make_pair(
+ 148,
+ std::vector<uint8_t>(
+ {0x39, 0x57, 0xa9, 0x76, 0xb9, 0xf1, 0x88, 0x7b, 0xf0, 0x04, 0xa8,
+ 0xdc, 0xa9, 0x42, 0xc9, 0x2d, 0x2b, 0x37, 0xea, 0x52, 0x60, 0x0f,
+ 0x25, 0xe0, 0xc9, 0xbc, 0x57, 0x07, 0xd0, 0x27, 0x9c, 0x00, 0xc6,
+ 0xe8, 0x5a, 0x83, 0x9b, 0x0d, 0x2d, 0x8e, 0xb5, 0x9c, 0x51, 0xd9,
+ 0x47, 0x88, 0xeb, 0xe6, 0x24, 0x74, 0xa7, 0x91, 0xca, 0xdf, 0x52,
+ 0xcc, 0xcf, 0x20, 0xf5, 0x07, 0x0b, 0x65, 0x73, 0xfc})),
+ std::make_pair(
+ 149,
+ std::vector<uint8_t>(
+ {0xea, 0xa2, 0x37, 0x6d, 0x55, 0x38, 0x0b, 0xf7, 0x72, 0xec, 0xca,
+ 0x9c, 0xb0, 0xaa, 0x46, 0x68, 0xc9, 0x5c, 0x70, 0x71, 0x62, 0xfa,
+ 0x86, 0xd5, 0x18, 0xc8, 0xce, 0x0c, 0xa9, 0xbf, 0x73, 0x62, 0xb9,
+ 0xf2, 0xa0, 0xad, 0xc3, 0xff, 0x59, 0x92, 0x2d, 0xf9, 0x21, 0xb9,
+ 0x45, 0x67, 0xe8, 0x1e, 0x45, 0x2f, 0x6c, 0x1a, 0x07, 0xfc, 0x81,
+ 0x7c, 0xeb, 0xe9, 0x96, 0x04, 0xb3, 0x50, 0x5d, 0x38})),
+ std::make_pair(
+ 150,
+ std::vector<uint8_t>(
+ {0xc1, 0xe2, 0xc7, 0x8b, 0x6b, 0x27, 0x34, 0xe2, 0x48, 0x0e, 0xc5,
+ 0x50, 0x43, 0x4c, 0xb5, 0xd6, 0x13, 0x11, 0x1a, 0xdc, 0xc2, 0x1d,
+ 0x47, 0x55, 0x45, 0xc3, 0xb1, 0xb7, 0xe6, 0xff, 0x12, 0x44, 0x44,
+ 0x76, 0xe5, 0xc0, 0x55, 0x13, 0x2e, 0x22, 0x29, 0xdc, 0x0f, 0x80,
+ 0x70, 0x44, 0xbb, 0x91, 0x9b, 0x1a, 0x56, 0x62, 0xdd, 0x38, 0xa9,
+ 0xee, 0x65, 0xe2, 0x43, 0xa3, 0x91, 0x1a, 0xed, 0x1a})),
+ std::make_pair(
+ 151,
+ std::vector<uint8_t>(
+ {0x8a, 0xb4, 0x87, 0x13, 0x38, 0x9d, 0xd0, 0xfc, 0xf9, 0xf9, 0x65,
+ 0xd3, 0xce, 0x66, 0xb1, 0xe5, 0x59, 0xa1, 0xf8, 0xc5, 0x87, 0x41,
+ 0xd6, 0x76, 0x83, 0xcd, 0x97, 0x13, 0x54, 0xf4, 0x52, 0xe6, 0x2d,
+ 0x02, 0x07, 0xa6, 0x5e, 0x43, 0x6c, 0x5d, 0x5d, 0x8f, 0x8e, 0xe7,
+ 0x1c, 0x6a, 0xbf, 0xe5, 0x0e, 0x66, 0x90, 0x04, 0xc3, 0x02, 0xb3,
+ 0x1a, 0x7e, 0xa8, 0x31, 0x1d, 0x4a, 0x91, 0x60, 0x51})),
+ std::make_pair(
+ 152,
+ std::vector<uint8_t>(
+ {0x24, 0xce, 0x0a, 0xdd, 0xaa, 0x4c, 0x65, 0x03, 0x8b, 0xd1, 0xb1,
+ 0xc0, 0xf1, 0x45, 0x2a, 0x0b, 0x12, 0x87, 0x77, 0xaa, 0xbc, 0x94,
+ 0xa2, 0x9d, 0xf2, 0xfd, 0x6c, 0x7e, 0x2f, 0x85, 0xf8, 0xab, 0x9a,
+ 0xc7, 0xef, 0xf5, 0x16, 0xb0, 0xe0, 0xa8, 0x25, 0xc8, 0x4a, 0x24,
+ 0xcf, 0xe4, 0x92, 0xea, 0xad, 0x0a, 0x63, 0x08, 0xe4, 0x6d, 0xd4,
+ 0x2f, 0xe8, 0x33, 0x3a, 0xb9, 0x71, 0xbb, 0x30, 0xca})),
+ std::make_pair(
+ 153,
+ std::vector<uint8_t>(
+ {0x51, 0x54, 0xf9, 0x29, 0xee, 0x03, 0x04, 0x5b, 0x6b, 0x0c, 0x00,
+ 0x04, 0xfa, 0x77, 0x8e, 0xde, 0xe1, 0xd1, 0x39, 0x89, 0x32, 0x67,
+ 0xcc, 0x84, 0x82, 0x5a, 0xd7, 0xb3, 0x6c, 0x63, 0xde, 0x32, 0x79,
+ 0x8e, 0x4a, 0x16, 0x6d, 0x24, 0x68, 0x65, 0x61, 0x35, 0x4f, 0x63,
+ 0xb0, 0x07, 0x09, 0xa1, 0x36, 0x4b, 0x3c, 0x24, 0x1d, 0xe3, 0xfe,
+ 0xbf, 0x07, 0x54, 0x04, 0x58, 0x97, 0x46, 0x7c, 0xd4})),
+ std::make_pair(
+ 154,
+ std::vector<uint8_t>(
+ {0xe7, 0x4e, 0x90, 0x79, 0x20, 0xfd, 0x87, 0xbd, 0x5a, 0xd6, 0x36,
+ 0xdd, 0x11, 0x08, 0x5e, 0x50, 0xee, 0x70, 0x45, 0x9c, 0x44, 0x3e,
+ 0x1c, 0xe5, 0x80, 0x9a, 0xf2, 0xbc, 0x2e, 0xba, 0x39, 0xf9, 0xe6,
+ 0xd7, 0x12, 0x8e, 0x0e, 0x37, 0x12, 0xc3, 0x16, 0xda, 0x06, 0xf4,
+ 0x70, 0x5d, 0x78, 0xa4, 0x83, 0x8e, 0x28, 0x12, 0x1d, 0x43, 0x44,
+ 0xa2, 0xc7, 0x9c, 0x5e, 0x0d, 0xb3, 0x07, 0xa6, 0x77})),
+ std::make_pair(
+ 155,
+ std::vector<uint8_t>(
+ {0xbf, 0x91, 0xa2, 0x23, 0x34, 0xba, 0xc2, 0x0f, 0x3f, 0xd8, 0x06,
+ 0x63, 0xb3, 0xcd, 0x06, 0xc4, 0xe8, 0x80, 0x2f, 0x30, 0xe6, 0xb5,
+ 0x9f, 0x90, 0xd3, 0x03, 0x5c, 0xc9, 0x79, 0x8a, 0x21, 0x7e, 0xd5,
+ 0xa3, 0x1a, 0xbb, 0xda, 0x7f, 0xa6, 0x84, 0x28, 0x27, 0xbd, 0xf2,
+ 0xa7, 0xa1, 0xc2, 0x1f, 0x6f, 0xcf, 0xcc, 0xbb, 0x54, 0xc6, 0xc5,
+ 0x29, 0x26, 0xf3, 0x2d, 0xa8, 0x16, 0x26, 0x9b, 0xe1})),
+ std::make_pair(
+ 156,
+ std::vector<uint8_t>(
+ {0xd9, 0xd5, 0xc7, 0x4b, 0xe5, 0x12, 0x1b, 0x0b, 0xd7, 0x42, 0xf2,
+ 0x6b, 0xff, 0xb8, 0xc8, 0x9f, 0x89, 0x17, 0x1f, 0x3f, 0x93, 0x49,
+ 0x13, 0x49, 0x2b, 0x09, 0x03, 0xc2, 0x71, 0xbb, 0xe2, 0xb3, 0x39,
+ 0x5e, 0xf2, 0x59, 0x66, 0x9b, 0xef, 0x43, 0xb5, 0x7f, 0x7f, 0xcc,
+ 0x30, 0x27, 0xdb, 0x01, 0x82, 0x3f, 0x6b, 0xae, 0xe6, 0x6e, 0x4f,
+ 0x9f, 0xea, 0xd4, 0xd6, 0x72, 0x6c, 0x74, 0x1f, 0xce})),
+ std::make_pair(
+ 157,
+ std::vector<uint8_t>(
+ {0x50, 0xc8, 0xb8, 0xcf, 0x34, 0xcd, 0x87, 0x9f, 0x80, 0xe2, 0xfa,
+ 0xab, 0x32, 0x30, 0xb0, 0xc0, 0xe1, 0xcc, 0x3e, 0x9d, 0xca, 0xde,
+ 0xb1, 0xb9, 0xd9, 0x7a, 0xb9, 0x23, 0x41, 0x5d, 0xd9, 0xa1, 0xfe,
+ 0x38, 0xad, 0xdd, 0x5c, 0x11, 0x75, 0x6c, 0x67, 0x99, 0x0b, 0x25,
+ 0x6e, 0x95, 0xad, 0x6d, 0x8f, 0x9f, 0xed, 0xce, 0x10, 0xbf, 0x1c,
+ 0x90, 0x67, 0x9c, 0xde, 0x0e, 0xcf, 0x1b, 0xe3, 0x47})),
+ std::make_pair(
+ 158,
+ std::vector<uint8_t>(
+ {0x0a, 0x38, 0x6e, 0x7c, 0xd5, 0xdd, 0x9b, 0x77, 0xa0, 0x35, 0xe0,
+ 0x9f, 0xe6, 0xfe, 0xe2, 0xc8, 0xce, 0x61, 0xb5, 0x38, 0x3c, 0x87,
+ 0xea, 0x43, 0x20, 0x50, 0x59, 0xc5, 0xe4, 0xcd, 0x4f, 0x44, 0x08,
+ 0x31, 0x9b, 0xb0, 0xa8, 0x23, 0x60, 0xf6, 0xa5, 0x8e, 0x6c, 0x9c,
+ 0xe3, 0xf4, 0x87, 0xc4, 0x46, 0x06, 0x3b, 0xf8, 0x13, 0xbc, 0x6b,
+ 0xa5, 0x35, 0xe1, 0x7f, 0xc1, 0x82, 0x6c, 0xfc, 0x91})),
+ std::make_pair(
+ 159,
+ std::vector<uint8_t>(
+ {0x1f, 0x14, 0x59, 0xcb, 0x6b, 0x61, 0xcb, 0xac, 0x5f, 0x0e, 0xfe,
+ 0x8f, 0xc4, 0x87, 0x53, 0x8f, 0x42, 0x54, 0x89, 0x87, 0xfc, 0xd5,
+ 0x62, 0x21, 0xcf, 0xa7, 0xbe, 0xb2, 0x25, 0x04, 0x76, 0x9e, 0x79,
+ 0x2c, 0x45, 0xad, 0xfb, 0x1d, 0x6b, 0x3d, 0x60, 0xd7, 0xb7, 0x49,
+ 0xc8, 0xa7, 0x5b, 0x0b, 0xdf, 0x14, 0xe8, 0xea, 0x72, 0x1b, 0x95,
+ 0xdc, 0xa5, 0x38, 0xca, 0x6e, 0x25, 0x71, 0x12, 0x09})),
+ std::make_pair(
+ 160,
+ std::vector<uint8_t>(
+ {0xe5, 0x8b, 0x38, 0x36, 0xb7, 0xd8, 0xfe, 0xdb, 0xb5, 0x0c, 0xa5,
+ 0x72, 0x5c, 0x65, 0x71, 0xe7, 0x4c, 0x07, 0x85, 0xe9, 0x78, 0x21,
+ 0xda, 0xb8, 0xb6, 0x29, 0x8c, 0x10, 0xe4, 0xc0, 0x79, 0xd4, 0xa6,
+ 0xcd, 0xf2, 0x2f, 0x0f, 0xed, 0xb5, 0x50, 0x32, 0x92, 0x5c, 0x16,
+ 0x74, 0x81, 0x15, 0xf0, 0x1a, 0x10, 0x5e, 0x77, 0xe0, 0x0c, 0xee,
+ 0x3d, 0x07, 0x92, 0x4d, 0xc0, 0xd8, 0xf9, 0x06, 0x59})),
+ std::make_pair(
+ 161,
+ std::vector<uint8_t>(
+ {0xb9, 0x29, 0xcc, 0x65, 0x05, 0xf0, 0x20, 0x15, 0x86, 0x72, 0xde,
+ 0xda, 0x56, 0xd0, 0xdb, 0x08, 0x1a, 0x2e, 0xe3, 0x4c, 0x00, 0xc1,
+ 0x10, 0x00, 0x29, 0xbd, 0xf8, 0xea, 0x98, 0x03, 0x4f, 0xa4, 0xbf,
+ 0x3e, 0x86, 0x55, 0xec, 0x69, 0x7f, 0xe3, 0x6f, 0x40, 0x55, 0x3c,
+ 0x5b, 0xb4, 0x68, 0x01, 0x64, 0x4a, 0x62, 0x7d, 0x33, 0x42, 0xf4,
+ 0xfc, 0x92, 0xb6, 0x1f, 0x03, 0x29, 0x0f, 0xb3, 0x81})),
+ std::make_pair(
+ 162,
+ std::vector<uint8_t>(
+ {0x72, 0xd3, 0x53, 0x99, 0x4b, 0x49, 0xd3, 0xe0, 0x31, 0x53, 0x92,
+ 0x9a, 0x1e, 0x4d, 0x4f, 0x18, 0x8e, 0xe5, 0x8a, 0xb9, 0xe7, 0x2e,
+ 0xe8, 0xe5, 0x12, 0xf2, 0x9b, 0xc7, 0x73, 0x91, 0x38, 0x19, 0xce,
+ 0x05, 0x7d, 0xdd, 0x70, 0x02, 0xc0, 0x43, 0x3e, 0xe0, 0xa1, 0x61,
+ 0x14, 0xe3, 0xd1, 0x56, 0xdd, 0x2c, 0x4a, 0x7e, 0x80, 0xee, 0x53,
+ 0x37, 0x8b, 0x86, 0x70, 0xf2, 0x3e, 0x33, 0xef, 0x56})),
+ std::make_pair(
+ 163,
+ std::vector<uint8_t>(
+ {0xc7, 0x0e, 0xf9, 0xbf, 0xd7, 0x75, 0xd4, 0x08, 0x17, 0x67, 0x37,
+ 0xa0, 0x73, 0x6d, 0x68, 0x51, 0x7c, 0xe1, 0xaa, 0xad, 0x7e, 0x81,
+ 0xa9, 0x3c, 0x8c, 0x1e, 0xd9, 0x67, 0xea, 0x21, 0x4f, 0x56, 0xc8,
+ 0xa3, 0x77, 0xb1, 0x76, 0x3e, 0x67, 0x66, 0x15, 0xb6, 0x0f, 0x39,
+ 0x88, 0x24, 0x1e, 0xae, 0x6e, 0xab, 0x96, 0x85, 0xa5, 0x12, 0x49,
+ 0x29, 0xd2, 0x81, 0x88, 0xf2, 0x9e, 0xab, 0x06, 0xf7})),
+ std::make_pair(
+ 164,
+ std::vector<uint8_t>(
+ {0xc2, 0x30, 0xf0, 0x80, 0x26, 0x79, 0xcb, 0x33, 0x82, 0x2e, 0xf8,
+ 0xb3, 0xb2, 0x1b, 0xf7, 0xa9, 0xa2, 0x89, 0x42, 0x09, 0x29, 0x01,
+ 0xd7, 0xda, 0xc3, 0x76, 0x03, 0x00, 0x83, 0x10, 0x26, 0xcf, 0x35,
+ 0x4c, 0x92, 0x32, 0xdf, 0x3e, 0x08, 0x4d, 0x99, 0x03, 0x13, 0x0c,
+ 0x60, 0x1f, 0x63, 0xc1, 0xf4, 0xa4, 0xa4, 0xb8, 0x10, 0x6e, 0x46,
+ 0x8c, 0xd4, 0x43, 0xbb, 0xe5, 0xa7, 0x34, 0xf4, 0x5f})),
+ std::make_pair(
+ 165,
+ std::vector<uint8_t>(
+ {0x6f, 0x43, 0x09, 0x4c, 0xaf, 0xb5, 0xeb, 0xf1, 0xf7, 0xa4, 0x93,
+ 0x7e, 0xc5, 0x0f, 0x56, 0xa4, 0xc9, 0xda, 0x30, 0x3c, 0xbb, 0x55,
+ 0xac, 0x1f, 0x27, 0xf1, 0xf1, 0x97, 0x6c, 0xd9, 0x6b, 0xed, 0xa9,
+ 0x46, 0x4f, 0x0e, 0x7b, 0x9c, 0x54, 0x62, 0x0b, 0x8a, 0x9f, 0xba,
+ 0x98, 0x31, 0x64, 0xb8, 0xbe, 0x35, 0x78, 0x42, 0x5a, 0x02, 0x4f,
+ 0x5f, 0xe1, 0x99, 0xc3, 0x63, 0x56, 0xb8, 0x89, 0x72})),
+ std::make_pair(
+ 166,
+ std::vector<uint8_t>(
+ {0x37, 0x45, 0x27, 0x3f, 0x4c, 0x38, 0x22, 0x5d, 0xb2, 0x33, 0x73,
+ 0x81, 0x87, 0x1a, 0x0c, 0x6a, 0xaf, 0xd3, 0xaf, 0x9b, 0x01, 0x8c,
+ 0x88, 0xaa, 0x02, 0x02, 0x58, 0x50, 0xa5, 0xdc, 0x3a, 0x42, 0xa1,
+ 0xa3, 0xe0, 0x3e, 0x56, 0xcb, 0xf1, 0xb0, 0x87, 0x6d, 0x63, 0xa4,
+ 0x41, 0xf1, 0xd2, 0x85, 0x6a, 0x39, 0xb8, 0x80, 0x1e, 0xb5, 0xaf,
+ 0x32, 0x52, 0x01, 0xc4, 0x15, 0xd6, 0x5e, 0x97, 0xfe})),
+ std::make_pair(
+ 167,
+ std::vector<uint8_t>(
+ {0xc5, 0x0c, 0x44, 0xcc, 0xa3, 0xec, 0x3e, 0xda, 0xae, 0x77, 0x9a,
+ 0x7e, 0x17, 0x94, 0x50, 0xeb, 0xdd, 0xa2, 0xf9, 0x70, 0x67, 0xc6,
+ 0x90, 0xaa, 0x6c, 0x5a, 0x4a, 0xc7, 0xc3, 0x01, 0x39, 0xbb, 0x27,
+ 0xc0, 0xdf, 0x4d, 0xb3, 0x22, 0x0e, 0x63, 0xcb, 0x11, 0x0d, 0x64,
+ 0xf3, 0x7f, 0xfe, 0x07, 0x8d, 0xb7, 0x26, 0x53, 0xe2, 0xda, 0xac,
+ 0xf9, 0x3a, 0xe3, 0xf0, 0xa2, 0xd1, 0xa7, 0xeb, 0x2e})),
+ std::make_pair(
+ 168,
+ std::vector<uint8_t>(
+ {0x8a, 0xef, 0x26, 0x3e, 0x38, 0x5c, 0xbc, 0x61, 0xe1, 0x9b, 0x28,
+ 0x91, 0x42, 0x43, 0x26, 0x2a, 0xf5, 0xaf, 0xe8, 0x72, 0x6a, 0xf3,
+ 0xce, 0x39, 0xa7, 0x9c, 0x27, 0x02, 0x8c, 0xf3, 0xec, 0xd3, 0xf8,
+ 0xd2, 0xdf, 0xd9, 0xcf, 0xc9, 0xad, 0x91, 0xb5, 0x8f, 0x6f, 0x20,
+ 0x77, 0x8f, 0xd5, 0xf0, 0x28, 0x94, 0xa3, 0xd9, 0x1c, 0x7d, 0x57,
+ 0xd1, 0xe4, 0xb8, 0x66, 0xa7, 0xf3, 0x64, 0xb6, 0xbe})),
+ std::make_pair(
+ 169,
+ std::vector<uint8_t>(
+ {0x28, 0x69, 0x61, 0x41, 0xde, 0x6e, 0x2d, 0x9b, 0xcb, 0x32, 0x35,
+ 0x57, 0x8a, 0x66, 0x16, 0x6c, 0x14, 0x48, 0xd3, 0xe9, 0x05, 0xa1,
+ 0xb4, 0x82, 0xd4, 0x23, 0xbe, 0x4b, 0xc5, 0x36, 0x9b, 0xc8, 0xc7,
+ 0x4d, 0xae, 0x0a, 0xcc, 0x9c, 0xc1, 0x23, 0xe1, 0xd8, 0xdd, 0xce,
+ 0x9f, 0x97, 0x91, 0x7e, 0x8c, 0x01, 0x9c, 0x55, 0x2d, 0xa3, 0x2d,
+ 0x39, 0xd2, 0x21, 0x9b, 0x9a, 0xbf, 0x0f, 0xa8, 0xc8})),
+ std::make_pair(
+ 170,
+ std::vector<uint8_t>(
+ {0x2f, 0xb9, 0xeb, 0x20, 0x85, 0x83, 0x01, 0x81, 0x90, 0x3a, 0x9d,
+ 0xaf, 0xe3, 0xdb, 0x42, 0x8e, 0xe1, 0x5b, 0xe7, 0x66, 0x22, 0x24,
+ 0xef, 0xd6, 0x43, 0x37, 0x1f, 0xb2, 0x56, 0x46, 0xae, 0xe7, 0x16,
+ 0xe5, 0x31, 0xec, 0xa6, 0x9b, 0x2b, 0xdc, 0x82, 0x33, 0xf1, 0xa8,
+ 0x08, 0x1f, 0xa4, 0x3d, 0xa1, 0x50, 0x03, 0x02, 0x97, 0x5a, 0x77,
+ 0xf4, 0x2f, 0xa5, 0x92, 0x13, 0x67, 0x10, 0xe9, 0xdc})),
+ std::make_pair(
+ 171,
+ std::vector<uint8_t>(
+ {0x66, 0xf9, 0xa7, 0x14, 0x3f, 0x7a, 0x33, 0x14, 0xa6, 0x69, 0xbf,
+ 0x2e, 0x24, 0xbb, 0xb3, 0x50, 0x14, 0x26, 0x1d, 0x63, 0x9f, 0x49,
+ 0x5b, 0x6c, 0x9c, 0x1f, 0x10, 0x4f, 0xe8, 0xe3, 0x20, 0xac, 0xa6,
+ 0x0d, 0x45, 0x50, 0xd6, 0x9d, 0x52, 0xed, 0xbd, 0x5a, 0x3c, 0xde,
+ 0xb4, 0x01, 0x4a, 0xe6, 0x5b, 0x1d, 0x87, 0xaa, 0x77, 0x0b, 0x69,
+ 0xae, 0x5c, 0x15, 0xf4, 0x33, 0x0b, 0x0b, 0x0a, 0xd8})),
+ std::make_pair(
+ 172,
+ std::vector<uint8_t>(
+ {0xf4, 0xc4, 0xdd, 0x1d, 0x59, 0x4c, 0x35, 0x65, 0xe3, 0xe2, 0x5c,
+ 0xa4, 0x3d, 0xad, 0x82, 0xf6, 0x2a, 0xbe, 0xa4, 0x83, 0x5e, 0xd4,
+ 0xcd, 0x81, 0x1b, 0xcd, 0x97, 0x5e, 0x46, 0x27, 0x98, 0x28, 0xd4,
+ 0x4d, 0x4c, 0x62, 0xc3, 0x67, 0x9f, 0x1b, 0x7f, 0x7b, 0x9d, 0xd4,
+ 0x57, 0x1d, 0x7b, 0x49, 0x55, 0x73, 0x47, 0xb8, 0xc5, 0x46, 0x0c,
+ 0xbd, 0xc1, 0xbe, 0xf6, 0x90, 0xfb, 0x2a, 0x08, 0xc0})),
+ std::make_pair(
+ 173,
+ std::vector<uint8_t>(
+ {0x8f, 0x1d, 0xc9, 0x64, 0x9c, 0x3a, 0x84, 0x55, 0x1f, 0x8f, 0x6e,
+ 0x91, 0xca, 0xc6, 0x82, 0x42, 0xa4, 0x3b, 0x1f, 0x8f, 0x32, 0x8e,
+ 0xe9, 0x22, 0x80, 0x25, 0x73, 0x87, 0xfa, 0x75, 0x59, 0xaa, 0x6d,
+ 0xb1, 0x2e, 0x4a, 0xea, 0xdc, 0x2d, 0x26, 0x09, 0x91, 0x78, 0x74,
+ 0x9c, 0x68, 0x64, 0xb3, 0x57, 0xf3, 0xf8, 0x3b, 0x2f, 0xb3, 0xef,
+ 0xa8, 0xd2, 0xa8, 0xdb, 0x05, 0x6b, 0xed, 0x6b, 0xcc})),
+ std::make_pair(
+ 174,
+ std::vector<uint8_t>(
+ {0x31, 0x39, 0xc1, 0xa7, 0xf9, 0x7a, 0xfd, 0x16, 0x75, 0xd4, 0x60,
+ 0xeb, 0xbc, 0x07, 0xf2, 0x72, 0x8a, 0xa1, 0x50, 0xdf, 0x84, 0x96,
+ 0x24, 0x51, 0x1e, 0xe0, 0x4b, 0x74, 0x3b, 0xa0, 0xa8, 0x33, 0x09,
+ 0x2f, 0x18, 0xc1, 0x2d, 0xc9, 0x1b, 0x4d, 0xd2, 0x43, 0xf3, 0x33,
+ 0x40, 0x2f, 0x59, 0xfe, 0x28, 0xab, 0xdb, 0xbb, 0xae, 0x30, 0x1e,
+ 0x7b, 0x65, 0x9c, 0x7a, 0x26, 0xd5, 0xc0, 0xf9, 0x79})),
+ std::make_pair(
+ 175,
+ std::vector<uint8_t>(
+ {0x06, 0xf9, 0x4a, 0x29, 0x96, 0x15, 0x8a, 0x81, 0x9f, 0xe3, 0x4c,
+ 0x40, 0xde, 0x3c, 0xf0, 0x37, 0x9f, 0xd9, 0xfb, 0x85, 0xb3, 0xe3,
+ 0x63, 0xba, 0x39, 0x26, 0xa0, 0xe7, 0xd9, 0x60, 0xe3, 0xf4, 0xc2,
+ 0xe0, 0xc7, 0x0c, 0x7c, 0xe0, 0xcc, 0xb2, 0xa6, 0x4f, 0xc2, 0x98,
+ 0x69, 0xf6, 0xe7, 0xab, 0x12, 0xbd, 0x4d, 0x3f, 0x14, 0xfc, 0xe9,
+ 0x43, 0x27, 0x90, 0x27, 0xe7, 0x85, 0xfb, 0x5c, 0x29})),
+ std::make_pair(
+ 176,
+ std::vector<uint8_t>(
+ {0xc2, 0x9c, 0x39, 0x9e, 0xf3, 0xee, 0xe8, 0x96, 0x1e, 0x87, 0x56,
+ 0x5c, 0x1c, 0xe2, 0x63, 0x92, 0x5f, 0xc3, 0xd0, 0xce, 0x26, 0x7d,
+ 0x13, 0xe4, 0x8d, 0xd9, 0xe7, 0x32, 0xee, 0x67, 0xb0, 0xf6, 0x9f,
+ 0xad, 0x56, 0x40, 0x1b, 0x0f, 0x10, 0xfc, 0xaa, 0xc1, 0x19, 0x20,
+ 0x10, 0x46, 0xcc, 0xa2, 0x8c, 0x5b, 0x14, 0xab, 0xde, 0xa3, 0x21,
+ 0x2a, 0xe6, 0x55, 0x62, 0xf7, 0xf1, 0x38, 0xdb, 0x3d})),
+ std::make_pair(
+ 177,
+ std::vector<uint8_t>(
+ {0x4c, 0xec, 0x4c, 0x9d, 0xf5, 0x2e, 0xef, 0x05, 0xc3, 0xf6, 0xfa,
+ 0xaa, 0x97, 0x91, 0xbc, 0x74, 0x45, 0x93, 0x71, 0x83, 0x22, 0x4e,
+ 0xcc, 0x37, 0xa1, 0xe5, 0x8d, 0x01, 0x32, 0xd3, 0x56, 0x17, 0x53,
+ 0x1d, 0x7e, 0x79, 0x5f, 0x52, 0xaf, 0x7b, 0x1e, 0xb9, 0xd1, 0x47,
+ 0xde, 0x12, 0x92, 0xd3, 0x45, 0xfe, 0x34, 0x18, 0x23, 0xf8, 0xe6,
+ 0xbc, 0x1e, 0x5b, 0xad, 0xca, 0x5c, 0x65, 0x61, 0x08})),
+ std::make_pair(
+ 178,
+ std::vector<uint8_t>(
+ {0x89, 0x8b, 0xfb, 0xae, 0x93, 0xb3, 0xe1, 0x8d, 0x00, 0x69, 0x7e,
+ 0xab, 0x7d, 0x97, 0x04, 0xfa, 0x36, 0xec, 0x33, 0x9d, 0x07, 0x61,
+ 0x31, 0xce, 0xfd, 0xf3, 0x0e, 0xdb, 0xe8, 0xd9, 0xcc, 0x81, 0xc3,
+ 0xa8, 0x0b, 0x12, 0x96, 0x59, 0xb1, 0x63, 0xa3, 0x23, 0xba, 0xb9,
+ 0x79, 0x3d, 0x4f, 0xee, 0xd9, 0x2d, 0x54, 0xda, 0xe9, 0x66, 0xc7,
+ 0x75, 0x29, 0x76, 0x4a, 0x09, 0xbe, 0x88, 0xdb, 0x45})),
+ std::make_pair(
+ 179,
+ std::vector<uint8_t>(
+ {0xee, 0x9b, 0xd0, 0x46, 0x9d, 0x3a, 0xaf, 0x4f, 0x14, 0x03, 0x5b,
+ 0xe4, 0x8a, 0x2c, 0x3b, 0x84, 0xd9, 0xb4, 0xb1, 0xff, 0xf1, 0xd9,
+ 0x45, 0xe1, 0xf1, 0xc1, 0xd3, 0x89, 0x80, 0xa9, 0x51, 0xbe, 0x19,
+ 0x7b, 0x25, 0xfe, 0x22, 0xc7, 0x31, 0xf2, 0x0a, 0xea, 0xcc, 0x93,
+ 0x0b, 0xa9, 0xc4, 0xa1, 0xf4, 0x76, 0x22, 0x27, 0x61, 0x7a, 0xd3,
+ 0x50, 0xfd, 0xab, 0xb4, 0xe8, 0x02, 0x73, 0xa0, 0xf4})),
+ std::make_pair(
+ 180,
+ std::vector<uint8_t>(
+ {0x3d, 0x4d, 0x31, 0x13, 0x30, 0x05, 0x81, 0xcd, 0x96, 0xac, 0xbf,
+ 0x09, 0x1c, 0x3d, 0x0f, 0x3c, 0x31, 0x01, 0x38, 0xcd, 0x69, 0x79,
+ 0xe6, 0x02, 0x6c, 0xde, 0x62, 0x3e, 0x2d, 0xd1, 0xb2, 0x4d, 0x4a,
+ 0x86, 0x38, 0xbe, 0xd1, 0x07, 0x33, 0x44, 0x78, 0x3a, 0xd0, 0x64,
+ 0x9c, 0xc6, 0x30, 0x5c, 0xce, 0xc0, 0x4b, 0xeb, 0x49, 0xf3, 0x1c,
+ 0x63, 0x30, 0x88, 0xa9, 0x9b, 0x65, 0x13, 0x02, 0x67})),
+ std::make_pair(
+ 181,
+ std::vector<uint8_t>(
+ {0x95, 0xc0, 0x59, 0x1a, 0xd9, 0x1f, 0x92, 0x1a, 0xc7, 0xbe, 0x6d,
+ 0x9c, 0xe3, 0x7e, 0x06, 0x63, 0xed, 0x80, 0x11, 0xc1, 0xcf, 0xd6,
+ 0xd0, 0x16, 0x2a, 0x55, 0x72, 0xe9, 0x43, 0x68, 0xba, 0xc0, 0x20,
+ 0x24, 0x48, 0x5e, 0x6a, 0x39, 0x85, 0x4a, 0xa4, 0x6f, 0xe3, 0x8e,
+ 0x97, 0xd6, 0xc6, 0xb1, 0x94, 0x7c, 0xd2, 0x72, 0xd8, 0x6b, 0x06,
+ 0xbb, 0x5b, 0x2f, 0x78, 0xb9, 0xb6, 0x8d, 0x55, 0x9d})),
+ std::make_pair(
+ 182,
+ std::vector<uint8_t>(
+ {0x22, 0x7b, 0x79, 0xde, 0xd3, 0x68, 0x15, 0x3b, 0xf4, 0x6c, 0x0a,
+ 0x3c, 0xa9, 0x78, 0xbf, 0xdb, 0xef, 0x31, 0xf3, 0x02, 0x4a, 0x56,
+ 0x65, 0x84, 0x24, 0x68, 0x49, 0x0b, 0x0f, 0xf7, 0x48, 0xae, 0x04,
+ 0xe7, 0x83, 0x2e, 0xd4, 0xc9, 0xf4, 0x9d, 0xe9, 0xb1, 0x70, 0x67,
+ 0x09, 0xd6, 0x23, 0xe5, 0xc8, 0xc1, 0x5e, 0x3c, 0xae, 0xca, 0xe8,
+ 0xd5, 0xe4, 0x33, 0x43, 0x0f, 0xf7, 0x2f, 0x20, 0xeb})),
+ std::make_pair(
+ 183,
+ std::vector<uint8_t>(
+ {0x5d, 0x34, 0xf3, 0x95, 0x2f, 0x01, 0x05, 0xee, 0xf8, 0x8a, 0xe8,
+ 0xb6, 0x4c, 0x6c, 0xe9, 0x5e, 0xbf, 0xad, 0xe0, 0xe0, 0x2c, 0x69,
+ 0xb0, 0x87, 0x62, 0xa8, 0x71, 0x2d, 0x2e, 0x49, 0x11, 0xad, 0x3f,
+ 0x94, 0x1f, 0xc4, 0x03, 0x4d, 0xc9, 0xb2, 0xe4, 0x79, 0xfd, 0xbc,
+ 0xd2, 0x79, 0xb9, 0x02, 0xfa, 0xf5, 0xd8, 0x38, 0xbb, 0x2e, 0x0c,
+ 0x64, 0x95, 0xd3, 0x72, 0xb5, 0xb7, 0x02, 0x98, 0x13})),
+ std::make_pair(
+ 184,
+ std::vector<uint8_t>(
+ {0x7f, 0x93, 0x9b, 0xf8, 0x35, 0x3a, 0xbc, 0xe4, 0x9e, 0x77, 0xf1,
+ 0x4f, 0x37, 0x50, 0xaf, 0x20, 0xb7, 0xb0, 0x39, 0x02, 0xe1, 0xa1,
+ 0xe7, 0xfb, 0x6a, 0xaf, 0x76, 0xd0, 0x25, 0x9c, 0xd4, 0x01, 0xa8,
+ 0x31, 0x90, 0xf1, 0x56, 0x40, 0xe7, 0x4f, 0x3e, 0x6c, 0x5a, 0x90,
+ 0xe8, 0x39, 0xc7, 0x82, 0x1f, 0x64, 0x74, 0x75, 0x7f, 0x75, 0xc7,
+ 0xbf, 0x90, 0x02, 0x08, 0x4d, 0xdc, 0x7a, 0x62, 0xdc})),
+ std::make_pair(
+ 185,
+ std::vector<uint8_t>(
+ {0x06, 0x2b, 0x61, 0xa2, 0xf9, 0xa3, 0x3a, 0x71, 0xd7, 0xd0, 0xa0,
+ 0x61, 0x19, 0x64, 0x4c, 0x70, 0xb0, 0x71, 0x6a, 0x50, 0x4d, 0xe7,
+ 0xe5, 0xe1, 0xbe, 0x49, 0xbd, 0x7b, 0x86, 0xe7, 0xed, 0x68, 0x17,
+ 0x71, 0x4f, 0x9f, 0x0f, 0xc3, 0x13, 0xd0, 0x61, 0x29, 0x59, 0x7e,
+ 0x9a, 0x22, 0x35, 0xec, 0x85, 0x21, 0xde, 0x36, 0xf7, 0x29, 0x0a,
+ 0x90, 0xcc, 0xfc, 0x1f, 0xfa, 0x6d, 0x0a, 0xee, 0x29})),
+ std::make_pair(
+ 186,
+ std::vector<uint8_t>(
+ {0xf2, 0x9e, 0x01, 0xee, 0xae, 0x64, 0x31, 0x1e, 0xb7, 0xf1, 0xc6,
+ 0x42, 0x2f, 0x94, 0x6b, 0xf7, 0xbe, 0xa3, 0x63, 0x79, 0x52, 0x3e,
+ 0x7b, 0x2b, 0xba, 0xba, 0x7d, 0x1d, 0x34, 0xa2, 0x2d, 0x5e, 0xa5,
+ 0xf1, 0xc5, 0xa0, 0x9d, 0x5c, 0xe1, 0xfe, 0x68, 0x2c, 0xce, 0xd9,
+ 0xa4, 0x79, 0x8d, 0x1a, 0x05, 0xb4, 0x6c, 0xd7, 0x2d, 0xff, 0x5c,
+ 0x1b, 0x35, 0x54, 0x40, 0xb2, 0xa2, 0xd4, 0x76, 0xbc})),
+ std::make_pair(
+ 187,
+ std::vector<uint8_t>(
+ {0xec, 0x38, 0xcd, 0x3b, 0xba, 0xb3, 0xef, 0x35, 0xd7, 0xcb, 0x6d,
+ 0x5c, 0x91, 0x42, 0x98, 0x35, 0x1d, 0x8a, 0x9d, 0xc9, 0x7f, 0xce,
+ 0xe0, 0x51, 0xa8, 0xa0, 0x2f, 0x58, 0xe3, 0xed, 0x61, 0x84, 0xd0,
+ 0xb7, 0x81, 0x0a, 0x56, 0x15, 0x41, 0x1a, 0xb1, 0xb9, 0x52, 0x09,
+ 0xc3, 0xc8, 0x10, 0x11, 0x4f, 0xde, 0xb2, 0x24, 0x52, 0x08, 0x4e,
+ 0x77, 0xf3, 0xf8, 0x47, 0xc6, 0xdb, 0xaa, 0xfe, 0x16})),
+ std::make_pair(
+ 188,
+ std::vector<uint8_t>(
+ {0xc2, 0xae, 0xf5, 0xe0, 0xca, 0x43, 0xe8, 0x26, 0x41, 0x56, 0x5b,
+ 0x8c, 0xb9, 0x43, 0xaa, 0x8b, 0xa5, 0x35, 0x50, 0xca, 0xef, 0x79,
+ 0x3b, 0x65, 0x32, 0xfa, 0xfa, 0xd9, 0x4b, 0x81, 0x60, 0x82, 0xf0,
+ 0x11, 0x3a, 0x3e, 0xa2, 0xf6, 0x36, 0x08, 0xab, 0x40, 0x43, 0x7e,
+ 0xcc, 0x0f, 0x02, 0x29, 0xcb, 0x8f, 0xa2, 0x24, 0xdc, 0xf1, 0xc4,
+ 0x78, 0xa6, 0x7d, 0x9b, 0x64, 0x16, 0x2b, 0x92, 0xd1})),
+ std::make_pair(
+ 189,
+ std::vector<uint8_t>(
+ {0x15, 0xf5, 0x34, 0xef, 0xff, 0x71, 0x05, 0xcd, 0x1c, 0x25, 0x4d,
+ 0x07, 0x4e, 0x27, 0xd5, 0x89, 0x8b, 0x89, 0x31, 0x3b, 0x7d, 0x36,
+ 0x6d, 0xc2, 0xd7, 0xd8, 0x71, 0x13, 0xfa, 0x7d, 0x53, 0xaa, 0xe1,
+ 0x3f, 0x6d, 0xba, 0x48, 0x7a, 0xd8, 0x10, 0x3d, 0x5e, 0x85, 0x4c,
+ 0x91, 0xfd, 0xb6, 0xe1, 0xe7, 0x4b, 0x2e, 0xf6, 0xd1, 0x43, 0x17,
+ 0x69, 0xc3, 0x07, 0x67, 0xdd, 0xe0, 0x67, 0xa3, 0x5c})),
+ std::make_pair(
+ 190,
+ std::vector<uint8_t>(
+ {0x89, 0xac, 0xbc, 0xa0, 0xb1, 0x69, 0x89, 0x7a, 0x0a, 0x27, 0x14,
+ 0xc2, 0xdf, 0x8c, 0x95, 0xb5, 0xb7, 0x9c, 0xb6, 0x93, 0x90, 0x14,
+ 0x2b, 0x7d, 0x60, 0x18, 0xbb, 0x3e, 0x30, 0x76, 0xb0, 0x99, 0xb7,
+ 0x9a, 0x96, 0x41, 0x52, 0xa9, 0xd9, 0x12, 0xb1, 0xb8, 0x64, 0x12,
+ 0xb7, 0xe3, 0x72, 0xe9, 0xce, 0xca, 0xd7, 0xf2, 0x5d, 0x4c, 0xba,
+ 0xb8, 0xa3, 0x17, 0xbe, 0x36, 0x49, 0x2a, 0x67, 0xd7})),
+ std::make_pair(
+ 191,
+ std::vector<uint8_t>(
+ {0xe3, 0xc0, 0x73, 0x91, 0x90, 0xed, 0x84, 0x9c, 0x9c, 0x96, 0x2f,
+ 0xd9, 0xdb, 0xb5, 0x5e, 0x20, 0x7e, 0x62, 0x4f, 0xca, 0xc1, 0xeb,
+ 0x41, 0x76, 0x91, 0x51, 0x54, 0x99, 0xee, 0xa8, 0xd8, 0x26, 0x7b,
+ 0x7e, 0x8f, 0x12, 0x87, 0xa6, 0x36, 0x33, 0xaf, 0x50, 0x11, 0xfd,
+ 0xe8, 0xc4, 0xdd, 0xf5, 0x5b, 0xfd, 0xf7, 0x22, 0xed, 0xf8, 0x88,
+ 0x31, 0x41, 0x4f, 0x2c, 0xfa, 0xed, 0x59, 0xcb, 0x9a})),
+ std::make_pair(
+ 192,
+ std::vector<uint8_t>(
+ {0x8d, 0x6c, 0xf8, 0x7c, 0x08, 0x38, 0x0d, 0x2d, 0x15, 0x06, 0xee,
+ 0xe4, 0x6f, 0xd4, 0x22, 0x2d, 0x21, 0xd8, 0xc0, 0x4e, 0x58, 0x5f,
+ 0xbf, 0xd0, 0x82, 0x69, 0xc9, 0x8f, 0x70, 0x28, 0x33, 0xa1, 0x56,
+ 0x32, 0x6a, 0x07, 0x24, 0x65, 0x64, 0x00, 0xee, 0x09, 0x35, 0x1d,
+ 0x57, 0xb4, 0x40, 0x17, 0x5e, 0x2a, 0x5d, 0xe9, 0x3c, 0xc5, 0xf8,
+ 0x0d, 0xb6, 0xda, 0xf8, 0x35, 0x76, 0xcf, 0x75, 0xfa})),
+ std::make_pair(
+ 193,
+ std::vector<uint8_t>(
+ {0xda, 0x24, 0xbe, 0xde, 0x38, 0x36, 0x66, 0xd5, 0x63, 0xee, 0xed,
+ 0x37, 0xf6, 0x31, 0x9b, 0xaf, 0x20, 0xd5, 0xc7, 0x5d, 0x16, 0x35,
+ 0xa6, 0xba, 0x5e, 0xf4, 0xcf, 0xa1, 0xac, 0x95, 0x48, 0x7e, 0x96,
+ 0xf8, 0xc0, 0x8a, 0xf6, 0x00, 0xaa, 0xb8, 0x7c, 0x98, 0x6e, 0xba,
+ 0xd4, 0x9f, 0xc7, 0x0a, 0x58, 0xb4, 0x89, 0x0b, 0x9c, 0x87, 0x6e,
+ 0x09, 0x10, 0x16, 0xda, 0xf4, 0x9e, 0x1d, 0x32, 0x2e})),
+ std::make_pair(
+ 194,
+ std::vector<uint8_t>(
+ {0xf9, 0xd1, 0xd1, 0xb1, 0xe8, 0x7e, 0xa7, 0xae, 0x75, 0x3a, 0x02,
+ 0x97, 0x50, 0xcc, 0x1c, 0xf3, 0xd0, 0x15, 0x7d, 0x41, 0x80, 0x5e,
+ 0x24, 0x5c, 0x56, 0x17, 0xbb, 0x93, 0x4e, 0x73, 0x2f, 0x0a, 0xe3,
+ 0x18, 0x0b, 0x78, 0xe0, 0x5b, 0xfe, 0x76, 0xc7, 0xc3, 0x05, 0x1e,
+ 0x3e, 0x3a, 0xc7, 0x8b, 0x9b, 0x50, 0xc0, 0x51, 0x42, 0x65, 0x7e,
+ 0x1e, 0x03, 0x21, 0x5d, 0x6e, 0xc7, 0xbf, 0xd0, 0xfc})),
+ std::make_pair(
+ 195,
+ std::vector<uint8_t>(
+ {0x11, 0xb7, 0xbc, 0x16, 0x68, 0x03, 0x20, 0x48, 0xaa, 0x43, 0x34,
+ 0x3d, 0xe4, 0x76, 0x39, 0x5e, 0x81, 0x4b, 0xbb, 0xc2, 0x23, 0x67,
+ 0x8d, 0xb9, 0x51, 0xa1, 0xb0, 0x3a, 0x02, 0x1e, 0xfa, 0xc9, 0x48,
+ 0xcf, 0xbe, 0x21, 0x5f, 0x97, 0xfe, 0x9a, 0x72, 0xa2, 0xf6, 0xbc,
+ 0x03, 0x9e, 0x39, 0x56, 0xbf, 0xa4, 0x17, 0xc1, 0xa9, 0xf1, 0x0d,
+ 0x6d, 0x7b, 0xa5, 0xd3, 0xd3, 0x2f, 0xf3, 0x23, 0xe5})),
+ std::make_pair(
+ 196,
+ std::vector<uint8_t>(
+ {0xb8, 0xd9, 0x00, 0x0e, 0x4f, 0xc2, 0xb0, 0x66, 0xed, 0xb9, 0x1a,
+ 0xfe, 0xe8, 0xe7, 0xeb, 0x0f, 0x24, 0xe3, 0xa2, 0x01, 0xdb, 0x8b,
+ 0x67, 0x93, 0xc0, 0x60, 0x85, 0x81, 0xe6, 0x28, 0xed, 0x0b, 0xcc,
+ 0x4e, 0x5a, 0xa6, 0x78, 0x79, 0x92, 0xa4, 0xbc, 0xc4, 0x4e, 0x28,
+ 0x80, 0x93, 0xe6, 0x3e, 0xe8, 0x3a, 0xbd, 0x0b, 0xc3, 0xec, 0x6d,
+ 0x09, 0x34, 0xa6, 0x74, 0xa4, 0xda, 0x13, 0x83, 0x8a})),
+ std::make_pair(
+ 197,
+ std::vector<uint8_t>(
+ {0xce, 0x32, 0x5e, 0x29, 0x4f, 0x9b, 0x67, 0x19, 0xd6, 0xb6, 0x12,
+ 0x78, 0x27, 0x6a, 0xe0, 0x6a, 0x25, 0x64, 0xc0, 0x3b, 0xb0, 0xb7,
+ 0x83, 0xfa, 0xfe, 0x78, 0x5b, 0xdf, 0x89, 0xc7, 0xd5, 0xac, 0xd8,
+ 0x3e, 0x78, 0x75, 0x6d, 0x30, 0x1b, 0x44, 0x56, 0x99, 0x02, 0x4e,
+ 0xae, 0xb7, 0x7b, 0x54, 0xd4, 0x77, 0x33, 0x6e, 0xc2, 0xa4, 0xf3,
+ 0x32, 0xf2, 0xb3, 0xf8, 0x87, 0x65, 0xdd, 0xb0, 0xc3})),
+ std::make_pair(
+ 198,
+ std::vector<uint8_t>(
+ {0x29, 0xac, 0xc3, 0x0e, 0x96, 0x03, 0xae, 0x2f, 0xcc, 0xf9, 0x0b,
+ 0xf9, 0x7e, 0x6c, 0xc4, 0x63, 0xeb, 0xe2, 0x8c, 0x1b, 0x2f, 0x9b,
+ 0x4b, 0x76, 0x5e, 0x70, 0x53, 0x7c, 0x25, 0xc7, 0x02, 0xa2, 0x9d,
+ 0xcb, 0xfb, 0xf1, 0x4c, 0x99, 0xc5, 0x43, 0x45, 0xba, 0x2b, 0x51,
+ 0xf1, 0x7b, 0x77, 0xb5, 0xf1, 0x5d, 0xb9, 0x2b, 0xba, 0xd8, 0xfa,
+ 0x95, 0xc4, 0x71, 0xf5, 0xd0, 0x70, 0xa1, 0x37, 0xcc})),
+ std::make_pair(
+ 199,
+ std::vector<uint8_t>(
+ {0x33, 0x79, 0xcb, 0xaa, 0xe5, 0x62, 0xa8, 0x7b, 0x4c, 0x04, 0x25,
+ 0x55, 0x0f, 0xfd, 0xd6, 0xbf, 0xe1, 0x20, 0x3f, 0x0d, 0x66, 0x6c,
+ 0xc7, 0xea, 0x09, 0x5b, 0xe4, 0x07, 0xa5, 0xdf, 0xe6, 0x1e, 0xe9,
+ 0x14, 0x41, 0xcd, 0x51, 0x54, 0xb3, 0xe5, 0x3b, 0x4f, 0x5f, 0xb3,
+ 0x1a, 0xd4, 0xc7, 0xa9, 0xad, 0x5c, 0x7a, 0xf4, 0xae, 0x67, 0x9a,
+ 0xa5, 0x1a, 0x54, 0x00, 0x3a, 0x54, 0xca, 0x6b, 0x2d})),
+ std::make_pair(
+ 200,
+ std::vector<uint8_t>(
+ {0x30, 0x95, 0xa3, 0x49, 0xd2, 0x45, 0x70, 0x8c, 0x7c, 0xf5, 0x50,
+ 0x11, 0x87, 0x03, 0xd7, 0x30, 0x2c, 0x27, 0xb6, 0x0a, 0xf5, 0xd4,
+ 0xe6, 0x7f, 0xc9, 0x78, 0xf8, 0xa4, 0xe6, 0x09, 0x53, 0xc7, 0xa0,
+ 0x4f, 0x92, 0xfc, 0xf4, 0x1a, 0xee, 0x64, 0x32, 0x1c, 0xcb, 0x70,
+ 0x7a, 0x89, 0x58, 0x51, 0x55, 0x2b, 0x1e, 0x37, 0xb0, 0x0b, 0xc5,
+ 0xe6, 0xb7, 0x2f, 0xa5, 0xbc, 0xef, 0x9e, 0x3f, 0xff})),
+ std::make_pair(
+ 201,
+ std::vector<uint8_t>(
+ {0x07, 0x26, 0x2d, 0x73, 0x8b, 0x09, 0x32, 0x1f, 0x4d, 0xbc, 0xce,
+ 0xc4, 0xbb, 0x26, 0xf4, 0x8c, 0xb0, 0xf0, 0xed, 0x24, 0x6c, 0xe0,
+ 0xb3, 0x1b, 0x9a, 0x6e, 0x7b, 0xc6, 0x83, 0x04, 0x9f, 0x1f, 0x3e,
+ 0x55, 0x45, 0xf2, 0x8c, 0xe9, 0x32, 0xdd, 0x98, 0x5c, 0x5a, 0xb0,
+ 0xf4, 0x3b, 0xd6, 0xde, 0x07, 0x70, 0x56, 0x0a, 0xf3, 0x29, 0x06,
+ 0x5e, 0xd2, 0xe4, 0x9d, 0x34, 0x62, 0x4c, 0x2c, 0xbb})),
+ std::make_pair(
+ 202,
+ std::vector<uint8_t>(
+ {0xb6, 0x40, 0x5e, 0xca, 0x8e, 0xe3, 0x31, 0x6c, 0x87, 0x06, 0x1c,
+ 0xc6, 0xec, 0x18, 0xdb, 0xa5, 0x3e, 0x6c, 0x25, 0x0c, 0x63, 0xba,
+ 0x1f, 0x3b, 0xae, 0x9e, 0x55, 0xdd, 0x34, 0x98, 0x03, 0x6a, 0xf0,
+ 0x8c, 0xd2, 0x72, 0xaa, 0x24, 0xd7, 0x13, 0xc6, 0x02, 0x0d, 0x77,
+ 0xab, 0x2f, 0x39, 0x19, 0xaf, 0x1a, 0x32, 0xf3, 0x07, 0x42, 0x06,
+ 0x18, 0xab, 0x97, 0xe7, 0x39, 0x53, 0x99, 0x4f, 0xb4})),
+ std::make_pair(
+ 203,
+ std::vector<uint8_t>(
+ {0x7e, 0xe6, 0x82, 0xf6, 0x31, 0x48, 0xee, 0x45, 0xf6, 0xe5, 0x31,
+ 0x5d, 0xa8, 0x1e, 0x5c, 0x6e, 0x55, 0x7c, 0x2c, 0x34, 0x64, 0x1f,
+ 0xc5, 0x09, 0xc7, 0xa5, 0x70, 0x10, 0x88, 0xc3, 0x8a, 0x74, 0x75,
+ 0x61, 0x68, 0xe2, 0xcd, 0x8d, 0x35, 0x1e, 0x88, 0xfd, 0x1a, 0x45,
+ 0x1f, 0x36, 0x0a, 0x01, 0xf5, 0xb2, 0x58, 0x0f, 0x9b, 0x5a, 0x2e,
+ 0x8c, 0xfc, 0x13, 0x8f, 0x3d, 0xd5, 0x9a, 0x3f, 0xfc})),
+ std::make_pair(
+ 204,
+ std::vector<uint8_t>(
+ {0x1d, 0x26, 0x3c, 0x17, 0x9d, 0x6b, 0x26, 0x8f, 0x6f, 0xa0, 0x16,
+ 0xf3, 0xa4, 0xf2, 0x9e, 0x94, 0x38, 0x91, 0x12, 0x5e, 0xd8, 0x59,
+ 0x3c, 0x81, 0x25, 0x60, 0x59, 0xf5, 0xa7, 0xb4, 0x4a, 0xf2, 0xdc,
+ 0xb2, 0x03, 0x0d, 0x17, 0x5c, 0x00, 0xe6, 0x2e, 0xca, 0xf7, 0xee,
+ 0x96, 0x68, 0x2a, 0xa0, 0x7a, 0xb2, 0x0a, 0x61, 0x10, 0x24, 0xa2,
+ 0x85, 0x32, 0xb1, 0xc2, 0x5b, 0x86, 0x65, 0x79, 0x02})),
+ std::make_pair(
+ 205,
+ std::vector<uint8_t>(
+ {0x10, 0x6d, 0x13, 0x2c, 0xbd, 0xb4, 0xcd, 0x25, 0x97, 0x81, 0x28,
+ 0x46, 0xe2, 0xbc, 0x1b, 0xf7, 0x32, 0xfe, 0xc5, 0xf0, 0xa5, 0xf6,
+ 0x5d, 0xbb, 0x39, 0xec, 0x4e, 0x6d, 0xc6, 0x4a, 0xb2, 0xce, 0x6d,
+ 0x24, 0x63, 0x0d, 0x0f, 0x15, 0xa8, 0x05, 0xc3, 0x54, 0x00, 0x25,
+ 0xd8, 0x4a, 0xfa, 0x98, 0xe3, 0x67, 0x03, 0xc3, 0xdb, 0xee, 0x71,
+ 0x3e, 0x72, 0xdd, 0xe8, 0x46, 0x5b, 0xc1, 0xbe, 0x7e})),
+ std::make_pair(
+ 206,
+ std::vector<uint8_t>(
+ {0x0e, 0x79, 0x96, 0x82, 0x26, 0x65, 0x06, 0x67, 0xa8, 0xd8, 0x62,
+ 0xea, 0x8d, 0xa4, 0x89, 0x1a, 0xf5, 0x6a, 0x4e, 0x3a, 0x8b, 0x6d,
+ 0x17, 0x50, 0xe3, 0x94, 0xf0, 0xde, 0xa7, 0x6d, 0x64, 0x0d, 0x85,
+ 0x07, 0x7b, 0xce, 0xc2, 0xcc, 0x86, 0x88, 0x6e, 0x50, 0x67, 0x51,
+ 0xb4, 0xf6, 0xa5, 0x83, 0x8f, 0x7f, 0x0b, 0x5f, 0xef, 0x76, 0x5d,
+ 0x9d, 0xc9, 0x0d, 0xcd, 0xcb, 0xaf, 0x07, 0x9f, 0x08})),
+ std::make_pair(
+ 207,
+ std::vector<uint8_t>(
+ {0x52, 0x11, 0x56, 0xa8, 0x2a, 0xb0, 0xc4, 0xe5, 0x66, 0xe5, 0x84,
+ 0x4d, 0x5e, 0x31, 0xad, 0x9a, 0xaf, 0x14, 0x4b, 0xbd, 0x5a, 0x46,
+ 0x4f, 0xdc, 0xa3, 0x4d, 0xbd, 0x57, 0x17, 0xe8, 0xff, 0x71, 0x1d,
+ 0x3f, 0xfe, 0xbb, 0xfa, 0x08, 0x5d, 0x67, 0xfe, 0x99, 0x6a, 0x34,
+ 0xf6, 0xd3, 0xe4, 0xe6, 0x0b, 0x13, 0x96, 0xbf, 0x4b, 0x16, 0x10,
+ 0xc2, 0x63, 0xbd, 0xbb, 0x83, 0x4d, 0x56, 0x08, 0x16})),
+ std::make_pair(
+ 208,
+ std::vector<uint8_t>(
+ {0x1a, 0xba, 0x88, 0xbe, 0xfc, 0x55, 0xbc, 0x25, 0xef, 0xbc, 0xe0,
+ 0x2d, 0xb8, 0xb9, 0x93, 0x3e, 0x46, 0xf5, 0x76, 0x61, 0xba, 0xea,
+ 0xbe, 0xb2, 0x1c, 0xc2, 0x57, 0x4d, 0x2a, 0x51, 0x8a, 0x3c, 0xba,
+ 0x5d, 0xc5, 0xa3, 0x8e, 0x49, 0x71, 0x34, 0x40, 0xb2, 0x5f, 0x9c,
+ 0x74, 0x4e, 0x75, 0xf6, 0xb8, 0x5c, 0x9d, 0x8f, 0x46, 0x81, 0xf6,
+ 0x76, 0x16, 0x0f, 0x61, 0x05, 0x35, 0x7b, 0x84, 0x06})),
+ std::make_pair(
+ 209,
+ std::vector<uint8_t>(
+ {0x5a, 0x99, 0x49, 0xfc, 0xb2, 0xc4, 0x73, 0xcd, 0xa9, 0x68, 0xac,
+ 0x1b, 0x5d, 0x08, 0x56, 0x6d, 0xc2, 0xd8, 0x16, 0xd9, 0x60, 0xf5,
+ 0x7e, 0x63, 0xb8, 0x98, 0xfa, 0x70, 0x1c, 0xf8, 0xeb, 0xd3, 0xf5,
+ 0x9b, 0x12, 0x4d, 0x95, 0xbf, 0xbb, 0xed, 0xc5, 0xf1, 0xcf, 0x0e,
+ 0x17, 0xd5, 0xea, 0xed, 0x0c, 0x02, 0xc5, 0x0b, 0x69, 0xd8, 0xa4,
+ 0x02, 0xca, 0xbc, 0xca, 0x44, 0x33, 0xb5, 0x1f, 0xd4})),
+ std::make_pair(
+ 210,
+ std::vector<uint8_t>(
+ {0xb0, 0xce, 0xad, 0x09, 0x80, 0x7c, 0x67, 0x2a, 0xf2, 0xeb, 0x2b,
+ 0x0f, 0x06, 0xdd, 0xe4, 0x6c, 0xf5, 0x37, 0x0e, 0x15, 0xa4, 0x09,
+ 0x6b, 0x1a, 0x7d, 0x7c, 0xbb, 0x36, 0xec, 0x31, 0xc2, 0x05, 0xfb,
+ 0xef, 0xca, 0x00, 0xb7, 0xa4, 0x16, 0x2f, 0xa8, 0x9f, 0xb4, 0xfb,
+ 0x3e, 0xb7, 0x8d, 0x79, 0x77, 0x0c, 0x23, 0xf4, 0x4e, 0x72, 0x06,
+ 0x66, 0x4c, 0xe3, 0xcd, 0x93, 0x1c, 0x29, 0x1e, 0x5d})),
+ std::make_pair(
+ 211,
+ std::vector<uint8_t>(
+ {0xbb, 0x66, 0x64, 0x93, 0x1e, 0xc9, 0x70, 0x44, 0xe4, 0x5b, 0x2a,
+ 0xe4, 0x20, 0xae, 0x1c, 0x55, 0x1a, 0x88, 0x74, 0xbc, 0x93, 0x7d,
+ 0x08, 0xe9, 0x69, 0x39, 0x9c, 0x39, 0x64, 0xeb, 0xdb, 0xa8, 0x34,
+ 0x6c, 0xdd, 0x5d, 0x09, 0xca, 0xaf, 0xe4, 0xc2, 0x8b, 0xa7, 0xec,
+ 0x78, 0x81, 0x91, 0xce, 0xca, 0x65, 0xdd, 0xd6, 0xf9, 0x5f, 0x18,
+ 0x58, 0x3e, 0x04, 0x0d, 0x0f, 0x30, 0xd0, 0x36, 0x4d})),
+ std::make_pair(
+ 212,
+ std::vector<uint8_t>(
+ {0x65, 0xbc, 0x77, 0x0a, 0x5f, 0xaa, 0x37, 0x92, 0x36, 0x98, 0x03,
+ 0x68, 0x3e, 0x84, 0x4b, 0x0b, 0xe7, 0xee, 0x96, 0xf2, 0x9f, 0x6d,
+ 0x6a, 0x35, 0x56, 0x80, 0x06, 0xbd, 0x55, 0x90, 0xf9, 0xa4, 0xef,
+ 0x63, 0x9b, 0x7a, 0x80, 0x61, 0xc7, 0xb0, 0x42, 0x4b, 0x66, 0xb6,
+ 0x0a, 0xc3, 0x4a, 0xf3, 0x11, 0x99, 0x05, 0xf3, 0x3a, 0x9d, 0x8c,
+ 0x3a, 0xe1, 0x83, 0x82, 0xca, 0x9b, 0x68, 0x99, 0x00})),
+ std::make_pair(
+ 213,
+ std::vector<uint8_t>(
+ {0xea, 0x9b, 0x4d, 0xca, 0x33, 0x33, 0x36, 0xaa, 0xf8, 0x39, 0xa4,
+ 0x5c, 0x6e, 0xaa, 0x48, 0xb8, 0xcb, 0x4c, 0x7d, 0xda, 0xbf, 0xfe,
+ 0xa4, 0xf6, 0x43, 0xd6, 0x35, 0x7e, 0xa6, 0x62, 0x8a, 0x48, 0x0a,
+ 0x5b, 0x45, 0xf2, 0xb0, 0x52, 0xc1, 0xb0, 0x7d, 0x1f, 0xed, 0xca,
+ 0x91, 0x8b, 0x6f, 0x11, 0x39, 0xd8, 0x0f, 0x74, 0xc2, 0x45, 0x10,
+ 0xdc, 0xba, 0xa4, 0xbe, 0x70, 0xea, 0xcc, 0x1b, 0x06})),
+ std::make_pair(
+ 214,
+ std::vector<uint8_t>(
+ {0xe6, 0x34, 0x2f, 0xb4, 0xa7, 0x80, 0xad, 0x97, 0x5d, 0x0e, 0x24,
+ 0xbc, 0xe1, 0x49, 0x98, 0x9b, 0x91, 0xd3, 0x60, 0x55, 0x7e, 0x87,
+ 0x99, 0x4f, 0x6b, 0x45, 0x7b, 0x89, 0x55, 0x75, 0xcc, 0x02, 0xd0,
+ 0xc1, 0x5b, 0xad, 0x3c, 0xe7, 0x57, 0x7f, 0x4c, 0x63, 0x92, 0x7f,
+ 0xf1, 0x3f, 0x3e, 0x38, 0x1f, 0xf7, 0xe7, 0x2b, 0xdb, 0xe7, 0x45,
+ 0x32, 0x48, 0x44, 0xa9, 0xd2, 0x7e, 0x3f, 0x1c, 0x01})),
+ std::make_pair(
+ 215,
+ std::vector<uint8_t>(
+ {0x3e, 0x20, 0x9c, 0x9b, 0x33, 0xe8, 0xe4, 0x61, 0x17, 0x8a, 0xb4,
+ 0x6b, 0x1c, 0x64, 0xb4, 0x9a, 0x07, 0xfb, 0x74, 0x5f, 0x1c, 0x8b,
+ 0xc9, 0x5f, 0xbf, 0xb9, 0x4c, 0x6b, 0x87, 0xc6, 0x95, 0x16, 0x65,
+ 0x1b, 0x26, 0x4e, 0xf9, 0x80, 0x93, 0x7f, 0xad, 0x41, 0x23, 0x8b,
+ 0x91, 0xdd, 0xc0, 0x11, 0xa5, 0xdd, 0x77, 0x7c, 0x7e, 0xfd, 0x44,
+ 0x94, 0xb4, 0xb6, 0xec, 0xd3, 0xa9, 0xc2, 0x2a, 0xc0})),
+ std::make_pair(
+ 216,
+ std::vector<uint8_t>(
+ {0xfd, 0x6a, 0x3d, 0x5b, 0x18, 0x75, 0xd8, 0x04, 0x86, 0xd6, 0xe6,
+ 0x96, 0x94, 0xa5, 0x6d, 0xbb, 0x04, 0xa9, 0x9a, 0x4d, 0x05, 0x1f,
+ 0x15, 0xdb, 0x26, 0x89, 0x77, 0x6b, 0xa1, 0xc4, 0x88, 0x2e, 0x6d,
+ 0x46, 0x2a, 0x60, 0x3b, 0x70, 0x15, 0xdc, 0x9f, 0x4b, 0x74, 0x50,
+ 0xf0, 0x53, 0x94, 0x30, 0x3b, 0x86, 0x52, 0xcf, 0xb4, 0x04, 0xa2,
+ 0x66, 0x96, 0x2c, 0x41, 0xba, 0xe6, 0xe1, 0x8a, 0x94})),
+ std::make_pair(
+ 217,
+ std::vector<uint8_t>(
+ {0x95, 0x1e, 0x27, 0x51, 0x7e, 0x6b, 0xad, 0x9e, 0x41, 0x95, 0xfc,
+ 0x86, 0x71, 0xde, 0xe3, 0xe7, 0xe9, 0xbe, 0x69, 0xce, 0xe1, 0x42,
+ 0x2c, 0xb9, 0xfe, 0xcf, 0xce, 0x0d, 0xba, 0x87, 0x5f, 0x7b, 0x31,
+ 0x0b, 0x93, 0xee, 0x3a, 0x3d, 0x55, 0x8f, 0x94, 0x1f, 0x63, 0x5f,
+ 0x66, 0x8f, 0xf8, 0x32, 0xd2, 0xc1, 0xd0, 0x33, 0xc5, 0xe2, 0xf0,
+ 0x99, 0x7e, 0x4c, 0x66, 0xf1, 0x47, 0x34, 0x4e, 0x02})),
+ std::make_pair(
+ 218,
+ std::vector<uint8_t>(
+ {0x8e, 0xba, 0x2f, 0x87, 0x4f, 0x1a, 0xe8, 0x40, 0x41, 0x90, 0x3c,
+ 0x7c, 0x42, 0x53, 0xc8, 0x22, 0x92, 0x53, 0x0f, 0xc8, 0x50, 0x95,
+ 0x50, 0xbf, 0xdc, 0x34, 0xc9, 0x5c, 0x7e, 0x28, 0x89, 0xd5, 0x65,
+ 0x0b, 0x0a, 0xd8, 0xcb, 0x98, 0x8e, 0x5c, 0x48, 0x94, 0xcb, 0x87,
+ 0xfb, 0xfb, 0xb1, 0x96, 0x12, 0xea, 0x93, 0xcc, 0xc4, 0xc5, 0xca,
+ 0xd1, 0x71, 0x58, 0xb9, 0x76, 0x34, 0x64, 0xb4, 0x92})),
+ std::make_pair(
+ 219,
+ std::vector<uint8_t>(
+ {0x16, 0xf7, 0x12, 0xea, 0xa1, 0xb7, 0xc6, 0x35, 0x47, 0x19, 0xa8,
+ 0xe7, 0xdb, 0xdf, 0xaf, 0x55, 0xe4, 0x06, 0x3a, 0x4d, 0x27, 0x7d,
+ 0x94, 0x75, 0x50, 0x01, 0x9b, 0x38, 0xdf, 0xb5, 0x64, 0x83, 0x09,
+ 0x11, 0x05, 0x7d, 0x50, 0x50, 0x61, 0x36, 0xe2, 0x39, 0x4c, 0x3b,
+ 0x28, 0x94, 0x5c, 0xc9, 0x64, 0x96, 0x7d, 0x54, 0xe3, 0x00, 0x0c,
+ 0x21, 0x81, 0x62, 0x6c, 0xfb, 0x9b, 0x73, 0xef, 0xd2})),
+ std::make_pair(
+ 220,
+ std::vector<uint8_t>(
+ {0xc3, 0x96, 0x39, 0xe7, 0xd5, 0xc7, 0xfb, 0x8c, 0xdd, 0x0f, 0xd3,
+ 0xe6, 0xa5, 0x20, 0x96, 0x03, 0x94, 0x37, 0x12, 0x2f, 0x21, 0xc7,
+ 0x8f, 0x16, 0x79, 0xce, 0xa9, 0xd7, 0x8a, 0x73, 0x4c, 0x56, 0xec,
+ 0xbe, 0xb2, 0x86, 0x54, 0xb4, 0xf1, 0x8e, 0x34, 0x2c, 0x33, 0x1f,
+ 0x6f, 0x72, 0x29, 0xec, 0x4b, 0x4b, 0xc2, 0x81, 0xb2, 0xd8, 0x0a,
+ 0x6e, 0xb5, 0x00, 0x43, 0xf3, 0x17, 0x96, 0xc8, 0x8c})),
+ std::make_pair(
+ 221,
+ std::vector<uint8_t>(
+ {0x72, 0xd0, 0x81, 0xaf, 0x99, 0xf8, 0xa1, 0x73, 0xdc, 0xc9, 0xa0,
+ 0xac, 0x4e, 0xb3, 0x55, 0x74, 0x05, 0x63, 0x9a, 0x29, 0x08, 0x4b,
+ 0x54, 0xa4, 0x01, 0x72, 0x91, 0x2a, 0x2f, 0x8a, 0x39, 0x51, 0x29,
+ 0xd5, 0x53, 0x6f, 0x09, 0x18, 0xe9, 0x02, 0xf9, 0xe8, 0xfa, 0x60,
+ 0x00, 0x99, 0x5f, 0x41, 0x68, 0xdd, 0xc5, 0xf8, 0x93, 0x01, 0x1b,
+ 0xe6, 0xa0, 0xdb, 0xc9, 0xb8, 0xa1, 0xa3, 0xf5, 0xbb})),
+ std::make_pair(
+ 222,
+ std::vector<uint8_t>(
+ {0xc1, 0x1a, 0xa8, 0x1e, 0x5e, 0xfd, 0x24, 0xd5, 0xfc, 0x27, 0xee,
+ 0x58, 0x6c, 0xfd, 0x88, 0x47, 0xfb, 0xb0, 0xe2, 0x76, 0x01, 0xcc,
+ 0xec, 0xe5, 0xec, 0xca, 0x01, 0x98, 0xe3, 0xc7, 0x76, 0x53, 0x93,
+ 0xbb, 0x74, 0x45, 0x7c, 0x7e, 0x7a, 0x27, 0xeb, 0x91, 0x70, 0x35,
+ 0x0e, 0x1f, 0xb5, 0x38, 0x57, 0x17, 0x75, 0x06, 0xbe, 0x3e, 0x76,
+ 0x2c, 0xc0, 0xf1, 0x4d, 0x8c, 0x3a, 0xfe, 0x90, 0x77})),
+ std::make_pair(
+ 223,
+ std::vector<uint8_t>(
+ {0xc2, 0x8f, 0x21, 0x50, 0xb4, 0x52, 0xe6, 0xc0, 0xc4, 0x24, 0xbc,
+ 0xde, 0x6f, 0x8d, 0x72, 0x00, 0x7f, 0x93, 0x10, 0xfe, 0xd7, 0xf2,
+ 0xf8, 0x7d, 0xe0, 0xdb, 0xb6, 0x4f, 0x44, 0x79, 0xd6, 0xc1, 0x44,
+ 0x1b, 0xa6, 0x6f, 0x44, 0xb2, 0xac, 0xce, 0xe6, 0x16, 0x09, 0x17,
+ 0x7e, 0xd3, 0x40, 0x12, 0x8b, 0x40, 0x7e, 0xce, 0xc7, 0xc6, 0x4b,
+ 0xbe, 0x50, 0xd6, 0x3d, 0x22, 0xd8, 0x62, 0x77, 0x27})),
+ std::make_pair(
+ 224,
+ std::vector<uint8_t>(
+ {0xf6, 0x3d, 0x88, 0x12, 0x28, 0x77, 0xec, 0x30, 0xb8, 0xc8, 0xb0,
+ 0x0d, 0x22, 0xe8, 0x90, 0x00, 0xa9, 0x66, 0x42, 0x61, 0x12, 0xbd,
+ 0x44, 0x16, 0x6e, 0x2f, 0x52, 0x5b, 0x76, 0x9c, 0xcb, 0xe9, 0xb2,
+ 0x86, 0xd4, 0x37, 0xa0, 0x12, 0x91, 0x30, 0xdd, 0xe1, 0xa8, 0x6c,
+ 0x43, 0xe0, 0x4b, 0xed, 0xb5, 0x94, 0xe6, 0x71, 0xd9, 0x82, 0x83,
+ 0xaf, 0xe6, 0x4c, 0xe3, 0x31, 0xde, 0x98, 0x28, 0xfd})),
+ std::make_pair(
+ 225,
+ std::vector<uint8_t>(
+ {0x34, 0x8b, 0x05, 0x32, 0x88, 0x0b, 0x88, 0xa6, 0x61, 0x4a, 0x8d,
+ 0x74, 0x08, 0xc3, 0xf9, 0x13, 0x35, 0x7f, 0xbb, 0x60, 0xe9, 0x95,
+ 0xc6, 0x02, 0x05, 0xbe, 0x91, 0x39, 0xe7, 0x49, 0x98, 0xae, 0xde,
+ 0x7f, 0x45, 0x81, 0xe4, 0x2f, 0x6b, 0x52, 0x69, 0x8f, 0x7f, 0xa1,
+ 0x21, 0x97, 0x08, 0xc1, 0x44, 0x98, 0x06, 0x7f, 0xd1, 0xe0, 0x95,
+ 0x02, 0xde, 0x83, 0xa7, 0x7d, 0xd2, 0x81, 0x15, 0x0c})),
+ std::make_pair(
+ 226,
+ std::vector<uint8_t>(
+ {0x51, 0x33, 0xdc, 0x8b, 0xef, 0x72, 0x53, 0x59, 0xdf, 0xf5, 0x97,
+ 0x92, 0xd8, 0x5e, 0xaf, 0x75, 0xb7, 0xe1, 0xdc, 0xd1, 0x97, 0x8b,
+ 0x01, 0xc3, 0x5b, 0x1b, 0x85, 0xfc, 0xeb, 0xc6, 0x33, 0x88, 0xad,
+ 0x99, 0xa1, 0x7b, 0x63, 0x46, 0xa2, 0x17, 0xdc, 0x1a, 0x96, 0x22,
+ 0xeb, 0xd1, 0x22, 0xec, 0xf6, 0x91, 0x3c, 0x4d, 0x31, 0xa6, 0xb5,
+ 0x2a, 0x69, 0x5b, 0x86, 0xaf, 0x00, 0xd7, 0x41, 0xa0})),
+ std::make_pair(
+ 227,
+ std::vector<uint8_t>(
+ {0x27, 0x53, 0xc4, 0xc0, 0xe9, 0x8e, 0xca, 0xd8, 0x06, 0xe8, 0x87,
+ 0x80, 0xec, 0x27, 0xfc, 0xcd, 0x0f, 0x5c, 0x1a, 0xb5, 0x47, 0xf9,
+ 0xe4, 0xbf, 0x16, 0x59, 0xd1, 0x92, 0xc2, 0x3a, 0xa2, 0xcc, 0x97,
+ 0x1b, 0x58, 0xb6, 0x80, 0x25, 0x80, 0xba, 0xef, 0x8a, 0xdc, 0x3b,
+ 0x77, 0x6e, 0xf7, 0x08, 0x6b, 0x25, 0x45, 0xc2, 0x98, 0x7f, 0x34,
+ 0x8e, 0xe3, 0x71, 0x9c, 0xde, 0xf2, 0x58, 0xc4, 0x03})),
+ std::make_pair(
+ 228,
+ std::vector<uint8_t>(
+ {0xb1, 0x66, 0x35, 0x73, 0xce, 0x4b, 0x9d, 0x8c, 0xae, 0xfc, 0x86,
+ 0x50, 0x12, 0xf3, 0xe3, 0x97, 0x14, 0xb9, 0x89, 0x8a, 0x5d, 0xa6,
+ 0xce, 0x17, 0xc2, 0x5a, 0x6a, 0x47, 0x93, 0x1a, 0x9d, 0xdb, 0x9b,
+ 0xbe, 0x98, 0xad, 0xaa, 0x55, 0x3b, 0xee, 0xd4, 0x36, 0xe8, 0x95,
+ 0x78, 0x45, 0x54, 0x16, 0xc2, 0xa5, 0x2a, 0x52, 0x5c, 0xf2, 0x86,
+ 0x2b, 0x8d, 0x1d, 0x49, 0xa2, 0x53, 0x1b, 0x73, 0x91})),
+ std::make_pair(
+ 229,
+ std::vector<uint8_t>(
+ {0x64, 0xf5, 0x8b, 0xd6, 0xbf, 0xc8, 0x56, 0xf5, 0xe8, 0x73, 0xb2,
+ 0xa2, 0x95, 0x6e, 0xa0, 0xed, 0xa0, 0xd6, 0xdb, 0x0d, 0xa3, 0x9c,
+ 0x8c, 0x7f, 0xc6, 0x7c, 0x9f, 0x9f, 0xee, 0xfc, 0xff, 0x30, 0x72,
+ 0xcd, 0xf9, 0xe6, 0xea, 0x37, 0xf6, 0x9a, 0x44, 0xf0, 0xc6, 0x1a,
+ 0xa0, 0xda, 0x36, 0x93, 0xc2, 0xdb, 0x5b, 0x54, 0x96, 0x0c, 0x02,
+ 0x81, 0xa0, 0x88, 0x15, 0x1d, 0xb4, 0x2b, 0x11, 0xe8})),
+ std::make_pair(
+ 230,
+ std::vector<uint8_t>(
+ {0x07, 0x64, 0xc7, 0xbe, 0x28, 0x12, 0x5d, 0x90, 0x65, 0xc4, 0xb9,
+ 0x8a, 0x69, 0xd6, 0x0a, 0xed, 0xe7, 0x03, 0x54, 0x7c, 0x66, 0xa1,
+ 0x2e, 0x17, 0xe1, 0xc6, 0x18, 0x99, 0x41, 0x32, 0xf5, 0xef, 0x82,
+ 0x48, 0x2c, 0x1e, 0x3f, 0xe3, 0x14, 0x6c, 0xc6, 0x53, 0x76, 0xcc,
+ 0x10, 0x9f, 0x01, 0x38, 0xed, 0x9a, 0x80, 0xe4, 0x9f, 0x1f, 0x3c,
+ 0x7d, 0x61, 0x0d, 0x2f, 0x24, 0x32, 0xf2, 0x06, 0x05})),
+ std::make_pair(
+ 231,
+ std::vector<uint8_t>(
+ {0xf7, 0x48, 0x78, 0x43, 0x98, 0xa2, 0xff, 0x03, 0xeb, 0xeb, 0x07,
+ 0xe1, 0x55, 0xe6, 0x61, 0x16, 0xa8, 0x39, 0x74, 0x1a, 0x33, 0x6e,
+ 0x32, 0xda, 0x71, 0xec, 0x69, 0x60, 0x01, 0xf0, 0xad, 0x1b, 0x25,
+ 0xcd, 0x48, 0xc6, 0x9c, 0xfc, 0xa7, 0x26, 0x5e, 0xca, 0x1d, 0xd7,
+ 0x19, 0x04, 0xa0, 0xce, 0x74, 0x8a, 0xc4, 0x12, 0x4f, 0x35, 0x71,
+ 0x07, 0x6d, 0xfa, 0x71, 0x16, 0xa9, 0xcf, 0x00, 0xe9})),
+ std::make_pair(
+ 232,
+ std::vector<uint8_t>(
+ {0x3f, 0x0d, 0xbc, 0x01, 0x86, 0xbc, 0xeb, 0x6b, 0x78, 0x5b, 0xa7,
+ 0x8d, 0x2a, 0x2a, 0x01, 0x3c, 0x91, 0x0b, 0xe1, 0x57, 0xbd, 0xaf,
+ 0xfa, 0xe8, 0x1b, 0xb6, 0x66, 0x3b, 0x1a, 0x73, 0x72, 0x2f, 0x7f,
+ 0x12, 0x28, 0x79, 0x5f, 0x3e, 0xca, 0xda, 0x87, 0xcf, 0x6e, 0xf0,
+ 0x07, 0x84, 0x74, 0xaf, 0x73, 0xf3, 0x1e, 0xca, 0x0c, 0xc2, 0x00,
+ 0xed, 0x97, 0x5b, 0x68, 0x93, 0xf7, 0x61, 0xcb, 0x6d})),
+ std::make_pair(
+ 233,
+ std::vector<uint8_t>(
+ {0xd4, 0x76, 0x2c, 0xd4, 0x59, 0x98, 0x76, 0xca, 0x75, 0xb2, 0xb8,
+ 0xfe, 0x24, 0x99, 0x44, 0xdb, 0xd2, 0x7a, 0xce, 0x74, 0x1f, 0xda,
+ 0xb9, 0x36, 0x16, 0xcb, 0xc6, 0xe4, 0x25, 0x46, 0x0f, 0xeb, 0x51,
+ 0xd4, 0xe7, 0xad, 0xcc, 0x38, 0x18, 0x0e, 0x7f, 0xc4, 0x7c, 0x89,
+ 0x02, 0x4a, 0x7f, 0x56, 0x19, 0x1a, 0xdb, 0x87, 0x8d, 0xfd, 0xe4,
+ 0xea, 0xd6, 0x22, 0x23, 0xf5, 0xa2, 0x61, 0x0e, 0xfe})),
+ std::make_pair(
+ 234,
+ std::vector<uint8_t>(
+ {0xcd, 0x36, 0xb3, 0xd5, 0xb4, 0xc9, 0x1b, 0x90, 0xfc, 0xbb, 0xa7,
+ 0x95, 0x13, 0xcf, 0xee, 0x19, 0x07, 0xd8, 0x64, 0x5a, 0x16, 0x2a,
+ 0xfd, 0x0c, 0xd4, 0xcf, 0x41, 0x92, 0xd4, 0xa5, 0xf4, 0xc8, 0x92,
+ 0x18, 0x3a, 0x8e, 0xac, 0xdb, 0x2b, 0x6b, 0x6a, 0x9d, 0x9a, 0xa8,
+ 0xc1, 0x1a, 0xc1, 0xb2, 0x61, 0xb3, 0x80, 0xdb, 0xee, 0x24, 0xca,
+ 0x46, 0x8f, 0x1b, 0xfd, 0x04, 0x3c, 0x58, 0xee, 0xfe})),
+ std::make_pair(
+ 235,
+ std::vector<uint8_t>(
+ {0x98, 0x59, 0x34, 0x52, 0x28, 0x16, 0x61, 0xa5, 0x3c, 0x48, 0xa9,
+ 0xd8, 0xcd, 0x79, 0x08, 0x26, 0xc1, 0xa1, 0xce, 0x56, 0x77, 0x38,
+ 0x05, 0x3d, 0x0b, 0xee, 0x4a, 0x91, 0xa3, 0xd5, 0xbd, 0x92, 0xee,
+ 0xfd, 0xba, 0xbe, 0xbe, 0x32, 0x04, 0xf2, 0x03, 0x1c, 0xa5, 0xf7,
+ 0x81, 0xbd, 0xa9, 0x9e, 0xf5, 0xd8, 0xae, 0x56, 0xe5, 0xb0, 0x4a,
+ 0x9e, 0x1e, 0xcd, 0x21, 0xb0, 0xeb, 0x05, 0xd3, 0xe1})),
+ std::make_pair(
+ 236,
+ std::vector<uint8_t>(
+ {0x77, 0x1f, 0x57, 0xdd, 0x27, 0x75, 0xcc, 0xda, 0xb5, 0x59, 0x21,
+ 0xd3, 0xe8, 0xe3, 0x0c, 0xcf, 0x48, 0x4d, 0x61, 0xfe, 0x1c, 0x1b,
+ 0x9c, 0x2a, 0xe8, 0x19, 0xd0, 0xfb, 0x2a, 0x12, 0xfa, 0xb9, 0xbe,
+ 0x70, 0xc4, 0xa7, 0xa1, 0x38, 0xda, 0x84, 0xe8, 0x28, 0x04, 0x35,
+ 0xda, 0xad, 0xe5, 0xbb, 0xe6, 0x6a, 0xf0, 0x83, 0x6a, 0x15, 0x4f,
+ 0x81, 0x7f, 0xb1, 0x7f, 0x33, 0x97, 0xe7, 0x25, 0xa3})),
+ std::make_pair(
+ 237,
+ std::vector<uint8_t>(
+ {0xc6, 0x08, 0x97, 0xc6, 0xf8, 0x28, 0xe2, 0x1f, 0x16, 0xfb, 0xb5,
+ 0xf1, 0x5b, 0x32, 0x3f, 0x87, 0xb6, 0xc8, 0x95, 0x5e, 0xab, 0xf1,
+ 0xd3, 0x80, 0x61, 0xf7, 0x07, 0xf6, 0x08, 0xab, 0xdd, 0x99, 0x3f,
+ 0xac, 0x30, 0x70, 0x63, 0x3e, 0x28, 0x6c, 0xf8, 0x33, 0x9c, 0xe2,
+ 0x95, 0xdd, 0x35, 0x2d, 0xf4, 0xb4, 0xb4, 0x0b, 0x2f, 0x29, 0xda,
+ 0x1d, 0xd5, 0x0b, 0x3a, 0x05, 0xd0, 0x79, 0xe6, 0xbb})),
+ std::make_pair(
+ 238,
+ std::vector<uint8_t>(
+ {0x82, 0x10, 0xcd, 0x2c, 0x2d, 0x3b, 0x13, 0x5c, 0x2c, 0xf0, 0x7f,
+ 0xa0, 0xd1, 0x43, 0x3c, 0xd7, 0x71, 0xf3, 0x25, 0xd0, 0x75, 0xc6,
+ 0x46, 0x9d, 0x9c, 0x7f, 0x1b, 0xa0, 0x94, 0x3c, 0xd4, 0xab, 0x09,
+ 0x80, 0x8c, 0xab, 0xf4, 0xac, 0xb9, 0xce, 0x5b, 0xb8, 0x8b, 0x49,
+ 0x89, 0x29, 0xb4, 0xb8, 0x47, 0xf6, 0x81, 0xad, 0x2c, 0x49, 0x0d,
+ 0x04, 0x2d, 0xb2, 0xae, 0xc9, 0x42, 0x14, 0xb0, 0x6b})),
+ std::make_pair(
+ 239,
+ std::vector<uint8_t>(
+ {0x1d, 0x4e, 0xdf, 0xff, 0xd8, 0xfd, 0x80, 0xf7, 0xe4, 0x10, 0x78,
+ 0x40, 0xfa, 0x3a, 0xa3, 0x1e, 0x32, 0x59, 0x84, 0x91, 0xe4, 0xaf,
+ 0x70, 0x13, 0xc1, 0x97, 0xa6, 0x5b, 0x7f, 0x36, 0xdd, 0x3a, 0xc4,
+ 0xb4, 0x78, 0x45, 0x61, 0x11, 0xcd, 0x43, 0x09, 0xd9, 0x24, 0x35,
+ 0x10, 0x78, 0x2f, 0xa3, 0x1b, 0x7c, 0x4c, 0x95, 0xfa, 0x95, 0x15,
+ 0x20, 0xd0, 0x20, 0xeb, 0x7e, 0x5c, 0x36, 0xe4, 0xef})),
+ std::make_pair(
+ 240,
+ std::vector<uint8_t>(
+ {0xaf, 0x8e, 0x6e, 0x91, 0xfa, 0xb4, 0x6c, 0xe4, 0x87, 0x3e, 0x1a,
+ 0x50, 0xa8, 0xef, 0x44, 0x8c, 0xc2, 0x91, 0x21, 0xf7, 0xf7, 0x4d,
+ 0xee, 0xf3, 0x4a, 0x71, 0xef, 0x89, 0xcc, 0x00, 0xd9, 0x27, 0x4b,
+ 0xc6, 0xc2, 0x45, 0x4b, 0xbb, 0x32, 0x30, 0xd8, 0xb2, 0xec, 0x94,
+ 0xc6, 0x2b, 0x1d, 0xec, 0x85, 0xf3, 0x59, 0x3b, 0xfa, 0x30, 0xea,
+ 0x6f, 0x7a, 0x44, 0xd7, 0xc0, 0x94, 0x65, 0xa2, 0x53})),
+ std::make_pair(
+ 241,
+ std::vector<uint8_t>(
+ {0x29, 0xfd, 0x38, 0x4e, 0xd4, 0x90, 0x6f, 0x2d, 0x13, 0xaa, 0x9f,
+ 0xe7, 0xaf, 0x90, 0x59, 0x90, 0x93, 0x8b, 0xed, 0x80, 0x7f, 0x18,
+ 0x32, 0x45, 0x4a, 0x37, 0x2a, 0xb4, 0x12, 0xee, 0xa1, 0xf5, 0x62,
+ 0x5a, 0x1f, 0xcc, 0x9a, 0xc8, 0x34, 0x3b, 0x7c, 0x67, 0xc5, 0xab,
+ 0xa6, 0xe0, 0xb1, 0xcc, 0x46, 0x44, 0x65, 0x49, 0x13, 0x69, 0x2c,
+ 0x6b, 0x39, 0xeb, 0x91, 0x87, 0xce, 0xac, 0xd3, 0xec})),
+ std::make_pair(
+ 242,
+ std::vector<uint8_t>(
+ {0xa2, 0x68, 0xc7, 0x88, 0x5d, 0x98, 0x74, 0xa5, 0x1c, 0x44, 0xdf,
+ 0xfe, 0xd8, 0xea, 0x53, 0xe9, 0x4f, 0x78, 0x45, 0x6e, 0x0b, 0x2e,
+ 0xd9, 0x9f, 0xf5, 0xa3, 0x92, 0x47, 0x60, 0x81, 0x38, 0x26, 0xd9,
+ 0x60, 0xa1, 0x5e, 0xdb, 0xed, 0xbb, 0x5d, 0xe5, 0x22, 0x6b, 0xa4,
+ 0xb0, 0x74, 0xe7, 0x1b, 0x05, 0xc5, 0x5b, 0x97, 0x56, 0xbb, 0x79,
+ 0xe5, 0x5c, 0x02, 0x75, 0x4c, 0x2c, 0x7b, 0x6c, 0x8a})),
+ std::make_pair(
+ 243,
+ std::vector<uint8_t>(
+ {0x0c, 0xf8, 0x54, 0x54, 0x88, 0xd5, 0x6a, 0x86, 0x81, 0x7c, 0xd7,
+ 0xec, 0xb1, 0x0f, 0x71, 0x16, 0xb7, 0xea, 0x53, 0x0a, 0x45, 0xb6,
+ 0xea, 0x49, 0x7b, 0x6c, 0x72, 0xc9, 0x97, 0xe0, 0x9e, 0x3d, 0x0d,
+ 0xa8, 0x69, 0x8f, 0x46, 0xbb, 0x00, 0x6f, 0xc9, 0x77, 0xc2, 0xcd,
+ 0x3d, 0x11, 0x77, 0x46, 0x3a, 0xc9, 0x05, 0x7f, 0xdd, 0x16, 0x62,
+ 0xc8, 0x5d, 0x0c, 0x12, 0x64, 0x43, 0xc1, 0x04, 0x73})),
+ std::make_pair(
+ 244,
+ std::vector<uint8_t>(
+ {0xb3, 0x96, 0x14, 0x26, 0x8f, 0xdd, 0x87, 0x81, 0x51, 0x5e, 0x2c,
+ 0xfe, 0xbf, 0x89, 0xb4, 0xd5, 0x40, 0x2b, 0xab, 0x10, 0xc2, 0x26,
+ 0xe6, 0x34, 0x4e, 0x6b, 0x9a, 0xe0, 0x00, 0xfb, 0x0d, 0x6c, 0x79,
+ 0xcb, 0x2f, 0x3e, 0xc8, 0x0e, 0x80, 0xea, 0xeb, 0x19, 0x80, 0xd2,
+ 0xf8, 0x69, 0x89, 0x16, 0xbd, 0x2e, 0x9f, 0x74, 0x72, 0x36, 0x65,
+ 0x51, 0x16, 0x64, 0x9c, 0xd3, 0xca, 0x23, 0xa8, 0x37})),
+ std::make_pair(
+ 245,
+ std::vector<uint8_t>(
+ {0x74, 0xbe, 0xf0, 0x92, 0xfc, 0x6f, 0x1e, 0x5d, 0xba, 0x36, 0x63,
+ 0xa3, 0xfb, 0x00, 0x3b, 0x2a, 0x5b, 0xa2, 0x57, 0x49, 0x65, 0x36,
+ 0xd9, 0x9f, 0x62, 0xb9, 0xd7, 0x3f, 0x8f, 0x9e, 0xb3, 0xce, 0x9f,
+ 0xf3, 0xee, 0xc7, 0x09, 0xeb, 0x88, 0x36, 0x55, 0xec, 0x9e, 0xb8,
+ 0x96, 0xb9, 0x12, 0x8f, 0x2a, 0xfc, 0x89, 0xcf, 0x7d, 0x1a, 0xb5,
+ 0x8a, 0x72, 0xf4, 0xa3, 0xbf, 0x03, 0x4d, 0x2b, 0x4a})),
+ std::make_pair(
+ 246,
+ std::vector<uint8_t>(
+ {0x3a, 0x98, 0x8d, 0x38, 0xd7, 0x56, 0x11, 0xf3, 0xef, 0x38, 0xb8,
+ 0x77, 0x49, 0x80, 0xb3, 0x3e, 0x57, 0x3b, 0x6c, 0x57, 0xbe, 0xe0,
+ 0x46, 0x9b, 0xa5, 0xee, 0xd9, 0xb4, 0x4f, 0x29, 0x94, 0x5e, 0x73,
+ 0x47, 0x96, 0x7f, 0xba, 0x2c, 0x16, 0x2e, 0x1c, 0x3b, 0xe7, 0xf3,
+ 0x10, 0xf2, 0xf7, 0x5e, 0xe2, 0x38, 0x1e, 0x7b, 0xfd, 0x6b, 0x3f,
+ 0x0b, 0xae, 0xa8, 0xd9, 0x5d, 0xfb, 0x1d, 0xaf, 0xb1})),
+ std::make_pair(
+ 247,
+ std::vector<uint8_t>(
+ {0x58, 0xae, 0xdf, 0xce, 0x6f, 0x67, 0xdd, 0xc8, 0x5a, 0x28, 0xc9,
+ 0x92, 0xf1, 0xc0, 0xbd, 0x09, 0x69, 0xf0, 0x41, 0xe6, 0x6f, 0x1e,
+ 0xe8, 0x80, 0x20, 0xa1, 0x25, 0xcb, 0xfc, 0xfe, 0xbc, 0xd6, 0x17,
+ 0x09, 0xc9, 0xc4, 0xeb, 0xa1, 0x92, 0xc1, 0x5e, 0x69, 0xf0, 0x20,
+ 0xd4, 0x62, 0x48, 0x60, 0x19, 0xfa, 0x8d, 0xea, 0x0c, 0xd7, 0xa4,
+ 0x29, 0x21, 0xa1, 0x9d, 0x2f, 0xe5, 0x46, 0xd4, 0x3d})),
+ std::make_pair(
+ 248,
+ std::vector<uint8_t>(
+ {0x93, 0x47, 0xbd, 0x29, 0x14, 0x73, 0xe6, 0xb4, 0xe3, 0x68, 0x43,
+ 0x7b, 0x8e, 0x56, 0x1e, 0x06, 0x5f, 0x64, 0x9a, 0x6d, 0x8a, 0xda,
+ 0x47, 0x9a, 0xd0, 0x9b, 0x19, 0x99, 0xa8, 0xf2, 0x6b, 0x91, 0xcf,
+ 0x61, 0x20, 0xfd, 0x3b, 0xfe, 0x01, 0x4e, 0x83, 0xf2, 0x3a, 0xcf,
+ 0xa4, 0xc0, 0xad, 0x7b, 0x37, 0x12, 0xb2, 0xc3, 0xc0, 0x73, 0x32,
+ 0x70, 0x66, 0x31, 0x12, 0xcc, 0xd9, 0x28, 0x5c, 0xd9})),
+ std::make_pair(
+ 249,
+ std::vector<uint8_t>(
+ {0xb3, 0x21, 0x63, 0xe7, 0xc5, 0xdb, 0xb5, 0xf5, 0x1f, 0xdc, 0x11,
+ 0xd2, 0xea, 0xc8, 0x75, 0xef, 0xbb, 0xcb, 0x7e, 0x76, 0x99, 0x09,
+ 0x0a, 0x7e, 0x7f, 0xf8, 0xa8, 0xd5, 0x07, 0x95, 0xaf, 0x5d, 0x74,
+ 0xd9, 0xff, 0x98, 0x54, 0x3e, 0xf8, 0xcd, 0xf8, 0x9a, 0xc1, 0x3d,
+ 0x04, 0x85, 0x27, 0x87, 0x56, 0xe0, 0xef, 0x00, 0xc8, 0x17, 0x74,
+ 0x56, 0x61, 0xe1, 0xd5, 0x9f, 0xe3, 0x8e, 0x75, 0x37})),
+ std::make_pair(
+ 250,
+ std::vector<uint8_t>(
+ {0x10, 0x85, 0xd7, 0x83, 0x07, 0xb1, 0xc4, 0xb0, 0x08, 0xc5, 0x7a,
+ 0x2e, 0x7e, 0x5b, 0x23, 0x46, 0x58, 0xa0, 0xa8, 0x2e, 0x4f, 0xf1,
+ 0xe4, 0xaa, 0xac, 0x72, 0xb3, 0x12, 0xfd, 0xa0, 0xfe, 0x27, 0xd2,
+ 0x33, 0xbc, 0x5b, 0x10, 0xe9, 0xcc, 0x17, 0xfd, 0xc7, 0x69, 0x7b,
+ 0x54, 0x0c, 0x7d, 0x95, 0xeb, 0x21, 0x5a, 0x19, 0xa1, 0xa0, 0xe2,
+ 0x0e, 0x1a, 0xbf, 0xa1, 0x26, 0xef, 0xd5, 0x68, 0xc7})),
+ std::make_pair(
+ 251,
+ std::vector<uint8_t>(
+ {0x4e, 0x5c, 0x73, 0x4c, 0x7d, 0xde, 0x01, 0x1d, 0x83, 0xea, 0xc2,
+ 0xb7, 0x34, 0x7b, 0x37, 0x35, 0x94, 0xf9, 0x2d, 0x70, 0x91, 0xb9,
+ 0xca, 0x34, 0xcb, 0x9c, 0x6f, 0x39, 0xbd, 0xf5, 0xa8, 0xd2, 0xf1,
+ 0x34, 0x37, 0x9e, 0x16, 0xd8, 0x22, 0xf6, 0x52, 0x21, 0x70, 0xcc,
+ 0xf2, 0xdd, 0xd5, 0x5c, 0x84, 0xb9, 0xe6, 0xc6, 0x4f, 0xc9, 0x27,
+ 0xac, 0x4c, 0xf8, 0xdf, 0xb2, 0xa1, 0x77, 0x01, 0xf2})),
+ std::make_pair(
+ 252,
+ std::vector<uint8_t>(
+ {0x69, 0x5d, 0x83, 0xbd, 0x99, 0x0a, 0x11, 0x17, 0xb3, 0xd0, 0xce,
+ 0x06, 0xcc, 0x88, 0x80, 0x27, 0xd1, 0x2a, 0x05, 0x4c, 0x26, 0x77,
+ 0xfd, 0x82, 0xf0, 0xd4, 0xfb, 0xfc, 0x93, 0x57, 0x55, 0x23, 0xe7,
+ 0x99, 0x1a, 0x5e, 0x35, 0xa3, 0x75, 0x2e, 0x9b, 0x70, 0xce, 0x62,
+ 0x99, 0x2e, 0x26, 0x8a, 0x87, 0x77, 0x44, 0xcd, 0xd4, 0x35, 0xf5,
+ 0xf1, 0x30, 0x86, 0x9c, 0x9a, 0x20, 0x74, 0xb3, 0x38})),
+ std::make_pair(
+ 253,
+ std::vector<uint8_t>(
+ {0xa6, 0x21, 0x37, 0x43, 0x56, 0x8e, 0x3b, 0x31, 0x58, 0xb9, 0x18,
+ 0x43, 0x01, 0xf3, 0x69, 0x08, 0x47, 0x55, 0x4c, 0x68, 0x45, 0x7c,
+ 0xb4, 0x0f, 0xc9, 0xa4, 0xb8, 0xcf, 0xd8, 0xd4, 0xa1, 0x18, 0xc3,
+ 0x01, 0xa0, 0x77, 0x37, 0xae, 0xda, 0x0f, 0x92, 0x9c, 0x68, 0x91,
+ 0x3c, 0x5f, 0x51, 0xc8, 0x03, 0x94, 0xf5, 0x3b, 0xff, 0x1c, 0x3e,
+ 0x83, 0xb2, 0xe4, 0x0c, 0xa9, 0x7e, 0xba, 0x9e, 0x15})),
+ std::make_pair(
+ 254,
+ std::vector<uint8_t>(
+ {0xd4, 0x44, 0xbf, 0xa2, 0x36, 0x2a, 0x96, 0xdf, 0x21, 0x3d, 0x07,
+ 0x0e, 0x33, 0xfa, 0x84, 0x1f, 0x51, 0x33, 0x4e, 0x4e, 0x76, 0x86,
+ 0x6b, 0x81, 0x39, 0xe8, 0xaf, 0x3b, 0xb3, 0x39, 0x8b, 0xe2, 0xdf,
+ 0xad, 0xdc, 0xbc, 0x56, 0xb9, 0x14, 0x6d, 0xe9, 0xf6, 0x81, 0x18,
+ 0xdc, 0x58, 0x29, 0xe7, 0x4b, 0x0c, 0x28, 0xd7, 0x71, 0x19, 0x07,
+ 0xb1, 0x21, 0xf9, 0x16, 0x1c, 0xb9, 0x2b, 0x69, 0xa9})),
+ std::make_pair(
+ 255,
+ std::vector<uint8_t>(
+ {0x14, 0x27, 0x09, 0xd6, 0x2e, 0x28, 0xfc, 0xcc, 0xd0, 0xaf, 0x97,
+ 0xfa, 0xd0, 0xf8, 0x46, 0x5b, 0x97, 0x1e, 0x82, 0x20, 0x1d, 0xc5,
+ 0x10, 0x70, 0xfa, 0xa0, 0x37, 0x2a, 0xa4, 0x3e, 0x92, 0x48, 0x4b,
+ 0xe1, 0xc1, 0xe7, 0x3b, 0xa1, 0x09, 0x06, 0xd5, 0xd1, 0x85, 0x3d,
+ 0xb6, 0xa4, 0x10, 0x6e, 0x0a, 0x7b, 0xf9, 0x80, 0x0d, 0x37, 0x3d,
+ 0x6d, 0xee, 0x2d, 0x46, 0xd6, 0x2e, 0xf2, 0xa4, 0x61}))}; \ No newline at end of file
diff --git a/security/nss/gtests/freebl_gtest/rsa_unittest.cc b/security/nss/gtests/freebl_gtest/rsa_unittest.cc
new file mode 100644
index 000000000..5c667a1d1
--- /dev/null
+++ b/security/nss/gtests/freebl_gtest/rsa_unittest.cc
@@ -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/.
+
+#include "gtest/gtest.h"
+
+#include <stdint.h>
+
+#include "blapi.h"
+#include "secitem.h"
+
+template <class T>
+struct ScopedDelete {
+ void operator()(T* ptr) {
+ if (ptr) {
+ PORT_FreeArena(ptr->arena, PR_TRUE);
+ }
+ }
+};
+
+typedef std::unique_ptr<RSAPrivateKey, ScopedDelete<RSAPrivateKey>>
+ ScopedRSAPrivateKey;
+
+class RSANewKeyTest : public ::testing::Test {
+ protected:
+ RSAPrivateKey* CreateKeyWithExponent(int keySizeInBits,
+ unsigned char publicExponent) {
+ SECItem exp = {siBuffer, 0, 0};
+ unsigned char pubExp[1] = {publicExponent};
+ exp.data = pubExp;
+ exp.len = 1;
+
+ return RSA_NewKey(keySizeInBits, &exp);
+ }
+};
+
+TEST_F(RSANewKeyTest, expOneTest) {
+ ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x01));
+ ASSERT_TRUE(key == nullptr);
+}
+TEST_F(RSANewKeyTest, expTwoTest) {
+ ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x02));
+ ASSERT_TRUE(key == nullptr);
+}
+TEST_F(RSANewKeyTest, expFourTest) {
+ ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x04));
+ ASSERT_TRUE(key == nullptr);
+}
+TEST_F(RSANewKeyTest, WrongKeysizeTest) {
+ ScopedRSAPrivateKey key(CreateKeyWithExponent(2047, 0x03));
+ ASSERT_TRUE(key == nullptr);
+}
+
+TEST_F(RSANewKeyTest, expThreeTest) {
+ ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x03));
+#ifdef NSS_FIPS_DISABLED
+ ASSERT_TRUE(key != nullptr);
+#else
+ ASSERT_TRUE(key == nullptr);
+#endif
+}
diff --git a/security/nss/gtests/manifest.mn b/security/nss/gtests/manifest.mn
index 1ae4cab77..13048f037 100644
--- a/security/nss/gtests/manifest.mn
+++ b/security/nss/gtests/manifest.mn
@@ -13,7 +13,6 @@ LIB_SRCDIRS = \
ifneq ($(NSS_BUILD_WITHOUT_UTIL),1)
UTIL_SRCDIRS = \
util_gtest \
- der_gtest \
$(NULL)
endif
@@ -22,9 +21,12 @@ ifneq ($(NSS_BUILD_UTIL_ONLY),1)
NSS_SRCDIRS = \
certdb_gtest \
certhigh_gtest \
+ cryptohi_gtest \
+ der_gtest \
pk11_gtest \
+ softoken_gtest \
ssl_gtest \
- nss_bogo_shim \
+ nss_bogo_shim \
$(NULL)
endif
endif
diff --git a/security/nss/gtests/nss_bogo_shim/Makefile b/security/nss/gtests/nss_bogo_shim/Makefile
index fd6426d89..a2ac4b145 100644
--- a/security/nss/gtests/nss_bogo_shim/Makefile
+++ b/security/nss/gtests/nss_bogo_shim/Makefile
@@ -30,10 +30,6 @@ include ../common/gtest.mk
CFLAGS += -I$(CORE_DEPTH)/lib/ssl
-ifdef NSS_SSL_ENABLE_ZLIB
-include $(CORE_DEPTH)/coreconf/zlib.mk
-endif
-
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
@@ -48,5 +44,3 @@ include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################
-
-
diff --git a/security/nss/gtests/nss_bogo_shim/config.json b/security/nss/gtests/nss_bogo_shim/config.json
index 4109bd2ca..fed7ca993 100644
--- a/security/nss/gtests/nss_bogo_shim/config.json
+++ b/security/nss/gtests/nss_bogo_shim/config.json
@@ -1,12 +1,14 @@
{
"DisabledTests": {
"### These tests break whenever we rev versions, so just leave them here for easy uncommenting":"",
- "#*TLS13*":"(NSS=18, BoGo=16)",
- "#*HelloRetryRequest*":"(NSS=18, BoGo=16)",
- "#*KeyShare*":"(NSS=18, BoGo=16)",
- "#*EncryptedExtensions*":"(NSS=18, BoGo=16)",
- "#*SecondClientHello*":"(NSS=18, BoGo=16)",
- "#*IgnoreClientVersionOrder*":"(NSS=18, BoGo=16)",
+ "*TLS13*":"(NSS=19, BoGo=18)",
+ "*HelloRetryRequest*":"(NSS=19, BoGo=18)",
+ "*KeyShare*":"(NSS=19, BoGo=18)",
+ "*EncryptedExtensions*":"(NSS=19, BoGo=18)",
+ "*SecondClientHello*":"(NSS=19, BoGo=18)",
+ "*IgnoreClientVersionOrder*":"(NSS=19, BoGo=18)",
+ "SkipEarlyData*":"(NSS=19, BoGo=18)",
+ "*Binder*":"(NSS=19, BoGo=18)",
"Resume-Server-BinderWrongLength":"Alert disagreement (Bug 1317633)",
"Resume-Server-NoPSKBinder":"Alert disagreement (Bug 1317633)",
"CheckRecordVersion-TLS*":"Bug 1317634",
@@ -66,4 +68,3 @@
":DIGEST_CHECK_FAILED:":"SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE"
}
}
-
diff --git a/security/nss/gtests/pk11_gtest/manifest.mn b/security/nss/gtests/pk11_gtest/manifest.mn
index fb773ee18..a3dff9d10 100644
--- a/security/nss/gtests/pk11_gtest/manifest.mn
+++ b/security/nss/gtests/pk11_gtest/manifest.mn
@@ -11,6 +11,7 @@ CPPSRCS = \
pk11_chacha20poly1305_unittest.cc \
pk11_curve25519_unittest.cc \
pk11_ecdsa_unittest.cc \
+ pk11_encrypt_derive_unittest.cc \
pk11_export_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
@@ -23,10 +24,12 @@ INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
-I$(CORE_DEPTH)/gtests/common \
-I$(CORE_DEPTH)/cpputil
-REQUIRES = nspr nss libdbm gtest
+REQUIRES = nspr nss libdbm gtest cpputil
PROGRAM = pk11_gtest
-EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) $(EXTRA_OBJS) \
- $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX)
+EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)cpputil.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \
+ $(NULL)
diff --git a/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc
index a54190c7c..fb0659852 100644
--- a/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc
@@ -15,103 +15,117 @@
namespace nss_test {
-class Pkcs11EcdsaTest : public Pk11SignatureTest {
+class Pkcs11EcdsaTestBase : public Pk11SignatureTest {
protected:
- CK_MECHANISM_TYPE mechanism() { return CKM_ECDSA; }
- SECItem* parameters() { return nullptr; }
+ Pkcs11EcdsaTestBase(SECOidTag hash_oid)
+ : Pk11SignatureTest(CKM_ECDSA, hash_oid) {}
};
-class Pkcs11EcdsaSha256Test : public Pkcs11EcdsaTest {
- protected:
- SECOidTag hashOID() { return SEC_OID_SHA256; }
+struct Pkcs11EcdsaTestParams {
+ SECOidTag hash_oid_;
+ Pkcs11SignatureTestParams sig_params_;
};
-class Pkcs11EcdsaSha384Test : public Pkcs11EcdsaTest {
- protected:
- SECOidTag hashOID() { return SEC_OID_SHA384; }
+class Pkcs11EcdsaTest
+ : public Pkcs11EcdsaTestBase,
+ public ::testing::WithParamInterface<Pkcs11EcdsaTestParams> {
+ public:
+ Pkcs11EcdsaTest() : Pkcs11EcdsaTestBase(GetParam().hash_oid_) {}
};
-class Pkcs11EcdsaSha512Test : public Pkcs11EcdsaTest {
- protected:
- SECOidTag hashOID() { return SEC_OID_SHA512; }
-};
+TEST_P(Pkcs11EcdsaTest, Verify) { Verify(GetParam().sig_params_); }
-TEST_F(Pkcs11EcdsaSha256Test, VerifyP256) {
- SIG_TEST_VECTOR_VERIFY(kP256Spki, kP256Data, kP256Signature)
-}
-TEST_F(Pkcs11EcdsaSha256Test, SignAndVerifyP256) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kP256Pkcs8, kP256Spki, kP256Data)
+TEST_P(Pkcs11EcdsaTest, SignAndVerify) {
+ SignAndVerify(GetParam().sig_params_);
}
-TEST_F(Pkcs11EcdsaSha384Test, VerifyP384) {
- SIG_TEST_VECTOR_VERIFY(kP384Spki, kP384Data, kP384Signature)
-}
-TEST_F(Pkcs11EcdsaSha384Test, SignAndVerifyP384) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kP384Pkcs8, kP384Spki, kP384Data)
-}
-
-TEST_F(Pkcs11EcdsaSha512Test, VerifyP521) {
- SIG_TEST_VECTOR_VERIFY(kP521Spki, kP521Data, kP521Signature)
-}
-TEST_F(Pkcs11EcdsaSha512Test, SignAndVerifyP521) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kP521Pkcs8, kP521Spki, kP521Data)
-}
+static const Pkcs11EcdsaTestParams kEcdsaVectors[] = {
+ {SEC_OID_SHA256,
+ {DataBuffer(kP256Pkcs8, sizeof(kP256Pkcs8)),
+ DataBuffer(kP256Spki, sizeof(kP256Spki)),
+ DataBuffer(kP256Data, sizeof(kP256Data)),
+ DataBuffer(kP256Signature, sizeof(kP256Signature))}},
+ {SEC_OID_SHA384,
+ {DataBuffer(kP384Pkcs8, sizeof(kP384Pkcs8)),
+ DataBuffer(kP384Spki, sizeof(kP384Spki)),
+ DataBuffer(kP384Data, sizeof(kP384Data)),
+ DataBuffer(kP384Signature, sizeof(kP384Signature))}},
+ {SEC_OID_SHA512,
+ {DataBuffer(kP521Pkcs8, sizeof(kP521Pkcs8)),
+ DataBuffer(kP521Spki, sizeof(kP521Spki)),
+ DataBuffer(kP521Data, sizeof(kP521Data)),
+ DataBuffer(kP521Signature, sizeof(kP521Signature))}}};
+
+INSTANTIATE_TEST_CASE_P(EcdsaSignVerify, Pkcs11EcdsaTest,
+ ::testing::ValuesIn(kEcdsaVectors));
+
+class Pkcs11EcdsaSha256Test : public Pkcs11EcdsaTestBase {
+ public:
+ Pkcs11EcdsaSha256Test() : Pkcs11EcdsaTestBase(SEC_OID_SHA256) {}
+};
// Importing a private key in PKCS#8 format must fail when the outer AlgID
// struct contains neither id-ecPublicKey nor a namedCurve parameter.
TEST_F(Pkcs11EcdsaSha256Test, ImportNoCurveOIDOrAlgorithmParams) {
- EXPECT_FALSE(ImportPrivateKey(kP256Pkcs8NoCurveOIDOrAlgorithmParams,
- sizeof(kP256Pkcs8NoCurveOIDOrAlgorithmParams)));
+ DataBuffer k(kP256Pkcs8NoCurveOIDOrAlgorithmParams,
+ sizeof(kP256Pkcs8NoCurveOIDOrAlgorithmParams));
+ EXPECT_FALSE(ImportPrivateKey(k));
};
// Importing a private key in PKCS#8 format must succeed when only the outer
// AlgID struct contains the namedCurve parameters.
TEST_F(Pkcs11EcdsaSha256Test, ImportOnlyAlgorithmParams) {
- EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(
- kP256Pkcs8OnlyAlgorithmParams, sizeof(kP256Pkcs8OnlyAlgorithmParams),
- kP256Data, sizeof(kP256Data)));
+ DataBuffer k(kP256Pkcs8OnlyAlgorithmParams,
+ sizeof(kP256Pkcs8OnlyAlgorithmParams));
+ DataBuffer data(kP256Data, sizeof(kP256Data));
+ DataBuffer sig;
+ EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig));
};
// Importing a private key in PKCS#8 format must succeed when the outer AlgID
// struct and the inner ECPrivateKey contain the same namedCurve parameters.
// The inner curveOID is always ignored, so only the outer one will be used.
TEST_F(Pkcs11EcdsaSha256Test, ImportMatchingCurveOIDAndAlgorithmParams) {
- EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(
- kP256Pkcs8MatchingCurveOIDAndAlgorithmParams,
- sizeof(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams), kP256Data,
- sizeof(kP256Data)));
+ DataBuffer k(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams,
+ sizeof(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams));
+ DataBuffer data(kP256Data, sizeof(kP256Data));
+ DataBuffer sig;
+ EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig));
};
// Importing a private key in PKCS#8 format must succeed when the outer AlgID
// struct and the inner ECPrivateKey contain dissimilar namedCurve parameters.
// The inner curveOID is always ignored, so only the outer one will be used.
TEST_F(Pkcs11EcdsaSha256Test, ImportDissimilarCurveOIDAndAlgorithmParams) {
- EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(
- kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams,
- sizeof(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams), kP256Data,
- sizeof(kP256Data)));
+ DataBuffer k(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams,
+ sizeof(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams));
+ DataBuffer data(kP256Data, sizeof(kP256Data));
+ DataBuffer sig;
+ EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig));
};
// Importing a private key in PKCS#8 format must fail when the outer ASN.1
// AlgorithmID struct contains only id-ecPublicKey but no namedCurve parameter.
TEST_F(Pkcs11EcdsaSha256Test, ImportNoAlgorithmParams) {
- EXPECT_FALSE(ImportPrivateKey(kP256Pkcs8NoAlgorithmParams,
- sizeof(kP256Pkcs8NoAlgorithmParams)));
+ DataBuffer k(kP256Pkcs8NoAlgorithmParams,
+ sizeof(kP256Pkcs8NoAlgorithmParams));
+ EXPECT_FALSE(ImportPrivateKey(k));
};
// Importing a private key in PKCS#8 format must fail when id-ecPublicKey is
// given (so we know it's an EC key) but the namedCurve parameter is unknown.
TEST_F(Pkcs11EcdsaSha256Test, ImportInvalidAlgorithmParams) {
- EXPECT_FALSE(ImportPrivateKey(kP256Pkcs8InvalidAlgorithmParams,
- sizeof(kP256Pkcs8InvalidAlgorithmParams)));
+ DataBuffer k(kP256Pkcs8InvalidAlgorithmParams,
+ sizeof(kP256Pkcs8InvalidAlgorithmParams));
+ EXPECT_FALSE(ImportPrivateKey(k));
};
// Importing a private key in PKCS#8 format with a point not on the curve will
// succeed. Using the contained public key however will fail when trying to
// import it before using it for any operation.
TEST_F(Pkcs11EcdsaSha256Test, ImportPointNotOnCurve) {
- ScopedSECKEYPrivateKey privKey(ImportPrivateKey(
- kP256Pkcs8PointNotOnCurve, sizeof(kP256Pkcs8PointNotOnCurve)));
+ DataBuffer k(kP256Pkcs8PointNotOnCurve, sizeof(kP256Pkcs8PointNotOnCurve));
+ ScopedSECKEYPrivateKey privKey(ImportPrivateKey(k));
ASSERT_TRUE(privKey);
ScopedSECKEYPublicKey pubKey(SECKEY_ConvertToPublicKey(privKey.get()));
@@ -127,23 +141,23 @@ TEST_F(Pkcs11EcdsaSha256Test, ImportPointNotOnCurve) {
// Importing a private key in PKCS#8 format must fail when no point is given.
// PK11 currently offers no APIs to derive raw public keys from private values.
TEST_F(Pkcs11EcdsaSha256Test, ImportNoPublicKey) {
- EXPECT_FALSE(
- ImportPrivateKey(kP256Pkcs8NoPublicKey, sizeof(kP256Pkcs8NoPublicKey)));
+ DataBuffer k(kP256Pkcs8NoPublicKey, sizeof(kP256Pkcs8NoPublicKey));
+ EXPECT_FALSE(ImportPrivateKey(k));
};
// Importing a public key in SPKI format must fail when id-ecPublicKey is
// given (so we know it's an EC key) but the namedCurve parameter is missing.
TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiNoAlgorithmParams) {
- EXPECT_FALSE(ImportPublicKey(kP256SpkiNoAlgorithmParams,
- sizeof(kP256SpkiNoAlgorithmParams)));
+ DataBuffer k(kP256SpkiNoAlgorithmParams, sizeof(kP256SpkiNoAlgorithmParams));
+ EXPECT_FALSE(ImportPublicKey(k));
}
// Importing a public key in SPKI format with a point not on the curve will
// succeed. Using the public key however will fail when trying to import
// it before using it for any operation.
TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiPointNotOnCurve) {
- ScopedSECKEYPublicKey pubKey(ImportPublicKey(
- kP256SpkiPointNotOnCurve, sizeof(kP256SpkiPointNotOnCurve)));
+ DataBuffer k(kP256SpkiPointNotOnCurve, sizeof(kP256SpkiPointNotOnCurve));
+ ScopedSECKEYPublicKey pubKey(ImportPublicKey(k));
ASSERT_TRUE(pubKey);
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
diff --git a/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc
new file mode 100644
index 000000000..aa92756f2
--- /dev/null
+++ b/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc
@@ -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/. */
+
+#include "pk11pub.h"
+#include "nssutil.h"
+#include <stdio.h>
+#include "prerror.h"
+#include "nss.h"
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+#include "cpputil.h"
+#include "databuffer.h"
+#include "util.h"
+
+#define MAX_KEY_SIZE 24
+
+namespace nss_test {
+
+static const uint8_t kIv[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+static const uint8_t kInput[] = {
+ 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0xff, 0xee, 0xdd, 0xcc,
+ 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
+
+class EncryptDeriveTest
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<CK_MECHANISM_TYPE> {
+ public:
+ void TestEncryptDerive() {
+ ScopedPK11SymKey derived_key(PK11_Derive(key_.get(), derive_mech(),
+ derive_param(), encrypt_mech(),
+ CKA_DECRYPT, keysize()));
+ ASSERT_TRUE(derived_key);
+
+ uint8_t derived_key_data[MAX_KEY_SIZE];
+ ASSERT_GE(sizeof(derived_key_data), keysize());
+ GetKeyData(derived_key, derived_key_data, keysize());
+ RemoveChecksum(derived_key_data);
+
+ uint8_t reference_key_data[MAX_KEY_SIZE];
+ unsigned int reference_len = 0;
+ SECStatus rv = PK11_Encrypt(key_.get(), encrypt_mech(), encrypt_param(),
+ reference_key_data, &reference_len, keysize(),
+ kInput, keysize());
+ ASSERT_EQ(SECSuccess, rv);
+ ASSERT_EQ(keysize(), static_cast<size_t>(reference_len));
+ RemoveChecksum(reference_key_data);
+
+ EXPECT_EQ(DataBuffer(reference_key_data, keysize()),
+ DataBuffer(derived_key_data, keysize()));
+ }
+
+ protected:
+ unsigned int keysize() const { return 16; }
+
+ private:
+ CK_MECHANISM_TYPE encrypt_mech() const { return GetParam(); }
+
+ CK_MECHANISM_TYPE derive_mech() const {
+ switch (encrypt_mech()) {
+ case CKM_DES3_ECB:
+ return CKM_DES3_ECB_ENCRYPT_DATA;
+ case CKM_DES3_CBC:
+ return CKM_DES3_CBC_ENCRYPT_DATA;
+ case CKM_AES_ECB:
+ return CKM_AES_ECB_ENCRYPT_DATA;
+ case CKM_AES_CBC:
+ return CKM_AES_CBC_ENCRYPT_DATA;
+ case CKM_CAMELLIA_ECB:
+ return CKM_CAMELLIA_ECB_ENCRYPT_DATA;
+ case CKM_CAMELLIA_CBC:
+ return CKM_CAMELLIA_CBC_ENCRYPT_DATA;
+ case CKM_SEED_ECB:
+ return CKM_SEED_ECB_ENCRYPT_DATA;
+ case CKM_SEED_CBC:
+ return CKM_SEED_CBC_ENCRYPT_DATA;
+ default:
+ ADD_FAILURE() << "Unknown mechanism";
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+ }
+
+ SECItem* derive_param() const {
+ static CK_AES_CBC_ENCRYPT_DATA_PARAMS aes_data;
+ static CK_DES_CBC_ENCRYPT_DATA_PARAMS des_data;
+ static CK_KEY_DERIVATION_STRING_DATA string_data;
+ static SECItem param = {siBuffer, NULL, 0};
+
+ switch (encrypt_mech()) {
+ case CKM_DES3_ECB:
+ case CKM_AES_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_SEED_ECB:
+ string_data.pData = toUcharPtr(kInput);
+ string_data.ulLen = keysize();
+ param.data = reinterpret_cast<uint8_t*>(&string_data);
+ param.len = sizeof(string_data);
+ break;
+
+ case CKM_DES3_CBC:
+ des_data.pData = toUcharPtr(kInput);
+ des_data.length = keysize();
+ PORT_Memcpy(des_data.iv, kIv, 8);
+ param.data = reinterpret_cast<uint8_t*>(&des_data);
+ param.len = sizeof(des_data);
+ break;
+
+ case CKM_AES_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_SEED_CBC:
+ aes_data.pData = toUcharPtr(kInput);
+ aes_data.length = keysize();
+ PORT_Memcpy(aes_data.iv, kIv, keysize());
+ param.data = reinterpret_cast<uint8_t*>(&aes_data);
+ param.len = sizeof(aes_data);
+ break;
+
+ default:
+ ADD_FAILURE() << "Unknown mechanism";
+ break;
+ }
+ return &param;
+ }
+
+ SECItem* encrypt_param() const {
+ static SECItem param = {siBuffer, NULL, 0};
+
+ switch (encrypt_mech()) {
+ case CKM_DES3_ECB:
+ case CKM_AES_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_SEED_ECB:
+ // No parameter needed here.
+ break;
+
+ case CKM_DES3_CBC:
+ case CKM_AES_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_SEED_CBC:
+ param.data = toUcharPtr(kIv);
+ param.len = keysize();
+ break;
+
+ default:
+ ADD_FAILURE() << "Unknown mechanism";
+ break;
+ }
+ return &param;
+ }
+
+ virtual void SetUp() {
+ slot_.reset(PK11_GetBestSlot(derive_mech(), NULL));
+ ASSERT_TRUE(slot_);
+
+ key_.reset(PK11_TokenKeyGenWithFlags(slot_.get(), encrypt_mech(), NULL,
+ keysize(), NULL,
+ CKF_ENCRYPT | CKF_DERIVE, 0, NULL));
+ ASSERT_TRUE(key_);
+ }
+
+ void GetKeyData(ScopedPK11SymKey& key, uint8_t* buf, size_t max_len) const {
+ ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(key.get()));
+ SECItem* data = PK11_GetKeyData(key.get());
+ ASSERT_TRUE(data);
+ ASSERT_EQ(max_len, static_cast<size_t>(data->len));
+ PORT_Memcpy(buf, data->data, data->len);
+ }
+
+ // Remove checksum if the key is a 3DES key.
+ void RemoveChecksum(uint8_t* key_data) const {
+ if (encrypt_mech() != CKM_DES3_CBC && encrypt_mech() != CKM_DES3_ECB) {
+ return;
+ }
+ for (size_t i = 0; i < keysize(); ++i) {
+ key_data[i] &= 0xfe;
+ }
+ }
+
+ ScopedPK11SlotInfo slot_;
+ ScopedPK11SymKey key_;
+};
+
+TEST_P(EncryptDeriveTest, Test) { TestEncryptDerive(); }
+
+static const CK_MECHANISM_TYPE kEncryptDeriveMechanisms[] = {
+ CKM_DES3_ECB, CKM_DES3_CBC, CKM_AES_ECB, CKM_AES_ECB, CKM_AES_CBC,
+ CKM_CAMELLIA_ECB, CKM_CAMELLIA_CBC, CKM_SEED_ECB, CKM_SEED_CBC};
+
+INSTANTIATE_TEST_CASE_P(EncryptDeriveTests, EncryptDeriveTest,
+ ::testing::ValuesIn(kEncryptDeriveMechanisms));
+
+// This class handles the case where 3DES takes a 192-bit key
+// where all 24 octets will be used.
+class EncryptDerive3Test : public EncryptDeriveTest {
+ protected:
+ unsigned int keysize() const { return 24; }
+};
+
+TEST_P(EncryptDerive3Test, Test) { TestEncryptDerive(); }
+
+static const CK_MECHANISM_TYPE kDES3EncryptDeriveMechanisms[] = {CKM_DES3_ECB,
+ CKM_DES3_CBC};
+
+INSTANTIATE_TEST_CASE_P(Encrypt3DeriveTests, EncryptDerive3Test,
+ ::testing::ValuesIn(kDES3EncryptDeriveMechanisms));
+
+} // namespace nss_test
diff --git a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
index c47ff4778..076b4d37f 100644
--- a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
+++ b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
@@ -16,6 +16,7 @@
'pk11_chacha20poly1305_unittest.cc',
'pk11_curve25519_unittest.cc',
'pk11_ecdsa_unittest.cc',
+ 'pk11_encrypt_derive_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
'pk11_prng_unittest.cc',
@@ -26,6 +27,7 @@
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
+ '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
],
'conditions': [
diff --git a/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc
index 012bae0e9..6c8c5ab4e 100644
--- a/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc
@@ -12,14 +12,14 @@
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
-#include "pk11_rsapss_vectors.h"
#include "pk11_signature_test.h"
+#include "pk11_rsapss_vectors.h"
namespace nss_test {
-class Pkcs11RsaPssVectorTest : public Pk11SignatureTest {
+class Pkcs11RsaPssTest : public Pk11SignatureTest {
public:
- Pkcs11RsaPssVectorTest() {
+ Pkcs11RsaPssTest() : Pk11SignatureTest(CKM_RSA_PKCS_PSS, SEC_OID_SHA1) {
rsaPssParams_.hashAlg = CKM_SHA_1;
rsaPssParams_.mgf = CKG_MGF1_SHA1;
rsaPssParams_.sLen = HASH_ResultLenByOidTag(SEC_OID_SHA1);
@@ -30,16 +30,14 @@ class Pkcs11RsaPssVectorTest : public Pk11SignatureTest {
}
protected:
- CK_MECHANISM_TYPE mechanism() { return CKM_RSA_PKCS_PSS; }
- SECItem* parameters() { return &params_; }
- SECOidTag hashOID() { return SEC_OID_SHA1; }
+ const SECItem* parameters() const { return &params_; }
private:
CK_RSA_PKCS_PSS_PARAMS rsaPssParams_;
SECItem params_;
};
-TEST_F(Pkcs11RsaPssVectorTest, GenerateAndSignAndVerify) {
+TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) {
// Sign data with a 1024-bit RSA key, using PSS/SHA-256.
SECOidTag hashOid = SEC_OID_SHA256;
CK_MECHANISM_TYPE hashMech = CKM_SHA256;
@@ -95,105 +93,56 @@ TEST_F(Pkcs11RsaPssVectorTest, GenerateAndSignAndVerify) {
EXPECT_EQ(rv, SECFailure);
}
-// RSA-PSS test vectors, pss-vect.txt, Example 1.1: A 1024-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature1) {
- SIG_TEST_VECTOR_VERIFY(kTestVector1Spki, kTestVector1Data, kTestVector1Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify1) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector1Pkcs8, kTestVector1Spki,
- kTestVector1Data);
-}
+class Pkcs11RsaPssVectorTest
+ : public Pkcs11RsaPssTest,
+ public ::testing::WithParamInterface<Pkcs11SignatureTestParams> {};
-// RSA-PSS test vectors, pss-vect.txt, Example 2.1: A 1025-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature2) {
- SIG_TEST_VECTOR_VERIFY(kTestVector2Spki, kTestVector2Data, kTestVector2Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify2) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector2Pkcs8, kTestVector2Spki,
- kTestVector2Data);
-}
+TEST_P(Pkcs11RsaPssVectorTest, Verify) { Verify(GetParam()); }
-// RSA-PSS test vectors, pss-vect.txt, Example 3.1: A 1026-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature3) {
- SIG_TEST_VECTOR_VERIFY(kTestVector3Spki, kTestVector3Data, kTestVector3Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify3) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector3Pkcs8, kTestVector3Spki,
- kTestVector3Data);
-}
+TEST_P(Pkcs11RsaPssVectorTest, SignAndVerify) { SignAndVerify(GetParam()); }
-// RSA-PSS test vectors, pss-vect.txt, Example 4.1: A 1027-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature4) {
- SIG_TEST_VECTOR_VERIFY(kTestVector4Spki, kTestVector4Data, kTestVector4Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify4) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector4Pkcs8, kTestVector4Spki,
- kTestVector4Data);
-}
-
-// RSA-PSS test vectors, pss-vect.txt, Example 5.1: A 1028-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature5) {
- SIG_TEST_VECTOR_VERIFY(kTestVector5Spki, kTestVector5Data, kTestVector5Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify5) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector5Pkcs8, kTestVector5Spki,
- kTestVector5Data);
-}
-
-// RSA-PSS test vectors, pss-vect.txt, Example 6.1: A 1029-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature6) {
- SIG_TEST_VECTOR_VERIFY(kTestVector6Spki, kTestVector6Data, kTestVector6Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify6) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector6Pkcs8, kTestVector6Spki,
- kTestVector6Data);
-}
-
-// RSA-PSS test vectors, pss-vect.txt, Example 7.1: A 1030-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature7) {
- SIG_TEST_VECTOR_VERIFY(kTestVector7Spki, kTestVector7Data, kTestVector7Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify7) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector7Pkcs8, kTestVector7Spki,
- kTestVector7Data);
-}
-
-// RSA-PSS test vectors, pss-vect.txt, Example 8.1: A 1031-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature8) {
- SIG_TEST_VECTOR_VERIFY(kTestVector8Spki, kTestVector8Data, kTestVector8Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify8) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector8Pkcs8, kTestVector8Spki,
- kTestVector8Data);
-}
-
-// RSA-PSS test vectors, pss-vect.txt, Example 9.1: A 1536-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature9) {
- SIG_TEST_VECTOR_VERIFY(kTestVector9Spki, kTestVector9Data, kTestVector9Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify9) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector9Pkcs8, kTestVector9Spki,
- kTestVector9Data);
-}
-
-// RSA-PSS test vectors, pss-vect.txt, Example 10.1: A 2048-bit RSA Key Pair
-// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
-TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature10) {
- SIG_TEST_VECTOR_VERIFY(kTestVector10Spki, kTestVector10Data,
- kTestVector10Sig);
-}
-TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify10) {
- SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector10Pkcs8, kTestVector10Spki,
- kTestVector10Data);
-}
+#define VECTOR(pkcs8, spki, data, sig) \
+ { \
+ DataBuffer(pkcs8, sizeof(pkcs8)), DataBuffer(spki, sizeof(spki)), \
+ DataBuffer(data, sizeof(data)), DataBuffer(sig, sizeof(sig)) \
+ }
+#define VECTOR_N(n) \
+ VECTOR(kTestVector##n##Pkcs8, kTestVector##n##Spki, kTestVector##n##Data, \
+ kTestVector##n##Sig)
+
+static const Pkcs11SignatureTestParams kRsaPssVectors[] = {
+ // RSA-PSS test vectors, pss-vect.txt, Example 1.1: A 1024-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(1),
+ // RSA-PSS test vectors, pss-vect.txt, Example 2.1: A 1025-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(2),
+ // RSA-PSS test vectors, pss-vect.txt, Example 3.1: A 1026-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(3),
+ // RSA-PSS test vectors, pss-vect.txt, Example 4.1: A 1027-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(4),
+ // RSA-PSS test vectors, pss-vect.txt, Example 5.1: A 1028-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(5),
+ // RSA-PSS test vectors, pss-vect.txt, Example 6.1: A 1029-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(6),
+ // RSA-PSS test vectors, pss-vect.txt, Example 7.1: A 1030-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(7),
+ // RSA-PSS test vectors, pss-vect.txt, Example 8.1: A 1031-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(8),
+ // RSA-PSS test vectors, pss-vect.txt, Example 9.1: A 1536-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(9),
+ // RSA-PSS test vectors, pss-vect.txt, Example 10.1: A 2048-bit RSA Key Pair
+ // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
+ VECTOR_N(10)};
+
+INSTANTIATE_TEST_CASE_P(RsaPssSignVerify, Pkcs11RsaPssVectorTest,
+ ::testing::ValuesIn(kRsaPssVectors));
} // namespace nss_test
diff --git a/security/nss/gtests/pk11_gtest/pk11_signature_test.h b/security/nss/gtests/pk11_gtest/pk11_signature_test.h
index e6a0a9c57..b14104371 100644
--- a/security/nss/gtests/pk11_gtest/pk11_signature_test.h
+++ b/security/nss/gtests/pk11_gtest/pk11_signature_test.h
@@ -9,26 +9,37 @@
#include "cpputil.h"
#include "scoped_ptrs.h"
+#include "databuffer.h"
#include "gtest/gtest.h"
namespace nss_test {
+// For test vectors.
+struct Pkcs11SignatureTestParams {
+ const DataBuffer pkcs8_;
+ const DataBuffer spki_;
+ const DataBuffer data_;
+ const DataBuffer signature_;
+};
+
class Pk11SignatureTest : public ::testing::Test {
protected:
- virtual CK_MECHANISM_TYPE mechanism() = 0;
- virtual SECItem* parameters() = 0;
- virtual SECOidTag hashOID() = 0;
+ Pk11SignatureTest(CK_MECHANISM_TYPE mechanism, SECOidTag hash_oid)
+ : mechanism_(mechanism), hash_oid_(hash_oid) {}
- ScopedSECKEYPrivateKey ImportPrivateKey(const uint8_t* pkcs8,
- size_t pkcs8_len) {
+ virtual const SECItem* parameters() const { return nullptr; }
+ CK_MECHANISM_TYPE mechanism() const { return mechanism_; }
+
+ ScopedSECKEYPrivateKey ImportPrivateKey(const DataBuffer& pkcs8) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
+ ADD_FAILURE() << "No slot";
return nullptr;
}
- SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8),
- static_cast<unsigned int>(pkcs8_len)};
+ SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8.data()),
+ static_cast<unsigned int>(pkcs8.len())};
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
@@ -42,9 +53,9 @@ class Pk11SignatureTest : public ::testing::Test {
return ScopedSECKEYPrivateKey(key);
}
- ScopedSECKEYPublicKey ImportPublicKey(const uint8_t* spki, size_t spki_len) {
- SECItem spkiItem = {siBuffer, toUcharPtr(spki),
- static_cast<unsigned int>(spki_len)};
+ ScopedSECKEYPublicKey ImportPublicKey(const DataBuffer& spki) {
+ SECItem spkiItem = {siBuffer, toUcharPtr(spki.data()),
+ static_cast<unsigned int>(spki.len())};
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
@@ -52,87 +63,74 @@ class Pk11SignatureTest : public ::testing::Test {
return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
}
- ScopedSECItem ComputeHash(const uint8_t* data, size_t len) {
- unsigned int hLen = HASH_ResultLenByOidTag(hashOID());
- ScopedSECItem hash(SECITEM_AllocItem(nullptr, nullptr, hLen));
- if (!hash) {
- return nullptr;
- }
-
- SECStatus rv = PK11_HashBuf(hashOID(), hash->data, data, len);
- if (rv != SECSuccess) {
- return nullptr;
- }
-
- return hash;
+ bool ComputeHash(const DataBuffer& data, DataBuffer* hash) {
+ hash->Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(hash_oid_)));
+ SECStatus rv =
+ PK11_HashBuf(hash_oid_, hash->data(), data.data(), data.len());
+ return rv == SECSuccess;
}
- ScopedSECItem SignHashedData(ScopedSECKEYPrivateKey& privKey,
- ScopedSECItem& hash) {
- unsigned int sLen = PK11_SignatureLen(privKey.get());
- ScopedSECItem sig(SECITEM_AllocItem(nullptr, nullptr, sLen));
- if (!sig) {
- return nullptr;
- }
-
- SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism(),
- parameters(), sig.get(), hash.get());
- if (rv != SECSuccess) {
- return nullptr;
- }
-
- return sig;
+ bool SignHashedData(ScopedSECKEYPrivateKey& privKey, const DataBuffer& hash,
+ DataBuffer* sig) {
+ SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
+ static_cast<unsigned int>(hash.len())};
+ int sigLen = PK11_SignatureLen(privKey.get());
+ EXPECT_LT(0, sigLen);
+ sig->Allocate(static_cast<size_t>(sigLen));
+ SECItem sigItem = {siBuffer, toUcharPtr(sig->data()),
+ static_cast<unsigned int>(sig->len())};
+ SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism_,
+ parameters(), &sigItem, &hashItem);
+ return rv == SECSuccess;
}
- ScopedSECItem ImportPrivateKeyAndSignHashedData(const uint8_t* pkcs8,
- size_t pkcs8_len,
- const uint8_t* data,
- size_t data_len) {
- ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8, pkcs8_len));
+ bool ImportPrivateKeyAndSignHashedData(const DataBuffer& pkcs8,
+ const DataBuffer& data,
+ DataBuffer* sig) {
+ ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8));
if (!privKey) {
- return nullptr;
+ return false;
}
- ScopedSECItem hash(ComputeHash(data, data_len));
- if (!hash) {
- return nullptr;
+ DataBuffer hash;
+ if (!ComputeHash(data, &hash)) {
+ ADD_FAILURE() << "Failed to compute hash";
+ return false;
}
-
- return ScopedSECItem(SignHashedData(privKey, hash));
+ return SignHashedData(privKey, hash, sig);
}
- void Verify(const uint8_t* spki, size_t spki_len, const uint8_t* data,
- size_t data_len, const uint8_t* sig, size_t sig_len) {
- ScopedSECKEYPublicKey pubKey(ImportPublicKey(spki, spki_len));
+ void Verify(const Pkcs11SignatureTestParams& params, const DataBuffer& sig) {
+ ScopedSECKEYPublicKey pubKey(ImportPublicKey(params.spki_));
ASSERT_TRUE(pubKey);
- ScopedSECItem hash(ComputeHash(data, data_len));
- ASSERT_TRUE(hash);
-
- SECItem sigItem = {siBuffer, toUcharPtr(sig),
- static_cast<unsigned int>(sig_len)};
+ DataBuffer hash;
+ ASSERT_TRUE(ComputeHash(params.data_, &hash));
// Verify.
+ SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
+ static_cast<unsigned int>(hash.len())};
+ SECItem sigItem = {siBuffer, toUcharPtr(sig.data()),
+ static_cast<unsigned int>(sig.len())};
SECStatus rv = PK11_VerifyWithMechanism(
- pubKey.get(), mechanism(), parameters(), &sigItem, hash.get(), nullptr);
+ pubKey.get(), mechanism_, parameters(), &sigItem, &hashItem, nullptr);
EXPECT_EQ(rv, SECSuccess);
}
- void SignAndVerify(const uint8_t* pkcs8, size_t pkcs8_len,
- const uint8_t* spki, size_t spki_len, const uint8_t* data,
- size_t data_len) {
- ScopedSECItem sig(
- ImportPrivateKeyAndSignHashedData(pkcs8, pkcs8_len, data, data_len));
- ASSERT_TRUE(sig);
-
- Verify(spki, spki_len, data, data_len, sig->data, sig->len);
+ void Verify(const Pkcs11SignatureTestParams& params) {
+ Verify(params, params.signature_);
}
-};
-#define SIG_TEST_VECTOR_VERIFY(spki, data, sig) \
- Verify(spki, sizeof(spki), data, sizeof(data), sig, sizeof(sig));
+ void SignAndVerify(const Pkcs11SignatureTestParams& params) {
+ DataBuffer sig;
+ ASSERT_TRUE(
+ ImportPrivateKeyAndSignHashedData(params.pkcs8_, params.data_, &sig));
+ Verify(params, sig);
+ }
-#define SIG_TEST_VECTOR_SIGN_VERIFY(pkcs8, spki, data) \
- SignAndVerify(pkcs8, sizeof(pkcs8), spki, sizeof(spki), data, sizeof(data));
+ private:
+ CK_MECHANISM_TYPE mechanism_;
+ SECOidTag hash_oid_;
+};
} // namespace nss_test
diff --git a/security/nss/gtests/softoken_gtest/Makefile b/security/nss/gtests/softoken_gtest/Makefile
new file mode 100644
index 000000000..996669782
--- /dev/null
+++ b/security/nss/gtests/softoken_gtest/Makefile
@@ -0,0 +1,45 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include ../common/gtest.mk
+
+CFLAGS += -I$(CORE_DEPTH)/lib/util
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
diff --git a/security/nss/gtests/softoken_gtest/manifest.mn b/security/nss/gtests/softoken_gtest/manifest.mn
new file mode 100644
index 000000000..4b34c099f
--- /dev/null
+++ b/security/nss/gtests/softoken_gtest/manifest.mn
@@ -0,0 +1,25 @@
+# -*- makefile -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+CORE_DEPTH = ../..
+DEPTH = ../..
+MODULE = nss
+
+CPPSRCS = \
+ softoken_gtest.cc \
+ $(NULL)
+
+INCLUDES += \
+ -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
+ -I$(CORE_DEPTH)/cpputil \
+ $(NULL)
+
+REQUIRES = nspr gtest
+
+PROGRAM = softoken_gtest
+
+EXTRA_LIBS = \
+ $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \
+ $(NULL)
diff --git a/security/nss/gtests/softoken_gtest/softoken_gtest.cc b/security/nss/gtests/softoken_gtest/softoken_gtest.cc
new file mode 100644
index 000000000..d61e2e75f
--- /dev/null
+++ b/security/nss/gtests/softoken_gtest/softoken_gtest.cc
@@ -0,0 +1,360 @@
+#include <cstdlib>
+#if defined(_WIN32)
+#include <windows.h>
+#include <codecvt>
+#endif
+
+#include "cert.h"
+#include "certdb.h"
+#include "nspr.h"
+#include "nss.h"
+#include "pk11pub.h"
+#include "secerr.h"
+
+#include "scoped_ptrs.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+namespace nss_test {
+
+// Given a prefix, attempts to create a unique directory that the user can do
+// work in without impacting other tests. For example, if given the prefix
+// "scratch", a directory like "scratch05c17b25" will be created in the current
+// working directory (or the location specified by NSS_GTEST_WORKDIR, if
+// defined).
+// Upon destruction, the implementation will attempt to delete the directory.
+// However, no attempt is made to first remove files in the directory - the
+// user is responsible for this. If the directory is not empty, deleting it will
+// fail.
+// Statistically, it is technically possible to fail to create a unique
+// directory name, but this is extremely unlikely given the expected workload of
+// this implementation.
+class ScopedUniqueDirectory {
+ public:
+ explicit ScopedUniqueDirectory(const std::string &prefix);
+
+ // NB: the directory must be empty upon destruction
+ ~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); }
+
+ const std::string &GetPath() { return mPath; }
+ const std::string &GetUTF8Path() { return mUTF8Path; }
+
+ private:
+ static const int RETRY_LIMIT = 5;
+ static void GenerateRandomName(/*in/out*/ std::string &prefix);
+ static bool TryMakingDirectory(/*in/out*/ std::string &prefix);
+
+ std::string mPath;
+ std::string mUTF8Path;
+};
+
+ScopedUniqueDirectory::ScopedUniqueDirectory(const std::string &prefix) {
+ std::string path;
+ const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
+ if (workingDirectory) {
+ path.assign(workingDirectory);
+ }
+ path.append(prefix);
+ for (int i = 0; i < RETRY_LIMIT; i++) {
+ std::string pathCopy(path);
+ // TryMakingDirectory will modify its input. If it fails, we want to throw
+ // away the modified result.
+ if (TryMakingDirectory(pathCopy)) {
+ mPath.assign(pathCopy);
+ break;
+ }
+ }
+ assert(mPath.length() > 0);
+#if defined(_WIN32)
+ // sqldb always uses UTF-8 regardless of the current system locale.
+ DWORD len =
+ MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0);
+ std::vector<wchar_t> buf(len, L'\0');
+ MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(),
+ buf.size());
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
+ mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end()));
+#else
+ mUTF8Path = mPath;
+#endif
+}
+
+void ScopedUniqueDirectory::GenerateRandomName(std::string &prefix) {
+ std::stringstream ss;
+ ss << prefix;
+ // RAND_MAX is at least 32767.
+ ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
+ // This will overwrite the value of prefix. This is a little inefficient, but
+ // at least it makes the code simple.
+ ss >> prefix;
+}
+
+bool ScopedUniqueDirectory::TryMakingDirectory(std::string &prefix) {
+ GenerateRandomName(prefix);
+#if defined(_WIN32)
+ return _mkdir(prefix.c_str()) == 0;
+#else
+ return mkdir(prefix.c_str(), 0777) == 0;
+#endif
+}
+
+class SoftokenTest : public ::testing::Test {
+ protected:
+ SoftokenTest() : mNSSDBDir("SoftokenTest.d-") {}
+ SoftokenTest(const std::string &prefix) : mNSSDBDir(prefix) {}
+
+ virtual void SetUp() {
+ std::string nssInitArg("sql:");
+ nssInitArg.append(mNSSDBDir.GetUTF8Path());
+ ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB,
+ NSS_INIT_NOROOTINIT));
+ }
+
+ virtual void TearDown() {
+ ASSERT_EQ(SECSuccess, NSS_Shutdown());
+ const std::string &nssDBDirPath = mNSSDBDir.GetPath();
+ ASSERT_EQ(0, unlink((nssDBDirPath + "/cert9.db").c_str()));
+ ASSERT_EQ(0, unlink((nssDBDirPath + "/key4.db").c_str()));
+ ASSERT_EQ(0, unlink((nssDBDirPath + "/pkcs11.txt").c_str()));
+ }
+
+ ScopedUniqueDirectory mNSSDBDir;
+};
+
+TEST_F(SoftokenTest, ResetSoftokenEmptyPassword) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
+ EXPECT_EQ(SECSuccess, PK11_ResetToken(slot.get(), nullptr));
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
+}
+
+TEST_F(SoftokenTest, ResetSoftokenNonEmptyPassword) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, "password"));
+ EXPECT_EQ(SECSuccess, PK11_ResetToken(slot.get(), nullptr));
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, "password2"));
+}
+
+// Test certificate to use in the CreateObject tests.
+static const CK_OBJECT_CLASS cko_nss_trust = CKO_NSS_TRUST;
+static const CK_BBOOL ck_false = CK_FALSE;
+static const CK_BBOOL ck_true = CK_TRUE;
+static const CK_TRUST ckt_nss_must_verify_trust = CKT_NSS_MUST_VERIFY_TRUST;
+static const CK_TRUST ckt_nss_trusted_delegator = CKT_NSS_TRUSTED_DELEGATOR;
+static const CK_ATTRIBUTE attributes[] = {
+ {CKA_CLASS, (void *)&cko_nss_trust, (PRUint32)sizeof(CK_OBJECT_CLASS)},
+ {CKA_TOKEN, (void *)&ck_true, (PRUint32)sizeof(CK_BBOOL)},
+ {CKA_PRIVATE, (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL)},
+ {CKA_MODIFIABLE, (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL)},
+ {CKA_LABEL,
+ (void *)"Symantec Class 2 Public Primary Certification Authority - G4",
+ (PRUint32)61},
+ {CKA_CERT_SHA1_HASH,
+ (void *)"\147\044\220\056\110\001\260\042\226\100\020\106\264\261\147\054"
+ "\251\165\375\053",
+ (PRUint32)20},
+ {CKA_CERT_MD5_HASH,
+ (void *)"\160\325\060\361\332\224\227\324\327\164\337\276\355\150\336\226",
+ (PRUint32)16},
+ {CKA_ISSUER,
+ (void *)"\060\201\224\061\013\060\011\006\003\125\004\006\023\002\125\123"
+ "\061\035\060\033\006\003\125\004\012\023\024\123\171\155\141\156"
+ "\164\145\143\040\103\157\162\160\157\162\141\164\151\157\156\061"
+ "\037\060\035\006\003\125\004\013\023\026\123\171\155\141\156\164"
+ "\145\143\040\124\162\165\163\164\040\116\145\164\167\157\162\153"
+ "\061\105\060\103\006\003\125\004\003\023\074\123\171\155\141\156"
+ "\164\145\143\040\103\154\141\163\163\040\062\040\120\165\142\154"
+ "\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151"
+ "\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151"
+ "\164\171\040\055\040\107\064",
+ (PRUint32)151},
+ {CKA_SERIAL_NUMBER,
+ (void *)"\002\020\064\027\145\022\100\073\267\126\200\055\200\313\171\125"
+ "\246\036",
+ (PRUint32)18},
+ {CKA_TRUST_SERVER_AUTH, (void *)&ckt_nss_must_verify_trust,
+ (PRUint32)sizeof(CK_TRUST)},
+ {CKA_TRUST_EMAIL_PROTECTION, (void *)&ckt_nss_trusted_delegator,
+ (PRUint32)sizeof(CK_TRUST)},
+ {CKA_TRUST_CODE_SIGNING, (void *)&ckt_nss_must_verify_trust,
+ (PRUint32)sizeof(CK_TRUST)},
+ {CKA_TRUST_STEP_UP_APPROVED, (void *)&ck_false,
+ (PRUint32)sizeof(CK_BBOOL)}};
+
+TEST_F(SoftokenTest, CreateObjectNonEmptyPassword) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, "password"));
+ EXPECT_EQ(SECSuccess, PK11_Logout(slot.get()));
+ ScopedPK11GenericObject obj(PK11_CreateGenericObject(
+ slot.get(), attributes, PR_ARRAY_SIZE(attributes), true));
+ EXPECT_EQ(nullptr, obj);
+}
+
+TEST_F(SoftokenTest, CreateObjectChangePassword) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
+ EXPECT_EQ(SECSuccess, PK11_ChangePW(slot.get(), "", "password"));
+ EXPECT_EQ(SECSuccess, PK11_Logout(slot.get()));
+ ScopedPK11GenericObject obj(PK11_CreateGenericObject(
+ slot.get(), attributes, PR_ARRAY_SIZE(attributes), true));
+ EXPECT_EQ(nullptr, obj);
+}
+
+TEST_F(SoftokenTest, CreateObjectChangeToEmptyPassword) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, "password"));
+ EXPECT_EQ(SECSuccess, PK11_ChangePW(slot.get(), "password", ""));
+ // PK11_Logout returnes an error and SEC_ERROR_TOKEN_NOT_LOGGED_IN if the user
+ // is not "logged in".
+ EXPECT_EQ(SECFailure, PK11_Logout(slot.get()));
+ EXPECT_EQ(SEC_ERROR_TOKEN_NOT_LOGGED_IN, PORT_GetError());
+ ScopedPK11GenericObject obj(PK11_CreateGenericObject(
+ slot.get(), attributes, PR_ARRAY_SIZE(attributes), true));
+ // Because there's no password we can't logout and the operation should have
+ // succeeded.
+ EXPECT_NE(nullptr, obj);
+}
+
+class SoftokenNonAsciiTest : public SoftokenTest {
+ protected:
+ SoftokenNonAsciiTest() : SoftokenTest("SoftokenTest.\xF7-") {}
+};
+
+TEST_F(SoftokenNonAsciiTest, NonAsciiPathWorking) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
+ EXPECT_EQ(SECSuccess, PK11_ResetToken(slot.get(), nullptr));
+ EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
+}
+
+// This is just any X509 certificate. Its contents don't matter.
+static unsigned char certDER[] = {
+ 0x30, 0x82, 0x01, 0xEF, 0x30, 0x82, 0x01, 0x94, 0xA0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x49, 0xC4, 0xC4, 0x4A, 0xB6, 0x86, 0x07, 0xA3, 0x06,
+ 0xDC, 0x4D, 0xC8, 0xC3, 0xFE, 0xC7, 0x21, 0x3A, 0x2D, 0xE4, 0xDA, 0x30,
+ 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+ 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C,
+ 0x04, 0x74, 0x65, 0x73, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31,
+ 0x35, 0x31, 0x31, 0x32, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
+ 0x18, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0C, 0x04, 0x74, 0x65, 0x73, 0x74, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82,
+ 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x88, 0x51, 0xA8, 0x44,
+ 0x8E, 0x16, 0xD6, 0x41, 0xFD, 0x6E, 0xB6, 0x88, 0x06, 0x36, 0x10, 0x3D,
+ 0x3C, 0x13, 0xD9, 0xEA, 0xE4, 0x35, 0x4A, 0xB4, 0xEC, 0xF5, 0x68, 0x57,
+ 0x6C, 0x24, 0x7B, 0xC1, 0xC7, 0x25, 0xA8, 0xE0, 0xD8, 0x1F, 0xBD, 0xB1,
+ 0x9C, 0x06, 0x9B, 0x6E, 0x1A, 0x86, 0xF2, 0x6B, 0xE2, 0xAF, 0x5A, 0x75,
+ 0x6B, 0x6A, 0x64, 0x71, 0x08, 0x7A, 0xA5, 0x5A, 0xA7, 0x45, 0x87, 0xF7,
+ 0x1C, 0xD5, 0x24, 0x9C, 0x02, 0x7E, 0xCD, 0x43, 0xFC, 0x1E, 0x69, 0xD0,
+ 0x38, 0x20, 0x29, 0x93, 0xAB, 0x20, 0xC3, 0x49, 0xE4, 0xDB, 0xB9, 0x4C,
+ 0xC2, 0x6B, 0x6C, 0x0E, 0xED, 0x15, 0x82, 0x0F, 0xF1, 0x7E, 0xAD, 0x69,
+ 0x1A, 0xB1, 0xD3, 0x02, 0x3A, 0x8B, 0x2A, 0x41, 0xEE, 0xA7, 0x70, 0xE0,
+ 0x0F, 0x0D, 0x8D, 0xFD, 0x66, 0x0B, 0x2B, 0xB0, 0x24, 0x92, 0xA4, 0x7D,
+ 0xB9, 0x88, 0x61, 0x79, 0x90, 0xB1, 0x57, 0x90, 0x3D, 0xD2, 0x3B, 0xC5,
+ 0xE0, 0xB8, 0x48, 0x1F, 0xA8, 0x37, 0xD3, 0x88, 0x43, 0xEF, 0x27, 0x16,
+ 0xD8, 0x55, 0xB7, 0x66, 0x5A, 0xAA, 0x7E, 0x02, 0x90, 0x2F, 0x3A, 0x7B,
+ 0x10, 0x80, 0x06, 0x24, 0xCC, 0x1C, 0x6C, 0x97, 0xAD, 0x96, 0x61, 0x5B,
+ 0xB7, 0xE2, 0x96, 0x12, 0xC0, 0x75, 0x31, 0xA3, 0x0C, 0x91, 0xDD, 0xB4,
+ 0xCA, 0xF7, 0xFC, 0xAD, 0x1D, 0x25, 0xD3, 0x09, 0xEF, 0xB9, 0x17, 0x0E,
+ 0xA7, 0x68, 0xE1, 0xB3, 0x7B, 0x2F, 0x22, 0x6F, 0x69, 0xE3, 0xB4, 0x8A,
+ 0x95, 0x61, 0x1D, 0xEE, 0x26, 0xD6, 0x25, 0x9D, 0xAB, 0x91, 0x08, 0x4E,
+ 0x36, 0xCB, 0x1C, 0x24, 0x04, 0x2C, 0xBF, 0x16, 0x8B, 0x2F, 0xE5, 0xF1,
+ 0x8F, 0x99, 0x17, 0x31, 0xB8, 0xB3, 0xFE, 0x49, 0x23, 0xFA, 0x72, 0x51,
+ 0xC4, 0x31, 0xD5, 0x03, 0xAC, 0xDA, 0x18, 0x0A, 0x35, 0xED, 0x8D, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20,
+ 0x5C, 0x75, 0x51, 0x9F, 0x13, 0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20,
+ 0xA3, 0xBC, 0x06, 0x30, 0x91, 0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64,
+ 0xEC, 0xFD, 0xCB, 0x42, 0x80, 0x0A, 0x70, 0xE6, 0x02, 0x21, 0x00, 0x82,
+ 0x12, 0xF7, 0xE5, 0xEA, 0x40, 0x27, 0xFD, 0xF7, 0xC0, 0x0E, 0x25, 0xF3,
+ 0x3E, 0x34, 0x95, 0x80, 0xB9, 0xA3, 0x38, 0xE0, 0x56, 0x68, 0xDA, 0xE5,
+ 0xC1, 0xF5, 0x37, 0xC7, 0xB5, 0xCE, 0x0D};
+
+struct PasswordPair {
+ const char *mInitialPassword;
+ const char *mSecondPassword;
+};
+
+class SoftokenPasswordChangeTest
+ : public SoftokenTest,
+ public ::testing::WithParamInterface<PasswordPair> {};
+
+TEST_P(SoftokenPasswordChangeTest, KeepTrustAfterPasswordChange) {
+ const PasswordPair &passwords = GetParam();
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ // Set a password.
+ EXPECT_EQ(SECSuccess,
+ PK11_InitPin(slot.get(), nullptr, passwords.mInitialPassword));
+ SECItem certDERItem = {siBuffer, certDER, sizeof(certDER)};
+ // Import a certificate.
+ ScopedCERTCertificate cert(CERT_NewTempCertificate(
+ CERT_GetDefaultCertDB(), &certDERItem, nullptr, true, true));
+ EXPECT_TRUE(cert);
+ SECStatus result =
+ PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE, "test", false);
+ EXPECT_EQ(SECSuccess, result);
+ // Set a trust value.
+ CERTCertTrust trust = {CERTDB_TRUSTED_CLIENT_CA | CERTDB_NS_TRUSTED_CA |
+ CERTDB_TRUSTED_CA | CERTDB_VALID_CA,
+ 0, 0};
+ result = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
+ EXPECT_EQ(SECSuccess, result);
+ // Release the certificate to ensure we get it from the DB rather than an
+ // in-memory cache, below.
+ cert = nullptr;
+ // Change the password.
+ result = PK11_ChangePW(slot.get(), passwords.mInitialPassword,
+ passwords.mSecondPassword);
+ EXPECT_EQ(SECSuccess, result);
+ // Look up the certificate again.
+ ScopedCERTCertificate newCert(
+ PK11_FindCertFromDERCertItem(slot.get(), &certDERItem, nullptr));
+ EXPECT_TRUE(newCert.get());
+ // The trust should be the same as before.
+ CERTCertTrust newTrust = {0, 0, 0};
+ result = CERT_GetCertTrust(newCert.get(), &newTrust);
+ EXPECT_EQ(SECSuccess, result);
+ EXPECT_EQ(trust.sslFlags, newTrust.sslFlags);
+ EXPECT_EQ(trust.emailFlags, newTrust.emailFlags);
+ EXPECT_EQ(trust.objectSigningFlags, newTrust.objectSigningFlags);
+}
+
+static const PasswordPair PASSWORD_CHANGE_TESTS[] = {
+ {"password", ""}, // non-empty to empty password
+ {"", "password"}, // empty to non-empty password
+ {"password", "password2"}, // non-empty to non-empty password
+};
+
+INSTANTIATE_TEST_CASE_P(SoftokenPasswordChangeTests, SoftokenPasswordChangeTest,
+ ::testing::ValuesIn(PASSWORD_CHANGE_TESTS));
+
+class SoftokenNoDBTest : public ::testing::Test {};
+
+TEST_F(SoftokenNoDBTest, NeedUserInitNoDB) {
+ ASSERT_EQ(SECSuccess, NSS_NoDB_Init("."));
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(slot);
+ EXPECT_EQ(PR_FALSE, PK11_NeedUserInit(slot.get()));
+
+ // When shutting down in here we have to release the slot first.
+ slot = nullptr;
+ ASSERT_EQ(SECSuccess, NSS_Shutdown());
+}
+
+} // namespace nss_test
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/security/nss/gtests/softoken_gtest/softoken_gtest.gyp b/security/nss/gtests/softoken_gtest/softoken_gtest.gyp
new file mode 100644
index 000000000..cff0ea414
--- /dev/null
+++ b/security/nss/gtests/softoken_gtest/softoken_gtest.gyp
@@ -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/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi',
+ '../common/gtest.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'softoken_gtest',
+ 'type': 'executable',
+ 'sources': [
+ 'softoken_gtest.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports',
+ '<(DEPTH)/lib/util/util.gyp:nssutil3',
+ '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
+ ],
+ 'conditions': [
+ [ 'test_build==1', {
+ 'dependencies': [
+ '<(DEPTH)/lib/nss/nss.gyp:nss_static',
+ '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
+ '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
+ '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
+ '<(DEPTH)/lib/certdb/certdb.gyp:certdb',
+ '<(DEPTH)/lib/base/base.gyp:nssb',
+ '<(DEPTH)/lib/dev/dev.gyp:nssdev',
+ '<(DEPTH)/lib/pki/pki.gyp:nsspki',
+ '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
+ ],
+ }, {
+ 'dependencies': [
+ '<(DEPTH)/lib/nss/nss.gyp:nss3',
+ '<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
+ ],
+ }],
+ ],
+ }
+ ],
+ 'target_defaults': {
+ 'include_dirs': [
+ '../../lib/util'
+ ]
+ },
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/gtests/ssl_gtest/Makefile b/security/nss/gtests/ssl_gtest/Makefile
index a9a9290e0..95c111aeb 100644
--- a/security/nss/gtests/ssl_gtest/Makefile
+++ b/security/nss/gtests/ssl_gtest/Makefile
@@ -29,10 +29,6 @@ include ../common/gtest.mk
CFLAGS += -I$(CORE_DEPTH)/lib/ssl
-ifdef NSS_SSL_ENABLE_ZLIB
-include $(CORE_DEPTH)/coreconf/zlib.mk
-endif
-
ifdef NSS_DISABLE_TLS_1_3
NSS_DISABLE_TLS_1_3=1
# Run parameterized tests only, for which we can easily exclude TLS 1.3
diff --git a/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc b/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc
new file mode 100644
index 000000000..110cfa13a
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern "C" {
+#include "sslbloom.h"
+}
+
+#include "gtest_utils.h"
+
+namespace nss_test {
+
+// Some random-ish inputs to test with. These don't result in collisions in any
+// of the configurations that are tested below.
+static const uint8_t kHashes1[] = {
+ 0x79, 0x53, 0xb8, 0xdd, 0x6b, 0x98, 0xce, 0x00, 0xb7, 0xdc, 0xe8,
+ 0x03, 0x70, 0x8c, 0xe3, 0xac, 0x06, 0x8b, 0x22, 0xfd, 0x0e, 0x34,
+ 0x48, 0xe6, 0xe5, 0xe0, 0x8a, 0xd6, 0x16, 0x18, 0xe5, 0x48};
+static const uint8_t kHashes2[] = {
+ 0xc6, 0xdd, 0x6e, 0xc4, 0x76, 0xb8, 0x55, 0xf2, 0xa4, 0xfc, 0x59,
+ 0x04, 0xa4, 0x90, 0xdc, 0xa7, 0xa7, 0x0d, 0x94, 0x8f, 0xc2, 0xdc,
+ 0x15, 0x6d, 0x48, 0x93, 0x9d, 0x05, 0xbb, 0x9a, 0xbc, 0xc1};
+
+typedef struct {
+ unsigned int k;
+ unsigned int bits;
+} BloomFilterConfig;
+
+class BloomFilterTest
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<BloomFilterConfig> {
+ public:
+ BloomFilterTest() : filter_() {}
+
+ void SetUp() { Init(); }
+
+ void TearDown() { sslBloom_Destroy(&filter_); }
+
+ protected:
+ void Init() {
+ if (filter_.filter) {
+ sslBloom_Destroy(&filter_);
+ }
+ ASSERT_EQ(SECSuccess,
+ sslBloom_Init(&filter_, GetParam().k, GetParam().bits));
+ }
+
+ bool Check(const uint8_t* hashes) {
+ return sslBloom_Check(&filter_, hashes) ? true : false;
+ }
+
+ void Add(const uint8_t* hashes, bool expect_collision = false) {
+ EXPECT_EQ(expect_collision, sslBloom_Add(&filter_, hashes) ? true : false);
+ EXPECT_TRUE(Check(hashes));
+ }
+
+ sslBloomFilter filter_;
+};
+
+TEST_P(BloomFilterTest, InitOnly) {}
+
+TEST_P(BloomFilterTest, AddToEmpty) {
+ EXPECT_FALSE(Check(kHashes1));
+ Add(kHashes1);
+}
+
+TEST_P(BloomFilterTest, AddTwo) {
+ Add(kHashes1);
+ Add(kHashes2);
+}
+
+TEST_P(BloomFilterTest, AddOneTwice) {
+ Add(kHashes1);
+ Add(kHashes1, true);
+}
+
+TEST_P(BloomFilterTest, Zero) {
+ Add(kHashes1);
+ sslBloom_Zero(&filter_);
+ EXPECT_FALSE(Check(kHashes1));
+ EXPECT_FALSE(Check(kHashes2));
+}
+
+TEST_P(BloomFilterTest, Fill) {
+ sslBloom_Fill(&filter_);
+ EXPECT_TRUE(Check(kHashes1));
+ EXPECT_TRUE(Check(kHashes2));
+}
+
+static const BloomFilterConfig kBloomFilterConfigurations[] = {
+ {1, 1}, // 1 hash, 1 bit input - high chance of collision.
+ {1, 2}, // 1 hash, 2 bits - smaller than the basic unit size.
+ {1, 3}, // 1 hash, 3 bits - same as basic unit size.
+ {1, 4}, // 1 hash, 4 bits - 2 octets each.
+ {3, 10}, // 3 hashes over a reasonable number of bits.
+ {3, 3}, // Test that we can read multiple bits.
+ {4, 15}, // A credible filter.
+ {2, 18}, // A moderately large allocation.
+ {16, 16}, // Insane, use all of the bits from the hashes.
+ {16, 9}, // This also uses all of the bits from the hashes.
+};
+
+INSTANTIATE_TEST_CASE_P(BloomFilterConfigurations, BloomFilterTest,
+ ::testing::ValuesIn(kBloomFilterConfigurations));
+
+} // namespace nspr_test
diff --git a/security/nss/gtests/ssl_gtest/libssl_internals.c b/security/nss/gtests/ssl_gtest/libssl_internals.c
index 97b8354ae..887d85278 100644
--- a/security/nss/gtests/ssl_gtest/libssl_internals.c
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.c
@@ -34,18 +34,17 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
return SECFailure;
}
- ssl3_InitState(ss);
ssl3_RestartHandshakeHashes(ss);
// Ensure we don't overrun hs.client_random.
rnd_len = PR_MIN(SSL3_RANDOM_LENGTH, rnd_len);
- // Zero the client_random struct.
- PORT_Memset(&ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
+ // Zero the client_random.
+ PORT_Memset(ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
// Copy over the challenge bytes.
size_t offset = SSL3_RANDOM_LENGTH - rnd_len;
- PORT_Memcpy(&ss->ssl3.hs.client_random.rand[offset], rnd, rnd_len);
+ PORT_Memcpy(ss->ssl3.hs.client_random + offset, rnd, rnd_len);
// Rehash the SSLv2 client hello message.
return ssl3_UpdateHandshakeHashes(ss, msg, msg_len);
@@ -73,10 +72,11 @@ SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu) {
return SECFailure;
}
ss->ssl3.mtu = mtu;
+ ss->ssl3.hs.rtRetries = 0; /* Avoid DTLS shrinking the MTU any more. */
return SECSuccess;
}
-PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd) {
+PRInt32 SSLInt_CountCipherSpecs(PRFileDesc *fd) {
PRCList *cur_p;
PRInt32 ct = 0;
@@ -92,7 +92,7 @@ PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd) {
return ct;
}
-void SSLInt_PrintTls13CipherSpecs(PRFileDesc *fd) {
+void SSLInt_PrintCipherSpecs(const char *label, PRFileDesc *fd) {
PRCList *cur_p;
sslSocket *ss = ssl_FindSocket(fd);
@@ -100,27 +100,31 @@ void SSLInt_PrintTls13CipherSpecs(PRFileDesc *fd) {
return;
}
- fprintf(stderr, "Cipher specs\n");
+ fprintf(stderr, "Cipher specs for %s\n", label);
for (cur_p = PR_NEXT_LINK(&ss->ssl3.hs.cipherSpecs);
cur_p != &ss->ssl3.hs.cipherSpecs; cur_p = PR_NEXT_LINK(cur_p)) {
ssl3CipherSpec *spec = (ssl3CipherSpec *)cur_p;
- fprintf(stderr, " %s\n", spec->phase);
+ fprintf(stderr, " %s spec epoch=%d (%s) refct=%d\n", SPEC_DIR(spec),
+ spec->epoch, spec->phase, spec->refCt);
}
}
-/* Force a timer expiry by backdating when the timer was started.
- * We could set the remaining time to 0 but then backoff would not
- * work properly if we decide to test it. */
-void SSLInt_ForceTimerExpiry(PRFileDesc *fd) {
+/* Force a timer expiry by backdating when all active timers were started. We
+ * could set the remaining time to 0 but then backoff would not work properly if
+ * we decide to test it. */
+SECStatus SSLInt_ShiftDtlsTimers(PRFileDesc *fd, PRIntervalTime shift) {
+ size_t i;
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
- return;
+ return SECFailure;
}
- if (!ss->ssl3.hs.rtTimerCb) return;
-
- ss->ssl3.hs.rtTimerStarted =
- PR_IntervalNow() - PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs + 1);
+ for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+ if (ss->ssl3.hs.timers[i].cb) {
+ ss->ssl3.hs.timers[i].started -= shift;
+ }
+ }
+ return SECSuccess;
}
#define CHECK_SECRET(secret) \
@@ -136,7 +140,6 @@ PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd) {
}
CHECK_SECRET(currentSecret);
- CHECK_SECRET(resumptionMasterSecret);
CHECK_SECRET(dheSecret);
CHECK_SECRET(clientEarlyTrafficSecret);
CHECK_SECRET(clientHsTrafficSecret);
@@ -226,28 +229,7 @@ PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type) {
return PR_TRUE;
}
-PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd) {
- sslSocket *ss = ssl_FindSocket(fd);
- if (!ss) {
- return PR_FALSE;
- }
-
- ssl_GetSSL3HandshakeLock(ss);
- ssl_GetXmitBufLock(ss);
-
- SECStatus rv = tls13_SendNewSessionTicket(ss);
- if (rv == SECSuccess) {
- rv = ssl3_FlushHandshake(ss, 0);
- }
-
- ssl_ReleaseXmitBufLock(ss);
- ssl_ReleaseSSL3HandshakeLock(ss);
-
- return rv == SECSuccess;
-}
-
SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
- PRUint64 epoch;
sslSocket *ss;
ssl3CipherSpec *spec;
@@ -255,43 +237,40 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
if (!ss) {
return SECFailure;
}
- if (to >= (1ULL << 48)) {
+ if (to >= RECORD_SEQ_MAX) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_GetSpecWriteLock(ss);
spec = ss->ssl3.crSpec;
- epoch = spec->read_seq_num >> 48;
- spec->read_seq_num = (epoch << 48) | to;
+ spec->seqNum = to;
/* For DTLS, we need to fix the record sequence number. For this, we can just
* scrub the entire structure on the assumption that the new sequence number
* is far enough past the last received sequence number. */
- if (to <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
+ if (spec->seqNum <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- dtls_RecordSetRecvd(&spec->recvdRecords, to);
+ dtls_RecordSetRecvd(&spec->recvdRecords, spec->seqNum);
ssl_ReleaseSpecWriteLock(ss);
return SECSuccess;
}
SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
- PRUint64 epoch;
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
- if (to >= (1ULL << 48)) {
+ if (to >= RECORD_SEQ_MAX) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_GetSpecWriteLock(ss);
- epoch = ss->ssl3.cwSpec->write_seq_num >> 48;
- ss->ssl3.cwSpec->write_seq_num = (epoch << 48) | to;
+ ss->ssl3.cwSpec->seqNum = to;
ssl_ReleaseSpecWriteLock(ss);
return SECSuccess;
}
@@ -305,9 +284,9 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra) {
return SECFailure;
}
ssl_GetSpecReadLock(ss);
- to = ss->ssl3.cwSpec->write_seq_num + DTLS_RECVD_RECORDS_WINDOW + extra;
+ to = ss->ssl3.cwSpec->seqNum + DTLS_RECVD_RECORDS_WINDOW + extra;
ssl_ReleaseSpecReadLock(ss);
- return SSLInt_AdvanceWriteSeqNum(fd, to & RECORD_SEQ_MAX);
+ return SSLInt_AdvanceWriteSeqNum(fd, to);
}
SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
@@ -333,46 +312,20 @@ SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
return SECSuccess;
}
-static ssl3KeyMaterial *GetKeyingMaterial(PRBool isServer,
- ssl3CipherSpec *spec) {
- return isServer ? &spec->server : &spec->client;
+PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec) {
+ return spec->keyMaterial.key;
}
-PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec) {
- return GetKeyingMaterial(isServer, spec)->write_key;
+SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec) {
+ return spec->cipherDef->calg;
}
-SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
- ssl3CipherSpec *spec) {
- return spec->cipher_def->calg;
+const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec) {
+ return spec->keyMaterial.iv;
}
-unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec) {
- return GetKeyingMaterial(isServer, spec)->write_iv;
-}
-
-SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd) {
- sslSocket *ss;
-
- ss = ssl_FindSocket(fd);
- if (!ss) {
- return SECFailure;
- }
-
- ss->opt.enableShortHeaders = PR_TRUE;
- return SECSuccess;
-}
-
-SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result) {
- sslSocket *ss;
-
- ss = ssl_FindSocket(fd);
- if (!ss) {
- return SECFailure;
- }
-
- *result = ss->ssl3.hs.shortHeaders;
- return SECSuccess;
+PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec) {
+ return spec->epoch;
}
void SSLInt_SetTicketLifetime(uint32_t lifetime) {
@@ -405,3 +358,21 @@ SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) {
return SECSuccess;
}
+
+void SSLInt_RolloverAntiReplay(void) {
+ tls13_AntiReplayRollover(ssl_TimeUsec());
+}
+
+SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
+ PRUint16 *writeEpoch) {
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss || !readEpoch || !writeEpoch) {
+ return SECFailure;
+ }
+
+ ssl_GetSpecReadLock(ss);
+ *readEpoch = ss->ssl3.crSpec->epoch;
+ *writeEpoch = ss->ssl3.cwSpec->epoch;
+ ssl_ReleaseSpecReadLock(ss);
+ return SECSuccess;
+}
diff --git a/security/nss/gtests/ssl_gtest/libssl_internals.h b/security/nss/gtests/ssl_gtest/libssl_internals.h
index 33709c4b4..95d4afdaf 100644
--- a/security/nss/gtests/ssl_gtest/libssl_internals.h
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.h
@@ -24,9 +24,9 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext);
void SSLInt_ClearSelfEncryptKey();
void SSLInt_SetSelfEncryptMacKey(PK11SymKey *key);
-PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd);
-void SSLInt_PrintTls13CipherSpecs(PRFileDesc *fd);
-void SSLInt_ForceTimerExpiry(PRFileDesc *fd);
+PRInt32 SSLInt_CountCipherSpecs(PRFileDesc *fd);
+void SSLInt_PrintCipherSpecs(const char *label, PRFileDesc *fd);
+SECStatus SSLInt_ShiftDtlsTimers(PRFileDesc *fd, PRIntervalTime shift);
SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu);
PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd);
PRBool SSLInt_DamageClientHsTrafficSecret(PRFileDesc *fd);
@@ -35,23 +35,23 @@ PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd);
SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len);
PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType);
PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type);
-PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd);
SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
+SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
+ PRUint16 *writeEpoch);
SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
sslCipherSpecChangedFunc func,
void *arg);
-PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec);
-SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
- ssl3CipherSpec *spec);
-unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec);
-SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd);
-SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result);
+PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec);
+PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec);
+SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec);
+const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec);
void SSLInt_SetTicketLifetime(uint32_t lifetime);
void SSLInt_SetMaxEarlyDataSize(uint32_t size);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
+void SSLInt_RolloverAntiReplay(void);
#endif // ndef libssl_internals_h_
diff --git a/security/nss/gtests/ssl_gtest/manifest.mn b/security/nss/gtests/ssl_gtest/manifest.mn
index cc729c0f1..5d893bab3 100644
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -12,11 +12,13 @@ CSRCS = \
$(NULL)
CPPSRCS = \
+ bloomfilter_unittest.cc \
ssl_0rtt_unittest.cc \
ssl_agent_unittest.cc \
ssl_auth_unittest.cc \
ssl_cert_ext_unittest.cc \
ssl_ciphersuite_unittest.cc \
+ ssl_custext_unittest.cc \
ssl_damage_unittest.cc \
ssl_dhe_unittest.cc \
ssl_drop_unittest.cc \
@@ -29,11 +31,16 @@ CPPSRCS = \
ssl_gather_unittest.cc \
ssl_gtest.cc \
ssl_hrr_unittest.cc \
+ ssl_keylog_unittest.cc \
+ ssl_keyupdate_unittest.cc \
ssl_loopback_unittest.cc \
+ ssl_misc_unittest.cc \
ssl_record_unittest.cc \
ssl_resumption_unittest.cc \
+ ssl_renegotiation_unittest.cc \
ssl_skip_unittest.cc \
ssl_staticrsa_unittest.cc \
+ ssl_tls13compat_unittest.cc \
ssl_v2_client_hello_unittest.cc \
ssl_version_unittest.cc \
ssl_versionpolicy_unittest.cc \
diff --git a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
index 85b7011a1..a60295490 100644
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -7,6 +7,7 @@
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
+#include "sslexp.h"
#include "sslproto.h"
extern "C" {
@@ -44,6 +45,93 @@ TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
SendReceive();
}
+TEST_P(TlsConnectTls13, ZeroRttApparentReplayAfterRestart) {
+ // The test fixtures call SSL_SetupAntiReplay() in SetUp(). This results in
+ // 0-RTT being rejected until at least one window passes. SetupFor0Rtt()
+ // forces a rollover of the anti-replay filters, which clears this state.
+ // Here, we do the setup manually here without that forced rollover.
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->Set0RttEnabled(true); // So we signal that we allow 0-RTT.
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys();
+
+ Reset();
+ StartConnect();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
+class TlsZeroRttReplayTest : public TlsConnectTls13 {
+ private:
+ class SaveFirstPacket : public PacketFilter {
+ public:
+ PacketFilter::Action Filter(const DataBuffer& input,
+ DataBuffer* output) override {
+ if (!packet_.len() && input.len()) {
+ packet_ = input;
+ }
+ return KEEP;
+ }
+
+ const DataBuffer& packet() const { return packet_; }
+
+ private:
+ DataBuffer packet_;
+ };
+
+ protected:
+ void RunTest(bool rollover) {
+ // Run the initial handshake
+ SetupForZeroRtt();
+
+ // Now run a true 0-RTT handshake, but capture the first packet.
+ auto first_packet = std::make_shared<SaveFirstPacket>();
+ client_->SetPacketFilter(first_packet);
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ EXPECT_LT(0U, first_packet->packet().len());
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+
+ if (rollover) {
+ SSLInt_RolloverAntiReplay();
+ }
+
+ // Now replay that packet against the server.
+ Reset();
+ server_->StartConnect();
+ server_->Set0RttEnabled(true);
+
+ // Capture the early_data extension, which should not appear.
+ auto early_data_ext =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_early_data_xtn);
+ server_->SetPacketFilter(early_data_ext);
+
+ // Finally, replay the ClientHello and force the server to consume it. Stop
+ // after the server sends its first flight; the client will not be able to
+ // complete this handshake.
+ server_->adapter()->PacketReceived(first_packet->packet());
+ server_->Handshake();
+ EXPECT_FALSE(early_data_ext->captured());
+ }
+};
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplay) { RunTest(false); }
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { RunTest(true); }
+
// Test that we don't try to send 0-RTT data when the server sent
// us a ticket without the 0-RTT flags.
TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
@@ -52,8 +140,7 @@ TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
Reset();
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
// Now turn on 0-RTT but too late for the ticket.
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
@@ -80,8 +167,7 @@ TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
ExpectResumption(RESUME_NONE);
server_->Set0RttEnabled(true);
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
// Client sends ordinary ClientHello.
client_->Handshake();
@@ -99,6 +185,61 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
CheckKeys();
}
+// A small sleep after sending the ClientHello means that the ticket age that
+// arrives at the server is too low. With a small tolerance for variation in
+// ticket age (which is determined by the |window| parameter that is passed to
+// SSL_SetupAntiReplay()), the server then rejects early data.
+TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
+ SSLInt_RolloverAntiReplay(); // Make sure to flush replay state.
+ SSLInt_RolloverAntiReplay();
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false, []() {
+ PR_Sleep(PR_MillisecondsToInterval(10));
+ return true;
+ });
+ Handshake();
+ ExpectEarlyDataAccepted(false);
+ CheckConnected();
+ SendReceive();
+}
+
+// In this test, we falsely inflate the estimate of the RTT by delaying the
+// ServerHello on the first handshake. This results in the server estimating a
+// higher value of the ticket age than the client ultimately provides. Add a
+// small tolerance for variation in ticket age and the ticket will appear to
+// arrive prematurely, causing the server to reject early data.
+TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->Set0RttEnabled(true);
+ StartConnect();
+ client_->Handshake(); // ClientHello
+ server_->Handshake(); // ServerHello
+ PR_Sleep(PR_MillisecondsToInterval(10));
+ Handshake(); // Remainder of handshake
+ CheckConnected();
+ SendReceive();
+ CheckKeys();
+
+ Reset();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
+ SSLInt_RolloverAntiReplay(); // Make sure to flush replay state.
+ SSLInt_RolloverAntiReplay();
+ ExpectResumption(RESUME_TICKET);
+ ExpectEarlyDataAccepted(false);
+ StartConnect();
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
EnableAlpn();
SetupForZeroRtt();
@@ -117,6 +258,14 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
CheckAlpn("a");
}
+// NOTE: In this test and those below, the client always sends
+// post-ServerHello alerts with the handshake keys, even if the server
+// has accepted 0-RTT. In some cases, as with errors in
+// EncryptedExtensions, the client can't know the server's behavior,
+// and in others it's just simpler. What the server is expecting
+// depends on whether it accepted 0-RTT or not. Eventually, we may
+// make the server trial decrypt.
+//
// Have the server negotiate a different ALPN value, and therefore
// reject 0-RTT.
TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeServer) {
@@ -155,12 +304,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnServer) {
client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, sizeof(b)));
client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
- ExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
return true;
});
- Handshake();
+ if (variant_ == ssl_variant_stream) {
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ } else {
+ client_->Handshake();
+ }
client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
- server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Set up with no ALPN and then set the client so it thinks it has ALPN.
@@ -175,12 +329,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) {
PRUint8 b[] = {'b'};
EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
- ExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
return true;
});
- Handshake();
+ if (variant_ == ssl_variant_stream) {
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ } else {
+ client_->Handshake();
+ }
client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
- server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Remove the old ALPN value and so the client will not offer early data.
@@ -218,9 +377,7 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngrade) {
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
- client_->StartConnect();
- server_->StartConnect();
-
+ StartConnect();
// We will send the early data xtn without sending actual early data. Thus
// a 1.2 server shouldn't fail until the client sends an alert because the
// client sends end_of_early_data only after reading the server's flight.
@@ -261,9 +418,7 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
- client_->StartConnect();
- server_->StartConnect();
-
+ StartConnect();
// Send the early data xtn in the CH, followed by early app data. The server
// will fail right after sending its flight, when receiving the early data.
client_->Set0RttEnabled(true);
@@ -310,7 +465,6 @@ TEST_P(TlsConnectTls13, SendTooMuchEarlyData) {
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
- ExpectAlert(client_, kTlsAlertEndOfEarlyData);
client_->Handshake();
CheckEarlyDataLimit(client_, short_size);
@@ -364,7 +518,6 @@ TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
- client_->ExpectSendAlert(kTlsAlertEndOfEarlyData);
client_->Handshake(); // Send ClientHello
CheckEarlyDataLimit(client_, limit);
@@ -399,4 +552,86 @@ TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
}
}
+class PacketCoalesceFilter : public PacketFilter {
+ public:
+ PacketCoalesceFilter() : packet_data_() {}
+
+ void SendCoalesced(std::shared_ptr<TlsAgent> agent) {
+ agent->SendDirect(packet_data_);
+ }
+
+ protected:
+ PacketFilter::Action Filter(const DataBuffer& input,
+ DataBuffer* output) override {
+ packet_data_.Write(packet_data_.len(), input);
+ return DROP;
+ }
+
+ private:
+ DataBuffer packet_data_;
+};
+
+TEST_P(TlsConnectTls13, ZeroRttOrdering) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+
+ // Send out the ClientHello.
+ client_->Handshake();
+
+ // Now, coalesce the next three things from the client: early data, second
+ // flight and 1-RTT data.
+ auto coalesce = std::make_shared<PacketCoalesceFilter>();
+ client_->SetPacketFilter(coalesce);
+
+ // Send (and hold) early data.
+ static const std::vector<uint8_t> early_data = {3, 2, 1};
+ EXPECT_EQ(static_cast<PRInt32>(early_data.size()),
+ PR_Write(client_->ssl_fd(), early_data.data(), early_data.size()));
+
+ // Send (and hold) the second client handshake flight.
+ // The client sends EndOfEarlyData after seeing the server Finished.
+ server_->Handshake();
+ client_->Handshake();
+
+ // Send (and hold) 1-RTT data.
+ static const std::vector<uint8_t> late_data = {7, 8, 9, 10};
+ EXPECT_EQ(static_cast<PRInt32>(late_data.size()),
+ PR_Write(client_->ssl_fd(), late_data.data(), late_data.size()));
+
+ // Now release them all at once.
+ coalesce->SendCoalesced(client_);
+
+ // Now ensure that the three steps are exposed in the right order on the
+ // server: delivery of early data, handshake callback, delivery of 1-RTT.
+ size_t step = 0;
+ server_->SetHandshakeCallback([&step](TlsAgent*) {
+ EXPECT_EQ(1U, step);
+ ++step;
+ });
+
+ std::vector<uint8_t> buf(10);
+ PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.size());
+ ASSERT_EQ(static_cast<PRInt32>(early_data.size()), read);
+ buf.resize(read);
+ EXPECT_EQ(early_data, buf);
+ EXPECT_EQ(0U, step);
+ ++step;
+
+ // The third read should be after the handshake callback and should return the
+ // data that was sent after the handshake completed.
+ buf.resize(10);
+ read = PR_Read(server_->ssl_fd(), buf.data(), buf.size());
+ ASSERT_EQ(static_cast<PRInt32>(late_data.size()), read);
+ buf.resize(read);
+ EXPECT_EQ(late_data, buf);
+ EXPECT_EQ(2U, step);
+}
+
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
+ TlsConnectTestBase::kTlsVariantsAll);
+#endif
+
} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
index 5035a338d..0aa9a4c78 100644
--- a/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
@@ -31,7 +31,7 @@ const static uint8_t kCannedTls13ClientHello[] = {
0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00,
0x0a, 0x00, 0x12, 0x00, 0x10, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01,
- 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x28, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x33, 0x00,
0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04, 0x86, 0x4a, 0xb9, 0xdc,
0x6a, 0x38, 0xa7, 0xce, 0xe7, 0xc2, 0x4f, 0xa6, 0x28, 0xb9, 0xdc, 0x65,
0xbf, 0x73, 0x47, 0x3c, 0x9c, 0x65, 0x8c, 0x47, 0x6d, 0x57, 0x22, 0x8a,
@@ -44,13 +44,14 @@ const static uint8_t kCannedTls13ClientHello[] = {
0x02, 0x05, 0x02, 0x06, 0x02, 0x02, 0x02};
const static uint8_t kCannedTls13ServerHello[] = {
- 0x7f, kD13, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3, 0xf0,
- 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b, 0xdf, 0xe5,
- 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76, 0x08, 0x13, 0x01,
- 0x00, 0x28, 0x00, 0x28, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf,
- 0x23, 0x17, 0x64, 0x23, 0x03, 0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65,
- 0x24, 0xa1, 0x6c, 0xa9, 0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a,
- 0xcb, 0xe3, 0x08, 0x84, 0xae, 0x19};
+ 0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3,
+ 0xf0, 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b,
+ 0xdf, 0xe5, 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76,
+ 0x08, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24,
+ 0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf, 0x23, 0x17, 0x64, 0x23, 0x03,
+ 0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65, 0x24, 0xa1, 0x6c, 0xa9,
+ 0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a, 0xcb, 0xe3, 0x08,
+ 0x84, 0xae, 0x19, 0x00, 0x2b, 0x00, 0x02, 0x7f, kD13};
static const char *k0RttData = "ABCDEF";
TEST_P(TlsAgentTest, EarlyFinished) {
diff --git a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
index dbcbc9aa3..dbcdd92ea 100644
--- a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -29,7 +29,25 @@ TEST_P(TlsConnectGeneric, ServerAuthBigRsa) {
}
TEST_P(TlsConnectGeneric, ServerAuthRsaChain) {
- Reset(TlsAgent::kServerRsaChain);
+ Reset("rsa_chain");
+ Connect();
+ CheckKeys();
+ size_t chain_length;
+ EXPECT_TRUE(client_->GetPeerChainLength(&chain_length));
+ EXPECT_EQ(2UL, chain_length);
+}
+
+TEST_P(TlsConnectGeneric, ServerAuthRsaPssChain) {
+ Reset("rsa_pss_chain");
+ Connect();
+ CheckKeys();
+ size_t chain_length;
+ EXPECT_TRUE(client_->GetPeerChainLength(&chain_length));
+ EXPECT_EQ(2UL, chain_length);
+}
+
+TEST_P(TlsConnectGeneric, ServerAuthRsaCARsaPssChain) {
+ Reset("rsa_ca_rsa_pss_chain");
Connect();
CheckKeys();
size_t chain_length;
@@ -141,13 +159,11 @@ TEST_P(TlsConnectTls12, ClientAuthBigRsaCheckSigAlg) {
class TlsZeroCertificateRequestSigAlgsFilter : public TlsHandshakeFilter {
public:
+ TlsZeroCertificateRequestSigAlgsFilter()
+ : TlsHandshakeFilter({kTlsHandshakeCertificateRequest}) {}
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) {
- if (header.handshake_type() != kTlsHandshakeCertificateRequest) {
- return KEEP;
- }
-
TlsParser parser(input);
std::cerr << "Zeroing CertReq.supported_signature_algorithms" << std::endl;
@@ -581,8 +597,7 @@ class EnforceNoActivity : public PacketFilter {
TEST_P(TlsConnectGenericPre13, AuthCompleteDelayed) {
client_->SetAuthCertificateCallback(AuthCompleteBlock);
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
client_->Handshake(); // Send ClientHello
server_->Handshake(); // Send ServerHello
client_->Handshake(); // Send ClientKeyExchange and Finished
@@ -610,8 +625,7 @@ TEST_P(TlsConnectGenericPre13, AuthCompleteDelayed) {
TEST_P(TlsConnectTls13, AuthCompleteDelayed) {
client_->SetAuthCertificateCallback(AuthCompleteBlock);
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
client_->Handshake(); // Send ClientHello
server_->Handshake(); // Send ServerHello
EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
diff --git a/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
index 3463782e0..36ee104af 100644
--- a/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
@@ -82,9 +82,8 @@ TEST_P(TlsConnectGenericPre13, SignedCertificateTimestampsLegacy) {
ssl_kea_rsa));
EXPECT_EQ(SECSuccess, SSL_SetSignedCertTimestamps(server_->ssl_fd(),
&kSctItem, ssl_kea_rsa));
- EXPECT_EQ(SECSuccess,
- SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
- PR_TRUE));
+
+ client_->SetOption(SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
SignedCertificateTimestampsExtractor timestamps_extractor(client_);
Connect();
@@ -96,9 +95,7 @@ TEST_P(TlsConnectGeneric, SignedCertificateTimestampsSuccess) {
EnsureTlsSetup();
EXPECT_TRUE(
server_->ConfigServerCert(TlsAgent::kServerRsa, true, &kExtraSctData));
- EXPECT_EQ(SECSuccess,
- SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
- PR_TRUE));
+ client_->SetOption(SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
SignedCertificateTimestampsExtractor timestamps_extractor(client_);
Connect();
@@ -120,9 +117,7 @@ TEST_P(TlsConnectGeneric, SignedCertificateTimestampsInactiveClient) {
TEST_P(TlsConnectGeneric, SignedCertificateTimestampsInactiveServer) {
EnsureTlsSetup();
- EXPECT_EQ(SECSuccess,
- SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
- PR_TRUE));
+ client_->SetOption(SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
SignedCertificateTimestampsExtractor timestamps_extractor(client_);
Connect();
@@ -173,16 +168,14 @@ TEST_P(TlsConnectGeneric, OcspNotRequested) {
// Even if the client asks, the server has nothing unless it is configured.
TEST_P(TlsConnectGeneric, OcspNotProvided) {
EnsureTlsSetup();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_ENABLE_OCSP_STAPLING, PR_TRUE));
+ client_->SetOption(SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
client_->SetAuthCertificateCallback(CheckNoOCSP);
Connect();
}
TEST_P(TlsConnectGenericPre13, OcspMangled) {
EnsureTlsSetup();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_ENABLE_OCSP_STAPLING, PR_TRUE));
+ client_->SetOption(SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
EXPECT_TRUE(
server_->ConfigServerCert(TlsAgent::kServerRsa, true, &kOcspExtraData));
@@ -197,8 +190,7 @@ TEST_P(TlsConnectGenericPre13, OcspMangled) {
TEST_P(TlsConnectGeneric, OcspSuccess) {
EnsureTlsSetup();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_ENABLE_OCSP_STAPLING, PR_TRUE));
+ client_->SetOption(SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
auto capture_ocsp =
std::make_shared<TlsExtensionCapture>(ssl_cert_status_xtn);
server_->SetPacketFilter(capture_ocsp);
@@ -225,8 +217,7 @@ TEST_P(TlsConnectGeneric, OcspSuccess) {
TEST_P(TlsConnectGeneric, OcspHugeSuccess) {
EnsureTlsSetup();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_ENABLE_OCSP_STAPLING, PR_TRUE));
+ client_->SetOption(SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
uint8_t hugeOcspValue[16385];
memset(hugeOcspValue, 0xa1, sizeof(hugeOcspValue));
diff --git a/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
index 85c30b2bf..810656868 100644
--- a/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
@@ -31,11 +31,11 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
public:
TlsCipherSuiteTestBase(SSLProtocolVariant variant, uint16_t version,
uint16_t cipher_suite, SSLNamedGroup group,
- SSLSignatureScheme signature_scheme)
+ SSLSignatureScheme sig_scheme)
: TlsConnectTestBase(variant, version),
cipher_suite_(cipher_suite),
group_(group),
- signature_scheme_(signature_scheme),
+ sig_scheme_(sig_scheme),
csinfo_({0}) {
SECStatus rv =
SSL_GetCipherSuiteInfo(cipher_suite_, &csinfo_, sizeof(csinfo_));
@@ -60,14 +60,14 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
server_->ConfigNamedGroups(groups);
kea_type_ = SSLInt_GetKEAType(group_);
- client_->SetSignatureSchemes(&signature_scheme_, 1);
- server_->SetSignatureSchemes(&signature_scheme_, 1);
+ client_->SetSignatureSchemes(&sig_scheme_, 1);
+ server_->SetSignatureSchemes(&sig_scheme_, 1);
}
}
virtual void SetupCertificate() {
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
- switch (signature_scheme_) {
+ switch (sig_scheme_) {
case ssl_sig_rsa_pkcs1_sha256:
case ssl_sig_rsa_pkcs1_sha384:
case ssl_sig_rsa_pkcs1_sha512:
@@ -93,8 +93,7 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
auth_type_ = ssl_auth_ecdsa;
break;
default:
- ASSERT_TRUE(false) << "Unsupported signature scheme: "
- << signature_scheme_;
+ ADD_FAILURE() << "Unsupported signature scheme: " << sig_scheme_;
break;
}
} else {
@@ -187,7 +186,7 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
SSLAuthType auth_type_;
SSLKEAType kea_type_;
SSLNamedGroup group_;
- SSLSignatureScheme signature_scheme_;
+ SSLSignatureScheme sig_scheme_;
SSLCipherSuiteInfo csinfo_;
};
@@ -236,27 +235,29 @@ TEST_P(TlsCipherSuiteTest, ResumeCipherSuite) {
ConnectAndCheckCipherSuite();
}
-// This only works for stream ciphers because we modify the sequence number -
-// which is included explicitly in the DTLS record header - and that trips a
-// different error code. Note that the message that the client sends would not
-// decrypt (the nonce/IV wouldn't match), but the record limit is hit before
-// attempting to decrypt a record.
TEST_P(TlsCipherSuiteTest, ReadLimit) {
SetupCertificate();
EnableSingleCipher();
ConnectAndCheckCipherSuite();
- EXPECT_EQ(SECSuccess,
- SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), last_safe_write()));
- EXPECT_EQ(SECSuccess,
- SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), last_safe_write()));
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
+ uint64_t last = last_safe_write();
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), last));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), last));
- client_->SendData(10, 10);
- server_->ReadBytes(); // This should be OK.
+ client_->SendData(10, 10);
+ server_->ReadBytes(); // This should be OK.
+ } else {
+ // In TLS 1.3, reading or writing triggers a KeyUpdate. That would mean
+ // that the sequence numbers would reset and we wouldn't hit the limit. So
+ // we move the sequence number to one less than the limit directly and don't
+ // test sending and receiving just before the limit.
+ uint64_t last = record_limit() - 1;
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), last));
+ }
- // The payload needs to be big enough to pass for encrypted. In the extreme
- // case (TLS 1.3), this means 1 for payload, 1 for content type and 16 for
- // authentication tag.
- static const uint8_t payload[18] = {6};
+ // The payload needs to be big enough to pass for encrypted. The code checks
+ // the limit before it tries to decrypt.
+ static const uint8_t payload[32] = {6};
DataBuffer record;
uint64_t epoch;
if (variant_ == ssl_variant_datagram) {
@@ -271,13 +272,17 @@ TEST_P(TlsCipherSuiteTest, ReadLimit) {
TlsAgentTestBase::MakeRecord(variant_, kTlsApplicationDataType, version_,
payload, sizeof(payload), &record,
(epoch << 48) | record_limit());
- server_->adapter()->PacketReceived(record);
+ client_->SendDirect(record);
server_->ExpectReadWriteError();
server_->ReadBytes();
EXPECT_EQ(SSL_ERROR_TOO_MANY_RECORDS, server_->error_code());
}
TEST_P(TlsCipherSuiteTest, WriteLimit) {
+ // This asserts in TLS 1.3 because we expect an automatic update.
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return;
+ }
SetupCertificate();
EnableSingleCipher();
ConnectAndCheckCipherSuite();
diff --git a/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc
new file mode 100644
index 000000000..dad944a1f
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc
@@ -0,0 +1,503 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ssl.h"
+#include "ssl3prot.h"
+#include "sslerr.h"
+#include "sslproto.h"
+#include "sslexp.h"
+
+#include <memory>
+
+#include "tls_connect.h"
+
+namespace nss_test {
+
+static void IncrementCounterArg(void *arg) {
+ if (arg) {
+ auto *called = reinterpret_cast<size_t *>(arg);
+ ++*called;
+ }
+}
+
+PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+ PRUint8 *data, unsigned int *len,
+ unsigned int maxLen, void *arg) {
+ IncrementCounterArg(arg);
+ return PR_FALSE;
+}
+
+PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+ PRUint8 *data, unsigned int *len,
+ unsigned int maxLen, void *arg) {
+ IncrementCounterArg(arg);
+ return PR_TRUE;
+}
+
+SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+ const PRUint8 *data, unsigned int len,
+ SSLAlertDescription *alert, void *arg) {
+ return SECSuccess;
+}
+
+// All of the (current) set of supported extensions, plus a few extra.
+static const uint16_t kManyExtensions[] = {
+ ssl_server_name_xtn,
+ ssl_cert_status_xtn,
+ ssl_supported_groups_xtn,
+ ssl_ec_point_formats_xtn,
+ ssl_signature_algorithms_xtn,
+ ssl_signature_algorithms_cert_xtn,
+ ssl_use_srtp_xtn,
+ ssl_app_layer_protocol_xtn,
+ ssl_signed_cert_timestamp_xtn,
+ ssl_padding_xtn,
+ ssl_extended_master_secret_xtn,
+ ssl_session_ticket_xtn,
+ ssl_tls13_key_share_xtn,
+ ssl_tls13_pre_shared_key_xtn,
+ ssl_tls13_early_data_xtn,
+ ssl_tls13_supported_versions_xtn,
+ ssl_tls13_cookie_xtn,
+ ssl_tls13_psk_key_exchange_modes_xtn,
+ ssl_tls13_ticket_early_data_info_xtn,
+ ssl_tls13_certificate_authorities_xtn,
+ ssl_next_proto_nego_xtn,
+ ssl_renegotiation_info_xtn,
+ ssl_tls13_short_header_xtn,
+ 1,
+ 0xffff};
+// The list here includes all extensions we expect to use (SSL_MAX_EXTENSIONS),
+// plus the deprecated values (see sslt.h), and two extra dummy values.
+PR_STATIC_ASSERT((SSL_MAX_EXTENSIONS + 5) == PR_ARRAY_SIZE(kManyExtensions));
+
+void InstallManyWriters(std::shared_ptr<TlsAgent> agent,
+ SSLExtensionWriter writer, size_t *installed = nullptr,
+ size_t *called = nullptr) {
+ for (size_t i = 0; i < PR_ARRAY_SIZE(kManyExtensions); ++i) {
+ SSLExtensionSupport support = ssl_ext_none;
+ SECStatus rv = SSL_GetExtensionSupport(kManyExtensions[i], &support);
+ ASSERT_EQ(SECSuccess, rv) << "SSL_GetExtensionSupport cannot fail";
+
+ rv = SSL_InstallExtensionHooks(agent->ssl_fd(), kManyExtensions[i], writer,
+ called, NoopExtensionHandler, nullptr);
+ if (support == ssl_ext_native_only) {
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ } else {
+ if (installed) {
+ ++*installed;
+ }
+ EXPECT_EQ(SECSuccess, rv);
+ }
+ }
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopClient) {
+ EnsureTlsSetup();
+ size_t installed = 0;
+ size_t called = 0;
+ InstallManyWriters(client_, NoopExtensionWriter, &installed, &called);
+ EXPECT_LT(0U, installed);
+ Connect();
+ EXPECT_EQ(installed, called);
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopServer) {
+ EnsureTlsSetup();
+ size_t installed = 0;
+ size_t called = 0;
+ InstallManyWriters(server_, NoopExtensionWriter, &installed, &called);
+ EXPECT_LT(0U, installed);
+ Connect();
+ // Extension writers are all called for each of ServerHello,
+ // EncryptedExtensions, and Certificate.
+ EXPECT_EQ(installed * 3, called);
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterClient) {
+ EnsureTlsSetup();
+ InstallManyWriters(client_, EmptyExtensionWriter);
+ InstallManyWriters(server_, EmptyExtensionWriter);
+ Connect();
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterServer) {
+ EnsureTlsSetup();
+ InstallManyWriters(server_, EmptyExtensionWriter);
+ // Sending extensions that the client doesn't expect leads to extensions
+ // appearing even if the client didn't send one, or in the wrong messages.
+ client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ ConnectExpectFail();
+}
+
+// Install an writer to disable sending of a natively-supported extension.
+TEST_F(TlsConnectStreamTls13, CustomExtensionWriterDisable) {
+ EnsureTlsSetup();
+
+ // This option enables sending the extension via the native support.
+ SECStatus rv = SSL_OptionSet(client_->ssl_fd(),
+ SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // This installs an override that doesn't do anything. You have to specify
+ // something; passing all nullptr values removes an existing handler.
+ rv = SSL_InstallExtensionHooks(
+ client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NoopExtensionWriter,
+ nullptr, NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+ auto capture =
+ std::make_shared<TlsExtensionCapture>(ssl_signed_cert_timestamp_xtn);
+ client_->SetPacketFilter(capture);
+
+ Connect();
+ // So nothing will be sent.
+ EXPECT_FALSE(capture->captured());
+}
+
+// An extension that is unlikely to be parsed as valid.
+static uint8_t kNonsenseExtension[] = {91, 82, 73, 64, 55, 46, 37, 28, 19};
+
+static PRBool NonsenseExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+ PRUint8 *data, unsigned int *len,
+ unsigned int maxLen, void *arg) {
+ TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg);
+ EXPECT_NE(nullptr, agent);
+ EXPECT_NE(nullptr, data);
+ EXPECT_NE(nullptr, len);
+ EXPECT_EQ(0U, *len);
+ EXPECT_LT(0U, maxLen);
+ EXPECT_EQ(agent->ssl_fd(), fd);
+
+ if (message != ssl_hs_client_hello && message != ssl_hs_server_hello &&
+ message != ssl_hs_encrypted_extensions) {
+ return PR_FALSE;
+ }
+
+ *len = static_cast<unsigned int>(sizeof(kNonsenseExtension));
+ EXPECT_GE(maxLen, *len);
+ if (maxLen < *len) {
+ return PR_FALSE;
+ }
+ PORT_Memcpy(data, kNonsenseExtension, *len);
+ return PR_TRUE;
+}
+
+// Override the extension handler for an natively-supported and produce
+// nonsense, which results in a handshake failure.
+TEST_F(TlsConnectStreamTls13, CustomExtensionOverride) {
+ EnsureTlsSetup();
+
+ // This option enables sending the extension via the native support.
+ SECStatus rv = SSL_OptionSet(client_->ssl_fd(),
+ SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // This installs an override that sends nonsense.
+ rv = SSL_InstallExtensionHooks(
+ client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NonsenseExtensionWriter,
+ client_.get(), NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Capture it to see what we got.
+ auto capture =
+ std::make_shared<TlsExtensionCapture>(ssl_signed_cert_timestamp_xtn);
+ client_->SetPacketFilter(capture);
+
+ ConnectExpectAlert(server_, kTlsAlertDecodeError);
+
+ EXPECT_TRUE(capture->captured());
+ EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+ capture->extension());
+}
+
+static SECStatus NonsenseExtensionHandler(PRFileDesc *fd,
+ SSLHandshakeType message,
+ const PRUint8 *data, unsigned int len,
+ SSLAlertDescription *alert,
+ void *arg) {
+ TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg);
+ EXPECT_EQ(agent->ssl_fd(), fd);
+ if (agent->role() == TlsAgent::SERVER) {
+ EXPECT_EQ(ssl_hs_client_hello, message);
+ } else {
+ EXPECT_TRUE(message == ssl_hs_server_hello ||
+ message == ssl_hs_encrypted_extensions);
+ }
+ EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+ DataBuffer(data, len));
+ EXPECT_NE(nullptr, alert);
+ return SECSuccess;
+}
+
+// Send nonsense in an extension from client to server.
+TEST_F(TlsConnectStreamTls13, CustomExtensionClientToServer) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nonsense.
+ const uint16_t extension_code = 0xffe5;
+ SECStatus rv = SSL_InstallExtensionHooks(
+ client_->ssl_fd(), extension_code, NonsenseExtensionWriter, client_.get(),
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Capture it to see what we got.
+ auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+ client_->SetPacketFilter(capture);
+
+ // Handle it so that the handshake completes.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ NoopExtensionWriter, nullptr,
+ NonsenseExtensionHandler, server_.get());
+ EXPECT_EQ(SECSuccess, rv);
+
+ Connect();
+
+ EXPECT_TRUE(capture->captured());
+ EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+ capture->extension());
+}
+
+static PRBool NonsenseExtensionWriterSH(PRFileDesc *fd,
+ SSLHandshakeType message, PRUint8 *data,
+ unsigned int *len, unsigned int maxLen,
+ void *arg) {
+ if (message == ssl_hs_server_hello) {
+ return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg);
+ }
+ return PR_FALSE;
+}
+
+// Send nonsense in an extension from server to client, in ServerHello.
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientSH) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nothing but expects nonsense.
+ const uint16_t extension_code = 0xff5e;
+ SECStatus rv = SSL_InstallExtensionHooks(
+ client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr,
+ NonsenseExtensionHandler, client_.get());
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Have the server send nonsense.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ NonsenseExtensionWriterSH, server_.get(),
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Capture the extension from the ServerHello only and check it.
+ auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+ capture->SetHandshakeTypes({kTlsHandshakeServerHello});
+ server_->SetPacketFilter(capture);
+
+ Connect();
+
+ EXPECT_TRUE(capture->captured());
+ EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+ capture->extension());
+}
+
+static PRBool NonsenseExtensionWriterEE(PRFileDesc *fd,
+ SSLHandshakeType message, PRUint8 *data,
+ unsigned int *len, unsigned int maxLen,
+ void *arg) {
+ if (message == ssl_hs_encrypted_extensions) {
+ return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg);
+ }
+ return PR_FALSE;
+}
+
+// Send nonsense in an extension from server to client, in EncryptedExtensions.
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientEE) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nothing but expects nonsense.
+ const uint16_t extension_code = 0xff5e;
+ SECStatus rv = SSL_InstallExtensionHooks(
+ client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr,
+ NonsenseExtensionHandler, client_.get());
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Have the server send nonsense.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ NonsenseExtensionWriterEE, server_.get(),
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Capture the extension from the EncryptedExtensions only and check it.
+ auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+ capture->SetHandshakeTypes({kTlsHandshakeEncryptedExtensions});
+ server_->SetTlsRecordFilter(capture);
+
+ Connect();
+
+ EXPECT_TRUE(capture->captured());
+ EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+ capture->extension());
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionUnsolicitedServer) {
+ EnsureTlsSetup();
+
+ const uint16_t extension_code = 0xff5e;
+ SECStatus rv = SSL_InstallExtensionHooks(
+ server_->ssl_fd(), extension_code, NonsenseExtensionWriter, server_.get(),
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Capture it to see what we got.
+ auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+ server_->SetPacketFilter(capture);
+
+ client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ ConnectExpectFail();
+
+ EXPECT_TRUE(capture->captured());
+ EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+ capture->extension());
+}
+
+SECStatus RejectExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+ const PRUint8 *data, unsigned int len,
+ SSLAlertDescription *alert, void *arg) {
+ return SECFailure;
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerReject) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nonsense.
+ const uint16_t extension_code = 0xffe7;
+ SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+ EmptyExtensionWriter, nullptr,
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Reject the extension for no good reason.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ NoopExtensionWriter, nullptr,
+ RejectExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+}
+
+// Send nonsense in an extension from client to server.
+TEST_F(TlsConnectStreamTls13, CustomExtensionClientReject) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nothing but expects nonsense.
+ const uint16_t extension_code = 0xff58;
+ SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+ EmptyExtensionWriter, nullptr,
+ RejectExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Have the server send nonsense.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ EmptyExtensionWriter, nullptr,
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ ConnectExpectFail();
+}
+
+static const uint8_t kCustomAlert = 0xf6;
+
+SECStatus AlertExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+ const PRUint8 *data, unsigned int len,
+ SSLAlertDescription *alert, void *arg) {
+ *alert = kCustomAlert;
+ return SECFailure;
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerRejectAlert) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nonsense.
+ const uint16_t extension_code = 0xffea;
+ SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+ EmptyExtensionWriter, nullptr,
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Reject the extension for no good reason.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ NoopExtensionWriter, nullptr,
+ AlertExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ ConnectExpectAlert(server_, kCustomAlert);
+}
+
+// Send nonsense in an extension from client to server.
+TEST_F(TlsConnectStreamTls13, CustomExtensionClientRejectAlert) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nothing but expects nonsense.
+ const uint16_t extension_code = 0xff5a;
+ SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+ EmptyExtensionWriter, nullptr,
+ AlertExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ // Have the server send nonsense.
+ rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+ EmptyExtensionWriter, nullptr,
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+
+ client_->ExpectSendAlert(kCustomAlert);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ ConnectExpectFail();
+}
+
+// Configure a custom extension hook badly.
+TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyWriter) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nothing but expects nonsense.
+ SECStatus rv =
+ SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6c, EmptyExtensionWriter,
+ nullptr, nullptr, nullptr);
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyHandler) {
+ EnsureTlsSetup();
+
+ // This installs an override that sends nothing but expects nonsense.
+ SECStatus rv =
+ SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6d, nullptr, nullptr,
+ NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionOverrunBuffer) {
+ EnsureTlsSetup();
+ // This doesn't actually overrun the buffer, but it says that it does.
+ auto overrun_writer = [](PRFileDesc *fd, SSLHandshakeType message,
+ PRUint8 *data, unsigned int *len,
+ unsigned int maxLen, void *arg) -> PRBool {
+ *len = maxLen + 1;
+ return PR_TRUE;
+ };
+ SECStatus rv =
+ SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff71, overrun_writer,
+ nullptr, NoopExtensionHandler, nullptr);
+ EXPECT_EQ(SECSuccess, rv);
+ client_->StartConnect();
+ client_->Handshake();
+ client_->CheckErrorCode(SEC_ERROR_APPLICATION_CALLBACK_ERROR);
+}
+
+} // namespace "nss_test"
diff --git a/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
index 69fd00331..d1668b823 100644
--- a/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
@@ -29,8 +29,7 @@ TEST_F(TlsConnectTest, DamageSecretHandleClientFinished) {
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
client_->Handshake();
server_->Handshake();
std::cerr << "Damaging HS secret" << std::endl;
@@ -51,16 +50,12 @@ TEST_F(TlsConnectTest, DamageSecretHandleServerFinished) {
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
- client_->ExpectSendAlert(kTlsAlertDecryptError);
- // The server can't read the client's alert, so it also sends an alert.
- server_->ExpectSendAlert(kTlsAlertBadRecordMac);
server_->SetPacketFilter(std::make_shared<AfterRecordN>(
server_, client_,
0, // ServerHello.
[this]() { SSLInt_DamageServerHsTrafficSecret(client_->ssl_fd()); }));
- ConnectExpectFail();
+ ConnectExpectAlert(client_, kTlsAlertDecryptError);
client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
- server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
}
TEST_P(TlsConnectGenericPre13, DamageServerSignature) {
@@ -79,16 +74,7 @@ TEST_P(TlsConnectTls13, DamageServerSignature) {
auto filter =
std::make_shared<TlsLastByteDamager>(kTlsHandshakeCertificateVerify);
server_->SetTlsRecordFilter(filter);
- filter->EnableDecryption();
- client_->ExpectSendAlert(kTlsAlertDecryptError);
- // The server can't read the client's alert, so it also sends an alert.
- if (variant_ == ssl_variant_stream) {
- server_->ExpectSendAlert(kTlsAlertBadRecordMac);
- ConnectExpectFail();
- server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
- } else {
- ConnectExpectFailOneSide(TlsAgent::CLIENT);
- }
+ ConnectExpectAlert(client_, kTlsAlertDecryptError);
client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
}
@@ -100,11 +86,9 @@ TEST_P(TlsConnectGeneric, DamageClientSignature) {
std::make_shared<TlsLastByteDamager>(kTlsHandshakeCertificateVerify);
client_->SetTlsRecordFilter(filter);
server_->ExpectSendAlert(kTlsAlertDecryptError);
- filter->EnableDecryption();
// Do these handshakes by hand to avoid race condition on
// the client processing the server's alert.
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
client_->Handshake();
server_->Handshake();
client_->Handshake();
diff --git a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
index 97943303a..4aa3bb639 100644
--- a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
@@ -59,8 +59,7 @@ TEST_P(TlsConnectTls13, SharesForBothEcdheAndDhe) {
TEST_P(TlsConnectGeneric, ConnectFfdheClient) {
EnableOnlyDheCiphers();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
auto groups_capture =
std::make_shared<TlsExtensionCapture>(ssl_supported_groups_xtn);
auto shares_capture =
@@ -90,8 +89,7 @@ TEST_P(TlsConnectGeneric, ConnectFfdheClient) {
// because the client automatically sends the supported groups extension.
TEST_P(TlsConnectGenericPre13, ConnectFfdheServer) {
EnableOnlyDheCiphers();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ server_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
Connect();
@@ -105,14 +103,11 @@ TEST_P(TlsConnectGenericPre13, ConnectFfdheServer) {
class TlsDheServerKeyExchangeDamager : public TlsHandshakeFilter {
public:
- TlsDheServerKeyExchangeDamager() {}
+ TlsDheServerKeyExchangeDamager()
+ : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
// Damage the first octet of dh_p. Anything other than the known prime will
// be rejected as "weak" when we have SSL_REQUIRE_DH_NAMED_GROUPS enabled.
*output = input;
@@ -126,8 +121,7 @@ class TlsDheServerKeyExchangeDamager : public TlsHandshakeFilter {
// the signature until everything else has been checked.
TEST_P(TlsConnectGenericPre13, DamageServerKeyShare) {
EnableOnlyDheCiphers();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
server_->SetPacketFilter(std::make_shared<TlsDheServerKeyExchangeDamager>());
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
@@ -147,7 +141,8 @@ class TlsDheSkeChangeY : public TlsHandshakeFilter {
kYZeroPad
};
- TlsDheSkeChangeY(ChangeYTo change) : change_Y_(change) {}
+ TlsDheSkeChangeY(uint8_t handshake_type, ChangeYTo change)
+ : TlsHandshakeFilter({handshake_type}), change_Y_(change) {}
protected:
void ChangeY(const DataBuffer& input, DataBuffer* output, size_t offset,
@@ -213,7 +208,9 @@ class TlsDheSkeChangeY : public TlsHandshakeFilter {
class TlsDheSkeChangeYServer : public TlsDheSkeChangeY {
public:
TlsDheSkeChangeYServer(ChangeYTo change, bool modify)
- : TlsDheSkeChangeY(change), modify_(modify), p_() {}
+ : TlsDheSkeChangeY(kTlsHandshakeServerKeyExchange, change),
+ modify_(modify),
+ p_() {}
const DataBuffer& prime() const { return p_; }
@@ -221,10 +218,6 @@ class TlsDheSkeChangeYServer : public TlsDheSkeChangeY {
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) override {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
size_t offset = 2;
// Read dh_p
uint32_t dh_len = 0;
@@ -254,16 +247,13 @@ class TlsDheSkeChangeYClient : public TlsDheSkeChangeY {
TlsDheSkeChangeYClient(
ChangeYTo change,
std::shared_ptr<const TlsDheSkeChangeYServer> server_filter)
- : TlsDheSkeChangeY(change), server_filter_(server_filter) {}
+ : TlsDheSkeChangeY(kTlsHandshakeClientKeyExchange, change),
+ server_filter_(server_filter) {}
protected:
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) override {
- if (header.handshake_type() != kTlsHandshakeClientKeyExchange) {
- return KEEP;
- }
-
ChangeY(input, output, 0, server_filter_->prime());
return CHANGE;
}
@@ -289,8 +279,7 @@ class TlsDamageDHYTest
TEST_P(TlsDamageDHYTest, DamageServerY) {
EnableOnlyDheCiphers();
if (std::get<3>(GetParam())) {
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
}
TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam());
server_->SetPacketFilter(
@@ -320,8 +309,7 @@ TEST_P(TlsDamageDHYTest, DamageServerY) {
TEST_P(TlsDamageDHYTest, DamageClientY) {
EnableOnlyDheCiphers();
if (std::get<3>(GetParam())) {
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
}
// The filter on the server is required to capture the prime.
auto server_filter =
@@ -370,13 +358,10 @@ INSTANTIATE_TEST_CASE_P(
class TlsDheSkeMakePEven : public TlsHandshakeFilter {
public:
+ TlsDheSkeMakePEven() : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
// Find the end of dh_p
uint32_t dh_len = 0;
EXPECT_TRUE(input.Read(0, 2, &dh_len));
@@ -404,13 +389,10 @@ TEST_P(TlsConnectGenericPre13, MakeDhePEven) {
class TlsDheSkeZeroPadP : public TlsHandshakeFilter {
public:
+ TlsDheSkeZeroPadP() : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
*output = input;
uint32_t dh_len = 0;
EXPECT_TRUE(input.Read(0, 2, &dh_len));
@@ -445,8 +427,7 @@ TEST_P(TlsConnectGenericPre13, PadDheP) {
// Note: This test case can take ages to generate the weak DH key.
TEST_P(TlsConnectGenericPre13, WeakDHGroup) {
EnableOnlyDheCiphers();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
EXPECT_EQ(SECSuccess,
SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE));
@@ -496,8 +477,7 @@ TEST_P(TlsConnectTls13, NamedGroupMismatch13) {
// custom group in contrast to the previous test.
TEST_P(TlsConnectGenericPre13, RequireNamedGroupsMismatchPre13) {
EnableOnlyDheCiphers();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
static const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ffdhe_3072};
static const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ec_secp256r1,
ssl_grp_ffdhe_2048};
@@ -525,8 +505,7 @@ TEST_P(TlsConnectGenericPre13, PreferredFfdhe) {
TEST_P(TlsConnectGenericPre13, MismatchDHE) {
EnableOnlyDheCiphers();
- EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
- SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+ client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE);
static const SSLDHEGroupType serverGroups[] = {ssl_ff_dhe_3072_group};
EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), serverGroups,
PR_ARRAY_SIZE(serverGroups)));
@@ -544,7 +523,8 @@ TEST_P(TlsConnectTls13, ResumeFfdhe) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
Connect();
SendReceive(); // Need to read so that we absorb the session ticket.
- CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
+ CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign,
+ ssl_sig_rsa_pss_sha256);
Reset();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
@@ -557,7 +537,8 @@ TEST_P(TlsConnectTls13, ResumeFfdhe) {
server_->SetPacketFilter(serverCapture);
ExpectResumption(RESUME_TICKET);
Connect();
- CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none);
+ CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign,
+ ssl_sig_rsa_pss_sha256);
ASSERT_LT(0UL, clientCapture->extension().len());
ASSERT_LT(0UL, serverCapture->extension().len());
}
@@ -565,16 +546,15 @@ TEST_P(TlsConnectTls13, ResumeFfdhe) {
class TlsDheSkeChangeSignature : public TlsHandshakeFilter {
public:
TlsDheSkeChangeSignature(uint16_t version, const uint8_t* data, size_t len)
- : version_(version), data_(data), len_(len) {}
+ : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}),
+ version_(version),
+ data_(data),
+ len_(len) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
TlsParser parser(input);
EXPECT_TRUE(parser.SkipVariable(2)); // dh_p
EXPECT_TRUE(parser.SkipVariable(2)); // dh_g
diff --git a/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
index 3cc3b0e62..c059e9938 100644
--- a/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
@@ -6,6 +6,7 @@
#include "secerr.h"
#include "ssl.h"
+#include "sslexp.h"
extern "C" {
// This is not something that should make you happy.
@@ -20,13 +21,13 @@ extern "C" {
namespace nss_test {
-TEST_P(TlsConnectDatagram, DropClientFirstFlightOnce) {
+TEST_P(TlsConnectDatagramPre13, DropClientFirstFlightOnce) {
client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x1));
Connect();
SendReceive();
}
-TEST_P(TlsConnectDatagram, DropServerFirstFlightOnce) {
+TEST_P(TlsConnectDatagramPre13, DropServerFirstFlightOnce) {
server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x1));
Connect();
SendReceive();
@@ -35,36 +36,760 @@ TEST_P(TlsConnectDatagram, DropServerFirstFlightOnce) {
// This drops the first transmission from both the client and server of all
// flights that they send. Note: In DTLS 1.3, the shorter handshake means that
// this will also drop some application data, so we can't call SendReceive().
-TEST_P(TlsConnectDatagram, DropAllFirstTransmissions) {
+TEST_P(TlsConnectDatagramPre13, DropAllFirstTransmissions) {
client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x15));
server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x5));
Connect();
}
// This drops the server's first flight three times.
-TEST_P(TlsConnectDatagram, DropServerFirstFlightThrice) {
+TEST_P(TlsConnectDatagramPre13, DropServerFirstFlightThrice) {
server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x7));
Connect();
}
// This drops the client's second flight once
-TEST_P(TlsConnectDatagram, DropClientSecondFlightOnce) {
+TEST_P(TlsConnectDatagramPre13, DropClientSecondFlightOnce) {
client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x2));
Connect();
}
// This drops the client's second flight three times.
-TEST_P(TlsConnectDatagram, DropClientSecondFlightThrice) {
+TEST_P(TlsConnectDatagramPre13, DropClientSecondFlightThrice) {
client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0xe));
Connect();
}
// This drops the server's second flight three times.
-TEST_P(TlsConnectDatagram, DropServerSecondFlightThrice) {
+TEST_P(TlsConnectDatagramPre13, DropServerSecondFlightThrice) {
server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0xe));
Connect();
}
+class TlsDropDatagram13 : public TlsConnectDatagram13 {
+ public:
+ TlsDropDatagram13()
+ : client_filters_(),
+ server_filters_(),
+ expected_client_acks_(0),
+ expected_server_acks_(1) {}
+
+ void SetUp() {
+ TlsConnectDatagram13::SetUp();
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+ SetFilters();
+ }
+
+ void SetFilters() {
+ EnsureTlsSetup();
+ client_->SetPacketFilter(client_filters_.chain_);
+ client_filters_.ack_->SetAgent(client_.get());
+ client_filters_.ack_->EnableDecryption();
+ server_->SetPacketFilter(server_filters_.chain_);
+ server_filters_.ack_->SetAgent(server_.get());
+ server_filters_.ack_->EnableDecryption();
+ }
+
+ void HandshakeAndAck(const std::shared_ptr<TlsAgent>& agent) {
+ agent->Handshake(); // Read flight.
+ ShiftDtlsTimers();
+ agent->Handshake(); // Generate ACK.
+ }
+
+ void ShrinkPostServerHelloMtu() {
+ // Abuse the custom extension mechanism to modify the MTU so that the
+ // Certificate message is split into two pieces.
+ ASSERT_EQ(
+ SECSuccess,
+ SSL_InstallExtensionHooks(
+ server_->ssl_fd(), 1,
+ [](PRFileDesc* fd, SSLHandshakeType message, PRUint8* data,
+ unsigned int* len, unsigned int maxLen, void* arg) -> PRBool {
+ SSLInt_SetMTU(fd, 500); // Splits the certificate.
+ return PR_FALSE;
+ },
+ nullptr,
+ [](PRFileDesc* fd, SSLHandshakeType message, const PRUint8* data,
+ unsigned int len, SSLAlertDescription* alert,
+ void* arg) -> SECStatus { return SECSuccess; },
+ nullptr));
+ }
+
+ protected:
+ class DropAckChain {
+ public:
+ DropAckChain()
+ : records_(std::make_shared<TlsRecordRecorder>()),
+ ack_(std::make_shared<TlsRecordRecorder>(content_ack)),
+ drop_(std::make_shared<SelectiveRecordDropFilter>(0, false)),
+ chain_(std::make_shared<ChainedPacketFilter>(
+ ChainedPacketFilterInit({records_, ack_, drop_}))) {}
+
+ const TlsRecord& record(size_t i) const { return records_->record(i); }
+
+ std::shared_ptr<TlsRecordRecorder> records_;
+ std::shared_ptr<TlsRecordRecorder> ack_;
+ std::shared_ptr<SelectiveRecordDropFilter> drop_;
+ std::shared_ptr<PacketFilter> chain_;
+ };
+
+ void CheckAcks(const DropAckChain& chain, size_t index,
+ std::vector<uint64_t> acks) {
+ const DataBuffer& buf = chain.ack_->record(index).buffer;
+ size_t offset = 0;
+
+ EXPECT_EQ(acks.size() * 8, buf.len());
+ if ((acks.size() * 8) != buf.len()) {
+ while (offset < buf.len()) {
+ uint64_t ack;
+ ASSERT_TRUE(buf.Read(offset, 8, &ack));
+ offset += 8;
+ std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
+ }
+ return;
+ }
+
+ for (size_t i = 0; i < acks.size(); ++i) {
+ uint64_t a = acks[i];
+ uint64_t ack;
+ ASSERT_TRUE(buf.Read(offset, 8, &ack));
+ offset += 8;
+ if (a != ack) {
+ ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
+ << " got=0x" << ack << std::dec;
+ }
+ }
+ }
+
+ void CheckedHandshakeSendReceive() {
+ Handshake();
+ CheckPostHandshake();
+ }
+
+ void CheckPostHandshake() {
+ CheckConnected();
+ SendReceive();
+ EXPECT_EQ(expected_client_acks_, client_filters_.ack_->count());
+ EXPECT_EQ(expected_server_acks_, server_filters_.ack_->count());
+ }
+
+ protected:
+ DropAckChain client_filters_;
+ DropAckChain server_filters_;
+ size_t expected_client_acks_;
+ size_t expected_server_acks_;
+};
+
+// All of these tests produce a minimum one ACK, from the server
+// to the client upon receiving the client Finished.
+// Dropping complete first and second flights does not produce
+// ACKs
+TEST_F(TlsDropDatagram13, DropClientFirstFlightOnce) {
+ client_filters_.drop_->Reset({0});
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ CheckedHandshakeSendReceive();
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsDropDatagram13, DropServerFirstFlightOnce) {
+ server_filters_.drop_->Reset(0xff);
+ StartConnect();
+ client_->Handshake();
+ // Send the first flight, all dropped.
+ server_->Handshake();
+ server_filters_.drop_->Disable();
+ CheckedHandshakeSendReceive();
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Dropping the server's first record also does not produce
+// an ACK because the next record is ignored.
+// TODO(ekr@rtfm.com): We should generate an empty ACK.
+TEST_F(TlsDropDatagram13, DropServerFirstRecordOnce) {
+ server_filters_.drop_->Reset({0});
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ Handshake();
+ CheckedHandshakeSendReceive();
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Dropping the second packet of the server's flight should
+// produce an ACK.
+TEST_F(TlsDropDatagram13, DropServerSecondRecordOnce) {
+ server_filters_.drop_->Reset({1});
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ HandshakeAndAck(client_);
+ expected_client_acks_ = 1;
+ CheckedHandshakeSendReceive();
+ CheckAcks(client_filters_, 0, {0});
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Drop the server ACK and verify that the client retransmits
+// the ClientHello.
+TEST_F(TlsDropDatagram13, DropServerAckOnce) {
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ // At this point the server has sent it's first flight,
+ // so make it drop the ACK.
+ server_filters_.drop_->Reset({0});
+ client_->Handshake(); // Send the client Finished.
+ server_->Handshake(); // Receive the Finished and send the ACK.
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+ // Wait for the DTLS timeout to make sure we retransmit the
+ // Finished.
+ ShiftDtlsTimers();
+ client_->Handshake(); // Retransmit the Finished.
+ server_->Handshake(); // Read the Finished and send an ACK.
+ uint8_t buf[1];
+ PRInt32 rv = PR_Read(client_->ssl_fd(), buf, sizeof(buf));
+ expected_server_acks_ = 2;
+ EXPECT_GT(0, rv);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+ CheckPostHandshake();
+ // There should be two copies of the finished ACK
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Drop the client certificate verify.
+TEST_F(TlsDropDatagram13, DropClientCertVerify) {
+ StartConnect();
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+ client_->Handshake();
+ server_->Handshake();
+ // Have the client drop Cert Verify
+ client_filters_.drop_->Reset({1});
+ expected_server_acks_ = 2;
+ CheckedHandshakeSendReceive();
+ // Ack of the Cert.
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ // Ack of the whole client handshake.
+ CheckAcks(
+ server_filters_, 1,
+ {0x0002000000000000ULL, // CH (we drop everything after this on client)
+ 0x0002000000000003ULL, // CT (2)
+ 0x0002000000000004ULL} // FIN (2)
+ );
+}
+
+// Shrink the MTU down so that certs get split and drop the first piece.
+TEST_F(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
+ server_filters_.drop_->Reset({2});
+ StartConnect();
+ ShrinkPostServerHelloMtu();
+ client_->Handshake();
+ server_->Handshake();
+ // Check that things got split.
+ EXPECT_EQ(6UL,
+ server_filters_.records_->count()); // SH, EE, CT1, CT2, CV, FIN
+ size_t ct1_size = server_filters_.record(2).buffer.len();
+ server_filters_.records_->Clear();
+ expected_client_acks_ = 1;
+ HandshakeAndAck(client_);
+ server_->Handshake(); // Retransmit
+ EXPECT_EQ(3UL, server_filters_.records_->count()); // CT2, CV, FIN
+ // Check that the first record is CT1 (which is identical to the same
+ // as the previous CT1).
+ EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
+ CheckedHandshakeSendReceive();
+ CheckAcks(client_filters_, 0,
+ {0, // SH
+ 0x0002000000000000ULL, // EE
+ 0x0002000000000002ULL} // CT2
+ );
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Shrink the MTU down so that certs get split and drop the second piece.
+TEST_F(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
+ server_filters_.drop_->Reset({3});
+ StartConnect();
+ ShrinkPostServerHelloMtu();
+ client_->Handshake();
+ server_->Handshake();
+ // Check that things got split.
+ EXPECT_EQ(6UL,
+ server_filters_.records_->count()); // SH, EE, CT1, CT2, CV, FIN
+ size_t ct1_size = server_filters_.record(3).buffer.len();
+ server_filters_.records_->Clear();
+ expected_client_acks_ = 1;
+ HandshakeAndAck(client_);
+ server_->Handshake(); // Retransmit
+ EXPECT_EQ(3UL, server_filters_.records_->count()); // CT1, CV, FIN
+ // Check that the first record is CT1
+ EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
+ CheckedHandshakeSendReceive();
+ CheckAcks(client_filters_, 0,
+ {
+ 0, // SH
+ 0x0002000000000000ULL, // EE
+ 0x0002000000000001ULL, // CT1
+ });
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// In this test, the Certificate message is sent four times, we drop all or part
+// of the first three attempts:
+// 1. Without fragmentation so that we can see how big it is - we drop that.
+// 2. In two pieces - we drop half AND the resulting ACK.
+// 3. In three pieces - we drop the middle piece.
+//
+// After that we let all the ACKs through and allow the handshake to complete
+// without further interference.
+//
+// This allows us to test that ranges of handshake messages are sent correctly
+// even when there are overlapping acknowledgments; that ACKs with duplicate or
+// overlapping message ranges are handled properly; and that extra
+// retransmissions are handled properly.
+class TlsFragmentationAndRecoveryTest : public TlsDropDatagram13 {
+ public:
+ TlsFragmentationAndRecoveryTest() : cert_len_(0) {}
+
+ protected:
+ void RunTest(size_t dropped_half) {
+ FirstFlightDropCertificate();
+
+ SecondAttemptDropHalf(dropped_half);
+ size_t dropped_half_size = server_record_len(dropped_half);
+ size_t second_flight_count = server_filters_.records_->count();
+
+ ThirdAttemptDropMiddle();
+ size_t repaired_third_size = server_record_len((dropped_half == 0) ? 0 : 2);
+ size_t third_flight_count = server_filters_.records_->count();
+
+ AckAndCompleteRetransmission();
+ size_t final_server_flight_count = server_filters_.records_->count();
+ EXPECT_LE(3U, final_server_flight_count); // CT(sixth), CV, Fin
+ CheckSizeOfSixth(dropped_half_size, repaired_third_size);
+
+ SendDelayedAck();
+ // Same number of messages as the last flight.
+ EXPECT_EQ(final_server_flight_count, server_filters_.records_->count());
+ // Double check that the Certificate size is still correct.
+ CheckSizeOfSixth(dropped_half_size, repaired_third_size);
+
+ CompleteHandshake(final_server_flight_count);
+
+ // This is the ACK for the first attempt to send a whole certificate.
+ std::vector<uint64_t> client_acks = {
+ 0, // SH
+ 0x0002000000000000ULL // EE
+ };
+ CheckAcks(client_filters_, 0, client_acks);
+ // And from the second attempt for the half was kept (we delayed this ACK).
+ client_acks.push_back(0x0002000000000000ULL + second_flight_count +
+ ~dropped_half % 2);
+ CheckAcks(client_filters_, 1, client_acks);
+ // And the third attempt where the first and last thirds got through.
+ client_acks.push_back(0x0002000000000000ULL + second_flight_count +
+ third_flight_count - 1);
+ client_acks.push_back(0x0002000000000000ULL + second_flight_count +
+ third_flight_count + 1);
+ CheckAcks(client_filters_, 2, client_acks);
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ }
+
+ private:
+ void FirstFlightDropCertificate() {
+ StartConnect();
+ client_->Handshake();
+
+ // Note: 1 << N is the Nth packet, starting from zero.
+ server_filters_.drop_->Reset(1 << 2); // Drop Cert0.
+ server_->Handshake();
+ EXPECT_EQ(5U, server_filters_.records_->count()); // SH, EE, CT, CV, Fin
+ cert_len_ = server_filters_.records_->record(2).buffer.len();
+
+ HandshakeAndAck(client_);
+ EXPECT_EQ(2U, client_filters_.records_->count());
+ }
+
+ // Lower the MTU so that the server has to split the certificate in two
+ // pieces. The server resends Certificate (in two), plus CV and Fin.
+ void SecondAttemptDropHalf(size_t dropped_half) {
+ ASSERT_LE(0U, dropped_half);
+ ASSERT_GT(2U, dropped_half);
+ server_filters_.records_->Clear();
+ server_filters_.drop_->Reset({dropped_half}); // Drop Cert1[half]
+ SplitServerMtu(2);
+ server_->Handshake();
+ EXPECT_LE(4U, server_filters_.records_->count()); // CT x2, CV, Fin
+
+ // Generate and capture the ACK from the client.
+ client_filters_.drop_->Reset({0});
+ HandshakeAndAck(client_);
+ EXPECT_EQ(3U, client_filters_.records_->count());
+ }
+
+ // Lower the MTU again so that the server sends Certificate cut into three
+ // pieces. Drop the middle piece.
+ void ThirdAttemptDropMiddle() {
+ server_filters_.records_->Clear();
+ server_filters_.drop_->Reset({1}); // Drop Cert2[1] (of 3)
+ SplitServerMtu(3);
+ // Because we dropped the client ACK, the server retransmits on a timer.
+ ShiftDtlsTimers();
+ server_->Handshake();
+ EXPECT_LE(5U, server_filters_.records_->count()); // CT x3, CV, Fin
+ }
+
+ void AckAndCompleteRetransmission() {
+ // Generate ACKs.
+ HandshakeAndAck(client_);
+ // The server should send the final sixth of the certificate: the client has
+ // acknowledged the first half and the last third. Also send CV and Fin.
+ server_filters_.records_->Clear();
+ server_->Handshake();
+ }
+
+ void CheckSizeOfSixth(size_t size_of_half, size_t size_of_third) {
+ // Work out if the final sixth is the right size. We get the records with
+ // overheads added, which obscures the length of the payload. We want to
+ // ensure that the server only sent the missing sixth of the Certificate.
+ //
+ // We captured |size_of_half + overhead| and |size_of_third + overhead| and
+ // want to calculate |size_of_third - size_of_third + overhead|. We can't
+ // calculate |overhead|, but it is is (currently) always a handshake message
+ // header, a content type, and an authentication tag:
+ static const size_t record_overhead = 12 + 1 + 16;
+ EXPECT_EQ(size_of_half - size_of_third + record_overhead,
+ server_filters_.records_->record(0).buffer.len());
+ }
+
+ void SendDelayedAck() {
+ // Send the ACK we held back. The reordered ACK doesn't add new
+ // information,
+ // but triggers an extra retransmission of the missing records again (even
+ // though the client has all that it needs).
+ client_->SendRecordDirect(client_filters_.records_->record(2));
+ server_filters_.records_->Clear();
+ server_->Handshake();
+ }
+
+ void CompleteHandshake(size_t extra_retransmissions) {
+ // All this messing around shouldn't cause a failure...
+ Handshake();
+ // ...but it leaves a mess. Add an extra few calls to Handshake() for the
+ // client so that it absorbs the extra retransmissions.
+ for (size_t i = 0; i < extra_retransmissions; ++i) {
+ client_->Handshake();
+ }
+ CheckConnected();
+ }
+
+ // Split the server MTU so that the Certificate is split into |count| pieces.
+ // The calculation doesn't need to be perfect as long as the Certificate
+ // message is split into the right number of pieces.
+ void SplitServerMtu(size_t count) {
+ // Set the MTU based on the formula:
+ // bare_size = cert_len_ - actual_overhead
+ // MTU = ceil(bare_size / count) + pessimistic_overhead
+ //
+ // actual_overhead is the amount of actual overhead on the record we
+ // captured, which is (note that our length doesn't include the header):
+ static const size_t actual_overhead = 12 + // handshake message header
+ 1 + // content type
+ 16; // authentication tag
+ size_t bare_size = cert_len_ - actual_overhead;
+
+ // pessimistic_overhead is the amount of expansion that NSS assumes will be
+ // added to each handshake record. Right now, that is DTLS_MIN_FRAGMENT:
+ static const size_t pessimistic_overhead =
+ 12 + // handshake message header
+ 1 + // content type
+ 13 + // record header length
+ 64; // maximum record expansion: IV, MAC and block cipher expansion
+
+ size_t mtu = (bare_size + count - 1) / count + pessimistic_overhead;
+ if (g_ssl_gtest_verbose) {
+ std::cerr << "server: set MTU to " << mtu << std::endl;
+ }
+ EXPECT_EQ(SECSuccess, SSLInt_SetMTU(server_->ssl_fd(), mtu));
+ }
+
+ size_t server_record_len(size_t index) const {
+ return server_filters_.records_->record(index).buffer.len();
+ }
+
+ size_t cert_len_;
+};
+
+TEST_F(TlsFragmentationAndRecoveryTest, DropFirstHalf) { RunTest(0); }
+
+TEST_F(TlsFragmentationAndRecoveryTest, DropSecondHalf) { RunTest(1); }
+
+TEST_F(TlsDropDatagram13, NoDropsDuringZeroRtt) {
+ SetupForZeroRtt();
+ SetFilters();
+ std::cerr << "Starting second handshake" << std::endl;
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsDropDatagram13, DropEEDuringZeroRtt) {
+ SetupForZeroRtt();
+ SetFilters();
+ std::cerr << "Starting second handshake" << std::endl;
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ server_filters_.drop_->Reset({1});
+ ZeroRttSendReceive(true, true);
+ HandshakeAndAck(client_);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+ CheckAcks(client_filters_, 0, {0});
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+class TlsReorderDatagram13 : public TlsDropDatagram13 {
+ public:
+ TlsReorderDatagram13() {}
+
+ // Send records from the records buffer in the given order.
+ void ReSend(TlsAgent::Role side, std::vector<size_t> indices) {
+ std::shared_ptr<TlsAgent> agent;
+ std::shared_ptr<TlsRecordRecorder> records;
+
+ if (side == TlsAgent::CLIENT) {
+ agent = client_;
+ records = client_filters_.records_;
+ } else {
+ agent = server_;
+ records = server_filters_.records_;
+ }
+
+ for (auto i : indices) {
+ agent->SendRecordDirect(records->record(i));
+ }
+ }
+};
+
+// Reorder the server records so that EE comes at the end
+// of the flight and will still produce an ACK.
+TEST_F(TlsDropDatagram13, ReorderServerEE) {
+ server_filters_.drop_->Reset({1});
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ // We dropped EE, now reinject.
+ server_->SendRecordDirect(server_filters_.record(1));
+ expected_client_acks_ = 1;
+ HandshakeAndAck(client_);
+ CheckedHandshakeSendReceive();
+ CheckAcks(client_filters_, 0,
+ {
+ 0, // SH
+ 0x0002000000000000, // EE
+ });
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// The client sends an out of order non-handshake message
+// but with the handshake key.
+class TlsSendCipherSpecCapturer {
+ public:
+ TlsSendCipherSpecCapturer(std::shared_ptr<TlsAgent>& agent)
+ : send_cipher_specs_() {
+ SSLInt_SetCipherSpecChangeFunc(agent->ssl_fd(), CipherSpecChanged,
+ (void*)this);
+ }
+
+ std::shared_ptr<TlsCipherSpec> spec(size_t i) {
+ if (i >= send_cipher_specs_.size()) {
+ return nullptr;
+ }
+ return send_cipher_specs_[i];
+ }
+
+ private:
+ static void CipherSpecChanged(void* arg, PRBool sending,
+ ssl3CipherSpec* newSpec) {
+ if (!sending) {
+ return;
+ }
+
+ auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
+
+ auto spec = std::make_shared<TlsCipherSpec>();
+ bool ret = spec->Init(SSLInt_CipherSpecToEpoch(newSpec),
+ SSLInt_CipherSpecToAlgorithm(newSpec),
+ SSLInt_CipherSpecToKey(newSpec),
+ SSLInt_CipherSpecToIv(newSpec));
+ EXPECT_EQ(true, ret);
+ self->send_cipher_specs_.push_back(spec);
+ }
+
+ std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
+};
+
+TEST_F(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
+ StartConnect();
+ TlsSendCipherSpecCapturer capturer(client_);
+ client_->Handshake();
+ server_->Handshake();
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+ server_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+ // After the client sends Finished, inject an app data record
+ // with the handshake key. This should produce an alert.
+ uint8_t buf[] = {'a', 'b', 'c'};
+ auto spec = capturer.spec(0);
+ ASSERT_NE(nullptr, spec.get());
+ ASSERT_EQ(2, spec->epoch());
+ ASSERT_TRUE(client_->SendEncryptedRecord(
+ spec, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 0x0002000000000002,
+ kTlsApplicationDataType, DataBuffer(buf, sizeof(buf))));
+
+ // Now have the server consume the bogus message.
+ server_->ExpectSendAlert(illegal_parameter, kTlsAlertFatal);
+ server_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
+ EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, PORT_GetError());
+}
+
+TEST_F(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
+ StartConnect();
+ TlsSendCipherSpecCapturer capturer(client_);
+ client_->Handshake();
+ server_->Handshake();
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+ server_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+ // Inject a new bogus handshake record, which the server responds
+ // to by just ACKing the original one (we ignore the contents).
+ uint8_t buf[] = {'a', 'b', 'c'};
+ auto spec = capturer.spec(0);
+ ASSERT_NE(nullptr, spec.get());
+ ASSERT_EQ(2, spec->epoch());
+ ASSERT_TRUE(client_->SendEncryptedRecord(
+ spec, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 0x0002000000000002,
+ kTlsHandshakeType, DataBuffer(buf, sizeof(buf))));
+ server_->Handshake();
+ EXPECT_EQ(2UL, server_filters_.ack_->count());
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
+}
+
+// Shrink the MTU down so that certs get split and then swap the first and
+// second pieces of the server certificate.
+TEST_F(TlsReorderDatagram13, ReorderServerCertificate) {
+ StartConnect();
+ ShrinkPostServerHelloMtu();
+ client_->Handshake();
+ // Drop the entire handshake flight so we can reorder.
+ server_filters_.drop_->Reset(0xff);
+ server_->Handshake();
+ // Check that things got split.
+ EXPECT_EQ(6UL,
+ server_filters_.records_->count()); // CH, EE, CT1, CT2, CV, FIN
+ // Now re-send things in a different order.
+ ReSend(TlsAgent::SERVER, std::vector<size_t>{0, 1, 3, 2, 4, 5});
+ // Clear.
+ server_filters_.drop_->Disable();
+ server_filters_.records_->Clear();
+ // Wait for client to send ACK.
+ ShiftDtlsTimers();
+ CheckedHandshakeSendReceive();
+ EXPECT_EQ(2UL, server_filters_.records_->count()); // ACK + Data
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
+ SetupForZeroRtt();
+ SetFilters();
+ std::cerr << "Starting second handshake" << std::endl;
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ // Send the client's first flight of zero RTT data.
+ ZeroRttSendReceive(true, true);
+ // Now send another client application data record but
+ // capture it.
+ client_filters_.records_->Clear();
+ client_filters_.drop_->Reset(0xff);
+ const char* k0RttData = "123456";
+ const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+ PRInt32 rv =
+ PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen); // 0-RTT write.
+ EXPECT_EQ(k0RttDataLen, rv);
+ EXPECT_EQ(1UL, client_filters_.records_->count()); // data
+ server_->Handshake();
+ client_->Handshake();
+ ExpectEarlyDataAccepted(true);
+ // The server still hasn't received anything at this point.
+ EXPECT_EQ(3UL, client_filters_.records_->count()); // data, EOED, FIN
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+ EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+ // Now re-send the client's messages: EOED, data, FIN
+ ReSend(TlsAgent::CLIENT, std::vector<size_t>({1, 0, 2}));
+ server_->Handshake();
+ CheckConnected();
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ uint8_t buf[8];
+ rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+ EXPECT_EQ(-1, rv);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
+TEST_F(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
+ SetupForZeroRtt();
+ SetFilters();
+ std::cerr << "Starting second handshake" << std::endl;
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ // Send the client's first flight of zero RTT data.
+ ZeroRttSendReceive(true, true);
+ // Now send another client application data record but
+ // capture it.
+ client_filters_.records_->Clear();
+ client_filters_.drop_->Reset(0xff);
+ const char* k0RttData = "123456";
+ const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+ PRInt32 rv =
+ PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen); // 0-RTT write.
+ EXPECT_EQ(k0RttDataLen, rv);
+ EXPECT_EQ(1UL, client_filters_.records_->count()); // data
+ server_->Handshake();
+ client_->Handshake();
+ ExpectEarlyDataAccepted(true);
+ // The server still hasn't received anything at this point.
+ EXPECT_EQ(3UL, client_filters_.records_->count()); // EOED, FIN, Data
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+ EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+ // Now re-send the client's messages: EOED, FIN, Data
+ ReSend(TlsAgent::CLIENT, std::vector<size_t>({1, 2, 0}));
+ server_->Handshake();
+ CheckConnected();
+ CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ uint8_t buf[8];
+ rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+ EXPECT_EQ(-1, rv);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
static void GetCipherAndLimit(uint16_t version, uint16_t* cipher,
uint64_t* limit = nullptr) {
uint64_t l;
@@ -111,7 +836,6 @@ TEST_P(TlsConnectDatagram12Plus, MissAWindow) {
GetCipherAndLimit(version_, &cipher);
server_->EnableSingleCipher(cipher);
Connect();
-
EXPECT_EQ(SECSuccess, SSLInt_AdvanceWriteSeqByAWindow(client_->ssl_fd(), 0));
SendReceive();
}
@@ -129,5 +853,7 @@ TEST_P(TlsConnectDatagram12Plus, MissAWindowAndOne) {
INSTANTIATE_TEST_CASE_P(Datagram12Plus, TlsConnectDatagram12Plus,
TlsConnectTestBase::kTlsV12Plus);
+INSTANTIATE_TEST_CASE_P(DatagramPre13, TlsConnectDatagramPre13,
+ TlsConnectTestBase::kTlsV11V12);
} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
index 1e406b6c2..e0f8b1f55 100644
--- a/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
@@ -193,7 +193,9 @@ TEST_P(TlsConnectGenericPre13, P384PriorityFromModelSocket) {
class TlsKeyExchangeGroupCapture : public TlsHandshakeFilter {
public:
- TlsKeyExchangeGroupCapture() : group_(ssl_grp_none) {}
+ TlsKeyExchangeGroupCapture()
+ : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}),
+ group_(ssl_grp_none) {}
SSLNamedGroup group() const { return group_; }
@@ -201,10 +203,6 @@ class TlsKeyExchangeGroupCapture : public TlsHandshakeFilter {
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
const DataBuffer &input,
DataBuffer *output) {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
uint32_t value = 0;
EXPECT_TRUE(input.Read(0, 1, &value));
EXPECT_EQ(3U, value) << "curve type has to be 3";
@@ -518,16 +516,12 @@ TEST_P(TlsKeyExchangeTest13, MultipleClientShares) {
// Replace the point in the client key exchange message with an empty one
class ECCClientKEXFilter : public TlsHandshakeFilter {
public:
- ECCClientKEXFilter() {}
+ ECCClientKEXFilter() : TlsHandshakeFilter({kTlsHandshakeClientKeyExchange}) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
const DataBuffer &input,
DataBuffer *output) {
- if (header.handshake_type() != kTlsHandshakeClientKeyExchange) {
- return KEEP;
- }
-
// Replace the client key exchange message with an empty point
output->Allocate(1);
output->Write(0, 0U, 1); // set point length 0
@@ -538,20 +532,16 @@ class ECCClientKEXFilter : public TlsHandshakeFilter {
// Replace the point in the server key exchange message with an empty one
class ECCServerKEXFilter : public TlsHandshakeFilter {
public:
- ECCServerKEXFilter() {}
+ ECCServerKEXFilter() : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
const DataBuffer &input,
DataBuffer *output) {
- if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
- return KEEP;
- }
-
// Replace the server key exchange message with an empty point
output->Allocate(4);
output->Write(0, 3U, 1); // named curve
- uint32_t curve;
+ uint32_t curve = 0;
EXPECT_TRUE(input.Read(1, 2, &curve)); // get curve id
output->Write(1, curve, 2); // write curve id
output->Write(3, 0U, 1); // point length 0
diff --git a/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
index be407b42e..c42883eb7 100644
--- a/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
@@ -118,7 +118,6 @@ int32_t RegularExporterShouldFail(TlsAgent* agent, const SECItem* srvNameArr,
TEST_P(TlsConnectTls13, EarlyExporter) {
SetupForZeroRtt();
- ExpectAlert(client_, kTlsAlertEndOfEarlyData);
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
diff --git a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
index d15139419..4142ab07a 100644
--- a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -61,60 +61,14 @@ class TlsExtensionDamager : public TlsExtensionFilter {
size_t index_;
};
-class TlsExtensionInjector : public TlsHandshakeFilter {
- public:
- TlsExtensionInjector(uint16_t ext, DataBuffer& data)
- : extension_(ext), data_(data) {}
-
- virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
- const DataBuffer& input,
- DataBuffer* output) {
- TlsParser parser(input);
- if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
- return KEEP;
- }
- size_t offset = parser.consumed();
-
- *output = input;
-
- // Increase the size of the extensions.
- uint16_t ext_len;
- memcpy(&ext_len, output->data() + offset, sizeof(ext_len));
- ext_len = htons(ntohs(ext_len) + data_.len() + 4);
- memcpy(output->data() + offset, &ext_len, sizeof(ext_len));
-
- // Insert the extension type and length.
- DataBuffer type_length;
- type_length.Allocate(4);
- type_length.Write(0, extension_, 2);
- type_length.Write(2, data_.len(), 2);
- output->Splice(type_length, offset + 2);
-
- // Insert the payload.
- if (data_.len() > 0) {
- output->Splice(data_, offset + 6);
- }
-
- return CHANGE;
- }
-
- private:
- const uint16_t extension_;
- const DataBuffer data_;
-};
-
class TlsExtensionAppender : public TlsHandshakeFilter {
public:
TlsExtensionAppender(uint8_t handshake_type, uint16_t ext, DataBuffer& data)
- : handshake_type_(handshake_type), extension_(ext), data_(data) {}
+ : TlsHandshakeFilter({handshake_type}), extension_(ext), data_(data) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
- if (header.handshake_type() != handshake_type_) {
- return KEEP;
- }
-
TlsParser parser(input);
if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
return KEEP;
@@ -159,7 +113,6 @@ class TlsExtensionAppender : public TlsHandshakeFilter {
return true;
}
- const uint8_t handshake_type_;
const uint16_t extension_;
const DataBuffer data_;
};
@@ -200,8 +153,7 @@ class TlsExtensionTestBase : public TlsConnectTestBase {
client_->ConfigNamedGroups(client_groups);
server_->ConfigNamedGroups(server_groups);
EnsureTlsSetup();
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
client_->Handshake(); // Send ClientHello
server_->Handshake(); // Send HRR.
client_->SetPacketFilter(std::make_shared<TlsExtensionDropper>(type));
@@ -1009,7 +961,6 @@ class TlsBogusExtensionTest : public TlsConnectTestBase,
std::make_shared<TlsExtensionAppender>(message, extension, empty);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
server_->SetTlsRecordFilter(filter);
- filter->EnableDecryption();
} else {
server_->SetPacketFilter(filter);
}
@@ -1032,17 +983,20 @@ class TlsBogusExtensionTestPre13 : public TlsBogusExtensionTest {
class TlsBogusExtensionTest13 : public TlsBogusExtensionTest {
protected:
void ConnectAndFail(uint8_t message) override {
- if (message == kTlsHandshakeHelloRetryRequest) {
+ if (message != kTlsHandshakeServerHello) {
ConnectExpectAlert(client_, kTlsAlertUnsupportedExtension);
return;
}
- client_->StartConnect();
- server_->StartConnect();
+ FailWithAlert(kTlsAlertUnsupportedExtension);
+ }
+
+ void FailWithAlert(uint8_t alert) {
+ StartConnect();
client_->Handshake(); // ClientHello
server_->Handshake(); // ServerHello
- client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+ client_->ExpectSendAlert(alert);
client_->Handshake();
if (variant_ == ssl_variant_stream) {
server_->ExpectSendAlert(kTlsAlertBadRecordMac);
@@ -1067,9 +1021,12 @@ TEST_P(TlsBogusExtensionTest13, AddBogusExtensionCertificate) {
Run(kTlsHandshakeCertificate);
}
+// It's perfectly valid to set unknown extensions in CertificateRequest.
TEST_P(TlsBogusExtensionTest13, AddBogusExtensionCertificateRequest) {
server_->RequestClientAuth(false);
- Run(kTlsHandshakeCertificateRequest);
+ AddFilter(kTlsHandshakeCertificateRequest, 0xff);
+ ConnectExpectAlert(client_, kTlsAlertDecryptError);
+ client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
}
TEST_P(TlsBogusExtensionTest13, AddBogusExtensionHelloRetryRequest) {
@@ -1079,10 +1036,6 @@ TEST_P(TlsBogusExtensionTest13, AddBogusExtensionHelloRetryRequest) {
Run(kTlsHandshakeHelloRetryRequest);
}
-TEST_P(TlsBogusExtensionTest13, AddVersionExtensionServerHello) {
- Run(kTlsHandshakeServerHello, ssl_tls13_supported_versions_xtn);
-}
-
TEST_P(TlsBogusExtensionTest13, AddVersionExtensionEncryptedExtensions) {
Run(kTlsHandshakeEncryptedExtensions, ssl_tls13_supported_versions_xtn);
}
@@ -1096,13 +1049,6 @@ TEST_P(TlsBogusExtensionTest13, AddVersionExtensionCertificateRequest) {
Run(kTlsHandshakeCertificateRequest, ssl_tls13_supported_versions_xtn);
}
-TEST_P(TlsBogusExtensionTest13, AddVersionExtensionHelloRetryRequest) {
- static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
- server_->ConfigNamedGroups(groups);
-
- Run(kTlsHandshakeHelloRetryRequest, ssl_tls13_supported_versions_xtn);
-}
-
// NewSessionTicket allows unknown extensions AND it isn't protected by the
// Finished. So adding an unknown extension doesn't cause an error.
TEST_P(TlsBogusExtensionTest13, AddBogusExtensionNewSessionTicket) {
diff --git a/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
index 44cacce46..64b824786 100644
--- a/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
@@ -51,10 +51,16 @@ class RecordFragmenter : public PacketFilter {
while (parser.remaining()) {
TlsHandshakeFilter::HandshakeHeader handshake_header;
DataBuffer handshake_body;
- if (!handshake_header.Parse(&parser, record_header, &handshake_body)) {
+ bool complete = false;
+ if (!handshake_header.Parse(&parser, record_header, DataBuffer(),
+ &handshake_body, &complete)) {
ADD_FAILURE() << "couldn't parse handshake header";
return false;
}
+ if (!complete) {
+ ADD_FAILURE() << "don't want to deal with fragmented messages";
+ return false;
+ }
DataBuffer record_fragment;
// We can't fragment handshake records that are too small.
@@ -82,7 +88,7 @@ class RecordFragmenter : public PacketFilter {
while (parser.remaining()) {
TlsRecordHeader header;
DataBuffer record;
- if (!header.Parse(&parser, &record)) {
+ if (!header.Parse(0, &parser, &record)) {
ADD_FAILURE() << "bad record header";
return false;
}
diff --git a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
index 1587b66de..ab4c0eab7 100644
--- a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -47,9 +47,9 @@ class TlsApplicationDataRecorder : public TlsRecordFilter {
// Ensure that ssl_Time() returns a constant value.
FUZZ_F(TlsFuzzTest, SSL_Time_Constant) {
- PRUint32 now = ssl_Time();
+ PRUint32 now = ssl_TimeSec();
PR_Sleep(PR_SecondsToInterval(2));
- EXPECT_EQ(ssl_Time(), now);
+ EXPECT_EQ(ssl_TimeSec(), now);
}
// Check that due to the deterministic PRNG we derive
@@ -215,58 +215,6 @@ FUZZ_P(TlsConnectGeneric, SessionTicketResumption) {
SendReceive();
}
-class TlsSessionTicketMacDamager : public TlsExtensionFilter {
- public:
- TlsSessionTicketMacDamager() {}
- virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
- const DataBuffer& input,
- DataBuffer* output) {
- if (extension_type != ssl_session_ticket_xtn &&
- extension_type != ssl_tls13_pre_shared_key_xtn) {
- return KEEP;
- }
-
- *output = input;
-
- // Handle everything before TLS 1.3.
- if (extension_type == ssl_session_ticket_xtn) {
- // Modify the last byte of the MAC.
- output->data()[output->len() - 1] ^= 0xff;
- }
-
- // Handle TLS 1.3.
- if (extension_type == ssl_tls13_pre_shared_key_xtn) {
- TlsParser parser(input);
-
- uint32_t ids_len;
- EXPECT_TRUE(parser.Read(&ids_len, 2) && ids_len > 0);
-
- uint32_t ticket_len;
- EXPECT_TRUE(parser.Read(&ticket_len, 2) && ticket_len > 0);
-
- // Modify the last byte of the MAC.
- output->data()[2 + 2 + ticket_len - 1] ^= 0xff;
- }
-
- return CHANGE;
- }
-};
-
-// Check that session ticket resumption works with a bad MAC.
-FUZZ_P(TlsConnectGeneric, SessionTicketResumptionBadMac) {
- ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
- Connect();
- SendReceive();
-
- Reset();
- ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
- ExpectResumption(RESUME_TICKET);
-
- client_->SetPacketFilter(std::make_shared<TlsSessionTicketMacDamager>());
- Connect();
- SendReceive();
-}
-
// Check that session tickets are not encrypted.
FUZZ_P(TlsConnectGeneric, UnencryptedSessionTickets) {
ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
@@ -276,10 +224,13 @@ FUZZ_P(TlsConnectGeneric, UnencryptedSessionTickets) {
server_->SetPacketFilter(i1);
Connect();
+ std::cerr << "ticket" << i1->buffer() << std::endl;
size_t offset = 4; /* lifetime */
if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
- offset += 1 + 1 + /* ke_modes */
- 1 + 1; /* auth_modes */
+ offset += 4; /* ticket_age_add */
+ uint32_t nonce_len = 0;
+ EXPECT_TRUE(i1->buffer().Read(offset, 1, &nonce_len));
+ offset += 1 + nonce_len;
}
offset += 2 + /* ticket length */
2; /* TLS_EX_SESS_TICKET_VERSION */
diff --git a/security/nss/gtests/ssl_gtest/ssl_gtest.cc b/security/nss/gtests/ssl_gtest/ssl_gtest.cc
index cd10076b8..2fff9d7cb 100644
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.cc
@@ -6,6 +6,7 @@
#include <cstdlib>
#include "test_io.h"
+#include "databuffer.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
@@ -28,6 +29,7 @@ int main(int argc, char** argv) {
++i;
} else if (!strcmp(argv[i], "-v")) {
g_ssl_gtest_verbose = true;
+ nss_test::DataBuffer::SetLogLimit(16384);
}
}
diff --git a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
index 8cd7d1009..e2a8d830a 100644
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -11,6 +11,7 @@
'target_name': 'ssl_gtest',
'type': 'executable',
'sources': [
+ 'bloomfilter_unittest.cc',
'libssl_internals.c',
'selfencrypt_unittest.cc',
'ssl_0rtt_unittest.cc',
@@ -18,6 +19,7 @@
'ssl_auth_unittest.cc',
'ssl_cert_ext_unittest.cc',
'ssl_ciphersuite_unittest.cc',
+ 'ssl_custext_unittest.cc',
'ssl_damage_unittest.cc',
'ssl_dhe_unittest.cc',
'ssl_drop_unittest.cc',
@@ -30,11 +32,16 @@
'ssl_gather_unittest.cc',
'ssl_gtest.cc',
'ssl_hrr_unittest.cc',
+ 'ssl_keylog_unittest.cc',
+ 'ssl_keyupdate_unittest.cc',
'ssl_loopback_unittest.cc',
+ 'ssl_misc_unittest.cc',
'ssl_record_unittest.cc',
'ssl_resumption_unittest.cc',
+ 'ssl_renegotiation_unittest.cc',
'ssl_skip_unittest.cc',
'ssl_staticrsa_unittest.cc',
+ 'ssl_tls13compat_unittest.cc',
'ssl_v2_client_hello_unittest.cc',
'ssl_version_unittest.cc',
'ssl_versionpolicy_unittest.cc',
diff --git a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
index 39055f641..93e19a720 100644
--- a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
@@ -187,6 +187,590 @@ TEST_P(TlsConnectTls13, RetryWithSameKeyShare) {
EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
}
+// Here we modify the second ClientHello so that the client retries with the
+// same shares, even though the server wanted something else.
+TEST_P(TlsConnectTls13, RetryWithTwoShares) {
+ EnsureTlsSetup();
+ EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+ client_->SetPacketFilter(std::make_shared<KeyShareReplayer>());
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
+ ssl_grp_ec_secp521r1};
+ server_->ConfigNamedGroups(groups);
+ ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
+ EXPECT_EQ(SSL_ERROR_BAD_2ND_CLIENT_HELLO, server_->error_code());
+ EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackAccept) {
+ EnsureTlsSetup();
+
+ auto accept_hello = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) {
+ auto* called = reinterpret_cast<bool*>(arg);
+ *called = true;
+
+ EXPECT_TRUE(firstHello);
+ EXPECT_EQ(0U, clientTokenLen);
+ return ssl_hello_retry_accept;
+ };
+
+ bool cb_run = false;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ accept_hello, &cb_run));
+ Connect();
+ EXPECT_TRUE(cb_run);
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackAcceptGroupMismatch) {
+ EnsureTlsSetup();
+
+ auto accept_hello_twice = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen,
+ unsigned int appTokenMax, void* arg) {
+ auto* called = reinterpret_cast<size_t*>(arg);
+ ++*called;
+
+ EXPECT_EQ(0U, clientTokenLen);
+ return ssl_hello_retry_accept;
+ };
+
+ auto capture = std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
+ capture->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ server_->SetPacketFilter(capture);
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ server_->ConfigNamedGroups(groups);
+
+ size_t cb_run = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+ server_->ssl_fd(), accept_hello_twice, &cb_run));
+ Connect();
+ EXPECT_EQ(2U, cb_run);
+ EXPECT_TRUE(capture->captured()) << "expected a cookie in HelloRetryRequest";
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackFail) {
+ EnsureTlsSetup();
+
+ auto fail_hello = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) {
+ auto* called = reinterpret_cast<bool*>(arg);
+ *called = true;
+
+ EXPECT_TRUE(firstHello);
+ EXPECT_EQ(0U, clientTokenLen);
+ return ssl_hello_retry_fail;
+ };
+
+ bool cb_run = false;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ fail_hello, &cb_run));
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+ server_->CheckErrorCode(SSL_ERROR_APPLICATION_ABORT);
+ EXPECT_TRUE(cb_run);
+}
+
+// Asking for retry twice isn't allowed.
+TEST_P(TlsConnectTls13, RetryCallbackRequestHrrTwice) {
+ EnsureTlsSetup();
+
+ auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) -> SSLHelloRetryRequestAction {
+ return ssl_hello_retry_request;
+ };
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ bad_callback, NULL));
+ ConnectExpectAlert(server_, kTlsAlertInternalError);
+ server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+// Accepting the CH and modifying the token isn't allowed.
+TEST_P(TlsConnectTls13, RetryCallbackAcceptAndSetToken) {
+ EnsureTlsSetup();
+
+ auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) -> SSLHelloRetryRequestAction {
+ *appTokenLen = 1;
+ return ssl_hello_retry_accept;
+ };
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ bad_callback, NULL));
+ ConnectExpectAlert(server_, kTlsAlertInternalError);
+ server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+// As above, but with reject.
+TEST_P(TlsConnectTls13, RetryCallbackRejectAndSetToken) {
+ EnsureTlsSetup();
+
+ auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) -> SSLHelloRetryRequestAction {
+ *appTokenLen = 1;
+ return ssl_hello_retry_fail;
+ };
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ bad_callback, NULL));
+ ConnectExpectAlert(server_, kTlsAlertInternalError);
+ server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+// This is a (pretend) buffer overflow.
+TEST_P(TlsConnectTls13, RetryCallbackSetTooLargeToken) {
+ EnsureTlsSetup();
+
+ auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) -> SSLHelloRetryRequestAction {
+ *appTokenLen = appTokenMax + 1;
+ return ssl_hello_retry_accept;
+ };
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ bad_callback, NULL));
+ ConnectExpectAlert(server_, kTlsAlertInternalError);
+ server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+SSLHelloRetryRequestAction RetryHello(PRBool firstHello,
+ const PRUint8* clientToken,
+ unsigned int clientTokenLen,
+ PRUint8* appToken,
+ unsigned int* appTokenLen,
+ unsigned int appTokenMax, void* arg) {
+ auto* called = reinterpret_cast<size_t*>(arg);
+ ++*called;
+
+ EXPECT_EQ(0U, clientTokenLen);
+ return firstHello ? ssl_hello_retry_request : ssl_hello_retry_accept;
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetry) {
+ EnsureTlsSetup();
+
+ auto capture_hrr = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+ ssl_hs_hello_retry_request);
+ auto capture_key_share =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ std::vector<std::shared_ptr<PacketFilter>> chain = {capture_hrr,
+ capture_key_share};
+ server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(chain));
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ RetryHello, &cb_called));
+
+ // Do the first message exchange.
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+
+ EXPECT_EQ(1U, cb_called) << "callback should be called once here";
+ EXPECT_LT(0U, capture_hrr->buffer().len()) << "HelloRetryRequest expected";
+ EXPECT_FALSE(capture_key_share->captured())
+ << "no key_share extension expected";
+
+ auto capture_cookie =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
+ client_->SetPacketFilter(capture_cookie);
+
+ Handshake();
+ CheckConnected();
+ EXPECT_EQ(2U, cb_called);
+ EXPECT_TRUE(capture_cookie->captured()) << "should have a cookie";
+}
+
+static size_t CountShares(const DataBuffer& key_share) {
+ size_t count = 0;
+ uint32_t len = 0;
+ size_t offset = 2;
+
+ EXPECT_TRUE(key_share.Read(0, 2, &len));
+ EXPECT_EQ(key_share.len() - 2, len);
+ while (offset < key_share.len()) {
+ offset += 2; // Skip KeyShareEntry.group
+ EXPECT_TRUE(key_share.Read(offset, 2, &len));
+ offset += 2 + len; // Skip KeyShareEntry.key_exchange
+ ++count;
+ }
+ return count;
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithAdditionalShares) {
+ EnsureTlsSetup();
+ EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+
+ auto capture_server =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ capture_server->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ server_->SetPacketFilter(capture_server);
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ RetryHello, &cb_called));
+
+ // Do the first message exchange.
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+
+ EXPECT_EQ(1U, cb_called) << "callback should be called once here";
+ EXPECT_FALSE(capture_server->captured())
+ << "no key_share extension expected from server";
+
+ auto capture_client_2nd =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ client_->SetPacketFilter(capture_client_2nd);
+
+ Handshake();
+ CheckConnected();
+ EXPECT_EQ(2U, cb_called);
+ EXPECT_TRUE(capture_client_2nd->captured()) << "client should send key_share";
+ EXPECT_EQ(2U, CountShares(capture_client_2nd->extension()))
+ << "client should still send two shares";
+}
+
+// The callback should be run even if we have another reason to send
+// HelloRetryRequest. In this case, the server sends HRR because the server
+// wants a P-384 key share and the client didn't offer one.
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithGroupMismatch) {
+ EnsureTlsSetup();
+
+ auto capture_cookie =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
+ capture_cookie->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ auto capture_key_share =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+ ChainedPacketFilterInit{capture_cookie, capture_key_share}));
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ server_->ConfigNamedGroups(groups);
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ RetryHello, &cb_called));
+ Connect();
+ EXPECT_EQ(2U, cb_called);
+ EXPECT_TRUE(capture_cookie->captured()) << "cookie expected";
+ EXPECT_TRUE(capture_key_share->captured()) << "key_share expected";
+}
+
+static const uint8_t kApplicationToken[] = {0x92, 0x44, 0x00};
+
+SSLHelloRetryRequestAction RetryHelloWithToken(
+ PRBool firstHello, const PRUint8* clientToken, unsigned int clientTokenLen,
+ PRUint8* appToken, unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) {
+ auto* called = reinterpret_cast<size_t*>(arg);
+ ++*called;
+
+ if (firstHello) {
+ memcpy(appToken, kApplicationToken, sizeof(kApplicationToken));
+ *appTokenLen = sizeof(kApplicationToken);
+ return ssl_hello_retry_request;
+ }
+
+ EXPECT_EQ(DataBuffer(kApplicationToken, sizeof(kApplicationToken)),
+ DataBuffer(clientToken, static_cast<size_t>(clientTokenLen)));
+ return ssl_hello_retry_accept;
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithToken) {
+ EnsureTlsSetup();
+
+ auto capture_key_share =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ server_->SetPacketFilter(capture_key_share);
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess,
+ SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ RetryHelloWithToken, &cb_called));
+ Connect();
+ EXPECT_EQ(2U, cb_called);
+ EXPECT_FALSE(capture_key_share->captured()) << "no key share expected";
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithTokenAndGroupMismatch) {
+ EnsureTlsSetup();
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ server_->ConfigNamedGroups(groups);
+
+ auto capture_key_share =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ server_->SetPacketFilter(capture_key_share);
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess,
+ SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ RetryHelloWithToken, &cb_called));
+ Connect();
+ EXPECT_EQ(2U, cb_called);
+ EXPECT_TRUE(capture_key_share->captured()) << "key share expected";
+}
+
+SSLHelloRetryRequestAction CheckTicketToken(
+ PRBool firstHello, const PRUint8* clientToken, unsigned int clientTokenLen,
+ PRUint8* appToken, unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) {
+ auto* called = reinterpret_cast<bool*>(arg);
+ *called = true;
+
+ EXPECT_TRUE(firstHello);
+ EXPECT_EQ(DataBuffer(kApplicationToken, sizeof(kApplicationToken)),
+ DataBuffer(clientToken, static_cast<size_t>(clientTokenLen)));
+ return ssl_hello_retry_accept;
+}
+
+// Stream because SSL_SendSessionTicket only supports that.
+TEST_F(TlsConnectStreamTls13, RetryCallbackWithSessionTicketToken) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ EXPECT_EQ(SECSuccess,
+ SSL_SendSessionTicket(server_->ssl_fd(), kApplicationToken,
+ sizeof(kApplicationToken)));
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_TICKET);
+
+ bool cb_run = false;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+ server_->ssl_fd(), CheckTicketToken, &cb_run));
+ Connect();
+ EXPECT_TRUE(cb_run);
+}
+
+void TriggerHelloRetryRequest(std::shared_ptr<TlsAgent>& client,
+ std::shared_ptr<TlsAgent>& server) {
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server->ssl_fd(),
+ RetryHello, &cb_called));
+
+ // Start the handshake.
+ client->StartConnect();
+ server->StartConnect();
+ client->Handshake();
+ server->Handshake();
+ EXPECT_EQ(1U, cb_called);
+}
+
+TEST_P(TlsConnectTls13, RetryStateless) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ Handshake();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, RetryStatefulDropCookie) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ TriggerHelloRetryRequest(client_, server_);
+ client_->SetPacketFilter(
+ std::make_shared<TlsExtensionDropper>(ssl_tls13_cookie_xtn));
+
+ ExpectAlert(server_, kTlsAlertMissingExtension);
+ Handshake();
+ client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_MISSING_COOKIE_EXTENSION);
+}
+
+// Stream only because DTLS drops bad packets.
+TEST_F(TlsConnectStreamTls13, RetryStatelessDamageFirstClientHello) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ auto damage_ch = std::make_shared<TlsExtensionInjector>(0xfff3, DataBuffer());
+ client_->SetPacketFilter(damage_ch);
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ // Key exchange fails when the handshake continues because client and server
+ // disagree about the transcript.
+ client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+}
+
+TEST_F(TlsConnectStreamTls13, RetryStatelessDamageSecondClientHello) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ auto damage_ch = std::make_shared<TlsExtensionInjector>(0xfff3, DataBuffer());
+ client_->SetPacketFilter(damage_ch);
+
+ // Key exchange fails when the handshake continues because client and server
+ // disagree about the transcript.
+ client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+}
+
+// Read the cipher suite from the HRR and disable it on the identified agent.
+static void DisableSuiteFromHrr(
+ std::shared_ptr<TlsAgent>& agent,
+ std::shared_ptr<TlsInspectorRecordHandshakeMessage>& capture_hrr) {
+ uint32_t tmp;
+ size_t offset = 2 + 32; // skip version + server_random
+ ASSERT_TRUE(
+ capture_hrr->buffer().Read(offset, 1, &tmp)); // session_id length
+ EXPECT_EQ(0U, tmp);
+ offset += 1 + tmp;
+ ASSERT_TRUE(capture_hrr->buffer().Read(offset, 2, &tmp)); // suite
+ EXPECT_EQ(
+ SECSuccess,
+ SSL_CipherPrefSet(agent->ssl_fd(), static_cast<uint16_t>(tmp), PR_FALSE));
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableSuiteClient) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ auto capture_hrr = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+ ssl_hs_hello_retry_request);
+ server_->SetPacketFilter(capture_hrr);
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ DisableSuiteFromHrr(client_, capture_hrr);
+
+ // The client thinks that the HelloRetryRequest is bad, even though its
+ // because it changed its mind about the cipher suite.
+ ExpectAlert(client_, kTlsAlertIllegalParameter);
+ Handshake();
+ client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+ server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableSuiteServer) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ auto capture_hrr = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+ ssl_hs_hello_retry_request);
+ server_->SetPacketFilter(capture_hrr);
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ DisableSuiteFromHrr(server_, capture_hrr);
+
+ ExpectAlert(server_, kTlsAlertIllegalParameter);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableGroupClient) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ client_->ConfigNamedGroups(groups);
+
+ // We're into undefined behavior on the client side, but - at the point this
+ // test was written - the client here doesn't amend its key shares because the
+ // server doesn't ask it to. The server notices that the key share (x25519)
+ // doesn't match the negotiated group (P-384) and objects.
+ ExpectAlert(server_, kTlsAlertIllegalParameter);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableGroupServer) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ TriggerHelloRetryRequest(client_, server_);
+ MakeNewServer();
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ server_->ConfigNamedGroups(groups);
+
+ ExpectAlert(server_, kTlsAlertIllegalParameter);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessBadCookie) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+
+ TriggerHelloRetryRequest(client_, server_);
+
+ // Now replace the self-encrypt MAC key with a garbage key.
+ static const uint8_t bad_hmac_key[32] = {0};
+ SECItem key_item = {siBuffer, const_cast<uint8_t*>(bad_hmac_key),
+ sizeof(bad_hmac_key)};
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ PK11SymKey* hmac_key =
+ PK11_ImportSymKey(slot.get(), CKM_SHA256_HMAC, PK11_OriginUnwrap,
+ CKA_SIGN, &key_item, nullptr);
+ ASSERT_NE(nullptr, hmac_key);
+ SSLInt_SetSelfEncryptMacKey(hmac_key); // Passes ownership.
+
+ MakeNewServer();
+
+ ExpectAlert(server_, kTlsAlertIllegalParameter);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+// Stream because the server doesn't consume the alert and terminate.
+TEST_F(TlsConnectStreamTls13, RetryWithDifferentCipherSuite) {
+ EnsureTlsSetup();
+ // Force a HelloRetryRequest.
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ server_->ConfigNamedGroups(groups);
+ // Then switch out the default suite (TLS_AES_128_GCM_SHA256).
+ server_->SetPacketFilter(std::make_shared<SelectedCipherSuiteReplacer>(
+ TLS_CHACHA20_POLY1305_SHA256));
+
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ ConnectExpectFail();
+ EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
+ EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
+}
+
// This tests that the second attempt at sending a ClientHello (after receiving
// a HelloRetryRequest) is correctly retransmitted.
TEST_F(TlsConnectDatagram13, DropClientSecondFlightWithHelloRetry) {
@@ -233,6 +817,54 @@ TEST_P(TlsKeyExchange13, ConnectEcdhePreferenceMismatchHrrExtraShares) {
CheckKEXDetails(client_groups, client_groups);
}
+// The callback should be run even if we have another reason to send
+// HelloRetryRequest. In this case, the server sends HRR because the server
+// wants an X25519 key share and the client didn't offer one.
+TEST_P(TlsKeyExchange13,
+ RetryCallbackRetryWithGroupMismatchAndAdditionalShares) {
+ EnsureKeyShareSetup();
+
+ static const std::vector<SSLNamedGroup> client_groups = {
+ ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
+ client_->ConfigNamedGroups(client_groups);
+ static const std::vector<SSLNamedGroup> server_groups = {
+ ssl_grp_ec_curve25519};
+ server_->ConfigNamedGroups(server_groups);
+ EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+
+ auto capture_server =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+ capture_server->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+ server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+ ChainedPacketFilterInit{capture_hrr_, capture_server}));
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ RetryHello, &cb_called));
+
+ // Do the first message exchange.
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+
+ EXPECT_EQ(1U, cb_called) << "callback should be called once here";
+ EXPECT_TRUE(capture_server->captured()) << "key_share extension expected";
+
+ uint32_t server_group = 0;
+ EXPECT_TRUE(capture_server->extension().Read(0, 2, &server_group));
+ EXPECT_EQ(ssl_grp_ec_curve25519, static_cast<SSLNamedGroup>(server_group));
+
+ Handshake();
+ CheckConnected();
+ EXPECT_EQ(2U, cb_called);
+ EXPECT_TRUE(shares_capture2_->captured()) << "client should send shares";
+
+ CheckKeys();
+ static const std::vector<SSLNamedGroup> client_shares(
+ client_groups.begin(), client_groups.begin() + 2);
+ CheckKEXDetails(client_groups, client_shares, server_groups[0]);
+}
+
TEST_F(TlsConnectTest, Select12AfterHelloRetryRequest) {
EnsureTlsSetup();
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
@@ -245,8 +877,7 @@ TEST_F(TlsConnectTest, Select12AfterHelloRetryRequest) {
static const std::vector<SSLNamedGroup> server_groups = {
ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
server_->ConfigNamedGroups(server_groups);
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
client_->Handshake();
server_->Handshake();
@@ -276,15 +907,30 @@ class HelloRetryRequestAgentTest : public TlsAgentTestClient {
void MakeCannedHrr(const uint8_t* body, size_t len, DataBuffer* hrr_record,
uint32_t seq_num = 0) const {
DataBuffer hrr_data;
- hrr_data.Allocate(len + 4);
+ const uint8_t ssl_hello_retry_random[] = {
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C,
+ 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB,
+ 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};
+
+ hrr_data.Allocate(len + 6);
size_t i = 0;
+ i = hrr_data.Write(i, 0x0303, 2);
+ i = hrr_data.Write(i, ssl_hello_retry_random,
+ sizeof(ssl_hello_retry_random));
+ i = hrr_data.Write(i, static_cast<uint32_t>(0), 1); // session_id
+ i = hrr_data.Write(i, TLS_AES_128_GCM_SHA256, 2);
+ i = hrr_data.Write(i, ssl_compression_null, 1);
+ // Add extensions. First a length, which includes the supported version.
+ i = hrr_data.Write(i, static_cast<uint32_t>(len) + 6, 2);
+ // Now the supported version.
+ i = hrr_data.Write(i, ssl_tls13_supported_versions_xtn, 2);
+ i = hrr_data.Write(i, 2, 2);
i = hrr_data.Write(i, 0x7f00 | TLS_1_3_DRAFT_VERSION, 2);
- i = hrr_data.Write(i, static_cast<uint32_t>(len), 2);
if (len) {
hrr_data.Write(i, body, len);
}
DataBuffer hrr;
- MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, hrr_data.data(),
+ MakeHandshakeMessage(kTlsHandshakeServerHello, hrr_data.data(),
hrr_data.len(), &hrr, seq_num);
MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3, hrr.data(),
hrr.len(), hrr_record, seq_num);
@@ -334,28 +980,6 @@ TEST_P(HelloRetryRequestAgentTest, HandleNoopHelloRetryRequest) {
SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
}
-TEST_P(HelloRetryRequestAgentTest, HandleHelloRetryRequestCookie) {
- const uint8_t canned_cookie_hrr[] = {
- static_cast<uint8_t>(ssl_tls13_cookie_xtn >> 8),
- static_cast<uint8_t>(ssl_tls13_cookie_xtn),
- 0,
- 5, // length of cookie extension
- 0,
- 3, // cookie value length
- 0xc0,
- 0x0c,
- 0x13};
- DataBuffer hrr;
- MakeCannedHrr(canned_cookie_hrr, sizeof(canned_cookie_hrr), &hrr);
- auto capture = std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
- agent_->SetPacketFilter(capture);
- ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
- const size_t cookie_pos = 2 + 2; // cookie_xtn, extension len
- DataBuffer cookie(canned_cookie_hrr + cookie_pos,
- sizeof(canned_cookie_hrr) - cookie_pos);
- EXPECT_EQ(cookie, capture->extension());
-}
-
INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
diff --git a/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc
new file mode 100644
index 000000000..8ed342305
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef NSS_ALLOW_SSLKEYLOGFILE
+
+#include <cstdlib>
+#include <fstream>
+#include <sstream>
+
+#include "gtest_utils.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+static const std::string keylog_file_path = "keylog.txt";
+static const std::string keylog_env = "SSLKEYLOGFILE=" + keylog_file_path;
+
+class KeyLogFileTest : public TlsConnectGeneric {
+ public:
+ void SetUp() {
+ TlsConnectTestBase::SetUp();
+ // Remove previous results (if any).
+ (void)remove(keylog_file_path.c_str());
+ PR_SetEnv(keylog_env.c_str());
+ }
+
+ void CheckKeyLog() {
+ std::ifstream f(keylog_file_path);
+ std::map<std::string, size_t> labels;
+ std::set<std::string> client_randoms;
+ for (std::string line; std::getline(f, line);) {
+ if (line[0] == '#') {
+ continue;
+ }
+
+ std::istringstream iss(line);
+ std::string label, client_random, secret;
+ iss >> label >> client_random >> secret;
+
+ ASSERT_EQ(64U, client_random.size());
+ client_randoms.insert(client_random);
+ labels[label]++;
+ }
+
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
+ ASSERT_EQ(1U, client_randoms.size());
+ } else {
+ /* two handshakes for 0-RTT */
+ ASSERT_EQ(2U, client_randoms.size());
+ }
+
+ // Every entry occurs twice (one log from server, one from client).
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
+ ASSERT_EQ(2U, labels["CLIENT_RANDOM"]);
+ } else {
+ ASSERT_EQ(2U, labels["CLIENT_EARLY_TRAFFIC_SECRET"]);
+ ASSERT_EQ(2U, labels["EARLY_EXPORTER_SECRET"]);
+ ASSERT_EQ(4U, labels["CLIENT_HANDSHAKE_TRAFFIC_SECRET"]);
+ ASSERT_EQ(4U, labels["SERVER_HANDSHAKE_TRAFFIC_SECRET"]);
+ ASSERT_EQ(4U, labels["CLIENT_TRAFFIC_SECRET_0"]);
+ ASSERT_EQ(4U, labels["SERVER_TRAFFIC_SECRET_0"]);
+ ASSERT_EQ(4U, labels["EXPORTER_SECRET"]);
+ }
+ }
+
+ void ConnectAndCheck() {
+ // This is a child process, ensure that error messages immediately
+ // propagate or else it will not be visible.
+ ::testing::GTEST_FLAG(throw_on_failure) = true;
+
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+ } else {
+ Connect();
+ }
+ CheckKeyLog();
+ _exit(0);
+ }
+};
+
+// Tests are run in a separate process to ensure that NSS is not initialized yet
+// and can process the SSLKEYLOGFILE environment variable.
+
+TEST_P(KeyLogFileTest, KeyLogFile) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+
+ ASSERT_EXIT(ConnectAndCheck(), ::testing::ExitedWithCode(0), "");
+}
+
+INSTANTIATE_TEST_CASE_P(
+ KeyLogFileDTLS12, KeyLogFileTest,
+ ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
+ TlsConnectTestBase::kTlsV11V12));
+INSTANTIATE_TEST_CASE_P(
+ KeyLogFileTLS12, KeyLogFileTest,
+ ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
+ TlsConnectTestBase::kTlsV10ToV12));
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_CASE_P(
+ KeyLogFileTLS13, KeyLogFileTest,
+ ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
+ TlsConnectTestBase::kTlsV13));
+#endif
+
+} // namespace nss_test
+
+#endif // NSS_ALLOW_SSLKEYLOGFILE
diff --git a/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc
new file mode 100644
index 000000000..d03775c25
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "secerr.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+extern "C" {
+// This is not something that should make you happy.
+#include "libssl_internals.h"
+}
+
+#include "gtest_utils.h"
+#include "scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+// All stream only tests; DTLS isn't supported yet.
+
+TEST_F(TlsConnectTest, KeyUpdateClient) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ SendReceive(50);
+ SendReceive(60);
+ CheckEpochs(4, 3);
+}
+
+TEST_F(TlsConnectTest, KeyUpdateClientRequestUpdate) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
+ // SendReceive() only gives each peer one chance to read. This isn't enough
+ // when the read on one side generates another handshake message. A second
+ // read gives each peer an extra chance to consume the KeyUpdate.
+ SendReceive(50);
+ SendReceive(60); // Cumulative count.
+ CheckEpochs(4, 4);
+}
+
+TEST_F(TlsConnectTest, KeyUpdateServer) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+ SendReceive(50);
+ SendReceive(60);
+ CheckEpochs(3, 4);
+}
+
+TEST_F(TlsConnectTest, KeyUpdateServerRequestUpdate) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ SendReceive(50);
+ SendReceive(60);
+ CheckEpochs(4, 4);
+}
+
+TEST_F(TlsConnectTest, KeyUpdateConsecutiveRequests) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ SendReceive(50);
+ SendReceive(60);
+ // The server should have updated twice, but the client should have declined
+ // to respond to the second request from the server, since it doesn't send
+ // anything in between those two requests.
+ CheckEpochs(4, 5);
+}
+
+// Check that a local update can be immediately followed by a remotely triggered
+// update even if there is no use of the keys.
+TEST_F(TlsConnectTest, KeyUpdateLocalUpdateThenConsecutiveRequests) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ // This should trigger an update on the client.
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // The client should update for the first request.
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ // ...but not the second.
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ SendReceive(50);
+ SendReceive(60);
+ // Both should have updated twice.
+ CheckEpochs(5, 5);
+}
+
+TEST_F(TlsConnectTest, KeyUpdateMultiple) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ SendReceive(50);
+ SendReceive(60);
+ CheckEpochs(5, 6);
+}
+
+// Both ask the other for an update, and both should react.
+TEST_F(TlsConnectTest, KeyUpdateBothRequest) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ SendReceive(50);
+ SendReceive(60);
+ CheckEpochs(5, 5);
+}
+
+// If the sequence number exceeds the number of writes before an automatic
+// update (currently 3/4 of the max records for the cipher suite), then the
+// stack should send an update automatically (but not request one).
+TEST_F(TlsConnectTest, KeyUpdateAutomaticOnWrite) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+
+ // Set this to one below the write threshold.
+ uint64_t threshold = (0x5aULL << 28) * 3 / 4;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ // This should be OK.
+ client_->SendData(10);
+ server_->ReadBytes();
+
+ // This should cause the client to update.
+ client_->SendData(10);
+ server_->ReadBytes();
+
+ SendReceive(100);
+ CheckEpochs(4, 3);
+}
+
+// If the sequence number exceeds a certain number of reads (currently 7/8 of
+// the max records for the cipher suite), then the stack should send AND request
+// an update automatically. However, the sender (client) will be above its
+// automatic update threshold, so the KeyUpdate - that it sends with the old
+// cipher spec - will exceed the receiver (server) automatic update threshold.
+// The receiver gets a packet with a sequence number over its automatic read
+// update threshold. Even though the sender has updated, the code that checks
+// the sequence numbers at the receiver doesn't know this and it will request an
+// update. This causes two updates: one from the sender (without requesting a
+// response) and one from the receiver (which does request a response).
+TEST_F(TlsConnectTest, KeyUpdateAutomaticOnRead) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+
+ // Move to right at the read threshold. Unlike the write test, we can't send
+ // packets because that would cause the client to update, which would spoil
+ // the test.
+ uint64_t threshold = ((0x5aULL << 28) * 7 / 8) + 1;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ // This should cause the client to update, but not early enough to prevent the
+ // server from updating also.
+ client_->SendData(10);
+ server_->ReadBytes();
+
+ // Need two SendReceive() calls to ensure that the update that the server
+ // requested is properly generated and consumed.
+ SendReceive(70);
+ SendReceive(80);
+ CheckEpochs(5, 4);
+}
+
+} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
index 77703dd8e..4bc6e60ab 100644
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -6,6 +6,7 @@
#include <functional>
#include <memory>
+#include <vector>
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
@@ -84,13 +85,13 @@ class TlsAlertRecorder : public TlsRecordFilter {
};
class HelloTruncator : public TlsHandshakeFilter {
+ public:
+ HelloTruncator()
+ : TlsHandshakeFilter(
+ {kTlsHandshakeClientHello, kTlsHandshakeServerHello}) {}
PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) override {
- if (header.handshake_type() != kTlsHandshakeClientHello &&
- header.handshake_type() != kTlsHandshakeServerHello) {
- return KEEP;
- }
output->Assign(input.data(), input.len() - 1);
return CHANGE;
}
@@ -102,9 +103,9 @@ TEST_P(TlsConnectGeneric, CaptureAlertServer) {
auto alert_recorder = std::make_shared<TlsAlertRecorder>();
server_->SetPacketFilter(alert_recorder);
- ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
+ ConnectExpectAlert(server_, kTlsAlertDecodeError);
EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
- EXPECT_EQ(kTlsAlertIllegalParameter, alert_recorder->description());
+ EXPECT_EQ(kTlsAlertDecodeError, alert_recorder->description());
}
TEST_P(TlsConnectGenericPre13, CaptureAlertClient) {
@@ -123,8 +124,7 @@ TEST_P(TlsConnectTls13, CaptureAlertClient) {
auto alert_recorder = std::make_shared<TlsAlertRecorder>();
client_->SetPacketFilter(alert_recorder);
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
client_->Handshake();
client_->ExpectSendAlert(kTlsAlertDecodeError);
@@ -166,26 +166,107 @@ TEST_P(TlsConnectDatagram, ConnectSrtp) {
SendReceive();
}
-// 1.3 is disabled in the next few tests because we don't
-// presently support resumption in 1.3.
-TEST_P(TlsConnectStreamPre13, ConnectAndClientRenegotiate) {
+TEST_P(TlsConnectGeneric, ConnectSendReceive) {
Connect();
- server_->PrepareForRenegotiate();
- client_->StartRenegotiate();
- Handshake();
- CheckConnected();
+ SendReceive();
+}
+
+class SaveTlsRecord : public TlsRecordFilter {
+ public:
+ SaveTlsRecord(size_t index) : index_(index), count_(0), contents_() {}
+
+ const DataBuffer& contents() const { return contents_; }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& data,
+ DataBuffer* changed) override {
+ if (count_++ == index_) {
+ contents_ = data;
+ }
+ return KEEP;
+ }
+
+ private:
+ const size_t index_;
+ size_t count_;
+ DataBuffer contents_;
+};
+
+// Check that decrypting filters work and can read any record.
+// This test (currently) only works in TLS 1.3 where we can decrypt.
+TEST_F(TlsConnectStreamTls13, DecryptRecordClient) {
+ EnsureTlsSetup();
+ // 0 = ClientHello, 1 = Finished, 2 = SendReceive, 3 = SendBuffer
+ auto saved = std::make_shared<SaveTlsRecord>(3);
+ client_->SetTlsRecordFilter(saved);
+ Connect();
+ SendReceive();
+
+ static const uint8_t data[] = {0xde, 0xad, 0xdc};
+ DataBuffer buf(data, sizeof(data));
+ client_->SendBuffer(buf);
+ EXPECT_EQ(buf, saved->contents());
}
-TEST_P(TlsConnectStreamPre13, ConnectAndServerRenegotiate) {
+TEST_F(TlsConnectStreamTls13, DecryptRecordServer) {
+ EnsureTlsSetup();
+ // Disable tickets so that we are sure to not get NewSessionTicket.
+ EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
+ SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
+ // 0 = ServerHello, 1 = other handshake, 2 = SendReceive, 3 = SendBuffer
+ auto saved = std::make_shared<SaveTlsRecord>(3);
+ server_->SetTlsRecordFilter(saved);
Connect();
- client_->PrepareForRenegotiate();
- server_->StartRenegotiate();
- Handshake();
- CheckConnected();
+ SendReceive();
+
+ static const uint8_t data[] = {0xde, 0xad, 0xd5};
+ DataBuffer buf(data, sizeof(data));
+ server_->SendBuffer(buf);
+ EXPECT_EQ(buf, saved->contents());
}
-TEST_P(TlsConnectGeneric, ConnectSendReceive) {
+class DropTlsRecord : public TlsRecordFilter {
+ public:
+ DropTlsRecord(size_t index) : index_(index), count_(0) {}
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& data,
+ DataBuffer* changed) override {
+ if (count_++ == index_) {
+ return DROP;
+ }
+ return KEEP;
+ }
+
+ private:
+ const size_t index_;
+ size_t count_;
+};
+
+// Test that decrypting filters work correctly and are able to drop records.
+TEST_F(TlsConnectStreamTls13, DropRecordServer) {
+ EnsureTlsSetup();
+ // Disable session tickets so that the server doesn't send an extra record.
+ EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
+ SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
+
+ // 0 = ServerHello, 1 = other handshake, 2 = first write
+ server_->SetTlsRecordFilter(std::make_shared<DropTlsRecord>(2));
+ Connect();
+ server_->SendData(23, 23); // This should be dropped, so it won't be counted.
+ server_->ResetSentBytes();
+ SendReceive();
+}
+
+TEST_F(TlsConnectStreamTls13, DropRecordClient) {
+ EnsureTlsSetup();
+ // 0 = ClientHello, 1 = Finished, 2 = first write
+ client_->SetTlsRecordFilter(std::make_shared<DropTlsRecord>(2));
Connect();
+ client_->SendData(26, 26); // This should be dropped, so it won't be counted.
+ client_->ResetSentBytes();
SendReceive();
}
@@ -224,29 +305,70 @@ TEST_P(TlsConnectStream, ShortRead) {
ASSERT_EQ(50U, client_->received_bytes());
}
-TEST_P(TlsConnectGeneric, ConnectWithCompressionMaybe) {
+// We enable compression via the API but it's disabled internally,
+// so we should never get it.
+TEST_P(TlsConnectGeneric, ConnectWithCompressionEnabled) {
EnsureTlsSetup();
- client_->EnableCompression();
- server_->EnableCompression();
+ client_->SetOption(SSL_ENABLE_DEFLATE, PR_TRUE);
+ server_->SetOption(SSL_ENABLE_DEFLATE, PR_TRUE);
Connect();
- EXPECT_EQ(client_->version() < SSL_LIBRARY_VERSION_TLS_1_3 &&
- variant_ != ssl_variant_datagram,
- client_->is_compressed());
+ EXPECT_FALSE(client_->is_compressed());
SendReceive();
}
-TEST_P(TlsConnectDatagram, TestDtlsHolddownExpiry) {
+class TlsHolddownTest : public TlsConnectDatagram {
+ protected:
+ // This causes all timers to run to completion. It advances the clock and
+ // handshakes on both peers until both peers have no more timers pending,
+ // which should happen at the end of a handshake. This is necessary to ensure
+ // that the relatively long holddown timer expires, but that any other timers
+ // also expire and run correctly.
+ void RunAllTimersDown() {
+ while (true) {
+ PRIntervalTime time;
+ SECStatus rv = DTLS_GetHandshakeTimeout(client_->ssl_fd(), &time);
+ if (rv != SECSuccess) {
+ rv = DTLS_GetHandshakeTimeout(server_->ssl_fd(), &time);
+ if (rv != SECSuccess) {
+ break; // Neither peer has an outstanding timer.
+ }
+ }
+
+ if (g_ssl_gtest_verbose) {
+ std::cerr << "Shifting timers" << std::endl;
+ }
+ ShiftDtlsTimers();
+ Handshake();
+ }
+ }
+};
+
+TEST_P(TlsHolddownTest, TestDtlsHolddownExpiry) {
Connect();
- std::cerr << "Expiring holddown timer\n";
- SSLInt_ForceTimerExpiry(client_->ssl_fd());
- SSLInt_ForceTimerExpiry(server_->ssl_fd());
+ std::cerr << "Expiring holddown timer" << std::endl;
+ RunAllTimersDown();
SendReceive();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
// One for send, one for receive.
- EXPECT_EQ(2, SSLInt_CountTls13CipherSpecs(client_->ssl_fd()));
+ EXPECT_EQ(2, SSLInt_CountCipherSpecs(client_->ssl_fd()));
}
}
+TEST_P(TlsHolddownTest, TestDtlsHolddownExpiryResumption) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ RunAllTimersDown();
+ SendReceive();
+ // One for send, one for receive.
+ EXPECT_EQ(2, SSLInt_CountCipherSpecs(client_->ssl_fd()));
+}
+
class TlsPreCCSHeaderInjector : public TlsRecordFilter {
public:
TlsPreCCSHeaderInjector() {}
@@ -274,8 +396,7 @@ TEST_P(TlsConnectStreamPre13, ClientFinishedHeaderBeforeCCS) {
TEST_P(TlsConnectStreamPre13, ServerFinishedHeaderBeforeCCS) {
server_->SetPacketFilter(std::make_shared<TlsPreCCSHeaderInjector>());
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
ExpectAlert(client_, kTlsAlertUnexpectedMessage);
Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
@@ -306,21 +427,65 @@ TEST_P(TlsConnectTls13, AlertWrongLevel) {
TEST_F(TlsConnectStreamTls13, Tls13FailedWriteSecondFlight) {
EnsureTlsSetup();
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
client_->Handshake();
server_->Handshake(); // Send first flight.
- client_->adapter()->CloseWrites();
+ client_->adapter()->SetWriteError(PR_IO_ERROR);
client_->Handshake(); // This will get an error, but shouldn't crash.
client_->CheckErrorCode(SSL_ERROR_SOCKET_WRITE_FAILURE);
}
-TEST_F(TlsConnectStreamTls13, NegotiateShortHeaders) {
- client_->SetShortHeadersEnabled();
- server_->SetShortHeadersEnabled();
- client_->ExpectShortHeaders();
- server_->ExpectShortHeaders();
+TEST_P(TlsConnectDatagram, BlockedWrite) {
+ Connect();
+
+ // Mark the socket as blocked.
+ client_->adapter()->SetWriteError(PR_WOULD_BLOCK_ERROR);
+ static const uint8_t data[] = {1, 2, 3};
+ int32_t rv = PR_Write(client_->ssl_fd(), data, sizeof(data));
+ EXPECT_GT(0, rv);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+ // Remove the write error and though the previous write failed, future reads
+ // and writes should just work as if it never happened.
+ client_->adapter()->SetWriteError(0);
+ SendReceive();
+}
+
+TEST_F(TlsConnectTest, ConnectSSLv3) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_3_0);
+ EnableOnlyStaticRsaCiphers();
Connect();
+ CheckKeys(ssl_kea_rsa, ssl_grp_none, ssl_auth_rsa_decrypt, ssl_sig_none);
+}
+
+TEST_F(TlsConnectTest, ConnectSSLv3ClientAuth) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_3_0);
+ EnableOnlyStaticRsaCiphers();
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+ Connect();
+ CheckKeys(ssl_kea_rsa, ssl_grp_none, ssl_auth_rsa_decrypt, ssl_sig_none);
+}
+
+static size_t ExpectedCbcLen(size_t in, size_t hmac = 20, size_t block = 16) {
+ // MAC-then-Encrypt expansion formula:
+ return ((in + hmac + (block - 1)) / block) * block;
+}
+
+TEST_F(TlsConnectTest, OneNRecordSplitting) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_0);
+ EnsureTlsSetup();
+ ConnectWithCipherSuite(TLS_RSA_WITH_AES_128_CBC_SHA);
+ auto records = std::make_shared<TlsRecordRecorder>();
+ server_->SetPacketFilter(records);
+ // This should be split into 1, 16384 and 20.
+ DataBuffer big_buffer;
+ big_buffer.Allocate(1 + 16384 + 20);
+ server_->SendBuffer(big_buffer);
+ ASSERT_EQ(3U, records->count());
+ EXPECT_EQ(ExpectedCbcLen(1), records->record(0).buffer.len());
+ EXPECT_EQ(ExpectedCbcLen(16384), records->record(1).buffer.len());
+ EXPECT_EQ(ExpectedCbcLen(20), records->record(2).buffer.len());
}
INSTANTIATE_TEST_CASE_P(
@@ -336,6 +501,8 @@ INSTANTIATE_TEST_CASE_P(StreamOnly, TlsConnectStream,
TlsConnectTestBase::kTlsVAll);
INSTANTIATE_TEST_CASE_P(DatagramOnly, TlsConnectDatagram,
TlsConnectTestBase::kTlsV11Plus);
+INSTANTIATE_TEST_CASE_P(DatagramHolddown, TlsHolddownTest,
+ TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_CASE_P(
Pre12Stream, TlsConnectPre12,
diff --git a/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc
new file mode 100644
index 000000000..2b1b92dcd
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sslexp.h"
+
+#include "gtest_utils.h"
+
+namespace nss_test {
+
+class MiscTest : public ::testing::Test {};
+
+TEST_F(MiscTest, NonExistentExperimentalAPI) {
+ EXPECT_EQ(nullptr, SSL_GetExperimentalAPI("blah"));
+ EXPECT_EQ(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, PORT_GetError());
+}
+
+} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc
index ef81b222c..d1d496f49 100644
--- a/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc
@@ -10,6 +10,8 @@
#include "databuffer.h"
#include "gtest_utils.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
namespace nss_test {
@@ -51,8 +53,8 @@ class TlsPaddingTest
<< " total length=" << plaintext_.len() << std::endl;
std::cerr << "Plaintext: " << plaintext_ << std::endl;
sslBuffer s;
- s.buf = const_cast<unsigned char *>(
- static_cast<const unsigned char *>(plaintext_.data()));
+ s.buf = const_cast<unsigned char*>(
+ static_cast<const unsigned char*>(plaintext_.data()));
s.len = plaintext_.len();
SECStatus rv = ssl_RemoveTLSCBCPadding(&s, kMacSize);
if (expect_success) {
@@ -99,6 +101,73 @@ TEST_P(TlsPaddingTest, LastByteOfPadWrong) {
}
}
+class RecordReplacer : public TlsRecordFilter {
+ public:
+ RecordReplacer(size_t size)
+ : TlsRecordFilter(), enabled_(false), size_(size) {}
+
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& data,
+ DataBuffer* changed) override {
+ if (!enabled_) {
+ return KEEP;
+ }
+
+ EXPECT_EQ(kTlsApplicationDataType, header.content_type());
+ changed->Allocate(size_);
+
+ for (size_t i = 0; i < size_; ++i) {
+ changed->data()[i] = i & 0xff;
+ }
+
+ enabled_ = false;
+ return CHANGE;
+ }
+
+ void Enable() { enabled_ = true; }
+
+ private:
+ bool enabled_;
+ size_t size_;
+};
+
+TEST_F(TlsConnectStreamTls13, LargeRecord) {
+ EnsureTlsSetup();
+
+ const size_t record_limit = 16384;
+ auto replacer = std::make_shared<RecordReplacer>(record_limit);
+ client_->SetTlsRecordFilter(replacer);
+ Connect();
+
+ replacer->Enable();
+ client_->SendData(10);
+ WAIT_(server_->received_bytes() == record_limit, 2000);
+ ASSERT_EQ(record_limit, server_->received_bytes());
+}
+
+TEST_F(TlsConnectStreamTls13, TooLargeRecord) {
+ EnsureTlsSetup();
+
+ const size_t record_limit = 16384;
+ auto replacer = std::make_shared<RecordReplacer>(record_limit + 1);
+ client_->SetTlsRecordFilter(replacer);
+ Connect();
+
+ replacer->Enable();
+ ExpectAlert(server_, kTlsAlertRecordOverflow);
+ client_->SendData(10); // This is expanded.
+
+ uint8_t buf[record_limit + 2];
+ PRInt32 rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+ EXPECT_GT(0, rv);
+ EXPECT_EQ(SSL_ERROR_RX_RECORD_TOO_LONG, PORT_GetError());
+
+ // Read the server alert.
+ rv = PR_Read(client_->ssl_fd(), buf, sizeof(buf));
+ EXPECT_GT(0, rv);
+ EXPECT_EQ(SSL_ERROR_RECORD_OVERFLOW_ALERT, PORT_GetError());
+}
+
const static size_t kContentSizesArr[] = {
1, kMacSize - 1, kMacSize, 30, 31, 32, 36, 256, 257, 287, 288};
diff --git a/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc
new file mode 100644
index 000000000..a902a5f7f
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <functional>
+#include <memory>
+#include "secerr.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+// 1.3 is disabled in the next few tests because we don't
+// presently support resumption in 1.3.
+TEST_P(TlsConnectStreamPre13, RenegotiateClient) {
+ Connect();
+ server_->PrepareForRenegotiate();
+ client_->StartRenegotiate();
+ Handshake();
+ CheckConnected();
+}
+
+TEST_P(TlsConnectStreamPre13, RenegotiateServer) {
+ Connect();
+ client_->PrepareForRenegotiate();
+ server_->StartRenegotiate();
+ Handshake();
+ CheckConnected();
+}
+
+// The renegotiation options shouldn't cause an error if TLS 1.3 is chosen.
+TEST_F(TlsConnectTest, RenegotiationConfigTls13) {
+ EnsureTlsSetup();
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->SetOption(SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED);
+ server_->SetOption(SSL_REQUIRE_SAFE_NEGOTIATION, PR_TRUE);
+ Connect();
+ SendReceive();
+ CheckKeys();
+}
+
+TEST_P(TlsConnectStream, ConnectTls10AndServerRenegotiateHigher) {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+ return;
+ }
+ // Set the client so it will accept any version from 1.0
+ // to |version_|.
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0);
+ // Reset version so that the checks succeed.
+ uint16_t test_version = version_;
+ version_ = SSL_LIBRARY_VERSION_TLS_1_0;
+ Connect();
+
+ // Now renegotiate, with the server being set to do
+ // |version_|.
+ client_->PrepareForRenegotiate();
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
+ // Reset version and cipher suite so that the preinfo callback
+ // doesn't fail.
+ server_->ResetPreliminaryInfo();
+ server_->StartRenegotiate();
+
+ if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ } else {
+ ExpectAlert(server_, kTlsAlertProtocolVersion);
+ }
+
+ Handshake();
+ if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ // In TLS 1.3, the server detects this problem.
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
+ } else {
+ client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+ }
+}
+
+TEST_P(TlsConnectStream, ConnectTls10AndClientRenegotiateHigher) {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+ return;
+ }
+ // Set the client so it will accept any version from 1.0
+ // to |version_|.
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0);
+ // Reset version so that the checks succeed.
+ uint16_t test_version = version_;
+ version_ = SSL_LIBRARY_VERSION_TLS_1_0;
+ Connect();
+
+ // Now renegotiate, with the server being set to do
+ // |version_|.
+ server_->PrepareForRenegotiate();
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
+ // Reset version and cipher suite so that the preinfo callback
+ // doesn't fail.
+ server_->ResetPreliminaryInfo();
+ client_->StartRenegotiate();
+ if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ } else {
+ ExpectAlert(server_, kTlsAlertProtocolVersion);
+ }
+ Handshake();
+ if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ // In TLS 1.3, the server detects this problem.
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
+ } else {
+ client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+ }
+}
+
+TEST_P(TlsConnectStream, ConnectAndServerRenegotiateLower) {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+ return;
+ }
+ Connect();
+
+ // Now renegotiate with the server set to TLS 1.0.
+ client_->PrepareForRenegotiate();
+ server_->PrepareForRenegotiate();
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
+ // Reset version and cipher suite so that the preinfo callback
+ // doesn't fail.
+ server_->ResetPreliminaryInfo();
+
+ SECStatus rv = SSL_ReHandshake(server_->ssl_fd(), PR_TRUE);
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ EXPECT_EQ(SECFailure, rv);
+ return;
+ }
+ ASSERT_EQ(SECSuccess, rv);
+
+ // Now, before handshaking, tweak the server configuration.
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0);
+
+ // The server should catch the own error.
+ ExpectAlert(server_, kTlsAlertProtocolVersion);
+
+ Handshake();
+ client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+}
+
+TEST_P(TlsConnectStream, ConnectAndServerWontRenegotiateLower) {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+ return;
+ }
+ Connect();
+
+ // Now renegotiate with the server set to TLS 1.0.
+ client_->PrepareForRenegotiate();
+ server_->PrepareForRenegotiate();
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0);
+ // Reset version and cipher suite so that the preinfo callback
+ // doesn't fail.
+ server_->ResetPreliminaryInfo();
+
+ EXPECT_EQ(SECFailure, SSL_ReHandshake(server_->ssl_fd(), PR_TRUE));
+}
+
+TEST_P(TlsConnectStream, ConnectAndClientWontRenegotiateLower) {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+ return;
+ }
+ Connect();
+
+ // Now renegotiate with the client set to TLS 1.0.
+ client_->PrepareForRenegotiate();
+ server_->PrepareForRenegotiate();
+ server_->ResetPreliminaryInfo();
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0);
+ // The client will refuse to renegotiate down.
+ EXPECT_EQ(SECFailure, SSL_ReHandshake(client_->ssl_fd(), PR_TRUE));
+}
+
+TEST_F(TlsConnectTest, Tls13RejectsRehandshakeClient) {
+ EnsureTlsSetup();
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ SECStatus rv = SSL_ReHandshake(client_->ssl_fd(), PR_TRUE);
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, PORT_GetError());
+}
+
+TEST_F(TlsConnectTest, Tls13RejectsRehandshakeServer) {
+ EnsureTlsSetup();
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ SECStatus rv = SSL_ReHandshake(server_->ssl_fd(), PR_TRUE);
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, PORT_GetError());
+}
+
+} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
index ce0e3ca8d..a413caf2c 100644
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -9,6 +9,7 @@
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
+#include "sslexp.h"
#include "sslproto.h"
extern "C" {
@@ -246,8 +247,7 @@ TEST_P(TlsConnectGeneric, ConnectWithExpiredTicketAtServer) {
: ssl_session_ticket_xtn;
auto capture = std::make_shared<TlsExtensionCapture>(xtn);
client_->SetPacketFilter(capture);
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
client_->Handshake();
EXPECT_TRUE(capture->captured());
EXPECT_LT(0U, capture->extension().len());
@@ -355,10 +355,7 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) {
// This test parses the ServerKeyExchange, which isn't in 1.3
TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) {
- server_->EnsureTlsSetup();
- SECStatus rv =
- SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
+ server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
auto i1 = std::make_shared<TlsInspectorRecordHandshakeMessage>(
kTlsHandshakeServerKeyExchange);
server_->SetPacketFilter(i1);
@@ -369,9 +366,7 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) {
// Restart
Reset();
- server_->EnsureTlsSetup();
- rv = SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
+ server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
auto i2 = std::make_shared<TlsInspectorRecordHandshakeMessage>(
kTlsHandshakeServerKeyExchange);
server_->SetPacketFilter(i2);
@@ -401,7 +396,8 @@ TEST_P(TlsConnectTls13, TestTls13ResumeDifferentGroup) {
client_->ConfigNamedGroups(kFFDHEGroups);
server_->ConfigNamedGroups(kFFDHEGroups);
Connect();
- CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none);
+ CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign,
+ ssl_sig_rsa_pss_sha256);
}
// We need to enable different cipher suites at different times in the following
@@ -461,36 +457,6 @@ TEST_P(TlsConnectGeneric, TestResumeServerDifferentCipher) {
CheckKeys();
}
-class SelectedCipherSuiteReplacer : public TlsHandshakeFilter {
- public:
- SelectedCipherSuiteReplacer(uint16_t suite) : cipher_suite_(suite) {}
-
- protected:
- PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
- const DataBuffer& input,
- DataBuffer* output) override {
- if (header.handshake_type() != kTlsHandshakeServerHello) {
- return KEEP;
- }
-
- *output = input;
- uint32_t temp = 0;
- EXPECT_TRUE(input.Read(0, 2, &temp));
- // Cipher suite is after version(2) and random(32).
- size_t pos = 34;
- if (temp < SSL_LIBRARY_VERSION_TLS_1_3) {
- // In old versions, we have to skip a session_id too.
- EXPECT_TRUE(input.Read(pos, 1, &temp));
- pos += 1 + temp;
- }
- output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2);
- return CHANGE;
- }
-
- private:
- uint16_t cipher_suite_;
-};
-
// Test that the client doesn't tolerate the server picking a different cipher
// suite for resumption.
TEST_P(TlsConnectStream, TestResumptionOverrideCipher) {
@@ -524,16 +490,13 @@ TEST_P(TlsConnectStream, TestResumptionOverrideCipher) {
class SelectedVersionReplacer : public TlsHandshakeFilter {
public:
- SelectedVersionReplacer(uint16_t version) : version_(version) {}
+ SelectedVersionReplacer(uint16_t version)
+ : TlsHandshakeFilter({kTlsHandshakeServerHello}), version_(version) {}
protected:
PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) override {
- if (header.handshake_type() != kTlsHandshakeServerHello) {
- return KEEP;
- }
-
*output = input;
output->Write(0, static_cast<uint32_t>(version_), 2);
return CHANGE;
@@ -609,7 +572,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
- ssl_sig_none);
+ ssl_sig_rsa_pss_sha256);
// The filter will go away when we reset, so save the captured extension.
DataBuffer initialTicket(c1->extension());
ASSERT_LT(0U, initialTicket.len());
@@ -627,7 +590,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
- ssl_sig_none);
+ ssl_sig_rsa_pss_sha256);
ASSERT_LT(0U, c2->extension().len());
ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd()));
@@ -652,18 +615,158 @@ TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) {
// Clear the session ticket keys to invalidate the old ticket.
SSLInt_ClearSelfEncryptKey();
- SSLInt_SendNewSessionTicket(server_->ssl_fd());
+ SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0);
+
+ SendReceive(); // Need to read so that we absorb the session tickets.
+ CheckKeys();
+
+ // Resume the connection.
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+}
+
+// Check that the value captured in a NewSessionTicket message matches the value
+// captured from a pre_shared_key extension.
+void NstTicketMatchesPskIdentity(const DataBuffer& nst, const DataBuffer& psk) {
+ uint32_t len;
+
+ size_t offset = 4 + 4; // Skip ticket_lifetime and ticket_age_add.
+ ASSERT_TRUE(nst.Read(offset, 1, &len));
+ offset += 1 + len; // Skip ticket_nonce.
+
+ ASSERT_TRUE(nst.Read(offset, 2, &len));
+ offset += 2; // Skip the ticket length.
+ ASSERT_LE(offset + len, nst.len());
+ DataBuffer nst_ticket(nst.data() + offset, static_cast<size_t>(len));
+
+ offset = 2; // Skip the identities length.
+ ASSERT_TRUE(psk.Read(offset, 2, &len));
+ offset += 2; // Skip the identity length.
+ ASSERT_LE(offset + len, psk.len());
+ DataBuffer psk_ticket(psk.data() + offset, static_cast<size_t>(len));
+
+ EXPECT_EQ(nst_ticket, psk_ticket);
+}
+
+TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNSTWithToken) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+
+ auto nst_capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+ ssl_hs_new_session_ticket);
+ server_->SetTlsRecordFilter(nst_capture);
+ Connect();
+
+ // Clear the session ticket keys to invalidate the old ticket.
+ SSLInt_ClearSelfEncryptKey();
+ nst_capture->Reset();
+ uint8_t token[] = {0x20, 0x20, 0xff, 0x00};
+ EXPECT_EQ(SECSuccess,
+ SSL_SendSessionTicket(server_->ssl_fd(), token, sizeof(token)));
SendReceive(); // Need to read so that we absorb the session tickets.
CheckKeys();
+ EXPECT_LT(0U, nst_capture->buffer().len());
// Resume the connection.
Reset();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
ExpectResumption(RESUME_TICKET);
+
+ auto psk_capture =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
+ client_->SetPacketFilter(psk_capture);
Connect();
SendReceive();
+
+ NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension());
+}
+
+// Disable SSL_ENABLE_SESSION_TICKETS but ensure that tickets can still be sent
+// by invoking SSL_SendSessionTicket directly (and that the ticket is usable).
+TEST_F(TlsConnectTest, SendSessionTicketWithTicketsDisabled) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+
+ EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
+ SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
+
+ auto nst_capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+ ssl_hs_new_session_ticket);
+ server_->SetTlsRecordFilter(nst_capture);
+ Connect();
+
+ EXPECT_EQ(0U, nst_capture->buffer().len()) << "expect nothing captured yet";
+
+ EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
+ EXPECT_LT(0U, nst_capture->buffer().len()) << "should capture now";
+
+ SendReceive(); // Ensure that the client reads the ticket.
+
+ // Resume the connection.
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ExpectResumption(RESUME_TICKET);
+
+ auto psk_capture =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
+ client_->SetPacketFilter(psk_capture);
+ Connect();
+ SendReceive();
+
+ NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension());
+}
+
+// Test calling SSL_SendSessionTicket in inappropriate conditions.
+TEST_F(TlsConnectTest, SendSessionTicketInappropriate) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_2);
+
+ EXPECT_EQ(SECFailure, SSL_SendSessionTicket(client_->ssl_fd(), NULL, 0))
+ << "clients can't send tickets";
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+
+ StartConnect();
+
+ EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0))
+ << "no ticket before the handshake has started";
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ Handshake();
+ EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0))
+ << "no special tickets in TLS 1.2";
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectTest, SendSessionTicketMassiveToken) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ // It should be safe to set length with a NULL token because the length should
+ // be checked before reading token.
+ EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0x1ffff))
+ << "this is clearly too big";
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+
+ static const uint8_t big_token[0xffff] = {1};
+ EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), big_token,
+ sizeof(big_token)))
+ << "this is too big, but that's not immediately obvious";
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectDatagram13, SendSessionTicketDtls) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0))
+ << "no extra tickets in DTLS until we have Ack support";
+ EXPECT_EQ(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION, PORT_GetError());
}
TEST_F(TlsConnectTest, TestTls13ResumptionDowngrade) {
@@ -719,13 +822,84 @@ TEST_F(TlsConnectTest, TestTls13ResumptionForcedDowngrade) {
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256));
filters.push_back(
std::make_shared<SelectedVersionReplacer>(SSL_LIBRARY_VERSION_TLS_1_2));
+
+ // Drop a bunch of extensions so that we get past the SH processing. The
+ // version extension says TLS 1.3, which is counter to our goal, the others
+ // are not permitted in TLS 1.2 handshakes.
+ filters.push_back(
+ std::make_shared<TlsExtensionDropper>(ssl_tls13_supported_versions_xtn));
+ filters.push_back(
+ std::make_shared<TlsExtensionDropper>(ssl_tls13_key_share_xtn));
+ filters.push_back(
+ std::make_shared<TlsExtensionDropper>(ssl_tls13_pre_shared_key_xtn));
server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(filters));
- client_->ExpectSendAlert(kTlsAlertDecodeError);
+ // The client here generates an unexpected_message alert when it receives an
+ // encrypted handshake message from the server (EncryptedExtension). The
+ // client expects to receive an unencrypted TLS 1.2 Certificate message.
+ // The server can't decrypt the alert.
+ client_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
server_->ExpectSendAlert(kTlsAlertBadRecordMac); // Server can't read
ConnectExpectFail();
- client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
}
+TEST_P(TlsConnectGeneric, ReConnectTicket) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ server_->EnableSingleCipher(ChooseOneCipher(version_));
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+ ssl_sig_rsa_pss_sha256);
+ // Resume
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ // Only the client knows this.
+ CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+ ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+}
+
+TEST_P(TlsConnectGenericPre13, ReConnectCache) {
+ ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
+ server_->EnableSingleCipher(ChooseOneCipher(version_));
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+ ssl_sig_rsa_pss_sha256);
+ // Resume
+ Reset();
+ ExpectResumption(RESUME_SESSIONID);
+ Connect();
+ CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+ ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+}
+
+TEST_P(TlsConnectGeneric, ReConnectAgainTicket) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ server_->EnableSingleCipher(ChooseOneCipher(version_));
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+ ssl_sig_rsa_pss_sha256);
+ // Resume
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ // Only the client knows this.
+ CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+ ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+ // Resume connection again
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ ExpectResumption(RESUME_TICKET, 2);
+ Connect();
+ // Only the client knows this.
+ CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+ ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+}
+
} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
index a130ef77f..335bfecfa 100644
--- a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
@@ -43,7 +43,14 @@ class TlsHandshakeSkipFilter : public TlsRecordFilter {
size_t start = parser.consumed();
TlsHandshakeFilter::HandshakeHeader header;
DataBuffer ignored;
- if (!header.Parse(&parser, record_header, &ignored)) {
+ bool complete = false;
+ if (!header.Parse(&parser, record_header, DataBuffer(), &ignored,
+ &complete)) {
+ ADD_FAILURE() << "Error parsing handshake header";
+ return KEEP;
+ }
+ if (!complete) {
+ ADD_FAILURE() << "Don't want to deal with fragmented input";
return KEEP;
}
@@ -101,26 +108,15 @@ class Tls13SkipTest : public TlsConnectTestBase,
void ServerSkipTest(std::shared_ptr<TlsRecordFilter> filter, int32_t error) {
EnsureTlsSetup();
server_->SetTlsRecordFilter(filter);
- filter->EnableDecryption();
- client_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
- if (variant_ == ssl_variant_stream) {
- server_->ExpectSendAlert(kTlsAlertBadRecordMac);
- ConnectExpectFail();
- } else {
- ConnectExpectFailOneSide(TlsAgent::CLIENT);
- }
+ ExpectAlert(client_, kTlsAlertUnexpectedMessage);
+ ConnectExpectFail();
client_->CheckErrorCode(error);
- if (variant_ == ssl_variant_stream) {
- server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
- } else {
- ASSERT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
- }
+ server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
void ClientSkipTest(std::shared_ptr<TlsRecordFilter> filter, int32_t error) {
EnsureTlsSetup();
client_->SetTlsRecordFilter(filter);
- filter->EnableDecryption();
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
ConnectExpectFailOneSide(TlsAgent::SERVER);
@@ -171,11 +167,10 @@ TEST_P(TlsSkipTest, SkipServerKeyExchangeEcdsa) {
}
TEST_P(TlsSkipTest, SkipCertAndKeyExch) {
- auto chain = std::make_shared<ChainedPacketFilter>();
- chain->Add(
- std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificate));
- chain->Add(
- std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeServerKeyExchange));
+ auto chain = std::make_shared<ChainedPacketFilter>(ChainedPacketFilterInit{
+ std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificate),
+ std::make_shared<TlsHandshakeSkipFilter>(
+ kTlsHandshakeServerKeyExchange)});
ServerSkipTest(chain);
client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
}
diff --git a/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc
index 8db1f30e1..e7fe44d92 100644
--- a/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc
@@ -71,7 +71,7 @@ TEST_P(TlsConnectGenericPre13, ConnectStaticRSABogusPMSVersionIgnore) {
EnableOnlyStaticRsaCiphers();
client_->SetPacketFilter(
std::make_shared<TlsInspectorClientHelloVersionChanger>(server_));
- server_->DisableRollbackDetection();
+ server_->SetOption(SSL_ROLLBACK_DETECTION, PR_FALSE);
Connect();
}
@@ -102,7 +102,7 @@ TEST_P(TlsConnectStreamPre13,
EnableExtendedMasterSecret();
client_->SetPacketFilter(
std::make_shared<TlsInspectorClientHelloVersionChanger>(server_));
- server_->DisableRollbackDetection();
+ server_->SetOption(SSL_ROLLBACK_DETECTION, PR_FALSE);
Connect();
}
diff --git a/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc
new file mode 100644
index 000000000..75cee52fc
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <memory>
+#include <vector>
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+class Tls13CompatTest : public TlsConnectStreamTls13 {
+ protected:
+ void EnableCompatMode() {
+ client_->SetOption(SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE);
+ }
+
+ void InstallFilters() {
+ EnsureTlsSetup();
+ client_recorders_.Install(client_);
+ server_recorders_.Install(server_);
+ }
+
+ void CheckRecordVersions() {
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0,
+ client_recorders_.records_->record(0).header.version());
+ CheckRecordsAreTls12("client", client_recorders_.records_, 1);
+ CheckRecordsAreTls12("server", server_recorders_.records_, 0);
+ }
+
+ void CheckHelloVersions() {
+ uint32_t ver;
+ ASSERT_TRUE(server_recorders_.hello_->buffer().Read(0, 2, &ver));
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, static_cast<uint16_t>(ver));
+ ASSERT_TRUE(client_recorders_.hello_->buffer().Read(0, 2, &ver));
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, static_cast<uint16_t>(ver));
+ }
+
+ void CheckForCCS(bool expected_client, bool expected_server) {
+ client_recorders_.CheckForCCS(expected_client);
+ server_recorders_.CheckForCCS(expected_server);
+ }
+
+ void CheckForRegularHandshake() {
+ CheckRecordVersions();
+ CheckHelloVersions();
+ EXPECT_EQ(0U, client_recorders_.session_id_length());
+ EXPECT_EQ(0U, server_recorders_.session_id_length());
+ CheckForCCS(false, false);
+ }
+
+ void CheckForCompatHandshake() {
+ CheckRecordVersions();
+ CheckHelloVersions();
+ EXPECT_EQ(32U, client_recorders_.session_id_length());
+ EXPECT_EQ(32U, server_recorders_.session_id_length());
+ CheckForCCS(true, true);
+ }
+
+ private:
+ struct Recorders {
+ Recorders()
+ : records_(new TlsRecordRecorder()),
+ hello_(new TlsInspectorRecordHandshakeMessage(std::set<uint8_t>(
+ {kTlsHandshakeClientHello, kTlsHandshakeServerHello}))) {}
+
+ uint8_t session_id_length() const {
+ // session_id is always after version (2) and random (32).
+ uint32_t len = 0;
+ EXPECT_TRUE(hello_->buffer().Read(2 + 32, 1, &len));
+ return static_cast<uint8_t>(len);
+ }
+
+ void CheckForCCS(bool expected) const {
+ EXPECT_LT(0U, records_->count());
+ for (size_t i = 0; i < records_->count(); ++i) {
+ // Only the second record can be a CCS.
+ bool expected_match = expected && (i == 1);
+ EXPECT_EQ(expected_match,
+ kTlsChangeCipherSpecType ==
+ records_->record(i).header.content_type());
+ }
+ }
+
+ void Install(std::shared_ptr<TlsAgent>& agent) {
+ agent->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+ ChainedPacketFilterInit({records_, hello_})));
+ }
+
+ std::shared_ptr<TlsRecordRecorder> records_;
+ std::shared_ptr<TlsInspectorRecordHandshakeMessage> hello_;
+ };
+
+ void CheckRecordsAreTls12(const std::string& agent,
+ const std::shared_ptr<TlsRecordRecorder>& records,
+ size_t start) {
+ EXPECT_LE(start, records->count());
+ for (size_t i = start; i < records->count(); ++i) {
+ EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_2,
+ records->record(i).header.version())
+ << agent << ": record " << i << " has wrong version";
+ }
+ }
+
+ Recorders client_recorders_;
+ Recorders server_recorders_;
+};
+
+TEST_F(Tls13CompatTest, Disabled) {
+ InstallFilters();
+ Connect();
+ CheckForRegularHandshake();
+}
+
+TEST_F(Tls13CompatTest, Enabled) {
+ EnableCompatMode();
+ InstallFilters();
+ Connect();
+ CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledZeroRtt) {
+ SetupForZeroRtt();
+ EnableCompatMode();
+ InstallFilters();
+
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ CheckForCCS(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+
+ CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledHrr) {
+ EnableCompatMode();
+ InstallFilters();
+
+ // Force a HelloRetryRequest. The server sends CCS immediately.
+ server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+ client_->StartConnect();
+ server_->StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ CheckForCCS(false, true);
+
+ Handshake();
+ CheckConnected();
+ CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledStatelessHrr) {
+ EnableCompatMode();
+ InstallFilters();
+
+ // Force a HelloRetryRequest
+ server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+ client_->StartConnect();
+ server_->StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ CheckForCCS(false, true);
+
+ // A new server should just work, but not send another CCS.
+ MakeNewServer();
+ InstallFilters();
+ server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+
+ Handshake();
+ CheckConnected();
+ CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledHrrZeroRtt) {
+ SetupForZeroRtt();
+ EnableCompatMode();
+ InstallFilters();
+ server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+
+ // With 0-RTT, the client sends CCS immediately. With HRR, the server sends
+ // CCS immediately too.
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false);
+ CheckForCCS(true, true);
+
+ Handshake();
+ ExpectEarlyDataAccepted(false);
+ CheckConnected();
+ CheckForCompatHandshake();
+}
+
+static const uint8_t kCannedCcs[] = {
+ kTlsChangeCipherSpecType,
+ SSL_LIBRARY_VERSION_TLS_1_2 >> 8,
+ SSL_LIBRARY_VERSION_TLS_1_2 & 0xff,
+ 0,
+ 1, // length
+ 1 // change_cipher_spec_choice
+};
+
+// A ChangeCipherSpec is ignored by a server because we have to tolerate it for
+// compatibility mode. That doesn't mean that we have to tolerate it
+// unconditionally. If we negotiate 1.3, we expect to see a cookie extension.
+TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHello13) {
+ EnsureTlsSetup();
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ // Client sends CCS before starting the handshake.
+ client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+ ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+// A ChangeCipherSpec is ignored by a server because we have to tolerate it for
+// compatibility mode. That doesn't mean that we have to tolerate it
+// unconditionally. If we negotiate 1.3, we expect to see a cookie extension.
+TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHelloTwice) {
+ EnsureTlsSetup();
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ // Client sends CCS before starting the handshake.
+ client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+ client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+ ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+// If we negotiate 1.2, we abort.
+TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHello12) {
+ EnsureTlsSetup();
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ // Client sends CCS before starting the handshake.
+ client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+ ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+TEST_F(TlsConnectDatagram13, CompatModeDtlsClient) {
+ EnsureTlsSetup();
+ client_->SetOption(SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE);
+ auto client_records = std::make_shared<TlsRecordRecorder>();
+ client_->SetPacketFilter(client_records);
+ auto server_records = std::make_shared<TlsRecordRecorder>();
+ server_->SetPacketFilter(server_records);
+ Connect();
+
+ ASSERT_EQ(2U, client_records->count()); // CH, Fin
+ EXPECT_EQ(kTlsHandshakeType, client_records->record(0).header.content_type());
+ EXPECT_EQ(kTlsApplicationDataType,
+ client_records->record(1).header.content_type());
+
+ ASSERT_EQ(6U, server_records->count()); // SH, EE, CT, CV, Fin, Ack
+ EXPECT_EQ(kTlsHandshakeType, server_records->record(0).header.content_type());
+ for (size_t i = 1; i < server_records->count(); ++i) {
+ EXPECT_EQ(kTlsApplicationDataType,
+ server_records->record(i).header.content_type());
+ }
+}
+
+class AddSessionIdFilter : public TlsHandshakeFilter {
+ public:
+ AddSessionIdFilter() : TlsHandshakeFilter({ssl_hs_client_hello}) {}
+
+ protected:
+ PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output) override {
+ uint32_t session_id_len = 0;
+ EXPECT_TRUE(input.Read(2 + 32, 1, &session_id_len));
+ EXPECT_EQ(0U, session_id_len);
+ uint8_t session_id[33] = {32}; // 32 for length, the rest zero.
+ *output = input;
+ output->Splice(session_id, sizeof(session_id), 34, 1);
+ return CHANGE;
+ }
+};
+
+// Adding a session ID to a DTLS ClientHello should not trigger compatibility
+// mode. It should be ignored instead.
+TEST_F(TlsConnectDatagram13, CompatModeDtlsServer) {
+ EnsureTlsSetup();
+ auto client_records = std::make_shared<TlsRecordRecorder>();
+ client_->SetPacketFilter(
+ std::make_shared<ChainedPacketFilter>(ChainedPacketFilterInit(
+ {client_records, std::make_shared<AddSessionIdFilter>()})));
+ auto server_hello = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+ kTlsHandshakeServerHello);
+ auto server_records = std::make_shared<TlsRecordRecorder>();
+ server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+ ChainedPacketFilterInit({server_records, server_hello})));
+ StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ // The client will consume the ServerHello, but discard everything else
+ // because it doesn't decrypt. And don't wait around for the client to ACK.
+ client_->Handshake();
+
+ ASSERT_EQ(1U, client_records->count());
+ EXPECT_EQ(kTlsHandshakeType, client_records->record(0).header.content_type());
+
+ ASSERT_EQ(5U, server_records->count()); // SH, EE, CT, CV, Fin
+ EXPECT_EQ(kTlsHandshakeType, server_records->record(0).header.content_type());
+ for (size_t i = 1; i < server_records->count(); ++i) {
+ EXPECT_EQ(kTlsApplicationDataType,
+ server_records->record(i).header.content_type());
+ }
+
+ uint32_t session_id_len = 0;
+ EXPECT_TRUE(server_hello->buffer().Read(2 + 32, 1, &session_id_len));
+ EXPECT_EQ(0U, session_id_len);
+}
+
+} // nss_test
diff --git a/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc
index 110e3e0b6..2f8ddd6fe 100644
--- a/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc
@@ -153,13 +153,6 @@ class SSLv2ClientHelloTestF : public TlsConnectTestBase {
client_->SetPacketFilter(filter_);
}
- void RequireSafeRenegotiation() {
- server_->EnsureTlsSetup();
- SECStatus rv =
- SSL_OptionSet(server_->ssl_fd(), SSL_REQUIRE_SAFE_NEGOTIATION, PR_TRUE);
- EXPECT_EQ(rv, SECSuccess);
- }
-
void SetExpectedVersion(uint16_t version) {
TlsConnectTestBase::SetExpectedVersion(version);
filter_->SetVersion(version);
@@ -319,7 +312,7 @@ TEST_P(SSLv2ClientHelloTest, BigClientRandom) {
// Connection must fail if we require safe renegotiation but the client doesn't
// include TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the list of cipher suites.
TEST_P(SSLv2ClientHelloTest, RequireSafeRenegotiation) {
- RequireSafeRenegotiation();
+ server_->SetOption(SSL_REQUIRE_SAFE_NEGOTIATION, PR_TRUE);
SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
EXPECT_EQ(SSL_ERROR_UNSAFE_NEGOTIATION, server_->error_code());
@@ -328,7 +321,7 @@ TEST_P(SSLv2ClientHelloTest, RequireSafeRenegotiation) {
// Connection must succeed when requiring safe renegotiation and the client
// includes TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the list of cipher suites.
TEST_P(SSLv2ClientHelloTest, RequireSafeRenegotiationWithSCSV) {
- RequireSafeRenegotiation();
+ server_->SetOption(SSL_REQUIRE_SAFE_NEGOTIATION, PR_TRUE);
std::vector<uint16_t> cipher_suites = {TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
TLS_EMPTY_RENEGOTIATION_INFO_SCSV};
SetAvailableCipherSuites(cipher_suites);
diff --git a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
index 379a67e35..9db293b07 100644
--- a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -128,12 +128,12 @@ TEST_F(TlsConnectTest, TestFallbackFromTls13) {
#endif
TEST_P(TlsConnectGeneric, TestFallbackSCSVVersionMatch) {
- client_->SetFallbackSCSVEnabled(true);
+ client_->SetOption(SSL_ENABLE_FALLBACK_SCSV, PR_TRUE);
Connect();
}
TEST_P(TlsConnectGenericPre13, TestFallbackSCSVVersionMismatch) {
- client_->SetFallbackSCSVEnabled(true);
+ client_->SetOption(SSL_ENABLE_FALLBACK_SCSV, PR_TRUE);
server_->SetVersionRange(version_, version_ + 1);
ConnectExpectAlert(server_, kTlsAlertInappropriateFallback);
client_->CheckErrorCode(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT);
@@ -155,107 +155,10 @@ TEST_F(TlsConnectTest, DisallowSSLv3HelloWithTLSv13Enabled) {
EXPECT_EQ(SECFailure, rv);
}
-TEST_P(TlsConnectStream, ConnectTls10AndServerRenegotiateHigher) {
- if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
- return;
- }
- // Set the client so it will accept any version from 1.0
- // to |version_|.
- client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
- server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
- SSL_LIBRARY_VERSION_TLS_1_0);
- // Reset version so that the checks succeed.
- uint16_t test_version = version_;
- version_ = SSL_LIBRARY_VERSION_TLS_1_0;
- Connect();
-
- // Now renegotiate, with the server being set to do
- // |version_|.
- client_->PrepareForRenegotiate();
- server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
- // Reset version and cipher suite so that the preinfo callback
- // doesn't fail.
- server_->ResetPreliminaryInfo();
- server_->StartRenegotiate();
-
- if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- ExpectAlert(server_, kTlsAlertUnexpectedMessage);
- } else {
- ExpectAlert(client_, kTlsAlertIllegalParameter);
- }
-
- Handshake();
- if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- // In TLS 1.3, the server detects this problem.
- client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
- server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
- } else {
- client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
- server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
- }
-}
-
-TEST_P(TlsConnectStream, ConnectTls10AndClientRenegotiateHigher) {
- if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
- return;
- }
- // Set the client so it will accept any version from 1.0
- // to |version_|.
- client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
- server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
- SSL_LIBRARY_VERSION_TLS_1_0);
- // Reset version so that the checks succeed.
- uint16_t test_version = version_;
- version_ = SSL_LIBRARY_VERSION_TLS_1_0;
- Connect();
-
- // Now renegotiate, with the server being set to do
- // |version_|.
- server_->PrepareForRenegotiate();
- server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
- // Reset version and cipher suite so that the preinfo callback
- // doesn't fail.
- server_->ResetPreliminaryInfo();
- client_->StartRenegotiate();
- if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- ExpectAlert(server_, kTlsAlertUnexpectedMessage);
- } else {
- ExpectAlert(client_, kTlsAlertIllegalParameter);
- }
- Handshake();
- if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- // In TLS 1.3, the server detects this problem.
- client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
- server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
- } else {
- client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
- server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
- }
-}
-
-TEST_F(TlsConnectTest, Tls13RejectsRehandshakeClient) {
- EnsureTlsSetup();
- ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
- Connect();
- SECStatus rv = SSL_ReHandshake(client_->ssl_fd(), PR_TRUE);
- EXPECT_EQ(SECFailure, rv);
- EXPECT_EQ(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, PORT_GetError());
-}
-
-TEST_F(TlsConnectTest, Tls13RejectsRehandshakeServer) {
- EnsureTlsSetup();
- ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
- Connect();
- SECStatus rv = SSL_ReHandshake(server_->ssl_fd(), PR_TRUE);
- EXPECT_EQ(SECFailure, rv);
- EXPECT_EQ(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, PORT_GetError());
-}
-
TEST_P(TlsConnectGeneric, AlertBeforeServerHello) {
EnsureTlsSetup();
client_->ExpectReceiveAlert(kTlsAlertUnrecognizedName, kTlsAlertWarning);
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
client_->Handshake(); // Send ClientHello.
static const uint8_t kWarningAlert[] = {kTlsAlertWarning,
kTlsAlertUnrecognizedName};
@@ -314,20 +217,20 @@ TEST_F(TlsConnectStreamTls13, Tls14ClientHelloWithSupportedVersions) {
client_->SetPacketFilter(
std::make_shared<TlsInspectorClientHelloVersionSetter>(
SSL_LIBRARY_VERSION_TLS_1_3 + 1));
- auto capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
- kTlsHandshakeServerHello);
+ auto capture =
+ std::make_shared<TlsExtensionCapture>(ssl_tls13_supported_versions_xtn);
server_->SetPacketFilter(capture);
client_->ExpectSendAlert(kTlsAlertBadRecordMac);
server_->ExpectSendAlert(kTlsAlertBadRecordMac);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
- const DataBuffer& server_hello = capture->buffer();
- ASSERT_GT(server_hello.len(), 2U);
- uint32_t ver;
- ASSERT_TRUE(server_hello.Read(0, 2, &ver));
+
+ ASSERT_EQ(2U, capture->extension().len());
+ uint32_t version = 0;
+ ASSERT_TRUE(capture->extension().Read(0, 2, &version));
// This way we don't need to change with new draft version.
- ASSERT_LT(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), ver);
+ ASSERT_LT(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), version);
}
} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/test_io.cc b/security/nss/gtests/ssl_gtest/test_io.cc
index b9f0c672e..adcdbfbaf 100644
--- a/security/nss/gtests/ssl_gtest/test_io.cc
+++ b/security/nss/gtests/ssl_gtest/test_io.cc
@@ -98,8 +98,13 @@ int32_t DummyPrSocket::Recv(PRFileDesc *f, void *buf, int32_t buflen,
}
int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
+ if (write_error_) {
+ PR_SetError(write_error_, 0);
+ return -1;
+ }
+
auto peer = peer_.lock();
- if (!peer || !writeable_) {
+ if (!peer) {
PR_SetError(PR_IO_ERROR, 0);
return -1;
}
@@ -109,7 +114,7 @@ int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
DataBuffer filtered;
PacketFilter::Action action = PacketFilter::KEEP;
if (filter_) {
- action = filter_->Filter(packet, &filtered);
+ action = filter_->Process(packet, &filtered);
}
switch (action) {
case PacketFilter::CHANGE:
diff --git a/security/nss/gtests/ssl_gtest/test_io.h b/security/nss/gtests/ssl_gtest/test_io.h
index ac2497222..469d90a7c 100644
--- a/security/nss/gtests/ssl_gtest/test_io.h
+++ b/security/nss/gtests/ssl_gtest/test_io.h
@@ -33,9 +33,18 @@ class PacketFilter {
CHANGE, // change the packet to a different value
DROP // drop the packet
};
-
+ PacketFilter(bool enabled = true) : enabled_(enabled) {}
virtual ~PacketFilter() {}
+ virtual Action Process(const DataBuffer& input, DataBuffer* output) {
+ if (!enabled_) {
+ return KEEP;
+ }
+ return Filter(input, output);
+ }
+ void Enable() { enabled_ = true; }
+ void Disable() { enabled_ = false; }
+
// The packet filter takes input and has the option of mutating it.
//
// A filter that modifies the data places the modified data in *output and
@@ -43,6 +52,9 @@ class PacketFilter {
// case the value in *output is ignored. A Filter can return DROP, in which
// case the packet is dropped (and *output is ignored).
virtual Action Filter(const DataBuffer& input, DataBuffer* output) = 0;
+
+ private:
+ bool enabled_;
};
class DummyPrSocket : public DummyIOLayerMethods {
@@ -53,7 +65,7 @@ class DummyPrSocket : public DummyIOLayerMethods {
peer_(),
input_(),
filter_(nullptr),
- writeable_(true) {}
+ write_error_(0) {}
virtual ~DummyPrSocket() {}
// Create a file descriptor that will reference this object. The fd must not
@@ -71,7 +83,7 @@ class DummyPrSocket : public DummyIOLayerMethods {
int32_t Recv(PRFileDesc* f, void* buf, int32_t buflen, int32_t flags,
PRIntervalTime to) override;
int32_t Write(PRFileDesc* f, const void* buf, int32_t length) override;
- void CloseWrites() { writeable_ = false; }
+ void SetWriteError(PRErrorCode code) { write_error_ = code; }
SSLProtocolVariant variant() const { return variant_; }
bool readable() const { return !input_.empty(); }
@@ -98,7 +110,7 @@ class DummyPrSocket : public DummyIOLayerMethods {
std::weak_ptr<DummyPrSocket> peer_;
std::queue<Packet> input_;
std::shared_ptr<PacketFilter> filter_;
- bool writeable_;
+ PRErrorCode write_error_;
};
// Marker interface.
diff --git a/security/nss/gtests/ssl_gtest/tls_agent.cc b/security/nss/gtests/ssl_gtest/tls_agent.cc
index d6d91f7f7..3b939bba8 100644
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -10,6 +10,7 @@
#include "pk11func.h"
#include "ssl.h"
#include "sslerr.h"
+#include "sslexp.h"
#include "sslproto.h"
#include "tls_parser.h"
@@ -35,7 +36,6 @@ const std::string TlsAgent::kServerRsa = "rsa"; // both sign and encrypt
const std::string TlsAgent::kServerRsaSign = "rsa_sign";
const std::string TlsAgent::kServerRsaPss = "rsa_pss";
const std::string TlsAgent::kServerRsaDecrypt = "rsa_decrypt";
-const std::string TlsAgent::kServerRsaChain = "rsa_chain";
const std::string TlsAgent::kServerEcdsa256 = "ecdsa256";
const std::string TlsAgent::kServerEcdsa384 = "ecdsa384";
const std::string TlsAgent::kServerEcdsa521 = "ecdsa521";
@@ -73,7 +73,6 @@ TlsAgent::TlsAgent(const std::string& name, Role role,
handshake_callback_(),
auth_certificate_callback_(),
sni_callback_(),
- expect_short_headers_(false),
skip_version_checks_(false) {
memset(&info_, 0, sizeof(info_));
memset(&csinfo_, 0, sizeof(csinfo_));
@@ -93,11 +92,11 @@ TlsAgent::~TlsAgent() {
// Add failures manually, if any, so we don't throw in a destructor.
if (expected_received_alert_ != kTlsAlertCloseNotify ||
expected_received_alert_level_ != kTlsAlertWarning) {
- ADD_FAILURE() << "Wrong expected_received_alert status";
+ ADD_FAILURE() << "Wrong expected_received_alert status: " << role_str();
}
if (expected_sent_alert_ != kTlsAlertCloseNotify ||
expected_sent_alert_level_ != kTlsAlertWarning) {
- ADD_FAILURE() << "Wrong expected_sent_alert status";
+ ADD_FAILURE() << "Wrong expected_sent_alert status: " << role_str();
}
}
@@ -258,13 +257,10 @@ void TlsAgent::CheckCipherSuite(uint16_t cipher_suite) {
}
void TlsAgent::RequestClientAuth(bool requireAuth) {
- EXPECT_TRUE(EnsureTlsSetup());
ASSERT_EQ(SERVER, role_);
- EXPECT_EQ(SECSuccess,
- SSL_OptionSet(ssl_fd(), SSL_REQUEST_CERTIFICATE, PR_TRUE));
- EXPECT_EQ(SECSuccess, SSL_OptionSet(ssl_fd(), SSL_REQUIRE_CERTIFICATE,
- requireAuth ? PR_TRUE : PR_FALSE));
+ SetOption(SSL_REQUEST_CERTIFICATE, PR_TRUE);
+ SetOption(SSL_REQUIRE_CERTIFICATE, requireAuth ? PR_TRUE : PR_FALSE);
EXPECT_EQ(SECSuccess, SSL_AuthCertificateHook(
ssl_fd(), &TlsAgent::ClientAuthenticated, this));
@@ -376,42 +372,8 @@ void TlsAgent::ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups) {
EXPECT_EQ(SECSuccess, rv);
}
-void TlsAgent::SetSessionTicketsEnabled(bool en) {
- EXPECT_TRUE(EnsureTlsSetup());
-
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_ENABLE_SESSION_TICKETS,
- en ? PR_TRUE : PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
-}
-
-void TlsAgent::SetSessionCacheEnabled(bool en) {
- EXPECT_TRUE(EnsureTlsSetup());
-
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_NO_CACHE, en ? PR_FALSE : PR_TRUE);
- EXPECT_EQ(SECSuccess, rv);
-}
-
void TlsAgent::Set0RttEnabled(bool en) {
- EXPECT_TRUE(EnsureTlsSetup());
-
- SECStatus rv =
- SSL_OptionSet(ssl_fd(), SSL_ENABLE_0RTT_DATA, en ? PR_TRUE : PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
-}
-
-void TlsAgent::SetFallbackSCSVEnabled(bool en) {
- EXPECT_TRUE(role_ == CLIENT && EnsureTlsSetup());
-
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_ENABLE_FALLBACK_SCSV,
- en ? PR_TRUE : PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
-}
-
-void TlsAgent::SetShortHeadersEnabled() {
- EXPECT_TRUE(EnsureTlsSetup());
-
- SECStatus rv = SSLInt_EnableShortHeaders(ssl_fd());
- EXPECT_EQ(SECSuccess, rv);
+ SetOption(SSL_ENABLE_0RTT_DATA, en ? PR_TRUE : PR_FALSE);
}
void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
@@ -437,8 +399,6 @@ void TlsAgent::SetServerKeyBits(uint16_t bits) { server_key_bits_ = bits; }
void TlsAgent::ExpectReadWriteError() { expect_readwrite_error_ = true; }
-void TlsAgent::ExpectShortHeaders() { expect_short_headers_ = true; }
-
void TlsAgent::SkipVersionChecks() { skip_version_checks_ = true; }
void TlsAgent::SetSignatureSchemes(const SSLSignatureScheme* schemes,
@@ -517,6 +477,12 @@ void TlsAgent::CheckKEA(SSLKEAType kea_type, SSLNamedGroup kea_group,
}
}
+void TlsAgent::CheckOriginalKEA(SSLNamedGroup kea_group) const {
+ if (kea_group != ssl_grp_ffdhe_custom) {
+ EXPECT_EQ(kea_group, info_.originalKeaGroup);
+ }
+}
+
void TlsAgent::CheckAuthType(SSLAuthType auth_type,
SSLSignatureScheme sig_scheme) const {
EXPECT_EQ(STATE_CONNECTED, state_);
@@ -569,8 +535,7 @@ void TlsAgent::EnableFalseStart() {
falsestart_enabled_ = true;
EXPECT_EQ(SECSuccess, SSL_SetCanFalseStartCallback(
ssl_fd(), CanFalseStartCallback, this));
- EXPECT_EQ(SECSuccess,
- SSL_OptionSet(ssl_fd(), SSL_ENABLE_FALSE_START, PR_TRUE));
+ SetOption(SSL_ENABLE_FALSE_START, PR_TRUE);
}
void TlsAgent::ExpectResumption() { expect_resumption_ = true; }
@@ -578,7 +543,7 @@ void TlsAgent::ExpectResumption() { expect_resumption_ = true; }
void TlsAgent::EnableAlpn(const uint8_t* val, size_t len) {
EXPECT_TRUE(EnsureTlsSetup());
- EXPECT_EQ(SECSuccess, SSL_OptionSet(ssl_fd(), SSL_ENABLE_ALPN, PR_TRUE));
+ SetOption(SSL_ENABLE_ALPN, PR_TRUE);
EXPECT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd(), val, len));
}
@@ -622,12 +587,8 @@ void TlsAgent::CheckErrorCode(int32_t expected) const {
}
static uint8_t GetExpectedAlertLevel(uint8_t alert) {
- switch (alert) {
- case kTlsAlertCloseNotify:
- case kTlsAlertEndOfEarlyData:
- return kTlsAlertWarning;
- default:
- break;
+ if (alert == kTlsAlertCloseNotify) {
+ return kTlsAlertWarning;
}
return kTlsAlertFatal;
}
@@ -730,6 +691,50 @@ void TlsAgent::ResetPreliminaryInfo() {
expected_cipher_suite_ = 0;
}
+void TlsAgent::ValidateCipherSpecs() {
+ PRInt32 cipherSpecs = SSLInt_CountCipherSpecs(ssl_fd());
+ // We use one ciphersuite in each direction.
+ PRInt32 expected = 2;
+ if (variant_ == ssl_variant_datagram) {
+ // For DTLS 1.3, the client retains the cipher spec for early data and the
+ // handshake so that it can retransmit EndOfEarlyData and its final flight.
+ // It also retains the handshake read cipher spec so that it can read ACKs
+ // from the server. The server retains the handshake read cipher spec so it
+ // can read the client's retransmitted Finished.
+ if (expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ if (role_ == CLIENT) {
+ expected = info_.earlyDataAccepted ? 5 : 4;
+ } else {
+ expected = 3;
+ }
+ } else {
+ // For DTLS 1.1 and 1.2, the last endpoint to send maintains a cipher spec
+ // until the holddown timer runs down.
+ if (expect_resumption_) {
+ if (role_ == CLIENT) {
+ expected = 3;
+ }
+ } else {
+ if (role_ == SERVER) {
+ expected = 3;
+ }
+ }
+ }
+ }
+ // This function will be run before the handshake completes if false start is
+ // enabled. In that case, the client will still be reading cleartext, but
+ // will have a spec prepared for reading ciphertext. With DTLS, the client
+ // will also have a spec retained for retransmission of handshake messages.
+ if (role_ == CLIENT && falsestart_enabled_ && !handshake_callback_called_) {
+ EXPECT_GT(SSL_LIBRARY_VERSION_TLS_1_3, expected_version_);
+ expected = (variant_ == ssl_variant_datagram) ? 4 : 3;
+ }
+ EXPECT_EQ(expected, cipherSpecs);
+ if (expected != cipherSpecs) {
+ SSLInt_PrintCipherSpecs(role_str().c_str(), ssl_fd());
+ }
+}
+
void TlsAgent::Connected() {
if (state_ == STATE_CONNECTED) {
return;
@@ -743,6 +748,8 @@ void TlsAgent::Connected() {
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(sizeof(info_), info_.length);
+ EXPECT_EQ(expect_resumption_, info_.resumed == PR_TRUE);
+
// Preliminary values are exposed through callbacks during the handshake.
// If either expected values were set or the callbacks were called, check
// that the final values are correct.
@@ -753,32 +760,13 @@ void TlsAgent::Connected() {
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(sizeof(csinfo_), csinfo_.length);
- if (expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
- PRInt32 cipherSuites = SSLInt_CountTls13CipherSpecs(ssl_fd());
- // We use one ciphersuite in each direction, plus one that's kept around
- // by DTLS for retransmission.
- PRInt32 expected =
- ((variant_ == ssl_variant_datagram) && (role_ == CLIENT)) ? 3 : 2;
- EXPECT_EQ(expected, cipherSuites);
- if (expected != cipherSuites) {
- SSLInt_PrintTls13CipherSpecs(ssl_fd());
- }
- }
+ ValidateCipherSpecs();
- PRBool short_headers;
- rv = SSLInt_UsingShortHeaders(ssl_fd(), &short_headers);
- EXPECT_EQ(SECSuccess, rv);
- EXPECT_EQ((PRBool)expect_short_headers_, short_headers);
SetState(STATE_CONNECTED);
}
void TlsAgent::EnableExtendedMasterSecret() {
- ASSERT_TRUE(EnsureTlsSetup());
-
- SECStatus rv =
- SSL_OptionSet(ssl_fd(), SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
-
- ASSERT_EQ(SECSuccess, rv);
+ SetOption(SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
}
void TlsAgent::CheckExtendedMasterSecret(bool expected) {
@@ -801,21 +789,6 @@ void TlsAgent::CheckSecretsDestroyed() {
ASSERT_EQ(PR_TRUE, SSLInt_CheckSecretsDestroyed(ssl_fd()));
}
-void TlsAgent::DisableRollbackDetection() {
- ASSERT_TRUE(EnsureTlsSetup());
-
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_ROLLBACK_DETECTION, PR_FALSE);
-
- ASSERT_EQ(SECSuccess, rv);
-}
-
-void TlsAgent::EnableCompression() {
- ASSERT_TRUE(EnsureTlsSetup());
-
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_ENABLE_DEFLATE, PR_TRUE);
- ASSERT_EQ(SECSuccess, rv);
-}
-
void TlsAgent::SetDowngradeCheckVersion(uint16_t version) {
ASSERT_TRUE(EnsureTlsSetup());
@@ -883,6 +856,14 @@ void TlsAgent::SendDirect(const DataBuffer& buf) {
}
}
+void TlsAgent::SendRecordDirect(const TlsRecord& record) {
+ DataBuffer buf;
+
+ auto rv = record.header.Write(&buf, 0, record.buffer);
+ EXPECT_EQ(record.header.header_length() + record.buffer.len(), rv);
+ SendDirect(buf);
+}
+
static bool ErrorIsNonFatal(PRErrorCode code) {
return code == PR_WOULD_BLOCK_ERROR || code == SSL_ERROR_RX_SHORT_DTLS_READ;
}
@@ -918,6 +899,27 @@ void TlsAgent::SendBuffer(const DataBuffer& buf) {
}
}
+bool TlsAgent::SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
+ uint16_t wireVersion, uint64_t seq,
+ uint8_t ct, const DataBuffer& buf) {
+ LOGV("Writing " << buf.len() << " bytes");
+ // Ensure we are a TLS 1.3 cipher agent.
+ EXPECT_GE(expected_version_, SSL_LIBRARY_VERSION_TLS_1_3);
+ TlsRecordHeader header(wireVersion, kTlsApplicationDataType, seq);
+ DataBuffer padded = buf;
+ padded.Write(padded.len(), ct, 1);
+ DataBuffer ciphertext;
+ if (!spec->Protect(header, padded, &ciphertext)) {
+ return false;
+ }
+
+ DataBuffer record;
+ auto rv = header.Write(&record, 0, ciphertext);
+ EXPECT_EQ(header.header_length() + ciphertext.len(), rv);
+ SendDirect(record);
+ return true;
+}
+
void TlsAgent::ReadBytes(size_t amount) {
uint8_t block[16384];
@@ -951,23 +953,20 @@ void TlsAgent::ReadBytes(size_t amount) {
void TlsAgent::ResetSentBytes() { send_ctr_ = 0; }
-void TlsAgent::ConfigureSessionCache(SessionResumptionMode mode) {
- EXPECT_TRUE(EnsureTlsSetup());
-
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_NO_CACHE,
- mode & RESUME_SESSIONID ? PR_FALSE : PR_TRUE);
- EXPECT_EQ(SECSuccess, rv);
+void TlsAgent::SetOption(int32_t option, int value) {
+ ASSERT_TRUE(EnsureTlsSetup());
+ EXPECT_EQ(SECSuccess, SSL_OptionSet(ssl_fd(), option, value));
+}
- rv = SSL_OptionSet(ssl_fd(), SSL_ENABLE_SESSION_TICKETS,
- mode & RESUME_TICKET ? PR_TRUE : PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
+void TlsAgent::ConfigureSessionCache(SessionResumptionMode mode) {
+ SetOption(SSL_NO_CACHE, mode & RESUME_SESSIONID ? PR_FALSE : PR_TRUE);
+ SetOption(SSL_ENABLE_SESSION_TICKETS,
+ mode & RESUME_TICKET ? PR_TRUE : PR_FALSE);
}
void TlsAgent::DisableECDHEServerKeyReuse() {
- ASSERT_TRUE(EnsureTlsSetup());
ASSERT_EQ(TlsAgent::SERVER, role_);
- SECStatus rv = SSL_OptionSet(ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
- EXPECT_EQ(SECSuccess, rv);
+ SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
}
static const std::string kTlsRolesAllArr[] = {"CLIENT", "SERVER"};
diff --git a/security/nss/gtests/ssl_gtest/tls_agent.h b/security/nss/gtests/ssl_gtest/tls_agent.h
index 4bccb9a84..b3fd892ae 100644
--- a/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -66,7 +66,6 @@ class TlsAgent : public PollTarget {
static const std::string kServerRsaSign;
static const std::string kServerRsaPss;
static const std::string kServerRsaDecrypt;
- static const std::string kServerRsaChain; // A cert that requires a chain.
static const std::string kServerEcdsa256;
static const std::string kServerEcdsa384;
static const std::string kServerEcdsa521;
@@ -81,9 +80,11 @@ class TlsAgent : public PollTarget {
adapter_->SetPeer(peer->adapter_);
}
+ // Set a filter that can access plaintext (TLS 1.3 only).
void SetTlsRecordFilter(std::shared_ptr<TlsRecordFilter> filter) {
filter->SetAgent(this);
adapter_->SetPacketFilter(filter);
+ filter->EnableDecryption();
}
void SetPacketFilter(std::shared_ptr<PacketFilter> filter) {
@@ -95,6 +96,7 @@ class TlsAgent : public PollTarget {
void StartConnect(PRFileDesc* model = nullptr);
void CheckKEA(SSLKEAType kea_type, SSLNamedGroup group,
size_t kea_size = 0) const;
+ void CheckOriginalKEA(SSLNamedGroup kea_group) const;
void CheckAuthType(SSLAuthType auth_type,
SSLSignatureScheme sig_scheme) const;
@@ -121,12 +123,10 @@ class TlsAgent : public PollTarget {
void SetupClientAuth();
void RequestClientAuth(bool requireAuth);
+ void SetOption(int32_t option, int value);
void ConfigureSessionCache(SessionResumptionMode mode);
- void SetSessionTicketsEnabled(bool en);
- void SetSessionCacheEnabled(bool en);
void Set0RttEnabled(bool en);
void SetFallbackSCSVEnabled(bool en);
- void SetShortHeadersEnabled();
void SetVersionRange(uint16_t minver, uint16_t maxver);
void GetVersionRange(uint16_t* minver, uint16_t* maxver);
void CheckPreliminaryInfo();
@@ -136,7 +136,6 @@ class TlsAgent : public PollTarget {
void ExpectReadWriteError();
void EnableFalseStart();
void ExpectResumption();
- void ExpectShortHeaders();
void SkipVersionChecks();
void SetSignatureSchemes(const SSLSignatureScheme* schemes, size_t count);
void EnableAlpn(const uint8_t* val, size_t len);
@@ -149,15 +148,17 @@ class TlsAgent : public PollTarget {
// Send data on the socket, encrypting it.
void SendData(size_t bytes, size_t blocksize = 1024);
void SendBuffer(const DataBuffer& buf);
+ bool SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
+ uint16_t wireVersion, uint64_t seq, uint8_t ct,
+ const DataBuffer& buf);
// Send data directly to the underlying socket, skipping the TLS layer.
void SendDirect(const DataBuffer& buf);
+ void SendRecordDirect(const TlsRecord& record);
void ReadBytes(size_t max = 16384U);
void ResetSentBytes(); // Hack to test drops.
void EnableExtendedMasterSecret();
void CheckExtendedMasterSecret(bool expected);
void CheckEarlyDataAccepted(bool expected);
- void DisableRollbackDetection();
- void EnableCompression();
void SetDowngradeCheckVersion(uint16_t version);
void CheckSecretsDestroyed();
void ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups);
@@ -170,6 +171,8 @@ class TlsAgent : public PollTarget {
Role role() const { return role_; }
std::string role_str() const { return role_ == SERVER ? "server" : "client"; }
+ SSLProtocolVariant variant() const { return variant_; }
+
State state() const { return state_; }
const CERTCertificate* peer_cert() const {
@@ -253,6 +256,7 @@ class TlsAgent : public PollTarget {
const static char* states[];
void SetState(State state);
+ void ValidateCipherSpecs();
// Dummy auth certificate hook.
static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
@@ -388,7 +392,6 @@ class TlsAgent : public PollTarget {
HandshakeCallbackFunction handshake_callback_;
AuthCertificateCallbackFunction auth_certificate_callback_;
SniCallbackFunction sni_callback_;
- bool expect_short_headers_;
bool skip_version_checks_;
};
diff --git a/security/nss/gtests/ssl_gtest/tls_connect.cc b/security/nss/gtests/ssl_gtest/tls_connect.cc
index c8de5a1fe..0af5123e9 100644
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tls_connect.h"
+#include "sslexp.h"
extern "C" {
#include "libssl_internals.h"
}
@@ -88,6 +89,8 @@ std::string VersionString(uint16_t version) {
switch (version) {
case 0:
return "(no version)";
+ case SSL_LIBRARY_VERSION_3_0:
+ return "1.0";
case SSL_LIBRARY_VERSION_TLS_1_0:
return "1.0";
case SSL_LIBRARY_VERSION_TLS_1_1:
@@ -112,6 +115,7 @@ TlsConnectTestBase::TlsConnectTestBase(SSLProtocolVariant variant,
server_model_(nullptr),
version_(version),
expected_resumption_mode_(RESUME_NONE),
+ expected_resumptions_(0),
session_ids_(),
expect_extended_master_secret_(false),
expect_early_data_accepted_(false),
@@ -161,6 +165,22 @@ void TlsConnectTestBase::CheckShares(
EXPECT_EQ(shares.len(), i);
}
+void TlsConnectTestBase::CheckEpochs(uint16_t client_epoch,
+ uint16_t server_epoch) const {
+ uint16_t read_epoch = 0;
+ uint16_t write_epoch = 0;
+
+ EXPECT_EQ(SECSuccess,
+ SSLInt_GetEpochs(client_->ssl_fd(), &read_epoch, &write_epoch));
+ EXPECT_EQ(server_epoch, read_epoch) << "client read epoch";
+ EXPECT_EQ(client_epoch, write_epoch) << "client write epoch";
+
+ EXPECT_EQ(SECSuccess,
+ SSLInt_GetEpochs(server_->ssl_fd(), &read_epoch, &write_epoch));
+ EXPECT_EQ(client_epoch, read_epoch) << "server read epoch";
+ EXPECT_EQ(server_epoch, write_epoch) << "server write epoch";
+}
+
void TlsConnectTestBase::ClearStats() {
// Clear statistics.
SSL3Statistics* stats = SSL_GetStatistics();
@@ -178,6 +198,7 @@ void TlsConnectTestBase::SetUp() {
SSLInt_ClearSelfEncryptKey();
SSLInt_SetTicketLifetime(30);
SSLInt_SetMaxEarlyDataSize(1024);
+ SSL_SetupAntiReplay(1 * PR_USEC_PER_SEC, 1, 3);
ClearStats();
Init();
}
@@ -219,12 +240,27 @@ void TlsConnectTestBase::Reset(const std::string& server_name,
Init();
}
-void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected) {
+void TlsConnectTestBase::MakeNewServer() {
+ auto replacement = std::make_shared<TlsAgent>(
+ server_->name(), TlsAgent::SERVER, server_->variant());
+ server_ = replacement;
+ if (version_) {
+ server_->SetVersionRange(version_, version_);
+ }
+ client_->SetPeer(server_);
+ server_->SetPeer(client_);
+ server_->StartConnect();
+}
+
+void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected,
+ uint8_t num_resumptions) {
expected_resumption_mode_ = expected;
if (expected != RESUME_NONE) {
client_->ExpectResumption();
server_->ExpectResumption();
+ expected_resumptions_ = num_resumptions;
}
+ EXPECT_EQ(expected_resumptions_ == 0, expected == RESUME_NONE);
}
void TlsConnectTestBase::EnsureTlsSetup() {
@@ -258,6 +294,11 @@ void TlsConnectTestBase::Connect() {
CheckConnected();
}
+void TlsConnectTestBase::StartConnect() {
+ server_->StartConnect(server_model_ ? server_model_->ssl_fd() : nullptr);
+ client_->StartConnect(client_model_ ? client_model_->ssl_fd() : nullptr);
+}
+
void TlsConnectTestBase::ConnectWithCipherSuite(uint16_t cipher_suite) {
EnsureTlsSetup();
client_->EnableSingleCipher(cipher_suite);
@@ -274,6 +315,19 @@ void TlsConnectTestBase::ConnectWithCipherSuite(uint16_t cipher_suite) {
}
void TlsConnectTestBase::CheckConnected() {
+ // Have the client read handshake twice to make sure we get the
+ // NST and the ACK.
+ if (client_->version() >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+ variant_ == ssl_variant_datagram) {
+ client_->Handshake();
+ client_->Handshake();
+ auto suites = SSLInt_CountCipherSpecs(client_->ssl_fd());
+ // Verify that we dropped the client's retransmission cipher suites.
+ EXPECT_EQ(2, suites) << "Client has the wrong number of suites";
+ if (suites != 2) {
+ SSLInt_PrintCipherSpecs("client", client_->ssl_fd());
+ }
+ }
EXPECT_EQ(client_->version(), server_->version());
if (!skip_version_checks_) {
// Check the version is as expected
@@ -314,10 +368,12 @@ void TlsConnectTestBase::CheckConnected() {
void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
SSLAuthType auth_type,
SSLSignatureScheme sig_scheme) const {
- client_->CheckKEA(kea_type, kea_group);
- server_->CheckKEA(kea_type, kea_group);
- client_->CheckAuthType(auth_type, sig_scheme);
+ if (kea_group != ssl_grp_none) {
+ client_->CheckKEA(kea_type, kea_group);
+ server_->CheckKEA(kea_type, kea_group);
+ }
server_->CheckAuthType(auth_type, sig_scheme);
+ client_->CheckAuthType(auth_type, sig_scheme);
}
void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type,
@@ -372,9 +428,19 @@ void TlsConnectTestBase::CheckKeys() const {
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
}
+void TlsConnectTestBase::CheckKeysResumption(SSLKEAType kea_type,
+ SSLNamedGroup kea_group,
+ SSLNamedGroup original_kea_group,
+ SSLAuthType auth_type,
+ SSLSignatureScheme sig_scheme) {
+ CheckKeys(kea_type, kea_group, auth_type, sig_scheme);
+ EXPECT_TRUE(expected_resumption_mode_ != RESUME_NONE);
+ client_->CheckOriginalKEA(original_kea_group);
+ server_->CheckOriginalKEA(original_kea_group);
+}
+
void TlsConnectTestBase::ConnectExpectFail() {
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
Handshake();
ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
@@ -395,8 +461,7 @@ void TlsConnectTestBase::ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender,
}
void TlsConnectTestBase::ConnectExpectFailOneSide(TlsAgent::Role failing_side) {
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
client_->Handshake();
server_->Handshake();
@@ -455,29 +520,33 @@ void TlsConnectTestBase::EnableSomeEcdhCiphers() {
}
}
+void TlsConnectTestBase::ConfigureSelfEncrypt() {
+ ScopedCERTCertificate cert;
+ ScopedSECKEYPrivateKey privKey;
+ ASSERT_TRUE(
+ TlsAgent::LoadCertificate(TlsAgent::kServerRsaDecrypt, &cert, &privKey));
+
+ ScopedSECKEYPublicKey pubKey(CERT_ExtractPublicKey(cert.get()));
+ ASSERT_TRUE(pubKey);
+
+ EXPECT_EQ(SECSuccess,
+ SSL_SetSessionTicketKeyPair(pubKey.get(), privKey.get()));
+}
+
void TlsConnectTestBase::ConfigureSessionCache(SessionResumptionMode client,
SessionResumptionMode server) {
client_->ConfigureSessionCache(client);
server_->ConfigureSessionCache(server);
if ((server & RESUME_TICKET) != 0) {
- ScopedCERTCertificate cert;
- ScopedSECKEYPrivateKey privKey;
- ASSERT_TRUE(TlsAgent::LoadCertificate(TlsAgent::kServerRsaDecrypt, &cert,
- &privKey));
-
- ScopedSECKEYPublicKey pubKey(CERT_ExtractPublicKey(cert.get()));
- ASSERT_TRUE(pubKey);
-
- EXPECT_EQ(SECSuccess,
- SSL_SetSessionTicketKeyPair(pubKey.get(), privKey.get()));
+ ConfigureSelfEncrypt();
}
}
void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
EXPECT_NE(RESUME_BOTH, expected);
- int resume_count = expected ? 1 : 0;
- int stateless_count = (expected & RESUME_TICKET) ? 1 : 0;
+ int resume_count = expected ? expected_resumptions_ : 0;
+ int stateless_count = (expected & RESUME_TICKET) ? expected_resumptions_ : 0;
// Note: hch == server counter; hsh == client counter.
SSL3Statistics* stats = SSL_GetStatistics();
@@ -490,7 +559,7 @@ void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
if (expected != RESUME_NONE) {
if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
// Check that the last two session ids match.
- ASSERT_EQ(2U, session_ids_.size());
+ ASSERT_EQ(1U + expected_resumptions_, session_ids_.size());
EXPECT_EQ(session_ids_[session_ids_.size() - 1],
session_ids_[session_ids_.size() - 2]);
} else {
@@ -540,31 +609,28 @@ void TlsConnectTestBase::CheckSrtp() const {
server_->CheckSrtp();
}
-void TlsConnectTestBase::SendReceive() {
- client_->SendData(50);
- server_->SendData(50);
- Receive(50);
+void TlsConnectTestBase::SendReceive(size_t total) {
+ ASSERT_GT(total, client_->received_bytes());
+ ASSERT_GT(total, server_->received_bytes());
+ client_->SendData(total - server_->received_bytes());
+ server_->SendData(total - client_->received_bytes());
+ Receive(total); // Receive() is cumulative
}
// Do a first connection so we can do 0-RTT on the second one.
void TlsConnectTestBase::SetupForZeroRtt() {
+ // If we don't do this, then all 0-RTT attempts will be rejected.
+ SSLInt_RolloverAntiReplay();
+
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
- client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
- SSL_LIBRARY_VERSION_TLS_1_3);
- server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
- SSL_LIBRARY_VERSION_TLS_1_3);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true); // So we signal that we allow 0-RTT.
Connect();
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys();
Reset();
- client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
- SSL_LIBRARY_VERSION_TLS_1_3);
- server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
- SSL_LIBRARY_VERSION_TLS_1_3);
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
}
// Do a first connection so we can do resumption
@@ -584,10 +650,6 @@ void TlsConnectTestBase::ZeroRttSendReceive(
const char* k0RttData = "ABCDEF";
const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
- if (expect_writable && expect_readable) {
- ExpectAlert(client_, kTlsAlertEndOfEarlyData);
- }
-
client_->Handshake(); // Send ClientHello.
if (post_clienthello_check) {
if (!post_clienthello_check()) return;
@@ -599,7 +661,7 @@ void TlsConnectTestBase::ZeroRttSendReceive(
} else {
EXPECT_EQ(SECFailure, rv);
}
- server_->Handshake(); // Consume ClientHello, EE, Finished.
+ server_->Handshake(); // Consume ClientHello
std::vector<uint8_t> buf(k0RttDataLen);
rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen); // 0-RTT read
@@ -653,6 +715,30 @@ void TlsConnectTestBase::SkipVersionChecks() {
server_->SkipVersionChecks();
}
+// Shift the DTLS timers, to the minimum time necessary to let the next timer
+// run on either client or server. This allows tests to skip waiting without
+// having timers run out of order.
+void TlsConnectTestBase::ShiftDtlsTimers() {
+ PRIntervalTime time_shift = PR_INTERVAL_NO_TIMEOUT;
+ PRIntervalTime time;
+ SECStatus rv = DTLS_GetHandshakeTimeout(client_->ssl_fd(), &time);
+ if (rv == SECSuccess) {
+ time_shift = time;
+ }
+ rv = DTLS_GetHandshakeTimeout(server_->ssl_fd(), &time);
+ if (rv == SECSuccess &&
+ (time < time_shift || time_shift == PR_INTERVAL_NO_TIMEOUT)) {
+ time_shift = time;
+ }
+
+ if (time_shift == PR_INTERVAL_NO_TIMEOUT) {
+ return;
+ }
+
+ EXPECT_EQ(SECSuccess, SSLInt_ShiftDtlsTimers(client_->ssl_fd(), time_shift));
+ EXPECT_EQ(SECSuccess, SSLInt_ShiftDtlsTimers(server_->ssl_fd(), time_shift));
+}
+
TlsConnectGeneric::TlsConnectGeneric()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
@@ -691,11 +777,15 @@ void TlsKeyExchangeTest::ConfigNamedGroups(
}
std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetGroupDetails(
- const DataBuffer& ext) {
+ const std::shared_ptr<TlsExtensionCapture>& capture) {
+ EXPECT_TRUE(capture->captured());
+ const DataBuffer& ext = capture->extension();
+
uint32_t tmp = 0;
EXPECT_TRUE(ext.Read(0, 2, &tmp));
EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
EXPECT_TRUE(ext.len() % 2 == 0);
+
std::vector<SSLNamedGroup> groups;
for (size_t i = 1; i < ext.len() / 2; i += 1) {
EXPECT_TRUE(ext.Read(2 * i, 2, &tmp));
@@ -705,10 +795,14 @@ std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetGroupDetails(
}
std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetShareDetails(
- const DataBuffer& ext) {
+ const std::shared_ptr<TlsExtensionCapture>& capture) {
+ EXPECT_TRUE(capture->captured());
+ const DataBuffer& ext = capture->extension();
+
uint32_t tmp = 0;
EXPECT_TRUE(ext.Read(0, 2, &tmp));
EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
+
std::vector<SSLNamedGroup> shares;
size_t i = 2;
while (i < ext.len()) {
@@ -724,17 +818,15 @@ std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetShareDetails(
void TlsKeyExchangeTest::CheckKEXDetails(
const std::vector<SSLNamedGroup>& expected_groups,
const std::vector<SSLNamedGroup>& expected_shares, bool expect_hrr) {
- std::vector<SSLNamedGroup> groups =
- GetGroupDetails(groups_capture_->extension());
+ std::vector<SSLNamedGroup> groups = GetGroupDetails(groups_capture_);
EXPECT_EQ(expected_groups, groups);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
ASSERT_LT(0U, expected_shares.size());
- std::vector<SSLNamedGroup> shares =
- GetShareDetails(shares_capture_->extension());
+ std::vector<SSLNamedGroup> shares = GetShareDetails(shares_capture_);
EXPECT_EQ(expected_shares, shares);
} else {
- EXPECT_EQ(0U, shares_capture_->extension().len());
+ EXPECT_FALSE(shares_capture_->captured());
}
EXPECT_EQ(expect_hrr, capture_hrr_->buffer().len() != 0);
@@ -756,8 +848,6 @@ void TlsKeyExchangeTest::CheckKEXDetails(
EXPECT_NE(expected_share2, it);
}
std::vector<SSLNamedGroup> expected_shares2 = {expected_share2};
- std::vector<SSLNamedGroup> shares =
- GetShareDetails(shares_capture2_->extension());
- EXPECT_EQ(expected_shares2, shares);
+ EXPECT_EQ(expected_shares2, GetShareDetails(shares_capture2_));
}
} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/tls_connect.h b/security/nss/gtests/ssl_gtest/tls_connect.h
index 73e8dc81a..c650dda1d 100644
--- a/security/nss/gtests/ssl_gtest/tls_connect.h
+++ b/security/nss/gtests/ssl_gtest/tls_connect.h
@@ -61,7 +61,11 @@ class TlsConnectTestBase : public ::testing::Test {
// Reset, and update the certificate names on both peers
void Reset(const std::string& server_name,
const std::string& client_name = "client");
+ // Replace the server.
+ void MakeNewServer();
+ // Set up
+ void StartConnect();
// Run the handshake.
void Handshake();
// Connect and check that it works.
@@ -81,20 +85,28 @@ class TlsConnectTestBase : public ::testing::Test {
void CheckKeys(SSLKEAType kea_type, SSLAuthType auth_type) const;
// This version assumes defaults.
void CheckKeys() const;
+ // Check that keys on resumed sessions.
+ void CheckKeysResumption(SSLKEAType kea_type, SSLNamedGroup kea_group,
+ SSLNamedGroup original_kea_group,
+ SSLAuthType auth_type,
+ SSLSignatureScheme sig_scheme);
void CheckGroups(const DataBuffer& groups,
std::function<void(SSLNamedGroup)> check_group);
void CheckShares(const DataBuffer& shares,
std::function<void(SSLNamedGroup)> check_group);
+ void CheckEpochs(uint16_t client_epoch, uint16_t server_epoch) const;
void ConfigureVersion(uint16_t version);
void SetExpectedVersion(uint16_t version);
// Expect resumption of a particular type.
- void ExpectResumption(SessionResumptionMode expected);
+ void ExpectResumption(SessionResumptionMode expected,
+ uint8_t num_resumed = 1);
void DisableAllCiphers();
void EnableOnlyStaticRsaCiphers();
void EnableOnlyDheCiphers();
void EnableSomeEcdhCiphers();
void EnableExtendedMasterSecret();
+ void ConfigureSelfEncrypt();
void ConfigureSessionCache(SessionResumptionMode client,
SessionResumptionMode server);
void EnableAlpn();
@@ -103,7 +115,7 @@ class TlsConnectTestBase : public ::testing::Test {
void CheckAlpn(const std::string& val);
void EnableSrtp();
void CheckSrtp() const;
- void SendReceive();
+ void SendReceive(size_t total = 50);
void SetupForZeroRtt();
void SetupForResume();
void ZeroRttSendReceive(
@@ -115,6 +127,9 @@ class TlsConnectTestBase : public ::testing::Test {
void DisableECDHEServerKeyReuse();
void SkipVersionChecks();
+ // Move the DTLS timers for both endpoints to pop the next timer.
+ void ShiftDtlsTimers();
+
protected:
SSLProtocolVariant variant_;
std::shared_ptr<TlsAgent> client_;
@@ -123,6 +138,7 @@ class TlsConnectTestBase : public ::testing::Test {
std::unique_ptr<TlsAgent> server_model_;
uint16_t version_;
SessionResumptionMode expected_resumption_mode_;
+ uint8_t expected_resumptions_;
std::vector<std::vector<uint8_t>> session_ids_;
// A simple value of "a", "b". Note that the preferred value of "a" is placed
@@ -244,6 +260,11 @@ class TlsConnectDatagram13 : public TlsConnectTestBase {
: TlsConnectTestBase(ssl_variant_datagram, SSL_LIBRARY_VERSION_TLS_1_3) {}
};
+class TlsConnectDatagramPre13 : public TlsConnectDatagram {
+ public:
+ TlsConnectDatagramPre13() {}
+};
+
// A variant that is used only with Pre13.
class TlsConnectGenericPre13 : public TlsConnectGeneric {};
@@ -256,8 +277,10 @@ class TlsKeyExchangeTest : public TlsConnectGeneric {
void EnsureKeyShareSetup();
void ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups);
- std::vector<SSLNamedGroup> GetGroupDetails(const DataBuffer& ext);
- std::vector<SSLNamedGroup> GetShareDetails(const DataBuffer& ext);
+ std::vector<SSLNamedGroup> GetGroupDetails(
+ const std::shared_ptr<TlsExtensionCapture>& capture);
+ std::vector<SSLNamedGroup> GetShareDetails(
+ const std::shared_ptr<TlsExtensionCapture>& capture);
void CheckKEXDetails(const std::vector<SSLNamedGroup>& expectedGroups,
const std::vector<SSLNamedGroup>& expectedShares);
void CheckKEXDetails(const std::vector<SSLNamedGroup>& expectedGroups,
diff --git a/security/nss/gtests/ssl_gtest/tls_filter.cc b/security/nss/gtests/ssl_gtest/tls_filter.cc
index 76d9aaaff..89f201295 100644
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -12,6 +12,7 @@ extern "C" {
#include "libssl_internals.h"
}
+#include <cassert>
#include <iostream>
#include "gtest_utils.h"
#include "tls_agent.h"
@@ -57,17 +58,22 @@ void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
PRBool isServer = self->agent()->role() == TlsAgent::SERVER;
if (g_ssl_gtest_verbose) {
- std::cerr << "Cipher spec changed. Role="
- << (isServer ? "server" : "client")
- << " direction=" << (sending ? "send" : "receive") << std::endl;
+ std::cerr << (isServer ? "server" : "client") << ": "
+ << (sending ? "send" : "receive")
+ << " cipher spec changed: " << newSpec->epoch << " ("
+ << newSpec->phase << ")" << std::endl;
+ }
+ if (!sending) {
+ return;
}
- if (!sending) return;
+ self->in_sequence_number_ = 0;
+ self->out_sequence_number_ = 0;
+ self->dropped_record_ = false;
self->cipher_spec_.reset(new TlsCipherSpec());
- bool ret =
- self->cipher_spec_->Init(SSLInt_CipherSpecToAlgorithm(isServer, newSpec),
- SSLInt_CipherSpecToKey(isServer, newSpec),
- SSLInt_CipherSpecToIv(isServer, newSpec));
+ bool ret = self->cipher_spec_->Init(
+ SSLInt_CipherSpecToEpoch(newSpec), SSLInt_CipherSpecToAlgorithm(newSpec),
+ SSLInt_CipherSpecToKey(newSpec), SSLInt_CipherSpecToIv(newSpec));
EXPECT_EQ(true, ret);
}
@@ -83,11 +89,23 @@ PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
TlsRecordHeader header;
DataBuffer record;
- if (!header.Parse(&parser, &record)) {
+ if (!header.Parse(in_sequence_number_, &parser, &record)) {
ADD_FAILURE() << "not a valid record";
return KEEP;
}
+ // Track the sequence number, which is necessary for stream mode (the
+ // sequence number is in the header for datagram).
+ //
+ // This isn't perfectly robust. If there is a change from an active cipher
+ // spec to another active cipher spec (KeyUpdate for instance) AND writes
+ // are consolidated across that change AND packets were dropped from the
+ // older epoch, we will not correctly re-encrypt records in the old epoch to
+ // update their sequence numbers.
+ if (cipher_spec_ && header.content_type() == kTlsApplicationDataType) {
+ ++in_sequence_number_;
+ }
+
if (FilterRecord(header, record, &offset, output) != KEEP) {
changed = true;
} else {
@@ -120,30 +138,49 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
header.sequence_number()};
PacketFilter::Action action = FilterRecord(real_header, plaintext, &filtered);
+ // In stream mode, even if something doesn't change we need to re-encrypt if
+ // previous packets were dropped.
if (action == KEEP) {
- return KEEP;
+ if (header.is_dtls() || !dropped_record_) {
+ return KEEP;
+ }
+ filtered = plaintext;
}
if (action == DROP) {
- std::cerr << "record drop: " << record << std::endl;
+ std::cerr << "record drop: " << header << ":" << record << std::endl;
+ dropped_record_ = true;
return DROP;
}
EXPECT_GT(0x10000U, filtered.len());
- std::cerr << "record old: " << plaintext << std::endl;
- std::cerr << "record new: " << filtered << std::endl;
+ if (action != KEEP) {
+ std::cerr << "record old: " << plaintext << std::endl;
+ std::cerr << "record new: " << filtered << std::endl;
+ }
+
+ uint64_t seq_num;
+ if (header.is_dtls() || !cipher_spec_ ||
+ header.content_type() != kTlsApplicationDataType) {
+ seq_num = header.sequence_number();
+ } else {
+ seq_num = out_sequence_number_++;
+ }
+ TlsRecordHeader out_header = {header.version(), header.content_type(),
+ seq_num};
DataBuffer ciphertext;
- bool rv = Protect(header, inner_content_type, filtered, &ciphertext);
+ bool rv = Protect(out_header, inner_content_type, filtered, &ciphertext);
EXPECT_TRUE(rv);
if (!rv) {
return KEEP;
}
- *offset = header.Write(output, *offset, ciphertext);
+ *offset = out_header.Write(output, *offset, ciphertext);
return CHANGE;
}
-bool TlsRecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
+bool TlsRecordHeader::Parse(uint64_t sequence_number, TlsParser* parser,
+ DataBuffer* body) {
if (!parser->Read(&content_type_)) {
return false;
}
@@ -154,7 +191,7 @@ bool TlsRecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
}
version_ = version;
- sequence_number_ = 0;
+ // If this is DTLS, overwrite the sequence number.
if (IsDtls(version)) {
uint32_t tmp;
if (!parser->Read(&tmp, 4)) {
@@ -165,6 +202,8 @@ bool TlsRecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
return false;
}
sequence_number_ |= static_cast<uint64_t>(tmp);
+ } else {
+ sequence_number_ = sequence_number;
}
return parser->ReadVariable(body, 2);
}
@@ -193,7 +232,9 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
return true;
}
- if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) return false;
+ if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) {
+ return false;
+ }
size_t len = plaintext->len();
while (len > 0 && !plaintext->data()[len - 1]) {
@@ -206,6 +247,11 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
*inner_content_type = plaintext->data()[len - 1];
plaintext->Truncate(len - 1);
+ if (g_ssl_gtest_verbose) {
+ std::cerr << "unprotect: " << std::hex << header.sequence_number()
+ << std::dec << " type=" << static_cast<int>(*inner_content_type)
+ << " " << *plaintext << std::endl;
+ }
return true;
}
@@ -218,16 +264,44 @@ bool TlsRecordFilter::Protect(const TlsRecordHeader& header,
*ciphertext = plaintext;
return true;
}
+ if (g_ssl_gtest_verbose) {
+ std::cerr << "protect: " << header.sequence_number() << std::endl;
+ }
DataBuffer padded = plaintext;
padded.Write(padded.len(), inner_content_type, 1);
return cipher_spec_->Protect(header, padded, ciphertext);
}
+bool IsHelloRetry(const DataBuffer& body) {
+ static const uint8_t ssl_hello_retry_random[] = {
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C,
+ 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB,
+ 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};
+ return memcmp(body.data() + 2, ssl_hello_retry_random,
+ sizeof(ssl_hello_retry_random)) == 0;
+}
+
+bool TlsHandshakeFilter::IsFilteredType(const HandshakeHeader& header,
+ const DataBuffer& body) {
+ if (handshake_types_.empty()) {
+ return true;
+ }
+
+ uint8_t type = header.handshake_type();
+ if (type == kTlsHandshakeServerHello) {
+ if (IsHelloRetry(body)) {
+ type = kTlsHandshakeHelloRetryRequest;
+ }
+ }
+ return handshake_types_.count(type) > 0U;
+}
+
PacketFilter::Action TlsHandshakeFilter::FilterRecord(
const TlsRecordHeader& record_header, const DataBuffer& input,
DataBuffer* output) {
// Check that the first byte is as requested.
- if (record_header.content_type() != kTlsHandshakeType) {
+ if ((record_header.content_type() != kTlsHandshakeType) &&
+ (record_header.content_type() != kTlsAltHandshakeType)) {
return KEEP;
}
@@ -239,12 +313,29 @@ PacketFilter::Action TlsHandshakeFilter::FilterRecord(
while (parser.remaining()) {
HandshakeHeader header;
DataBuffer handshake;
- if (!header.Parse(&parser, record_header, &handshake)) {
+ bool complete = false;
+ if (!header.Parse(&parser, record_header, preceding_fragment_, &handshake,
+ &complete)) {
return KEEP;
}
+ if (!complete) {
+ EXPECT_TRUE(record_header.is_dtls());
+ // Save the fragment and drop it from this record. Fragments are
+ // coalesced with the last fragment of the handshake message.
+ changed = true;
+ preceding_fragment_.Assign(handshake);
+ continue;
+ }
+ preceding_fragment_.Truncate(0);
+
DataBuffer filtered;
- PacketFilter::Action action = FilterHandshake(header, handshake, &filtered);
+ PacketFilter::Action action;
+ if (!IsFilteredType(header, handshake)) {
+ action = KEEP;
+ } else {
+ action = FilterHandshake(header, handshake, &filtered);
+ }
if (action == DROP) {
changed = true;
std::cerr << "handshake drop: " << handshake << std::endl;
@@ -258,6 +349,8 @@ PacketFilter::Action TlsHandshakeFilter::FilterRecord(
std::cerr << "handshake old: " << handshake << std::endl;
std::cerr << "handshake new: " << filtered << std::endl;
source = &filtered;
+ } else if (preceding_fragment_.len()) {
+ changed = true;
}
offset = header.Write(output, offset, *source);
@@ -267,12 +360,16 @@ PacketFilter::Action TlsHandshakeFilter::FilterRecord(
}
bool TlsHandshakeFilter::HandshakeHeader::ReadLength(
- TlsParser* parser, const TlsRecordHeader& header, uint32_t* length) {
- if (!parser->Read(length, 3)) {
+ TlsParser* parser, const TlsRecordHeader& header, uint32_t expected_offset,
+ uint32_t* length, bool* last_fragment) {
+ uint32_t message_length;
+ if (!parser->Read(&message_length, 3)) {
return false; // malformed
}
if (!header.is_dtls()) {
+ *last_fragment = true;
+ *length = message_length;
return true; // nothing left to do
}
@@ -283,32 +380,50 @@ bool TlsHandshakeFilter::HandshakeHeader::ReadLength(
}
message_seq_ = message_seq_tmp;
- uint32_t fragment_offset;
- if (!parser->Read(&fragment_offset, 3)) {
+ uint32_t offset = 0;
+ if (!parser->Read(&offset, 3)) {
+ return false;
+ }
+ // We only parse if the fragments are all complete and in order.
+ if (offset != expected_offset) {
+ EXPECT_NE(0U, header.epoch())
+ << "Received out of order handshake fragment for epoch 0";
return false;
}
- uint32_t fragment_length;
- if (!parser->Read(&fragment_length, 3)) {
+ // For DTLS, we return the length of just this fragment.
+ if (!parser->Read(length, 3)) {
return false;
}
- // All current tests where we are using this code don't fragment.
- return (fragment_offset == 0 && fragment_length == *length);
+ // It's a fragment if the entire message is longer than what we have.
+ *last_fragment = message_length == (*length + offset);
+ return true;
}
bool TlsHandshakeFilter::HandshakeHeader::Parse(
- TlsParser* parser, const TlsRecordHeader& record_header, DataBuffer* body) {
+ TlsParser* parser, const TlsRecordHeader& record_header,
+ const DataBuffer& preceding_fragment, DataBuffer* body, bool* complete) {
+ *complete = false;
+
version_ = record_header.version();
if (!parser->Read(&handshake_type_)) {
return false; // malformed
}
+
uint32_t length;
- if (!ReadLength(parser, record_header, &length)) {
+ if (!ReadLength(parser, record_header, preceding_fragment.len(), &length,
+ complete)) {
return false;
}
- return parser->Read(body, length);
+ if (!parser->Read(body, length)) {
+ return false;
+ }
+ if (preceding_fragment.len()) {
+ body->Splice(preceding_fragment, 0);
+ }
+ return true;
}
size_t TlsHandshakeFilter::HandshakeHeader::WriteFragment(
@@ -345,20 +460,23 @@ PacketFilter::Action TlsInspectorRecordHandshakeMessage::FilterHandshake(
return KEEP;
}
- if (header.handshake_type() == handshake_type_) {
- buffer_ = input;
- }
+ buffer_ = input;
return KEEP;
}
PacketFilter::Action TlsInspectorReplaceHandshakeMessage::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
- if (header.handshake_type() == handshake_type_) {
- *output = buffer_;
- return CHANGE;
- }
+ *output = buffer_;
+ return CHANGE;
+}
+PacketFilter::Action TlsRecordRecorder::FilterRecord(
+ const TlsRecordHeader& header, const DataBuffer& input,
+ DataBuffer* output) {
+ if (!filter_ || (header.content_type() == ct_)) {
+ records_.push_back({header, input});
+ }
return KEEP;
}
@@ -369,15 +487,30 @@ PacketFilter::Action TlsConversationRecorder::FilterRecord(
return KEEP;
}
+PacketFilter::Action TlsHeaderRecorder::FilterRecord(
+ const TlsRecordHeader& header, const DataBuffer& input,
+ DataBuffer* output) {
+ headers_.push_back(header);
+ return KEEP;
+}
+
+const TlsRecordHeader* TlsHeaderRecorder::header(size_t index) {
+ if (index > headers_.size() + 1) {
+ return nullptr;
+ }
+ return &headers_[index];
+}
+
PacketFilter::Action ChainedPacketFilter::Filter(const DataBuffer& input,
DataBuffer* output) {
DataBuffer in(input);
bool changed = false;
for (auto it = filters_.begin(); it != filters_.end(); ++it) {
- PacketFilter::Action action = (*it)->Filter(in, output);
+ PacketFilter::Action action = (*it)->Process(in, output);
if (action == DROP) {
return DROP;
}
+
if (action == CHANGE) {
in = *output;
changed = true;
@@ -430,15 +563,6 @@ bool FindServerHelloExtensions(TlsParser* parser, const TlsVersioned& header) {
return true;
}
-static bool FindHelloRetryExtensions(TlsParser* parser,
- const TlsVersioned& header) {
- // TODO for -19 add cipher suite
- if (!parser->Skip(2)) { // version
- return false;
- }
- return true;
-}
-
bool FindEncryptedExtensions(TlsParser* parser, const TlsVersioned& header) {
return true;
}
@@ -448,13 +572,6 @@ static bool FindCertReqExtensions(TlsParser* parser,
if (!parser->SkipVariable(1)) { // request context
return false;
}
- // TODO remove the next two for -19
- if (!parser->SkipVariable(2)) { // signature_algorithms
- return false;
- }
- if (!parser->SkipVariable(2)) { // certificate_authorities
- return false;
- }
return true;
}
@@ -478,6 +595,9 @@ static bool FindNewSessionTicketExtensions(TlsParser* parser,
if (!parser->Skip(8)) { // lifetime, age add
return false;
}
+ if (!parser->SkipVariable(1)) { // ticket_nonce
+ return false;
+ }
if (!parser->SkipVariable(2)) { // ticket
return false;
}
@@ -487,7 +607,6 @@ static bool FindNewSessionTicketExtensions(TlsParser* parser,
static const std::map<uint16_t, TlsExtensionFinder> kExtensionFinders = {
{kTlsHandshakeClientHello, FindClientHelloExtensions},
{kTlsHandshakeServerHello, FindServerHelloExtensions},
- {kTlsHandshakeHelloRetryRequest, FindHelloRetryExtensions},
{kTlsHandshakeEncryptedExtensions, FindEncryptedExtensions},
{kTlsHandshakeCertificateRequest, FindCertReqExtensions},
{kTlsHandshakeCertificate, FindCertificateExtensions},
@@ -505,10 +624,6 @@ bool TlsExtensionFilter::FindExtensions(TlsParser* parser,
PacketFilter::Action TlsExtensionFilter::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
- if (handshake_types_.count(header.handshake_type()) == 0) {
- return KEEP;
- }
-
TlsParser parser(input);
if (!FindExtensions(&parser, header)) {
return KEEP;
@@ -610,6 +725,38 @@ PacketFilter::Action TlsExtensionDropper::FilterExtension(
return KEEP;
}
+PacketFilter::Action TlsExtensionInjector::FilterHandshake(
+ const HandshakeHeader& header, const DataBuffer& input,
+ DataBuffer* output) {
+ TlsParser parser(input);
+ if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
+ return KEEP;
+ }
+ size_t offset = parser.consumed();
+
+ *output = input;
+
+ // Increase the size of the extensions.
+ uint16_t ext_len;
+ memcpy(&ext_len, output->data() + offset, sizeof(ext_len));
+ ext_len = htons(ntohs(ext_len) + data_.len() + 4);
+ memcpy(output->data() + offset, &ext_len, sizeof(ext_len));
+
+ // Insert the extension type and length.
+ DataBuffer type_length;
+ type_length.Allocate(4);
+ type_length.Write(0, extension_, 2);
+ type_length.Write(2, data_.len(), 2);
+ output->Splice(type_length, offset + 2);
+
+ // Insert the payload.
+ if (data_.len() > 0) {
+ output->Splice(data_, offset + 6);
+ }
+
+ return CHANGE;
+}
+
PacketFilter::Action AfterRecordN::FilterRecord(const TlsRecordHeader& header,
const DataBuffer& body,
DataBuffer* out) {
@@ -628,10 +775,8 @@ PacketFilter::Action AfterRecordN::FilterRecord(const TlsRecordHeader& header,
PacketFilter::Action TlsInspectorClientHelloVersionChanger::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
- if (header.handshake_type() == kTlsHandshakeClientKeyExchange) {
- EXPECT_EQ(SECSuccess,
- SSLInt_IncrementClientHandshakeVersion(server_.lock()->ssl_fd()));
- }
+ EXPECT_EQ(SECSuccess,
+ SSLInt_IncrementClientHandshakeVersion(server_.lock()->ssl_fd()));
return KEEP;
}
@@ -643,15 +788,49 @@ PacketFilter::Action SelectiveDropFilter::Filter(const DataBuffer& input,
return ((1 << counter_++) & pattern_) ? DROP : KEEP;
}
+PacketFilter::Action SelectiveRecordDropFilter::FilterRecord(
+ const TlsRecordHeader& header, const DataBuffer& data,
+ DataBuffer* changed) {
+ if (counter_ >= 32) {
+ return KEEP;
+ }
+ return ((1 << counter_++) & pattern_) ? DROP : KEEP;
+}
+
+/* static */ uint32_t SelectiveRecordDropFilter::ToPattern(
+ std::initializer_list<size_t> records) {
+ uint32_t pattern = 0;
+ for (auto it = records.begin(); it != records.end(); ++it) {
+ EXPECT_GT(32U, *it);
+ assert(*it < 32U);
+ pattern |= 1 << *it;
+ }
+ return pattern;
+}
+
PacketFilter::Action TlsInspectorClientHelloVersionSetter::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
- if (header.handshake_type() == kTlsHandshakeClientHello) {
- *output = input;
- output->Write(0, version_, 2);
- return CHANGE;
- }
- return KEEP;
+ *output = input;
+ output->Write(0, version_, 2);
+ return CHANGE;
+}
+
+PacketFilter::Action SelectedCipherSuiteReplacer::FilterHandshake(
+ const HandshakeHeader& header, const DataBuffer& input,
+ DataBuffer* output) {
+ *output = input;
+ uint32_t temp = 0;
+ EXPECT_TRUE(input.Read(0, 2, &temp));
+ // Cipher suite is after version(2) and random(32).
+ size_t pos = 34;
+ if (temp < SSL_LIBRARY_VERSION_TLS_1_3) {
+ // In old versions, we have to skip a session_id too.
+ EXPECT_TRUE(input.Read(pos, 1, &temp));
+ pos += 1 + temp;
+ }
+ output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2);
+ return CHANGE;
}
} // namespace nss_test
diff --git a/security/nss/gtests/ssl_gtest/tls_filter.h b/security/nss/gtests/ssl_gtest/tls_filter.h
index e4030e23f..1db3b90f6 100644
--- a/security/nss/gtests/ssl_gtest/tls_filter.h
+++ b/security/nss/gtests/ssl_gtest/tls_filter.h
@@ -50,10 +50,13 @@ class TlsRecordHeader : public TlsVersioned {
uint8_t content_type() const { return content_type_; }
uint64_t sequence_number() const { return sequence_number_; }
- size_t header_length() const { return is_dtls() ? 11 : 3; }
+ uint16_t epoch() const {
+ return static_cast<uint16_t>(sequence_number_ >> 48);
+ }
+ size_t header_length() const { return is_dtls() ? 13 : 5; }
// Parse the header; return true if successful; body in an outparam if OK.
- bool Parse(TlsParser* parser, DataBuffer* body);
+ bool Parse(uint64_t sequence_number, TlsParser* parser, DataBuffer* body);
// Write the header and body to a buffer at the given offset.
// Return the offset of the end of the write.
size_t Write(DataBuffer* buffer, size_t offset, const DataBuffer& body) const;
@@ -63,10 +66,21 @@ class TlsRecordHeader : public TlsVersioned {
uint64_t sequence_number_;
};
+struct TlsRecord {
+ const TlsRecordHeader header;
+ const DataBuffer buffer;
+};
+
// Abstract filter that operates on entire (D)TLS records.
class TlsRecordFilter : public PacketFilter {
public:
- TlsRecordFilter() : agent_(nullptr), count_(0), cipher_spec_() {}
+ TlsRecordFilter()
+ : agent_(nullptr),
+ count_(0),
+ cipher_spec_(),
+ dropped_record_(false),
+ in_sequence_number_(0),
+ out_sequence_number_(0) {}
void SetAgent(const TlsAgent* agent) { agent_ = agent; }
const TlsAgent* agent() const { return agent_; }
@@ -115,14 +129,21 @@ class TlsRecordFilter : public PacketFilter {
const TlsAgent* agent_;
size_t count_;
std::unique_ptr<TlsCipherSpec> cipher_spec_;
+ // Whether we dropped a record since the cipher spec changed.
+ bool dropped_record_;
+ // The sequence number we use for reading records as they are written.
+ uint64_t in_sequence_number_;
+ // The sequence number we use for writing modified records.
+ uint64_t out_sequence_number_;
};
-inline std::ostream& operator<<(std::ostream& stream, TlsVersioned v) {
+inline std::ostream& operator<<(std::ostream& stream, const TlsVersioned& v) {
v.WriteStream(stream);
return stream;
}
-inline std::ostream& operator<<(std::ostream& stream, TlsRecordHeader& hdr) {
+inline std::ostream& operator<<(std::ostream& stream,
+ const TlsRecordHeader& hdr) {
hdr.WriteStream(stream);
stream << ' ';
switch (hdr.content_type()) {
@@ -133,13 +154,17 @@ inline std::ostream& operator<<(std::ostream& stream, TlsRecordHeader& hdr) {
stream << "Alert";
break;
case kTlsHandshakeType:
+ case kTlsAltHandshakeType:
stream << "Handshake";
break;
case kTlsApplicationDataType:
stream << "Data";
break;
+ case kTlsAckType:
+ stream << "ACK";
+ break;
default:
- stream << '<' << hdr.content_type() << '>';
+ stream << '<' << static_cast<int>(hdr.content_type()) << '>';
break;
}
return stream << ' ' << std::hex << hdr.sequence_number() << std::dec;
@@ -150,7 +175,16 @@ inline std::ostream& operator<<(std::ostream& stream, TlsRecordHeader& hdr) {
// records and that they don't span records or anything crazy like that.
class TlsHandshakeFilter : public TlsRecordFilter {
public:
- TlsHandshakeFilter() {}
+ TlsHandshakeFilter() : handshake_types_(), preceding_fragment_() {}
+ TlsHandshakeFilter(const std::set<uint8_t>& types)
+ : handshake_types_(types), preceding_fragment_() {}
+
+ // This filter can be set to be selective based on handshake message type. If
+ // this function isn't used (or the set is empty), then all handshake messages
+ // will be filtered.
+ void SetHandshakeTypes(const std::set<uint8_t>& types) {
+ handshake_types_ = types;
+ }
class HandshakeHeader : public TlsVersioned {
public:
@@ -158,7 +192,8 @@ class TlsHandshakeFilter : public TlsRecordFilter {
uint8_t handshake_type() const { return handshake_type_; }
bool Parse(TlsParser* parser, const TlsRecordHeader& record_header,
- DataBuffer* body);
+ const DataBuffer& preceding_fragment, DataBuffer* body,
+ bool* complete);
size_t Write(DataBuffer* buffer, size_t offset,
const DataBuffer& body) const;
size_t WriteFragment(DataBuffer* buffer, size_t offset,
@@ -169,7 +204,8 @@ class TlsHandshakeFilter : public TlsRecordFilter {
// Reads the length from the record header.
// This also reads the DTLS fragment information and checks it.
bool ReadLength(TlsParser* parser, const TlsRecordHeader& header,
- uint32_t* length);
+ uint32_t expected_offset, uint32_t* length,
+ bool* last_fragment);
uint8_t handshake_type_;
uint16_t message_seq_;
@@ -185,22 +221,30 @@ class TlsHandshakeFilter : public TlsRecordFilter {
DataBuffer* output) = 0;
private:
+ bool IsFilteredType(const HandshakeHeader& header,
+ const DataBuffer& handshake);
+
+ std::set<uint8_t> handshake_types_;
+ DataBuffer preceding_fragment_;
};
// Make a copy of the first instance of a handshake message.
class TlsInspectorRecordHandshakeMessage : public TlsHandshakeFilter {
public:
TlsInspectorRecordHandshakeMessage(uint8_t handshake_type)
- : handshake_type_(handshake_type), buffer_() {}
+ : TlsHandshakeFilter({handshake_type}), buffer_() {}
+ TlsInspectorRecordHandshakeMessage(const std::set<uint8_t>& handshake_types)
+ : TlsHandshakeFilter(handshake_types), buffer_() {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output);
+ void Reset() { buffer_.Truncate(0); }
+
const DataBuffer& buffer() const { return buffer_; }
private:
- uint8_t handshake_type_;
DataBuffer buffer_;
};
@@ -209,17 +253,39 @@ class TlsInspectorReplaceHandshakeMessage : public TlsHandshakeFilter {
public:
TlsInspectorReplaceHandshakeMessage(uint8_t handshake_type,
const DataBuffer& replacement)
- : handshake_type_(handshake_type), buffer_(replacement) {}
+ : TlsHandshakeFilter({handshake_type}), buffer_(replacement) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output);
private:
- uint8_t handshake_type_;
DataBuffer buffer_;
};
+// Make a copy of each record of a given type.
+class TlsRecordRecorder : public TlsRecordFilter {
+ public:
+ TlsRecordRecorder(uint8_t ct) : filter_(true), ct_(ct), records_() {}
+ TlsRecordRecorder()
+ : filter_(false),
+ ct_(content_handshake), // dummy (<optional> is C++14)
+ records_() {}
+ virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output);
+
+ size_t count() const { return records_.size(); }
+ void Clear() { records_.clear(); }
+
+ const TlsRecord& record(size_t i) const { return records_[i]; }
+
+ private:
+ bool filter_;
+ uint8_t ct_;
+ std::vector<TlsRecord> records_;
+};
+
// Make a copy of the complete conversation.
class TlsConversationRecorder : public TlsRecordFilter {
public:
@@ -230,15 +296,31 @@ class TlsConversationRecorder : public TlsRecordFilter {
DataBuffer* output);
private:
- DataBuffer& buffer_;
+ DataBuffer buffer_;
};
+// Make a copy of the records
+class TlsHeaderRecorder : public TlsRecordFilter {
+ public:
+ virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output);
+ const TlsRecordHeader* header(size_t index);
+
+ private:
+ std::vector<TlsRecordHeader> headers_;
+};
+
+typedef std::initializer_list<std::shared_ptr<PacketFilter>>
+ ChainedPacketFilterInit;
+
// Runs multiple packet filters in series.
class ChainedPacketFilter : public PacketFilter {
public:
ChainedPacketFilter() {}
ChainedPacketFilter(const std::vector<std::shared_ptr<PacketFilter>> filters)
: filters_(filters.begin(), filters.end()) {}
+ ChainedPacketFilter(ChainedPacketFilterInit il) : filters_(il) {}
virtual ~ChainedPacketFilter() {}
virtual PacketFilter::Action Filter(const DataBuffer& input,
@@ -256,13 +338,13 @@ typedef std::function<bool(TlsParser* parser, const TlsVersioned& header)>
class TlsExtensionFilter : public TlsHandshakeFilter {
public:
- TlsExtensionFilter() : handshake_types_() {
- handshake_types_.insert(kTlsHandshakeClientHello);
- handshake_types_.insert(kTlsHandshakeServerHello);
- }
+ TlsExtensionFilter()
+ : TlsHandshakeFilter({kTlsHandshakeClientHello, kTlsHandshakeServerHello,
+ kTlsHandshakeHelloRetryRequest,
+ kTlsHandshakeEncryptedExtensions}) {}
TlsExtensionFilter(const std::set<uint8_t>& types)
- : handshake_types_(types) {}
+ : TlsHandshakeFilter(types) {}
static bool FindExtensions(TlsParser* parser, const HandshakeHeader& header);
@@ -279,8 +361,6 @@ class TlsExtensionFilter : public TlsHandshakeFilter {
PacketFilter::Action FilterExtensions(TlsParser* parser,
const DataBuffer& input,
DataBuffer* output);
-
- std::set<uint8_t> handshake_types_;
};
class TlsExtensionCapture : public TlsExtensionFilter {
@@ -326,6 +406,21 @@ class TlsExtensionDropper : public TlsExtensionFilter {
uint16_t extension_;
};
+class TlsExtensionInjector : public TlsHandshakeFilter {
+ public:
+ TlsExtensionInjector(uint16_t ext, const DataBuffer& data)
+ : extension_(ext), data_(data) {}
+
+ protected:
+ PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output) override;
+
+ private:
+ const uint16_t extension_;
+ const DataBuffer data_;
+};
+
class TlsAgent;
typedef std::function<void(void)> VoidFunction;
@@ -352,7 +447,7 @@ class AfterRecordN : public TlsRecordFilter {
class TlsInspectorClientHelloVersionChanger : public TlsHandshakeFilter {
public:
TlsInspectorClientHelloVersionChanger(std::shared_ptr<TlsAgent>& server)
- : server_(server) {}
+ : TlsHandshakeFilter({kTlsHandshakeClientKeyExchange}), server_(server) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
@@ -377,10 +472,47 @@ class SelectiveDropFilter : public PacketFilter {
uint8_t counter_;
};
+// This class selectively drops complete records. The difference from
+// SelectiveDropFilter is that if multiple DTLS records are in the same
+// datagram, we just drop one.
+class SelectiveRecordDropFilter : public TlsRecordFilter {
+ public:
+ SelectiveRecordDropFilter(uint32_t pattern, bool enabled = true)
+ : pattern_(pattern), counter_(0) {
+ if (!enabled) {
+ Disable();
+ }
+ }
+ SelectiveRecordDropFilter(std::initializer_list<size_t> records)
+ : SelectiveRecordDropFilter(ToPattern(records), true) {}
+
+ void Reset(uint32_t pattern) {
+ counter_ = 0;
+ PacketFilter::Enable();
+ pattern_ = pattern;
+ }
+
+ void Reset(std::initializer_list<size_t> records) {
+ Reset(ToPattern(records));
+ }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& data,
+ DataBuffer* changed) override;
+
+ private:
+ static uint32_t ToPattern(std::initializer_list<size_t> records);
+
+ uint32_t pattern_;
+ uint8_t counter_;
+};
+
// Set the version number in the ClientHello.
class TlsInspectorClientHelloVersionSetter : public TlsHandshakeFilter {
public:
- TlsInspectorClientHelloVersionSetter(uint16_t version) : version_(version) {}
+ TlsInspectorClientHelloVersionSetter(uint16_t version)
+ : TlsHandshakeFilter({kTlsHandshakeClientHello}), version_(version) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
@@ -411,6 +543,20 @@ class TlsLastByteDamager : public TlsHandshakeFilter {
uint8_t type_;
};
+class SelectedCipherSuiteReplacer : public TlsHandshakeFilter {
+ public:
+ SelectedCipherSuiteReplacer(uint16_t suite)
+ : TlsHandshakeFilter({kTlsHandshakeServerHello}), cipher_suite_(suite) {}
+
+ protected:
+ PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output) override;
+
+ private:
+ uint16_t cipher_suite_;
+};
+
} // namespace nss_test
#endif
diff --git a/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc b/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc
index 51ff938b1..45f6cf2bd 100644
--- a/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc
@@ -241,13 +241,13 @@ TEST_P(TlsHkdfTest, HkdfExpandLabel) {
{/* ssl_hash_md5 */},
{/* ssl_hash_sha1 */},
{/* ssl_hash_sha224 */},
- {0x34, 0x7c, 0x67, 0x80, 0xff, 0x0b, 0xba, 0xd7, 0x1c, 0x28, 0x3b,
- 0x16, 0xeb, 0x2f, 0x9c, 0xf6, 0x2d, 0x24, 0xe6, 0xcd, 0xb6, 0x13,
- 0xd5, 0x17, 0x76, 0x54, 0x8c, 0xb0, 0x7d, 0xcd, 0xe7, 0x4c},
- {0x4b, 0x1e, 0x5e, 0xc1, 0x49, 0x30, 0x78, 0xea, 0x35, 0xbd, 0x3f, 0x01,
- 0x04, 0xe6, 0x1a, 0xea, 0x14, 0xcc, 0x18, 0x2a, 0xd1, 0xc4, 0x76, 0x21,
- 0xc4, 0x64, 0xc0, 0x4e, 0x4b, 0x36, 0x16, 0x05, 0x6f, 0x04, 0xab, 0xe9,
- 0x43, 0xb1, 0x2d, 0xa8, 0xa7, 0x17, 0x9a, 0x5f, 0x09, 0x91, 0x7d, 0x1f}};
+ {0xc6, 0xdd, 0x6e, 0xc4, 0x76, 0xb8, 0x55, 0xf2, 0xa4, 0xfc, 0x59,
+ 0x04, 0xa4, 0x90, 0xdc, 0xa7, 0xa7, 0x0d, 0x94, 0x8f, 0xc2, 0xdc,
+ 0x15, 0x6d, 0x48, 0x93, 0x9d, 0x05, 0xbb, 0x9a, 0xbc, 0xc1},
+ {0x41, 0xea, 0x77, 0x09, 0x8c, 0x90, 0x04, 0x10, 0xec, 0xbc, 0x37, 0xd8,
+ 0x5b, 0x54, 0xcd, 0x7b, 0x08, 0x15, 0x13, 0x20, 0xed, 0x1e, 0x3f, 0x54,
+ 0x74, 0xf7, 0x8b, 0x06, 0x38, 0x28, 0x06, 0x37, 0x75, 0x23, 0xa2, 0xb7,
+ 0x34, 0xb1, 0x72, 0x2e, 0x59, 0x6d, 0x5a, 0x31, 0xf5, 0x53, 0xab, 0x99}};
const DataBuffer expected_data(tv[hash_type_], kHashLength[hash_type_]);
HkdfExpandLabel(&k1_, hash_type_, kSessionHash, kHashLength[hash_type_],
diff --git a/security/nss/gtests/ssl_gtest/tls_protect.cc b/security/nss/gtests/ssl_gtest/tls_protect.cc
index efcd89e14..6c945f66e 100644
--- a/security/nss/gtests/ssl_gtest/tls_protect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_protect.cc
@@ -32,7 +32,6 @@ void AeadCipher::FormatNonce(uint64_t seq, uint8_t *nonce) {
}
DataBuffer d(nonce, 12);
- std::cerr << "Nonce " << d << std::endl;
}
bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
@@ -92,8 +91,9 @@ bool AeadCipherChacha20Poly1305::Aead(bool decrypt, uint64_t seq,
in, inlen, out, outlen, maxlen);
}
-bool TlsCipherSpec::Init(SSLCipherAlgorithm cipher, PK11SymKey *key,
- const uint8_t *iv) {
+bool TlsCipherSpec::Init(uint16_t epoch, SSLCipherAlgorithm cipher,
+ PK11SymKey *key, const uint8_t *iv) {
+ epoch_ = epoch;
switch (cipher) {
case ssl_calg_aes_gcm:
aead_.reset(new AeadCipherAesGcm());
diff --git a/security/nss/gtests/ssl_gtest/tls_protect.h b/security/nss/gtests/ssl_gtest/tls_protect.h
index 4efbd6e6b..93ffd6322 100644
--- a/security/nss/gtests/ssl_gtest/tls_protect.h
+++ b/security/nss/gtests/ssl_gtest/tls_protect.h
@@ -20,7 +20,7 @@ class TlsRecordHeader;
class AeadCipher {
public:
AeadCipher(CK_MECHANISM_TYPE mech) : mech_(mech), key_(nullptr) {}
- ~AeadCipher();
+ virtual ~AeadCipher();
bool Init(PK11SymKey *key, const uint8_t *iv);
virtual bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
@@ -58,16 +58,19 @@ class AeadCipherAesGcm : public AeadCipher {
// Our analog of ssl3CipherSpec
class TlsCipherSpec {
public:
- TlsCipherSpec() : aead_() {}
+ TlsCipherSpec() : epoch_(0), aead_() {}
- bool Init(SSLCipherAlgorithm cipher, PK11SymKey *key, const uint8_t *iv);
+ bool Init(uint16_t epoch, SSLCipherAlgorithm cipher, PK11SymKey *key,
+ const uint8_t *iv);
bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
DataBuffer *ciphertext);
bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
DataBuffer *plaintext);
+ uint16_t epoch() const { return epoch_; }
private:
+ uint16_t epoch_;
std::unique_ptr<AeadCipher> aead_;
};
diff --git a/security/nss/gtests/util_gtest/manifest.mn b/security/nss/gtests/util_gtest/manifest.mn
index edede657f..a90e8431e 100644
--- a/security/nss/gtests/util_gtest/manifest.mn
+++ b/security/nss/gtests/util_gtest/manifest.mn
@@ -10,6 +10,8 @@ CPPSRCS = \
util_utf8_unittest.cc \
util_b64_unittest.cc \
util_pkcs11uri_unittest.cc \
+ util_aligned_malloc_unittest.cc \
+ util_memcmpzero_unittest.cc \
$(NULL)
INCLUDES += \
diff --git a/security/nss/gtests/util_gtest/util_aligned_malloc_unittest.cc b/security/nss/gtests/util_gtest/util_aligned_malloc_unittest.cc
new file mode 100644
index 000000000..9745ca7d3
--- /dev/null
+++ b/security/nss/gtests/util_gtest/util_aligned_malloc_unittest.cc
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs_util.h"
+
+namespace nss_test {
+
+struct SomeContext {
+ uint8_t some_buf[13];
+ void *mem;
+};
+
+template <class T>
+struct ScopedDelete {
+ void operator()(T *ptr) {
+ if (ptr) {
+ PORT_Free(ptr->mem);
+ }
+ }
+};
+typedef std::unique_ptr<SomeContext, ScopedDelete<SomeContext> >
+ ScopedSomeContext;
+
+class AlignedMallocTest : public ::testing::Test,
+ public ::testing::WithParamInterface<size_t> {
+ protected:
+ ScopedSomeContext test_align_new(size_t alignment) {
+ ScopedSomeContext ctx(PORT_ZNewAligned(SomeContext, alignment, mem));
+ return ctx;
+ };
+ ScopedSomeContext test_align_alloc(size_t alignment) {
+ void *mem = nullptr;
+ ScopedSomeContext ctx((SomeContext *)PORT_ZAllocAligned(sizeof(SomeContext),
+ alignment, &mem));
+ if (ctx) {
+ ctx->mem = mem;
+ }
+ return ctx;
+ }
+};
+
+TEST_P(AlignedMallocTest, TestNew) {
+ size_t alignment = GetParam();
+ ScopedSomeContext ctx = test_align_new(alignment);
+ EXPECT_TRUE(ctx.get());
+ EXPECT_EQ(0U, (uintptr_t)ctx.get() % alignment);
+}
+
+TEST_P(AlignedMallocTest, TestAlloc) {
+ size_t alignment = GetParam();
+ ScopedSomeContext ctx = test_align_alloc(alignment);
+ EXPECT_TRUE(ctx.get());
+ EXPECT_EQ(0U, (uintptr_t)ctx.get() % alignment);
+}
+
+class AlignedMallocTestBadSize : public AlignedMallocTest {};
+
+TEST_P(AlignedMallocTestBadSize, TestNew) {
+ size_t alignment = GetParam();
+ ScopedSomeContext ctx = test_align_new(alignment);
+ EXPECT_FALSE(ctx.get());
+}
+
+TEST_P(AlignedMallocTestBadSize, TestAlloc) {
+ size_t alignment = GetParam();
+ ScopedSomeContext ctx = test_align_alloc(alignment);
+ EXPECT_FALSE(ctx.get());
+}
+
+static const size_t kSizes[] = {1, 2, 4, 8, 16, 32, 64};
+static const size_t kBadSizes[] = {0, 7, 17, 24, 56};
+
+INSTANTIATE_TEST_CASE_P(AllAligned, AlignedMallocTest,
+ ::testing::ValuesIn(kSizes));
+INSTANTIATE_TEST_CASE_P(AllAlignedBadSize, AlignedMallocTestBadSize,
+ ::testing::ValuesIn(kBadSizes));
+
+} // namespace nss_test
diff --git a/security/nss/gtests/util_gtest/util_gtest.gyp b/security/nss/gtests/util_gtest/util_gtest.gyp
index 7abd71b2f..1c54329b2 100644
--- a/security/nss/gtests/util_gtest/util_gtest.gyp
+++ b/security/nss/gtests/util_gtest/util_gtest.gyp
@@ -14,6 +14,8 @@
'util_utf8_unittest.cc',
'util_b64_unittest.cc',
'util_pkcs11uri_unittest.cc',
+ 'util_aligned_malloc_unittest.cc',
+ 'util_memcmpzero_unittest.cc',
'<(DEPTH)/gtests/common/gtests.cc',
],
'dependencies': [
diff --git a/security/nss/gtests/util_gtest/util_memcmpzero_unittest.cc b/security/nss/gtests/util_gtest/util_memcmpzero_unittest.cc
new file mode 100644
index 000000000..29cac3f67
--- /dev/null
+++ b/security/nss/gtests/util_gtest/util_memcmpzero_unittest.cc
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs_util.h"
+
+namespace nss_test {
+
+class MemcmpZeroTest : public ::testing::Test {
+ protected:
+ unsigned int test_memcmp_zero(const std::vector<uint8_t> &mem) {
+ return NSS_SecureMemcmpZero(mem.data(), mem.size());
+ };
+};
+
+TEST_F(MemcmpZeroTest, TestMemcmpZeroTrue) {
+ unsigned int rv = test_memcmp_zero(std::vector<uint8_t>(37, 0));
+ EXPECT_EQ(0U, rv);
+}
+
+TEST_F(MemcmpZeroTest, TestMemcmpZeroFalse5) {
+ std::vector<uint8_t> vec(37, 0);
+ vec[5] = 1;
+ unsigned int rv = test_memcmp_zero(vec);
+ EXPECT_NE(0U, rv);
+}
+
+TEST_F(MemcmpZeroTest, TestMemcmpZeroFalse37) {
+ std::vector<uint8_t> vec(37, 0);
+ vec[vec.size() - 1] = 0xFF;
+ unsigned int rv = test_memcmp_zero(vec);
+ EXPECT_NE(0U, rv);
+}
+
+TEST_F(MemcmpZeroTest, TestMemcmpZeroFalse0) {
+ std::vector<uint8_t> vec(37, 0);
+ vec[0] = 1;
+ unsigned int rv = test_memcmp_zero(vec);
+ EXPECT_NE(0U, rv);
+}
+
+} // namespace nss_test
diff --git a/security/nss/help.txt b/security/nss/help.txt
new file mode 100644
index 000000000..03ed36e6c
--- /dev/null
+++ b/security/nss/help.txt
@@ -0,0 +1,48 @@
+Usage: build.sh [-hcv] [-cc] [-j <n>] [--nspr] [--gyp|-g] [--opt|-o] [-m32]
+ [--test] [--pprof] [--scan-build[=output]] [--ct-verif]
+ [--asan] [--ubsan] [--msan] [--sancov[=edge|bb|func|...]]
+ [--disable-tests] [--fuzz[=tls|oss]] [--system-sqlite]
+ [--no-zdefs] [--with-nspr] [--system-nspr] [--enable-libpkix]
+ [--enable-fips]
+
+This script builds NSS with gyp and ninja.
+
+This build system is still under development. It does not yet support all
+the features or platforms that NSS supports.
+
+NSS build tool options:
+
+ -h display this help and exit
+ -c clean before build
+ -cc clean without building
+ -v verbose build
+ -j <n> run at most <n> concurrent jobs
+ --nspr force a rebuild of NSPR
+ --gyp|-g force a rerun of gyp
+ --opt|-o do an opt build
+ -m32 do a 32-bit build on a 64-bit system
+ --clang build with clang and clang++
+ --gcc build with gcc and g++
+ --test ignore map files and export everything we have
+ --fuzz build fuzzing targets (this always enables test builds)
+ --fuzz=tls to enable TLS fuzzing mode
+ --fuzz=oss to build for OSS-Fuzz
+ --pprof build with gperftool support
+ --ct-verif build with valgrind for ct-verif
+ --scan-build run the build with scan-build (scan-build has to be in the path)
+ --scan-build=/out/path sets the output path for scan-build
+ --asan do an asan build
+ --ubsan do an ubsan build
+ --ubsan=bool,shift,... sets specific UB sanitizers
+ --msan do an msan build
+ --sancov do sanitize coverage builds
+ --sancov=func sets coverage to function level for example
+ --disable-tests don't build tests and corresponding cmdline utils
+ --system-sqlite use system sqlite
+ --no-zdefs don't set -Wl,-z,defs
+ --with-nspr don't build NSPR but use the one at the given location, e.g.
+ --with-nspr=/path/to/nspr/include:/path/to/nspr/lib
+ --system-nspr use system nspr. This requires an installation of NSPR and
+ might not work on all systems.
+ --enable-libpkix make libpkix part of the build.
+ --enable-fips don't disable FIPS checks.
diff --git a/security/nss/lib/certdb/alg1485.c b/security/nss/lib/certdb/alg1485.c
index 38b2fe4b5..9a69c5bc5 100644
--- a/security/nss/lib/certdb/alg1485.c
+++ b/security/nss/lib/certdb/alg1485.c
@@ -703,14 +703,19 @@ CERT_GetOidString(const SECItem* oid)
return NULL;
}
+ /* If the OID has length 1, we bail. */
+ if (oid->len < 2) {
+ return NULL;
+ }
+
/* first will point to the next sequence of bytes to decode */
first = (PRUint8*)oid->data;
/* stop points to one past the legitimate data */
stop = &first[oid->len];
/*
- * Check for our pseudo-encoded single-digit OIDs
- */
+ * Check for our pseudo-encoded single-digit OIDs
+ */
if ((*first == 0x80) && (2 == oid->len)) {
/* Funky encoding. The second byte is the number */
rvString = PR_smprintf("%lu", (PRUint32)first[1]);
@@ -728,6 +733,10 @@ CERT_GetOidString(const SECItem* oid)
break;
}
}
+ /* There's no first bit set, so this isn't valid. Bail.*/
+ if (last == stop) {
+ goto unsupported;
+ }
bytesBeforeLast = (unsigned int)(last - first);
if (bytesBeforeLast <= 3U) { /* 0-28 bit number */
PRUint32 n = 0;
@@ -748,12 +757,12 @@ CERT_GetOidString(const SECItem* oid)
CASE(2, 0x7f);
CASE(1, 0x7f);
case 0:
- n |=
- last[0] & 0x7f;
+ n |= last[0] & 0x7f;
break;
}
- if (last[0] & 0x80)
+ if (last[0] & 0x80) {
goto unsupported;
+ }
if (!rvString) {
/* This is the first number.. decompose it */
@@ -1305,8 +1314,7 @@ CERT_GetCertificateEmailAddress(CERTCertificate* cert)
}
} else if (current->type == certRFC822Name) {
rawEmailAddr =
- (char*)PORT_ArenaZAlloc(cert->arena, current->name.other.len +
- 1);
+ (char*)PORT_ArenaZAlloc(cert->arena, current->name.other.len + 1);
if (!rawEmailAddr) {
goto finish;
}
diff --git a/security/nss/lib/certdb/cert.h b/security/nss/lib/certdb/cert.h
index 4224da108..c76a5a9b0 100644
--- a/security/nss/lib/certdb/cert.h
+++ b/security/nss/lib/certdb/cert.h
@@ -504,6 +504,8 @@ extern CERTCertificate *CERT_FindCertByKeyID(CERTCertDBHandle *handle,
*/
extern CERTCertificate *CERT_FindCertByIssuerAndSN(
CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN);
+extern CERTCertificate *CERT_FindCertByIssuerAndSNCX(
+ CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN, void *wincx);
/*
** Find a certificate in the database by a subject key ID
@@ -547,6 +549,9 @@ CERTCertificate *CERT_FindCertByEmailAddr(CERTCertDBHandle *handle,
*/
CERTCertificate *CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle,
const char *name);
+CERTCertificate *CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle,
+ const char *name,
+ void *wincx);
/*
** Find a certificate in the database by a email address or nickname
@@ -555,6 +560,9 @@ CERTCertificate *CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle,
*/
CERTCertificate *CERT_FindCertByNicknameOrEmailAddrForUsage(
CERTCertDBHandle *handle, const char *name, SECCertUsage lookingForUsage);
+CERTCertificate *CERT_FindCertByNicknameOrEmailAddrForUsageCX(
+ CERTCertDBHandle *handle, const char *name, SECCertUsage lookingForUsage,
+ void *wincx);
/*
** Find a certificate in the database by a digest of a subject public key
diff --git a/security/nss/lib/certdb/certdb.c b/security/nss/lib/certdb/certdb.c
index 7864edc08..1a676a720 100644
--- a/security/nss/lib/certdb/certdb.c
+++ b/security/nss/lib/certdb/certdb.c
@@ -1192,6 +1192,7 @@ CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage)
case rsaKey:
requiredUsage |= KU_KEY_ENCIPHERMENT;
break;
+ case rsaPssKey:
case dsaKey:
requiredUsage |= KU_DIGITAL_SIGNATURE;
break;
diff --git a/security/nss/lib/certdb/crl.c b/security/nss/lib/certdb/crl.c
index 87469085e..d1c48dfba 100644
--- a/security/nss/lib/certdb/crl.c
+++ b/security/nss/lib/certdb/crl.c
@@ -1294,8 +1294,7 @@ DPCache_AddCRL(CRLDPCache* cache, CachedCrl* newcrl, PRBool* added)
}
}
- newcrls = (CachedCrl**)PORT_Realloc(cache->crls, (cache->ncrls + 1) *
- sizeof(CachedCrl*));
+ newcrls = (CachedCrl**)PORT_Realloc(cache->crls, (cache->ncrls + 1) * sizeof(CachedCrl*));
if (!newcrls) {
return SECFailure;
}
diff --git a/security/nss/lib/certdb/stanpcertdb.c b/security/nss/lib/certdb/stanpcertdb.c
index 4d42bd50d..beaa66040 100644
--- a/security/nss/lib/certdb/stanpcertdb.c
+++ b/security/nss/lib/certdb/stanpcertdb.c
@@ -457,15 +457,15 @@ __CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
return CERT_NewTempCertificate(handle, derCert, nickname, isperm, copyDER);
}
-/* maybe all the wincx's should be some const for internal token login? */
-CERTCertificate *
-CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
- CERTIssuerAndSN *issuerAndSN)
+static CERTCertificate *
+common_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
+ CERTIssuerAndSN *issuerAndSN,
+ void *wincx)
{
PK11SlotInfo *slot;
CERTCertificate *cert;
- cert = PK11_FindCertByIssuerAndSN(&slot, issuerAndSN, NULL);
+ cert = PK11_FindCertByIssuerAndSN(&slot, issuerAndSN, wincx);
if (cert && slot) {
PK11_FreeSlot(slot);
}
@@ -473,6 +473,23 @@ CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
return cert;
}
+/* maybe all the wincx's should be some const for internal token login? */
+CERTCertificate *
+CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
+ CERTIssuerAndSN *issuerAndSN)
+{
+ return common_FindCertByIssuerAndSN(handle, issuerAndSN, NULL);
+}
+
+/* maybe all the wincx's should be some const for internal token login? */
+CERTCertificate *
+CERT_FindCertByIssuerAndSNCX(CERTCertDBHandle *handle,
+ CERTIssuerAndSN *issuerAndSN,
+ void *wincx)
+{
+ return common_FindCertByIssuerAndSN(handle, issuerAndSN, wincx);
+}
+
static NSSCertificate *
get_best_temp_or_perm(NSSCertificate *ct, NSSCertificate *cp)
{
@@ -587,7 +604,8 @@ CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert)
static CERTCertificate *
common_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
const char *name, PRBool anyUsage,
- SECCertUsage lookingForUsage)
+ SECCertUsage lookingForUsage,
+ void *wincx)
{
NSSCryptoContext *cc;
NSSCertificate *c, *ct;
@@ -620,7 +638,7 @@ common_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
}
if (anyUsage) {
- cert = PK11_FindCertFromNickname(name, NULL);
+ cert = PK11_FindCertFromNickname(name, wincx);
} else {
if (ct) {
/* Does ct really have the required usage? */
@@ -632,7 +650,7 @@ common_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
}
}
- certlist = PK11_FindCertsFromNickname(name, NULL);
+ certlist = PK11_FindCertsFromNickname(name, wincx);
if (certlist) {
SECStatus rv =
CERT_FilterCertListByUsage(certlist, lookingForUsage, PR_FALSE);
@@ -659,7 +677,15 @@ CERTCertificate *
CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, const char *name)
{
return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE,
- 0);
+ 0, NULL);
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle, const char *name,
+ void *wincx)
+{
+ return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE,
+ 0, wincx);
}
CERTCertificate *
@@ -668,7 +694,17 @@ CERT_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
SECCertUsage lookingForUsage)
{
return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE,
- lookingForUsage);
+ lookingForUsage, NULL);
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddrForUsageCX(CERTCertDBHandle *handle,
+ const char *name,
+ SECCertUsage lookingForUsage,
+ void *wincx)
+{
+ return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE,
+ lookingForUsage, wincx);
}
static void
diff --git a/security/nss/lib/ckfw/builtins/certdata.txt b/security/nss/lib/ckfw/builtins/certdata.txt
index 45b659b7a..5d2baf3a5 100644
--- a/security/nss/lib/ckfw/builtins/certdata.txt
+++ b/security/nss/lib/ckfw/builtins/certdata.txt
@@ -69,34 +69,6 @@ CKA_PRIVATE CK_BBOOL CK_FALSE
CKA_MODIFIABLE CK_BBOOL CK_FALSE
CKA_LABEL UTF8 "Mozilla Builtin Roots"
-# Distrust "Distrust a pb.com certificate that does not comply with the baseline requirements."
-# Issuer: OU=Equifax Secure Certificate Authority,O=Equifax,C=US
-# Serial Number: 1407252 (0x157914)
-# Subject: CN=*.pb.com,OU=Meters,O=Pitney Bowes,L=Danbury,ST=Connecticut,C=US
-# Not Valid Before: Mon Feb 01 14:54:04 2010
-# Not Valid After : Tue Sep 30 00:00:00 2014
-# Fingerprint (MD5): 8F:46:BE:99:47:6F:93:DC:5C:01:54:50:D0:4A:BD:AC
-# Fingerprint (SHA1): 30:F1:82:CA:1A:5E:4E:4F:F3:6E:D0:E6:38:18:B8:B9:41:CB:5F:8C
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Distrust a pb.com certificate that does not comply with the baseline requirements."
-CKA_ISSUER MULTILINE_OCTAL
-\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\020\060\016\006\003\125\004\012\023\007\105\161\165\151\146\141
-\170\061\055\060\053\006\003\125\004\013\023\044\105\161\165\151
-\146\141\170\040\123\145\143\165\162\145\040\103\145\162\164\151
-\146\151\143\141\164\145\040\101\165\164\150\157\162\151\164\171
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\003\025\171\024
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
#
# Certificate "GlobalSign Root CA"
#
@@ -2426,7 +2398,7 @@ END
CKA_SERIAL_NUMBER MULTILINE_OCTAL
\002\003\001\000\040
END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
@@ -3684,7 +3656,7 @@ END
CKA_SERIAL_NUMBER MULTILINE_OCTAL
\002\001\000
END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
@@ -3843,7 +3815,7 @@ END
CKA_SERIAL_NUMBER MULTILINE_OCTAL
\002\001\000
END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
@@ -4293,213 +4265,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "StartCom Certification Authority"
-#
-# Issuer: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Serial Number: 1 (0x1)
-# Subject: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Not Valid Before: Sun Sep 17 19:46:36 2006
-# Not Valid After : Wed Sep 17 19:46:36 2036
-# Fingerprint (MD5): 22:4D:8F:8A:FC:F7:35:C2:BB:57:34:90:7B:8B:22:16
-# Fingerprint (SHA1): 3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "StartCom Certification Authority"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013
-\023\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154
-\040\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147
-\156\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123
-\164\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143
-\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013
-\023\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154
-\040\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147
-\156\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123
-\164\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143
-\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\001
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\007\311\060\202\005\261\240\003\002\001\002\002\001\001
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061\026
-\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103\157
-\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013\023
-\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154\040
-\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147\156
-\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123\164
-\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143\141
-\164\151\157\156\040\101\165\164\150\157\162\151\164\171\060\036
-\027\015\060\066\060\071\061\067\061\071\064\066\063\066\132\027
-\015\063\066\060\071\061\067\061\071\064\066\063\066\132\060\175
-\061\013\060\011\006\003\125\004\006\023\002\111\114\061\026\060
-\024\006\003\125\004\012\023\015\123\164\141\162\164\103\157\155
-\040\114\164\144\056\061\053\060\051\006\003\125\004\013\023\042
-\123\145\143\165\162\145\040\104\151\147\151\164\141\154\040\103
-\145\162\164\151\146\151\143\141\164\145\040\123\151\147\156\151
-\156\147\061\051\060\047\006\003\125\004\003\023\040\123\164\141
-\162\164\103\157\155\040\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\060\202\002
-\042\060\015\006\011\052\206\110\206\367\015\001\001\001\005\000
-\003\202\002\017\000\060\202\002\012\002\202\002\001\000\301\210
-\333\011\274\154\106\174\170\237\225\173\265\063\220\362\162\142
-\326\301\066\040\042\044\136\316\351\167\362\103\012\242\006\144
-\244\314\216\066\370\070\346\043\360\156\155\261\074\335\162\243
-\205\034\241\323\075\264\063\053\323\057\257\376\352\260\101\131
-\147\266\304\006\175\012\236\164\205\326\171\114\200\067\172\337
-\071\005\122\131\367\364\033\106\103\244\322\205\205\322\303\161
-\363\165\142\064\272\054\212\177\036\217\356\355\064\320\021\307
-\226\315\122\075\272\063\326\335\115\336\013\073\112\113\237\302
-\046\057\372\265\026\034\162\065\167\312\074\135\346\312\341\046
-\213\032\066\166\134\001\333\164\024\045\376\355\265\240\210\017
-\335\170\312\055\037\007\227\060\001\055\162\171\372\106\326\023
-\052\250\271\246\253\203\111\035\345\362\357\335\344\001\216\030
-\012\217\143\123\026\205\142\251\016\031\072\314\265\146\246\302
-\153\164\007\344\053\341\166\076\264\155\330\366\104\341\163\142
-\037\073\304\276\240\123\126\045\154\121\011\367\252\253\312\277
-\166\375\155\233\363\235\333\277\075\146\274\014\126\252\257\230
-\110\225\072\113\337\247\130\120\331\070\165\251\133\352\103\014
-\002\377\231\353\350\154\115\160\133\051\145\234\335\252\135\314
-\257\001\061\354\014\353\322\215\350\352\234\173\346\156\367\047
-\146\014\032\110\327\156\102\343\077\336\041\076\173\341\015\160
-\373\143\252\250\154\032\124\264\134\045\172\311\242\311\213\026
-\246\273\054\176\027\136\005\115\130\156\022\035\001\356\022\020
-\015\306\062\177\030\377\374\364\372\315\156\221\350\066\111\276
-\032\110\151\213\302\226\115\032\022\262\151\027\301\012\220\326
-\372\171\042\110\277\272\173\151\370\160\307\372\172\067\330\330
-\015\322\166\117\127\377\220\267\343\221\322\335\357\302\140\267
-\147\072\335\376\252\234\360\324\213\177\162\042\316\306\237\227
-\266\370\257\212\240\020\250\331\373\030\306\266\265\134\122\074
-\211\266\031\052\163\001\012\017\003\263\022\140\362\172\057\201
-\333\243\156\377\046\060\227\365\213\335\211\127\266\255\075\263
-\257\053\305\267\166\002\360\245\326\053\232\206\024\052\162\366
-\343\063\214\135\011\113\023\337\273\214\164\023\122\113\002\003
-\001\000\001\243\202\002\122\060\202\002\116\060\014\006\003\125
-\035\023\004\005\060\003\001\001\377\060\013\006\003\125\035\017
-\004\004\003\002\001\256\060\035\006\003\125\035\016\004\026\004
-\024\116\013\357\032\244\100\133\245\027\151\207\060\312\064\150
-\103\320\101\256\362\060\144\006\003\125\035\037\004\135\060\133
-\060\054\240\052\240\050\206\046\150\164\164\160\072\057\057\143
-\145\162\164\056\163\164\141\162\164\143\157\155\056\157\162\147
-\057\163\146\163\143\141\055\143\162\154\056\143\162\154\060\053
-\240\051\240\047\206\045\150\164\164\160\072\057\057\143\162\154
-\056\163\164\141\162\164\143\157\155\056\157\162\147\057\163\146
-\163\143\141\055\143\162\154\056\143\162\154\060\202\001\135\006
-\003\125\035\040\004\202\001\124\060\202\001\120\060\202\001\114
-\006\013\053\006\001\004\001\201\265\067\001\001\001\060\202\001
-\073\060\057\006\010\053\006\001\005\005\007\002\001\026\043\150
-\164\164\160\072\057\057\143\145\162\164\056\163\164\141\162\164
-\143\157\155\056\157\162\147\057\160\157\154\151\143\171\056\160
-\144\146\060\065\006\010\053\006\001\005\005\007\002\001\026\051
-\150\164\164\160\072\057\057\143\145\162\164\056\163\164\141\162
-\164\143\157\155\056\157\162\147\057\151\156\164\145\162\155\145
-\144\151\141\164\145\056\160\144\146\060\201\320\006\010\053\006
-\001\005\005\007\002\002\060\201\303\060\047\026\040\123\164\141
-\162\164\040\103\157\155\155\145\162\143\151\141\154\040\050\123
-\164\141\162\164\103\157\155\051\040\114\164\144\056\060\003\002
-\001\001\032\201\227\114\151\155\151\164\145\144\040\114\151\141
-\142\151\154\151\164\171\054\040\162\145\141\144\040\164\150\145
-\040\163\145\143\164\151\157\156\040\052\114\145\147\141\154\040
-\114\151\155\151\164\141\164\151\157\156\163\052\040\157\146\040
-\164\150\145\040\123\164\141\162\164\103\157\155\040\103\145\162
-\164\151\146\151\143\141\164\151\157\156\040\101\165\164\150\157
-\162\151\164\171\040\120\157\154\151\143\171\040\141\166\141\151
-\154\141\142\154\145\040\141\164\040\150\164\164\160\072\057\057
-\143\145\162\164\056\163\164\141\162\164\143\157\155\056\157\162
-\147\057\160\157\154\151\143\171\056\160\144\146\060\021\006\011
-\140\206\110\001\206\370\102\001\001\004\004\003\002\000\007\060
-\070\006\011\140\206\110\001\206\370\102\001\015\004\053\026\051
-\123\164\141\162\164\103\157\155\040\106\162\145\145\040\123\123
-\114\040\103\145\162\164\151\146\151\143\141\164\151\157\156\040
-\101\165\164\150\157\162\151\164\171\060\015\006\011\052\206\110
-\206\367\015\001\001\005\005\000\003\202\002\001\000\026\154\231
-\364\146\014\064\365\320\205\136\175\012\354\332\020\116\070\034
-\136\337\246\045\005\113\221\062\301\350\073\361\075\335\104\011
-\133\007\111\212\051\313\146\002\267\261\232\367\045\230\011\074
-\216\033\341\335\066\207\053\113\273\150\323\071\146\075\240\046
-\307\362\071\221\035\121\253\202\173\176\325\316\132\344\342\003
-\127\160\151\227\010\371\136\130\246\012\337\214\006\232\105\026
-\026\070\012\136\127\366\142\307\172\002\005\346\274\036\265\362
-\236\364\251\051\203\370\262\024\343\156\050\207\104\303\220\032
-\336\070\251\074\254\103\115\144\105\316\335\050\251\134\362\163
-\173\004\370\027\350\253\261\363\056\134\144\156\163\061\072\022
-\270\274\263\021\344\175\217\201\121\232\073\215\211\364\115\223
-\146\173\074\003\355\323\232\035\232\363\145\120\365\240\320\165
-\237\057\257\360\352\202\103\230\370\151\234\211\171\304\103\216
-\106\162\343\144\066\022\257\367\045\036\070\211\220\167\176\303
-\153\152\271\303\313\104\113\254\170\220\213\347\307\054\036\113
-\021\104\310\064\122\047\315\012\135\237\205\301\211\325\032\170
-\362\225\020\123\062\335\200\204\146\165\331\265\150\050\373\141
-\056\276\204\250\070\300\231\022\206\245\036\147\144\255\006\056
-\057\251\160\205\307\226\017\174\211\145\365\216\103\124\016\253
-\335\245\200\071\224\140\300\064\311\226\160\054\243\022\365\037
-\110\173\275\034\176\153\267\235\220\364\042\073\256\370\374\052
-\312\372\202\122\240\357\257\113\125\223\353\301\265\360\042\213
-\254\064\116\046\042\004\241\207\054\165\112\267\345\175\023\327
-\270\014\144\300\066\322\311\057\206\022\214\043\011\301\033\202
-\073\163\111\243\152\127\207\224\345\326\170\305\231\103\143\343
-\115\340\167\055\341\145\231\162\151\004\032\107\011\346\017\001
-\126\044\373\037\277\016\171\251\130\056\271\304\011\001\176\225
-\272\155\000\006\076\262\352\112\020\071\330\320\053\365\277\354
-\165\277\227\002\305\011\033\010\334\125\067\342\201\373\067\204
-\103\142\040\312\347\126\113\145\352\376\154\301\044\223\044\241
-\064\353\005\377\232\042\256\233\175\077\361\145\121\012\246\060
-\152\263\364\210\034\200\015\374\162\212\350\203\136
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for Certificate "StartCom Certification Authority"
-# Issuer: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Serial Number: 1 (0x1)
-# Subject: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Not Valid Before: Sun Sep 17 19:46:36 2006
-# Not Valid After : Wed Sep 17 19:46:36 2036
-# Fingerprint (MD5): 22:4D:8F:8A:FC:F7:35:C2:BB:57:34:90:7B:8B:22:16
-# Fingerprint (SHA1): 3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "StartCom Certification Authority"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\076\053\367\362\003\033\226\363\214\346\304\330\250\135\076\055
-\130\107\152\017
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\042\115\217\212\374\367\065\302\273\127\064\220\173\213\042\026
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013
-\023\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154
-\040\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147
-\156\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123
-\164\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143
-\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\001
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "Taiwan GRCA"
#
# Issuer: O=Government Root Certification Authority,C=TW
@@ -5345,149 +5110,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "DST ACES CA X6"
-#
-# Issuer: CN=DST ACES CA X6,OU=DST ACES,O=Digital Signature Trust,C=US
-# Serial Number:0d:5e:99:0a:d6:9d:b7:78:ec:d8:07:56:3b:86:15:d9
-# Subject: CN=DST ACES CA X6,OU=DST ACES,O=Digital Signature Trust,C=US
-# Not Valid Before: Thu Nov 20 21:19:58 2003
-# Not Valid After : Mon Nov 20 21:19:58 2017
-# Fingerprint (MD5): 21:D8:4C:82:2B:99:09:33:A2:EB:14:24:8D:8E:5F:E8
-# Fingerprint (SHA1): 40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "DST ACES CA X6"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\133\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\040\060\036\006\003\125\004\012\023\027\104\151\147\151\164\141
-\154\040\123\151\147\156\141\164\165\162\145\040\124\162\165\163
-\164\061\021\060\017\006\003\125\004\013\023\010\104\123\124\040
-\101\103\105\123\061\027\060\025\006\003\125\004\003\023\016\104
-\123\124\040\101\103\105\123\040\103\101\040\130\066
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\133\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\040\060\036\006\003\125\004\012\023\027\104\151\147\151\164\141
-\154\040\123\151\147\156\141\164\165\162\145\040\124\162\165\163
-\164\061\021\060\017\006\003\125\004\013\023\010\104\123\124\040
-\101\103\105\123\061\027\060\025\006\003\125\004\003\023\016\104
-\123\124\040\101\103\105\123\040\103\101\040\130\066
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\015\136\231\012\326\235\267\170\354\330\007\126\073\206
-\025\331
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\004\011\060\202\002\361\240\003\002\001\002\002\020\015
-\136\231\012\326\235\267\170\354\330\007\126\073\206\025\331\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\133
-\061\013\060\011\006\003\125\004\006\023\002\125\123\061\040\060
-\036\006\003\125\004\012\023\027\104\151\147\151\164\141\154\040
-\123\151\147\156\141\164\165\162\145\040\124\162\165\163\164\061
-\021\060\017\006\003\125\004\013\023\010\104\123\124\040\101\103
-\105\123\061\027\060\025\006\003\125\004\003\023\016\104\123\124
-\040\101\103\105\123\040\103\101\040\130\066\060\036\027\015\060
-\063\061\061\062\060\062\061\061\071\065\070\132\027\015\061\067
-\061\061\062\060\062\061\061\071\065\070\132\060\133\061\013\060
-\011\006\003\125\004\006\023\002\125\123\061\040\060\036\006\003
-\125\004\012\023\027\104\151\147\151\164\141\154\040\123\151\147
-\156\141\164\165\162\145\040\124\162\165\163\164\061\021\060\017
-\006\003\125\004\013\023\010\104\123\124\040\101\103\105\123\061
-\027\060\025\006\003\125\004\003\023\016\104\123\124\040\101\103
-\105\123\040\103\101\040\130\066\060\202\001\042\060\015\006\011
-\052\206\110\206\367\015\001\001\001\005\000\003\202\001\017\000
-\060\202\001\012\002\202\001\001\000\271\075\365\054\311\224\334
-\165\212\225\135\143\350\204\167\166\146\271\131\221\134\106\335
-\222\076\237\371\016\003\264\075\141\222\275\043\046\265\143\356
-\222\322\236\326\074\310\015\220\137\144\201\261\250\010\015\114
-\330\371\323\005\050\122\264\001\045\305\225\034\014\176\076\020
-\204\165\317\301\031\221\143\317\350\250\221\210\271\103\122\273
-\200\261\125\211\213\061\372\320\267\166\276\101\075\060\232\244
-\042\045\027\163\350\036\342\323\254\052\275\133\070\041\325\052
-\113\327\125\175\343\072\125\275\327\155\153\002\127\153\346\107
-\174\010\310\202\272\336\247\207\075\241\155\270\060\126\302\263
-\002\201\137\055\365\342\232\060\030\050\270\146\323\313\001\226
-\157\352\212\105\125\326\340\235\377\147\053\027\002\246\116\032
-\152\021\013\176\267\173\347\230\326\214\166\157\301\073\333\120
-\223\176\345\320\216\037\067\270\275\272\306\237\154\351\174\063
-\362\062\074\046\107\372\047\044\002\311\176\035\133\210\102\023
-\152\065\174\175\065\351\056\146\221\162\223\325\062\046\304\164
-\365\123\243\263\135\232\366\011\313\002\003\001\000\001\243\201
-\310\060\201\305\060\017\006\003\125\035\023\001\001\377\004\005
-\060\003\001\001\377\060\016\006\003\125\035\017\001\001\377\004
-\004\003\002\001\306\060\037\006\003\125\035\021\004\030\060\026
-\201\024\160\153\151\055\157\160\163\100\164\162\165\163\164\144
-\163\164\056\143\157\155\060\142\006\003\125\035\040\004\133\060
-\131\060\127\006\012\140\206\110\001\145\003\002\001\001\001\060
-\111\060\107\006\010\053\006\001\005\005\007\002\001\026\073\150
-\164\164\160\072\057\057\167\167\167\056\164\162\165\163\164\144
-\163\164\056\143\157\155\057\143\145\162\164\151\146\151\143\141
-\164\145\163\057\160\157\154\151\143\171\057\101\103\105\123\055
-\151\156\144\145\170\056\150\164\155\154\060\035\006\003\125\035
-\016\004\026\004\024\011\162\006\116\030\103\017\345\326\314\303
-\152\213\061\173\170\217\250\203\270\060\015\006\011\052\206\110
-\206\367\015\001\001\005\005\000\003\202\001\001\000\243\330\216
-\326\262\333\316\005\347\062\315\001\323\004\003\345\166\344\126
-\053\234\231\220\350\010\060\154\337\175\075\356\345\277\265\044
-\100\204\111\341\321\050\256\304\302\072\123\060\210\361\365\167
-\156\121\312\372\377\231\257\044\137\033\240\375\362\254\204\312
-\337\251\360\137\004\056\255\026\277\041\227\020\201\075\343\377
-\207\215\062\334\224\345\107\212\136\152\023\311\224\225\075\322
-\356\310\064\225\320\200\324\255\062\010\200\124\074\340\275\122
-\123\327\122\174\262\151\077\177\172\317\152\164\312\372\004\052
-\234\114\132\006\245\351\040\255\105\146\017\151\361\335\277\351
-\343\062\213\372\340\301\206\115\162\074\056\330\223\170\012\052
-\370\330\322\047\075\031\211\137\132\173\212\073\314\014\332\121
-\256\307\013\367\053\260\067\005\354\274\127\043\342\070\322\233
-\150\363\126\022\210\117\102\174\270\061\304\265\333\344\310\041
-\064\351\110\021\065\356\372\307\222\127\305\237\064\344\307\366
-\367\016\013\114\234\150\170\173\161\061\307\353\036\340\147\101
-\363\267\240\247\315\345\172\063\066\152\372\232\053
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for Certificate "DST ACES CA X6"
-# Issuer: CN=DST ACES CA X6,OU=DST ACES,O=Digital Signature Trust,C=US
-# Serial Number:0d:5e:99:0a:d6:9d:b7:78:ec:d8:07:56:3b:86:15:d9
-# Subject: CN=DST ACES CA X6,OU=DST ACES,O=Digital Signature Trust,C=US
-# Not Valid Before: Thu Nov 20 21:19:58 2003
-# Not Valid After : Mon Nov 20 21:19:58 2017
-# Fingerprint (MD5): 21:D8:4C:82:2B:99:09:33:A2:EB:14:24:8D:8E:5F:E8
-# Fingerprint (SHA1): 40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "DST ACES CA X6"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\100\124\332\157\034\077\100\164\254\355\017\354\315\333\171\321
-\123\373\220\035
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\041\330\114\202\053\231\011\063\242\353\024\044\215\216\137\350
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\133\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\040\060\036\006\003\125\004\012\023\027\104\151\147\151\164\141
-\154\040\123\151\147\156\141\164\165\162\145\040\124\162\165\163
-\164\061\021\060\017\006\003\125\004\013\023\010\104\123\124\040
-\101\103\105\123\061\027\060\025\006\003\125\004\003\023\016\104
-\123\124\040\101\103\105\123\040\103\101\040\130\066
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\015\136\231\012\326\235\267\170\354\330\007\126\073\206
-\025\331
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "SwissSign Platinum CA - G2"
#
# Issuer: CN=SwissSign Platinum CA - G2,O=SwissSign AG,C=CH
@@ -7152,311 +6774,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "MD5 Collisions Forged Rogue CA 25c3"
-#
-# Issuer: CN=Equifax Secure Global eBusiness CA-1,O=Equifax Secure Inc.,C=US
-# Serial Number: 66 (0x42)
-# Subject: CN=MD5 Collisions Inc. (http://www.phreedom.org/md5)
-# Not Valid Before: Sat Jul 31 00:00:01 2004
-# Not Valid After : Thu Sep 02 00:00:01 2004
-# Fingerprint (MD5): 16:7A:13:15:B9:17:39:A3:F1:05:6A:E6:3E:D9:3A:38
-# Fingerprint (SHA1): 64:23:13:7E:5C:53:D6:4A:A6:64:85:ED:36:54:F5:AB:05:5A:8B:8A
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "MD5 Collisions Forged Rogue CA 25c3"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\074\061\072\060\070\006\003\125\004\003\023\061\115\104\065
-\040\103\157\154\154\151\163\151\157\156\163\040\111\156\143\056
-\040\050\150\164\164\160\072\057\057\167\167\167\056\160\150\162
-\145\145\144\157\155\056\157\162\147\057\155\144\065\051
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\132\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\034\060\032\006\003\125\004\012\023\023\105\161\165\151\146\141
-\170\040\123\145\143\165\162\145\040\111\156\143\056\061\055\060
-\053\006\003\125\004\003\023\044\105\161\165\151\146\141\170\040
-\123\145\143\165\162\145\040\107\154\157\142\141\154\040\145\102
-\165\163\151\156\145\163\163\040\103\101\055\061
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\102
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\004\062\060\202\003\233\240\003\002\001\002\002\001\102
-\060\015\006\011\052\206\110\206\367\015\001\001\004\005\000\060
-\132\061\013\060\011\006\003\125\004\006\023\002\125\123\061\034
-\060\032\006\003\125\004\012\023\023\105\161\165\151\146\141\170
-\040\123\145\143\165\162\145\040\111\156\143\056\061\055\060\053
-\006\003\125\004\003\023\044\105\161\165\151\146\141\170\040\123
-\145\143\165\162\145\040\107\154\157\142\141\154\040\145\102\165
-\163\151\156\145\163\163\040\103\101\055\061\060\036\027\015\060
-\064\060\067\063\061\060\060\060\060\060\061\132\027\015\060\064
-\060\071\060\062\060\060\060\060\060\061\132\060\074\061\072\060
-\070\006\003\125\004\003\023\061\115\104\065\040\103\157\154\154
-\151\163\151\157\156\163\040\111\156\143\056\040\050\150\164\164
-\160\072\057\057\167\167\167\056\160\150\162\145\145\144\157\155
-\056\157\162\147\057\155\144\065\051\060\201\237\060\015\006\011
-\052\206\110\206\367\015\001\001\001\005\000\003\201\215\000\060
-\201\211\002\201\201\000\272\246\131\311\054\050\326\052\260\370
-\355\237\106\244\244\067\356\016\031\150\131\321\263\003\231\121
-\326\026\232\136\067\153\025\340\016\113\365\204\144\370\243\333
-\101\157\065\325\233\025\037\333\304\070\122\160\201\227\136\217
-\240\265\367\176\071\360\062\254\036\255\104\322\263\372\110\303
-\316\221\233\354\364\234\174\341\132\365\310\067\153\232\203\336
-\347\312\040\227\061\102\163\025\221\150\364\210\257\371\050\050
-\305\351\017\163\260\027\113\023\114\231\165\320\104\346\176\010
-\154\032\362\117\033\101\002\003\001\000\001\243\202\002\044\060
-\202\002\040\060\013\006\003\125\035\017\004\004\003\002\001\306
-\060\017\006\003\125\035\023\001\001\377\004\005\060\003\001\001
-\377\060\035\006\003\125\035\016\004\026\004\024\247\004\140\037
-\253\162\103\010\305\177\010\220\125\126\034\326\316\346\070\353
-\060\037\006\003\125\035\043\004\030\060\026\200\024\276\250\240
-\164\162\120\153\104\267\311\043\330\373\250\377\263\127\153\150
-\154\060\202\001\276\006\011\140\206\110\001\206\370\102\001\015
-\004\202\001\257\026\202\001\253\063\000\000\000\047\136\071\340
-\211\141\017\116\243\305\105\013\066\273\001\321\123\252\303\010
-\217\157\370\117\076\207\207\104\021\334\140\340\337\222\125\371
-\270\163\033\124\223\305\237\320\106\304\140\266\065\142\315\271
-\257\034\250\151\032\311\133\074\226\067\300\355\147\357\273\376
-\300\213\234\120\057\051\275\203\042\236\216\010\372\254\023\160
-\242\130\177\142\142\212\021\367\211\366\337\266\147\131\163\026
-\373\143\026\212\264\221\070\316\056\365\266\276\114\244\224\111
-\344\145\021\012\102\025\311\301\060\342\151\325\105\175\245\046
-\273\271\141\354\142\144\360\071\341\347\274\150\330\120\121\236
-\035\140\323\321\243\247\012\370\003\040\241\160\001\027\221\066
-\117\002\160\061\206\203\335\367\017\330\007\035\021\263\023\004
-\245\334\360\256\120\261\050\016\143\151\052\014\202\157\217\107
-\063\337\154\242\006\222\361\117\105\276\331\060\066\243\053\214
-\326\167\256\065\143\177\116\114\232\223\110\066\331\237\002\003
-\001\000\001\243\201\275\060\201\272\060\016\006\003\125\035\017
-\001\001\377\004\004\003\002\004\360\060\035\006\003\125\035\016
-\004\026\004\024\315\246\203\372\245\140\067\367\226\067\027\051
-\336\101\170\361\207\211\125\347\060\073\006\003\125\035\037\004
-\064\060\062\060\060\240\056\240\054\206\052\150\164\164\160\072
-\057\057\143\162\154\056\147\145\157\164\162\165\163\164\056\143
-\157\155\057\143\162\154\163\057\147\154\157\142\141\154\143\141
-\061\056\143\162\154\060\037\006\003\125\035\043\004\030\060\026
-\200\024\276\250\240\164\162\120\153\104\267\311\043\330\373\250
-\377\263\127\153\150\154\060\035\006\003\125\035\045\004\026\060
-\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006\001
-\005\005\007\003\002\060\014\006\003\125\035\023\001\001\377\004
-\002\060\000\060\015\006\011\052\206\110\206\367\015\001\001\004
-\005\000\003\201\201\000\247\041\002\215\321\016\242\200\167\045
-\375\103\140\025\217\354\357\220\107\324\204\102\025\046\021\034
-\315\302\074\020\051\251\266\337\253\127\165\221\332\345\053\263
-\220\105\034\060\143\126\077\212\331\120\372\355\130\154\300\145
-\254\146\127\336\034\306\166\073\365\000\016\216\105\316\177\114
-\220\354\053\306\315\263\264\217\142\320\376\267\305\046\162\104
-\355\366\230\133\256\313\321\225\365\332\010\276\150\106\261\165
-\310\354\035\217\036\172\224\361\252\123\170\242\105\256\124\352
-\321\236\164\310\166\147
-END
-
-# Trust for Certificate "MD5 Collisions Forged Rogue CA 25c3"
-# Issuer: CN=Equifax Secure Global eBusiness CA-1,O=Equifax Secure Inc.,C=US
-# Serial Number: 66 (0x42)
-# Subject: CN=MD5 Collisions Inc. (http://www.phreedom.org/md5)
-# Not Valid Before: Sat Jul 31 00:00:01 2004
-# Not Valid After : Thu Sep 02 00:00:01 2004
-# Fingerprint (MD5): 16:7A:13:15:B9:17:39:A3:F1:05:6A:E6:3E:D9:3A:38
-# Fingerprint (SHA1): 64:23:13:7E:5C:53:D6:4A:A6:64:85:ED:36:54:F5:AB:05:5A:8B:8A
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "MD5 Collisions Forged Rogue CA 25c3"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\144\043\023\176\134\123\326\112\246\144\205\355\066\124\365\253
-\005\132\213\212
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\026\172\023\025\271\027\071\243\361\005\152\346\076\331\072\070
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\132\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\034\060\032\006\003\125\004\012\023\023\105\161\165\151\146\141
-\170\040\123\145\143\165\162\145\040\111\156\143\056\061\055\060
-\053\006\003\125\004\003\023\044\105\161\165\151\146\141\170\040
-\123\145\143\165\162\145\040\107\154\157\142\141\154\040\145\102
-\165\163\151\156\145\163\163\040\103\101\055\061
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\102
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-# Distrust "Distrusted AC DG Tresor SSL"
-# Issuer: CN=AC DGTPE Signature Authentification,O=DGTPE,C=FR
-# Serial Number: 204199 (0x31da7)
-# Subject: CN=AC DG Tr..sor SSL,O=DG Tr..sor,C=FR
-# Not Valid Before: Thu Jul 18 10:05:28 2013
-# Not Valid After : Fri Jul 18 10:05:28 2014
-# Fingerprint (MD5): 3A:EA:9E:FC:00:0C:E2:06:6C:E0:AC:39:C1:31:DE:C8
-# Fingerprint (SHA1): 5C:E3:39:46:5F:41:A1:E4:23:14:9F:65:54:40:95:40:4D:E6:EB:E2
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Distrusted AC DG Tresor SSL"
-CKA_ISSUER MULTILINE_OCTAL
-\060\113\061\013\060\011\006\003\125\004\006\023\002\106\122\061
-\016\060\014\006\003\125\004\012\023\005\104\107\124\120\105\061
-\054\060\052\006\003\125\004\003\023\043\101\103\040\104\107\124
-\120\105\040\123\151\147\156\141\164\165\162\145\040\101\165\164
-\150\145\156\164\151\146\151\143\141\164\151\157\156
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\003\003\035\247
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Security Communication EV RootCA1"
-#
-# Issuer: OU=Security Communication EV RootCA1,O="SECOM Trust Systems CO.,LTD.",C=JP
-# Serial Number: 0 (0x0)
-# Subject: OU=Security Communication EV RootCA1,O="SECOM Trust Systems CO.,LTD.",C=JP
-# Not Valid Before: Wed Jun 06 02:12:32 2007
-# Not Valid After : Sat Jun 06 02:12:32 2037
-# Fingerprint (MD5): 22:2D:A6:01:EA:7C:0A:F7:F0:6C:56:43:3F:77:76:D3
-# Fingerprint (SHA1): FE:B8:C4:32:DC:F9:76:9A:CE:AE:3D:D8:90:8F:FD:28:86:65:64:7D
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Security Communication EV RootCA1"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\140\061\013\060\011\006\003\125\004\006\023\002\112\120\061
-\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040
-\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117
-\056\054\114\124\104\056\061\052\060\050\006\003\125\004\013\023
-\041\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156
-\151\143\141\164\151\157\156\040\105\126\040\122\157\157\164\103
-\101\061
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\140\061\013\060\011\006\003\125\004\006\023\002\112\120\061
-\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040
-\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117
-\056\054\114\124\104\056\061\052\060\050\006\003\125\004\013\023
-\041\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156
-\151\143\141\164\151\157\156\040\105\126\040\122\157\157\164\103
-\101\061
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\000
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\003\175\060\202\002\145\240\003\002\001\002\002\001\000
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\140\061\013\060\011\006\003\125\004\006\023\002\112\120\061\045
-\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040\124
-\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117\056
-\054\114\124\104\056\061\052\060\050\006\003\125\004\013\023\041
-\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156\151
-\143\141\164\151\157\156\040\105\126\040\122\157\157\164\103\101
-\061\060\036\027\015\060\067\060\066\060\066\060\062\061\062\063
-\062\132\027\015\063\067\060\066\060\066\060\062\061\062\063\062
-\132\060\140\061\013\060\011\006\003\125\004\006\023\002\112\120
-\061\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115
-\040\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103
-\117\056\054\114\124\104\056\061\052\060\050\006\003\125\004\013
-\023\041\123\145\143\165\162\151\164\171\040\103\157\155\155\165
-\156\151\143\141\164\151\157\156\040\105\126\040\122\157\157\164
-\103\101\061\060\202\001\042\060\015\006\011\052\206\110\206\367
-\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002
-\202\001\001\000\274\177\354\127\233\044\340\376\234\272\102\171
-\251\210\212\372\200\340\365\007\051\103\352\216\012\064\066\215
-\034\372\247\265\071\170\377\227\165\367\057\344\252\153\004\204
-\104\312\246\342\150\216\375\125\120\142\017\244\161\016\316\007
-\070\055\102\205\120\255\074\226\157\213\325\242\016\317\336\111
-\211\075\326\144\056\070\345\036\154\265\127\212\236\357\110\016
-\315\172\151\026\207\104\265\220\344\006\235\256\241\004\227\130
-\171\357\040\112\202\153\214\042\277\354\037\017\351\204\161\355
-\361\016\344\270\030\023\314\126\066\135\321\232\036\121\153\071
-\156\140\166\210\064\013\363\263\321\260\235\312\141\342\144\035
-\301\106\007\270\143\335\036\063\145\263\216\011\125\122\075\265
-\275\377\007\353\255\141\125\030\054\251\151\230\112\252\100\305
-\063\024\145\164\000\371\221\336\257\003\110\305\100\124\334\017
-\204\220\150\040\305\222\226\334\056\345\002\105\252\300\137\124
-\370\155\352\111\317\135\154\113\257\357\232\302\126\134\306\065
-\126\102\152\060\137\302\253\366\342\075\077\263\311\021\217\061
-\114\327\237\111\002\003\001\000\001\243\102\060\100\060\035\006
-\003\125\035\016\004\026\004\024\065\112\365\115\257\077\327\202
-\070\254\253\161\145\027\165\214\235\125\223\346\060\016\006\003
-\125\035\017\001\001\377\004\004\003\002\001\006\060\017\006\003
-\125\035\023\001\001\377\004\005\060\003\001\001\377\060\015\006
-\011\052\206\110\206\367\015\001\001\005\005\000\003\202\001\001
-\000\250\207\351\354\370\100\147\135\303\301\146\307\100\113\227
-\374\207\023\220\132\304\357\240\312\137\213\267\247\267\361\326
-\265\144\267\212\263\270\033\314\332\373\254\146\210\101\316\350
-\374\344\333\036\210\246\355\047\120\033\002\060\044\106\171\376
-\004\207\160\227\100\163\321\300\301\127\031\232\151\245\047\231
-\253\235\142\204\366\121\301\054\311\043\025\330\050\267\253\045
-\023\265\106\341\206\002\377\046\214\304\210\222\035\126\376\031
-\147\362\125\344\200\243\153\234\253\167\341\121\161\015\040\333
-\020\232\333\275\166\171\007\167\231\050\255\232\136\332\261\117
-\104\054\065\216\245\226\307\375\203\360\130\306\171\326\230\174
-\250\215\376\206\076\007\026\222\341\173\347\035\354\063\166\176
-\102\056\112\205\371\221\211\150\204\003\201\245\233\232\276\343
-\067\305\124\253\126\073\030\055\101\244\014\370\102\333\231\240
-\340\162\157\273\135\341\026\117\123\012\144\371\116\364\277\116
-\124\275\170\154\210\352\277\234\023\044\302\160\151\242\177\017
-\310\074\255\010\311\260\230\100\243\052\347\210\203\355\167\217
-\164
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for Certificate "Security Communication EV RootCA1"
-# Issuer: OU=Security Communication EV RootCA1,O="SECOM Trust Systems CO.,LTD.",C=JP
-# Serial Number: 0 (0x0)
-# Subject: OU=Security Communication EV RootCA1,O="SECOM Trust Systems CO.,LTD.",C=JP
-# Not Valid Before: Wed Jun 06 02:12:32 2007
-# Not Valid After : Sat Jun 06 02:12:32 2037
-# Fingerprint (MD5): 22:2D:A6:01:EA:7C:0A:F7:F0:6C:56:43:3F:77:76:D3
-# Fingerprint (SHA1): FE:B8:C4:32:DC:F9:76:9A:CE:AE:3D:D8:90:8F:FD:28:86:65:64:7D
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Security Communication EV RootCA1"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\376\270\304\062\334\371\166\232\316\256\075\330\220\217\375\050
-\206\145\144\175
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\042\055\246\001\352\174\012\367\360\154\126\103\077\167\166\323
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\140\061\013\060\011\006\003\125\004\006\023\002\112\120\061
-\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040
-\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117
-\056\054\114\124\104\056\061\052\060\050\006\003\125\004\013\023
-\041\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156
-\151\143\141\164\151\157\156\040\105\126\040\122\157\157\164\103
-\101\061
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\000
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "OISTE WISeKey Global Root GA CA"
#
# Issuer: CN=OISTE WISeKey Global Root GA CA,OU=OISTE Foundation Endorsed,OU=Copyright (c) 2005,O=WISeKey,C=CH
@@ -8651,203 +7968,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "TUBITAK UEKAE Kok Sertifika Hizmet Saglayicisi - Surum 3"
-#
-# Issuer: CN=T..B..TAK UEKAE K..k Sertifika Hizmet Sa..lay..c..s.. - S..r..m ...,OU=Kamu Sertifikasyon Merkezi,OU=Ulusal Elektronik ve Kriptoloji Ara..t..rma Enstit..s.. - UEKAE,O=T..rkiye Bilimsel ve Teknolojik Ara..t..rma Kurumu - T..B..TAK,L=Gebze - Kocaeli,C=TR
-# Serial Number: 17 (0x11)
-# Subject: CN=T..B..TAK UEKAE K..k Sertifika Hizmet Sa..lay..c..s.. - S..r..m ...,OU=Kamu Sertifikasyon Merkezi,OU=Ulusal Elektronik ve Kriptoloji Ara..t..rma Enstit..s.. - UEKAE,O=T..rkiye Bilimsel ve Teknolojik Ara..t..rma Kurumu - T..B..TAK,L=Gebze - Kocaeli,C=TR
-# Not Valid Before: Fri Aug 24 11:37:07 2007
-# Not Valid After : Mon Aug 21 11:37:07 2017
-# Fingerprint (MD5): ED:41:F5:8C:50:C5:2B:9C:73:E6:EE:6C:EB:C2:A8:26
-# Fingerprint (SHA1): 1B:4B:39:61:26:27:6B:64:91:A2:68:6D:D7:02:43:21:2D:1F:1D:96
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\202\001\053\061\013\060\011\006\003\125\004\006\023\002\124
-\122\061\030\060\026\006\003\125\004\007\014\017\107\145\142\172
-\145\040\055\040\113\157\143\141\145\154\151\061\107\060\105\006
-\003\125\004\012\014\076\124\303\274\162\153\151\171\145\040\102
-\151\154\151\155\163\145\154\040\166\145\040\124\145\153\156\157
-\154\157\152\151\153\040\101\162\141\305\237\164\304\261\162\155
-\141\040\113\165\162\165\155\165\040\055\040\124\303\234\102\304
-\260\124\101\113\061\110\060\106\006\003\125\004\013\014\077\125
-\154\165\163\141\154\040\105\154\145\153\164\162\157\156\151\153
-\040\166\145\040\113\162\151\160\164\157\154\157\152\151\040\101
-\162\141\305\237\164\304\261\162\155\141\040\105\156\163\164\151
-\164\303\274\163\303\274\040\055\040\125\105\113\101\105\061\043
-\060\041\006\003\125\004\013\014\032\113\141\155\165\040\123\145
-\162\164\151\146\151\153\141\163\171\157\156\040\115\145\162\153
-\145\172\151\061\112\060\110\006\003\125\004\003\014\101\124\303
-\234\102\304\260\124\101\113\040\125\105\113\101\105\040\113\303
-\266\153\040\123\145\162\164\151\146\151\153\141\040\110\151\172
-\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304\261
-\163\304\261\040\055\040\123\303\274\162\303\274\155\040\063
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\202\001\053\061\013\060\011\006\003\125\004\006\023\002\124
-\122\061\030\060\026\006\003\125\004\007\014\017\107\145\142\172
-\145\040\055\040\113\157\143\141\145\154\151\061\107\060\105\006
-\003\125\004\012\014\076\124\303\274\162\153\151\171\145\040\102
-\151\154\151\155\163\145\154\040\166\145\040\124\145\153\156\157
-\154\157\152\151\153\040\101\162\141\305\237\164\304\261\162\155
-\141\040\113\165\162\165\155\165\040\055\040\124\303\234\102\304
-\260\124\101\113\061\110\060\106\006\003\125\004\013\014\077\125
-\154\165\163\141\154\040\105\154\145\153\164\162\157\156\151\153
-\040\166\145\040\113\162\151\160\164\157\154\157\152\151\040\101
-\162\141\305\237\164\304\261\162\155\141\040\105\156\163\164\151
-\164\303\274\163\303\274\040\055\040\125\105\113\101\105\061\043
-\060\041\006\003\125\004\013\014\032\113\141\155\165\040\123\145
-\162\164\151\146\151\153\141\163\171\157\156\040\115\145\162\153
-\145\172\151\061\112\060\110\006\003\125\004\003\014\101\124\303
-\234\102\304\260\124\101\113\040\125\105\113\101\105\040\113\303
-\266\153\040\123\145\162\164\151\146\151\153\141\040\110\151\172
-\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304\261
-\163\304\261\040\055\040\123\303\274\162\303\274\155\040\063
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\021
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\027\060\202\003\377\240\003\002\001\002\002\001\021
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\202\001\053\061\013\060\011\006\003\125\004\006\023\002\124\122
-\061\030\060\026\006\003\125\004\007\014\017\107\145\142\172\145
-\040\055\040\113\157\143\141\145\154\151\061\107\060\105\006\003
-\125\004\012\014\076\124\303\274\162\153\151\171\145\040\102\151
-\154\151\155\163\145\154\040\166\145\040\124\145\153\156\157\154
-\157\152\151\153\040\101\162\141\305\237\164\304\261\162\155\141
-\040\113\165\162\165\155\165\040\055\040\124\303\234\102\304\260
-\124\101\113\061\110\060\106\006\003\125\004\013\014\077\125\154
-\165\163\141\154\040\105\154\145\153\164\162\157\156\151\153\040
-\166\145\040\113\162\151\160\164\157\154\157\152\151\040\101\162
-\141\305\237\164\304\261\162\155\141\040\105\156\163\164\151\164
-\303\274\163\303\274\040\055\040\125\105\113\101\105\061\043\060
-\041\006\003\125\004\013\014\032\113\141\155\165\040\123\145\162
-\164\151\146\151\153\141\163\171\157\156\040\115\145\162\153\145
-\172\151\061\112\060\110\006\003\125\004\003\014\101\124\303\234
-\102\304\260\124\101\113\040\125\105\113\101\105\040\113\303\266
-\153\040\123\145\162\164\151\146\151\153\141\040\110\151\172\155
-\145\164\040\123\141\304\237\154\141\171\304\261\143\304\261\163
-\304\261\040\055\040\123\303\274\162\303\274\155\040\063\060\036
-\027\015\060\067\060\070\062\064\061\061\063\067\060\067\132\027
-\015\061\067\060\070\062\061\061\061\063\067\060\067\132\060\202
-\001\053\061\013\060\011\006\003\125\004\006\023\002\124\122\061
-\030\060\026\006\003\125\004\007\014\017\107\145\142\172\145\040
-\055\040\113\157\143\141\145\154\151\061\107\060\105\006\003\125
-\004\012\014\076\124\303\274\162\153\151\171\145\040\102\151\154
-\151\155\163\145\154\040\166\145\040\124\145\153\156\157\154\157
-\152\151\153\040\101\162\141\305\237\164\304\261\162\155\141\040
-\113\165\162\165\155\165\040\055\040\124\303\234\102\304\260\124
-\101\113\061\110\060\106\006\003\125\004\013\014\077\125\154\165
-\163\141\154\040\105\154\145\153\164\162\157\156\151\153\040\166
-\145\040\113\162\151\160\164\157\154\157\152\151\040\101\162\141
-\305\237\164\304\261\162\155\141\040\105\156\163\164\151\164\303
-\274\163\303\274\040\055\040\125\105\113\101\105\061\043\060\041
-\006\003\125\004\013\014\032\113\141\155\165\040\123\145\162\164
-\151\146\151\153\141\163\171\157\156\040\115\145\162\153\145\172
-\151\061\112\060\110\006\003\125\004\003\014\101\124\303\234\102
-\304\260\124\101\113\040\125\105\113\101\105\040\113\303\266\153
-\040\123\145\162\164\151\146\151\153\141\040\110\151\172\155\145
-\164\040\123\141\304\237\154\141\171\304\261\143\304\261\163\304
-\261\040\055\040\123\303\274\162\303\274\155\040\063\060\202\001
-\042\060\015\006\011\052\206\110\206\367\015\001\001\001\005\000
-\003\202\001\017\000\060\202\001\012\002\202\001\001\000\212\155
-\113\377\020\210\072\303\366\176\224\350\352\040\144\160\256\041
-\201\276\072\173\074\333\361\035\122\177\131\372\363\042\114\225
-\240\220\274\110\116\021\253\373\267\265\215\172\203\050\214\046
-\106\330\116\225\100\207\141\237\305\236\155\201\207\127\154\212
-\073\264\146\352\314\100\374\343\252\154\262\313\001\333\062\277
-\322\353\205\317\241\015\125\303\133\070\127\160\270\165\306\171
-\321\024\060\355\033\130\133\153\357\065\362\241\041\116\305\316
-\174\231\137\154\271\270\042\223\120\247\315\114\160\152\276\152
-\005\177\023\234\053\036\352\376\107\316\004\245\157\254\223\056
-\174\053\237\236\171\023\221\350\352\236\312\070\165\216\142\260
-\225\223\052\345\337\351\136\227\156\040\137\137\204\172\104\071
-\031\100\034\272\125\053\373\060\262\201\357\204\343\334\354\230
-\070\071\003\205\010\251\124\003\005\051\360\311\217\213\352\013
-\206\145\031\021\323\351\011\043\336\150\223\003\311\066\034\041
-\156\316\214\146\361\231\060\330\327\263\303\035\370\201\056\250
-\275\202\013\146\376\202\313\341\340\032\202\303\100\201\002\003
-\001\000\001\243\102\060\100\060\035\006\003\125\035\016\004\026
-\004\024\275\210\207\311\217\366\244\012\013\252\353\305\376\221
-\043\235\253\112\212\062\060\016\006\003\125\035\017\001\001\377
-\004\004\003\002\001\006\060\017\006\003\125\035\023\001\001\377
-\004\005\060\003\001\001\377\060\015\006\011\052\206\110\206\367
-\015\001\001\005\005\000\003\202\001\001\000\035\174\372\111\217
-\064\351\267\046\222\026\232\005\164\347\113\320\155\071\154\303
-\046\366\316\270\061\274\304\337\274\052\370\067\221\030\334\004
-\310\144\231\053\030\155\200\003\131\311\256\370\130\320\076\355
-\303\043\237\151\074\206\070\034\236\357\332\047\170\321\204\067
-\161\212\074\113\071\317\176\105\006\326\055\330\212\115\170\022
-\326\255\302\323\313\322\320\101\363\046\066\112\233\225\154\014
-\356\345\321\103\047\146\301\210\367\172\263\040\154\352\260\151
-\053\307\040\350\014\003\304\101\005\231\342\077\344\153\370\240
-\206\201\307\204\306\037\325\113\201\022\262\026\041\054\023\241
-\200\262\136\014\112\023\236\040\330\142\100\253\220\352\144\112
-\057\254\015\001\022\171\105\250\057\207\031\150\310\342\205\307
-\060\262\165\371\070\077\262\300\223\264\153\342\003\104\316\147
-\240\337\211\326\255\214\166\243\023\303\224\141\053\153\331\154
-\301\007\012\042\007\205\154\205\044\106\251\276\077\213\170\204
-\202\176\044\014\235\375\201\067\343\045\250\355\066\116\225\054
-\311\234\220\332\354\251\102\074\255\266\002
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for Certificate "TUBITAK UEKAE Kok Sertifika Hizmet Saglayicisi - Surum 3"
-# Issuer: CN=T..B..TAK UEKAE K..k Sertifika Hizmet Sa..lay..c..s.. - S..r..m ...,OU=Kamu Sertifikasyon Merkezi,OU=Ulusal Elektronik ve Kriptoloji Ara..t..rma Enstit..s.. - UEKAE,O=T..rkiye Bilimsel ve Teknolojik Ara..t..rma Kurumu - T..B..TAK,L=Gebze - Kocaeli,C=TR
-# Serial Number: 17 (0x11)
-# Subject: CN=T..B..TAK UEKAE K..k Sertifika Hizmet Sa..lay..c..s.. - S..r..m ...,OU=Kamu Sertifikasyon Merkezi,OU=Ulusal Elektronik ve Kriptoloji Ara..t..rma Enstit..s.. - UEKAE,O=T..rkiye Bilimsel ve Teknolojik Ara..t..rma Kurumu - T..B..TAK,L=Gebze - Kocaeli,C=TR
-# Not Valid Before: Fri Aug 24 11:37:07 2007
-# Not Valid After : Mon Aug 21 11:37:07 2017
-# Fingerprint (MD5): ED:41:F5:8C:50:C5:2B:9C:73:E6:EE:6C:EB:C2:A8:26
-# Fingerprint (SHA1): 1B:4B:39:61:26:27:6B:64:91:A2:68:6D:D7:02:43:21:2D:1F:1D:96
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\033\113\071\141\046\047\153\144\221\242\150\155\327\002\103\041
-\055\037\035\226
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\355\101\365\214\120\305\053\234\163\346\356\154\353\302\250\046
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\202\001\053\061\013\060\011\006\003\125\004\006\023\002\124
-\122\061\030\060\026\006\003\125\004\007\014\017\107\145\142\172
-\145\040\055\040\113\157\143\141\145\154\151\061\107\060\105\006
-\003\125\004\012\014\076\124\303\274\162\153\151\171\145\040\102
-\151\154\151\155\163\145\154\040\166\145\040\124\145\153\156\157
-\154\157\152\151\153\040\101\162\141\305\237\164\304\261\162\155
-\141\040\113\165\162\165\155\165\040\055\040\124\303\234\102\304
-\260\124\101\113\061\110\060\106\006\003\125\004\013\014\077\125
-\154\165\163\141\154\040\105\154\145\153\164\162\157\156\151\153
-\040\166\145\040\113\162\151\160\164\157\154\157\152\151\040\101
-\162\141\305\237\164\304\261\162\155\141\040\105\156\163\164\151
-\164\303\274\163\303\274\040\055\040\125\105\113\101\105\061\043
-\060\041\006\003\125\004\013\014\032\113\141\155\165\040\123\145
-\162\164\151\146\151\153\141\163\171\157\156\040\115\145\162\153
-\145\172\151\061\112\060\110\006\003\125\004\003\014\101\124\303
-\234\102\304\260\124\101\113\040\125\105\113\101\105\040\113\303
-\266\153\040\123\145\162\164\151\146\151\153\141\040\110\151\172
-\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304\261
-\163\304\261\040\055\040\123\303\274\162\303\274\155\040\063
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\021
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "certSIGN ROOT CA"
#
# Issuer: OU=certSIGN ROOT CA,O=certSIGN,C=RO
@@ -10461,172 +9581,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "ACEDICOM Root"
-#
-# Issuer: C=ES,O=EDICOM,OU=PKI,CN=ACEDICOM Root
-# Serial Number:61:8d:c7:86:3b:01:82:05
-# Subject: C=ES,O=EDICOM,OU=PKI,CN=ACEDICOM Root
-# Not Valid Before: Fri Apr 18 16:24:22 2008
-# Not Valid After : Thu Apr 13 16:24:22 2028
-# Fingerprint (MD5): 42:81:A0:E2:1C:E3:55:10:DE:55:89:42:65:96:22:E6
-# Fingerprint (SHA1): E0:B4:32:2E:B2:F6:A5:68:B6:54:53:84:48:18:4A:50:36:87:43:84
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "ACEDICOM Root"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\104\061\026\060\024\006\003\125\004\003\014\015\101\103\105
-\104\111\103\117\115\040\122\157\157\164\061\014\060\012\006\003
-\125\004\013\014\003\120\113\111\061\017\060\015\006\003\125\004
-\012\014\006\105\104\111\103\117\115\061\013\060\011\006\003\125
-\004\006\023\002\105\123
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\104\061\026\060\024\006\003\125\004\003\014\015\101\103\105
-\104\111\103\117\115\040\122\157\157\164\061\014\060\012\006\003
-\125\004\013\014\003\120\113\111\061\017\060\015\006\003\125\004
-\012\014\006\105\104\111\103\117\115\061\013\060\011\006\003\125
-\004\006\023\002\105\123
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\010\141\215\307\206\073\001\202\005
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\265\060\202\003\235\240\003\002\001\002\002\010\141
-\215\307\206\073\001\202\005\060\015\006\011\052\206\110\206\367
-\015\001\001\005\005\000\060\104\061\026\060\024\006\003\125\004
-\003\014\015\101\103\105\104\111\103\117\115\040\122\157\157\164
-\061\014\060\012\006\003\125\004\013\014\003\120\113\111\061\017
-\060\015\006\003\125\004\012\014\006\105\104\111\103\117\115\061
-\013\060\011\006\003\125\004\006\023\002\105\123\060\036\027\015
-\060\070\060\064\061\070\061\066\062\064\062\062\132\027\015\062
-\070\060\064\061\063\061\066\062\064\062\062\132\060\104\061\026
-\060\024\006\003\125\004\003\014\015\101\103\105\104\111\103\117
-\115\040\122\157\157\164\061\014\060\012\006\003\125\004\013\014
-\003\120\113\111\061\017\060\015\006\003\125\004\012\014\006\105
-\104\111\103\117\115\061\013\060\011\006\003\125\004\006\023\002
-\105\123\060\202\002\042\060\015\006\011\052\206\110\206\367\015
-\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
-\002\001\000\377\222\225\341\150\006\166\264\054\310\130\110\312
-\375\200\124\051\125\143\044\377\220\145\233\020\165\173\303\152
-\333\142\002\001\362\030\206\265\174\132\070\261\344\130\271\373
-\323\330\055\237\275\062\067\277\054\025\155\276\265\364\041\322
-\023\221\331\007\255\001\005\326\363\275\167\316\137\102\201\012
-\371\152\343\203\000\250\053\056\125\023\143\201\312\107\034\173
-\134\026\127\172\033\203\140\004\072\076\145\303\315\001\336\336
-\244\326\014\272\216\336\331\004\356\027\126\042\233\217\143\375
-\115\026\013\267\173\167\214\371\045\265\321\155\231\022\056\117
-\032\270\346\352\004\222\256\075\021\271\121\102\075\207\260\061
-\205\257\171\132\234\376\347\116\136\222\117\103\374\253\072\255
-\245\022\046\146\271\342\014\327\230\316\324\130\245\225\100\012
-\267\104\235\023\164\053\302\245\353\042\025\230\020\330\213\305
-\004\237\035\217\140\345\006\033\233\317\271\171\240\075\242\043
-\077\102\077\153\372\034\003\173\060\215\316\154\300\277\346\033
-\137\277\147\270\204\031\325\025\357\173\313\220\066\061\142\311
-\274\002\253\106\137\233\376\032\150\224\064\075\220\216\255\366
-\344\035\011\177\112\210\070\077\276\147\375\064\226\365\035\274
-\060\164\313\070\356\325\154\253\324\374\364\000\267\000\133\205
-\062\026\166\063\351\330\243\231\235\005\000\252\026\346\363\201
-\175\157\175\252\206\155\255\025\164\323\304\242\161\252\364\024
-\175\347\062\270\037\274\325\361\116\275\157\027\002\071\327\016
-\225\102\072\307\000\076\351\046\143\021\352\013\321\112\377\030
-\235\262\327\173\057\072\331\226\373\350\036\222\256\023\125\310
-\331\047\366\334\110\033\260\044\301\205\343\167\235\232\244\363
-\014\021\035\015\310\264\024\356\265\202\127\011\277\040\130\177
-\057\042\043\330\160\313\171\154\311\113\362\251\052\310\374\207
-\053\327\032\120\370\047\350\057\103\343\072\275\330\127\161\375
-\316\246\122\133\371\335\115\355\345\366\157\211\355\273\223\234
-\166\041\165\360\222\114\051\367\057\234\001\056\376\120\106\236
-\144\014\024\263\007\133\305\302\163\154\361\007\134\105\044\024
-\065\256\203\361\152\115\211\172\372\263\330\055\146\360\066\207
-\365\053\123\002\003\001\000\001\243\201\252\060\201\247\060\017
-\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377\060
-\037\006\003\125\035\043\004\030\060\026\200\024\246\263\341\053
-\053\111\266\327\163\241\252\224\365\001\347\163\145\114\254\120
-\060\016\006\003\125\035\017\001\001\377\004\004\003\002\001\206
-\060\035\006\003\125\035\016\004\026\004\024\246\263\341\053\053
-\111\266\327\163\241\252\224\365\001\347\163\145\114\254\120\060
-\104\006\003\125\035\040\004\075\060\073\060\071\006\004\125\035
-\040\000\060\061\060\057\006\010\053\006\001\005\005\007\002\001
-\026\043\150\164\164\160\072\057\057\141\143\145\144\151\143\157
-\155\056\145\144\151\143\157\155\147\162\157\165\160\056\143\157
-\155\057\144\157\143\060\015\006\011\052\206\110\206\367\015\001
-\001\005\005\000\003\202\002\001\000\316\054\013\122\121\142\046
-\175\014\047\203\217\305\366\332\240\150\173\117\222\136\352\244
-\163\062\021\123\104\262\104\313\235\354\017\171\102\263\020\246
-\307\015\235\313\266\372\077\072\174\352\277\210\123\033\074\367
-\202\372\005\065\063\341\065\250\127\300\347\375\215\117\077\223
-\062\117\170\146\003\167\007\130\351\225\310\176\076\320\171\000
-\214\362\033\121\063\233\274\224\351\072\173\156\122\055\062\236
-\043\244\105\373\266\056\023\260\213\030\261\335\316\325\035\247
-\102\177\125\276\373\133\273\107\324\374\044\315\004\256\226\005
-\025\326\254\316\060\363\312\013\305\272\342\042\340\246\255\042
-\344\002\356\164\021\177\114\377\170\035\065\332\346\002\064\353
-\030\022\141\167\006\011\026\143\352\030\255\242\207\037\362\307
-\200\011\011\165\116\020\250\217\075\206\270\165\021\300\044\142
-\212\226\173\112\105\351\354\131\305\276\153\203\346\341\350\254
-\265\060\036\376\005\007\200\371\341\043\015\120\217\005\230\377
-\054\137\350\073\266\255\317\201\265\041\207\312\010\052\043\047
-\060\040\053\317\355\224\133\254\262\172\322\307\050\241\212\013
-\233\115\112\054\155\205\077\011\162\074\147\342\331\334\007\272
-\353\145\173\132\001\143\326\220\133\117\027\146\075\177\013\031
-\243\223\143\020\122\052\237\024\026\130\342\334\245\364\241\026
-\213\016\221\213\201\312\233\131\372\330\153\221\007\145\125\137
-\122\037\257\072\373\220\335\151\245\133\234\155\016\054\266\372
-\316\254\245\174\062\112\147\100\334\060\064\043\335\327\004\043
-\146\360\374\125\200\247\373\146\031\202\065\147\142\160\071\136
-\157\307\352\220\100\104\010\036\270\262\326\333\356\131\247\015
-\030\171\064\274\124\030\136\123\312\064\121\355\105\012\346\216
-\307\202\066\076\247\070\143\251\060\054\027\020\140\222\237\125
-\207\022\131\020\302\017\147\151\021\314\116\036\176\112\232\255
-\257\100\250\165\254\126\220\164\270\240\234\245\171\157\334\351
-\032\310\151\005\351\272\372\003\263\174\344\340\116\302\316\235
-\350\266\106\015\156\176\127\072\147\224\302\313\037\234\167\112
-\147\116\151\206\103\223\070\373\266\333\117\203\221\324\140\176
-\113\076\053\070\007\125\230\136\244
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for Certificate "ACEDICOM Root"
-# Issuer: C=ES,O=EDICOM,OU=PKI,CN=ACEDICOM Root
-# Serial Number:61:8d:c7:86:3b:01:82:05
-# Subject: C=ES,O=EDICOM,OU=PKI,CN=ACEDICOM Root
-# Not Valid Before: Fri Apr 18 16:24:22 2008
-# Not Valid After : Thu Apr 13 16:24:22 2028
-# Fingerprint (MD5): 42:81:A0:E2:1C:E3:55:10:DE:55:89:42:65:96:22:E6
-# Fingerprint (SHA1): E0:B4:32:2E:B2:F6:A5:68:B6:54:53:84:48:18:4A:50:36:87:43:84
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "ACEDICOM Root"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\340\264\062\056\262\366\245\150\266\124\123\204\110\030\112\120
-\066\207\103\204
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\102\201\240\342\034\343\125\020\336\125\211\102\145\226\042\346
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\104\061\026\060\024\006\003\125\004\003\014\015\101\103\105
-\104\111\103\117\115\040\122\157\157\164\061\014\060\012\006\003
-\125\004\013\014\003\120\113\111\061\017\060\015\006\003\125\004
-\012\014\006\105\104\111\103\117\115\061\013\060\011\006\003\125
-\004\006\023\002\105\123
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\010\141\215\307\206\073\001\202\005
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-
-#
# Certificate "Microsec e-Szigno Root CA 2009"
#
# Issuer: E=info@e-szigno.hu,CN=Microsec e-Szigno Root CA 2009,O=Microsec Ltd.,L=Budapest,C=HU
@@ -11667,1725 +10621,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "Bogus Mozilla Addons"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:92:39:d5:34:8f:40:d1:69:5a:74:54:70:e1:f2:3f:43
-# Subject: CN=addons.mozilla.org,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 84:C5:18:67:1F:2A:1A:90:BE:E2:B1:18:4F:03:00:32
-# Fingerprint (SHA1): 30:5F:8B:D1:7A:A2:CB:C4:83:A4:C4:1B:19:A3:9A:0C:75:DA:39:D6
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Mozilla Addons"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\342\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\033\060\031\006\003\125\004
-\003\023\022\141\144\144\157\156\163\056\155\157\172\151\154\154
-\141\056\157\162\147
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\222\071\325\064\217\100\321\151\132\164\124\160\341
-\362\077\103
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\370\060\202\004\340\240\003\002\001\002\002\021\000
-\222\071\325\064\217\100\321\151\132\164\124\160\341\362\077\103
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025
-\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145
-\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025
-\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145
-\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030
-\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164
-\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004
-\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164
-\055\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060
-\063\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063
-\061\064\062\063\065\071\065\071\132\060\201\342\061\013\060\011
-\006\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125
-\004\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125
-\004\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006
-\003\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060
-\025\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154
-\141\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023
-\013\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021
-\006\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164
-\056\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164
-\145\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040
-\103\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006
-\003\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123
-\114\061\033\060\031\006\003\125\004\003\023\022\141\144\144\157
-\156\163\056\155\157\172\151\154\154\141\056\157\162\147\060\202
-\001\042\060\015\006\011\052\206\110\206\367\015\001\001\001\005
-\000\003\202\001\017\000\060\202\001\012\002\202\001\001\000\253
-\306\155\066\363\025\163\170\203\163\316\164\205\325\256\354\262
-\360\340\044\037\023\203\270\040\254\273\232\376\210\273\253\241
-\035\013\037\105\000\252\111\267\065\067\014\152\357\107\114\271
-\321\276\343\127\022\004\215\222\307\266\354\001\274\266\332\307
-\201\070\040\255\162\205\346\016\374\201\154\007\255\150\166\070
-\305\104\327\314\306\112\305\227\076\144\364\121\346\360\176\262
-\354\126\367\045\202\115\111\230\313\026\230\335\043\361\211\221
-\321\027\227\100\231\046\326\342\242\053\136\337\275\211\362\033
-\032\123\055\314\120\101\172\320\075\052\014\125\160\024\001\351
-\130\111\020\172\013\223\202\213\341\036\355\072\200\020\202\316
-\226\212\064\360\314\327\323\271\264\120\207\125\124\011\270\235
-\102\050\125\000\345\214\065\124\277\335\045\221\106\267\015\345
-\135\203\250\345\213\373\204\344\074\256\166\332\304\103\053\133
-\164\013\370\276\135\150\361\170\133\265\316\175\361\135\231\100
-\332\312\356\070\201\120\276\230\241\154\270\044\255\363\257\214
-\017\327\021\050\054\204\030\114\175\265\331\217\060\265\033\002
-\003\001\000\001\243\202\001\360\060\202\001\354\060\037\006\003
-\125\035\043\004\030\060\026\200\024\241\162\137\046\033\050\230
-\103\225\135\007\067\325\205\226\235\113\322\303\105\060\035\006
-\003\125\035\016\004\026\004\024\335\200\322\124\075\367\114\160
-\312\243\260\335\064\172\062\344\350\073\132\073\060\016\006\003
-\125\035\017\001\001\377\004\004\003\002\005\240\060\014\006\003
-\125\035\023\001\001\377\004\002\060\000\060\035\006\003\125\035
-\045\004\026\060\024\006\010\053\006\001\005\005\007\003\001\006
-\010\053\006\001\005\005\007\003\002\060\106\006\003\125\035\040
-\004\077\060\075\060\073\006\014\053\006\001\004\001\262\061\001
-\002\001\003\004\060\053\060\051\006\010\053\006\001\005\005\007
-\002\001\026\035\150\164\164\160\163\072\057\057\163\145\143\165
-\162\145\056\143\157\155\157\144\157\056\143\157\155\057\103\120
-\123\060\173\006\003\125\035\037\004\164\060\162\060\070\240\066
-\240\064\206\062\150\164\164\160\072\057\057\143\162\154\056\143
-\157\155\157\144\157\143\141\056\143\157\155\057\125\124\116\055
-\125\123\105\122\106\151\162\163\164\055\110\141\162\144\167\141
-\162\145\056\143\162\154\060\066\240\064\240\062\206\060\150\164
-\164\160\072\057\057\143\162\154\056\143\157\155\157\144\157\056
-\156\145\164\057\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145\056\143\162\154\060\161
-\006\010\053\006\001\005\005\007\001\001\004\145\060\143\060\073
-\006\010\053\006\001\005\005\007\060\002\206\057\150\164\164\160
-\072\057\057\143\162\164\056\143\157\155\157\144\157\143\141\056
-\143\157\155\057\125\124\116\101\144\144\124\162\165\163\164\123
-\145\162\166\145\162\103\101\056\143\162\164\060\044\006\010\053
-\006\001\005\005\007\060\001\206\030\150\164\164\160\072\057\057
-\157\143\163\160\056\143\157\155\157\144\157\143\141\056\143\157
-\155\060\065\006\003\125\035\021\004\056\060\054\202\022\141\144
-\144\157\156\163\056\155\157\172\151\154\154\141\056\157\162\147
-\202\026\167\167\167\056\141\144\144\157\156\163\056\155\157\172
-\151\154\154\141\056\157\162\147\060\015\006\011\052\206\110\206
-\367\015\001\001\005\005\000\003\202\001\001\000\063\073\143\025
-\374\261\354\024\054\223\335\165\224\336\201\132\331\116\231\276
-\373\112\244\071\125\115\241\100\172\336\023\052\207\251\067\317
-\350\325\373\255\321\173\155\157\214\040\207\202\124\346\127\111
-\274\040\050\204\315\326\001\331\223\213\027\156\043\146\345\204
-\310\200\077\306\241\160\200\344\354\115\035\371\374\221\132\163
-\142\051\232\367\040\034\141\340\213\071\237\312\274\176\215\335
-\274\331\261\343\237\236\337\025\123\221\041\122\013\331\032\043
-\017\146\066\333\254\223\226\112\243\245\042\317\051\367\242\231
-\250\366\266\331\100\256\331\176\266\366\130\056\233\254\066\312
-\144\217\145\122\334\206\234\202\253\156\120\113\332\137\372\005
-\000\210\060\016\336\215\126\277\201\107\215\075\006\342\262\142
-\222\147\217\236\310\232\262\345\006\270\160\044\270\167\174\043
-\012\070\303\171\010\330\261\121\235\254\225\021\307\100\027\236
-\243\034\217\362\021\247\150\047\332\111\005\204\030\174\130\055
-\001\147\134\345\237\241\051\273\112\071\105\057\277\021\252\171
-\242\355\264\324\265\145\103\267\223\106\212\323
-END
-
-# Trust for Certificate "Bogus Mozilla Addons"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:92:39:d5:34:8f:40:d1:69:5a:74:54:70:e1:f2:3f:43
-# Subject: CN=addons.mozilla.org,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 84:C5:18:67:1F:2A:1A:90:BE:E2:B1:18:4F:03:00:32
-# Fingerprint (SHA1): 30:5F:8B:D1:7A:A2:CB:C4:83:A4:C4:1B:19:A3:9A:0C:75:DA:39:D6
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Mozilla Addons"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\060\137\213\321\172\242\313\304\203\244\304\033\031\243\232\014
-\165\332\071\326
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\204\305\030\147\037\052\032\220\276\342\261\030\117\003\000\062
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\222\071\325\064\217\100\321\151\132\164\124\160\341
-\362\077\103
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus Global Trustee"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:d8:f3:5f:4e:b7:87:2b:2d:ab:06:92:e3:15:38:2f:b0
-# Subject: CN=global trustee,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Global Trustee,O=Global Trustee,STREET=Sea Village 10,L=Tampa,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): FE:0D:01:6E:71:CB:8C:D8:3F:0E:0C:CD:49:35:B8:57
-# Fingerprint (SHA1): 61:79:3F:CB:FA:4F:90:08:30:9B:BA:5F:F1:2D:2C:B2:9C:D4:15:1A
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Global Trustee"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\343\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\016\060\014\006\003\125\004\007\023\005\124\141\155
-\160\141\061\027\060\025\006\003\125\004\011\023\016\123\145\141
-\040\126\151\154\154\141\147\145\040\061\060\061\027\060\025\006
-\003\125\004\012\023\016\107\154\157\142\141\154\040\124\162\165
-\163\164\145\145\061\027\060\025\006\003\125\004\013\023\016\107
-\154\157\142\141\154\040\124\162\165\163\164\145\145\061\050\060
-\046\006\003\125\004\013\023\037\110\157\163\164\145\144\040\142
-\171\040\107\124\111\040\107\162\157\165\160\040\103\157\162\160
-\157\162\141\164\151\157\156\061\024\060\022\006\003\125\004\013
-\023\013\120\154\141\164\151\156\165\155\123\123\114\061\027\060
-\025\006\003\125\004\003\023\016\147\154\157\142\141\154\040\164
-\162\165\163\164\145\145
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\330\363\137\116\267\207\053\055\253\006\222\343\025
-\070\057\260
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\006\335\060\202\005\305\240\003\002\001\002\002\021\000
-\330\363\137\116\267\207\053\055\253\006\222\343\025\070\057\260
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025
-\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145
-\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025
-\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145
-\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030
-\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164
-\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004
-\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164
-\055\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060
-\063\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063
-\061\064\062\063\065\071\065\071\132\060\201\343\061\013\060\011
-\006\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125
-\004\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125
-\004\010\023\007\106\154\157\162\151\144\141\061\016\060\014\006
-\003\125\004\007\023\005\124\141\155\160\141\061\027\060\025\006
-\003\125\004\011\023\016\123\145\141\040\126\151\154\154\141\147
-\145\040\061\060\061\027\060\025\006\003\125\004\012\023\016\107
-\154\157\142\141\154\040\124\162\165\163\164\145\145\061\027\060
-\025\006\003\125\004\013\023\016\107\154\157\142\141\154\040\124
-\162\165\163\164\145\145\061\050\060\046\006\003\125\004\013\023
-\037\110\157\163\164\145\144\040\142\171\040\107\124\111\040\107
-\162\157\165\160\040\103\157\162\160\157\162\141\164\151\157\156
-\061\024\060\022\006\003\125\004\013\023\013\120\154\141\164\151
-\156\165\155\123\123\114\061\027\060\025\006\003\125\004\003\023
-\016\147\154\157\142\141\154\040\164\162\165\163\164\145\145\060
-\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001\001
-\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001\000
-\331\164\362\252\101\035\337\365\302\026\103\111\134\051\277\266
-\211\164\051\274\234\215\014\106\117\131\176\262\101\027\146\064
-\014\145\211\341\154\045\343\206\012\236\042\105\042\214\335\235
-\346\243\225\336\334\210\002\125\134\343\133\221\165\353\046\151
-\143\271\056\306\312\056\047\337\210\272\002\040\156\376\271\013
-\051\327\247\326\327\110\032\034\316\335\037\251\047\016\142\117
-\241\226\036\335\124\072\064\143\112\166\365\167\175\131\147\330
-\020\324\265\017\072\103\042\230\333\364\011\304\012\160\316\335
-\220\324\057\357\164\023\303\315\302\211\071\142\025\235\346\164
-\250\350\233\360\143\156\234\211\266\016\255\233\367\314\202\350
-\350\055\270\013\332\042\354\111\205\007\210\231\230\077\364\164
-\251\011\367\201\174\227\013\131\231\030\162\213\333\224\202\053
-\247\350\252\153\227\277\210\176\165\260\213\105\105\014\307\250
-\011\352\033\101\130\060\073\137\170\145\025\064\322\344\074\064
-\015\035\330\144\074\212\245\126\111\231\050\055\113\362\317\315
-\331\156\111\144\233\251\171\220\167\125\251\010\033\255\032\164
-\236\340\003\223\012\011\267\255\247\264\134\357\203\154\267\232
-\264\306\150\100\200\035\102\321\156\171\233\251\031\041\232\234
-\371\206\055\000\321\064\376\340\266\371\125\266\365\046\305\225
-\026\245\174\163\237\012\051\211\254\072\230\367\233\164\147\267
-\220\267\135\011\043\152\152\355\054\020\356\123\012\020\360\026
-\037\127\263\261\015\171\221\031\260\353\315\060\077\240\024\137
-\263\306\375\134\063\247\260\377\230\260\125\214\271\245\362\157
-\107\044\111\041\151\314\102\242\121\000\100\205\214\202\202\253
-\062\245\313\232\334\320\331\030\015\337\031\364\257\203\015\301
-\076\061\333\044\110\266\165\200\241\341\311\167\144\036\247\345
-\213\177\025\115\113\247\302\320\355\171\225\136\221\061\354\030
-\377\116\237\110\024\352\165\272\041\316\051\166\351\037\116\121
-\207\056\263\314\004\140\272\043\037\037\145\262\012\270\325\156
-\217\113\102\211\107\251\201\220\133\053\262\266\256\346\240\160
-\173\170\220\012\172\305\345\347\305\373\012\366\057\151\214\214
-\037\127\340\006\231\377\021\325\122\062\040\227\047\230\356\145
-\002\003\001\000\001\243\202\001\324\060\202\001\320\060\037\006
-\003\125\035\043\004\030\060\026\200\024\241\162\137\046\033\050
-\230\103\225\135\007\067\325\205\226\235\113\322\303\105\060\035
-\006\003\125\035\016\004\026\004\024\267\303\336\032\103\355\101
-\227\251\217\051\170\234\003\271\254\100\102\000\254\060\016\006
-\003\125\035\017\001\001\377\004\004\003\002\005\240\060\014\006
-\003\125\035\023\001\001\377\004\002\060\000\060\035\006\003\125
-\035\045\004\026\060\024\006\010\053\006\001\005\005\007\003\001
-\006\010\053\006\001\005\005\007\003\002\060\106\006\003\125\035
-\040\004\077\060\075\060\073\006\014\053\006\001\004\001\262\061
-\001\002\001\003\004\060\053\060\051\006\010\053\006\001\005\005
-\007\002\001\026\035\150\164\164\160\163\072\057\057\163\145\143
-\165\162\145\056\143\157\155\157\144\157\056\143\157\155\057\103
-\120\123\060\173\006\003\125\035\037\004\164\060\162\060\070\240
-\066\240\064\206\062\150\164\164\160\072\057\057\143\162\154\056
-\143\157\155\157\144\157\143\141\056\143\157\155\057\125\124\116
-\055\125\123\105\122\106\151\162\163\164\055\110\141\162\144\167
-\141\162\145\056\143\162\154\060\066\240\064\240\062\206\060\150
-\164\164\160\072\057\057\143\162\154\056\143\157\155\157\144\157
-\056\156\145\164\057\125\124\116\055\125\123\105\122\106\151\162
-\163\164\055\110\141\162\144\167\141\162\145\056\143\162\154\060
-\161\006\010\053\006\001\005\005\007\001\001\004\145\060\143\060
-\073\006\010\053\006\001\005\005\007\060\002\206\057\150\164\164
-\160\072\057\057\143\162\164\056\143\157\155\157\144\157\143\141
-\056\143\157\155\057\125\124\116\101\144\144\124\162\165\163\164
-\123\145\162\166\145\162\103\101\056\143\162\164\060\044\006\010
-\053\006\001\005\005\007\060\001\206\030\150\164\164\160\072\057
-\057\157\143\163\160\056\143\157\155\157\144\157\143\141\056\143
-\157\155\060\031\006\003\125\035\021\004\022\060\020\202\016\147
-\154\157\142\141\154\040\164\162\165\163\164\145\145\060\015\006
-\011\052\206\110\206\367\015\001\001\005\005\000\003\202\001\001
-\000\217\272\165\272\071\324\046\323\160\017\304\263\002\247\305
-\022\043\161\311\376\143\351\243\142\170\044\104\117\324\271\021
-\076\037\307\050\347\125\153\356\364\341\000\221\206\212\311\011
-\153\237\056\244\105\071\321\141\142\136\223\245\005\105\170\237
-\140\022\054\364\154\145\145\015\314\106\064\213\050\272\240\306
-\364\231\161\144\363\042\166\254\117\363\142\311\247\063\132\007
-\037\075\311\206\200\334\333\004\057\207\047\350\277\110\104\201
-\300\360\111\043\156\037\345\344\003\206\044\023\242\205\142\174
-\130\004\312\346\215\023\162\012\272\126\104\242\017\274\373\240
-\075\015\052\177\373\236\251\011\075\267\132\324\212\215\341\045
-\350\244\011\204\160\255\022\104\271\317\271\063\172\272\134\346
-\113\246\273\005\006\230\377\362\230\122\173\167\200\047\112\331
-\342\372\271\122\324\373\373\346\326\055\236\217\301\025\104\215
-\233\164\057\356\224\132\116\323\304\213\212\254\103\235\163\366
-\256\014\207\211\255\207\311\311\307\335\272\024\140\172\370\265
-\065\235\302\215\306\226\201\015\251\122\212\051\100\004\351\031
-\264
-END
-
-# Trust for Certificate "Bogus Global Trustee"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:d8:f3:5f:4e:b7:87:2b:2d:ab:06:92:e3:15:38:2f:b0
-# Subject: CN=global trustee,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Global Trustee,O=Global Trustee,STREET=Sea Village 10,L=Tampa,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): FE:0D:01:6E:71:CB:8C:D8:3F:0E:0C:CD:49:35:B8:57
-# Fingerprint (SHA1): 61:79:3F:CB:FA:4F:90:08:30:9B:BA:5F:F1:2D:2C:B2:9C:D4:15:1A
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Global Trustee"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\141\171\077\313\372\117\220\010\060\233\272\137\361\055\054\262
-\234\324\025\032
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\376\015\001\156\161\313\214\330\077\016\014\315\111\065\270\127
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\330\363\137\116\267\207\053\055\253\006\222\343\025
-\070\057\260
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus GMail"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:04:7e:cb:e9:fc:a5:5f:7b:d0:9e:ae:36:e1:0c:ae:1e
-# Subject: CN=mail.google.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 4C:77:1F:EB:CA:31:C1:29:98:E9:2C:10:B3:AF:49:1C
-# Fingerprint (SHA1): 64:31:72:30:36:FD:26:DE:A5:02:79:2F:A5:95:92:24:93:03:0F:97
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus GMail"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\337\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\030\060\026\006\003\125\004
-\003\023\017\155\141\151\154\056\147\157\157\147\154\145\056\143
-\157\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\004\176\313\351\374\245\137\173\320\236\256\066\341\014
-\256\036
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\356\060\202\004\326\240\003\002\001\002\002\020\004
-\176\313\351\374\245\137\173\320\236\256\066\341\014\256\036\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\201
-\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061\013
-\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025\006
-\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145\040
-\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025\124
-\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145\164
-\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030\150
-\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164\162
-\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004\003
-\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164\055
-\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060\063
-\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063\061
-\064\062\063\065\071\065\071\132\060\201\337\061\013\060\011\006
-\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125\004
-\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125\004
-\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006\003
-\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060\025
-\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154\141
-\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023\013
-\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021\006
-\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164\056
-\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164\145
-\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040\103
-\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006\003
-\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123\114
-\061\030\060\026\006\003\125\004\003\023\017\155\141\151\154\056
-\147\157\157\147\154\145\056\143\157\155\060\202\001\042\060\015
-\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202\001
-\017\000\060\202\001\012\002\202\001\001\000\260\163\360\362\004
-\356\302\242\106\312\064\052\252\273\140\043\321\021\166\037\037
-\072\320\145\203\116\232\105\250\103\160\205\166\360\037\207\000
-\002\037\156\073\027\027\304\265\351\031\106\242\222\045\215\142
-\052\264\143\060\037\271\205\370\065\341\026\132\166\111\314\120
-\110\123\071\131\211\326\204\002\373\232\354\033\307\121\325\166
-\225\220\324\072\052\270\246\336\002\115\006\373\315\355\245\106
-\101\137\125\164\345\354\176\100\334\120\234\265\344\065\135\036
-\150\040\370\351\336\243\152\050\277\101\322\241\263\342\045\215
-\014\033\312\075\223\014\030\256\337\305\274\375\274\202\272\150
-\000\327\026\062\161\237\145\265\021\332\150\131\320\246\127\144
-\033\311\376\230\345\365\245\145\352\341\333\356\364\263\235\263
-\216\352\207\256\026\322\036\240\174\174\151\077\051\026\205\001
-\123\247\154\361\140\253\335\242\374\045\107\324\062\321\022\335
-\367\110\022\340\374\234\242\167\230\351\211\231\270\370\070\361
-\214\006\302\172\043\066\155\233\235\315\060\310\307\064\027\036
-\273\175\102\310\253\347\025\026\366\163\265\002\003\001\000\001
-\243\202\001\352\060\202\001\346\060\037\006\003\125\035\043\004
-\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135\007
-\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035\016
-\004\026\004\024\030\052\242\310\324\172\077\173\255\004\213\275
-\157\236\020\106\023\170\161\235\060\016\006\003\125\035\017\001
-\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023\001
-\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026\060
-\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006\001
-\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060\075
-\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003\004
-\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026\035
-\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056\143
-\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173\006
-\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206\062
-\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157\144
-\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105\122
-\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056\143
-\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072\057
-\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164\057
-\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110\141
-\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053\006
-\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053\006
-\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057\143
-\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155\057
-\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166\145
-\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005\005
-\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163\160
-\056\143\157\155\157\144\157\143\141\056\143\157\155\060\057\006
-\003\125\035\021\004\050\060\046\202\017\155\141\151\154\056\147
-\157\157\147\154\145\056\143\157\155\202\023\167\167\167\056\155
-\141\151\154\056\147\157\157\147\154\145\056\143\157\155\060\015
-\006\011\052\206\110\206\367\015\001\001\005\005\000\003\202\001
-\001\000\147\006\010\012\047\305\223\156\002\362\336\027\077\320
-\323\033\174\377\265\315\172\307\167\307\276\337\022\312\031\336
-\260\023\127\014\003\221\304\171\122\317\177\267\136\125\040\204
-\111\335\365\320\051\057\016\004\332\131\236\016\023\237\364\300
-\062\233\377\241\021\044\052\227\243\362\077\075\052\153\250\255
-\214\031\165\225\016\035\045\375\117\304\172\025\303\035\307\023
-\100\310\015\276\227\140\162\246\376\045\276\217\354\325\246\206
-\303\041\134\131\122\331\152\013\134\237\113\336\265\371\354\342
-\364\305\314\142\123\166\211\145\344\051\332\267\277\226\340\140
-\215\015\267\011\125\326\100\125\035\301\362\226\041\165\257\211
-\206\037\135\201\227\051\050\036\051\327\226\301\040\003\062\173
-\000\073\152\067\027\132\243\263\032\157\062\073\156\361\243\135
-\253\253\314\052\313\060\014\037\065\043\213\151\104\134\352\254
-\050\140\355\253\153\143\236\366\222\274\275\232\132\046\114\305
-\230\270\016\031\076\374\005\061\343\026\331\375\220\005\003\206
-\306\127\001\037\177\170\240\317\063\152\252\146\153\042\320\247
-\111\043
-END
-
-# Trust for Certificate "Bogus GMail"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:04:7e:cb:e9:fc:a5:5f:7b:d0:9e:ae:36:e1:0c:ae:1e
-# Subject: CN=mail.google.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 4C:77:1F:EB:CA:31:C1:29:98:E9:2C:10:B3:AF:49:1C
-# Fingerprint (SHA1): 64:31:72:30:36:FD:26:DE:A5:02:79:2F:A5:95:92:24:93:03:0F:97
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus GMail"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\144\061\162\060\066\375\046\336\245\002\171\057\245\225\222\044
-\223\003\017\227
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\114\167\037\353\312\061\301\051\230\351\054\020\263\257\111\034
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\004\176\313\351\374\245\137\173\320\236\256\066\341\014
-\256\036
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus Google"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:f5:c8:6a:f3:61:62:f1:3a:64:f5:4f:6d:c9:58:7c:06
-# Subject: CN=www.google.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 01:73:A9:58:F0:BC:C9:BE:94:2B:1A:4C:98:24:E3:B8
-# Fingerprint (SHA1): 19:16:A2:AF:34:6D:39:9F:50:31:3C:39:32:00:F1:41:40:45:66:16
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Google"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\336\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\027\060\025\006\003\125\004
-\003\023\016\167\167\167\056\147\157\157\147\154\145\056\143\157
-\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\365\310\152\363\141\142\361\072\144\365\117\155\311
-\130\174\006
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\344\060\202\004\314\240\003\002\001\002\002\021\000
-\365\310\152\363\141\142\361\072\144\365\117\155\311\130\174\006
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025
-\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145
-\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025
-\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145
-\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030
-\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164
-\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004
-\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164
-\055\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060
-\063\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063
-\061\064\062\063\065\071\065\071\132\060\201\336\061\013\060\011
-\006\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125
-\004\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125
-\004\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006
-\003\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060
-\025\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154
-\141\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023
-\013\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021
-\006\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164
-\056\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164
-\145\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040
-\103\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006
-\003\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123
-\114\061\027\060\025\006\003\125\004\003\023\016\167\167\167\056
-\147\157\157\147\154\145\056\143\157\155\060\202\001\042\060\015
-\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202\001
-\017\000\060\202\001\012\002\202\001\001\000\260\163\360\362\004
-\356\302\242\106\312\064\052\252\273\140\043\321\021\166\037\037
-\072\320\145\203\116\232\105\250\103\160\205\166\360\037\207\000
-\002\037\156\073\027\027\304\265\351\031\106\242\222\045\215\142
-\052\264\143\060\037\271\205\370\065\341\026\132\166\111\314\120
-\110\123\071\131\211\326\204\002\373\232\354\033\307\121\325\166
-\225\220\324\072\052\270\246\336\002\115\006\373\315\355\245\106
-\101\137\125\164\345\354\176\100\334\120\234\265\344\065\135\036
-\150\040\370\351\336\243\152\050\277\101\322\241\263\342\045\215
-\014\033\312\075\223\014\030\256\337\305\274\375\274\202\272\150
-\000\327\026\062\161\237\145\265\021\332\150\131\320\246\127\144
-\033\311\376\230\345\365\245\145\352\341\333\356\364\263\235\263
-\216\352\207\256\026\322\036\240\174\174\151\077\051\026\205\001
-\123\247\154\361\140\253\335\242\374\045\107\324\062\321\022\335
-\367\110\022\340\374\234\242\167\230\351\211\231\270\370\070\361
-\214\006\302\172\043\066\155\233\235\315\060\310\307\064\027\036
-\273\175\102\310\253\347\025\026\366\163\265\002\003\001\000\001
-\243\202\001\340\060\202\001\334\060\037\006\003\125\035\043\004
-\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135\007
-\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035\016
-\004\026\004\024\030\052\242\310\324\172\077\173\255\004\213\275
-\157\236\020\106\023\170\161\235\060\016\006\003\125\035\017\001
-\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023\001
-\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026\060
-\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006\001
-\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060\075
-\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003\004
-\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026\035
-\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056\143
-\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173\006
-\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206\062
-\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157\144
-\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105\122
-\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056\143
-\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072\057
-\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164\057
-\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110\141
-\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053\006
-\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053\006
-\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057\143
-\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155\057
-\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166\145
-\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005\005
-\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163\160
-\056\143\157\155\157\144\157\143\141\056\143\157\155\060\045\006
-\003\125\035\021\004\036\060\034\202\016\167\167\167\056\147\157
-\157\147\154\145\056\143\157\155\202\012\147\157\157\147\154\145
-\056\143\157\155\060\015\006\011\052\206\110\206\367\015\001\001
-\005\005\000\003\202\001\001\000\161\300\231\077\136\366\275\063
-\377\236\026\313\250\277\335\160\371\322\123\073\066\256\311\027
-\310\256\136\115\335\142\367\267\323\076\167\243\376\300\173\062
-\265\311\224\005\122\120\362\137\075\171\204\111\117\135\154\260
-\327\131\275\324\154\210\372\374\305\145\206\353\050\122\242\102
-\366\174\274\152\307\007\056\045\321\220\142\040\306\215\121\302
-\054\105\071\116\003\332\367\030\350\314\012\072\331\105\330\154
-\156\064\213\142\234\116\025\371\103\356\345\227\300\077\255\065
-\023\305\053\006\307\101\375\342\367\176\105\255\233\321\341\146
-\355\370\172\113\224\071\172\057\353\350\077\103\330\065\326\126
-\372\164\347\155\346\355\254\145\204\376\320\115\006\022\336\332
-\131\000\074\011\134\317\210\113\350\075\264\025\041\222\314\155
-\246\121\342\216\227\361\364\202\106\313\304\123\136\332\134\235
-\145\222\001\145\211\000\345\266\231\377\046\100\361\057\031\061
-\010\032\261\147\125\206\015\256\065\063\206\274\227\110\222\327
-\226\140\370\316\374\226\353\207\304\163\314\224\233\130\133\363
-\172\244\047\023\326\117\364\151
-END
-
-# Trust for Certificate "Bogus Google"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:f5:c8:6a:f3:61:62:f1:3a:64:f5:4f:6d:c9:58:7c:06
-# Subject: CN=www.google.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 01:73:A9:58:F0:BC:C9:BE:94:2B:1A:4C:98:24:E3:B8
-# Fingerprint (SHA1): 19:16:A2:AF:34:6D:39:9F:50:31:3C:39:32:00:F1:41:40:45:66:16
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Google"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\031\026\242\257\064\155\071\237\120\061\074\071\062\000\361\101
-\100\105\146\026
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\001\163\251\130\360\274\311\276\224\053\032\114\230\044\343\270
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\365\310\152\363\141\142\361\072\144\365\117\155\311
-\130\174\006
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus Skype"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:e9:02:8b:95:78:e4:15:dc:1a:71:0a:2b:88:15:44:47
-# Subject: CN=login.skype.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 85:A4:B4:C4:69:21:DF:A1:6A:0D:58:56:58:4B:33:44
-# Fingerprint (SHA1): 47:1C:94:9A:81:43:DB:5A:D5:CD:F1:C9:72:86:4A:25:04:FA:23:C9
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Skype"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\337\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\030\060\026\006\003\125\004
-\003\023\017\154\157\147\151\156\056\163\153\171\160\145\056\143
-\157\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\351\002\213\225\170\344\025\334\032\161\012\053\210
-\025\104\107
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\357\060\202\004\327\240\003\002\001\002\002\021\000
-\351\002\213\225\170\344\025\334\032\161\012\053\210\025\104\107
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025
-\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145
-\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025
-\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145
-\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030
-\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164
-\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004
-\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164
-\055\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060
-\063\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063
-\061\064\062\063\065\071\065\071\132\060\201\337\061\013\060\011
-\006\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125
-\004\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125
-\004\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006
-\003\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060
-\025\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154
-\141\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023
-\013\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021
-\006\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164
-\056\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164
-\145\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040
-\103\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006
-\003\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123
-\114\061\030\060\026\006\003\125\004\003\023\017\154\157\147\151
-\156\056\163\153\171\160\145\056\143\157\155\060\202\001\042\060
-\015\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202
-\001\017\000\060\202\001\012\002\202\001\001\000\260\170\231\206
-\016\242\163\043\324\132\303\111\353\261\066\214\174\312\204\256
-\074\257\070\210\050\231\215\055\130\023\261\227\170\076\122\040
-\147\254\133\163\230\154\062\125\311\160\321\331\252\025\350\056
-\046\205\201\274\126\344\274\200\143\333\116\327\365\002\276\121
-\143\036\074\333\337\327\000\135\132\271\345\173\152\352\070\040
-\262\073\266\356\165\124\204\371\246\312\070\160\335\277\260\377
-\245\205\135\264\101\376\335\075\331\052\341\060\103\032\230\171
-\223\240\137\340\147\154\225\372\076\172\256\161\173\343\155\210
-\102\077\045\324\356\276\150\150\254\255\254\140\340\040\243\071
-\203\271\133\050\243\223\155\241\275\166\012\343\353\256\207\047
-\016\124\217\264\110\014\232\124\364\135\216\067\120\334\136\244
-\213\153\113\334\246\363\064\276\167\131\042\210\377\031\053\155
-\166\144\163\332\014\207\007\053\232\067\072\320\342\214\366\066
-\062\153\232\171\314\322\073\223\157\032\115\154\346\301\235\100
-\254\055\164\303\276\352\134\163\145\001\051\261\052\277\160\131
-\301\316\306\303\242\310\105\137\272\147\075\017\002\003\001\000
-\001\243\202\001\352\060\202\001\346\060\037\006\003\125\035\043
-\004\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135
-\007\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035
-\016\004\026\004\024\325\216\132\121\023\264\051\015\061\266\034
-\215\076\121\121\061\012\063\252\201\060\016\006\003\125\035\017
-\001\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023
-\001\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026
-\060\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006
-\001\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060
-\075\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003
-\004\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026
-\035\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056
-\143\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173
-\006\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206
-\062\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157
-\144\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105
-\122\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056
-\143\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072
-\057\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164
-\057\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110
-\141\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053
-\006\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053
-\006\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057
-\143\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155
-\057\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166
-\145\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005
-\005\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163
-\160\056\143\157\155\157\144\157\143\141\056\143\157\155\060\057
-\006\003\125\035\021\004\050\060\046\202\017\154\157\147\151\156
-\056\163\153\171\160\145\056\143\157\155\202\023\167\167\167\056
-\154\157\147\151\156\056\163\153\171\160\145\056\143\157\155\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\003\202
-\001\001\000\010\362\201\165\221\273\316\022\004\030\302\115\132
-\373\106\220\012\124\104\364\362\335\007\201\360\037\246\172\157
-\237\317\270\016\054\117\234\304\232\365\250\366\272\244\311\172
-\135\261\342\132\312\074\372\140\250\150\076\313\272\055\342\315
-\326\266\344\222\074\151\255\127\352\250\057\070\020\204\162\345
-\150\161\355\276\353\156\030\357\143\172\276\347\044\377\300\143
-\375\130\073\114\201\222\330\051\253\216\065\135\327\323\011\153
-\205\323\325\163\005\104\342\345\273\203\123\020\313\362\317\267
-\156\341\151\267\241\222\144\305\317\315\202\273\066\240\070\255
-\327\044\337\123\374\077\142\267\267\325\307\127\343\223\061\160
-\216\044\211\206\312\143\053\071\272\135\331\152\140\354\241\116
-\212\376\123\370\136\222\337\057\134\046\027\155\003\175\002\017
-\017\252\103\147\155\260\142\277\176\123\335\314\354\170\163\225
-\345\245\366\000\243\004\375\077\004\052\263\230\305\267\003\034
-\333\311\120\253\260\005\035\036\276\126\264\317\076\102\023\224
-\236\371\347\001\201\245\170\157\014\172\166\254\005\206\354\254
-\302\021\254
-END
-
-# Trust for Certificate "Bogus Skype"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:e9:02:8b:95:78:e4:15:dc:1a:71:0a:2b:88:15:44:47
-# Subject: CN=login.skype.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 85:A4:B4:C4:69:21:DF:A1:6A:0D:58:56:58:4B:33:44
-# Fingerprint (SHA1): 47:1C:94:9A:81:43:DB:5A:D5:CD:F1:C9:72:86:4A:25:04:FA:23:C9
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Skype"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\107\034\224\232\201\103\333\132\325\315\361\311\162\206\112\045
-\004\372\043\311
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\205\244\264\304\151\041\337\241\152\015\130\126\130\113\063\104
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\351\002\213\225\170\344\025\334\032\161\012\053\210
-\025\104\107
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus Yahoo 1"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:d7:55:8f:da:f5:f1:10:5b:b2:13:28:2b:70:77:29:a3
-# Subject: CN=login.yahoo.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 0C:1F:BE:D3:FC:09:6E:E6:6E:C2:66:39:75:86:6B:EB
-# Fingerprint (SHA1): 63:FE:AE:96:0B:AA:91:E3:43:CE:2B:D8:B7:17:98:C7:6B:DB:77:D0
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Yahoo 1"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\337\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\030\060\026\006\003\125\004
-\003\023\017\154\157\147\151\156\056\171\141\150\157\157\056\143
-\157\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\327\125\217\332\365\361\020\133\262\023\050\053\160
-\167\051\243
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\357\060\202\004\327\240\003\002\001\002\002\021\000
-\327\125\217\332\365\361\020\133\262\023\050\053\160\167\051\243
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025
-\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145
-\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025
-\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145
-\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030
-\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164
-\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004
-\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164
-\055\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060
-\063\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063
-\061\064\062\063\065\071\065\071\132\060\201\337\061\013\060\011
-\006\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125
-\004\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125
-\004\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006
-\003\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060
-\025\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154
-\141\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023
-\013\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021
-\006\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164
-\056\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164
-\145\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040
-\103\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006
-\003\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123
-\114\061\030\060\026\006\003\125\004\003\023\017\154\157\147\151
-\156\056\171\141\150\157\157\056\143\157\155\060\202\001\042\060
-\015\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202
-\001\017\000\060\202\001\012\002\202\001\001\000\241\244\005\075
-\355\205\105\223\212\030\115\306\003\000\127\342\100\167\360\034
-\353\320\031\337\042\135\010\177\321\007\074\101\211\106\027\243
-\011\372\374\370\251\004\321\226\217\253\327\117\074\371\255\030
-\251\164\201\304\127\012\072\046\026\316\142\076\274\077\154\041
-\356\223\215\313\015\240\037\232\226\320\217\255\365\223\223\202
-\356\162\014\241\165\025\243\173\204\126\270\255\377\122\021\161
-\204\274\072\060\013\176\230\250\341\250\077\067\122\320\361\174
-\157\220\330\105\012\254\071\162\152\141\325\273\303\214\371\302
-\314\337\375\072\161\271\257\274\334\072\334\014\266\261\322\321
-\211\273\101\266\362\336\127\325\025\337\374\375\342\061\305\337
-\312\301\330\217\054\277\360\016\133\161\340\064\161\303\305\115
-\175\172\324\372\355\060\113\057\352\266\056\236\223\074\342\072
-\370\102\242\032\356\334\337\315\017\251\366\171\204\032\216\154
-\002\266\206\345\277\121\152\146\370\363\234\323\131\014\173\245
-\231\170\315\174\231\372\306\226\107\330\062\324\164\166\016\167
-\113\040\164\244\267\211\165\222\112\264\133\125\002\003\001\000
-\001\243\202\001\352\060\202\001\346\060\037\006\003\125\035\043
-\004\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135
-\007\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035
-\016\004\026\004\024\206\111\105\374\063\031\063\324\004\355\047
-\141\356\350\001\311\014\177\057\176\060\016\006\003\125\035\017
-\001\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023
-\001\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026
-\060\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006
-\001\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060
-\075\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003
-\004\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026
-\035\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056
-\143\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173
-\006\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206
-\062\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157
-\144\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105
-\122\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056
-\143\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072
-\057\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164
-\057\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110
-\141\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053
-\006\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053
-\006\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057
-\143\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155
-\057\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166
-\145\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005
-\005\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163
-\160\056\143\157\155\157\144\157\143\141\056\143\157\155\060\057
-\006\003\125\035\021\004\050\060\046\202\017\154\157\147\151\156
-\056\171\141\150\157\157\056\143\157\155\202\023\167\167\167\056
-\154\157\147\151\156\056\171\141\150\157\157\056\143\157\155\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\003\202
-\001\001\000\075\127\311\110\044\134\356\144\201\365\256\276\125
-\051\026\377\052\057\204\355\331\370\243\003\310\060\146\273\310
-\324\201\055\041\367\010\367\254\226\102\232\101\165\172\272\135
-\020\043\313\222\102\141\372\212\332\155\145\064\031\345\251\326
-\055\023\170\327\201\104\222\251\156\200\143\025\313\376\065\037
-\002\321\212\024\260\250\314\224\040\073\250\032\360\135\066\120
-\333\015\256\351\144\344\366\215\151\175\060\310\024\027\000\112
-\345\246\065\373\175\015\042\235\171\166\122\054\274\227\006\210
-\232\025\364\163\346\361\365\230\245\315\007\104\221\270\247\150
-\147\105\322\162\021\140\342\161\267\120\125\342\212\251\015\326
-\222\356\004\052\213\060\240\242\005\106\064\155\222\306\073\252
-\115\240\320\253\001\031\012\062\267\350\343\317\361\322\227\111
-\173\254\244\227\367\360\127\256\143\167\232\177\226\332\115\375
-\276\334\007\066\343\045\275\211\171\216\051\022\023\213\210\007
-\373\153\333\244\315\263\055\047\351\324\312\140\327\205\123\373
-\164\306\134\065\214\160\037\371\262\267\222\047\040\307\224\325
-\147\024\060
-END
-
-# Trust for Certificate "Bogus Yahoo 1"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:d7:55:8f:da:f5:f1:10:5b:b2:13:28:2b:70:77:29:a3
-# Subject: CN=login.yahoo.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 0C:1F:BE:D3:FC:09:6E:E6:6E:C2:66:39:75:86:6B:EB
-# Fingerprint (SHA1): 63:FE:AE:96:0B:AA:91:E3:43:CE:2B:D8:B7:17:98:C7:6B:DB:77:D0
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Yahoo 1"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\143\376\256\226\013\252\221\343\103\316\053\330\267\027\230\307
-\153\333\167\320
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\014\037\276\323\374\011\156\346\156\302\146\071\165\206\153\353
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\327\125\217\332\365\361\020\133\262\023\050\053\160
-\167\051\243
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus Yahoo 2"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:39:2a:43:4f:0e:07:df:1f:8a:a3:05:de:34:e0:c2:29
-# Subject: CN=login.yahoo.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 72:DC:C8:72:6C:53:3B:B2:FD:CC:5D:19:BD:AF:A6:31
-# Fingerprint (SHA1): D0:18:B6:2D:C5:18:90:72:47:DF:50:92:5B:B0:9A:CF:4A:5C:B3:AD
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Yahoo 2"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\337\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\030\060\026\006\003\125\004
-\003\023\017\154\157\147\151\156\056\171\141\150\157\157\056\143
-\157\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\071\052\103\117\016\007\337\037\212\243\005\336\064\340
-\302\051
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\331\060\202\004\301\240\003\002\001\002\002\020\071
-\052\103\117\016\007\337\037\212\243\005\336\064\340\302\051\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\201
-\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061\013
-\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025\006
-\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145\040
-\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025\124
-\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145\164
-\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030\150
-\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164\162
-\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004\003
-\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164\055
-\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060\063
-\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063\061
-\064\062\063\065\071\065\071\132\060\201\337\061\013\060\011\006
-\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125\004
-\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125\004
-\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006\003
-\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060\025
-\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154\141
-\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023\013
-\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021\006
-\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164\056
-\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164\145
-\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040\103
-\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006\003
-\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123\114
-\061\030\060\026\006\003\125\004\003\023\017\154\157\147\151\156
-\056\171\141\150\157\157\056\143\157\155\060\202\001\042\060\015
-\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202\001
-\017\000\060\202\001\012\002\202\001\001\000\241\244\005\075\355
-\205\105\223\212\030\115\306\003\000\127\342\100\167\360\034\353
-\320\031\337\042\135\010\177\321\007\074\101\211\106\027\243\011
-\372\374\370\251\004\321\226\217\253\327\117\074\371\255\030\251
-\164\201\304\127\012\072\046\026\316\142\076\274\077\154\041\356
-\223\215\313\015\240\037\232\226\320\217\255\365\223\223\202\356
-\162\014\241\165\025\243\173\204\126\270\255\377\122\021\161\204
-\274\072\060\013\176\230\250\341\250\077\067\122\320\361\174\157
-\220\330\105\012\254\071\162\152\141\325\273\303\214\371\302\314
-\337\375\072\161\271\257\274\334\072\334\014\266\261\322\321\211
-\273\101\266\362\336\127\325\025\337\374\375\342\061\305\337\312
-\301\330\217\054\277\360\016\133\161\340\064\161\303\305\115\175
-\172\324\372\355\060\113\057\352\266\056\236\223\074\342\072\370
-\102\242\032\356\334\337\315\017\251\366\171\204\032\216\154\002
-\266\206\345\277\121\152\146\370\363\234\323\131\014\173\245\231
-\170\315\174\231\372\306\226\107\330\062\324\164\166\016\167\113
-\040\164\244\267\211\165\222\112\264\133\125\002\003\001\000\001
-\243\202\001\325\060\202\001\321\060\037\006\003\125\035\043\004
-\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135\007
-\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035\016
-\004\026\004\024\206\111\105\374\063\031\063\324\004\355\047\141
-\356\350\001\311\014\177\057\176\060\016\006\003\125\035\017\001
-\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023\001
-\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026\060
-\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006\001
-\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060\075
-\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003\004
-\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026\035
-\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056\143
-\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173\006
-\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206\062
-\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157\144
-\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105\122
-\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056\143
-\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072\057
-\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164\057
-\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110\141
-\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053\006
-\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053\006
-\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057\143
-\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155\057
-\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166\145
-\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005\005
-\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163\160
-\056\143\157\155\157\144\157\143\141\056\143\157\155\060\032\006
-\003\125\035\021\004\023\060\021\202\017\154\157\147\151\156\056
-\171\141\150\157\157\056\143\157\155\060\015\006\011\052\206\110
-\206\367\015\001\001\005\005\000\003\202\001\001\000\127\142\341
-\167\353\374\037\277\210\123\257\130\323\324\326\155\147\060\027
-\100\276\340\037\144\336\207\025\314\340\244\126\251\321\237\371
-\001\376\002\261\261\352\342\137\356\161\026\061\371\010\325\302
-\327\232\233\262\132\070\327\251\177\351\207\153\061\371\013\254
-\331\375\120\161\340\333\202\222\017\201\234\215\167\351\353\056
-\352\324\043\101\207\354\055\262\170\263\216\261\147\322\356\161
-\003\010\022\231\263\002\051\157\336\213\336\301\251\003\012\132
-\063\034\075\021\003\306\110\014\230\234\025\056\331\246\205\122
-\347\005\212\256\060\043\353\355\050\154\140\351\055\177\217\107
-\213\057\320\334\346\273\017\176\137\362\110\201\216\120\004\143
-\261\121\200\165\232\251\266\020\034\020\137\157\030\157\340\016
-\226\105\316\356\361\265\040\333\357\332\156\310\225\343\366\105
-\375\312\374\245\137\111\155\006\036\322\336\141\075\025\175\067
-\345\034\065\216\006\302\153\367\264\250\050\054\061\313\252\264
-\247\227\117\235\212\366\257\176\067\271\173\075\337\222\146\213
-\217\116\235\306\066\347\134\246\253\022\017\326\317
-END
-
-# Trust for Certificate "Bogus Yahoo 2"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:39:2a:43:4f:0e:07:df:1f:8a:a3:05:de:34:e0:c2:29
-# Subject: CN=login.yahoo.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 72:DC:C8:72:6C:53:3B:B2:FD:CC:5D:19:BD:AF:A6:31
-# Fingerprint (SHA1): D0:18:B6:2D:C5:18:90:72:47:DF:50:92:5B:B0:9A:CF:4A:5C:B3:AD
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Yahoo 2"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\320\030\266\055\305\030\220\162\107\337\120\222\133\260\232\317
-\112\134\263\255
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\162\334\310\162\154\123\073\262\375\314\135\031\275\257\246\061
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\071\052\103\117\016\007\337\037\212\243\005\336\064\340
-\302\051
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus Yahoo 3"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:3e:75:ce:d4:6b:69:30:21:21:88:30:ae:86:a8:2a:71
-# Subject: CN=login.yahoo.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 4A:DC:3C:67:ED:21:CD:5B:CE:5D:C8:11:E4:9E:CF:3D
-# Fingerprint (SHA1): 80:96:2A:E4:D6:C5:B4:42:89:4E:95:A1:3E:4A:69:9E:07:D6:94:CF
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Yahoo 3"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\337\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\030\060\026\006\003\125\004
-\003\023\017\154\157\147\151\156\056\171\141\150\157\157\056\143
-\157\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\076\165\316\324\153\151\060\041\041\210\060\256\206\250
-\052\161
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\331\060\202\004\301\240\003\002\001\002\002\020\076
-\165\316\324\153\151\060\041\041\210\060\256\206\250\052\161\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\201
-\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061\013
-\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025\006
-\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145\040
-\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025\124
-\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145\164
-\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030\150
-\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164\162
-\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004\003
-\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164\055
-\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060\063
-\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063\061
-\064\062\063\065\071\065\071\132\060\201\337\061\013\060\011\006
-\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125\004
-\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125\004
-\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006\003
-\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060\025
-\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154\141
-\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023\013
-\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021\006
-\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164\056
-\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164\145
-\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040\103
-\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006\003
-\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123\114
-\061\030\060\026\006\003\125\004\003\023\017\154\157\147\151\156
-\056\171\141\150\157\157\056\143\157\155\060\202\001\042\060\015
-\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202\001
-\017\000\060\202\001\012\002\202\001\001\000\241\244\005\075\355
-\205\105\223\212\030\115\306\003\000\127\342\100\167\360\034\353
-\320\031\337\042\135\010\177\321\007\074\101\211\106\027\243\011
-\372\374\370\251\004\321\226\217\253\327\117\074\371\255\030\251
-\164\201\304\127\012\072\046\026\316\142\076\274\077\154\041\356
-\223\215\313\015\240\037\232\226\320\217\255\365\223\223\202\356
-\162\014\241\165\025\243\173\204\126\270\255\377\122\021\161\204
-\274\072\060\013\176\230\250\341\250\077\067\122\320\361\174\157
-\220\330\105\012\254\071\162\152\141\325\273\303\214\371\302\314
-\337\375\072\161\271\257\274\334\072\334\014\266\261\322\321\211
-\273\101\266\362\336\127\325\025\337\374\375\342\061\305\337\312
-\301\330\217\054\277\360\016\133\161\340\064\161\303\305\115\175
-\172\324\372\355\060\113\057\352\266\056\236\223\074\342\072\370
-\102\242\032\356\334\337\315\017\251\366\171\204\032\216\154\002
-\266\206\345\277\121\152\146\370\363\234\323\131\014\173\245\231
-\170\315\174\231\372\306\226\107\330\062\324\164\166\016\167\113
-\040\164\244\267\211\165\222\112\264\133\125\002\003\001\000\001
-\243\202\001\325\060\202\001\321\060\037\006\003\125\035\043\004
-\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135\007
-\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035\016
-\004\026\004\024\206\111\105\374\063\031\063\324\004\355\047\141
-\356\350\001\311\014\177\057\176\060\016\006\003\125\035\017\001
-\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023\001
-\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026\060
-\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006\001
-\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060\075
-\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003\004
-\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026\035
-\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056\143
-\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173\006
-\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206\062
-\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157\144
-\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105\122
-\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056\143
-\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072\057
-\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164\057
-\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110\141
-\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053\006
-\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053\006
-\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057\143
-\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155\057
-\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166\145
-\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005\005
-\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163\160
-\056\143\157\155\157\144\157\143\141\056\143\157\155\060\032\006
-\003\125\035\021\004\023\060\021\202\017\154\157\147\151\156\056
-\171\141\150\157\157\056\143\157\155\060\015\006\011\052\206\110
-\206\367\015\001\001\005\005\000\003\202\001\001\000\123\151\230
-\216\050\116\234\053\133\035\314\153\167\050\075\273\372\245\116
-\176\126\051\244\352\020\342\364\346\055\006\321\204\333\043\316
-\227\363\150\266\017\072\336\025\013\044\035\221\343\154\056\060
-\267\351\160\260\303\106\200\360\323\261\121\277\117\326\170\240
-\374\254\306\317\061\004\143\342\064\125\005\112\075\366\060\272
-\363\063\345\272\322\226\363\325\261\266\223\211\032\244\150\276
-\176\355\143\264\032\110\300\123\344\243\360\071\014\062\222\307
-\103\015\032\161\355\320\106\223\277\223\142\154\063\113\315\066
-\015\151\136\273\154\226\231\041\151\304\113\147\162\333\154\152
-\270\367\150\355\305\217\255\143\145\225\012\114\340\371\017\176
-\067\075\252\324\223\272\147\011\303\245\244\015\003\132\155\325
-\013\376\360\100\024\264\366\270\151\174\155\302\062\113\237\265
-\032\347\106\256\114\132\053\252\172\136\220\127\225\372\333\146
-\002\040\036\152\151\146\025\234\302\266\365\274\120\265\375\105
-\307\037\150\264\107\131\254\304\033\050\223\116\122\123\022\003
-\130\113\161\203\237\146\346\254\171\110\376\376\107
-END
-
-# Trust for Certificate "Bogus Yahoo 3"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:3e:75:ce:d4:6b:69:30:21:21:88:30:ae:86:a8:2a:71
-# Subject: CN=login.yahoo.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): 4A:DC:3C:67:ED:21:CD:5B:CE:5D:C8:11:E4:9E:CF:3D
-# Fingerprint (SHA1): 80:96:2A:E4:D6:C5:B4:42:89:4E:95:A1:3E:4A:69:9E:07:D6:94:CF
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus Yahoo 3"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\200\226\052\344\326\305\264\102\211\116\225\241\076\112\151\236
-\007\326\224\317
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\112\334\074\147\355\041\315\133\316\135\310\021\344\236\317\075
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\076\165\316\324\153\151\060\041\041\210\060\256\206\250
-\052\161
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Bogus live.com"
-#
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:b0:b7:13:3e:d0:96:f9:b5:6f:ae:91:c8:74:bd:3a:c0
-# Subject: CN=login.live.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): D0:D4:39:E3:CC:5C:52:DD:08:CD:E9:AB:E8:11:59:D4
-# Fingerprint (SHA1): CE:A5:86:B2:CE:59:3E:C7:D9:39:89:83:37:C5:78:14:70:8A:B2:BE
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus live.com"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\336\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\016\060\014\006\003\125\004\021\023\005\063\070\064\067\067
-\061\020\060\016\006\003\125\004\010\023\007\106\154\157\162\151
-\144\141\061\020\060\016\006\003\125\004\007\023\007\105\156\147
-\154\151\163\150\061\027\060\025\006\003\125\004\011\023\016\123
-\145\141\040\126\151\154\154\141\147\145\040\061\060\061\024\060
-\022\006\003\125\004\012\023\013\107\157\157\147\154\145\040\114
-\164\144\056\061\023\060\021\006\003\125\004\013\023\012\124\145
-\143\150\040\104\145\160\164\056\061\050\060\046\006\003\125\004
-\013\023\037\110\157\163\164\145\144\040\142\171\040\107\124\111
-\040\107\162\157\165\160\040\103\157\162\160\157\162\141\164\151
-\157\156\061\024\060\022\006\003\125\004\013\023\013\120\154\141
-\164\151\156\165\155\123\123\114\061\027\060\025\006\003\125\004
-\003\023\016\154\157\147\151\156\056\154\151\166\145\056\143\157
-\155
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\260\267\023\076\320\226\371\265\157\256\221\310\164
-\275\072\300
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\354\060\202\004\324\240\003\002\001\002\002\021\000
-\260\267\023\076\320\226\371\265\157\256\221\310\164\275\072\300
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060\025
-\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153\145
-\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023\025
-\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116\145
-\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023\030
-\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162\164
-\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125\004
-\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163\164
-\055\110\141\162\144\167\141\162\145\060\036\027\015\061\061\060
-\063\061\065\060\060\060\060\060\060\132\027\015\061\064\060\063
-\061\064\062\063\065\071\065\071\132\060\201\336\061\013\060\011
-\006\003\125\004\006\023\002\125\123\061\016\060\014\006\003\125
-\004\021\023\005\063\070\064\067\067\061\020\060\016\006\003\125
-\004\010\023\007\106\154\157\162\151\144\141\061\020\060\016\006
-\003\125\004\007\023\007\105\156\147\154\151\163\150\061\027\060
-\025\006\003\125\004\011\023\016\123\145\141\040\126\151\154\154
-\141\147\145\040\061\060\061\024\060\022\006\003\125\004\012\023
-\013\107\157\157\147\154\145\040\114\164\144\056\061\023\060\021
-\006\003\125\004\013\023\012\124\145\143\150\040\104\145\160\164
-\056\061\050\060\046\006\003\125\004\013\023\037\110\157\163\164
-\145\144\040\142\171\040\107\124\111\040\107\162\157\165\160\040
-\103\157\162\160\157\162\141\164\151\157\156\061\024\060\022\006
-\003\125\004\013\023\013\120\154\141\164\151\156\165\155\123\123
-\114\061\027\060\025\006\003\125\004\003\023\016\154\157\147\151
-\156\056\154\151\166\145\056\143\157\155\060\202\001\042\060\015
-\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202\001
-\017\000\060\202\001\012\002\202\001\001\000\363\374\053\057\357
-\341\255\131\360\102\074\302\361\202\277\054\101\223\321\366\230
-\063\225\114\274\142\361\225\130\010\266\351\173\167\110\260\323
-\334\027\077\274\156\346\354\036\354\215\027\376\034\044\306\076
-\147\075\222\225\242\060\300\247\127\040\317\160\210\227\112\005
-\223\171\223\102\227\057\076\377\304\024\024\050\242\023\066\264
-\370\356\276\035\274\170\135\141\223\137\353\210\327\321\344\053
-\232\315\130\342\007\105\237\117\270\271\100\152\063\054\133\041
-\003\132\112\224\362\172\227\131\033\250\265\102\330\203\000\252
-\064\314\247\166\320\107\003\137\005\257\073\341\271\241\064\045
-\267\154\137\232\060\204\230\302\302\327\362\270\102\112\020\125
-\275\372\123\201\135\215\150\146\105\054\122\176\345\304\004\303
-\124\347\303\071\332\172\112\305\271\230\202\040\341\054\140\127
-\277\272\362\106\000\274\137\072\334\343\063\227\370\112\230\271
-\354\063\117\055\140\154\025\222\246\201\112\013\351\354\166\160
-\064\061\027\160\346\160\113\216\213\323\165\313\170\111\253\146
-\233\206\237\217\251\304\001\350\312\033\347\002\003\001\000\001
-\243\202\001\350\060\202\001\344\060\037\006\003\125\035\043\004
-\030\060\026\200\024\241\162\137\046\033\050\230\103\225\135\007
-\067\325\205\226\235\113\322\303\105\060\035\006\003\125\035\016
-\004\026\004\024\324\144\366\251\350\245\176\327\277\143\122\003
-\203\123\333\305\101\215\352\200\060\016\006\003\125\035\017\001
-\001\377\004\004\003\002\005\240\060\014\006\003\125\035\023\001
-\001\377\004\002\060\000\060\035\006\003\125\035\045\004\026\060
-\024\006\010\053\006\001\005\005\007\003\001\006\010\053\006\001
-\005\005\007\003\002\060\106\006\003\125\035\040\004\077\060\075
-\060\073\006\014\053\006\001\004\001\262\061\001\002\001\003\004
-\060\053\060\051\006\010\053\006\001\005\005\007\002\001\026\035
-\150\164\164\160\163\072\057\057\163\145\143\165\162\145\056\143
-\157\155\157\144\157\056\143\157\155\057\103\120\123\060\173\006
-\003\125\035\037\004\164\060\162\060\070\240\066\240\064\206\062
-\150\164\164\160\072\057\057\143\162\154\056\143\157\155\157\144
-\157\143\141\056\143\157\155\057\125\124\116\055\125\123\105\122
-\106\151\162\163\164\055\110\141\162\144\167\141\162\145\056\143
-\162\154\060\066\240\064\240\062\206\060\150\164\164\160\072\057
-\057\143\162\154\056\143\157\155\157\144\157\056\156\145\164\057
-\125\124\116\055\125\123\105\122\106\151\162\163\164\055\110\141
-\162\144\167\141\162\145\056\143\162\154\060\161\006\010\053\006
-\001\005\005\007\001\001\004\145\060\143\060\073\006\010\053\006
-\001\005\005\007\060\002\206\057\150\164\164\160\072\057\057\143
-\162\164\056\143\157\155\157\144\157\143\141\056\143\157\155\057
-\125\124\116\101\144\144\124\162\165\163\164\123\145\162\166\145
-\162\103\101\056\143\162\164\060\044\006\010\053\006\001\005\005
-\007\060\001\206\030\150\164\164\160\072\057\057\157\143\163\160
-\056\143\157\155\157\144\157\143\141\056\143\157\155\060\055\006
-\003\125\035\021\004\046\060\044\202\016\154\157\147\151\156\056
-\154\151\166\145\056\143\157\155\202\022\167\167\167\056\154\157
-\147\151\156\056\154\151\166\145\056\143\157\155\060\015\006\011
-\052\206\110\206\367\015\001\001\005\005\000\003\202\001\001\000
-\124\343\244\232\044\322\363\035\102\255\033\360\036\253\373\332
-\325\252\351\317\132\263\036\127\173\061\362\156\127\113\061\257
-\063\273\266\015\025\307\136\131\001\316\104\265\267\277\011\311
-\325\334\151\204\351\305\032\267\360\076\324\300\044\275\051\137
-\264\351\326\130\353\105\021\211\064\064\323\021\353\064\316\052
-\117\000\075\366\162\357\151\146\300\237\232\254\176\160\120\254
-\125\107\332\276\103\133\354\213\310\305\043\204\311\237\266\122
-\010\317\221\033\057\200\151\346\064\063\346\263\237\244\345\015
-\232\025\371\127\374\013\251\101\013\365\377\130\101\222\042\047
-\146\022\006\307\052\330\131\247\306\337\104\022\117\300\250\177
-\247\101\310\310\151\377\272\005\056\227\255\073\320\353\363\025
-\155\176\033\345\272\335\064\276\042\021\354\150\230\063\201\002
-\152\013\023\125\171\061\165\116\072\310\266\023\275\227\157\067
-\012\013\055\210\016\336\147\220\302\263\312\040\312\232\121\364
-\144\076\333\364\056\105\362\307\107\027\250\364\372\220\132\177
-\200\246\202\254\344\154\201\106\273\122\205\040\044\370\200\352
-END
-
-# Trust for Certificate "Bogus live.com"
-# Issuer: CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
-# Serial Number:00:b0:b7:13:3e:d0:96:f9:b5:6f:ae:91:c8:74:bd:3a:c0
-# Subject: CN=login.live.com,OU=PlatinumSSL,OU=Hosted by GTI Group Corporation,OU=Tech Dept.,O=Google Ltd.,STREET=Sea Village 10,L=English,ST=Florida,postalCode=38477,C=US
-# Not Valid Before: Tue Mar 15 00:00:00 2011
-# Not Valid After : Fri Mar 14 23:59:59 2014
-# Fingerprint (MD5): D0:D4:39:E3:CC:5C:52:DD:08:CD:E9:AB:E8:11:59:D4
-# Fingerprint (SHA1): CE:A5:86:B2:CE:59:3E:C7:D9:39:89:83:37:C5:78:14:70:8A:B2:BE
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Bogus live.com"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\316\245\206\262\316\131\076\307\331\071\211\203\067\305\170\024
-\160\212\262\276
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\320\324\071\343\314\134\122\335\010\315\351\253\350\021\131\324
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\227\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\013\060\011\006\003\125\004\010\023\002\125\124\061\027\060
-\025\006\003\125\004\007\023\016\123\141\154\164\040\114\141\153
-\145\040\103\151\164\171\061\036\060\034\006\003\125\004\012\023
-\025\124\150\145\040\125\123\105\122\124\122\125\123\124\040\116
-\145\164\167\157\162\153\061\041\060\037\006\003\125\004\013\023
-\030\150\164\164\160\072\057\057\167\167\167\056\165\163\145\162
-\164\162\165\163\164\056\143\157\155\061\037\060\035\006\003\125
-\004\003\023\026\125\124\116\055\125\123\105\122\106\151\162\163
-\164\055\110\141\162\144\167\141\162\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\021\000\260\267\023\076\320\226\371\265\157\256\221\310\164
-\275\072\300
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "Go Daddy Root Certificate Authority - G2"
#
# Issuer: CN=Go Daddy Root Certificate Authority - G2,O="GoDaddy.com, Inc.",L=Scottsdale,ST=Arizona,C=US
@@ -14493,175 +11728,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "Certinomis - Autorité Racine"
-#
-# Issuer: CN=Certinomis - Autorit.. Racine,OU=0002 433998903,O=Certinomis,C=FR
-# Serial Number: 1 (0x1)
-# Subject: CN=Certinomis - Autorit.. Racine,OU=0002 433998903,O=Certinomis,C=FR
-# Not Valid Before: Wed Sep 17 08:28:59 2008
-# Not Valid After : Sun Sep 17 08:28:59 2028
-# Fingerprint (MD5): 7F:30:78:8C:03:E3:CA:C9:0A:E2:C9:EA:1E:AA:55:1A
-# Fingerprint (SHA1): 2E:14:DA:EC:28:F0:FA:1E:8E:38:9A:4E:AB:EB:26:C0:0A:D3:83:C3
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Certinomis - Autorité Racine"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\143\061\013\060\011\006\003\125\004\006\023\002\106\122\061
-\023\060\021\006\003\125\004\012\023\012\103\145\162\164\151\156
-\157\155\151\163\061\027\060\025\006\003\125\004\013\023\016\060
-\060\060\062\040\064\063\063\071\071\070\071\060\063\061\046\060
-\044\006\003\125\004\003\014\035\103\145\162\164\151\156\157\155
-\151\163\040\055\040\101\165\164\157\162\151\164\303\251\040\122
-\141\143\151\156\145
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\143\061\013\060\011\006\003\125\004\006\023\002\106\122\061
-\023\060\021\006\003\125\004\012\023\012\103\145\162\164\151\156
-\157\155\151\163\061\027\060\025\006\003\125\004\013\023\016\060
-\060\060\062\040\064\063\063\071\071\070\071\060\063\061\046\060
-\044\006\003\125\004\003\014\035\103\145\162\164\151\156\157\155
-\151\163\040\055\040\101\165\164\157\162\151\164\303\251\040\122
-\141\143\151\156\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\001
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\234\060\202\003\204\240\003\002\001\002\002\001\001
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\143\061\013\060\011\006\003\125\004\006\023\002\106\122\061\023
-\060\021\006\003\125\004\012\023\012\103\145\162\164\151\156\157
-\155\151\163\061\027\060\025\006\003\125\004\013\023\016\060\060
-\060\062\040\064\063\063\071\071\070\071\060\063\061\046\060\044
-\006\003\125\004\003\014\035\103\145\162\164\151\156\157\155\151
-\163\040\055\040\101\165\164\157\162\151\164\303\251\040\122\141
-\143\151\156\145\060\036\027\015\060\070\060\071\061\067\060\070
-\062\070\065\071\132\027\015\062\070\060\071\061\067\060\070\062
-\070\065\071\132\060\143\061\013\060\011\006\003\125\004\006\023
-\002\106\122\061\023\060\021\006\003\125\004\012\023\012\103\145
-\162\164\151\156\157\155\151\163\061\027\060\025\006\003\125\004
-\013\023\016\060\060\060\062\040\064\063\063\071\071\070\071\060
-\063\061\046\060\044\006\003\125\004\003\014\035\103\145\162\164
-\151\156\157\155\151\163\040\055\040\101\165\164\157\162\151\164
-\303\251\040\122\141\143\151\156\145\060\202\002\042\060\015\006
-\011\052\206\110\206\367\015\001\001\001\005\000\003\202\002\017
-\000\060\202\002\012\002\202\002\001\000\235\205\237\206\323\343
-\257\307\262\153\156\063\340\236\267\102\064\125\235\371\201\276
-\143\330\043\166\016\227\124\315\231\114\032\361\071\307\210\330
-\027\120\014\236\141\332\300\116\125\336\347\132\270\172\116\167
-\207\015\345\270\353\372\236\136\173\036\304\317\050\164\307\223
-\365\024\306\042\050\004\371\221\303\253\047\163\152\016\056\115
-\363\056\050\037\160\337\125\057\116\355\307\161\157\011\162\056
-\355\325\062\227\320\361\130\167\321\140\274\116\136\333\232\204
-\366\107\141\105\053\366\120\246\177\152\161\047\110\204\065\236
-\254\376\151\251\236\172\136\065\045\372\264\247\111\065\167\226
-\247\066\133\341\315\337\043\160\330\135\114\245\010\203\361\246
-\044\070\023\250\354\057\250\241\147\307\246\055\206\107\356\212
-\374\354\233\016\164\364\053\111\002\173\220\165\214\374\231\071
-\001\071\326\112\211\345\236\166\253\076\226\050\070\046\213\335
-\215\214\300\366\001\036\157\245\061\022\070\175\225\302\161\356
-\355\164\256\344\066\242\103\165\325\361\000\233\342\344\327\314
-\102\003\113\170\172\345\175\273\270\256\056\040\223\323\344\141
-\337\161\341\166\147\227\077\266\337\152\163\132\144\042\345\102
-\333\317\201\003\223\330\364\343\020\340\162\366\000\160\254\360
-\301\172\017\005\177\317\064\151\105\265\223\344\031\333\122\026
-\043\005\211\016\215\110\344\045\157\263\170\277\142\365\007\372
-\225\044\302\226\262\350\243\043\302\135\003\374\303\323\345\174
-\311\165\043\327\364\365\274\336\344\337\315\200\277\221\210\175
-\247\023\264\071\272\054\272\275\321\153\314\363\245\050\355\104
-\236\175\122\243\157\226\056\031\176\034\363\133\307\026\216\273
-\140\175\167\146\107\124\202\000\021\140\154\062\301\250\070\033
-\353\156\230\023\326\356\070\365\360\237\016\357\376\061\201\301
-\322\044\225\057\123\172\151\242\360\017\206\105\216\130\202\053
-\114\042\324\136\240\347\175\046\047\110\337\045\106\215\112\050
-\174\206\236\371\233\032\131\271\145\277\005\335\266\102\135\075
-\346\000\110\202\136\040\367\021\202\336\312\330\237\346\067\107
-\046\036\353\170\367\141\303\101\144\130\002\101\371\332\340\321
-\370\371\350\375\122\070\266\365\211\337\002\003\001\000\001\243
-\133\060\131\060\017\006\003\125\035\023\001\001\377\004\005\060
-\003\001\001\377\060\016\006\003\125\035\017\001\001\377\004\004
-\003\002\001\006\060\035\006\003\125\035\016\004\026\004\024\015
-\214\266\141\332\104\270\321\024\175\303\276\175\136\110\360\316
-\312\152\260\060\027\006\003\125\035\040\004\020\060\016\060\014
-\006\012\052\201\172\001\126\002\002\000\001\001\060\015\006\011
-\052\206\110\206\367\015\001\001\005\005\000\003\202\002\001\000
-\044\076\140\006\176\035\357\072\076\333\352\257\034\232\054\001
-\013\364\305\265\331\111\061\364\135\101\215\211\014\116\377\154
-\242\375\377\342\006\310\071\237\361\132\251\335\042\130\025\250
-\212\323\261\346\062\011\202\003\154\327\077\010\307\370\271\272
-\000\155\271\326\374\122\062\135\244\177\244\061\224\273\266\114
-\070\177\050\060\065\377\237\043\123\267\266\356\024\160\000\100
-\053\332\107\253\064\176\136\247\126\060\141\053\213\103\254\375
-\266\210\050\365\153\266\076\140\112\272\102\220\064\147\215\352
-\353\137\105\124\073\027\254\213\344\306\145\017\356\320\214\135
-\146\071\316\062\247\330\020\227\300\176\064\234\237\224\363\366
-\206\037\317\033\163\255\224\171\207\150\160\303\063\245\160\347
-\330\325\070\224\157\143\171\353\277\012\016\010\347\305\057\017
-\102\240\053\024\100\377\041\340\005\305\047\341\204\021\023\272
-\326\206\035\101\013\023\043\211\323\311\013\350\212\272\172\243
-\243\163\067\065\200\175\022\270\063\167\100\070\300\372\136\060
-\322\362\266\243\261\326\242\225\227\201\233\122\355\151\114\377
-\200\344\123\333\124\133\003\155\124\137\261\270\357\044\275\157
-\237\021\303\307\144\302\017\050\142\205\146\136\032\173\262\267
-\357\256\065\311\031\063\250\270\047\333\063\125\277\150\341\165
-\110\104\126\373\315\323\110\273\107\211\072\254\151\365\200\306
-\344\104\120\057\124\304\252\103\305\061\061\130\275\226\305\352
-\165\154\232\165\261\115\370\367\227\377\226\026\362\227\115\350
-\366\363\021\371\072\175\212\070\156\004\313\341\323\105\025\252
-\245\321\035\235\135\143\350\044\346\066\024\342\207\255\033\131
-\365\104\233\373\327\167\174\037\001\160\142\241\040\032\242\305
-\032\050\364\041\003\356\056\331\301\200\352\271\331\202\326\133
-\166\302\313\073\265\322\000\360\243\016\341\255\156\100\367\333
-\240\264\320\106\256\025\327\104\302\115\065\371\322\013\362\027
-\366\254\146\325\044\262\117\321\034\231\300\156\365\175\353\164
-\004\270\371\115\167\011\327\264\317\007\060\011\361\270\000\126
-\331\027\026\026\012\053\206\337\217\001\031\032\345\273\202\143
-\377\276\013\166\026\136\067\067\346\330\164\227\242\231\105\171
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for Certificate "Certinomis - Autorité Racine"
-# Issuer: CN=Certinomis - Autorit.. Racine,OU=0002 433998903,O=Certinomis,C=FR
-# Serial Number: 1 (0x1)
-# Subject: CN=Certinomis - Autorit.. Racine,OU=0002 433998903,O=Certinomis,C=FR
-# Not Valid Before: Wed Sep 17 08:28:59 2008
-# Not Valid After : Sun Sep 17 08:28:59 2028
-# Fingerprint (MD5): 7F:30:78:8C:03:E3:CA:C9:0A:E2:C9:EA:1E:AA:55:1A
-# Fingerprint (SHA1): 2E:14:DA:EC:28:F0:FA:1E:8E:38:9A:4E:AB:EB:26:C0:0A:D3:83:C3
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Certinomis - Autorité Racine"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\056\024\332\354\050\360\372\036\216\070\232\116\253\353\046\300
-\012\323\203\303
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\177\060\170\214\003\343\312\311\012\342\311\352\036\252\125\032
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\143\061\013\060\011\006\003\125\004\006\023\002\106\122\061
-\023\060\021\006\003\125\004\012\023\012\103\145\162\164\151\156
-\157\155\151\163\061\027\060\025\006\003\125\004\013\023\016\060
-\060\060\062\040\064\063\063\071\071\070\071\060\063\061\046\060
-\044\006\003\125\004\003\014\035\103\145\162\164\151\156\157\155
-\151\163\040\055\040\101\165\164\157\162\151\164\303\251\040\122
-\141\143\151\156\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\001
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "TWCA Root Certification Authority"
#
# Issuer: CN=TWCA Root Certification Authority,OU=Root CA,O=TAIWAN-CA,C=TW
@@ -14966,605 +12032,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "Explicitly Distrust DigiNotar Services 1024 CA"
-#
-# Issuer: E=info@diginotar.nl,CN=DigiNotar Services 1024 CA,O=DigiNotar,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: E=info@diginotar.nl,CN=DigiNotar Services 1024 CA,O=DigiNotar,C=NL
-# Not Valid Before: Thu Jul 26 15:59:01 2007
-# Not Valid After : Mon Aug 26 16:29:01 2013
-# Fingerprint (MD5): 2F:16:68:97:4C:68:4F:CE:52:8A:EC:53:8F:93:49:F8
-# Fingerprint (SHA1): 12:3B:EA:CA:66:67:77:61:E0:EB:68:F2:FE:ED:A2:0F:20:05:55:70
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Services 1024 CA"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\150\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\043\060\041\006\003\125\004\003\023\032\104\151
-\147\151\116\157\164\141\162\040\123\145\162\166\151\143\145\163
-\040\061\060\062\064\040\103\101\061\040\060\036\006\011\052\206
-\110\206\367\015\001\011\001\026\021\151\156\146\157\100\144\151
-\147\151\156\157\164\141\162\056\156\154
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\150\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\043\060\041\006\003\125\004\003\023\032\104\151
-\147\151\116\157\164\141\162\040\123\145\162\166\151\143\145\163
-\040\061\060\062\064\040\103\101\061\040\060\036\006\011\052\206
-\110\206\367\015\001\011\001\026\021\151\156\146\157\100\144\151
-\147\151\156\157\164\141\162\056\156\154
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\003\161\060\202\002\332\240\003\002\001\002\002\004\017
-\377\377\377\060\015\006\011\052\206\110\206\367\015\001\001\005
-\005\000\060\150\061\013\060\011\006\003\125\004\006\023\002\116
-\114\061\022\060\020\006\003\125\004\012\023\011\104\151\147\151
-\116\157\164\141\162\061\043\060\041\006\003\125\004\003\023\032
-\104\151\147\151\116\157\164\141\162\040\123\145\162\166\151\143
-\145\163\040\061\060\062\064\040\103\101\061\040\060\036\006\011
-\052\206\110\206\367\015\001\011\001\026\021\151\156\146\157\100
-\144\151\147\151\156\157\164\141\162\056\156\154\060\036\027\015
-\060\067\060\067\062\066\061\065\065\071\060\061\132\027\015\061
-\063\060\070\062\066\061\066\062\071\060\061\132\060\150\061\013
-\060\011\006\003\125\004\006\023\002\116\114\061\022\060\020\006
-\003\125\004\012\023\011\104\151\147\151\116\157\164\141\162\061
-\043\060\041\006\003\125\004\003\023\032\104\151\147\151\116\157
-\164\141\162\040\123\145\162\166\151\143\145\163\040\061\060\062
-\064\040\103\101\061\040\060\036\006\011\052\206\110\206\367\015
-\001\011\001\026\021\151\156\146\157\100\144\151\147\151\156\157
-\164\141\162\056\156\154\060\201\237\060\015\006\011\052\206\110
-\206\367\015\001\001\001\005\000\003\201\215\000\060\201\211\002
-\201\201\000\332\233\115\135\074\371\321\342\213\306\306\010\040
-\305\331\036\110\354\146\130\147\171\142\053\101\143\364\211\215
-\150\332\257\270\224\066\213\031\044\244\240\223\322\231\017\262
-\255\055\065\115\315\057\152\341\371\233\031\053\274\004\032\176
-\055\075\122\144\315\361\076\147\017\211\056\350\362\117\256\246
-\010\241\205\376\241\251\011\346\306\253\076\103\374\257\172\003
-\221\332\246\071\246\141\356\230\117\030\250\323\263\257\146\202
-\351\237\274\335\162\371\006\004\275\022\331\030\044\347\253\223
-\123\213\131\002\003\001\000\001\243\202\001\046\060\202\001\042
-\060\022\006\003\125\035\023\001\001\377\004\010\060\006\001\001
-\377\002\001\000\060\047\006\003\125\035\045\004\040\060\036\006
-\010\053\006\001\005\005\007\003\001\006\010\053\006\001\005\005
-\007\003\002\006\010\053\006\001\005\005\007\003\004\060\021\006
-\003\125\035\040\004\012\060\010\060\006\006\004\125\035\040\000
-\060\063\006\010\053\006\001\005\005\007\001\001\004\047\060\045
-\060\043\006\010\053\006\001\005\005\007\060\001\206\027\150\164
-\164\160\072\057\057\157\143\163\160\056\145\156\164\162\165\163
-\164\056\156\145\164\060\063\006\003\125\035\037\004\054\060\052
-\060\050\240\046\240\044\206\042\150\164\164\160\072\057\057\143
-\162\154\056\145\156\164\162\165\163\164\056\156\145\164\057\163
-\145\162\166\145\162\061\056\143\162\154\060\035\006\003\125\035
-\016\004\026\004\024\376\334\224\111\014\157\357\134\177\306\361
-\022\231\117\026\111\255\373\202\145\060\013\006\003\125\035\017
-\004\004\003\002\001\006\060\037\006\003\125\035\043\004\030\060
-\026\200\024\360\027\142\023\125\075\263\377\012\000\153\373\120
-\204\227\363\355\142\320\032\060\031\006\011\052\206\110\206\366
-\175\007\101\000\004\014\060\012\033\004\126\067\056\061\003\002
-\000\201\060\015\006\011\052\206\110\206\367\015\001\001\005\005
-\000\003\201\201\000\143\164\152\067\251\077\226\234\146\310\130
-\254\011\311\357\365\145\224\177\243\002\304\070\061\275\135\043
-\207\354\324\126\262\311\262\156\344\005\006\374\354\365\372\210
-\160\131\324\356\346\335\265\172\240\243\140\057\002\014\253\336
-\022\135\257\360\065\113\252\212\107\221\032\365\205\054\102\307
-\035\357\225\103\263\136\270\225\223\245\332\305\050\252\255\162
-\055\061\255\231\153\154\377\214\041\047\257\255\232\221\053\307
-\335\130\303\156\007\305\237\171\322\307\214\125\277\114\307\047
-\136\121\026\053\076
-END
-
-# Trust for Certificate "Explicitly Distrust DigiNotar Services 1024 CA"
-# Issuer: E=info@diginotar.nl,CN=DigiNotar Services 1024 CA,O=DigiNotar,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: E=info@diginotar.nl,CN=DigiNotar Services 1024 CA,O=DigiNotar,C=NL
-# Not Valid Before: Thu Jul 26 15:59:01 2007
-# Not Valid After : Mon Aug 26 16:29:01 2013
-# Fingerprint (MD5): 2F:16:68:97:4C:68:4F:CE:52:8A:EC:53:8F:93:49:F8
-# Fingerprint (SHA1): 12:3B:EA:CA:66:67:77:61:E0:EB:68:F2:FE:ED:A2:0F:20:05:55:70
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Services 1024 CA"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\022\073\352\312\146\147\167\141\340\353\150\362\376\355\242\017
-\040\005\125\160
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\057\026\150\227\114\150\117\316\122\212\354\123\217\223\111\370
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\150\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\043\060\041\006\003\125\004\003\023\032\104\151
-\147\151\116\157\164\141\162\040\123\145\162\166\151\143\145\163
-\040\061\060\062\064\040\103\101\061\040\060\036\006\011\052\206
-\110\206\367\015\001\011\001\026\021\151\156\146\157\100\144\151
-\147\151\156\157\164\141\162\056\156\154
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Explicitly Distrust DigiNotar Cyber CA"
-#
-# Issuer: E=info@diginotar.nl,CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: E=info@diginotar.nl,CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Not Valid Before: Wed Oct 04 10:54:12 2006
-# Not Valid After : Tue Oct 04 10:53:12 2011
-# Fingerprint (MD5): BC:BD:89:12:B4:FF:E5:F9:26:47:C8:60:36:5B:D9:54
-# Fingerprint (SHA1): A5:8E:A0:EC:F6:44:56:35:19:1D:68:5B:C7:A0:E4:1C:B0:4D:79:2E
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Cyber CA"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\140\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\033\060\031\006\003\125\004\003\023\022\104\151
-\147\151\116\157\164\141\162\040\103\171\142\145\162\040\103\101
-\061\040\060\036\006\011\052\206\110\206\367\015\001\011\001\026
-\021\151\156\146\157\100\144\151\147\151\156\157\164\141\162\056
-\156\154
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\140\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\033\060\031\006\003\125\004\003\023\022\104\151
-\147\151\116\157\164\141\162\040\103\171\142\145\162\040\103\101
-\061\040\060\036\006\011\052\206\110\206\367\015\001\011\001\026
-\021\151\156\146\157\100\144\151\147\151\156\157\164\141\162\056
-\156\154
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\105\060\202\004\256\240\003\002\001\002\002\004\017
-\377\377\377\060\015\006\011\052\206\110\206\367\015\001\001\005
-\005\000\060\140\061\013\060\011\006\003\125\004\006\023\002\116
-\114\061\022\060\020\006\003\125\004\012\023\011\104\151\147\151
-\116\157\164\141\162\061\033\060\031\006\003\125\004\003\023\022
-\104\151\147\151\116\157\164\141\162\040\103\171\142\145\162\040
-\103\101\061\040\060\036\006\011\052\206\110\206\367\015\001\011
-\001\026\021\151\156\146\157\100\144\151\147\151\156\157\164\141
-\162\056\156\154\060\036\027\015\060\066\061\060\060\064\061\060
-\065\064\061\062\132\027\015\061\061\061\060\060\064\061\060\065
-\063\061\062\132\060\140\061\013\060\011\006\003\125\004\006\023
-\002\116\114\061\022\060\020\006\003\125\004\012\023\011\104\151
-\147\151\116\157\164\141\162\061\033\060\031\006\003\125\004\003
-\023\022\104\151\147\151\116\157\164\141\162\040\103\171\142\145
-\162\040\103\101\061\040\060\036\006\011\052\206\110\206\367\015
-\001\011\001\026\021\151\156\146\157\100\144\151\147\151\156\157
-\164\141\162\056\156\154\060\202\002\042\060\015\006\011\052\206
-\110\206\367\015\001\001\001\005\000\003\202\002\017\000\060\202
-\002\012\002\202\002\001\000\322\316\025\012\055\250\136\204\147
-\255\375\276\357\106\307\310\271\317\163\374\364\064\271\371\054
-\103\347\140\023\075\172\343\262\317\073\147\154\220\255\300\271
-\077\204\122\360\065\102\334\164\334\050\073\275\122\264\247\254
-\162\105\027\306\360\211\353\264\252\045\362\135\113\136\321\331
-\207\272\326\175\174\365\316\062\237\020\063\305\261\112\273\136
-\221\061\302\320\351\101\302\221\144\176\011\101\073\333\213\010
-\067\152\252\312\122\336\265\071\036\300\210\003\245\077\213\231
-\023\141\103\265\233\202\263\356\040\157\317\241\104\242\352\057
-\153\100\237\217\053\127\255\241\123\302\205\042\151\235\240\077
-\121\337\013\101\221\015\245\341\250\252\134\111\010\135\275\336
-\160\101\261\017\311\143\153\323\177\064\164\002\057\064\132\170
-\165\034\150\172\201\147\212\363\332\100\360\140\143\364\222\040
-\327\003\246\075\243\036\147\304\204\033\101\245\311\214\346\275
-\352\110\266\005\026\010\263\067\022\132\367\141\074\367\070\157
-\056\227\340\157\126\070\124\323\050\265\255\024\156\056\113\144
-\265\047\145\267\165\045\011\266\007\075\225\126\002\012\202\140
-\262\163\105\340\063\046\121\164\232\271\324\120\034\366\115\133
-\133\122\122\023\132\246\177\247\016\341\350\101\124\147\230\214
-\207\325\311\323\154\313\323\124\222\006\011\064\101\367\201\157
-\077\236\311\174\165\125\260\347\301\263\167\350\303\304\000\065
-\225\100\160\020\112\005\336\045\273\237\131\245\144\274\107\140
-\277\140\343\166\213\023\125\335\341\164\172\271\317\044\246\152
-\177\336\144\042\104\130\150\202\152\020\371\075\345\076\033\271
-\275\374\042\364\140\004\211\273\125\155\050\125\372\336\216\215
-\033\041\024\327\067\213\064\173\115\366\262\262\020\317\063\261
-\175\034\142\231\110\313\053\154\166\226\125\277\031\015\035\037
-\273\145\252\033\216\231\265\306\050\220\345\202\055\170\120\040
-\232\375\171\057\044\177\360\211\051\151\364\175\315\163\276\263
-\355\116\301\321\355\122\136\217\367\270\327\215\207\255\262\331
-\033\121\022\377\126\263\341\257\064\175\134\244\170\210\020\236
-\235\003\306\245\252\242\044\121\367\111\024\305\261\356\131\103
-\225\337\253\150\050\060\077\002\003\001\000\001\243\202\001\206
-\060\202\001\202\060\022\006\003\125\035\023\001\001\377\004\010
-\060\006\001\001\377\002\001\001\060\123\006\003\125\035\040\004
-\114\060\112\060\110\006\011\053\006\001\004\001\261\076\001\000
-\060\073\060\071\006\010\053\006\001\005\005\007\002\001\026\055
-\150\164\164\160\072\057\057\167\167\167\056\160\165\142\154\151
-\143\055\164\162\165\163\164\056\143\157\155\057\103\120\123\057
-\117\155\156\151\122\157\157\164\056\150\164\155\154\060\016\006
-\003\125\035\017\001\001\377\004\004\003\002\001\006\060\201\240
-\006\003\125\035\043\004\201\230\060\201\225\200\024\246\014\035
-\237\141\377\007\027\265\277\070\106\333\103\060\325\216\260\122
-\006\241\171\244\167\060\165\061\013\060\011\006\003\125\004\006
-\023\002\125\123\061\030\060\026\006\003\125\004\012\023\017\107
-\124\105\040\103\157\162\160\157\162\141\164\151\157\156\061\047
-\060\045\006\003\125\004\013\023\036\107\124\105\040\103\171\142
-\145\162\124\162\165\163\164\040\123\157\154\165\164\151\157\156
-\163\054\040\111\156\143\056\061\043\060\041\006\003\125\004\003
-\023\032\107\124\105\040\103\171\142\145\162\124\162\165\163\164
-\040\107\154\157\142\141\154\040\122\157\157\164\202\002\001\245
-\060\105\006\003\125\035\037\004\076\060\074\060\072\240\070\240
-\066\206\064\150\164\164\160\072\057\057\167\167\167\056\160\165
-\142\154\151\143\055\164\162\165\163\164\056\143\157\155\057\143
-\147\151\055\142\151\156\057\103\122\114\057\062\060\061\070\057
-\143\144\160\056\143\162\154\060\035\006\003\125\035\016\004\026
-\004\024\253\371\150\337\317\112\067\327\173\105\214\137\162\336
-\100\104\303\145\273\302\060\015\006\011\052\206\110\206\367\015
-\001\001\005\005\000\003\201\201\000\217\150\153\245\133\007\272
-\104\146\016\034\250\134\060\173\063\344\012\046\004\374\357\236
-\032\070\326\056\241\037\320\231\107\302\165\144\044\375\236\073
-\050\166\271\046\050\141\221\014\155\054\370\004\237\174\120\001
-\325\343\151\257\357\025\322\105\233\044\011\052\146\005\117\045
-\201\312\135\276\252\301\131\047\256\063\216\202\367\337\164\260
-\125\263\216\370\347\067\310\156\252\126\104\366\275\123\201\043
-\226\075\264\372\062\212\123\146\104\045\242\045\306\246\074\045
-\214\360\340\050\006\042\267\046\101
-END
-
-# Trust for Certificate "Explicitly Distrust DigiNotar Cyber CA"
-# Issuer: E=info@diginotar.nl,CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: E=info@diginotar.nl,CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Not Valid Before: Wed Oct 04 10:54:12 2006
-# Not Valid After : Tue Oct 04 10:53:12 2011
-# Fingerprint (MD5): BC:BD:89:12:B4:FF:E5:F9:26:47:C8:60:36:5B:D9:54
-# Fingerprint (SHA1): A5:8E:A0:EC:F6:44:56:35:19:1D:68:5B:C7:A0:E4:1C:B0:4D:79:2E
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Cyber CA"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\245\216\240\354\366\104\126\065\031\035\150\133\307\240\344\034
-\260\115\171\056
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\274\275\211\022\264\377\345\371\046\107\310\140\066\133\331\124
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\140\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\033\060\031\006\003\125\004\003\023\022\104\151
-\147\151\116\157\164\141\162\040\103\171\142\145\162\040\103\101
-\061\040\060\036\006\011\052\206\110\206\367\015\001\011\001\026
-\021\151\156\146\157\100\144\151\147\151\156\157\164\141\162\056
-\156\154
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Explicitly Distrust DigiNotar Cyber CA 2nd"
-#
-# Issuer: CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Not Valid Before: Wed Sep 27 10:53:53 2006
-# Not Valid After : Fri Sep 20 09:44:07 2013
-# Fingerprint (MD5): F0:AE:A9:3D:F2:2C:88:DC:7C:85:1B:96:7D:5A:1C:11
-# Fingerprint (SHA1): 88:1E:45:05:0F:98:D9:59:FB:0A:35:F9:4C:0E:28:97:55:16:29:B3
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Cyber CA 2nd"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\076\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\033\060\031\006\003\125\004\003\023\022\104\151
-\147\151\116\157\164\141\162\040\103\171\142\145\162\040\103\101
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\076\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\033\060\031\006\003\125\004\003\023\022\104\151
-\147\151\116\157\164\141\162\040\103\171\142\145\162\040\103\101
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\001\060\202\004\152\240\003\002\001\002\002\004\017
-\377\377\377\060\015\006\011\052\206\110\206\367\015\001\001\005
-\005\000\060\076\061\013\060\011\006\003\125\004\006\023\002\116
-\114\061\022\060\020\006\003\125\004\012\023\011\104\151\147\151
-\116\157\164\141\162\061\033\060\031\006\003\125\004\003\023\022
-\104\151\147\151\116\157\164\141\162\040\103\171\142\145\162\040
-\103\101\060\036\027\015\060\066\060\071\062\067\061\060\065\063
-\065\063\132\027\015\061\063\060\071\062\060\060\071\064\064\060
-\067\132\060\076\061\013\060\011\006\003\125\004\006\023\002\116
-\114\061\022\060\020\006\003\125\004\012\023\011\104\151\147\151
-\116\157\164\141\162\061\033\060\031\006\003\125\004\003\023\022
-\104\151\147\151\116\157\164\141\162\040\103\171\142\145\162\040
-\103\101\060\202\002\042\060\015\006\011\052\206\110\206\367\015
-\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
-\002\001\000\322\316\025\012\055\250\136\204\147\255\375\276\357
-\106\307\310\271\317\163\374\364\064\271\371\054\103\347\140\023
-\075\172\343\262\317\073\147\154\220\255\300\271\077\204\122\360
-\065\102\334\164\334\050\073\275\122\264\247\254\162\105\027\306
-\360\211\353\264\252\045\362\135\113\136\321\331\207\272\326\175
-\174\365\316\062\237\020\063\305\261\112\273\136\221\061\302\320
-\351\101\302\221\144\176\011\101\073\333\213\010\067\152\252\312
-\122\336\265\071\036\300\210\003\245\077\213\231\023\141\103\265
-\233\202\263\356\040\157\317\241\104\242\352\057\153\100\237\217
-\053\127\255\241\123\302\205\042\151\235\240\077\121\337\013\101
-\221\015\245\341\250\252\134\111\010\135\275\336\160\101\261\017
-\311\143\153\323\177\064\164\002\057\064\132\170\165\034\150\172
-\201\147\212\363\332\100\360\140\143\364\222\040\327\003\246\075
-\243\036\147\304\204\033\101\245\311\214\346\275\352\110\266\005
-\026\010\263\067\022\132\367\141\074\367\070\157\056\227\340\157
-\126\070\124\323\050\265\255\024\156\056\113\144\265\047\145\267
-\165\045\011\266\007\075\225\126\002\012\202\140\262\163\105\340
-\063\046\121\164\232\271\324\120\034\366\115\133\133\122\122\023
-\132\246\177\247\016\341\350\101\124\147\230\214\207\325\311\323
-\154\313\323\124\222\006\011\064\101\367\201\157\077\236\311\174
-\165\125\260\347\301\263\167\350\303\304\000\065\225\100\160\020
-\112\005\336\045\273\237\131\245\144\274\107\140\277\140\343\166
-\213\023\125\335\341\164\172\271\317\044\246\152\177\336\144\042
-\104\130\150\202\152\020\371\075\345\076\033\271\275\374\042\364
-\140\004\211\273\125\155\050\125\372\336\216\215\033\041\024\327
-\067\213\064\173\115\366\262\262\020\317\063\261\175\034\142\231
-\110\313\053\154\166\226\125\277\031\015\035\037\273\145\252\033
-\216\231\265\306\050\220\345\202\055\170\120\040\232\375\171\057
-\044\177\360\211\051\151\364\175\315\163\276\263\355\116\301\321
-\355\122\136\217\367\270\327\215\207\255\262\331\033\121\022\377
-\126\263\341\257\064\175\134\244\170\210\020\236\235\003\306\245
-\252\242\044\121\367\111\024\305\261\356\131\103\225\337\253\150
-\050\060\077\002\003\001\000\001\243\202\001\206\060\202\001\202
-\060\022\006\003\125\035\023\001\001\377\004\010\060\006\001\001
-\377\002\001\001\060\123\006\003\125\035\040\004\114\060\112\060
-\110\006\011\053\006\001\004\001\261\076\001\000\060\073\060\071
-\006\010\053\006\001\005\005\007\002\001\026\055\150\164\164\160
-\072\057\057\167\167\167\056\160\165\142\154\151\143\055\164\162
-\165\163\164\056\143\157\155\057\103\120\123\057\117\155\156\151
-\122\157\157\164\056\150\164\155\154\060\016\006\003\125\035\017
-\001\001\377\004\004\003\002\001\006\060\201\240\006\003\125\035
-\043\004\201\230\060\201\225\200\024\246\014\035\237\141\377\007
-\027\265\277\070\106\333\103\060\325\216\260\122\006\241\171\244
-\167\060\165\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\030\060\026\006\003\125\004\012\023\017\107\124\105\040\103
-\157\162\160\157\162\141\164\151\157\156\061\047\060\045\006\003
-\125\004\013\023\036\107\124\105\040\103\171\142\145\162\124\162
-\165\163\164\040\123\157\154\165\164\151\157\156\163\054\040\111
-\156\143\056\061\043\060\041\006\003\125\004\003\023\032\107\124
-\105\040\103\171\142\145\162\124\162\165\163\164\040\107\154\157
-\142\141\154\040\122\157\157\164\202\002\001\245\060\105\006\003
-\125\035\037\004\076\060\074\060\072\240\070\240\066\206\064\150
-\164\164\160\072\057\057\167\167\167\056\160\165\142\154\151\143
-\055\164\162\165\163\164\056\143\157\155\057\143\147\151\055\142
-\151\156\057\103\122\114\057\062\060\061\070\057\143\144\160\056
-\143\162\154\060\035\006\003\125\035\016\004\026\004\024\253\371
-\150\337\317\112\067\327\173\105\214\137\162\336\100\104\303\145
-\273\302\060\015\006\011\052\206\110\206\367\015\001\001\005\005
-\000\003\201\201\000\011\312\142\017\215\273\112\340\324\172\065
-\053\006\055\321\050\141\266\254\001\373\203\111\274\256\324\057
-\055\206\256\031\203\245\326\035\023\342\027\276\376\062\164\351
-\172\024\070\312\224\136\367\051\001\151\161\033\221\032\375\243
-\273\252\035\312\173\342\026\375\241\243\016\363\014\137\262\341
-\040\061\224\053\136\222\166\355\372\351\265\043\246\277\012\073
-\003\251\157\122\140\124\315\137\351\267\057\174\242\047\375\101
-\203\165\266\015\373\170\046\363\261\105\351\062\225\052\032\065
-\041\225\305\242\165
-END
-
-# Trust for Certificate "Explicitly Distrust DigiNotar Cyber CA 2nd"
-# Issuer: CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: CN=DigiNotar Cyber CA,O=DigiNotar,C=NL
-# Not Valid Before: Wed Sep 27 10:53:53 2006
-# Not Valid After : Fri Sep 20 09:44:07 2013
-# Fingerprint (MD5): F0:AE:A9:3D:F2:2C:88:DC:7C:85:1B:96:7D:5A:1C:11
-# Fingerprint (SHA1): 88:1E:45:05:0F:98:D9:59:FB:0A:35:F9:4C:0E:28:97:55:16:29:B3
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Cyber CA 2nd"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\210\036\105\005\017\230\331\131\373\012\065\371\114\016\050\227
-\125\026\051\263
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\360\256\251\075\362\054\210\334\174\205\033\226\175\132\034\021
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\076\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
-\164\141\162\061\033\060\031\006\003\125\004\003\023\022\104\151
-\147\151\116\157\164\141\162\040\103\171\142\145\162\040\103\101
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Explicitly Distrusted DigiNotar PKIoverheid"
-#
-# Issuer: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven,O=DigiNotar B.V.,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven,O=DigiNotar B.V.,C=NL
-# Not Valid Before: Thu Jul 05 08:42:08 2007
-# Not Valid After : Mon Jul 27 08:39:47 2015
-# Fingerprint (MD5): A3:CF:B3:FF:F9:4F:A7:B1:EB:3A:75:58:4E:2E:9F:EA
-# Fingerprint (SHA1): A7:A8:C9:AC:F4:5F:90:92:76:86:B8:C0:A2:0E:93:58:7D:DE:30:E4
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted DigiNotar PKIoverheid"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\027\060\025\006\003\125\004\012\023\016\104\151\147\151\116\157
-\164\141\162\040\102\056\126\056\061\067\060\065\006\003\125\004
-\003\023\056\104\151\147\151\116\157\164\141\162\040\120\113\111
-\157\166\145\162\150\145\151\144\040\103\101\040\117\166\145\162
-\150\145\151\144\040\145\156\040\102\145\144\162\151\152\166\145
-\156
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\027\060\025\006\003\125\004\012\023\016\104\151\147\151\116\157
-\164\141\162\040\102\056\126\056\061\067\060\065\006\003\125\004
-\003\023\056\104\151\147\151\116\157\164\141\162\040\120\113\111
-\157\166\145\162\150\145\151\144\040\103\101\040\117\166\145\162
-\150\145\151\144\040\145\156\040\102\145\144\162\151\152\166\145
-\156
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\004\216\060\202\003\166\240\003\002\001\002\002\004\017
-\377\377\377\060\015\006\011\052\206\110\206\367\015\001\001\005
-\005\000\060\137\061\013\060\011\006\003\125\004\006\023\002\116
-\114\061\027\060\025\006\003\125\004\012\023\016\104\151\147\151
-\116\157\164\141\162\040\102\056\126\056\061\067\060\065\006\003
-\125\004\003\023\056\104\151\147\151\116\157\164\141\162\040\120
-\113\111\157\166\145\162\150\145\151\144\040\103\101\040\117\166
-\145\162\150\145\151\144\040\145\156\040\102\145\144\162\151\152
-\166\145\156\060\036\027\015\060\067\060\067\060\065\060\070\064
-\062\060\070\132\027\015\061\065\060\067\062\067\060\070\063\071
-\064\067\132\060\137\061\013\060\011\006\003\125\004\006\023\002
-\116\114\061\027\060\025\006\003\125\004\012\023\016\104\151\147
-\151\116\157\164\141\162\040\102\056\126\056\061\067\060\065\006
-\003\125\004\003\023\056\104\151\147\151\116\157\164\141\162\040
-\120\113\111\157\166\145\162\150\145\151\144\040\103\101\040\117
-\166\145\162\150\145\151\144\040\145\156\040\102\145\144\162\151
-\152\166\145\156\060\202\001\042\060\015\006\011\052\206\110\206
-\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012
-\002\202\001\001\000\334\275\322\247\116\152\012\273\073\242\205
-\341\177\000\255\276\264\060\150\230\007\315\240\172\304\224\317
-\161\371\212\067\344\123\353\127\166\314\213\346\154\376\356\207
-\125\310\076\273\004\071\000\247\200\170\254\133\117\176\364\275
-\270\124\270\161\073\007\061\111\071\223\124\174\040\073\171\053
-\217\273\141\220\175\261\254\346\037\220\056\235\105\001\251\144
-\055\115\303\057\271\347\120\325\116\052\134\253\166\166\067\106
-\327\171\354\102\231\367\242\354\244\211\160\334\070\053\207\246
-\252\044\346\235\222\044\033\276\366\375\324\057\031\027\172\346
-\062\007\224\124\005\123\103\351\154\274\257\107\313\274\313\375
-\275\073\104\022\201\361\153\113\273\355\264\317\253\045\117\030
-\322\314\002\374\243\117\265\102\063\313\131\315\011\334\323\120
-\375\240\166\214\254\176\146\212\102\366\255\034\222\363\266\373
-\024\106\353\115\327\057\060\340\155\356\133\066\276\104\164\267
-\040\005\127\205\115\350\000\031\242\366\014\346\256\241\300\102
-\337\247\254\202\135\307\150\267\030\346\211\113\232\153\372\316
-\171\371\363\054\247\002\003\001\000\001\243\202\001\120\060\202
-\001\114\060\110\006\003\125\035\040\004\101\060\077\060\075\006
-\004\125\035\040\000\060\065\060\063\006\010\053\006\001\005\005
-\007\002\001\026\047\150\164\164\160\072\057\057\167\167\167\056
-\144\151\147\151\156\157\164\141\162\056\156\154\057\143\160\163
-\057\160\153\151\157\166\145\162\150\145\151\144\060\017\006\003
-\125\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006
-\003\125\035\017\001\001\377\004\004\003\002\001\006\060\201\200
-\006\003\125\035\043\004\171\060\167\200\024\013\206\326\017\167
-\243\150\261\373\144\011\303\210\156\134\004\034\127\351\075\241
-\131\244\127\060\125\061\013\060\011\006\003\125\004\006\023\002
-\116\114\061\036\060\034\006\003\125\004\012\023\025\123\164\141
-\141\164\040\144\145\162\040\116\145\144\145\162\154\141\156\144
-\145\156\061\046\060\044\006\003\125\004\003\023\035\123\164\141
-\141\164\040\144\145\162\040\116\145\144\145\162\154\141\156\144
-\145\156\040\122\157\157\164\040\103\101\202\004\000\230\232\171
-\060\075\006\003\125\035\037\004\066\060\064\060\062\240\060\240
-\056\206\054\150\164\164\160\072\057\057\143\162\154\056\160\153
-\151\157\166\145\162\150\145\151\144\056\156\154\057\104\157\155
-\117\166\114\141\164\145\163\164\103\122\114\056\143\162\154\060
-\035\006\003\125\035\016\004\026\004\024\114\010\311\215\166\361
-\230\307\076\337\074\327\057\165\015\261\166\171\227\314\060\015
-\006\011\052\206\110\206\367\015\001\001\005\005\000\003\202\001
-\001\000\014\224\207\032\277\115\343\205\342\356\327\330\143\171
-\016\120\337\306\204\133\322\273\331\365\061\012\032\065\227\164
-\337\024\372\052\017\076\355\240\343\010\366\325\116\133\257\246
-\256\045\342\105\153\042\017\267\124\050\176\222\336\215\024\154
-\321\034\345\156\164\004\234\267\357\064\104\105\337\311\203\035
-\031\037\300\051\151\337\211\325\077\302\260\123\155\345\116\027
-\344\163\141\043\023\046\161\103\375\114\131\313\303\337\042\252
-\041\053\331\277\225\021\032\212\244\342\253\247\135\113\157\051
-\365\122\321\344\322\025\261\213\376\360\003\317\247\175\351\231
-\207\070\263\015\163\024\344\162\054\341\316\365\255\006\110\144
-\372\323\051\271\242\330\273\364\325\013\245\100\104\103\216\240
-\277\316\132\245\122\114\144\323\027\061\141\314\350\244\212\350
-\344\210\373\351\345\057\006\063\063\233\224\146\146\261\253\120
-\072\241\011\201\164\123\132\047\271\246\322\045\317\323\303\247
-\377\226\320\057\352\340\036\215\122\351\030\034\040\012\107\240
-\226\126\016\100\220\121\104\254\032\375\361\356\205\037\367\102
-\132\145
-END
-
-# Trust for Certificate "Explicitly Distrusted DigiNotar PKIoverheid"
-# Issuer: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven,O=DigiNotar B.V.,C=NL
-# Serial Number: 268435455 (0xfffffff)
-# Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven,O=DigiNotar B.V.,C=NL
-# Not Valid Before: Thu Jul 05 08:42:08 2007
-# Not Valid After : Mon Jul 27 08:39:47 2015
-# Fingerprint (MD5): A3:CF:B3:FF:F9:4F:A7:B1:EB:3A:75:58:4E:2E:9F:EA
-# Fingerprint (SHA1): A7:A8:C9:AC:F4:5F:90:92:76:86:B8:C0:A2:0E:93:58:7D:DE:30:E4
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted DigiNotar PKIoverheid"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\247\250\311\254\364\137\220\222\166\206\270\300\242\016\223\130
-\175\336\060\344
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\243\317\263\377\371\117\247\261\353\072\165\130\116\056\237\352
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061
-\027\060\025\006\003\125\004\012\023\016\104\151\147\151\116\157
-\164\141\162\040\102\056\126\056\061\067\060\065\006\003\125\004
-\003\023\056\104\151\147\151\116\157\164\141\162\040\120\113\111
-\157\166\145\162\150\145\151\144\040\103\101\040\117\166\145\162
-\150\145\151\144\040\145\156\040\102\145\144\162\151\152\166\145
-\156
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\017\377\377\377
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "Explicitly Distrusted DigiNotar PKIoverheid G2"
#
# Issuer: CN=DigiNotar PKIoverheid CA Organisatie - G2,O=DigiNotar B.V.,C=NL
@@ -15746,315 +12213,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (cyb)"
-#
-# Issuer: CN=GTE CyberTrust Global Root,OU="GTE CyberTrust Solutions, Inc.",O=GTE Corporation,C=US
-# Serial Number:07:ff:ff:ff:ff:ff
-# Subject: CN=Digisign Server ID (Enrich),OU=457608-K,O=Digicert Sdn. Bhd.,C=MY
-# Not Valid Before: Tue Jul 17 15:17:49 2007
-# Not Valid After : Tue Jul 17 15:16:55 2012
-# Fingerprint (MD5): D2:DE:AE:50:A4:98:2D:6F:37:B7:86:52:C8:2D:4B:6A
-# Fingerprint (SHA1): 55:50:AF:EC:BF:E8:C3:AD:C4:0B:E3:AD:0C:A7:E4:15:8C:39:59:4F
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (cyb)"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\143\061\013\060\011\006\003\125\004\006\023\002\115\131\061
-\033\060\031\006\003\125\004\012\023\022\104\151\147\151\143\145
-\162\164\040\123\144\156\056\040\102\150\144\056\061\021\060\017
-\006\003\125\004\013\023\010\064\065\067\066\060\070\055\113\061
-\044\060\042\006\003\125\004\003\023\033\104\151\147\151\163\151
-\147\156\040\123\145\162\166\145\162\040\111\104\040\050\105\156
-\162\151\143\150\051
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\030\060\026\006\003\125\004\012\023\017\107\124\105\040\103\157
-\162\160\157\162\141\164\151\157\156\061\047\060\045\006\003\125
-\004\013\023\036\107\124\105\040\103\171\142\145\162\124\162\165
-\163\164\040\123\157\154\165\164\151\157\156\163\054\040\111\156
-\143\056\061\043\060\041\006\003\125\004\003\023\032\107\124\105
-\040\103\171\142\145\162\124\162\165\163\164\040\107\154\157\142
-\141\154\040\122\157\157\164
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\006\007\377\377\377\377\377
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\003\315\060\202\003\066\240\003\002\001\002\002\006\007
-\377\377\377\377\377\060\015\006\011\052\206\110\206\367\015\001
-\001\005\005\000\060\165\061\013\060\011\006\003\125\004\006\023
-\002\125\123\061\030\060\026\006\003\125\004\012\023\017\107\124
-\105\040\103\157\162\160\157\162\141\164\151\157\156\061\047\060
-\045\006\003\125\004\013\023\036\107\124\105\040\103\171\142\145
-\162\124\162\165\163\164\040\123\157\154\165\164\151\157\156\163
-\054\040\111\156\143\056\061\043\060\041\006\003\125\004\003\023
-\032\107\124\105\040\103\171\142\145\162\124\162\165\163\164\040
-\107\154\157\142\141\154\040\122\157\157\164\060\036\027\015\060
-\067\060\067\061\067\061\065\061\067\064\071\132\027\015\061\062
-\060\067\061\067\061\065\061\066\065\065\132\060\143\061\013\060
-\011\006\003\125\004\006\023\002\115\131\061\033\060\031\006\003
-\125\004\012\023\022\104\151\147\151\143\145\162\164\040\123\144
-\156\056\040\102\150\144\056\061\021\060\017\006\003\125\004\013
-\023\010\064\065\067\066\060\070\055\113\061\044\060\042\006\003
-\125\004\003\023\033\104\151\147\151\163\151\147\156\040\123\145
-\162\166\145\162\040\111\104\040\050\105\156\162\151\143\150\051
-\060\201\237\060\015\006\011\052\206\110\206\367\015\001\001\001
-\005\000\003\201\215\000\060\201\211\002\201\201\000\255\250\144
-\113\115\207\307\204\131\271\373\220\106\240\246\211\300\361\376
-\325\332\124\202\067\015\231\053\105\046\012\350\126\260\177\312
-\250\364\216\107\204\001\202\051\343\263\152\265\221\363\373\225
-\205\274\162\250\144\350\012\100\234\305\364\161\256\173\173\152
-\007\352\220\024\117\215\211\257\224\253\262\006\324\002\152\173
-\230\037\131\271\072\315\124\372\040\337\262\052\012\351\270\335
-\151\220\300\051\323\116\320\227\355\146\314\305\031\111\006\177
-\372\136\054\174\173\205\033\062\102\337\173\225\045\002\003\001
-\000\001\243\202\001\170\060\202\001\164\060\022\006\003\125\035
-\023\001\001\377\004\010\060\006\001\001\377\002\001\000\060\134
-\006\003\125\035\040\004\125\060\123\060\110\006\011\053\006\001
-\004\001\261\076\001\000\060\073\060\071\006\010\053\006\001\005
-\005\007\002\001\026\055\150\164\164\160\072\057\057\143\171\142
-\145\162\164\162\165\163\164\056\157\155\156\151\162\157\157\164
-\056\143\157\155\057\162\145\160\157\163\151\164\157\162\171\056
-\143\146\155\060\007\006\005\140\203\112\001\001\060\016\006\003
-\125\035\017\001\001\377\004\004\003\002\001\346\060\201\211\006
-\003\125\035\043\004\201\201\060\177\241\171\244\167\060\165\061
-\013\060\011\006\003\125\004\006\023\002\125\123\061\030\060\026
-\006\003\125\004\012\023\017\107\124\105\040\103\157\162\160\157
-\162\141\164\151\157\156\061\047\060\045\006\003\125\004\013\023
-\036\107\124\105\040\103\171\142\145\162\124\162\165\163\164\040
-\123\157\154\165\164\151\157\156\163\054\040\111\156\143\056\061
-\043\060\041\006\003\125\004\003\023\032\107\124\105\040\103\171
-\142\145\162\124\162\165\163\164\040\107\154\157\142\141\154\040
-\122\157\157\164\202\002\001\245\060\105\006\003\125\035\037\004
-\076\060\074\060\072\240\070\240\066\206\064\150\164\164\160\072
-\057\057\167\167\167\056\160\165\142\154\151\143\055\164\162\165
-\163\164\056\143\157\155\057\143\147\151\055\142\151\156\057\103
-\122\114\057\062\060\061\070\057\143\144\160\056\143\162\154\060
-\035\006\003\125\035\016\004\026\004\024\306\026\223\116\026\027
-\354\026\256\214\224\166\363\206\155\305\164\156\204\167\060\015
-\006\011\052\206\110\206\367\015\001\001\005\005\000\003\201\201
-\000\166\000\173\246\170\053\146\035\216\136\066\306\244\216\005
-\362\043\222\174\223\147\323\364\300\012\175\213\055\331\352\325
-\157\032\363\341\112\051\132\042\204\115\120\057\113\014\362\377
-\205\302\173\125\324\104\202\276\155\254\147\216\274\264\037\222
-\234\121\200\032\024\366\156\253\141\210\013\255\034\177\367\113
-\120\121\326\145\033\246\107\161\025\136\260\161\363\065\024\362
-\067\275\143\310\325\360\223\132\064\137\330\075\350\135\367\305
-\036\300\345\317\037\206\044\251\074\007\146\315\301\322\066\143
-\131
-END
-
-# Trust for Certificate "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (cyb)"
-# Issuer: CN=GTE CyberTrust Global Root,OU="GTE CyberTrust Solutions, Inc.",O=GTE Corporation,C=US
-# Serial Number:07:ff:ff:ff:ff:ff
-# Subject: CN=Digisign Server ID (Enrich),OU=457608-K,O=Digicert Sdn. Bhd.,C=MY
-# Not Valid Before: Tue Jul 17 15:17:49 2007
-# Not Valid After : Tue Jul 17 15:16:55 2012
-# Fingerprint (MD5): D2:DE:AE:50:A4:98:2D:6F:37:B7:86:52:C8:2D:4B:6A
-# Fingerprint (SHA1): 55:50:AF:EC:BF:E8:C3:AD:C4:0B:E3:AD:0C:A7:E4:15:8C:39:59:4F
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (cyb)"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\125\120\257\354\277\350\303\255\304\013\343\255\014\247\344\025
-\214\071\131\117
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\322\336\256\120\244\230\055\157\067\267\206\122\310\055\113\152
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061
-\030\060\026\006\003\125\004\012\023\017\107\124\105\040\103\157
-\162\160\157\162\141\164\151\157\156\061\047\060\045\006\003\125
-\004\013\023\036\107\124\105\040\103\171\142\145\162\124\162\165
-\163\164\040\123\157\154\165\164\151\157\156\163\054\040\111\156
-\143\056\061\043\060\041\006\003\125\004\003\023\032\107\124\105
-\040\103\171\142\145\162\124\162\165\163\164\040\107\154\157\142
-\141\154\040\122\157\157\164
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\006\007\377\377\377\377\377
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (en)"
-#
-# Issuer: CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net
-# Serial Number:07:ff:ff:ff:ff:ff
-# Subject: CN=Digisign Server ID - (Enrich),OU=457608-K,O=Digicert Sdn. Bhd.,C=MY
-# Not Valid Before: Fri Jul 16 17:23:38 2010
-# Not Valid After : Thu Jul 16 17:53:38 2015
-# Fingerprint (MD5): D7:69:61:7F:35:0F:9C:46:A3:AA:EB:F8:55:FC:84:F2
-# Fingerprint (SHA1): 6B:3C:3B:80:AD:CA:A6:BA:8A:9F:54:A6:7A:ED:12:69:05:6D:31:26
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (en)"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\145\061\013\060\011\006\003\125\004\006\023\002\115\131\061
-\033\060\031\006\003\125\004\012\023\022\104\151\147\151\143\145
-\162\164\040\123\144\156\056\040\102\150\144\056\061\021\060\017
-\006\003\125\004\013\023\010\064\065\067\066\060\070\055\113\061
-\046\060\044\006\003\125\004\003\023\035\104\151\147\151\163\151
-\147\156\040\123\145\162\166\145\162\040\111\104\040\055\040\050
-\105\156\162\151\143\150\051
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\264\061\024\060\022\006\003\125\004\012\023\013\105\156
-\164\162\165\163\164\056\156\145\164\061\100\060\076\006\003\125
-\004\013\024\067\167\167\167\056\145\156\164\162\165\163\164\056
-\156\145\164\057\103\120\123\137\062\060\064\070\040\151\156\143
-\157\162\160\056\040\142\171\040\162\145\146\056\040\050\154\151
-\155\151\164\163\040\154\151\141\142\056\051\061\045\060\043\006
-\003\125\004\013\023\034\050\143\051\040\061\071\071\071\040\105
-\156\164\162\165\163\164\056\156\145\164\040\114\151\155\151\164
-\145\144\061\063\060\061\006\003\125\004\003\023\052\105\156\164
-\162\165\163\164\056\156\145\164\040\103\145\162\164\151\146\151
-\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-\040\050\062\060\064\070\051
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\006\007\377\377\377\377\377
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\004\320\060\202\003\270\240\003\002\001\002\002\006\007
-\377\377\377\377\377\060\015\006\011\052\206\110\206\367\015\001
-\001\005\005\000\060\201\264\061\024\060\022\006\003\125\004\012
-\023\013\105\156\164\162\165\163\164\056\156\145\164\061\100\060
-\076\006\003\125\004\013\024\067\167\167\167\056\145\156\164\162
-\165\163\164\056\156\145\164\057\103\120\123\137\062\060\064\070
-\040\151\156\143\157\162\160\056\040\142\171\040\162\145\146\056
-\040\050\154\151\155\151\164\163\040\154\151\141\142\056\051\061
-\045\060\043\006\003\125\004\013\023\034\050\143\051\040\061\071
-\071\071\040\105\156\164\162\165\163\164\056\156\145\164\040\114
-\151\155\151\164\145\144\061\063\060\061\006\003\125\004\003\023
-\052\105\156\164\162\165\163\164\056\156\145\164\040\103\145\162
-\164\151\146\151\143\141\164\151\157\156\040\101\165\164\150\157
-\162\151\164\171\040\050\062\060\064\070\051\060\036\027\015\061
-\060\060\067\061\066\061\067\062\063\063\070\132\027\015\061\065
-\060\067\061\066\061\067\065\063\063\070\132\060\145\061\013\060
-\011\006\003\125\004\006\023\002\115\131\061\033\060\031\006\003
-\125\004\012\023\022\104\151\147\151\143\145\162\164\040\123\144
-\156\056\040\102\150\144\056\061\021\060\017\006\003\125\004\013
-\023\010\064\065\067\066\060\070\055\113\061\046\060\044\006\003
-\125\004\003\023\035\104\151\147\151\163\151\147\156\040\123\145
-\162\166\145\162\040\111\104\040\055\040\050\105\156\162\151\143
-\150\051\060\202\001\042\060\015\006\011\052\206\110\206\367\015
-\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002\202
-\001\001\000\305\211\344\364\015\006\100\222\131\307\032\263\065
-\321\016\114\052\063\371\370\257\312\236\177\356\271\247\155\140
-\364\124\350\157\325\233\363\033\143\061\004\150\162\321\064\026
-\214\264\027\054\227\336\163\305\330\220\025\240\032\053\365\313
-\263\110\206\104\360\035\210\114\316\101\102\032\357\365\014\336
-\376\100\332\071\040\367\006\125\072\152\235\106\301\322\157\245
-\262\310\127\076\051\243\234\340\351\205\167\146\350\230\247\044
-\176\276\300\131\040\345\104\157\266\127\330\276\316\302\145\167
-\130\306\141\101\321\164\004\310\177\111\102\305\162\251\162\026
-\356\214\335\022\135\264\112\324\321\257\120\267\330\252\165\166
-\150\255\076\135\252\060\155\141\250\253\020\133\076\023\277\063
-\340\257\104\235\070\042\133\357\114\057\246\161\046\025\046\312
-\050\214\331\372\216\216\251\242\024\065\342\233\044\210\264\364
-\177\205\235\203\117\007\241\266\024\220\066\304\064\034\215\046
-\141\155\023\157\170\276\350\217\047\307\113\204\226\243\206\150
-\014\043\276\013\354\214\224\000\251\004\212\023\220\367\337\205
-\154\014\261\002\003\001\000\001\243\202\001\064\060\202\001\060
-\060\016\006\003\125\035\017\001\001\377\004\004\003\002\001\006
-\060\022\006\003\125\035\023\001\001\377\004\010\060\006\001\001
-\377\002\001\000\060\047\006\003\125\035\045\004\040\060\036\006
-\010\053\006\001\005\005\007\003\001\006\010\053\006\001\005\005
-\007\003\002\006\010\053\006\001\005\005\007\003\004\060\063\006
-\010\053\006\001\005\005\007\001\001\004\047\060\045\060\043\006
-\010\053\006\001\005\005\007\060\001\206\027\150\164\164\160\072
-\057\057\157\143\163\160\056\145\156\164\162\165\163\164\056\156
-\145\164\060\104\006\003\125\035\040\004\075\060\073\060\071\006
-\005\140\203\112\001\001\060\060\060\056\006\010\053\006\001\005
-\005\007\002\001\026\042\150\164\164\160\072\057\057\167\167\167
-\056\144\151\147\151\143\145\162\164\056\143\157\155\056\155\171
-\057\143\160\163\056\150\164\155\060\062\006\003\125\035\037\004
-\053\060\051\060\047\240\045\240\043\206\041\150\164\164\160\072
-\057\057\143\162\154\056\145\156\164\162\165\163\164\056\156\145
-\164\057\062\060\064\070\143\141\056\143\162\154\060\021\006\003
-\125\035\016\004\012\004\010\114\116\314\045\050\003\051\201\060
-\037\006\003\125\035\043\004\030\060\026\200\024\125\344\201\321
-\021\200\276\330\211\271\010\243\061\371\241\044\011\026\271\160
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\003
-\202\001\001\000\227\114\357\112\072\111\254\162\374\060\040\153
-\264\051\133\247\305\225\004\220\371\062\325\302\205\152\336\003
-\241\067\371\211\000\260\132\254\125\176\333\103\065\377\311\001
-\370\121\276\314\046\312\310\152\244\304\124\076\046\036\347\014
-\243\315\227\147\224\335\246\102\353\134\315\217\071\171\153\063
-\171\041\006\171\372\202\104\025\231\314\301\267\071\323\106\142
-\174\262\160\353\157\316\040\252\076\031\267\351\164\202\234\264
-\245\113\115\141\000\067\344\207\322\362\024\072\144\174\270\251
-\173\141\340\223\042\347\325\237\076\107\346\066\166\240\123\330
-\000\003\072\017\265\063\376\226\312\323\322\202\072\056\335\327
-\110\341\344\247\151\314\034\351\231\112\347\312\160\105\327\013
-\007\016\232\165\033\320\057\222\157\366\244\007\303\275\034\113
-\246\204\266\175\250\232\251\322\247\051\361\013\127\151\036\227
-\127\046\354\053\103\254\324\105\203\005\000\351\343\360\106\100
-\007\372\352\261\121\163\223\034\245\335\123\021\067\310\052\247
-\025\047\035\264\252\314\177\252\061\060\374\270\105\237\110\011
-\355\020\342\305
-END
-
-# Trust for Certificate "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (en)"
-# Issuer: CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net
-# Serial Number:07:ff:ff:ff:ff:ff
-# Subject: CN=Digisign Server ID - (Enrich),OU=457608-K,O=Digicert Sdn. Bhd.,C=MY
-# Not Valid Before: Fri Jul 16 17:23:38 2010
-# Not Valid After : Thu Jul 16 17:53:38 2015
-# Fingerprint (MD5): D7:69:61:7F:35:0F:9C:46:A3:AA:EB:F8:55:FC:84:F2
-# Fingerprint (SHA1): 6B:3C:3B:80:AD:CA:A6:BA:8A:9F:54:A6:7A:ED:12:69:05:6D:31:26
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted Malaysian Digicert Sdn. Bhd. (en)"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\153\074\073\200\255\312\246\272\212\237\124\246\172\355\022\151
-\005\155\061\046
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\327\151\141\177\065\017\234\106\243\252\353\370\125\374\204\362
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\264\061\024\060\022\006\003\125\004\012\023\013\105\156
-\164\162\165\163\164\056\156\145\164\061\100\060\076\006\003\125
-\004\013\024\067\167\167\167\056\145\156\164\162\165\163\164\056
-\156\145\164\057\103\120\123\137\062\060\064\070\040\151\156\143
-\157\162\160\056\040\142\171\040\162\145\146\056\040\050\154\151
-\155\151\164\163\040\154\151\141\142\056\051\061\045\060\043\006
-\003\125\004\013\023\034\050\143\051\040\061\071\071\071\040\105
-\156\164\162\165\163\164\056\156\145\164\040\114\151\155\151\164
-\145\144\061\063\060\061\006\003\125\004\003\023\052\105\156\164
-\162\165\163\164\056\156\145\164\040\103\145\162\164\151\146\151
-\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-\040\050\062\060\064\070\051
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\006\007\377\377\377\377\377
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-
-#
# Certificate "Security Communication RootCA2"
#
# Issuer: OU=Security Communication RootCA2,O="SECOM Trust Systems CO.,LTD.",C=JP
@@ -16900,372 +13058,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "StartCom Certification Authority"
-#
-# Issuer: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Serial Number: 45 (0x2d)
-# Subject: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Not Valid Before: Sun Sep 17 19:46:37 2006
-# Not Valid After : Wed Sep 17 19:46:36 2036
-# Fingerprint (MD5): C9:3B:0D:84:41:FC:A4:76:79:23:08:57:DE:10:19:16
-# Fingerprint (SHA1): A3:F1:33:3F:E2:42:BF:CF:C5:D1:4E:8F:39:42:98:40:68:10:D1:A0
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "StartCom Certification Authority"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013
-\023\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154
-\040\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147
-\156\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123
-\164\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143
-\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013
-\023\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154
-\040\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147
-\156\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123
-\164\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143
-\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\055
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\007\207\060\202\005\157\240\003\002\001\002\002\001\055
-\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060
-\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061\026
-\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103\157
-\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013\023
-\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154\040
-\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147\156
-\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123\164
-\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143\141
-\164\151\157\156\040\101\165\164\150\157\162\151\164\171\060\036
-\027\015\060\066\060\071\061\067\061\071\064\066\063\067\132\027
-\015\063\066\060\071\061\067\061\071\064\066\063\066\132\060\175
-\061\013\060\011\006\003\125\004\006\023\002\111\114\061\026\060
-\024\006\003\125\004\012\023\015\123\164\141\162\164\103\157\155
-\040\114\164\144\056\061\053\060\051\006\003\125\004\013\023\042
-\123\145\143\165\162\145\040\104\151\147\151\164\141\154\040\103
-\145\162\164\151\146\151\143\141\164\145\040\123\151\147\156\151
-\156\147\061\051\060\047\006\003\125\004\003\023\040\123\164\141
-\162\164\103\157\155\040\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\060\202\002
-\042\060\015\006\011\052\206\110\206\367\015\001\001\001\005\000
-\003\202\002\017\000\060\202\002\012\002\202\002\001\000\301\210
-\333\011\274\154\106\174\170\237\225\173\265\063\220\362\162\142
-\326\301\066\040\042\044\136\316\351\167\362\103\012\242\006\144
-\244\314\216\066\370\070\346\043\360\156\155\261\074\335\162\243
-\205\034\241\323\075\264\063\053\323\057\257\376\352\260\101\131
-\147\266\304\006\175\012\236\164\205\326\171\114\200\067\172\337
-\071\005\122\131\367\364\033\106\103\244\322\205\205\322\303\161
-\363\165\142\064\272\054\212\177\036\217\356\355\064\320\021\307
-\226\315\122\075\272\063\326\335\115\336\013\073\112\113\237\302
-\046\057\372\265\026\034\162\065\167\312\074\135\346\312\341\046
-\213\032\066\166\134\001\333\164\024\045\376\355\265\240\210\017
-\335\170\312\055\037\007\227\060\001\055\162\171\372\106\326\023
-\052\250\271\246\253\203\111\035\345\362\357\335\344\001\216\030
-\012\217\143\123\026\205\142\251\016\031\072\314\265\146\246\302
-\153\164\007\344\053\341\166\076\264\155\330\366\104\341\163\142
-\037\073\304\276\240\123\126\045\154\121\011\367\252\253\312\277
-\166\375\155\233\363\235\333\277\075\146\274\014\126\252\257\230
-\110\225\072\113\337\247\130\120\331\070\165\251\133\352\103\014
-\002\377\231\353\350\154\115\160\133\051\145\234\335\252\135\314
-\257\001\061\354\014\353\322\215\350\352\234\173\346\156\367\047
-\146\014\032\110\327\156\102\343\077\336\041\076\173\341\015\160
-\373\143\252\250\154\032\124\264\134\045\172\311\242\311\213\026
-\246\273\054\176\027\136\005\115\130\156\022\035\001\356\022\020
-\015\306\062\177\030\377\374\364\372\315\156\221\350\066\111\276
-\032\110\151\213\302\226\115\032\022\262\151\027\301\012\220\326
-\372\171\042\110\277\272\173\151\370\160\307\372\172\067\330\330
-\015\322\166\117\127\377\220\267\343\221\322\335\357\302\140\267
-\147\072\335\376\252\234\360\324\213\177\162\042\316\306\237\227
-\266\370\257\212\240\020\250\331\373\030\306\266\265\134\122\074
-\211\266\031\052\163\001\012\017\003\263\022\140\362\172\057\201
-\333\243\156\377\046\060\227\365\213\335\211\127\266\255\075\263
-\257\053\305\267\166\002\360\245\326\053\232\206\024\052\162\366
-\343\063\214\135\011\113\023\337\273\214\164\023\122\113\002\003
-\001\000\001\243\202\002\020\060\202\002\014\060\017\006\003\125
-\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006\003
-\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003
-\125\035\016\004\026\004\024\116\013\357\032\244\100\133\245\027
-\151\207\060\312\064\150\103\320\101\256\362\060\037\006\003\125
-\035\043\004\030\060\026\200\024\116\013\357\032\244\100\133\245
-\027\151\207\060\312\064\150\103\320\101\256\362\060\202\001\132
-\006\003\125\035\040\004\202\001\121\060\202\001\115\060\202\001
-\111\006\013\053\006\001\004\001\201\265\067\001\001\001\060\202
-\001\070\060\056\006\010\053\006\001\005\005\007\002\001\026\042
-\150\164\164\160\072\057\057\167\167\167\056\163\164\141\162\164
-\163\163\154\056\143\157\155\057\160\157\154\151\143\171\056\160
-\144\146\060\064\006\010\053\006\001\005\005\007\002\001\026\050
-\150\164\164\160\072\057\057\167\167\167\056\163\164\141\162\164
-\163\163\154\056\143\157\155\057\151\156\164\145\162\155\145\144
-\151\141\164\145\056\160\144\146\060\201\317\006\010\053\006\001
-\005\005\007\002\002\060\201\302\060\047\026\040\123\164\141\162
-\164\040\103\157\155\155\145\162\143\151\141\154\040\050\123\164
-\141\162\164\103\157\155\051\040\114\164\144\056\060\003\002\001
-\001\032\201\226\114\151\155\151\164\145\144\040\114\151\141\142
-\151\154\151\164\171\054\040\162\145\141\144\040\164\150\145\040
-\163\145\143\164\151\157\156\040\052\114\145\147\141\154\040\114
-\151\155\151\164\141\164\151\157\156\163\052\040\157\146\040\164
-\150\145\040\123\164\141\162\164\103\157\155\040\103\145\162\164
-\151\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162
-\151\164\171\040\120\157\154\151\143\171\040\141\166\141\151\154
-\141\142\154\145\040\141\164\040\150\164\164\160\072\057\057\167
-\167\167\056\163\164\141\162\164\163\163\154\056\143\157\155\057
-\160\157\154\151\143\171\056\160\144\146\060\021\006\011\140\206
-\110\001\206\370\102\001\001\004\004\003\002\000\007\060\070\006
-\011\140\206\110\001\206\370\102\001\015\004\053\026\051\123\164
-\141\162\164\103\157\155\040\106\162\145\145\040\123\123\114\040
-\103\145\162\164\151\146\151\143\141\164\151\157\156\040\101\165
-\164\150\157\162\151\164\171\060\015\006\011\052\206\110\206\367
-\015\001\001\013\005\000\003\202\002\001\000\216\217\347\334\224
-\171\174\361\205\177\237\111\157\153\312\135\373\214\376\004\305
-\301\142\321\175\102\212\274\123\267\224\003\146\060\077\261\347
-\012\247\120\040\125\045\177\166\172\024\015\353\004\016\100\346
-\076\330\210\253\007\047\203\251\165\246\067\163\307\375\113\322
-\115\255\027\100\310\106\276\073\177\121\374\303\266\005\061\334
-\315\205\042\116\161\267\362\161\136\260\032\306\272\223\213\170
-\222\112\205\370\170\017\203\376\057\255\054\367\344\244\273\055
-\320\347\015\072\270\076\316\366\170\366\256\107\044\312\243\065
-\066\316\307\306\207\230\332\354\373\351\262\316\047\233\210\303
-\004\241\366\013\131\150\257\311\333\020\017\115\366\144\143\134
-\245\022\157\222\262\223\224\307\210\027\016\223\266\176\142\213
-\220\177\253\116\237\374\343\165\024\117\052\062\337\133\015\340
-\365\173\223\015\253\241\317\207\341\245\004\105\350\074\022\245
-\011\305\260\321\267\123\363\140\024\272\205\151\152\041\174\037
-\165\141\027\040\027\173\154\073\101\051\134\341\254\132\321\315
-\214\233\353\140\035\031\354\367\345\260\332\371\171\030\245\105
-\077\111\103\127\322\335\044\325\054\243\375\221\215\047\265\345
-\353\024\006\232\114\173\041\273\072\255\060\006\030\300\330\301
-\153\054\177\131\134\135\221\261\160\042\127\353\212\153\110\112
-\325\017\051\354\306\100\300\057\210\114\150\001\027\167\364\044
-\031\117\275\372\341\262\040\041\113\335\032\330\051\175\252\270
-\336\124\354\041\125\200\154\036\365\060\310\243\020\345\262\346
-\052\024\061\303\205\055\214\230\261\206\132\117\211\131\055\271
-\307\367\034\310\212\177\300\235\005\112\346\102\117\142\243\155
-\051\244\037\205\253\333\345\201\310\255\052\075\114\135\133\204
-\046\161\304\205\136\161\044\312\245\033\154\330\141\323\032\340
-\124\333\316\272\251\062\265\042\366\163\101\011\135\270\027\135
-\016\017\231\220\326\107\332\157\012\072\142\050\024\147\202\331
-\361\320\200\131\233\313\061\330\233\017\214\167\116\265\150\212
-\362\154\366\044\016\055\154\160\305\163\321\336\024\320\161\217
-\266\323\173\002\366\343\270\324\011\156\153\236\165\204\071\346
-\177\045\245\362\110\000\300\244\001\332\077
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "StartCom Certification Authority"
-# Issuer: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Serial Number: 45 (0x2d)
-# Subject: CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
-# Not Valid Before: Sun Sep 17 19:46:37 2006
-# Not Valid After : Wed Sep 17 19:46:36 2036
-# Fingerprint (MD5): C9:3B:0D:84:41:FC:A4:76:79:23:08:57:DE:10:19:16
-# Fingerprint (SHA1): A3:F1:33:3F:E2:42:BF:CF:C5:D1:4E:8F:39:42:98:40:68:10:D1:A0
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "StartCom Certification Authority"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\243\361\063\077\342\102\277\317\305\321\116\217\071\102\230\100
-\150\020\321\240
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\311\073\015\204\101\374\244\166\171\043\010\127\336\020\031\026
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\175\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\053\060\051\006\003\125\004\013
-\023\042\123\145\143\165\162\145\040\104\151\147\151\164\141\154
-\040\103\145\162\164\151\146\151\143\141\164\145\040\123\151\147
-\156\151\156\147\061\051\060\047\006\003\125\004\003\023\040\123
-\164\141\162\164\103\157\155\040\103\145\162\164\151\146\151\143
-\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\055
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "StartCom Certification Authority G2"
-#
-# Issuer: CN=StartCom Certification Authority G2,O=StartCom Ltd.,C=IL
-# Serial Number: 59 (0x3b)
-# Subject: CN=StartCom Certification Authority G2,O=StartCom Ltd.,C=IL
-# Not Valid Before: Fri Jan 01 01:00:01 2010
-# Not Valid After : Sat Dec 31 23:59:01 2039
-# Fingerprint (MD5): 78:4B:FB:9E:64:82:0A:D3:B8:4C:62:F3:64:F2:90:64
-# Fingerprint (SHA1): 31:F1:FD:68:22:63:20:EE:C6:3B:3F:9D:EA:4A:3E:53:7C:7C:39:17
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "StartCom Certification Authority G2"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\123\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\054\060\052\006\003\125\004\003
-\023\043\123\164\141\162\164\103\157\155\040\103\145\162\164\151
-\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151
-\164\171\040\107\062
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\123\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\054\060\052\006\003\125\004\003
-\023\043\123\164\141\162\164\103\157\155\040\103\145\162\164\151
-\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151
-\164\171\040\107\062
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\073
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\143\060\202\003\113\240\003\002\001\002\002\001\073
-\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060
-\123\061\013\060\011\006\003\125\004\006\023\002\111\114\061\026
-\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103\157
-\155\040\114\164\144\056\061\054\060\052\006\003\125\004\003\023
-\043\123\164\141\162\164\103\157\155\040\103\145\162\164\151\146
-\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164
-\171\040\107\062\060\036\027\015\061\060\060\061\060\061\060\061
-\060\060\060\061\132\027\015\063\071\061\062\063\061\062\063\065
-\071\060\061\132\060\123\061\013\060\011\006\003\125\004\006\023
-\002\111\114\061\026\060\024\006\003\125\004\012\023\015\123\164
-\141\162\164\103\157\155\040\114\164\144\056\061\054\060\052\006
-\003\125\004\003\023\043\123\164\141\162\164\103\157\155\040\103
-\145\162\164\151\146\151\143\141\164\151\157\156\040\101\165\164
-\150\157\162\151\164\171\040\107\062\060\202\002\042\060\015\006
-\011\052\206\110\206\367\015\001\001\001\005\000\003\202\002\017
-\000\060\202\002\012\002\202\002\001\000\266\211\066\133\007\267
-\040\066\275\202\273\341\026\040\003\225\172\257\016\243\125\311
-\045\231\112\305\320\126\101\207\220\115\041\140\244\024\207\073
-\315\375\262\076\264\147\003\152\355\341\017\113\300\221\205\160
-\105\340\102\236\336\051\043\324\001\015\240\020\171\270\333\003
-\275\363\251\057\321\306\340\017\313\236\212\024\012\270\275\366
-\126\142\361\305\162\266\062\045\331\262\363\275\145\305\015\054
-\156\325\222\157\030\213\000\101\024\202\157\100\040\046\172\050
-\017\365\036\177\047\367\224\261\067\075\267\307\221\367\342\001
-\354\375\224\211\341\314\156\323\066\326\012\031\171\256\327\064
-\202\145\377\174\102\273\266\335\013\246\064\257\113\140\376\177
-\103\111\006\213\214\103\270\126\362\331\177\041\103\027\352\247
-\110\225\001\165\165\352\053\245\103\225\352\025\204\235\010\215
-\046\156\125\233\253\334\322\071\322\061\035\140\342\254\314\126
-\105\044\365\034\124\253\356\206\335\226\062\205\370\114\117\350
-\225\166\266\005\335\066\043\147\274\377\025\342\312\073\346\246
-\354\073\354\046\021\064\110\215\366\200\053\032\043\002\353\212
-\034\072\166\052\173\126\026\034\162\052\263\252\343\140\245\000
-\237\004\233\342\157\036\024\130\133\245\154\213\130\074\303\272
-\116\072\134\367\341\226\053\076\357\007\274\244\345\135\314\115
-\237\015\341\334\252\273\341\156\032\354\217\341\266\114\115\171
-\162\135\027\065\013\035\327\301\107\332\226\044\340\320\162\250
-\132\137\146\055\020\334\057\052\023\256\046\376\012\034\031\314
-\320\076\013\234\310\011\056\371\133\226\172\107\234\351\172\363
-\005\120\164\225\163\236\060\011\363\227\202\136\346\217\071\010
-\036\131\345\065\024\102\023\377\000\234\367\276\252\120\317\342
-\121\110\327\270\157\257\370\116\176\063\230\222\024\142\072\165
-\143\317\173\372\336\202\073\251\273\071\342\304\275\054\000\016
-\310\027\254\023\357\115\045\216\330\263\220\057\251\332\051\175
-\035\257\164\072\262\047\300\301\036\076\165\243\026\251\257\172
-\042\135\237\023\032\317\247\240\353\343\206\012\323\375\346\226
-\225\327\043\310\067\335\304\174\252\066\254\230\032\022\261\340
-\116\350\261\073\365\326\157\361\060\327\002\003\001\000\001\243
-\102\060\100\060\017\006\003\125\035\023\001\001\377\004\005\060
-\003\001\001\377\060\016\006\003\125\035\017\001\001\377\004\004
-\003\002\001\006\060\035\006\003\125\035\016\004\026\004\024\113
-\305\264\100\153\255\034\263\245\034\145\156\106\066\211\207\005
-\014\016\266\060\015\006\011\052\206\110\206\367\015\001\001\013
-\005\000\003\202\002\001\000\163\127\077\054\325\225\062\176\067
-\333\226\222\353\031\136\176\123\347\101\354\021\266\107\357\265
-\336\355\164\134\305\361\216\111\340\374\156\231\023\315\237\212
-\332\315\072\012\330\072\132\011\077\137\064\320\057\003\322\146
-\035\032\275\234\220\067\310\014\216\007\132\224\105\106\052\346
-\276\172\332\241\251\244\151\022\222\260\175\066\324\104\207\327
-\121\361\051\143\326\165\315\026\344\047\211\035\370\302\062\110
-\375\333\231\320\217\137\124\164\314\254\147\064\021\142\331\014
-\012\067\207\321\243\027\110\216\322\027\035\366\327\375\333\145
-\353\375\250\324\365\326\117\244\133\165\350\305\322\140\262\333
-\011\176\045\213\173\272\122\222\236\076\350\305\167\241\074\340
-\112\163\153\141\317\206\334\103\377\377\041\376\043\135\044\112
-\365\323\155\017\142\004\005\127\202\332\156\244\063\045\171\113
-\056\124\031\213\314\054\075\060\351\321\006\377\350\062\106\276
-\265\063\166\167\250\001\135\226\301\301\325\276\256\045\300\311
-\036\012\011\040\210\241\016\311\363\157\115\202\124\000\040\247
-\322\217\344\071\124\027\056\215\036\270\033\273\033\275\232\116
-\073\020\064\334\234\210\123\357\242\061\133\130\117\221\142\310
-\302\232\232\315\025\135\070\251\326\276\370\023\265\237\022\151
-\362\120\142\254\373\027\067\364\356\270\165\147\140\020\373\203
-\120\371\104\265\165\234\100\027\262\376\375\171\135\156\130\130
-\137\060\374\000\256\257\063\301\016\116\154\272\247\246\241\177
-\062\333\070\340\261\162\027\012\053\221\354\152\143\046\355\211
-\324\170\314\164\036\005\370\153\376\214\152\166\071\051\256\145
-\043\022\225\010\042\034\227\316\133\006\356\014\342\273\274\037
-\104\223\366\330\070\105\005\041\355\344\255\253\022\266\003\244
-\102\056\055\304\011\072\003\147\151\204\232\341\131\220\212\050
-\205\325\135\164\261\321\016\040\130\233\023\245\260\143\246\355
-\173\107\375\105\125\060\244\356\232\324\346\342\207\357\230\311
-\062\202\021\051\042\274\000\012\061\136\055\017\300\216\351\153
-\262\217\056\006\330\321\221\307\306\022\364\114\375\060\027\303
-\301\332\070\133\343\251\352\346\241\272\171\357\163\330\266\123
-\127\055\366\320\341\327\110
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "StartCom Certification Authority G2"
-# Issuer: CN=StartCom Certification Authority G2,O=StartCom Ltd.,C=IL
-# Serial Number: 59 (0x3b)
-# Subject: CN=StartCom Certification Authority G2,O=StartCom Ltd.,C=IL
-# Not Valid Before: Fri Jan 01 01:00:01 2010
-# Not Valid After : Sat Dec 31 23:59:01 2039
-# Fingerprint (MD5): 78:4B:FB:9E:64:82:0A:D3:B8:4C:62:F3:64:F2:90:64
-# Fingerprint (SHA1): 31:F1:FD:68:22:63:20:EE:C6:3B:3F:9D:EA:4A:3E:53:7C:7C:39:17
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "StartCom Certification Authority G2"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\061\361\375\150\042\143\040\356\306\073\077\235\352\112\076\123
-\174\174\071\027
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\170\113\373\236\144\202\012\323\270\114\142\363\144\362\220\144
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\123\061\013\060\011\006\003\125\004\006\023\002\111\114\061
-\026\060\024\006\003\125\004\012\023\015\123\164\141\162\164\103
-\157\155\040\114\164\144\056\061\054\060\052\006\003\125\004\003
-\023\043\123\164\141\162\164\103\157\155\040\103\145\162\164\151
-\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151
-\164\171\040\107\062
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\073
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "Buypass Class 2 Root CA"
#
# Issuer: CN=Buypass Class 2 Root CA,O=Buypass AS-983163327,C=NO
@@ -17947,172 +13739,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "TURKTRUST Certificate Services Provider Root 2007"
-#
-# Issuer: O=T..RKTRUST Bilgi ..leti..im ve Bili..im G..venli..i Hizmetleri A...,L=Ankara,C=TR,CN=T..RKTRUST Elektronik Sertifika Hizmet Sa..lay..c..s..
-# Serial Number: 1 (0x1)
-# Subject: O=T..RKTRUST Bilgi ..leti..im ve Bili..im G..venli..i Hizmetleri A...,L=Ankara,C=TR,CN=T..RKTRUST Elektronik Sertifika Hizmet Sa..lay..c..s..
-# Not Valid Before: Tue Dec 25 18:37:19 2007
-# Not Valid After : Fri Dec 22 18:37:19 2017
-# Fingerprint (MD5): 2B:70:20:56:86:82:A0:18:C8:07:53:12:28:70:21:72
-# Fingerprint (SHA1): F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "TURKTRUST Certificate Services Provider Root 2007"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\277\061\077\060\075\006\003\125\004\003\014\066\124\303
-\234\122\113\124\122\125\123\124\040\105\154\145\153\164\162\157
-\156\151\153\040\123\145\162\164\151\146\151\153\141\040\110\151
-\172\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304
-\261\163\304\261\061\013\060\011\006\003\125\004\006\023\002\124
-\122\061\017\060\015\006\003\125\004\007\014\006\101\156\153\141
-\162\141\061\136\060\134\006\003\125\004\012\014\125\124\303\234
-\122\113\124\122\125\123\124\040\102\151\154\147\151\040\304\260
-\154\145\164\151\305\237\151\155\040\166\145\040\102\151\154\151
-\305\237\151\155\040\107\303\274\166\145\156\154\151\304\237\151
-\040\110\151\172\155\145\164\154\145\162\151\040\101\056\305\236
-\056\040\050\143\051\040\101\162\141\154\304\261\153\040\062\060
-\060\067
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\277\061\077\060\075\006\003\125\004\003\014\066\124\303
-\234\122\113\124\122\125\123\124\040\105\154\145\153\164\162\157
-\156\151\153\040\123\145\162\164\151\146\151\153\141\040\110\151
-\172\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304
-\261\163\304\261\061\013\060\011\006\003\125\004\006\023\002\124
-\122\061\017\060\015\006\003\125\004\007\014\006\101\156\153\141
-\162\141\061\136\060\134\006\003\125\004\012\014\125\124\303\234
-\122\113\124\122\125\123\124\040\102\151\154\147\151\040\304\260
-\154\145\164\151\305\237\151\155\040\166\145\040\102\151\154\151
-\305\237\151\155\040\107\303\274\166\145\156\154\151\304\237\151
-\040\110\151\172\155\145\164\154\145\162\151\040\101\056\305\236
-\056\040\050\143\051\040\101\162\141\154\304\261\153\040\062\060
-\060\067
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\001
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\004\075\060\202\003\045\240\003\002\001\002\002\001\001
-\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060
-\201\277\061\077\060\075\006\003\125\004\003\014\066\124\303\234
-\122\113\124\122\125\123\124\040\105\154\145\153\164\162\157\156
-\151\153\040\123\145\162\164\151\146\151\153\141\040\110\151\172
-\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304\261
-\163\304\261\061\013\060\011\006\003\125\004\006\023\002\124\122
-\061\017\060\015\006\003\125\004\007\014\006\101\156\153\141\162
-\141\061\136\060\134\006\003\125\004\012\014\125\124\303\234\122
-\113\124\122\125\123\124\040\102\151\154\147\151\040\304\260\154
-\145\164\151\305\237\151\155\040\166\145\040\102\151\154\151\305
-\237\151\155\040\107\303\274\166\145\156\154\151\304\237\151\040
-\110\151\172\155\145\164\154\145\162\151\040\101\056\305\236\056
-\040\050\143\051\040\101\162\141\154\304\261\153\040\062\060\060
-\067\060\036\027\015\060\067\061\062\062\065\061\070\063\067\061
-\071\132\027\015\061\067\061\062\062\062\061\070\063\067\061\071
-\132\060\201\277\061\077\060\075\006\003\125\004\003\014\066\124
-\303\234\122\113\124\122\125\123\124\040\105\154\145\153\164\162
-\157\156\151\153\040\123\145\162\164\151\146\151\153\141\040\110
-\151\172\155\145\164\040\123\141\304\237\154\141\171\304\261\143
-\304\261\163\304\261\061\013\060\011\006\003\125\004\006\023\002
-\124\122\061\017\060\015\006\003\125\004\007\014\006\101\156\153
-\141\162\141\061\136\060\134\006\003\125\004\012\014\125\124\303
-\234\122\113\124\122\125\123\124\040\102\151\154\147\151\040\304
-\260\154\145\164\151\305\237\151\155\040\166\145\040\102\151\154
-\151\305\237\151\155\040\107\303\274\166\145\156\154\151\304\237
-\151\040\110\151\172\155\145\164\154\145\162\151\040\101\056\305
-\236\056\040\050\143\051\040\101\162\141\154\304\261\153\040\062
-\060\060\067\060\202\001\042\060\015\006\011\052\206\110\206\367
-\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002
-\202\001\001\000\253\267\076\012\214\310\245\130\025\346\212\357
-\047\075\112\264\350\045\323\315\063\302\040\334\031\356\210\077
-\115\142\360\335\023\167\217\141\251\052\265\324\362\271\061\130
-\051\073\057\077\152\234\157\163\166\045\356\064\040\200\356\352
-\267\360\304\012\315\053\206\224\311\343\140\261\104\122\262\132
-\051\264\221\227\203\330\267\246\024\057\051\111\242\363\005\006
-\373\264\117\332\241\154\232\146\237\360\103\011\312\352\162\217
-\353\000\327\065\071\327\126\027\107\027\060\364\276\277\077\302
-\150\257\066\100\301\251\364\251\247\350\020\153\010\212\367\206
-\036\334\232\052\025\006\366\243\360\364\340\307\024\324\121\177
-\317\264\333\155\257\107\226\027\233\167\161\330\247\161\235\044
-\014\366\224\077\205\061\022\117\272\356\116\202\270\271\076\217
-\043\067\136\314\242\252\165\367\030\157\011\323\256\247\124\050
-\064\373\341\340\073\140\175\240\276\171\211\206\310\237\055\371
-\012\113\304\120\242\347\375\171\026\307\172\013\030\317\316\114
-\357\175\326\007\157\230\361\257\261\301\172\327\201\065\270\252
-\027\264\340\313\002\003\001\000\001\243\102\060\100\060\035\006
-\003\125\035\016\004\026\004\024\051\305\220\253\045\257\021\344
-\141\277\243\377\210\141\221\346\016\376\234\201\060\016\006\003
-\125\035\017\001\001\377\004\004\003\002\001\006\060\017\006\003
-\125\035\023\001\001\377\004\005\060\003\001\001\377\060\015\006
-\011\052\206\110\206\367\015\001\001\005\005\000\003\202\001\001
-\000\020\015\332\370\072\354\050\321\024\225\202\261\022\054\121
-\172\101\045\066\114\237\354\077\037\204\235\145\124\134\250\026
-\002\100\372\156\032\067\204\357\162\235\206\012\125\235\126\050
-\254\146\054\320\072\126\223\064\007\045\255\010\260\217\310\017
-\011\131\312\235\230\034\345\124\370\271\105\177\152\227\157\210
-\150\115\112\006\046\067\210\002\016\266\306\326\162\231\316\153
-\167\332\142\061\244\126\037\256\137\215\167\332\135\366\210\374
-\032\331\236\265\201\360\062\270\343\210\320\234\363\152\240\271
-\233\024\131\065\066\117\317\363\216\136\135\027\255\025\225\330
-\335\262\325\025\156\000\116\263\113\317\146\224\344\340\315\265
-\005\332\143\127\213\345\263\252\333\300\056\034\220\104\333\032
-\135\030\244\356\276\004\133\231\325\161\137\125\145\144\142\325
-\242\233\004\131\206\310\142\167\347\174\202\105\152\075\027\277
-\354\235\165\014\256\243\157\132\323\057\230\066\364\360\365\031
-\253\021\135\310\246\343\052\130\152\102\011\303\275\222\046\146
-\062\015\135\010\125\164\377\214\230\320\012\246\204\152\321\071
-\175
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "TURKTRUST Certificate Services Provider Root 2007"
-# Issuer: O=T..RKTRUST Bilgi ..leti..im ve Bili..im G..venli..i Hizmetleri A...,L=Ankara,C=TR,CN=T..RKTRUST Elektronik Sertifika Hizmet Sa..lay..c..s..
-# Serial Number: 1 (0x1)
-# Subject: O=T..RKTRUST Bilgi ..leti..im ve Bili..im G..venli..i Hizmetleri A...,L=Ankara,C=TR,CN=T..RKTRUST Elektronik Sertifika Hizmet Sa..lay..c..s..
-# Not Valid Before: Tue Dec 25 18:37:19 2007
-# Not Valid After : Fri Dec 22 18:37:19 2017
-# Fingerprint (MD5): 2B:70:20:56:86:82:A0:18:C8:07:53:12:28:70:21:72
-# Fingerprint (SHA1): F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "TURKTRUST Certificate Services Provider Root 2007"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\361\177\157\266\061\334\231\343\243\310\177\376\034\361\201\020
-\210\331\140\063
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\053\160\040\126\206\202\240\030\310\007\123\022\050\160\041\162
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\277\061\077\060\075\006\003\125\004\003\014\066\124\303
-\234\122\113\124\122\125\123\124\040\105\154\145\153\164\162\157
-\156\151\153\040\123\145\162\164\151\146\151\153\141\040\110\151
-\172\155\145\164\040\123\141\304\237\154\141\171\304\261\143\304
-\261\163\304\261\061\013\060\011\006\003\125\004\006\023\002\124
-\122\061\017\060\015\006\003\125\004\007\014\006\101\156\153\141
-\162\141\061\136\060\134\006\003\125\004\012\014\125\124\303\234
-\122\113\124\122\125\123\124\040\102\151\154\147\151\040\304\260
-\154\145\164\151\305\237\151\155\040\166\145\040\102\151\154\151
-\305\237\151\155\040\107\303\274\166\145\156\154\151\304\237\151
-\040\110\151\172\155\145\164\154\145\162\151\040\101\056\305\236
-\056\040\050\143\051\040\101\162\141\154\304\261\153\040\062\060
-\060\067
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\001
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "D-TRUST Root Class 3 CA 2 2009"
#
# Issuer: CN=D-TRUST Root Class 3 CA 2 2009,O=D-Trust GmbH,C=DE
@@ -18399,269 +14025,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "PSCProcert"
-#
-# Issuer: E=acraiz@suscerte.gob.ve,OU=Superintendencia de Servicios de Certificacion Electronica,O=Sistema Nacional de Certificacion Electronica,ST=Distrito Capital,L=Caracas,C=VE,CN=Autoridad de Certificacion Raiz del Estado Venezolano
-# Serial Number: 11 (0xb)
-# Subject: CN=PSCProcert,C=VE,O=Sistema Nacional de Certificacion Electronica,OU=Proveedor de Certificados PROCERT,ST=Miranda,L=Chacao,E=contacto@procert.net.ve
-# Not Valid Before: Tue Dec 28 16:51:00 2010
-# Not Valid After : Fri Dec 25 23:59:59 2020
-# Fingerprint (MD5): E6:24:E9:12:01:AE:0C:DE:8E:85:C4:CE:A3:12:DD:EC
-# Fingerprint (SHA1): 70:C1:8D:74:B4:28:81:0A:E4:FD:A5:75:D7:01:9F:99:B0:3D:50:74
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "PSCProcert"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\321\061\046\060\044\006\011\052\206\110\206\367\015\001
-\011\001\026\027\143\157\156\164\141\143\164\157\100\160\162\157
-\143\145\162\164\056\156\145\164\056\166\145\061\017\060\015\006
-\003\125\004\007\023\006\103\150\141\143\141\157\061\020\060\016
-\006\003\125\004\010\023\007\115\151\162\141\156\144\141\061\052
-\060\050\006\003\125\004\013\023\041\120\162\157\166\145\145\144
-\157\162\040\144\145\040\103\145\162\164\151\146\151\143\141\144
-\157\163\040\120\122\117\103\105\122\124\061\066\060\064\006\003
-\125\004\012\023\055\123\151\163\164\145\155\141\040\116\141\143
-\151\157\156\141\154\040\144\145\040\103\145\162\164\151\146\151
-\143\141\143\151\157\156\040\105\154\145\143\164\162\157\156\151
-\143\141\061\013\060\011\006\003\125\004\006\023\002\126\105\061
-\023\060\021\006\003\125\004\003\023\012\120\123\103\120\162\157
-\143\145\162\164
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\202\001\036\061\076\060\074\006\003\125\004\003\023\065\101
-\165\164\157\162\151\144\141\144\040\144\145\040\103\145\162\164
-\151\146\151\143\141\143\151\157\156\040\122\141\151\172\040\144
-\145\154\040\105\163\164\141\144\157\040\126\145\156\145\172\157
-\154\141\156\157\061\013\060\011\006\003\125\004\006\023\002\126
-\105\061\020\060\016\006\003\125\004\007\023\007\103\141\162\141
-\143\141\163\061\031\060\027\006\003\125\004\010\023\020\104\151
-\163\164\162\151\164\157\040\103\141\160\151\164\141\154\061\066
-\060\064\006\003\125\004\012\023\055\123\151\163\164\145\155\141
-\040\116\141\143\151\157\156\141\154\040\144\145\040\103\145\162
-\164\151\146\151\143\141\143\151\157\156\040\105\154\145\143\164
-\162\157\156\151\143\141\061\103\060\101\006\003\125\004\013\023
-\072\123\165\160\145\162\151\156\164\145\156\144\145\156\143\151
-\141\040\144\145\040\123\145\162\166\151\143\151\157\163\040\144
-\145\040\103\145\162\164\151\146\151\143\141\143\151\157\156\040
-\105\154\145\143\164\162\157\156\151\143\141\061\045\060\043\006
-\011\052\206\110\206\367\015\001\011\001\026\026\141\143\162\141
-\151\172\100\163\165\163\143\145\162\164\145\056\147\157\142\056
-\166\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\013
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\011\206\060\202\007\156\240\003\002\001\002\002\001\013
-\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060
-\202\001\036\061\076\060\074\006\003\125\004\003\023\065\101\165
-\164\157\162\151\144\141\144\040\144\145\040\103\145\162\164\151
-\146\151\143\141\143\151\157\156\040\122\141\151\172\040\144\145
-\154\040\105\163\164\141\144\157\040\126\145\156\145\172\157\154
-\141\156\157\061\013\060\011\006\003\125\004\006\023\002\126\105
-\061\020\060\016\006\003\125\004\007\023\007\103\141\162\141\143
-\141\163\061\031\060\027\006\003\125\004\010\023\020\104\151\163
-\164\162\151\164\157\040\103\141\160\151\164\141\154\061\066\060
-\064\006\003\125\004\012\023\055\123\151\163\164\145\155\141\040
-\116\141\143\151\157\156\141\154\040\144\145\040\103\145\162\164
-\151\146\151\143\141\143\151\157\156\040\105\154\145\143\164\162
-\157\156\151\143\141\061\103\060\101\006\003\125\004\013\023\072
-\123\165\160\145\162\151\156\164\145\156\144\145\156\143\151\141
-\040\144\145\040\123\145\162\166\151\143\151\157\163\040\144\145
-\040\103\145\162\164\151\146\151\143\141\143\151\157\156\040\105
-\154\145\143\164\162\157\156\151\143\141\061\045\060\043\006\011
-\052\206\110\206\367\015\001\011\001\026\026\141\143\162\141\151
-\172\100\163\165\163\143\145\162\164\145\056\147\157\142\056\166
-\145\060\036\027\015\061\060\061\062\062\070\061\066\065\061\060
-\060\132\027\015\062\060\061\062\062\065\062\063\065\071\065\071
-\132\060\201\321\061\046\060\044\006\011\052\206\110\206\367\015
-\001\011\001\026\027\143\157\156\164\141\143\164\157\100\160\162
-\157\143\145\162\164\056\156\145\164\056\166\145\061\017\060\015
-\006\003\125\004\007\023\006\103\150\141\143\141\157\061\020\060
-\016\006\003\125\004\010\023\007\115\151\162\141\156\144\141\061
-\052\060\050\006\003\125\004\013\023\041\120\162\157\166\145\145
-\144\157\162\040\144\145\040\103\145\162\164\151\146\151\143\141
-\144\157\163\040\120\122\117\103\105\122\124\061\066\060\064\006
-\003\125\004\012\023\055\123\151\163\164\145\155\141\040\116\141
-\143\151\157\156\141\154\040\144\145\040\103\145\162\164\151\146
-\151\143\141\143\151\157\156\040\105\154\145\143\164\162\157\156
-\151\143\141\061\013\060\011\006\003\125\004\006\023\002\126\105
-\061\023\060\021\006\003\125\004\003\023\012\120\123\103\120\162
-\157\143\145\162\164\060\202\002\042\060\015\006\011\052\206\110
-\206\367\015\001\001\001\005\000\003\202\002\017\000\060\202\002
-\012\002\202\002\001\000\325\267\364\243\224\063\241\106\251\125
-\141\111\015\250\207\163\136\221\055\160\301\006\032\224\332\075
-\354\025\102\301\365\214\256\152\027\361\212\255\374\200\225\352
-\203\104\242\133\172\125\316\117\247\245\325\272\270\037\240\047
-\300\120\123\076\215\271\300\016\270\025\334\326\154\370\236\370
-\004\045\337\200\217\020\205\335\175\057\173\200\335\127\000\144
-\043\370\156\311\276\225\117\341\165\354\340\176\136\225\315\261
-\357\276\172\102\330\311\054\323\353\032\032\042\213\267\177\006
-\211\345\074\365\022\300\273\323\013\231\137\220\174\216\055\057
-\167\063\222\112\041\106\250\251\010\254\361\366\021\002\331\225
-\026\236\215\057\226\346\002\335\165\302\024\052\132\326\311\175
-\045\302\301\374\252\147\205\342\354\276\321\174\074\372\257\325
-\156\377\123\101\324\365\062\070\261\342\137\304\371\216\020\357
-\006\251\002\211\377\343\014\156\227\340\337\235\333\041\320\364
-\076\010\151\154\330\324\344\066\370\203\266\262\066\217\234\357
-\072\067\026\175\277\242\151\327\073\133\162\320\257\252\077\134
-\146\223\254\012\042\141\266\322\240\231\310\124\223\135\250\266
-\321\275\135\012\136\167\224\242\055\300\202\216\274\312\003\052
-\064\256\163\361\324\265\014\275\276\147\233\124\353\341\372\240
-\132\354\070\176\076\301\314\242\307\104\061\165\352\077\345\007
-\322\253\241\045\226\366\346\344\240\135\067\030\071\141\000\063
-\135\106\324\000\304\264\312\074\361\242\243\076\363\072\377\151
-\060\056\100\335\366\237\234\046\311\226\067\255\347\071\242\277
-\352\151\333\125\042\225\123\052\224\265\337\255\026\070\201\165
-\146\343\307\054\033\223\234\252\214\243\312\331\154\074\027\155
-\234\334\174\123\340\040\047\103\066\371\022\341\074\134\275\146
-\277\242\151\043\070\270\231\140\231\016\126\123\072\234\176\024
-\214\260\006\157\361\206\166\220\257\375\257\376\220\306\217\237
-\177\213\222\043\234\347\025\166\217\325\213\224\023\162\151\373
-\053\141\143\210\357\346\244\136\346\243\027\152\130\107\313\161
-\117\024\013\136\310\002\010\046\242\313\351\257\153\212\031\307
-\313\024\126\365\341\332\265\331\374\277\163\070\332\371\347\257
-\156\244\067\342\007\047\002\003\001\000\001\243\202\003\027\060
-\202\003\023\060\022\006\003\125\035\023\001\001\377\004\010\060
-\006\001\001\377\002\001\001\060\067\006\003\125\035\022\004\060
-\060\056\202\017\163\165\163\143\145\162\164\145\056\147\157\142
-\056\166\145\240\033\006\005\140\206\136\002\002\240\022\014\020
-\122\111\106\055\107\055\062\060\060\060\064\060\063\066\055\060
-\060\035\006\003\125\035\016\004\026\004\024\101\017\031\070\252
-\231\177\102\013\244\327\047\230\124\242\027\114\055\121\124\060
-\202\001\120\006\003\125\035\043\004\202\001\107\060\202\001\103
-\200\024\255\273\042\035\306\340\322\001\250\375\166\120\122\223
-\355\230\301\115\256\323\241\202\001\046\244\202\001\042\060\202
-\001\036\061\076\060\074\006\003\125\004\003\023\065\101\165\164
-\157\162\151\144\141\144\040\144\145\040\103\145\162\164\151\146
-\151\143\141\143\151\157\156\040\122\141\151\172\040\144\145\154
-\040\105\163\164\141\144\157\040\126\145\156\145\172\157\154\141
-\156\157\061\013\060\011\006\003\125\004\006\023\002\126\105\061
-\020\060\016\006\003\125\004\007\023\007\103\141\162\141\143\141
-\163\061\031\060\027\006\003\125\004\010\023\020\104\151\163\164
-\162\151\164\157\040\103\141\160\151\164\141\154\061\066\060\064
-\006\003\125\004\012\023\055\123\151\163\164\145\155\141\040\116
-\141\143\151\157\156\141\154\040\144\145\040\103\145\162\164\151
-\146\151\143\141\143\151\157\156\040\105\154\145\143\164\162\157
-\156\151\143\141\061\103\060\101\006\003\125\004\013\023\072\123
-\165\160\145\162\151\156\164\145\156\144\145\156\143\151\141\040
-\144\145\040\123\145\162\166\151\143\151\157\163\040\144\145\040
-\103\145\162\164\151\146\151\143\141\143\151\157\156\040\105\154
-\145\143\164\162\157\156\151\143\141\061\045\060\043\006\011\052
-\206\110\206\367\015\001\011\001\026\026\141\143\162\141\151\172
-\100\163\165\163\143\145\162\164\145\056\147\157\142\056\166\145
-\202\001\012\060\016\006\003\125\035\017\001\001\377\004\004\003
-\002\001\006\060\115\006\003\125\035\021\004\106\060\104\202\016
-\160\162\157\143\145\162\164\056\156\145\164\056\166\145\240\025
-\006\005\140\206\136\002\001\240\014\014\012\120\123\103\055\060
-\060\060\060\060\062\240\033\006\005\140\206\136\002\002\240\022
-\014\020\122\111\106\055\112\055\063\061\066\063\065\063\067\063
-\055\067\060\166\006\003\125\035\037\004\157\060\155\060\106\240
-\104\240\102\206\100\150\164\164\160\072\057\057\167\167\167\056
-\163\165\163\143\145\162\164\145\056\147\157\142\056\166\145\057
-\154\143\162\057\103\105\122\124\111\106\111\103\101\104\117\055
-\122\101\111\132\055\123\110\101\063\070\064\103\122\114\104\105
-\122\056\143\162\154\060\043\240\041\240\037\206\035\154\144\141
-\160\072\057\057\141\143\162\141\151\172\056\163\165\163\143\145
-\162\164\145\056\147\157\142\056\166\145\060\067\006\010\053\006
-\001\005\005\007\001\001\004\053\060\051\060\047\006\010\053\006
-\001\005\005\007\060\001\206\033\150\164\164\160\072\057\057\157
-\143\163\160\056\163\165\163\143\145\162\164\145\056\147\157\142
-\056\166\145\060\101\006\003\125\035\040\004\072\060\070\060\066
-\006\006\140\206\136\003\001\002\060\054\060\052\006\010\053\006
-\001\005\005\007\002\001\026\036\150\164\164\160\072\057\057\167
-\167\167\056\163\165\163\143\145\162\164\145\056\147\157\142\056
-\166\145\057\144\160\143\060\015\006\011\052\206\110\206\367\015
-\001\001\013\005\000\003\202\002\001\000\053\131\353\042\231\273
-\204\252\117\336\220\306\321\206\161\043\236\113\003\221\107\160
-\273\300\222\140\354\340\324\347\155\306\323\355\147\203\167\122
-\325\362\345\167\247\066\262\343\124\276\331\273\012\233\021\357
-\141\364\306\231\063\231\365\257\000\071\215\203\277\246\275\065
-\176\054\134\061\064\157\154\333\363\144\001\230\252\224\054\101
-\335\025\206\312\153\051\116\026\300\111\374\327\203\110\023\007
-\121\204\061\122\210\273\206\027\307\153\057\212\040\255\305\013
-\217\160\076\052\273\033\161\217\271\244\240\375\330\225\331\257
-\131\277\045\053\230\351\143\223\057\140\036\304\252\370\167\365
-\213\154\057\355\176\056\265\117\100\015\356\274\127\167\347\331
-\266\324\077\225\047\072\040\325\345\256\253\154\065\237\301\241
-\035\131\334\204\201\356\115\007\342\110\266\236\113\225\055\101
-\261\341\350\336\176\057\005\036\150\356\277\273\220\145\072\310
-\356\352\261\030\067\034\142\223\244\240\061\354\161\154\221\346
-\244\171\211\132\024\247\024\120\005\114\244\000\127\060\054\301
-\265\141\226\334\076\036\204\257\071\102\317\345\320\054\261\044
-\274\337\100\303\355\177\143\112\275\341\117\022\144\206\225\363
-\260\347\310\267\341\123\275\222\346\363\014\226\271\353\350\346
-\222\355\247\201\011\024\013\374\225\172\317\217\326\064\117\066
-\022\334\136\321\064\165\306\106\200\057\225\004\214\307\206\304
-\250\046\211\250\077\031\233\201\273\121\244\112\206\253\013\021
-\017\261\256\143\123\155\050\352\335\063\126\070\034\262\255\200
-\323\327\162\275\232\154\231\143\350\000\273\101\166\005\267\133
-\231\030\212\303\270\022\134\126\317\126\014\175\350\342\317\355
-\274\164\107\373\356\323\027\116\042\117\126\377\120\363\056\346
-\071\246\202\326\161\312\336\267\325\272\150\010\355\231\314\375
-\242\222\313\151\270\235\371\012\244\246\076\117\223\050\052\141
-\154\007\046\000\377\226\137\150\206\270\270\316\312\125\340\253
-\261\075\177\230\327\063\016\132\075\330\170\302\304\140\057\307
-\142\360\141\221\322\070\260\366\236\125\333\100\200\005\022\063
-\316\035\222\233\321\151\263\377\277\361\222\012\141\065\077\335
-\376\206\364\274\340\032\161\263\142\246
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "PSCProcert"
-# Issuer: E=acraiz@suscerte.gob.ve,OU=Superintendencia de Servicios de Certificacion Electronica,O=Sistema Nacional de Certificacion Electronica,ST=Distrito Capital,L=Caracas,C=VE,CN=Autoridad de Certificacion Raiz del Estado Venezolano
-# Serial Number: 11 (0xb)
-# Subject: CN=PSCProcert,C=VE,O=Sistema Nacional de Certificacion Electronica,OU=Proveedor de Certificados PROCERT,ST=Miranda,L=Chacao,E=contacto@procert.net.ve
-# Not Valid Before: Tue Dec 28 16:51:00 2010
-# Not Valid After : Fri Dec 25 23:59:59 2020
-# Fingerprint (MD5): E6:24:E9:12:01:AE:0C:DE:8E:85:C4:CE:A3:12:DD:EC
-# Fingerprint (SHA1): 70:C1:8D:74:B4:28:81:0A:E4:FD:A5:75:D7:01:9F:99:B0:3D:50:74
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "PSCProcert"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\160\301\215\164\264\050\201\012\344\375\245\165\327\001\237\231
-\260\075\120\164
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\346\044\351\022\001\256\014\336\216\205\304\316\243\022\335\354
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\202\001\036\061\076\060\074\006\003\125\004\003\023\065\101
-\165\164\157\162\151\144\141\144\040\144\145\040\103\145\162\164
-\151\146\151\143\141\143\151\157\156\040\122\141\151\172\040\144
-\145\154\040\105\163\164\141\144\157\040\126\145\156\145\172\157
-\154\141\156\157\061\013\060\011\006\003\125\004\006\023\002\126
-\105\061\020\060\016\006\003\125\004\007\023\007\103\141\162\141
-\143\141\163\061\031\060\027\006\003\125\004\010\023\020\104\151
-\163\164\162\151\164\157\040\103\141\160\151\164\141\154\061\066
-\060\064\006\003\125\004\012\023\055\123\151\163\164\145\155\141
-\040\116\141\143\151\157\156\141\154\040\144\145\040\103\145\162
-\164\151\146\151\143\141\143\151\157\156\040\105\154\145\143\164
-\162\157\156\151\143\141\061\103\060\101\006\003\125\004\013\023
-\072\123\165\160\145\162\151\156\164\145\156\144\145\156\143\151
-\141\040\144\145\040\123\145\162\166\151\143\151\157\163\040\144
-\145\040\103\145\162\164\151\146\151\143\141\143\151\157\156\040
-\105\154\145\143\164\162\157\156\151\143\141\061\045\060\043\006
-\011\052\206\110\206\367\015\001\011\001\026\026\141\143\162\141
-\151\172\100\163\165\163\143\145\162\164\145\056\147\157\142\056
-\166\145
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\001\013
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "Swisscom Root CA 2"
#
# Issuer: CN=Swisscom Root CA 2,OU=Digital Certificate Services,O=Swisscom,C=ch
@@ -18837,169 +14200,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "CA Disig Root R1"
-#
-# Issuer: CN=CA Disig Root R1,O=Disig a.s.,L=Bratislava,C=SK
-# Serial Number:00:c3:03:9a:ee:50:90:6e:28
-# Subject: CN=CA Disig Root R1,O=Disig a.s.,L=Bratislava,C=SK
-# Not Valid Before: Thu Jul 19 09:06:56 2012
-# Not Valid After : Sat Jul 19 09:06:56 2042
-# Fingerprint (MD5): BE:EC:11:93:9A:F5:69:21:BC:D7:C1:C0:67:89:CC:2A
-# Fingerprint (SHA1): 8E:1C:74:F8:A6:20:B9:E5:8A:F4:61:FA:EC:2B:47:56:51:1A:52:C6
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "CA Disig Root R1"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\122\061\013\060\011\006\003\125\004\006\023\002\123\113\061
-\023\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163
-\154\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104
-\151\163\151\147\040\141\056\163\056\061\031\060\027\006\003\125
-\004\003\023\020\103\101\040\104\151\163\151\147\040\122\157\157
-\164\040\122\061
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\122\061\013\060\011\006\003\125\004\006\023\002\123\113\061
-\023\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163
-\154\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104
-\151\163\151\147\040\141\056\163\056\061\031\060\027\006\003\125
-\004\003\023\020\103\101\040\104\151\163\151\147\040\122\157\157
-\164\040\122\061
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\011\000\303\003\232\356\120\220\156\050
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\151\060\202\003\121\240\003\002\001\002\002\011\000
-\303\003\232\356\120\220\156\050\060\015\006\011\052\206\110\206
-\367\015\001\001\005\005\000\060\122\061\013\060\011\006\003\125
-\004\006\023\002\123\113\061\023\060\021\006\003\125\004\007\023
-\012\102\162\141\164\151\163\154\141\166\141\061\023\060\021\006
-\003\125\004\012\023\012\104\151\163\151\147\040\141\056\163\056
-\061\031\060\027\006\003\125\004\003\023\020\103\101\040\104\151
-\163\151\147\040\122\157\157\164\040\122\061\060\036\027\015\061
-\062\060\067\061\071\060\071\060\066\065\066\132\027\015\064\062
-\060\067\061\071\060\071\060\066\065\066\132\060\122\061\013\060
-\011\006\003\125\004\006\023\002\123\113\061\023\060\021\006\003
-\125\004\007\023\012\102\162\141\164\151\163\154\141\166\141\061
-\023\060\021\006\003\125\004\012\023\012\104\151\163\151\147\040
-\141\056\163\056\061\031\060\027\006\003\125\004\003\023\020\103
-\101\040\104\151\163\151\147\040\122\157\157\164\040\122\061\060
-\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001\001
-\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001\000
-\252\303\170\367\334\230\243\247\132\136\167\030\262\335\004\144
-\017\143\375\233\226\011\200\325\350\252\245\342\234\046\224\072
-\350\231\163\214\235\337\327\337\203\363\170\117\100\341\177\322
-\247\322\345\312\023\223\347\355\306\167\137\066\265\224\257\350
-\070\216\333\233\345\174\273\314\215\353\165\163\341\044\315\346
-\247\055\031\056\330\326\212\153\024\353\010\142\012\330\334\263
-\000\115\303\043\174\137\103\010\043\062\022\334\355\014\255\300
-\175\017\245\172\102\331\132\160\331\277\247\327\001\034\366\233
-\253\216\267\112\206\170\240\036\126\061\256\357\202\012\200\101
-\367\033\311\256\253\062\046\324\054\153\355\175\153\344\342\136
-\042\012\105\313\204\061\115\254\376\333\321\107\272\371\140\227
-\071\261\145\307\336\373\231\344\012\042\261\055\115\345\110\046
-\151\253\342\252\363\373\374\222\051\062\351\263\076\115\037\047
-\241\315\216\271\027\373\045\076\311\156\363\167\332\015\022\366
-\135\307\273\066\020\325\124\326\363\340\342\107\110\346\336\024
-\332\141\122\257\046\264\365\161\117\311\327\322\006\337\143\312
-\377\041\350\131\006\340\010\325\204\025\123\367\103\345\174\305
-\240\211\230\153\163\306\150\316\145\336\275\177\005\367\261\356
-\366\127\241\140\225\305\314\352\223\072\276\231\256\233\002\243
-\255\311\026\265\316\335\136\231\170\176\032\071\176\262\300\005
-\244\300\202\245\243\107\236\214\352\134\266\274\147\333\346\052
-\115\322\004\334\243\256\105\367\274\213\234\034\247\326\325\003
-\334\010\313\056\026\312\134\100\063\350\147\303\056\347\246\104
-\352\021\105\034\065\145\055\036\105\141\044\033\202\056\245\235
-\063\135\145\370\101\371\056\313\224\077\037\243\014\061\044\104
-\355\307\136\255\120\272\306\101\233\254\360\027\145\300\370\135
-\157\133\240\012\064\074\356\327\352\210\237\230\371\257\116\044
-\372\227\262\144\166\332\253\364\355\343\303\140\357\325\371\002
-\310\055\237\203\257\147\151\006\247\061\125\325\317\113\157\377
-\004\005\307\130\254\137\026\033\345\322\243\353\061\333\037\063
-\025\115\320\362\245\123\365\313\341\075\116\150\055\330\022\335
-\252\362\346\115\233\111\345\305\050\241\272\260\132\306\240\265
-\002\003\001\000\001\243\102\060\100\060\017\006\003\125\035\023
-\001\001\377\004\005\060\003\001\001\377\060\016\006\003\125\035
-\017\001\001\377\004\004\003\002\001\006\060\035\006\003\125\035
-\016\004\026\004\024\211\012\264\070\223\032\346\253\356\233\221
-\030\371\365\074\076\065\320\323\202\060\015\006\011\052\206\110
-\206\367\015\001\001\005\005\000\003\202\002\001\000\062\213\366
-\235\112\311\276\024\345\214\254\070\312\072\011\324\033\316\206
-\263\335\353\324\272\050\276\022\256\105\054\004\164\254\023\121
-\305\130\030\146\115\202\332\325\334\223\300\047\341\276\174\237
-\122\236\022\126\366\325\234\251\364\165\234\372\067\022\217\034
-\223\354\127\376\007\017\253\325\022\367\017\256\141\136\126\200
-\111\365\374\060\365\233\117\037\101\057\034\204\323\211\307\342
-\332\002\166\355\011\317\154\301\270\034\203\034\026\372\224\315
-\175\240\310\030\322\310\235\156\365\275\151\324\155\075\065\350
-\036\242\117\140\327\007\051\374\262\243\244\235\156\025\222\126
-\031\114\012\260\351\174\322\031\115\102\106\354\275\375\366\127
-\133\335\230\176\244\115\314\162\003\203\130\135\357\223\072\101
-\172\143\252\174\072\250\365\254\244\321\335\242\055\266\052\374
-\237\001\216\342\020\261\304\312\344\147\333\125\045\031\077\375
-\350\066\176\263\341\341\201\257\021\026\213\120\227\140\031\202
-\000\300\153\115\163\270\321\023\007\076\352\266\061\117\360\102
-\232\155\342\021\164\345\224\254\215\204\225\074\041\257\305\332
-\107\310\337\071\142\142\313\133\120\013\327\201\100\005\234\233
-\355\272\266\213\036\004\157\226\040\071\355\244\175\051\333\110
-\316\202\334\324\002\215\035\004\061\132\307\113\360\154\141\122
-\327\264\121\302\201\154\315\341\373\247\241\322\222\166\317\261
-\017\067\130\244\362\122\161\147\077\014\210\170\200\211\301\310
-\265\037\222\143\276\247\172\212\126\054\032\250\246\234\265\135
-\263\143\320\023\040\241\353\221\154\320\215\175\257\337\013\344
-\027\271\206\236\070\261\224\014\130\214\340\125\252\073\143\155
-\232\211\140\270\144\052\222\306\067\364\176\103\103\267\163\350
-\001\347\177\227\017\327\362\173\031\375\032\327\217\311\372\205
-\153\172\235\236\211\266\246\050\231\223\210\100\367\076\315\121
-\243\312\352\357\171\107\041\265\376\062\342\307\303\121\157\276
-\200\164\360\244\303\072\362\117\351\137\337\031\012\362\073\023
-\103\254\061\244\263\347\353\374\030\326\001\251\363\052\217\066
-\016\353\264\261\274\267\114\311\153\277\241\363\331\364\355\342
-\360\343\355\144\236\075\057\226\122\117\200\123\213
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "CA Disig Root R1"
-# Issuer: CN=CA Disig Root R1,O=Disig a.s.,L=Bratislava,C=SK
-# Serial Number:00:c3:03:9a:ee:50:90:6e:28
-# Subject: CN=CA Disig Root R1,O=Disig a.s.,L=Bratislava,C=SK
-# Not Valid Before: Thu Jul 19 09:06:56 2012
-# Not Valid After : Sat Jul 19 09:06:56 2042
-# Fingerprint (MD5): BE:EC:11:93:9A:F5:69:21:BC:D7:C1:C0:67:89:CC:2A
-# Fingerprint (SHA1): 8E:1C:74:F8:A6:20:B9:E5:8A:F4:61:FA:EC:2B:47:56:51:1A:52:C6
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "CA Disig Root R1"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\216\034\164\370\246\040\271\345\212\364\141\372\354\053\107\126
-\121\032\122\306
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\276\354\021\223\232\365\151\041\274\327\301\300\147\211\314\052
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\122\061\013\060\011\006\003\125\004\006\023\002\123\113\061
-\023\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163
-\154\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104
-\151\163\151\147\040\141\056\163\056\061\031\060\027\006\003\125
-\004\003\023\020\103\101\040\104\151\163\151\147\040\122\157\157
-\164\040\122\061
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\011\000\303\003\232\356\120\220\156\050
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "CA Disig Root R2"
#
# Issuer: CN=CA Disig Root R2,O=Disig a.s.,L=Bratislava,C=SK
@@ -21317,333 +16517,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "WoSign"
-#
-# Issuer: CN=Certification Authority of WoSign,O=WoSign CA Limited,C=CN
-# Serial Number:5e:68:d6:11:71:94:63:50:56:00:68:f3:3e:c9:c5:91
-# Subject: CN=Certification Authority of WoSign,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Aug 08 01:00:01 2009
-# Not Valid After : Mon Aug 08 01:00:01 2039
-# Fingerprint (SHA-256): 4B:22:D5:A6:AE:C9:9F:3C:DB:79:AA:5E:C0:68:38:47:9C:D5:EC:BA:71:64:F7:F2:2D:C1:D6:5F:63:D8:57:08
-# Fingerprint (SHA1): B9:42:94:BF:91:EA:8F:B6:4B:E6:10:97:C7:FB:00:13:59:B6:76:CB
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "WoSign"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\125\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\052\060\050\006
-\003\125\004\003\023\041\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\040\157\146
-\040\127\157\123\151\147\156
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\125\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\052\060\050\006
-\003\125\004\003\023\041\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\040\157\146
-\040\127\157\123\151\147\156
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\136\150\326\021\161\224\143\120\126\000\150\363\076\311
-\305\221
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\166\060\202\003\136\240\003\002\001\002\002\020\136
-\150\326\021\161\224\143\120\126\000\150\363\076\311\305\221\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\125
-\061\013\060\011\006\003\125\004\006\023\002\103\116\061\032\060
-\030\006\003\125\004\012\023\021\127\157\123\151\147\156\040\103
-\101\040\114\151\155\151\164\145\144\061\052\060\050\006\003\125
-\004\003\023\041\103\145\162\164\151\146\151\143\141\164\151\157
-\156\040\101\165\164\150\157\162\151\164\171\040\157\146\040\127
-\157\123\151\147\156\060\036\027\015\060\071\060\070\060\070\060
-\061\060\060\060\061\132\027\015\063\071\060\070\060\070\060\061
-\060\060\060\061\132\060\125\061\013\060\011\006\003\125\004\006
-\023\002\103\116\061\032\060\030\006\003\125\004\012\023\021\127
-\157\123\151\147\156\040\103\101\040\114\151\155\151\164\145\144
-\061\052\060\050\006\003\125\004\003\023\041\103\145\162\164\151
-\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151
-\164\171\040\157\146\040\127\157\123\151\147\156\060\202\002\042
-\060\015\006\011\052\206\110\206\367\015\001\001\001\005\000\003
-\202\002\017\000\060\202\002\012\002\202\002\001\000\275\312\215
-\254\270\221\025\126\227\173\153\134\172\302\336\153\331\241\260
-\303\020\043\372\247\241\262\314\061\372\076\331\246\051\157\026
-\075\340\153\370\270\100\137\333\071\250\000\172\213\240\115\124
-\175\302\042\170\374\216\011\270\250\205\327\314\225\227\113\164
-\330\236\176\360\000\344\016\211\256\111\050\104\032\020\231\062
-\017\045\210\123\244\015\263\017\022\010\026\013\003\161\047\034
-\177\341\333\322\375\147\150\304\005\135\012\016\135\160\327\330
-\227\240\274\123\101\232\221\215\364\236\066\146\172\176\126\301
-\220\137\346\261\150\040\066\244\214\044\054\054\107\013\131\166
-\146\060\265\276\336\355\217\370\235\323\273\001\060\346\362\363
-\016\340\054\222\200\363\205\371\050\212\264\124\056\232\355\367
-\166\374\025\150\026\353\112\154\353\056\022\217\324\317\376\014
-\307\134\035\013\176\005\062\276\136\260\011\052\102\325\311\116
-\220\263\131\015\273\172\176\315\325\010\132\264\177\330\034\151
-\021\371\047\017\173\006\257\124\203\030\173\341\335\124\172\121
-\150\156\167\374\306\277\122\112\146\106\241\262\147\032\273\243
-\117\167\240\276\135\377\374\126\013\103\162\167\220\312\236\371
-\362\071\365\015\251\364\352\327\347\263\020\057\060\102\067\041
-\314\060\160\311\206\230\017\314\130\115\203\273\175\345\032\245
-\067\215\266\254\062\227\000\072\143\161\044\036\236\067\304\377
-\164\324\067\300\342\376\210\106\140\021\335\010\077\120\066\253
-\270\172\244\225\142\152\156\260\312\152\041\132\151\363\363\373
-\035\160\071\225\363\247\156\246\201\211\241\210\305\073\161\312
-\243\122\356\203\273\375\240\167\364\344\157\347\102\333\155\112
-\231\212\064\110\274\027\334\344\200\010\042\266\362\061\300\077
-\004\076\353\237\040\171\326\270\006\144\144\002\061\327\251\315
-\122\373\204\105\151\011\000\052\334\125\213\304\006\106\113\300
-\112\035\011\133\071\050\375\251\253\316\000\371\056\110\113\046
-\346\060\114\245\130\312\264\104\202\117\347\221\036\063\303\260
-\223\377\021\374\201\322\312\037\161\051\335\166\117\222\045\257
-\035\201\267\017\057\214\303\006\314\057\047\243\112\344\016\231
-\272\174\036\105\037\177\252\031\105\226\375\374\075\002\003\001
-\000\001\243\102\060\100\060\016\006\003\125\035\017\001\001\377
-\004\004\003\002\001\006\060\017\006\003\125\035\023\001\001\377
-\004\005\060\003\001\001\377\060\035\006\003\125\035\016\004\026
-\004\024\341\146\317\016\321\361\263\113\267\006\040\024\376\207
-\022\325\366\376\373\076\060\015\006\011\052\206\110\206\367\015
-\001\001\005\005\000\003\202\002\001\000\250\313\162\100\262\166
-\301\176\173\374\255\144\343\062\173\314\074\266\135\106\323\365
-\054\342\160\135\310\056\330\006\175\230\321\013\041\240\211\131
-\044\001\235\371\257\011\175\012\043\202\064\325\374\174\162\231
-\271\243\327\124\364\352\122\160\016\305\365\326\073\341\072\011
-\062\346\041\071\223\275\263\025\352\117\152\364\365\213\077\057
-\174\215\130\056\305\341\071\240\076\307\075\112\163\236\100\172
-\300\053\141\251\147\311\363\044\271\263\155\125\054\132\035\236
-\045\162\316\013\255\252\307\125\142\013\276\373\143\263\141\104
-\043\243\313\341\032\016\367\232\006\115\336\324\043\116\041\226
-\133\071\133\127\035\057\135\010\136\011\171\377\174\227\265\115
-\203\256\015\326\346\243\171\340\063\320\231\226\002\060\247\076
-\377\322\243\103\077\005\132\006\352\104\002\332\174\370\110\320
-\063\251\371\007\307\225\341\365\076\365\135\161\272\362\225\251
-\164\210\141\131\343\277\312\132\023\272\162\264\214\135\066\207
-\351\246\305\074\023\277\336\320\104\046\356\267\354\056\160\372
-\327\235\267\254\345\305\100\132\346\327\154\173\054\303\126\233
-\107\315\013\316\372\033\264\041\327\267\146\270\364\045\060\213
-\134\015\271\352\147\262\364\155\256\325\241\236\117\330\237\351
-\047\002\260\035\006\326\217\343\373\110\022\237\177\021\241\020
-\076\114\121\072\226\260\321\023\361\307\330\046\256\072\312\221
-\304\151\235\337\001\051\144\121\157\150\332\024\354\010\101\227
-\220\215\320\262\200\362\317\302\075\277\221\150\305\200\147\036
-\304\140\023\125\325\141\231\127\174\272\225\017\141\111\072\312
-\165\274\311\012\223\077\147\016\022\362\050\342\061\033\300\127
-\026\337\010\174\031\301\176\017\037\205\036\012\066\174\133\176
-\047\274\172\277\340\333\364\332\122\275\336\014\124\160\061\221
-\103\225\310\274\360\076\335\011\176\060\144\120\355\177\001\244
-\063\147\115\150\117\276\025\357\260\366\002\021\242\033\023\045
-\072\334\302\131\361\343\134\106\273\147\054\002\106\352\036\110
-\246\346\133\331\265\274\121\242\222\226\333\252\306\067\042\246
-\376\314\040\164\243\055\251\056\153\313\300\202\021\041\265\223
-\171\356\104\206\276\327\036\344\036\373
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "WoSign"
-# Issuer: CN=Certification Authority of WoSign,O=WoSign CA Limited,C=CN
-# Serial Number:5e:68:d6:11:71:94:63:50:56:00:68:f3:3e:c9:c5:91
-# Subject: CN=Certification Authority of WoSign,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Aug 08 01:00:01 2009
-# Not Valid After : Mon Aug 08 01:00:01 2039
-# Fingerprint (SHA-256): 4B:22:D5:A6:AE:C9:9F:3C:DB:79:AA:5E:C0:68:38:47:9C:D5:EC:BA:71:64:F7:F2:2D:C1:D6:5F:63:D8:57:08
-# Fingerprint (SHA1): B9:42:94:BF:91:EA:8F:B6:4B:E6:10:97:C7:FB:00:13:59:B6:76:CB
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "WoSign"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\271\102\224\277\221\352\217\266\113\346\020\227\307\373\000\023
-\131\266\166\313
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\241\362\371\265\322\310\172\164\270\363\005\361\327\341\204\215
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\125\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\052\060\050\006
-\003\125\004\003\023\041\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\040\157\146
-\040\127\157\123\151\147\156
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\136\150\326\021\161\224\143\120\126\000\150\363\076\311
-\305\221
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "WoSign China"
-#
-# Issuer: CN=CA ...............,O=WoSign CA Limited,C=CN
-# Serial Number:50:70:6b:cd:d8:13:fc:1b:4e:3b:33:72:d2:11:48:8d
-# Subject: CN=CA ...............,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Aug 08 01:00:01 2009
-# Not Valid After : Mon Aug 08 01:00:01 2039
-# Fingerprint (SHA-256): D6:F0:34:BD:94:AA:23:3F:02:97:EC:A4:24:5B:28:39:73:E4:47:AA:59:0F:31:0C:77:F4:8F:DF:83:11:22:54
-# Fingerprint (SHA1): 16:32:47:8D:89:F9:21:3A:92:00:85:63:F5:A4:A7:D3:12:40:8A:D6
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "WoSign China"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\106\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\033\060\031\006
-\003\125\004\003\014\022\103\101\040\346\262\203\351\200\232\346
-\240\271\350\257\201\344\271\246
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\106\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\033\060\031\006
-\003\125\004\003\014\022\103\101\040\346\262\203\351\200\232\346
-\240\271\350\257\201\344\271\246
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\120\160\153\315\330\023\374\033\116\073\063\162\322\021
-\110\215
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\130\060\202\003\100\240\003\002\001\002\002\020\120
-\160\153\315\330\023\374\033\116\073\063\162\322\021\110\215\060
-\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060\106
-\061\013\060\011\006\003\125\004\006\023\002\103\116\061\032\060
-\030\006\003\125\004\012\023\021\127\157\123\151\147\156\040\103
-\101\040\114\151\155\151\164\145\144\061\033\060\031\006\003\125
-\004\003\014\022\103\101\040\346\262\203\351\200\232\346\240\271
-\350\257\201\344\271\246\060\036\027\015\060\071\060\070\060\070
-\060\061\060\060\060\061\132\027\015\063\071\060\070\060\070\060
-\061\060\060\060\061\132\060\106\061\013\060\011\006\003\125\004
-\006\023\002\103\116\061\032\060\030\006\003\125\004\012\023\021
-\127\157\123\151\147\156\040\103\101\040\114\151\155\151\164\145
-\144\061\033\060\031\006\003\125\004\003\014\022\103\101\040\346
-\262\203\351\200\232\346\240\271\350\257\201\344\271\246\060\202
-\002\042\060\015\006\011\052\206\110\206\367\015\001\001\001\005
-\000\003\202\002\017\000\060\202\002\012\002\202\002\001\000\320
-\111\041\036\045\374\207\301\052\302\254\333\166\206\006\116\347
-\320\164\064\334\355\145\065\374\120\326\210\077\244\360\177\353
-\017\137\171\057\211\261\375\274\143\130\067\223\233\070\370\267
-\133\251\372\330\161\307\264\274\200\227\215\154\113\361\120\325
-\052\051\252\250\031\172\226\346\225\216\164\355\227\012\127\165
-\364\005\333\155\013\071\271\001\177\252\366\326\332\154\346\005
-\340\244\115\122\374\333\320\164\267\021\214\173\215\117\377\207
-\203\256\377\005\003\023\127\120\067\376\214\226\122\020\114\137
-\277\224\161\151\331\226\076\014\103\117\276\060\300\237\071\164
-\117\006\105\135\243\326\126\071\150\007\314\207\117\120\167\223
-\161\331\104\010\261\212\064\351\211\254\333\233\116\341\331\344
-\122\105\214\056\024\037\221\153\031\035\150\051\054\126\304\342
-\036\023\127\144\360\141\343\271\021\337\260\341\127\240\033\255
-\327\137\321\257\333\053\055\077\320\150\216\017\352\237\017\213
-\065\130\033\023\034\364\336\065\241\012\135\326\352\337\022\157
-\300\373\151\007\106\162\334\201\366\004\043\027\340\115\165\341
-\162\157\260\050\353\233\341\341\203\241\237\112\135\257\314\233
-\372\002\040\266\030\142\167\221\073\243\325\145\255\334\174\220
-\167\034\104\101\244\112\213\353\225\162\351\366\011\144\334\250
-\055\237\164\170\350\301\242\011\143\234\357\240\333\117\235\225
-\253\040\117\267\260\367\207\134\246\240\344\067\070\307\134\343
-\065\017\054\255\243\200\242\354\056\135\300\317\355\213\005\302
-\346\163\156\366\211\325\365\322\106\216\352\155\143\033\036\212
-\311\175\246\370\234\353\345\325\143\205\115\163\146\151\021\376
-\310\016\364\301\307\146\111\123\176\344\031\153\361\351\172\131
-\243\155\176\305\027\346\047\306\357\033\333\157\374\015\115\006
-\001\264\016\134\060\106\125\140\257\070\145\072\312\107\272\254
-\054\314\106\037\262\106\226\077\363\355\046\005\356\167\241\152
-\153\176\055\155\130\134\112\324\216\147\270\361\332\325\106\212
-\047\371\021\362\311\102\376\116\336\337\037\134\304\244\206\207
-\026\063\241\247\027\030\245\015\344\005\345\053\302\053\013\242
-\225\220\271\375\140\074\116\211\076\347\234\356\037\273\001\002
-\003\001\000\001\243\102\060\100\060\016\006\003\125\035\017\001
-\001\377\004\004\003\002\001\006\060\017\006\003\125\035\023\001
-\001\377\004\005\060\003\001\001\377\060\035\006\003\125\035\016
-\004\026\004\024\340\115\277\334\233\101\135\023\350\144\360\247
-\351\025\244\341\201\301\272\061\060\015\006\011\052\206\110\206
-\367\015\001\001\013\005\000\003\202\002\001\000\152\212\160\070
-\131\266\332\213\030\310\276\052\323\266\031\325\146\051\172\135
-\315\133\057\163\034\046\116\243\175\157\253\267\051\115\246\351
-\245\021\203\247\071\163\257\020\104\222\346\045\135\117\141\372
-\310\006\276\116\113\357\376\363\061\376\306\174\160\012\101\130
-\332\350\231\113\226\311\170\274\230\174\002\051\355\011\200\346
-\012\072\202\002\052\342\311\057\310\126\031\046\356\170\034\043
-\375\367\223\145\116\347\363\230\230\257\315\335\331\236\100\210
-\061\050\072\253\056\013\260\254\014\044\372\172\046\230\363\022
-\141\020\364\135\027\367\176\342\170\227\124\342\214\350\051\272
-\214\020\062\275\335\063\153\070\206\176\071\075\016\003\162\247
-\135\171\217\105\212\131\256\133\041\156\061\106\325\131\215\317
-\025\137\335\061\045\317\333\140\326\201\104\162\051\002\127\366
-\226\324\326\377\352\051\333\071\305\270\054\212\032\215\316\313
-\347\102\061\206\005\150\016\236\024\335\000\220\272\151\105\010
-\333\156\220\201\206\247\052\005\077\346\204\071\370\267\371\127
-\137\114\244\171\132\020\014\136\325\153\377\065\137\005\121\036
-\154\243\165\251\317\120\203\323\174\364\146\367\202\215\075\014
-\175\350\337\173\250\016\033\054\234\256\100\160\207\332\355\247
-\026\202\132\276\065\154\040\116\042\141\331\274\121\172\315\172
-\141\334\113\021\371\376\147\064\317\056\004\146\141\134\127\227
-\043\214\363\206\033\110\337\052\257\247\301\377\330\216\076\003
-\273\330\052\260\372\024\045\262\121\153\206\103\205\056\007\043
-\026\200\215\114\373\264\143\073\314\303\164\355\033\243\036\376
-\065\017\137\174\035\026\206\365\016\303\225\361\057\257\135\045
-\073\121\346\327\166\101\070\321\113\003\071\050\245\036\221\162
-\324\175\253\227\063\304\323\076\340\151\266\050\171\240\011\215
-\034\321\377\101\162\110\006\374\232\056\347\040\371\233\242\336
-\211\355\256\074\011\257\312\127\263\222\211\160\100\344\057\117
-\302\160\203\100\327\044\054\153\347\011\037\323\325\307\301\010
-\364\333\016\073\034\007\013\103\021\204\041\206\351\200\324\165
-\330\253\361\002\142\301\261\176\125\141\317\023\327\046\260\327
-\234\313\051\213\070\112\013\016\220\215\272\241
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "WoSign China"
-# Issuer: CN=CA ...............,O=WoSign CA Limited,C=CN
-# Serial Number:50:70:6b:cd:d8:13:fc:1b:4e:3b:33:72:d2:11:48:8d
-# Subject: CN=CA ...............,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Aug 08 01:00:01 2009
-# Not Valid After : Mon Aug 08 01:00:01 2039
-# Fingerprint (SHA-256): D6:F0:34:BD:94:AA:23:3F:02:97:EC:A4:24:5B:28:39:73:E4:47:AA:59:0F:31:0C:77:F4:8F:DF:83:11:22:54
-# Fingerprint (SHA1): 16:32:47:8D:89:F9:21:3A:92:00:85:63:F5:A4:A7:D3:12:40:8A:D6
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "WoSign China"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\026\062\107\215\211\371\041\072\222\000\205\143\365\244\247\323
-\022\100\212\326
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\170\203\133\122\026\166\304\044\073\203\170\350\254\332\232\223
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\106\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\033\060\031\006
-\003\125\004\003\014\022\103\101\040\346\262\203\351\200\232\346
-\240\271\350\257\201\344\271\246
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\120\160\153\315\330\023\374\033\116\073\063\162\322\021
-\110\215
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "COMODO RSA Certification Authority"
#
# Issuer: CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
@@ -22358,188 +17231,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "VeriSign-C3SSA-G2-temporary-intermediate-after-1024bit-removal"
-#
-# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU="(c) 2006 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
-# Serial Number:2f:00:6e:cd:17:70:66:e7:5f:a3:82:0a:79:1f:05:ae
-# Subject: CN=VeriSign Class 3 Secure Server CA - G2,OU=Terms of use at https://www.verisign.com/rpa (c)09,OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
-# Not Valid Before: Thu Mar 26 00:00:00 2009
-# Not Valid After : Sun Mar 24 23:59:59 2019
-# Fingerprint (SHA-256): 0A:41:51:D5:E5:8B:84:B8:AC:E5:3A:5C:12:12:2A:C9:59:CD:69:91:FB:B3:8E:99:B5:76:C0:AB:DA:C3:58:14
-# Fingerprint (SHA1): 76:44:59:78:1B:AC:B0:47:63:A5:D0:A1:58:91:65:26:1F:29:8E:3B
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "VeriSign-C3SSA-G2-temporary-intermediate-after-1024bit-removal"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\201\265\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123
-\151\147\156\054\040\111\156\143\056\061\037\060\035\006\003\125
-\004\013\023\026\126\145\162\151\123\151\147\156\040\124\162\165
-\163\164\040\116\145\164\167\157\162\153\061\073\060\071\006\003
-\125\004\013\023\062\124\145\162\155\163\040\157\146\040\165\163
-\145\040\141\164\040\150\164\164\160\163\072\057\057\167\167\167
-\056\166\145\162\151\163\151\147\156\056\143\157\155\057\162\160
-\141\040\050\143\051\060\071\061\057\060\055\006\003\125\004\003
-\023\046\126\145\162\151\123\151\147\156\040\103\154\141\163\163
-\040\063\040\123\145\143\165\162\145\040\123\145\162\166\145\162
-\040\103\101\040\055\040\107\062
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\312\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123
-\151\147\156\054\040\111\156\143\056\061\037\060\035\006\003\125
-\004\013\023\026\126\145\162\151\123\151\147\156\040\124\162\165
-\163\164\040\116\145\164\167\157\162\153\061\072\060\070\006\003
-\125\004\013\023\061\050\143\051\040\062\060\060\066\040\126\145
-\162\151\123\151\147\156\054\040\111\156\143\056\040\055\040\106
-\157\162\040\141\165\164\150\157\162\151\172\145\144\040\165\163
-\145\040\157\156\154\171\061\105\060\103\006\003\125\004\003\023
-\074\126\145\162\151\123\151\147\156\040\103\154\141\163\163\040
-\063\040\120\165\142\154\151\143\040\120\162\151\155\141\162\171
-\040\103\145\162\164\151\146\151\143\141\164\151\157\156\040\101
-\165\164\150\157\162\151\164\171\040\055\040\107\065
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\057\000\156\315\027\160\146\347\137\243\202\012\171\037
-\005\256
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\005\071\060\202\004\041\240\003\002\001\002\002\020\057
-\000\156\315\027\160\146\347\137\243\202\012\171\037\005\256\060
-\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\201
-\312\061\013\060\011\006\003\125\004\006\023\002\125\123\061\027
-\060\025\006\003\125\004\012\023\016\126\145\162\151\123\151\147
-\156\054\040\111\156\143\056\061\037\060\035\006\003\125\004\013
-\023\026\126\145\162\151\123\151\147\156\040\124\162\165\163\164
-\040\116\145\164\167\157\162\153\061\072\060\070\006\003\125\004
-\013\023\061\050\143\051\040\062\060\060\066\040\126\145\162\151
-\123\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162
-\040\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040
-\157\156\154\171\061\105\060\103\006\003\125\004\003\023\074\126
-\145\162\151\123\151\147\156\040\103\154\141\163\163\040\063\040
-\120\165\142\154\151\143\040\120\162\151\155\141\162\171\040\103
-\145\162\164\151\146\151\143\141\164\151\157\156\040\101\165\164
-\150\157\162\151\164\171\040\055\040\107\065\060\036\027\015\060
-\071\060\063\062\066\060\060\060\060\060\060\132\027\015\061\071
-\060\063\062\064\062\063\065\071\065\071\132\060\201\265\061\013
-\060\011\006\003\125\004\006\023\002\125\123\061\027\060\025\006
-\003\125\004\012\023\016\126\145\162\151\123\151\147\156\054\040
-\111\156\143\056\061\037\060\035\006\003\125\004\013\023\026\126
-\145\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145
-\164\167\157\162\153\061\073\060\071\006\003\125\004\013\023\062
-\124\145\162\155\163\040\157\146\040\165\163\145\040\141\164\040
-\150\164\164\160\163\072\057\057\167\167\167\056\166\145\162\151
-\163\151\147\156\056\143\157\155\057\162\160\141\040\050\143\051
-\060\071\061\057\060\055\006\003\125\004\003\023\046\126\145\162
-\151\123\151\147\156\040\103\154\141\163\163\040\063\040\123\145
-\143\165\162\145\040\123\145\162\166\145\162\040\103\101\040\055
-\040\107\062\060\202\001\042\060\015\006\011\052\206\110\206\367
-\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002
-\202\001\001\000\324\126\217\127\073\067\050\246\100\143\322\225
-\325\005\164\332\265\031\152\226\326\161\127\057\342\300\064\214
-\240\225\263\214\341\067\044\363\056\355\103\105\005\216\211\327
-\372\332\112\265\370\076\215\116\307\371\111\120\105\067\100\237
-\164\252\240\121\125\141\361\140\204\211\245\236\200\215\057\260
-\041\252\105\202\304\317\264\024\177\107\025\040\050\202\260\150
-\022\300\256\134\007\327\366\131\314\313\142\126\134\115\111\377
-\046\210\253\124\121\072\057\112\332\016\230\342\211\162\271\374
-\367\150\074\304\037\071\172\313\027\201\363\014\255\017\334\141
-\142\033\020\013\004\036\051\030\161\136\142\313\103\336\276\061
-\272\161\002\031\116\046\251\121\332\214\144\151\003\336\234\375
-\175\375\173\141\274\374\204\174\210\134\264\303\173\355\137\053
-\106\022\361\375\000\001\232\213\133\351\243\005\056\217\056\133
-\336\363\033\170\370\146\221\010\300\136\316\325\260\066\312\324
-\250\173\240\175\371\060\172\277\370\335\031\121\053\040\272\376
-\247\317\241\116\260\147\365\200\252\053\203\056\322\216\124\211
-\216\036\051\013\002\003\001\000\001\243\202\001\054\060\202\001
-\050\060\022\006\003\125\035\023\001\001\377\004\010\060\006\001
-\001\377\002\001\000\060\016\006\003\125\035\017\001\001\377\004
-\004\003\002\001\006\060\051\006\003\125\035\021\004\042\060\040
-\244\036\060\034\061\032\060\030\006\003\125\004\003\023\021\103
-\154\141\163\163\063\103\101\062\060\064\070\055\061\055\065\062
-\060\035\006\003\125\035\016\004\026\004\024\245\357\013\021\316
-\300\101\003\243\112\145\220\110\262\034\340\127\055\175\107\060
-\146\006\003\125\035\040\004\137\060\135\060\133\006\013\140\206
-\110\001\206\370\105\001\007\027\003\060\114\060\043\006\010\053
-\006\001\005\005\007\002\001\026\027\150\164\164\160\163\072\057
-\057\144\056\163\171\155\143\142\056\143\157\155\057\143\160\163
-\060\045\006\010\053\006\001\005\005\007\002\002\060\031\032\027
-\150\164\164\160\163\072\057\057\144\056\163\171\155\143\142\056
-\143\157\155\057\162\160\141\060\057\006\003\125\035\037\004\050
-\060\046\060\044\240\042\240\040\206\036\150\164\164\160\072\057
-\057\163\056\163\171\155\143\142\056\143\157\155\057\160\143\141
-\063\055\147\065\056\143\162\154\060\037\006\003\125\035\043\004
-\030\060\026\200\024\177\323\145\247\302\335\354\273\360\060\011
-\363\103\071\372\002\257\063\061\063\060\015\006\011\052\206\110
-\206\367\015\001\001\005\005\000\003\202\001\001\000\053\216\024
-\314\354\206\010\140\067\213\154\145\211\045\041\336\057\122\242
-\007\236\130\323\263\026\170\001\231\121\225\264\023\167\314\167
-\335\013\134\201\067\326\276\366\142\326\004\067\013\030\163\232
-\323\366\301\242\036\155\234\273\214\021\346\076\022\136\007\137
-\013\203\134\164\002\340\120\364\261\046\033\155\306\350\351\277
-\115\271\001\025\031\354\120\232\371\021\360\201\130\103\054\115
-\021\100\263\132\106\010\246\136\163\241\210\022\065\214\377\003
-\072\275\326\235\372\347\334\226\271\032\144\076\304\375\331\012
-\266\145\236\272\245\250\130\374\073\042\360\242\127\356\212\127
-\107\234\167\307\045\341\254\064\005\115\363\202\176\101\043\272
-\264\127\363\347\306\001\145\327\115\211\231\034\151\115\136\170
-\366\353\162\161\075\262\304\225\001\237\135\014\267\057\045\246
-\134\171\101\357\236\304\147\074\241\235\177\161\072\320\225\227
-\354\170\102\164\230\156\276\076\150\114\127\074\250\223\101\207
-\013\344\271\257\221\373\120\114\014\272\300\044\047\321\025\333
-\145\110\041\012\057\327\334\176\240\314\145\176\171
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "VeriSign-C3SSA-G2-temporary-intermediate-after-1024bit-removal"
-# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU="(c) 2006 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
-# Serial Number:2f:00:6e:cd:17:70:66:e7:5f:a3:82:0a:79:1f:05:ae
-# Subject: CN=VeriSign Class 3 Secure Server CA - G2,OU=Terms of use at https://www.verisign.com/rpa (c)09,OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
-# Not Valid Before: Thu Mar 26 00:00:00 2009
-# Not Valid After : Sun Mar 24 23:59:59 2019
-# Fingerprint (SHA-256): 0A:41:51:D5:E5:8B:84:B8:AC:E5:3A:5C:12:12:2A:C9:59:CD:69:91:FB:B3:8E:99:B5:76:C0:AB:DA:C3:58:14
-# Fingerprint (SHA1): 76:44:59:78:1B:AC:B0:47:63:A5:D0:A1:58:91:65:26:1F:29:8E:3B
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "VeriSign-C3SSA-G2-temporary-intermediate-after-1024bit-removal"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\166\104\131\170\033\254\260\107\143\245\320\241\130\221\145\046
-\037\051\216\073
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\277\022\155\372\174\325\133\046\171\072\215\252\021\357\057\134
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\201\312\061\013\060\011\006\003\125\004\006\023\002\125\123
-\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123
-\151\147\156\054\040\111\156\143\056\061\037\060\035\006\003\125
-\004\013\023\026\126\145\162\151\123\151\147\156\040\124\162\165
-\163\164\040\116\145\164\167\157\162\153\061\072\060\070\006\003
-\125\004\013\023\061\050\143\051\040\062\060\060\066\040\126\145
-\162\151\123\151\147\156\054\040\111\156\143\056\040\055\040\106
-\157\162\040\141\165\164\150\157\162\151\172\145\144\040\165\163
-\145\040\157\156\154\171\061\105\060\103\006\003\125\004\003\023
-\074\126\145\162\151\123\151\147\156\040\103\154\141\163\163\040
-\063\040\120\165\142\154\151\143\040\120\162\151\155\141\162\171
-\040\103\145\162\164\151\146\151\143\141\164\151\157\156\040\101
-\165\164\150\157\162\151\164\171\040\055\040\107\065
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\057\000\156\315\027\160\146\347\137\243\202\012\171\037
-\005\256
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "Staat der Nederlanden Root CA - G3"
#
# Issuer: CN=Staat der Nederlanden Root CA - G3,O=Staat der Nederlanden,C=NL
@@ -23818,149 +18509,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "Explicitly Distrusted MCSHOLDING CA"
-#
-# Issuer: CN=CNNIC ROOT,O=CNNIC,C=CN
-# Serial Number: 1228079246 (0x4933008e)
-# Subject: CN=MCSHOLDING TEST,O=MCSHOLDING,C=EG
-# Not Valid Before: Thu Mar 19 06:20:09 2015
-# Not Valid After : Fri Apr 03 06:20:09 2015
-# Fingerprint (SHA-256): 27:40:D9:56:B1:12:7B:79:1A:A1:B3:CC:64:4A:4D:BE:DB:A7:61:86:A2:36:38:B9:51:02:35:1A:83:4E:A8:61
-# Fingerprint (SHA1): E1:F3:59:1E:76:98:65:C4:E4:47:AC:C3:7E:AF:C9:E2:BF:E4:C5:76
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted MCSHOLDING CA"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\074\061\013\060\011\006\003\125\004\006\023\002\105\107\061
-\023\060\021\006\003\125\004\012\014\012\115\103\123\110\117\114
-\104\111\116\107\061\030\060\026\006\003\125\004\003\014\017\115
-\103\123\110\117\114\104\111\116\107\040\124\105\123\124
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\062\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\016\060\014\006\003\125\004\012\023\005\103\116\116\111\103\061
-\023\060\021\006\003\125\004\003\023\012\103\116\116\111\103\040
-\122\117\117\124
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\111\063\000\216
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\004\222\060\202\003\172\240\003\002\001\002\002\004\111
-\063\000\216\060\015\006\011\052\206\110\206\367\015\001\001\013
-\005\000\060\062\061\013\060\011\006\003\125\004\006\023\002\103
-\116\061\016\060\014\006\003\125\004\012\023\005\103\116\116\111
-\103\061\023\060\021\006\003\125\004\003\023\012\103\116\116\111
-\103\040\122\117\117\124\060\036\027\015\061\065\060\063\061\071
-\060\066\062\060\060\071\132\027\015\061\065\060\064\060\063\060
-\066\062\060\060\071\132\060\074\061\013\060\011\006\003\125\004
-\006\023\002\105\107\061\023\060\021\006\003\125\004\012\014\012
-\115\103\123\110\117\114\104\111\116\107\061\030\060\026\006\003
-\125\004\003\014\017\115\103\123\110\117\114\104\111\116\107\040
-\124\105\123\124\060\202\001\042\060\015\006\011\052\206\110\206
-\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012
-\002\202\001\001\000\245\371\165\014\006\256\356\014\021\315\226
-\063\115\153\316\300\112\014\075\135\353\322\113\011\177\347\107
-\054\254\161\000\371\010\257\064\361\243\152\307\374\346\253\316
-\320\276\312\315\052\230\230\271\320\216\063\111\007\141\040\321
-\132\064\316\203\024\006\171\216\032\277\333\344\240\070\072\356
-\224\271\243\240\130\072\211\024\254\140\076\003\324\307\315\073
-\034\260\232\210\032\111\020\251\260\262\375\345\350\341\004\342
-\352\202\155\376\014\121\105\221\255\165\042\256\377\117\220\013
-\300\123\145\167\076\036\302\126\265\066\306\326\205\314\016\203
-\032\063\037\166\231\133\053\227\053\213\327\321\024\025\114\235
-\131\327\200\057\244\242\205\325\210\066\002\140\125\312\130\337
-\223\374\112\142\007\226\323\304\372\277\215\001\047\227\057\246
-\134\164\361\072\102\156\135\171\024\060\061\032\074\331\262\127
-\115\340\270\077\017\151\061\242\235\145\231\331\326\061\207\265
-\230\046\337\360\313\273\025\300\044\023\142\122\032\153\313\105
-\007\227\343\304\224\136\311\015\107\054\351\317\351\364\217\376
-\065\341\062\347\061\002\003\001\000\001\243\202\001\244\060\202
-\001\240\060\166\006\010\053\006\001\005\005\007\001\001\004\152
-\060\150\060\051\006\010\053\006\001\005\005\007\060\001\206\035
-\150\164\164\160\072\057\057\157\143\163\160\143\156\156\151\143
-\162\157\157\164\056\143\156\156\151\143\056\143\156\060\073\006
-\010\053\006\001\005\005\007\060\002\206\057\150\164\164\160\072
-\057\057\167\167\167\056\143\156\156\151\143\056\143\156\057\144
-\157\167\156\154\157\141\144\057\143\145\162\164\057\103\116\116
-\111\103\122\117\117\124\056\143\145\162\060\037\006\003\125\035
-\043\004\030\060\026\200\024\145\362\061\255\052\367\367\335\122
-\226\012\307\002\301\016\357\246\325\073\021\060\017\006\003\125
-\035\023\001\001\377\004\005\060\003\001\001\377\060\077\006\003
-\125\035\040\004\070\060\066\060\064\006\012\053\006\001\004\001
-\201\351\014\001\006\060\046\060\044\006\010\053\006\001\005\005
-\007\002\001\026\030\150\164\164\160\072\057\057\167\167\167\056
-\143\156\156\151\143\056\143\156\057\143\160\163\057\060\201\206
-\006\003\125\035\037\004\177\060\175\060\102\240\100\240\076\244
-\074\060\072\061\013\060\011\006\003\125\004\006\023\002\103\116
-\061\016\060\014\006\003\125\004\012\014\005\103\116\116\111\103
-\061\014\060\012\006\003\125\004\013\014\003\143\162\154\061\015
-\060\013\006\003\125\004\003\014\004\143\162\154\061\060\067\240
-\065\240\063\206\061\150\164\164\160\072\057\057\143\162\154\056
-\143\156\156\151\143\056\143\156\057\144\157\167\156\154\157\141
-\144\057\162\157\157\164\163\150\141\062\143\162\154\057\103\122
-\114\061\056\143\162\154\060\013\006\003\125\035\017\004\004\003
-\002\001\006\060\035\006\003\125\035\016\004\026\004\024\104\244
-\211\253\024\137\075\157\040\074\252\174\372\031\256\364\110\140
-\005\265\060\015\006\011\052\206\110\206\367\015\001\001\013\005
-\000\003\202\001\001\000\134\264\365\123\233\117\271\340\204\211
-\061\276\236\056\352\236\041\113\245\217\155\241\246\363\057\110
-\353\351\333\255\036\061\200\320\171\073\020\357\232\044\367\223
-\033\065\363\032\302\307\302\054\012\177\157\133\361\137\163\221
-\004\373\015\171\015\351\032\006\326\203\375\116\140\235\154\222
-\103\114\352\144\230\104\253\327\373\107\320\257\037\144\114\342
-\335\167\150\026\302\054\241\240\201\227\000\102\037\176\040\170
-\350\306\120\035\013\177\025\223\131\130\100\024\204\360\247\220
-\153\066\005\147\352\177\042\155\273\321\245\046\115\263\060\244
-\130\324\133\265\032\214\120\214\270\015\341\240\007\263\017\130
-\316\327\005\265\175\065\171\157\242\333\014\000\052\150\044\214
-\176\234\301\166\111\272\174\146\021\336\362\107\316\376\320\316
-\125\276\010\332\362\171\046\052\025\071\316\153\030\246\337\330
-\207\050\231\224\016\055\150\241\232\316\122\066\234\053\354\264
-\150\263\154\025\254\313\160\102\362\304\101\245\310\374\041\170
-\123\167\062\040\251\041\114\162\342\323\262\311\166\033\030\130
-\102\013\102\222\263\344
-END
-
-# Distrust "Explicitly Distrusted MCSHOLDING CA"
-# Issuer: CN=CNNIC ROOT,O=CNNIC,C=CN
-# Serial Number: 1228079246 (0x4933008e)
-# Subject: CN=MCSHOLDING TEST,O=MCSHOLDING,C=EG
-# Not Valid Before: Thu Mar 19 06:20:09 2015
-# Not Valid After : Fri Apr 03 06:20:09 2015
-# Fingerprint (SHA-256): 27:40:D9:56:B1:12:7B:79:1A:A1:B3:CC:64:4A:4D:BE:DB:A7:61:86:A2:36:38:B9:51:02:35:1A:83:4E:A8:61
-# Fingerprint (SHA1): E1:F3:59:1E:76:98:65:C4:E4:47:AC:C3:7E:AF:C9:E2:BF:E4:C5:76
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Explicitly Distrusted MCSHOLDING CA"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\341\363\131\036\166\230\145\304\344\107\254\303\176\257\311\342
-\277\344\305\166
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\366\212\253\024\076\326\060\045\267\111\015\167\205\160\231\313
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\062\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\016\060\014\006\003\125\004\012\023\005\103\116\116\111\103\061
-\023\060\021\006\003\125\004\003\023\012\103\116\116\111\103\040
-\122\117\117\124
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\004\111\063\000\216
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5"
#
# Issuer: CN=T..RKTRUST Elektronik Sertifika Hizmet Sa..lay..c..s.. H5,O=T..RKTRUST Bilgi ..leti..im ve Bili..im G..venli..i Hizmetleri A....,L=Ankara,C=TR
@@ -24429,248 +18977,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
#
-# Certificate "Certification Authority of WoSign G2"
-#
-# Issuer: CN=Certification Authority of WoSign G2,O=WoSign CA Limited,C=CN
-# Serial Number:6b:25:da:8a:88:9d:7c:bc:0f:05:b3:b1:7a:61:45:44
-# Subject: CN=Certification Authority of WoSign G2,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Nov 08 00:58:58 2014
-# Not Valid After : Tue Nov 08 00:58:58 2044
-# Fingerprint (SHA-256): D4:87:A5:6F:83:B0:74:82:E8:5E:96:33:94:C1:EC:C2:C9:E5:1D:09:03:EE:94:6B:02:C3:01:58:1E:D9:9E:16
-# Fingerprint (SHA1): FB:ED:DC:90:65:B7:27:20:37:BC:55:0C:9C:56:DE:BB:F2:78:94:E1
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Certification Authority of WoSign G2"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\130\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\055\060\053\006
-\003\125\004\003\023\044\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\040\157\146
-\040\127\157\123\151\147\156\040\107\062
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\130\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\055\060\053\006
-\003\125\004\003\023\044\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\040\157\146
-\040\127\157\123\151\147\156\040\107\062
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\153\045\332\212\210\235\174\274\017\005\263\261\172\141
-\105\104
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\003\174\060\202\002\144\240\003\002\001\002\002\020\153
-\045\332\212\210\235\174\274\017\005\263\261\172\141\105\104\060
-\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060\130
-\061\013\060\011\006\003\125\004\006\023\002\103\116\061\032\060
-\030\006\003\125\004\012\023\021\127\157\123\151\147\156\040\103
-\101\040\114\151\155\151\164\145\144\061\055\060\053\006\003\125
-\004\003\023\044\103\145\162\164\151\146\151\143\141\164\151\157
-\156\040\101\165\164\150\157\162\151\164\171\040\157\146\040\127
-\157\123\151\147\156\040\107\062\060\036\027\015\061\064\061\061
-\060\070\060\060\065\070\065\070\132\027\015\064\064\061\061\060
-\070\060\060\065\070\065\070\132\060\130\061\013\060\011\006\003
-\125\004\006\023\002\103\116\061\032\060\030\006\003\125\004\012
-\023\021\127\157\123\151\147\156\040\103\101\040\114\151\155\151
-\164\145\144\061\055\060\053\006\003\125\004\003\023\044\103\145
-\162\164\151\146\151\143\141\164\151\157\156\040\101\165\164\150
-\157\162\151\164\171\040\157\146\040\127\157\123\151\147\156\040
-\107\062\060\202\001\042\060\015\006\011\052\206\110\206\367\015
-\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002\202
-\001\001\000\276\305\304\240\042\200\111\117\277\331\207\021\306
-\123\341\273\017\275\140\177\257\366\202\016\037\334\260\216\075
-\227\340\120\074\217\072\357\146\073\105\007\233\040\370\343\327
-\045\206\065\220\026\242\135\157\060\031\010\207\013\177\006\262
-\235\142\217\336\257\222\245\140\324\053\200\232\122\077\365\232
-\203\351\064\132\313\331\325\142\134\346\016\340\337\006\230\016
-\200\174\312\264\035\023\210\153\016\250\044\167\003\320\356\133
-\363\312\151\221\065\071\126\305\155\343\367\075\117\136\223\070
-\044\312\030\351\044\313\222\003\335\314\034\075\011\160\344\040
-\344\361\256\254\273\163\151\243\143\072\017\105\017\241\112\232
-\302\321\143\254\313\020\370\075\346\116\050\267\353\304\225\261
-\254\375\136\253\372\101\313\135\235\113\334\364\174\166\357\147
-\177\000\172\215\322\240\032\134\115\042\341\265\332\335\166\263
-\324\166\337\136\270\213\230\310\024\124\314\153\027\222\267\340
-\112\277\111\224\141\013\070\220\217\135\044\154\045\173\073\171
-\331\342\176\235\255\237\230\241\006\374\170\024\140\127\370\356
-\200\167\261\002\003\001\000\001\243\102\060\100\060\016\006\003
-\125\035\017\001\001\377\004\004\003\002\001\006\060\017\006\003
-\125\035\023\001\001\377\004\005\060\003\001\001\377\060\035\006
-\003\125\035\016\004\026\004\024\372\140\251\353\145\305\335\026
-\024\010\116\014\017\215\233\340\367\144\257\147\060\015\006\011
-\052\206\110\206\367\015\001\001\013\005\000\003\202\001\001\000
-\127\303\172\066\202\234\215\230\342\253\100\252\107\217\307\247
-\133\355\174\347\075\146\132\073\061\273\337\363\026\063\221\374
-\174\173\245\302\246\146\343\252\260\267\047\230\077\111\327\140
-\147\147\077\066\117\112\313\361\024\372\132\207\050\034\355\217
-\101\062\306\225\371\175\332\275\173\133\302\260\041\343\217\106
-\334\041\070\103\164\114\373\060\370\027\162\301\062\374\310\221
-\027\304\314\130\067\116\013\314\132\367\041\065\050\203\154\140
-\055\104\353\122\214\120\075\265\154\022\327\372\011\273\154\262
-\112\261\305\211\344\374\323\122\330\141\027\376\172\224\204\217
-\171\266\063\131\272\017\304\013\342\160\240\113\170\056\372\310
-\237\375\257\221\145\012\170\070\025\345\227\027\024\335\371\340
-\054\064\370\070\320\204\042\000\300\024\121\030\053\002\334\060
-\132\360\350\001\174\065\072\043\257\010\344\257\252\216\050\102
-\111\056\360\365\231\064\276\355\017\113\030\341\322\044\074\273
-\135\107\267\041\362\215\321\012\231\216\343\156\076\255\160\340
-\217\271\312\314\156\201\061\366\173\234\172\171\344\147\161\030
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "Certification Authority of WoSign G2"
-# Issuer: CN=Certification Authority of WoSign G2,O=WoSign CA Limited,C=CN
-# Serial Number:6b:25:da:8a:88:9d:7c:bc:0f:05:b3:b1:7a:61:45:44
-# Subject: CN=Certification Authority of WoSign G2,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Nov 08 00:58:58 2014
-# Not Valid After : Tue Nov 08 00:58:58 2044
-# Fingerprint (SHA-256): D4:87:A5:6F:83:B0:74:82:E8:5E:96:33:94:C1:EC:C2:C9:E5:1D:09:03:EE:94:6B:02:C3:01:58:1E:D9:9E:16
-# Fingerprint (SHA1): FB:ED:DC:90:65:B7:27:20:37:BC:55:0C:9C:56:DE:BB:F2:78:94:E1
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "Certification Authority of WoSign G2"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\373\355\334\220\145\267\047\040\067\274\125\014\234\126\336\273
-\362\170\224\341
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\310\034\175\031\252\313\161\223\362\120\370\122\250\036\272\140
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\130\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\055\060\053\006
-\003\125\004\003\023\044\103\145\162\164\151\146\151\143\141\164
-\151\157\156\040\101\165\164\150\157\162\151\164\171\040\157\146
-\040\127\157\123\151\147\156\040\107\062
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\153\045\332\212\210\235\174\274\017\005\263\261\172\141
-\105\104
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
-# Certificate "CA WoSign ECC Root"
-#
-# Issuer: CN=CA WoSign ECC Root,O=WoSign CA Limited,C=CN
-# Serial Number:68:4a:58:70:80:6b:f0:8f:02:fa:f6:de:e8:b0:90:90
-# Subject: CN=CA WoSign ECC Root,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Nov 08 00:58:58 2014
-# Not Valid After : Tue Nov 08 00:58:58 2044
-# Fingerprint (SHA-256): 8B:45:DA:1C:06:F7:91:EB:0C:AB:F2:6B:E5:88:F5:FB:23:16:5C:2E:61:4B:F8:85:56:2D:0D:CE:50:B2:9B:02
-# Fingerprint (SHA1): D2:7A:D2:BE:ED:94:C0:A1:3C:C7:25:21:EA:5D:71:BE:81:19:F3:2B
-CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "CA WoSign ECC Root"
-CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
-CKA_SUBJECT MULTILINE_OCTAL
-\060\106\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\033\060\031\006
-\003\125\004\003\023\022\103\101\040\127\157\123\151\147\156\040
-\105\103\103\040\122\157\157\164
-END
-CKA_ID UTF8 "0"
-CKA_ISSUER MULTILINE_OCTAL
-\060\106\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\033\060\031\006
-\003\125\004\003\023\022\103\101\040\127\157\123\151\147\156\040
-\105\103\103\040\122\157\157\164
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\150\112\130\160\200\153\360\217\002\372\366\336\350\260
-\220\220
-END
-CKA_VALUE MULTILINE_OCTAL
-\060\202\002\011\060\202\001\217\240\003\002\001\002\002\020\150
-\112\130\160\200\153\360\217\002\372\366\336\350\260\220\220\060
-\012\006\010\052\206\110\316\075\004\003\003\060\106\061\013\060
-\011\006\003\125\004\006\023\002\103\116\061\032\060\030\006\003
-\125\004\012\023\021\127\157\123\151\147\156\040\103\101\040\114
-\151\155\151\164\145\144\061\033\060\031\006\003\125\004\003\023
-\022\103\101\040\127\157\123\151\147\156\040\105\103\103\040\122
-\157\157\164\060\036\027\015\061\064\061\061\060\070\060\060\065
-\070\065\070\132\027\015\064\064\061\061\060\070\060\060\065\070
-\065\070\132\060\106\061\013\060\011\006\003\125\004\006\023\002
-\103\116\061\032\060\030\006\003\125\004\012\023\021\127\157\123
-\151\147\156\040\103\101\040\114\151\155\151\164\145\144\061\033
-\060\031\006\003\125\004\003\023\022\103\101\040\127\157\123\151
-\147\156\040\105\103\103\040\122\157\157\164\060\166\060\020\006
-\007\052\206\110\316\075\002\001\006\005\053\201\004\000\042\003
-\142\000\004\341\375\216\270\103\044\253\226\173\205\302\272\013
-\255\215\340\072\343\044\271\322\261\276\210\072\312\277\112\270
-\371\357\054\057\257\121\120\074\107\165\154\370\224\267\233\374
-\050\036\305\124\314\143\235\026\113\123\301\347\040\253\315\254
-\045\322\177\217\302\301\132\202\136\060\213\172\124\316\003\265
-\221\177\252\224\320\321\212\110\314\202\005\046\241\325\121\022
-\326\173\066\243\102\060\100\060\016\006\003\125\035\017\001\001
-\377\004\004\003\002\001\006\060\017\006\003\125\035\023\001\001
-\377\004\005\060\003\001\001\377\060\035\006\003\125\035\016\004
-\026\004\024\252\375\325\132\243\366\207\213\062\205\375\321\062
-\133\200\105\223\363\003\270\060\012\006\010\052\206\110\316\075
-\004\003\003\003\150\000\060\145\002\061\000\344\244\204\260\201
-\325\075\260\164\254\224\244\350\016\075\000\164\114\241\227\153
-\371\015\121\074\241\331\073\364\015\253\251\237\276\116\162\312
-\205\324\331\354\265\062\105\030\157\253\255\002\060\175\307\367
-\151\143\057\241\341\230\357\023\020\321\171\077\321\376\352\073
-\177\336\126\364\220\261\025\021\330\262\042\025\320\057\303\046
-\056\153\361\221\262\220\145\364\232\346\220\356\112
-END
-CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
-
-# Trust for "CA WoSign ECC Root"
-# Issuer: CN=CA WoSign ECC Root,O=WoSign CA Limited,C=CN
-# Serial Number:68:4a:58:70:80:6b:f0:8f:02:fa:f6:de:e8:b0:90:90
-# Subject: CN=CA WoSign ECC Root,O=WoSign CA Limited,C=CN
-# Not Valid Before: Sat Nov 08 00:58:58 2014
-# Not Valid After : Tue Nov 08 00:58:58 2044
-# Fingerprint (SHA-256): 8B:45:DA:1C:06:F7:91:EB:0C:AB:F2:6B:E5:88:F5:FB:23:16:5C:2E:61:4B:F8:85:56:2D:0D:CE:50:B2:9B:02
-# Fingerprint (SHA1): D2:7A:D2:BE:ED:94:C0:A1:3C:C7:25:21:EA:5D:71:BE:81:19:F3:2B
-CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
-CKA_TOKEN CK_BBOOL CK_TRUE
-CKA_PRIVATE CK_BBOOL CK_FALSE
-CKA_MODIFIABLE CK_BBOOL CK_FALSE
-CKA_LABEL UTF8 "CA WoSign ECC Root"
-CKA_CERT_SHA1_HASH MULTILINE_OCTAL
-\322\172\322\276\355\224\300\241\074\307\045\041\352\135\161\276
-\201\031\363\053
-END
-CKA_CERT_MD5_HASH MULTILINE_OCTAL
-\200\306\123\356\141\202\050\162\360\377\041\271\027\312\262\040
-END
-CKA_ISSUER MULTILINE_OCTAL
-\060\106\061\013\060\011\006\003\125\004\006\023\002\103\116\061
-\032\060\030\006\003\125\004\012\023\021\127\157\123\151\147\156
-\040\103\101\040\114\151\155\151\164\145\144\061\033\060\031\006
-\003\125\004\003\023\022\103\101\040\127\157\123\151\147\156\040
-\105\103\103\040\122\157\157\164
-END
-CKA_SERIAL_NUMBER MULTILINE_OCTAL
-\002\020\150\112\130\160\200\153\360\217\002\372\366\336\350\260
-\220\220
-END
-CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
-CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
-CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
-
-#
# Certificate "SZAFIR ROOT CA2"
#
# Issuer: CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL
@@ -27873,3 +22179,1284 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "GDCA TrustAUTH R5 ROOT"
+#
+# Issuer: CN=GDCA TrustAUTH R5 ROOT,O="GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.",C=CN
+# Serial Number:7d:09:97:fe:f0:47:ea:7a
+# Subject: CN=GDCA TrustAUTH R5 ROOT,O="GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.",C=CN
+# Not Valid Before: Wed Nov 26 05:13:15 2014
+# Not Valid After : Mon Dec 31 15:59:59 2040
+# Fingerprint (SHA-256): BF:FF:8F:D0:44:33:48:7D:6A:8A:A6:0C:1A:29:76:7A:9F:C2:BB:B0:5E:42:0F:71:3A:13:B9:92:89:1D:38:93
+# Fingerprint (SHA1): 0F:36:38:5B:81:1A:25:C3:9B:31:4E:83:CA:E9:34:66:70:CC:74:B4
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "GDCA TrustAUTH R5 ROOT"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\142\061\013\060\011\006\003\125\004\006\023\002\103\116\061
+\062\060\060\006\003\125\004\012\014\051\107\125\101\116\107\040
+\104\117\116\107\040\103\105\122\124\111\106\111\103\101\124\105
+\040\101\125\124\110\117\122\111\124\131\040\103\117\056\054\114
+\124\104\056\061\037\060\035\006\003\125\004\003\014\026\107\104
+\103\101\040\124\162\165\163\164\101\125\124\110\040\122\065\040
+\122\117\117\124
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\142\061\013\060\011\006\003\125\004\006\023\002\103\116\061
+\062\060\060\006\003\125\004\012\014\051\107\125\101\116\107\040
+\104\117\116\107\040\103\105\122\124\111\106\111\103\101\124\105
+\040\101\125\124\110\117\122\111\124\131\040\103\117\056\054\114
+\124\104\056\061\037\060\035\006\003\125\004\003\014\026\107\104
+\103\101\040\124\162\165\163\164\101\125\124\110\040\122\065\040
+\122\117\117\124
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\175\011\227\376\360\107\352\172
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\005\210\060\202\003\160\240\003\002\001\002\002\010\175
+\011\227\376\360\107\352\172\060\015\006\011\052\206\110\206\367
+\015\001\001\013\005\000\060\142\061\013\060\011\006\003\125\004
+\006\023\002\103\116\061\062\060\060\006\003\125\004\012\014\051
+\107\125\101\116\107\040\104\117\116\107\040\103\105\122\124\111
+\106\111\103\101\124\105\040\101\125\124\110\117\122\111\124\131
+\040\103\117\056\054\114\124\104\056\061\037\060\035\006\003\125
+\004\003\014\026\107\104\103\101\040\124\162\165\163\164\101\125
+\124\110\040\122\065\040\122\117\117\124\060\036\027\015\061\064
+\061\061\062\066\060\065\061\063\061\065\132\027\015\064\060\061
+\062\063\061\061\065\065\071\065\071\132\060\142\061\013\060\011
+\006\003\125\004\006\023\002\103\116\061\062\060\060\006\003\125
+\004\012\014\051\107\125\101\116\107\040\104\117\116\107\040\103
+\105\122\124\111\106\111\103\101\124\105\040\101\125\124\110\117
+\122\111\124\131\040\103\117\056\054\114\124\104\056\061\037\060
+\035\006\003\125\004\003\014\026\107\104\103\101\040\124\162\165
+\163\164\101\125\124\110\040\122\065\040\122\117\117\124\060\202
+\002\042\060\015\006\011\052\206\110\206\367\015\001\001\001\005
+\000\003\202\002\017\000\060\202\002\012\002\202\002\001\000\331
+\243\026\360\310\164\164\167\233\357\063\015\073\006\176\125\374
+\265\140\217\166\206\022\102\175\126\146\076\210\202\355\162\143
+\016\236\213\335\064\054\002\121\121\303\031\375\131\124\204\311
+\361\153\263\114\260\351\350\106\135\070\306\242\247\056\021\127
+\272\202\025\242\234\217\155\260\231\112\012\362\353\211\160\143
+\116\171\304\267\133\275\242\135\261\362\101\002\053\255\251\072
+\243\354\171\012\354\137\072\343\375\357\200\074\255\064\233\032
+\253\210\046\173\126\242\202\206\037\353\065\211\203\177\137\256
+\051\116\075\266\156\354\256\301\360\047\233\256\343\364\354\357
+\256\177\367\206\075\162\172\353\245\373\131\116\247\353\225\214
+\042\071\171\341\055\010\217\314\274\221\270\101\367\024\301\043
+\251\303\255\232\105\104\263\262\327\054\315\306\051\342\120\020
+\256\134\313\202\216\027\030\066\175\227\346\210\232\260\115\064
+\011\364\054\271\132\146\052\260\027\233\236\036\166\235\112\146
+\061\101\337\077\373\305\006\357\033\266\176\032\106\066\367\144
+\143\073\343\071\030\043\347\147\165\024\325\165\127\222\067\275
+\276\152\033\046\120\362\066\046\006\220\305\160\001\144\155\166
+\146\341\221\333\156\007\300\141\200\056\262\056\057\214\160\247
+\321\073\074\263\221\344\156\266\304\073\160\362\154\222\227\011
+\315\107\175\030\300\363\273\236\017\326\213\256\007\266\132\017
+\316\013\014\107\247\345\076\270\275\175\307\233\065\240\141\227
+\072\101\165\027\314\053\226\167\052\222\041\036\331\225\166\040
+\147\150\317\015\275\337\326\037\011\152\232\342\314\163\161\244
+\057\175\022\200\267\123\060\106\136\113\124\231\017\147\311\245
+\310\362\040\301\202\354\235\021\337\302\002\373\032\073\321\355
+\040\232\357\145\144\222\020\015\052\342\336\160\361\030\147\202
+\214\141\336\270\274\321\057\234\373\017\320\053\355\033\166\271
+\344\071\125\370\370\241\035\270\252\200\000\114\202\347\262\177
+\011\270\274\060\240\057\015\365\122\236\216\367\222\263\012\000
+\035\000\124\227\006\340\261\007\331\307\017\134\145\175\074\155
+\131\127\344\355\245\215\351\100\123\237\025\113\240\161\366\032
+\041\343\332\160\006\041\130\024\207\205\167\171\252\202\171\002
+\003\001\000\001\243\102\060\100\060\035\006\003\125\035\016\004
+\026\004\024\342\311\100\237\115\316\350\232\241\174\317\016\077
+\145\305\051\210\152\031\121\060\017\006\003\125\035\023\001\001
+\377\004\005\060\003\001\001\377\060\016\006\003\125\035\017\001
+\001\377\004\004\003\002\001\206\060\015\006\011\052\206\110\206
+\367\015\001\001\013\005\000\003\202\002\001\000\321\111\127\340
+\247\314\150\130\272\001\017\053\031\315\215\260\141\105\254\021
+\355\143\120\151\370\037\177\276\026\217\375\235\353\013\252\062
+\107\166\322\147\044\355\275\174\063\062\227\052\307\005\206\146
+\015\027\175\024\025\033\324\353\375\037\232\366\136\227\151\267
+\032\045\244\012\263\221\077\137\066\254\213\354\127\250\076\347
+\201\212\030\127\071\205\164\032\102\307\351\133\023\137\217\371
+\010\351\222\164\215\365\107\322\253\073\326\373\170\146\116\066
+\175\371\351\222\351\004\336\375\111\143\374\155\373\024\161\223
+\147\057\107\112\267\271\377\036\052\163\160\106\060\277\132\362
+\057\171\245\341\215\014\331\371\262\143\067\214\067\145\205\160
+\152\134\133\011\162\271\255\143\074\261\335\370\374\062\277\067
+\206\344\273\216\230\047\176\272\037\026\341\160\021\362\003\337
+\045\142\062\047\046\030\062\204\237\377\000\072\023\272\232\115
+\364\117\270\024\160\042\261\312\053\220\316\051\301\160\364\057
+\235\177\362\220\036\326\132\337\267\106\374\346\206\372\313\340
+\040\166\172\272\246\313\365\174\336\142\245\261\213\356\336\202
+\146\212\116\072\060\037\077\200\313\255\047\272\014\136\327\320
+\261\126\312\167\161\262\265\165\241\120\251\100\103\027\302\050
+\331\317\122\213\133\310\143\324\102\076\240\063\172\106\056\367
+\012\040\106\124\176\152\117\061\361\201\176\102\164\070\145\163
+\047\356\306\174\270\216\327\245\072\327\230\241\234\214\020\125
+\323\333\113\354\100\220\362\315\156\127\322\142\016\174\127\223
+\261\247\155\315\235\203\273\052\347\345\266\073\161\130\255\375
+\321\105\274\132\221\356\123\025\157\323\105\011\165\156\272\220
+\135\036\004\317\067\337\036\250\146\261\214\346\040\152\357\374
+\110\116\164\230\102\257\051\157\056\152\307\373\175\321\146\061
+\042\314\206\000\176\146\203\014\102\364\275\064\222\303\032\352
+\117\312\176\162\115\013\160\214\246\110\273\246\241\024\366\373
+\130\104\231\024\256\252\013\223\151\240\051\045\112\245\313\053
+\335\212\146\007\026\170\025\127\161\033\354\365\107\204\363\236
+\061\067\172\325\177\044\255\344\274\375\375\314\156\203\350\014
+\250\267\101\154\007\335\275\074\206\227\057\322
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "GDCA TrustAUTH R5 ROOT"
+# Issuer: CN=GDCA TrustAUTH R5 ROOT,O="GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.",C=CN
+# Serial Number:7d:09:97:fe:f0:47:ea:7a
+# Subject: CN=GDCA TrustAUTH R5 ROOT,O="GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.",C=CN
+# Not Valid Before: Wed Nov 26 05:13:15 2014
+# Not Valid After : Mon Dec 31 15:59:59 2040
+# Fingerprint (SHA-256): BF:FF:8F:D0:44:33:48:7D:6A:8A:A6:0C:1A:29:76:7A:9F:C2:BB:B0:5E:42:0F:71:3A:13:B9:92:89:1D:38:93
+# Fingerprint (SHA1): 0F:36:38:5B:81:1A:25:C3:9B:31:4E:83:CA:E9:34:66:70:CC:74:B4
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "GDCA TrustAUTH R5 ROOT"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\017\066\070\133\201\032\045\303\233\061\116\203\312\351\064\146
+\160\314\164\264
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\143\314\331\075\064\065\134\157\123\243\342\010\160\110\037\264
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\142\061\013\060\011\006\003\125\004\006\023\002\103\116\061
+\062\060\060\006\003\125\004\012\014\051\107\125\101\116\107\040
+\104\117\116\107\040\103\105\122\124\111\106\111\103\101\124\105
+\040\101\125\124\110\117\122\111\124\131\040\103\117\056\054\114
+\124\104\056\061\037\060\035\006\003\125\004\003\014\026\107\104
+\103\101\040\124\162\165\163\164\101\125\124\110\040\122\065\040
+\122\117\117\124
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\175\011\227\376\360\107\352\172
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "TrustCor RootCert CA-1"
+#
+# Issuer: CN=TrustCor RootCert CA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Serial Number:00:da:9b:ec:71:f3:03:b0:19
+# Subject: CN=TrustCor RootCert CA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Not Valid Before: Thu Feb 04 12:32:16 2016
+# Not Valid After : Mon Dec 31 17:23:16 2029
+# Fingerprint (SHA-256): D4:0E:9C:86:CD:8F:E4:68:C1:77:69:59:F4:9E:A7:74:FA:54:86:84:B6:C4:06:F3:90:92:61:F4:DC:E2:57:5C
+# Fingerprint (SHA1): FF:BD:CD:E7:82:C8:43:5E:3C:6F:26:86:5C:CA:A8:3A:45:5B:C3:0A
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "TrustCor RootCert CA-1"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\201\244\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\037\060\035\006\003\125\004\003\014
+\026\124\162\165\163\164\103\157\162\040\122\157\157\164\103\145
+\162\164\040\103\101\055\061
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\244\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\037\060\035\006\003\125\004\003\014
+\026\124\162\165\163\164\103\157\162\040\122\157\157\164\103\145
+\162\164\040\103\101\055\061
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\011\000\332\233\354\161\363\003\260\031
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\004\060\060\202\003\030\240\003\002\001\002\002\011\000
+\332\233\354\161\363\003\260\031\060\015\006\011\052\206\110\206
+\367\015\001\001\013\005\000\060\201\244\061\013\060\011\006\003
+\125\004\006\023\002\120\101\061\017\060\015\006\003\125\004\010
+\014\006\120\141\156\141\155\141\061\024\060\022\006\003\125\004
+\007\014\013\120\141\156\141\155\141\040\103\151\164\171\061\044
+\060\042\006\003\125\004\012\014\033\124\162\165\163\164\103\157
+\162\040\123\171\163\164\145\155\163\040\123\056\040\144\145\040
+\122\056\114\056\061\047\060\045\006\003\125\004\013\014\036\124
+\162\165\163\164\103\157\162\040\103\145\162\164\151\146\151\143
+\141\164\145\040\101\165\164\150\157\162\151\164\171\061\037\060
+\035\006\003\125\004\003\014\026\124\162\165\163\164\103\157\162
+\040\122\157\157\164\103\145\162\164\040\103\101\055\061\060\036
+\027\015\061\066\060\062\060\064\061\062\063\062\061\066\132\027
+\015\062\071\061\062\063\061\061\067\062\063\061\066\132\060\201
+\244\061\013\060\011\006\003\125\004\006\023\002\120\101\061\017
+\060\015\006\003\125\004\010\014\006\120\141\156\141\155\141\061
+\024\060\022\006\003\125\004\007\014\013\120\141\156\141\155\141
+\040\103\151\164\171\061\044\060\042\006\003\125\004\012\014\033
+\124\162\165\163\164\103\157\162\040\123\171\163\164\145\155\163
+\040\123\056\040\144\145\040\122\056\114\056\061\047\060\045\006
+\003\125\004\013\014\036\124\162\165\163\164\103\157\162\040\103
+\145\162\164\151\146\151\143\141\164\145\040\101\165\164\150\157
+\162\151\164\171\061\037\060\035\006\003\125\004\003\014\026\124
+\162\165\163\164\103\157\162\040\122\157\157\164\103\145\162\164
+\040\103\101\055\061\060\202\001\042\060\015\006\011\052\206\110
+\206\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001
+\012\002\202\001\001\000\277\216\267\225\342\302\046\022\153\063
+\031\307\100\130\012\253\131\252\215\000\243\374\200\307\120\173
+\216\324\040\046\272\062\022\330\043\124\111\045\020\042\230\235
+\106\322\301\311\236\116\033\056\054\016\070\363\032\045\150\034
+\246\132\005\346\036\213\110\277\230\226\164\076\151\312\351\265
+\170\245\006\274\325\000\136\011\012\362\047\172\122\374\055\325
+\261\352\264\211\141\044\363\032\023\333\251\317\122\355\014\044
+\272\271\236\354\176\000\164\372\223\255\154\051\222\256\121\264
+\273\323\127\277\263\363\250\215\234\364\044\113\052\326\231\236
+\364\236\376\300\176\102\072\347\013\225\123\332\267\150\016\220
+\114\373\160\077\217\112\054\224\363\046\335\143\151\251\224\330
+\020\116\305\107\010\220\231\033\027\115\271\154\156\357\140\225
+\021\216\041\200\265\275\240\163\330\320\262\167\304\105\352\132
+\046\373\146\166\166\370\006\037\141\155\017\125\305\203\267\020
+\126\162\006\007\245\363\261\032\003\005\144\016\235\132\212\326
+\206\160\033\044\336\376\050\212\053\320\152\260\374\172\242\334
+\262\171\016\213\145\017\002\003\001\000\001\243\143\060\141\060
+\035\006\003\125\035\016\004\026\004\024\356\153\111\074\172\077
+\015\343\261\011\267\212\310\253\031\237\163\063\120\347\060\037
+\006\003\125\035\043\004\030\060\026\200\024\356\153\111\074\172
+\077\015\343\261\011\267\212\310\253\031\237\163\063\120\347\060
+\017\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377
+\060\016\006\003\125\035\017\001\001\377\004\004\003\002\001\206
+\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000\003
+\202\001\001\000\045\030\324\221\217\023\356\217\036\035\021\123
+\332\055\104\051\031\240\036\153\061\236\115\016\236\255\075\134
+\101\157\225\053\044\241\171\230\072\070\066\373\273\146\236\110
+\377\220\220\357\075\324\270\233\264\207\165\077\040\233\316\162
+\317\241\125\301\115\144\242\031\006\241\007\063\014\013\051\345
+\361\352\253\243\354\265\012\164\220\307\175\162\362\327\134\237
+\221\357\221\213\267\334\355\146\242\317\216\146\073\274\237\072
+\002\340\047\335\026\230\300\225\324\012\244\344\201\232\165\224
+\065\234\220\137\210\067\006\255\131\225\012\260\321\147\323\031
+\312\211\347\062\132\066\034\076\202\250\132\223\276\306\320\144
+\221\266\317\331\266\030\317\333\176\322\145\243\246\304\216\027
+\061\301\373\176\166\333\323\205\343\130\262\167\172\166\073\154
+\057\120\034\347\333\366\147\171\037\365\202\225\232\007\247\024
+\257\217\334\050\041\147\011\322\326\115\132\034\031\034\216\167
+\134\303\224\044\075\062\153\113\176\324\170\224\203\276\067\115
+\316\137\307\036\116\074\340\211\063\225\013\017\245\062\326\074
+\132\171\054\031
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "TrustCor RootCert CA-1"
+# Issuer: CN=TrustCor RootCert CA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Serial Number:00:da:9b:ec:71:f3:03:b0:19
+# Subject: CN=TrustCor RootCert CA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Not Valid Before: Thu Feb 04 12:32:16 2016
+# Not Valid After : Mon Dec 31 17:23:16 2029
+# Fingerprint (SHA-256): D4:0E:9C:86:CD:8F:E4:68:C1:77:69:59:F4:9E:A7:74:FA:54:86:84:B6:C4:06:F3:90:92:61:F4:DC:E2:57:5C
+# Fingerprint (SHA1): FF:BD:CD:E7:82:C8:43:5E:3C:6F:26:86:5C:CA:A8:3A:45:5B:C3:0A
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "TrustCor RootCert CA-1"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\377\275\315\347\202\310\103\136\074\157\046\206\134\312\250\072
+\105\133\303\012
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\156\205\361\334\032\000\323\042\325\262\262\254\153\067\005\105
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\244\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\037\060\035\006\003\125\004\003\014
+\026\124\162\165\163\164\103\157\162\040\122\157\157\164\103\145
+\162\164\040\103\101\055\061
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\011\000\332\233\354\161\363\003\260\031
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "TrustCor RootCert CA-2"
+#
+# Issuer: CN=TrustCor RootCert CA-2,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Serial Number:25:a1:df:ca:33:cb:59:02
+# Subject: CN=TrustCor RootCert CA-2,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Not Valid Before: Thu Feb 04 12:32:23 2016
+# Not Valid After : Sun Dec 31 17:26:39 2034
+# Fingerprint (SHA-256): 07:53:E9:40:37:8C:1B:D5:E3:83:6E:39:5D:AE:A5:CB:83:9E:50:46:F1:BD:0E:AE:19:51:CF:10:FE:C7:C9:65
+# Fingerprint (SHA1): B8:BE:6D:CB:56:F1:55:B9:63:D4:12:CA:4E:06:34:C7:94:B2:1C:C0
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "TrustCor RootCert CA-2"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\201\244\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\037\060\035\006\003\125\004\003\014
+\026\124\162\165\163\164\103\157\162\040\122\157\157\164\103\145
+\162\164\040\103\101\055\062
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\244\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\037\060\035\006\003\125\004\003\014
+\026\124\162\165\163\164\103\157\162\040\122\157\157\164\103\145
+\162\164\040\103\101\055\062
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\045\241\337\312\063\313\131\002
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\006\057\060\202\004\027\240\003\002\001\002\002\010\045
+\241\337\312\063\313\131\002\060\015\006\011\052\206\110\206\367
+\015\001\001\013\005\000\060\201\244\061\013\060\011\006\003\125
+\004\006\023\002\120\101\061\017\060\015\006\003\125\004\010\014
+\006\120\141\156\141\155\141\061\024\060\022\006\003\125\004\007
+\014\013\120\141\156\141\155\141\040\103\151\164\171\061\044\060
+\042\006\003\125\004\012\014\033\124\162\165\163\164\103\157\162
+\040\123\171\163\164\145\155\163\040\123\056\040\144\145\040\122
+\056\114\056\061\047\060\045\006\003\125\004\013\014\036\124\162
+\165\163\164\103\157\162\040\103\145\162\164\151\146\151\143\141
+\164\145\040\101\165\164\150\157\162\151\164\171\061\037\060\035
+\006\003\125\004\003\014\026\124\162\165\163\164\103\157\162\040
+\122\157\157\164\103\145\162\164\040\103\101\055\062\060\036\027
+\015\061\066\060\062\060\064\061\062\063\062\062\063\132\027\015
+\063\064\061\062\063\061\061\067\062\066\063\071\132\060\201\244
+\061\013\060\011\006\003\125\004\006\023\002\120\101\061\017\060
+\015\006\003\125\004\010\014\006\120\141\156\141\155\141\061\024
+\060\022\006\003\125\004\007\014\013\120\141\156\141\155\141\040
+\103\151\164\171\061\044\060\042\006\003\125\004\012\014\033\124
+\162\165\163\164\103\157\162\040\123\171\163\164\145\155\163\040
+\123\056\040\144\145\040\122\056\114\056\061\047\060\045\006\003
+\125\004\013\014\036\124\162\165\163\164\103\157\162\040\103\145
+\162\164\151\146\151\143\141\164\145\040\101\165\164\150\157\162
+\151\164\171\061\037\060\035\006\003\125\004\003\014\026\124\162
+\165\163\164\103\157\162\040\122\157\157\164\103\145\162\164\040
+\103\101\055\062\060\202\002\042\060\015\006\011\052\206\110\206
+\367\015\001\001\001\005\000\003\202\002\017\000\060\202\002\012
+\002\202\002\001\000\247\040\156\302\052\242\142\044\225\220\166
+\310\070\176\200\322\253\301\233\145\005\224\364\301\012\020\325
+\002\254\355\237\223\307\207\310\260\047\053\102\014\075\012\076
+\101\132\236\165\335\215\312\340\233\354\150\062\244\151\222\150
+\214\013\201\016\126\240\076\032\335\054\045\024\202\057\227\323
+\144\106\364\124\251\334\072\124\055\061\053\231\202\362\331\052
+\327\357\161\000\270\061\244\276\172\044\007\303\102\040\362\212
+\324\222\004\033\145\126\114\154\324\373\266\141\132\107\043\264
+\330\151\264\267\072\320\164\074\014\165\241\214\116\166\241\351
+\333\052\245\073\372\316\260\377\176\152\050\375\047\034\310\261
+\351\051\361\127\156\144\264\320\301\025\155\016\276\056\016\106
+\310\136\364\121\376\357\016\143\072\073\161\272\317\157\131\312
+\014\343\233\135\111\270\114\342\127\261\230\212\102\127\234\166
+\357\357\275\321\150\250\322\364\011\273\167\065\276\045\202\010
+\304\026\054\104\040\126\251\104\021\167\357\135\264\035\252\136
+\153\076\213\062\366\007\057\127\004\222\312\365\376\235\302\351
+\350\263\216\114\113\002\061\331\344\074\110\202\047\367\030\202
+\166\110\072\161\261\023\241\071\325\056\305\064\302\035\142\205
+\337\003\376\115\364\257\075\337\134\133\215\372\160\341\245\176
+\047\307\206\056\152\217\022\306\204\136\103\121\120\234\031\233
+\170\346\374\366\355\107\176\173\075\146\357\023\023\210\137\074
+\241\143\373\371\254\207\065\237\363\202\236\244\077\012\234\061
+\151\213\231\244\210\112\216\156\146\115\357\026\304\017\171\050
+\041\140\015\205\026\175\327\124\070\361\222\126\375\265\063\114
+\203\334\327\020\237\113\375\306\370\102\275\272\174\163\002\340
+\377\175\315\133\341\324\254\141\173\127\325\112\173\133\324\205
+\130\047\135\277\370\053\140\254\240\046\256\024\041\047\306\167
+\232\063\200\074\136\106\077\367\303\261\243\206\063\306\350\136
+\015\271\065\054\252\106\301\205\002\165\200\240\353\044\373\025
+\252\344\147\177\156\167\077\364\004\212\057\174\173\343\027\141
+\360\335\011\251\040\310\276\011\244\320\176\104\303\262\060\112
+\070\252\251\354\030\232\007\202\053\333\270\234\030\255\332\340
+\106\027\254\317\135\002\003\001\000\001\243\143\060\141\060\035
+\006\003\125\035\016\004\026\004\024\331\376\041\100\156\224\236
+\274\233\075\234\175\230\040\031\345\214\060\142\262\060\037\006
+\003\125\035\043\004\030\060\026\200\024\331\376\041\100\156\224
+\236\274\233\075\234\175\230\040\031\345\214\060\142\262\060\017
+\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377\060
+\016\006\003\125\035\017\001\001\377\004\004\003\002\001\206\060
+\015\006\011\052\206\110\206\367\015\001\001\013\005\000\003\202
+\002\001\000\236\105\236\014\073\266\357\341\072\310\174\321\000
+\075\317\342\352\006\265\262\072\273\006\113\150\172\320\043\227
+\164\247\054\360\010\330\171\132\327\132\204\212\330\022\232\033
+\331\175\134\115\160\305\245\371\253\345\243\211\211\335\001\372
+\354\335\371\351\222\227\333\260\106\102\363\323\142\252\225\376
+\061\147\024\151\130\220\012\252\013\356\067\043\307\120\121\264
+\365\176\236\343\173\367\344\314\102\062\055\111\014\313\377\111
+\014\233\036\064\375\156\156\226\212\171\003\266\157\333\011\313
+\375\137\145\024\067\341\070\365\363\141\026\130\344\265\155\015
+\013\004\033\077\120\055\177\263\307\172\032\026\200\140\370\212
+\037\351\033\052\306\371\272\001\032\151\277\322\130\307\124\127
+\010\217\341\071\140\167\113\254\131\204\032\210\361\335\313\117
+\170\327\347\341\063\055\374\356\101\372\040\260\276\313\367\070
+\224\300\341\320\205\017\273\355\054\163\253\355\376\222\166\032
+\144\177\133\015\063\011\007\063\173\006\077\021\244\134\160\074
+\205\300\317\343\220\250\203\167\372\333\346\305\214\150\147\020
+\147\245\122\055\360\304\231\217\177\277\321\153\342\265\107\326
+\331\320\205\231\115\224\233\017\113\215\356\000\132\107\035\021
+\003\254\101\030\257\207\267\157\014\072\217\312\317\334\003\301
+\242\011\310\345\375\200\136\310\140\102\001\033\032\123\132\273
+\067\246\267\274\272\204\351\036\154\032\324\144\332\324\103\376
+\223\213\113\362\054\171\026\020\324\223\013\210\217\241\330\206
+\024\106\221\107\233\050\044\357\127\122\116\134\102\234\252\367
+\111\354\047\350\100\036\263\246\211\042\162\234\365\015\063\264
+\130\243\060\073\335\324\152\124\223\276\032\115\363\223\224\367
+\374\204\013\077\204\040\134\064\003\104\305\332\255\274\012\301
+\002\317\036\345\224\331\363\216\133\330\114\360\235\354\141\027
+\273\024\062\124\014\002\051\223\036\222\206\366\177\357\347\222
+\005\016\131\335\231\010\056\056\372\234\000\122\323\305\146\051
+\344\247\227\104\244\016\050\201\023\065\305\366\157\144\346\101
+\304\325\057\314\064\105\045\317\101\000\226\075\112\056\302\226
+\230\117\116\112\234\227\267\333\037\222\062\310\377\017\121\156
+\326\354\011
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "TrustCor RootCert CA-2"
+# Issuer: CN=TrustCor RootCert CA-2,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Serial Number:25:a1:df:ca:33:cb:59:02
+# Subject: CN=TrustCor RootCert CA-2,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Not Valid Before: Thu Feb 04 12:32:23 2016
+# Not Valid After : Sun Dec 31 17:26:39 2034
+# Fingerprint (SHA-256): 07:53:E9:40:37:8C:1B:D5:E3:83:6E:39:5D:AE:A5:CB:83:9E:50:46:F1:BD:0E:AE:19:51:CF:10:FE:C7:C9:65
+# Fingerprint (SHA1): B8:BE:6D:CB:56:F1:55:B9:63:D4:12:CA:4E:06:34:C7:94:B2:1C:C0
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "TrustCor RootCert CA-2"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\270\276\155\313\126\361\125\271\143\324\022\312\116\006\064\307
+\224\262\034\300
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\242\341\370\030\013\272\105\325\307\101\052\273\067\122\105\144
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\244\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\037\060\035\006\003\125\004\003\014
+\026\124\162\165\163\164\103\157\162\040\122\157\157\164\103\145
+\162\164\040\103\101\055\062
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\045\241\337\312\063\313\131\002
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "TrustCor ECA-1"
+#
+# Issuer: CN=TrustCor ECA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Serial Number:00:84:82:2c:5f:1c:62:d0:40
+# Subject: CN=TrustCor ECA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Not Valid Before: Thu Feb 04 12:32:33 2016
+# Not Valid After : Mon Dec 31 17:28:07 2029
+# Fingerprint (SHA-256): 5A:88:5D:B1:9C:01:D9:12:C5:75:93:88:93:8C:AF:BB:DF:03:1A:B2:D4:8E:91:EE:15:58:9B:42:97:1D:03:9C
+# Fingerprint (SHA1): 58:D1:DF:95:95:67:6B:63:C0:F0:5B:1C:17:4D:8B:84:0B:C8:78:BD
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "TrustCor ECA-1"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\201\234\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\027\060\025\006\003\125\004\003\014
+\016\124\162\165\163\164\103\157\162\040\105\103\101\055\061
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\234\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\027\060\025\006\003\125\004\003\014
+\016\124\162\165\163\164\103\157\162\040\105\103\101\055\061
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\011\000\204\202\054\137\034\142\320\100
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\004\040\060\202\003\010\240\003\002\001\002\002\011\000
+\204\202\054\137\034\142\320\100\060\015\006\011\052\206\110\206
+\367\015\001\001\013\005\000\060\201\234\061\013\060\011\006\003
+\125\004\006\023\002\120\101\061\017\060\015\006\003\125\004\010
+\014\006\120\141\156\141\155\141\061\024\060\022\006\003\125\004
+\007\014\013\120\141\156\141\155\141\040\103\151\164\171\061\044
+\060\042\006\003\125\004\012\014\033\124\162\165\163\164\103\157
+\162\040\123\171\163\164\145\155\163\040\123\056\040\144\145\040
+\122\056\114\056\061\047\060\045\006\003\125\004\013\014\036\124
+\162\165\163\164\103\157\162\040\103\145\162\164\151\146\151\143
+\141\164\145\040\101\165\164\150\157\162\151\164\171\061\027\060
+\025\006\003\125\004\003\014\016\124\162\165\163\164\103\157\162
+\040\105\103\101\055\061\060\036\027\015\061\066\060\062\060\064
+\061\062\063\062\063\063\132\027\015\062\071\061\062\063\061\061
+\067\062\070\060\067\132\060\201\234\061\013\060\011\006\003\125
+\004\006\023\002\120\101\061\017\060\015\006\003\125\004\010\014
+\006\120\141\156\141\155\141\061\024\060\022\006\003\125\004\007
+\014\013\120\141\156\141\155\141\040\103\151\164\171\061\044\060
+\042\006\003\125\004\012\014\033\124\162\165\163\164\103\157\162
+\040\123\171\163\164\145\155\163\040\123\056\040\144\145\040\122
+\056\114\056\061\047\060\045\006\003\125\004\013\014\036\124\162
+\165\163\164\103\157\162\040\103\145\162\164\151\146\151\143\141
+\164\145\040\101\165\164\150\157\162\151\164\171\061\027\060\025
+\006\003\125\004\003\014\016\124\162\165\163\164\103\157\162\040
+\105\103\101\055\061\060\202\001\042\060\015\006\011\052\206\110
+\206\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001
+\012\002\202\001\001\000\317\217\340\021\265\237\250\166\166\333
+\337\017\124\357\163\143\051\202\255\107\306\243\153\355\376\137
+\063\370\103\121\351\032\063\221\061\027\240\164\304\324\247\001
+\346\262\222\076\152\235\355\016\371\164\230\100\323\077\003\200
+\006\202\100\350\261\342\247\121\247\035\203\046\153\253\336\372
+\027\221\053\330\306\254\036\261\236\031\001\325\227\246\352\015
+\267\304\125\037\047\174\322\010\325\166\037\051\025\207\100\071
+\335\070\105\021\165\320\232\247\064\340\277\315\310\122\035\271
+\107\176\015\270\273\306\014\366\163\127\026\132\176\103\221\037
+\125\072\306\155\104\004\252\234\251\234\247\114\211\027\203\256
+\243\004\136\122\200\213\036\022\045\021\031\327\014\175\175\061
+\104\101\352\333\257\260\034\357\201\320\054\305\232\041\233\075
+\355\102\073\120\046\362\354\316\161\141\006\142\041\124\116\177
+\301\235\076\177\040\214\200\313\052\330\227\142\310\203\063\221
+\175\260\242\132\017\127\350\073\314\362\045\262\324\174\057\354
+\115\306\241\072\025\172\347\266\135\065\365\366\110\112\066\105
+\146\324\272\230\130\301\002\003\001\000\001\243\143\060\141\060
+\035\006\003\125\035\016\004\026\004\024\104\236\110\365\314\155
+\110\324\240\113\177\376\131\044\057\203\227\231\232\206\060\037
+\006\003\125\035\043\004\030\060\026\200\024\104\236\110\365\314
+\155\110\324\240\113\177\376\131\044\057\203\227\231\232\206\060
+\017\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377
+\060\016\006\003\125\035\017\001\001\377\004\004\003\002\001\206
+\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000\003
+\202\001\001\000\005\076\065\134\025\160\233\311\307\163\141\157
+\162\053\324\302\217\362\103\135\002\316\304\224\271\224\021\203
+\147\135\342\147\154\165\166\277\273\014\252\066\306\255\107\223
+\143\334\036\176\326\336\056\376\351\031\062\070\003\177\024\366
+\000\163\054\131\261\041\006\341\373\254\030\225\014\243\377\231
+\226\367\053\047\233\325\044\314\035\335\301\072\340\230\104\260
+\304\344\076\167\261\163\251\144\054\366\034\001\174\077\135\105
+\205\300\205\347\045\217\225\334\027\363\074\237\032\156\260\312
+\343\035\052\351\114\143\372\044\141\142\326\332\176\266\034\154
+\365\002\035\324\052\335\125\220\353\052\021\107\074\056\136\164
+\262\202\042\245\175\123\037\105\354\047\221\175\347\042\026\350
+\300\150\066\330\306\361\117\200\104\062\371\341\321\321\035\252
+\336\250\253\234\004\257\255\040\016\144\230\115\245\153\300\110
+\130\226\151\115\334\007\214\121\223\242\337\237\017\075\213\140
+\264\202\215\252\010\116\142\105\340\371\013\322\340\340\074\133
+\336\134\161\047\045\302\346\003\201\213\020\123\343\307\125\242
+\264\237\327\346
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "TrustCor ECA-1"
+# Issuer: CN=TrustCor ECA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Serial Number:00:84:82:2c:5f:1c:62:d0:40
+# Subject: CN=TrustCor ECA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA
+# Not Valid Before: Thu Feb 04 12:32:33 2016
+# Not Valid After : Mon Dec 31 17:28:07 2029
+# Fingerprint (SHA-256): 5A:88:5D:B1:9C:01:D9:12:C5:75:93:88:93:8C:AF:BB:DF:03:1A:B2:D4:8E:91:EE:15:58:9B:42:97:1D:03:9C
+# Fingerprint (SHA1): 58:D1:DF:95:95:67:6B:63:C0:F0:5B:1C:17:4D:8B:84:0B:C8:78:BD
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "TrustCor ECA-1"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\130\321\337\225\225\147\153\143\300\360\133\034\027\115\213\204
+\013\310\170\275
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\047\222\043\035\012\365\100\174\351\346\153\235\330\365\347\154
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\234\061\013\060\011\006\003\125\004\006\023\002\120\101
+\061\017\060\015\006\003\125\004\010\014\006\120\141\156\141\155
+\141\061\024\060\022\006\003\125\004\007\014\013\120\141\156\141
+\155\141\040\103\151\164\171\061\044\060\042\006\003\125\004\012
+\014\033\124\162\165\163\164\103\157\162\040\123\171\163\164\145
+\155\163\040\123\056\040\144\145\040\122\056\114\056\061\047\060
+\045\006\003\125\004\013\014\036\124\162\165\163\164\103\157\162
+\040\103\145\162\164\151\146\151\143\141\164\145\040\101\165\164
+\150\157\162\151\164\171\061\027\060\025\006\003\125\004\003\014
+\016\124\162\165\163\164\103\157\162\040\105\103\101\055\061
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\011\000\204\202\054\137\034\142\320\100
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "SSL.com Root Certification Authority RSA"
+#
+# Issuer: CN=SSL.com Root Certification Authority RSA,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:7b:2c:9b:d3:16:80:32:99
+# Subject: CN=SSL.com Root Certification Authority RSA,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Fri Feb 12 17:39:39 2016
+# Not Valid After : Tue Feb 12 17:39:39 2041
+# Fingerprint (SHA-256): 85:66:6A:56:2E:E0:BE:5C:E9:25:C1:D8:89:0A:6F:76:A8:7E:C1:6D:4D:7D:5F:29:EA:74:19:CF:20:12:3B:69
+# Fingerprint (SHA1): B7:AB:33:08:D1:EA:44:77:BA:14:80:12:5A:6F:BD:A9:36:49:0C:BB
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com Root Certification Authority RSA"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057\006
+\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122\157
+\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156
+\040\101\165\164\150\157\162\151\164\171\040\122\123\101
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057\006
+\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122\157
+\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156
+\040\101\165\164\150\157\162\151\164\171\040\122\123\101
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\173\054\233\323\026\200\062\231
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\005\335\060\202\003\305\240\003\002\001\002\002\010\173
+\054\233\323\026\200\062\231\060\015\006\011\052\206\110\206\367
+\015\001\001\013\005\000\060\174\061\013\060\011\006\003\125\004
+\006\023\002\125\123\061\016\060\014\006\003\125\004\010\014\005
+\124\145\170\141\163\061\020\060\016\006\003\125\004\007\014\007
+\110\157\165\163\164\157\156\061\030\060\026\006\003\125\004\012
+\014\017\123\123\114\040\103\157\162\160\157\162\141\164\151\157
+\156\061\061\060\057\006\003\125\004\003\014\050\123\123\114\056
+\143\157\155\040\122\157\157\164\040\103\145\162\164\151\146\151
+\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
+\040\122\123\101\060\036\027\015\061\066\060\062\061\062\061\067
+\063\071\063\071\132\027\015\064\061\060\062\061\062\061\067\063
+\071\063\071\132\060\174\061\013\060\011\006\003\125\004\006\023
+\002\125\123\061\016\060\014\006\003\125\004\010\014\005\124\145
+\170\141\163\061\020\060\016\006\003\125\004\007\014\007\110\157
+\165\163\164\157\156\061\030\060\026\006\003\125\004\012\014\017
+\123\123\114\040\103\157\162\160\157\162\141\164\151\157\156\061
+\061\060\057\006\003\125\004\003\014\050\123\123\114\056\143\157
+\155\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141
+\164\151\157\156\040\101\165\164\150\157\162\151\164\171\040\122
+\123\101\060\202\002\042\060\015\006\011\052\206\110\206\367\015
+\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
+\002\001\000\371\017\335\243\053\175\313\320\052\376\354\147\205
+\246\347\056\033\272\167\341\343\365\257\244\354\372\112\135\221
+\304\127\107\153\030\167\153\166\362\375\223\344\075\017\302\026
+\236\013\146\303\126\224\236\027\203\205\316\126\357\362\026\375
+\000\142\365\042\011\124\350\145\027\116\101\271\340\117\106\227
+\252\033\310\270\156\142\136\151\261\137\333\052\002\176\374\154
+\312\363\101\330\355\320\350\374\077\141\110\355\260\003\024\035
+\020\016\113\031\340\273\116\354\206\145\377\066\363\136\147\002
+\013\235\206\125\141\375\172\070\355\376\342\031\000\267\157\241
+\120\142\165\164\074\240\372\310\045\222\264\156\172\042\307\370
+\036\241\343\262\335\221\061\253\053\035\004\377\245\112\004\067
+\351\205\244\063\053\375\342\326\125\064\174\031\244\112\150\307
+\262\250\323\267\312\241\223\210\353\301\227\274\214\371\035\331
+\042\204\044\164\307\004\075\152\251\051\223\314\353\270\133\341
+\376\137\045\252\064\130\310\301\043\124\235\033\230\021\303\070
+\234\176\075\206\154\245\017\100\206\174\002\364\134\002\117\050
+\313\256\161\237\017\072\310\063\376\021\045\065\352\374\272\305
+\140\075\331\174\030\325\262\251\323\165\170\003\162\042\312\072
+\303\037\357\054\345\056\251\372\236\054\266\121\106\375\257\003
+\326\352\140\150\352\205\026\066\153\205\351\036\300\263\335\304
+\044\334\200\052\201\101\155\224\076\310\340\311\201\101\000\236
+\136\277\177\305\010\230\242\030\054\102\100\263\371\157\070\047
+\113\116\200\364\075\201\107\340\210\174\352\034\316\265\165\134
+\121\056\034\053\177\032\162\050\347\000\265\321\164\306\327\344
+\237\255\007\223\266\123\065\065\374\067\344\303\366\135\026\276
+\041\163\336\222\012\370\240\143\152\274\226\222\152\076\370\274
+\145\125\233\336\365\015\211\046\004\374\045\032\246\045\151\313
+\302\155\312\174\342\131\137\227\254\353\357\056\310\274\327\033
+\131\074\053\314\362\031\310\223\153\047\143\031\317\374\351\046
+\370\312\161\233\177\223\376\064\147\204\116\231\353\374\263\170
+\011\063\160\272\146\246\166\355\033\163\353\032\245\015\304\042
+\023\040\224\126\012\116\054\154\116\261\375\317\234\011\272\242
+\063\355\207\002\003\001\000\001\243\143\060\141\060\035\006\003
+\125\035\016\004\026\004\024\335\004\011\007\242\365\172\175\122
+\123\022\222\225\356\070\200\045\015\246\131\060\017\006\003\125
+\035\023\001\001\377\004\005\060\003\001\001\377\060\037\006\003
+\125\035\043\004\030\060\026\200\024\335\004\011\007\242\365\172
+\175\122\123\022\222\225\356\070\200\045\015\246\131\060\016\006
+\003\125\035\017\001\001\377\004\004\003\002\001\206\060\015\006
+\011\052\206\110\206\367\015\001\001\013\005\000\003\202\002\001
+\000\040\030\021\224\051\373\046\235\034\036\036\160\141\361\225
+\162\223\161\044\255\150\223\130\216\062\257\033\263\160\003\374
+\045\053\164\205\220\075\170\152\364\271\213\245\227\073\265\030
+\221\273\036\247\371\100\133\221\371\125\231\257\036\021\320\134
+\035\247\146\343\261\224\007\014\062\071\246\352\033\260\171\330
+\035\234\160\104\343\212\335\304\371\225\037\212\070\103\077\001
+\205\245\107\247\075\106\262\274\345\042\150\367\173\234\330\054
+\076\012\041\310\055\063\254\277\305\201\231\061\164\301\165\161
+\305\276\261\360\043\105\364\235\153\374\031\143\235\243\274\004
+\306\030\013\045\273\123\211\017\263\200\120\336\105\356\104\177
+\253\224\170\144\230\323\366\050\335\207\330\160\145\164\373\016
+\271\023\353\247\017\141\251\062\226\314\336\273\355\143\114\030
+\273\251\100\367\240\124\156\040\210\161\165\030\352\172\264\064
+\162\340\043\047\167\134\266\220\352\206\045\100\253\357\063\017
+\313\237\202\276\242\040\373\366\265\055\032\346\302\205\261\164
+\017\373\310\145\002\244\122\001\107\335\111\042\301\277\330\353
+\153\254\176\336\354\143\063\025\267\043\010\217\306\017\215\101
+\132\335\216\305\271\217\345\105\077\170\333\272\322\033\100\261
+\376\161\115\077\340\201\242\272\136\264\354\025\340\223\335\010
+\037\176\341\125\231\013\041\336\223\236\012\373\346\243\111\275
+\066\060\376\347\167\262\240\165\227\265\055\201\210\027\145\040
+\367\332\220\000\237\311\122\314\062\312\065\174\365\075\017\330
+\053\327\365\046\154\311\006\064\226\026\352\160\131\032\062\171
+\171\013\266\210\177\017\122\110\075\277\154\330\242\104\056\321
+\116\267\162\130\323\211\023\225\376\104\253\370\327\213\033\156
+\234\274\054\240\133\325\152\000\257\137\067\341\325\372\020\013
+\230\234\206\347\046\217\316\360\354\156\212\127\013\200\343\116
+\262\300\240\143\141\220\272\125\150\067\164\152\266\222\333\237
+\241\206\042\266\145\047\016\354\266\237\102\140\344\147\302\265
+\332\101\013\304\323\213\141\033\274\372\037\221\053\327\104\007
+\136\272\051\254\331\305\351\357\123\110\132\353\200\361\050\130
+\041\315\260\006\125\373\047\077\123\220\160\251\004\036\127\047
+\271
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "SSL.com Root Certification Authority RSA"
+# Issuer: CN=SSL.com Root Certification Authority RSA,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:7b:2c:9b:d3:16:80:32:99
+# Subject: CN=SSL.com Root Certification Authority RSA,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Fri Feb 12 17:39:39 2016
+# Not Valid After : Tue Feb 12 17:39:39 2041
+# Fingerprint (SHA-256): 85:66:6A:56:2E:E0:BE:5C:E9:25:C1:D8:89:0A:6F:76:A8:7E:C1:6D:4D:7D:5F:29:EA:74:19:CF:20:12:3B:69
+# Fingerprint (SHA1): B7:AB:33:08:D1:EA:44:77:BA:14:80:12:5A:6F:BD:A9:36:49:0C:BB
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com Root Certification Authority RSA"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\267\253\063\010\321\352\104\167\272\024\200\022\132\157\275\251
+\066\111\014\273
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\206\151\022\300\160\361\354\254\254\302\325\274\245\133\241\051
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057\006
+\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122\157
+\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156
+\040\101\165\164\150\157\162\151\164\171\040\122\123\101
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\173\054\233\323\026\200\062\231
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "SSL.com Root Certification Authority ECC"
+#
+# Issuer: CN=SSL.com Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:75:e6:df:cb:c1:68:5b:a8
+# Subject: CN=SSL.com Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Fri Feb 12 18:14:03 2016
+# Not Valid After : Tue Feb 12 18:14:03 2041
+# Fingerprint (SHA-256): 34:17:BB:06:CC:60:07:DA:1B:96:1C:92:0B:8A:B4:CE:3F:AD:82:0E:4A:A3:0B:9A:CB:C4:A7:4E:BD:CE:BC:65
+# Fingerprint (SHA1): C3:19:7C:39:24:E6:54:AF:1B:C4:AB:20:95:7A:E2:C3:0E:13:02:6A
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com Root Certification Authority ECC"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057\006
+\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122\157
+\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156
+\040\101\165\164\150\157\162\151\164\171\040\105\103\103
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057\006
+\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122\157
+\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156
+\040\101\165\164\150\157\162\151\164\171\040\105\103\103
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\165\346\337\313\301\150\133\250
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\002\215\060\202\002\024\240\003\002\001\002\002\010\165
+\346\337\313\301\150\133\250\060\012\006\010\052\206\110\316\075
+\004\003\002\060\174\061\013\060\011\006\003\125\004\006\023\002
+\125\123\061\016\060\014\006\003\125\004\010\014\005\124\145\170
+\141\163\061\020\060\016\006\003\125\004\007\014\007\110\157\165
+\163\164\157\156\061\030\060\026\006\003\125\004\012\014\017\123
+\123\114\040\103\157\162\160\157\162\141\164\151\157\156\061\061
+\060\057\006\003\125\004\003\014\050\123\123\114\056\143\157\155
+\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141\164
+\151\157\156\040\101\165\164\150\157\162\151\164\171\040\105\103
+\103\060\036\027\015\061\066\060\062\061\062\061\070\061\064\060
+\063\132\027\015\064\061\060\062\061\062\061\070\061\064\060\063
+\132\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123
+\061\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163
+\061\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164
+\157\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114
+\040\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057
+\006\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122
+\157\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157
+\156\040\101\165\164\150\157\162\151\164\171\040\105\103\103\060
+\166\060\020\006\007\052\206\110\316\075\002\001\006\005\053\201
+\004\000\042\003\142\000\004\105\156\251\120\304\246\043\066\236
+\137\050\215\027\313\226\042\144\077\334\172\216\035\314\010\263
+\242\161\044\272\216\111\271\004\033\107\226\130\253\055\225\310
+\355\236\010\065\310\047\353\211\214\123\130\353\142\212\376\360
+\133\017\153\061\122\143\101\073\211\315\354\354\266\215\031\323
+\064\007\334\273\306\006\177\302\105\225\354\313\177\250\043\340
+\011\351\201\372\363\107\323\243\143\060\141\060\035\006\003\125
+\035\016\004\026\004\024\202\321\205\163\060\347\065\004\323\216
+\002\222\373\345\244\321\304\041\350\315\060\017\006\003\125\035
+\023\001\001\377\004\005\060\003\001\001\377\060\037\006\003\125
+\035\043\004\030\060\026\200\024\202\321\205\163\060\347\065\004
+\323\216\002\222\373\345\244\321\304\041\350\315\060\016\006\003
+\125\035\017\001\001\377\004\004\003\002\001\206\060\012\006\010
+\052\206\110\316\075\004\003\002\003\147\000\060\144\002\060\157
+\347\353\131\021\244\140\317\141\260\226\173\355\005\371\057\023
+\221\334\355\345\374\120\153\021\106\106\263\034\041\000\142\273
+\276\303\347\350\315\007\231\371\015\013\135\162\076\304\252\002
+\060\037\274\272\013\342\060\044\373\174\155\200\125\012\231\076
+\200\015\063\345\146\243\263\243\273\245\325\213\217\011\054\246
+\135\176\342\360\007\010\150\155\322\174\151\156\137\337\345\152
+\145
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "SSL.com Root Certification Authority ECC"
+# Issuer: CN=SSL.com Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:75:e6:df:cb:c1:68:5b:a8
+# Subject: CN=SSL.com Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Fri Feb 12 18:14:03 2016
+# Not Valid After : Tue Feb 12 18:14:03 2041
+# Fingerprint (SHA-256): 34:17:BB:06:CC:60:07:DA:1B:96:1C:92:0B:8A:B4:CE:3F:AD:82:0E:4A:A3:0B:9A:CB:C4:A7:4E:BD:CE:BC:65
+# Fingerprint (SHA1): C3:19:7C:39:24:E6:54:AF:1B:C4:AB:20:95:7A:E2:C3:0E:13:02:6A
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com Root Certification Authority ECC"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\303\031\174\071\044\346\124\257\033\304\253\040\225\172\342\303
+\016\023\002\152
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\056\332\344\071\177\234\217\067\321\160\237\046\027\121\072\216
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\174\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\061\060\057\006
+\003\125\004\003\014\050\123\123\114\056\143\157\155\040\122\157
+\157\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156
+\040\101\165\164\150\157\162\151\164\171\040\105\103\103
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\165\346\337\313\301\150\133\250
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "SSL.com EV Root Certification Authority RSA R2"
+#
+# Issuer: CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:56:b6:29:cd:34:bc:78:f6
+# Subject: CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Wed May 31 18:14:37 2017
+# Not Valid After : Fri May 30 18:14:37 2042
+# Fingerprint (SHA-256): 2E:7B:F1:6C:C2:24:85:A7:BB:E2:AA:86:96:75:07:61:B0:AE:39:BE:3B:2F:E9:D0:CC:6D:4E:F7:34:91:42:5C
+# Fingerprint (SHA1): 74:3A:F0:52:9B:D0:32:A0:F4:4A:83:CD:D4:BA:A9:7B:7C:2E:C4:9A
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com EV Root Certification Authority RSA R2"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\201\202\061\013\060\011\006\003\125\004\006\023\002\125\123
+\061\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163
+\061\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164
+\157\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114
+\040\103\157\162\160\157\162\141\164\151\157\156\061\067\060\065
+\006\003\125\004\003\014\056\123\123\114\056\143\157\155\040\105
+\126\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141
+\164\151\157\156\040\101\165\164\150\157\162\151\164\171\040\122
+\123\101\040\122\062
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\202\061\013\060\011\006\003\125\004\006\023\002\125\123
+\061\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163
+\061\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164
+\157\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114
+\040\103\157\162\160\157\162\141\164\151\157\156\061\067\060\065
+\006\003\125\004\003\014\056\123\123\114\056\143\157\155\040\105
+\126\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141
+\164\151\157\156\040\101\165\164\150\157\162\151\164\171\040\122
+\123\101\040\122\062
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\126\266\051\315\064\274\170\366
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\005\353\060\202\003\323\240\003\002\001\002\002\010\126
+\266\051\315\064\274\170\366\060\015\006\011\052\206\110\206\367
+\015\001\001\013\005\000\060\201\202\061\013\060\011\006\003\125
+\004\006\023\002\125\123\061\016\060\014\006\003\125\004\010\014
+\005\124\145\170\141\163\061\020\060\016\006\003\125\004\007\014
+\007\110\157\165\163\164\157\156\061\030\060\026\006\003\125\004
+\012\014\017\123\123\114\040\103\157\162\160\157\162\141\164\151
+\157\156\061\067\060\065\006\003\125\004\003\014\056\123\123\114
+\056\143\157\155\040\105\126\040\122\157\157\164\040\103\145\162
+\164\151\146\151\143\141\164\151\157\156\040\101\165\164\150\157
+\162\151\164\171\040\122\123\101\040\122\062\060\036\027\015\061
+\067\060\065\063\061\061\070\061\064\063\067\132\027\015\064\062
+\060\065\063\060\061\070\061\064\063\067\132\060\201\202\061\013
+\060\011\006\003\125\004\006\023\002\125\123\061\016\060\014\006
+\003\125\004\010\014\005\124\145\170\141\163\061\020\060\016\006
+\003\125\004\007\014\007\110\157\165\163\164\157\156\061\030\060
+\026\006\003\125\004\012\014\017\123\123\114\040\103\157\162\160
+\157\162\141\164\151\157\156\061\067\060\065\006\003\125\004\003
+\014\056\123\123\114\056\143\157\155\040\105\126\040\122\157\157
+\164\040\103\145\162\164\151\146\151\143\141\164\151\157\156\040
+\101\165\164\150\157\162\151\164\171\040\122\123\101\040\122\062
+\060\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001
+\001\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001
+\000\217\066\145\100\341\326\115\300\327\264\351\106\332\153\352
+\063\107\315\114\371\175\175\276\275\055\075\360\333\170\341\206
+\245\331\272\011\127\150\355\127\076\240\320\010\101\203\347\050
+\101\044\037\343\162\025\320\001\032\373\136\160\043\262\313\237
+\071\343\317\305\116\306\222\155\046\306\173\273\263\332\047\235
+\012\206\351\201\067\005\376\360\161\161\354\303\034\351\143\242
+\027\024\235\357\033\147\323\205\125\002\002\326\111\311\314\132
+\341\261\367\157\062\237\311\324\073\210\101\250\234\275\313\253
+\333\155\173\011\037\242\114\162\220\332\053\010\374\317\074\124
+\316\147\017\250\317\135\226\031\013\304\343\162\353\255\321\175
+\035\047\357\222\353\020\277\133\353\073\257\317\200\335\301\322
+\226\004\133\172\176\244\251\074\070\166\244\142\216\240\071\136
+\352\167\317\135\000\131\217\146\054\076\007\242\243\005\046\021
+\151\227\352\205\267\017\226\013\113\310\100\341\120\272\056\212
+\313\367\017\232\042\347\177\232\067\023\315\362\115\023\153\041
+\321\300\314\042\362\241\106\366\104\151\234\312\141\065\007\000
+\157\326\141\010\021\352\272\270\366\351\263\140\345\115\271\354
+\237\024\146\311\127\130\333\315\207\151\370\212\206\022\003\107
+\277\146\023\166\254\167\175\064\044\205\203\315\327\252\234\220
+\032\237\041\054\177\170\267\144\270\330\350\246\364\170\263\125
+\313\204\322\062\304\170\256\243\217\141\335\316\010\123\255\354
+\210\374\025\344\232\015\346\237\032\167\316\114\217\270\024\025
+\075\142\234\206\070\006\000\146\022\344\131\166\132\123\300\002
+\230\242\020\053\150\104\173\216\171\316\063\112\166\252\133\201
+\026\033\265\212\330\320\000\173\136\142\264\011\326\206\143\016
+\246\005\225\111\272\050\213\210\223\262\064\034\330\244\125\156
+\267\034\320\336\231\125\073\043\364\042\340\371\051\146\046\354
+\040\120\167\333\112\013\217\276\345\002\140\160\101\136\324\256
+\120\071\042\024\046\313\262\073\163\164\125\107\007\171\201\071
+\250\060\023\104\345\004\212\256\226\023\045\102\017\271\123\304
+\233\374\315\344\034\336\074\372\253\326\006\112\037\147\246\230
+\060\034\335\054\333\334\030\225\127\146\306\377\134\213\126\365
+\167\002\003\001\000\001\243\143\060\141\060\017\006\003\125\035
+\023\001\001\377\004\005\060\003\001\001\377\060\037\006\003\125
+\035\043\004\030\060\026\200\024\371\140\273\324\343\325\064\366
+\270\365\006\200\045\247\163\333\106\151\250\236\060\035\006\003
+\125\035\016\004\026\004\024\371\140\273\324\343\325\064\366\270
+\365\006\200\045\247\163\333\106\151\250\236\060\016\006\003\125
+\035\017\001\001\377\004\004\003\002\001\206\060\015\006\011\052
+\206\110\206\367\015\001\001\013\005\000\003\202\002\001\000\126
+\263\216\313\012\235\111\216\277\244\304\221\273\146\027\005\121
+\230\165\373\345\120\054\172\236\361\024\372\253\323\212\076\377
+\221\051\217\143\213\330\264\251\124\001\015\276\223\206\057\371
+\112\155\307\136\365\127\371\312\125\034\022\276\107\017\066\305
+\337\152\267\333\165\302\107\045\177\271\361\143\370\150\055\125
+\004\321\362\215\260\244\317\274\074\136\037\170\347\245\240\040
+\160\260\004\305\267\367\162\247\336\042\015\275\063\045\106\214
+\144\222\046\343\076\056\143\226\332\233\214\075\370\030\011\327
+\003\314\175\206\202\340\312\004\007\121\120\327\377\222\325\014
+\357\332\206\237\231\327\353\267\257\150\342\071\046\224\272\150
+\267\277\203\323\352\172\147\075\142\147\256\045\345\162\350\342
+\344\354\256\022\366\113\053\074\237\351\260\100\363\070\124\263
+\375\267\150\310\332\306\217\121\074\262\373\221\334\034\347\233
+\235\341\267\015\162\217\342\244\304\251\170\371\353\024\254\306
+\103\005\302\145\071\050\030\002\303\202\262\235\005\276\145\355
+\226\137\145\164\074\373\011\065\056\173\234\023\375\033\017\135
+\307\155\201\072\126\017\314\073\341\257\002\057\042\254\106\312
+\106\074\240\034\114\326\104\264\136\056\134\025\146\011\341\046
+\051\376\306\122\141\272\261\163\377\303\014\234\345\154\152\224
+\077\024\312\100\026\225\204\363\131\251\254\137\114\141\223\155
+\321\073\314\242\225\014\042\246\147\147\104\056\271\331\322\212
+\101\263\146\013\132\373\175\043\245\362\032\260\377\336\233\203
+\224\056\321\077\337\222\267\221\257\005\073\145\307\240\154\261
+\315\142\022\303\220\033\343\045\316\064\274\157\167\166\261\020
+\303\367\005\032\300\326\257\164\142\110\027\167\222\151\220\141
+\034\336\225\200\164\124\217\030\034\303\363\003\320\277\244\103
+\165\206\123\030\172\012\056\011\034\066\237\221\375\202\212\042
+\113\321\016\120\045\335\313\003\014\027\311\203\000\010\116\065
+\115\212\213\355\360\002\224\146\054\104\177\313\225\047\226\027
+\255\011\060\254\266\161\027\156\213\027\366\034\011\324\055\073
+\230\245\161\323\124\023\331\140\363\365\113\146\117\372\361\356
+\040\022\215\264\254\127\261\105\143\241\254\166\251\302\373
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "SSL.com EV Root Certification Authority RSA R2"
+# Issuer: CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:56:b6:29:cd:34:bc:78:f6
+# Subject: CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Wed May 31 18:14:37 2017
+# Not Valid After : Fri May 30 18:14:37 2042
+# Fingerprint (SHA-256): 2E:7B:F1:6C:C2:24:85:A7:BB:E2:AA:86:96:75:07:61:B0:AE:39:BE:3B:2F:E9:D0:CC:6D:4E:F7:34:91:42:5C
+# Fingerprint (SHA1): 74:3A:F0:52:9B:D0:32:A0:F4:4A:83:CD:D4:BA:A9:7B:7C:2E:C4:9A
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com EV Root Certification Authority RSA R2"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\164\072\360\122\233\320\062\240\364\112\203\315\324\272\251\173
+\174\056\304\232
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\341\036\061\130\032\256\124\123\002\366\027\152\021\173\115\225
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\201\202\061\013\060\011\006\003\125\004\006\023\002\125\123
+\061\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163
+\061\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164
+\157\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114
+\040\103\157\162\160\157\162\141\164\151\157\156\061\067\060\065
+\006\003\125\004\003\014\056\123\123\114\056\143\157\155\040\105
+\126\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141
+\164\151\157\156\040\101\165\164\150\157\162\151\164\171\040\122
+\123\101\040\122\062
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\126\266\051\315\064\274\170\366
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
+
+#
+# Certificate "SSL.com EV Root Certification Authority ECC"
+#
+# Issuer: CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:2c:29:9c:5b:16:ed:05:95
+# Subject: CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Fri Feb 12 18:15:23 2016
+# Not Valid After : Tue Feb 12 18:15:23 2041
+# Fingerprint (SHA-256): 22:A2:C1:F7:BD:ED:70:4C:C1:E7:01:B5:F4:08:C3:10:88:0F:E9:56:B5:DE:2A:4A:44:F9:9C:87:3A:25:A7:C8
+# Fingerprint (SHA1): 4C:DD:51:A3:D1:F5:20:32:14:B0:C6:C5:32:23:03:91:C7:46:42:6D
+CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com EV Root Certification Authority ECC"
+CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
+CKA_SUBJECT MULTILINE_OCTAL
+\060\177\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\064\060\062\006
+\003\125\004\003\014\053\123\123\114\056\143\157\155\040\105\126
+\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141\164
+\151\157\156\040\101\165\164\150\157\162\151\164\171\040\105\103
+\103
+END
+CKA_ID UTF8 "0"
+CKA_ISSUER MULTILINE_OCTAL
+\060\177\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\064\060\062\006
+\003\125\004\003\014\053\123\123\114\056\143\157\155\040\105\126
+\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141\164
+\151\157\156\040\101\165\164\150\157\162\151\164\171\040\105\103
+\103
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\054\051\234\133\026\355\005\225
+END
+CKA_VALUE MULTILINE_OCTAL
+\060\202\002\224\060\202\002\032\240\003\002\001\002\002\010\054
+\051\234\133\026\355\005\225\060\012\006\010\052\206\110\316\075
+\004\003\002\060\177\061\013\060\011\006\003\125\004\006\023\002
+\125\123\061\016\060\014\006\003\125\004\010\014\005\124\145\170
+\141\163\061\020\060\016\006\003\125\004\007\014\007\110\157\165
+\163\164\157\156\061\030\060\026\006\003\125\004\012\014\017\123
+\123\114\040\103\157\162\160\157\162\141\164\151\157\156\061\064
+\060\062\006\003\125\004\003\014\053\123\123\114\056\143\157\155
+\040\105\126\040\122\157\157\164\040\103\145\162\164\151\146\151
+\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171
+\040\105\103\103\060\036\027\015\061\066\060\062\061\062\061\070
+\061\065\062\063\132\027\015\064\061\060\062\061\062\061\070\061
+\065\062\063\132\060\177\061\013\060\011\006\003\125\004\006\023
+\002\125\123\061\016\060\014\006\003\125\004\010\014\005\124\145
+\170\141\163\061\020\060\016\006\003\125\004\007\014\007\110\157
+\165\163\164\157\156\061\030\060\026\006\003\125\004\012\014\017
+\123\123\114\040\103\157\162\160\157\162\141\164\151\157\156\061
+\064\060\062\006\003\125\004\003\014\053\123\123\114\056\143\157
+\155\040\105\126\040\122\157\157\164\040\103\145\162\164\151\146
+\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164
+\171\040\105\103\103\060\166\060\020\006\007\052\206\110\316\075
+\002\001\006\005\053\201\004\000\042\003\142\000\004\252\022\107
+\220\230\033\373\357\303\100\007\203\040\116\361\060\202\242\006
+\321\362\222\206\141\362\366\041\150\312\000\304\307\352\103\000
+\124\206\334\375\037\337\000\270\101\142\134\334\160\026\062\336
+\037\231\324\314\305\007\310\010\037\141\026\007\121\075\175\134
+\007\123\343\065\070\214\337\315\237\331\056\015\112\266\031\056
+\132\160\132\006\355\276\360\241\260\312\320\011\051\243\143\060
+\141\060\035\006\003\125\035\016\004\026\004\024\133\312\136\345
+\336\322\201\252\315\250\055\144\121\266\331\162\233\227\346\117
+\060\017\006\003\125\035\023\001\001\377\004\005\060\003\001\001
+\377\060\037\006\003\125\035\043\004\030\060\026\200\024\133\312
+\136\345\336\322\201\252\315\250\055\144\121\266\331\162\233\227
+\346\117\060\016\006\003\125\035\017\001\001\377\004\004\003\002
+\001\206\060\012\006\010\052\206\110\316\075\004\003\002\003\150
+\000\060\145\002\061\000\212\346\100\211\067\353\351\325\023\331
+\312\324\153\044\363\260\075\207\106\130\032\354\261\337\157\373
+\126\272\160\153\307\070\314\350\261\214\117\017\367\361\147\166
+\016\203\320\036\121\217\002\060\075\366\043\050\046\114\306\140
+\207\223\046\233\262\065\036\272\326\367\074\321\034\316\372\045
+\074\246\032\201\025\133\363\022\017\154\356\145\212\311\207\250
+\371\007\340\142\232\214\134\112
+END
+CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
+
+# Trust for "SSL.com EV Root Certification Authority ECC"
+# Issuer: CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Serial Number:2c:29:9c:5b:16:ed:05:95
+# Subject: CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US
+# Not Valid Before: Fri Feb 12 18:15:23 2016
+# Not Valid After : Tue Feb 12 18:15:23 2041
+# Fingerprint (SHA-256): 22:A2:C1:F7:BD:ED:70:4C:C1:E7:01:B5:F4:08:C3:10:88:0F:E9:56:B5:DE:2A:4A:44:F9:9C:87:3A:25:A7:C8
+# Fingerprint (SHA1): 4C:DD:51:A3:D1:F5:20:32:14:B0:C6:C5:32:23:03:91:C7:46:42:6D
+CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
+CKA_TOKEN CK_BBOOL CK_TRUE
+CKA_PRIVATE CK_BBOOL CK_FALSE
+CKA_MODIFIABLE CK_BBOOL CK_FALSE
+CKA_LABEL UTF8 "SSL.com EV Root Certification Authority ECC"
+CKA_CERT_SHA1_HASH MULTILINE_OCTAL
+\114\335\121\243\321\365\040\062\024\260\306\305\062\043\003\221
+\307\106\102\155
+END
+CKA_CERT_MD5_HASH MULTILINE_OCTAL
+\131\123\042\145\203\102\001\124\300\316\102\271\132\174\362\220
+END
+CKA_ISSUER MULTILINE_OCTAL
+\060\177\061\013\060\011\006\003\125\004\006\023\002\125\123\061
+\016\060\014\006\003\125\004\010\014\005\124\145\170\141\163\061
+\020\060\016\006\003\125\004\007\014\007\110\157\165\163\164\157
+\156\061\030\060\026\006\003\125\004\012\014\017\123\123\114\040
+\103\157\162\160\157\162\141\164\151\157\156\061\064\060\062\006
+\003\125\004\003\014\053\123\123\114\056\143\157\155\040\105\126
+\040\122\157\157\164\040\103\145\162\164\151\146\151\143\141\164
+\151\157\156\040\101\165\164\150\157\162\151\164\171\040\105\103
+\103
+END
+CKA_SERIAL_NUMBER MULTILINE_OCTAL
+\002\010\054\051\234\133\026\355\005\225
+END
+CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
+CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
+CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
diff --git a/security/nss/lib/ckfw/builtins/nssckbi.h b/security/nss/lib/ckfw/builtins/nssckbi.h
index 498751d13..0189369b1 100644
--- a/security/nss/lib/ckfw/builtins/nssckbi.h
+++ b/security/nss/lib/ckfw/builtins/nssckbi.h
@@ -46,8 +46,8 @@
* It's recommend to switch back to 0 after having reached version 98/99.
*/
#define NSS_BUILTINS_LIBRARY_VERSION_MAJOR 2
-#define NSS_BUILTINS_LIBRARY_VERSION_MINOR 16
-#define NSS_BUILTINS_LIBRARY_VERSION "2.16"
+#define NSS_BUILTINS_LIBRARY_VERSION_MINOR 22
+#define NSS_BUILTINS_LIBRARY_VERSION "2.22"
/* These version numbers detail the semantic changes to the ckfw engine. */
#define NSS_BUILTINS_HARDWARE_VERSION_MAJOR 1
diff --git a/security/nss/lib/ckfw/capi/cfind.c b/security/nss/lib/ckfw/capi/cfind.c
index 9ea7fca61..9c4d4f1e7 100644
--- a/security/nss/lib/ckfw/capi/cfind.c
+++ b/security/nss/lib/ckfw/capi/cfind.c
@@ -331,8 +331,7 @@ collect_class(
nss_ZFreeIf(keyProvInfo);
if (provName &&
- (strncmp(provName, "Microsoft", sizeof("Microsoft") -
- 1) != 0)) {
+ (strncmp(provName, "Microsoft", sizeof("Microsoft") - 1) != 0)) {
continue;
}
} else {
diff --git a/security/nss/lib/cryptohi/cryptohi.h b/security/nss/lib/cryptohi/cryptohi.h
index f658daa9e..e529fa34f 100644
--- a/security/nss/lib/cryptohi/cryptohi.h
+++ b/security/nss/lib/cryptohi/cryptohi.h
@@ -60,6 +60,14 @@ extern SECItem *DSAU_DecodeDerSigToLen(const SECItem *item, unsigned int len);
extern SGNContext *SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *privKey);
/*
+** Create a new signature context from an algorithmID.
+** "alg" the signature algorithm to use
+** "privKey" the private key to use
+*/
+extern SGNContext *SGN_NewContextWithAlgorithmID(SECAlgorithmID *alg,
+ SECKEYPrivateKey *privKey);
+
+/*
** Destroy a signature-context object
** "cx" the object
** "freeit" if PR_TRUE then free the object as well as its sub-objects
@@ -106,6 +114,21 @@ extern SECStatus SEC_SignData(SECItem *result,
SECKEYPrivateKey *pk, SECOidTag algid);
/*
+** Sign a single block of data using private key encryption and given
+** signature/hash algorithm with parameters from an algorithmID.
+** "result" the final signature data (memory is allocated)
+** "buf" the input data to sign
+** "len" the amount of data to sign
+** "pk" the private key to encrypt with
+** "algid" the signature/hash algorithm to sign with
+** (must be compatible with the key type).
+*/
+extern SECStatus SEC_SignDataWithAlgorithmID(SECItem *result,
+ const unsigned char *buf, int len,
+ SECKEYPrivateKey *pk,
+ SECAlgorithmID *algid);
+
+/*
** Sign a pre-digested block of data using private key encryption, encoding
** The given signature/hash algorithm.
** "result" the final signature data (memory is allocated)
@@ -132,6 +155,27 @@ extern SECStatus SEC_DerSignData(PLArenaPool *arena, SECItem *result,
SECKEYPrivateKey *pk, SECOidTag algid);
/*
+** DER sign a single block of data using private key encryption and
+** the given signature/hash algorithm with parameters from an
+** algorithmID. This routine first computes a digital signature using
+** SEC_SignData, then wraps it with an CERTSignedData and then der
+** encodes the result.
+** "arena" is the memory arena to use to allocate data from
+** "result" the final der encoded data (memory is allocated)
+** "buf" the input data to sign
+** "len" the amount of data to sign
+** "pk" the private key to encrypt with
+** "algid" the signature/hash algorithm to sign with
+** (must be compatible with the key type).
+*/
+extern SECStatus SEC_DerSignDataWithAlgorithmID(PLArenaPool *arena,
+ SECItem *result,
+ const unsigned char *buf,
+ int len,
+ SECKEYPrivateKey *pk,
+ SECAlgorithmID *algid);
+
+/*
** Destroy a signed-data object.
** "sd" the object
** "freeit" if PR_TRUE then free the object as well as its sub-objects
@@ -146,6 +190,23 @@ extern void SEC_DestroySignedData(CERTSignedData *sd, PRBool freeit);
extern SECOidTag SEC_GetSignatureAlgorithmOidTag(KeyType keyType,
SECOidTag hashAlgTag);
+/*
+** Create algorithm parameters for signing. Return a new item
+** allocated from arena, or NULL on failure.
+** "arena" is the memory arena to use to allocate data from
+** "result" the encoded parameters (memory is allocated)
+** "signAlgTag" is the signing algorithm
+** "hashAlgTag" is the preferred hash algorithm
+** "params" is the default parameters
+** "key" is the private key
+*/
+extern SECItem *SEC_CreateSignatureAlgorithmParameters(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag signAlgTag,
+ SECOidTag hashAlgTag,
+ const SECItem *params,
+ const SECKEYPrivateKey *key);
+
/****************************************/
/*
** Signature verification operations
diff --git a/security/nss/lib/cryptohi/keyi.h b/security/nss/lib/cryptohi/keyi.h
index f8f5f7f7d..ee11fc905 100644
--- a/security/nss/lib/cryptohi/keyi.h
+++ b/security/nss/lib/cryptohi/keyi.h
@@ -17,6 +17,9 @@ KeyType seckey_GetKeyType(SECOidTag pubKeyOid);
SECStatus sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg,
const SECItem *param, SECOidTag *encalg, SECOidTag *hashalg);
+SECStatus sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech,
+ const SECKEYRSAPSSParams *params);
+
SEC_END_PROTOS
#endif /* _KEYHI_H_ */
diff --git a/security/nss/lib/cryptohi/seckey.c b/security/nss/lib/cryptohi/seckey.c
index 9ea48b767..0f9353f3b 100644
--- a/security/nss/lib/cryptohi/seckey.c
+++ b/security/nss/lib/cryptohi/seckey.c
@@ -221,8 +221,7 @@ SECKEY_CreateECPrivateKey(SECKEYECParams *param, SECKEYPublicKey **pubk, void *c
PK11_ATTR_SESSION |
PK11_ATTR_INSENSITIVE |
PK11_ATTR_PUBLIC,
- CKF_DERIVE, CKF_DERIVE |
- CKF_SIGN,
+ CKF_DERIVE, CKF_DERIVE | CKF_SIGN,
cx);
if (!privk)
privk = PK11_GenerateKeyPairWithOpFlags(slot, CKM_EC_KEY_PAIR_GEN,
@@ -230,8 +229,7 @@ SECKEY_CreateECPrivateKey(SECKEYECParams *param, SECKEYPublicKey **pubk, void *c
PK11_ATTR_SESSION |
PK11_ATTR_SENSITIVE |
PK11_ATTR_PRIVATE,
- CKF_DERIVE, CKF_DERIVE |
- CKF_SIGN,
+ CKF_DERIVE, CKF_DERIVE | CKF_SIGN,
cx);
PK11_FreeSlot(slot);
@@ -1048,6 +1046,7 @@ SECKEY_SignatureLen(const SECKEYPublicKey *pubk)
switch (pubk->keyType) {
case rsaKey:
+ case rsaPssKey:
b0 = pubk->u.rsa.modulus.data[0];
return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1;
case dsaKey:
@@ -1974,3 +1973,118 @@ SECKEY_GetECCOid(const SECKEYECParams *params)
return oidData->offset;
}
+
+static CK_MECHANISM_TYPE
+sec_GetHashMechanismByOidTag(SECOidTag tag)
+{
+ switch (tag) {
+ case SEC_OID_SHA512:
+ return CKM_SHA512;
+ case SEC_OID_SHA384:
+ return CKM_SHA384;
+ case SEC_OID_SHA256:
+ return CKM_SHA256;
+ case SEC_OID_SHA224:
+ return CKM_SHA224;
+ case SEC_OID_SHA1:
+ return CKM_SHA_1;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return CKM_INVALID_MECHANISM;
+ }
+}
+
+static CK_RSA_PKCS_MGF_TYPE
+sec_GetMgfTypeByOidTag(SECOidTag tag)
+{
+ switch (tag) {
+ case SEC_OID_SHA512:
+ return CKG_MGF1_SHA512;
+ case SEC_OID_SHA384:
+ return CKG_MGF1_SHA384;
+ case SEC_OID_SHA256:
+ return CKG_MGF1_SHA256;
+ case SEC_OID_SHA224:
+ return CKG_MGF1_SHA224;
+ case SEC_OID_SHA1:
+ return CKG_MGF1_SHA1;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return 0;
+ }
+}
+
+SECStatus
+sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech,
+ const SECKEYRSAPSSParams *params)
+{
+ SECStatus rv = SECSuccess;
+ SECOidTag hashAlgTag;
+ unsigned long saltLength;
+ unsigned long trailerField;
+
+ PORT_Memset(mech, 0, sizeof(CK_RSA_PKCS_PSS_PARAMS));
+
+ if (params->hashAlg) {
+ hashAlgTag = SECOID_GetAlgorithmTag(params->hashAlg);
+ } else {
+ hashAlgTag = SEC_OID_SHA1; /* default, SHA-1 */
+ }
+ mech->hashAlg = sec_GetHashMechanismByOidTag(hashAlgTag);
+ if (mech->hashAlg == CKM_INVALID_MECHANISM) {
+ return SECFailure;
+ }
+
+ if (params->maskAlg) {
+ SECAlgorithmID maskHashAlg;
+ SECOidTag maskHashAlgTag;
+ PORTCheapArenaPool tmpArena;
+
+ if (SECOID_GetAlgorithmTag(params->maskAlg) != SEC_OID_PKCS1_MGF1) {
+ /* only MGF1 is known to PKCS#11 */
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &maskHashAlg,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+ &params->maskAlg->parameters);
+ PORT_DestroyCheapArena(&tmpArena);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ maskHashAlgTag = SECOID_GetAlgorithmTag(&maskHashAlg);
+ mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlgTag);
+ if (mech->mgf == 0) {
+ return SECFailure;
+ }
+ } else {
+ mech->mgf = CKG_MGF1_SHA1; /* default, MGF1 with SHA-1 */
+ }
+
+ if (params->saltLength.data) {
+ rv = SEC_ASN1DecodeInteger((SECItem *)&params->saltLength, &saltLength);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ } else {
+ saltLength = 20; /* default, 20 */
+ }
+ mech->sLen = saltLength;
+
+ if (params->trailerField.data) {
+ rv = SEC_ASN1DecodeInteger((SECItem *)&params->trailerField, &trailerField);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ if (trailerField != 1) {
+ /* the value must be 1, which represents the trailer field
+ * with hexadecimal value 0xBC */
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ }
+
+ return rv;
+}
diff --git a/security/nss/lib/cryptohi/secsign.c b/security/nss/lib/cryptohi/secsign.c
index d06cb2e85..dc10f2fa6 100644
--- a/security/nss/lib/cryptohi/secsign.c
+++ b/security/nss/lib/cryptohi/secsign.c
@@ -22,10 +22,11 @@ struct SGNContextStr {
void *hashcx;
const SECHashObject *hashobj;
SECKEYPrivateKey *key;
+ SECItem *params;
};
-SGNContext *
-SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
+static SGNContext *
+sgn_NewContext(SECOidTag alg, SECItem *params, SECKEYPrivateKey *key)
{
SGNContext *cx;
SECOidTag hashalg, signalg;
@@ -40,7 +41,7 @@ SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
* it may just support CKM_SHA1_RSA_PKCS and/or CKM_MD5_RSA_PKCS.
*/
/* we have a private key, not a public key, so don't pass it in */
- rv = sec_DecodeSigAlg(NULL, alg, NULL, &signalg, &hashalg);
+ rv = sec_DecodeSigAlg(NULL, alg, params, &signalg, &hashalg);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return 0;
@@ -49,7 +50,8 @@ SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
/* verify our key type */
if (key->keyType != keyType &&
- !((key->keyType == dsaKey) && (keyType == fortezzaKey))) {
+ !((key->keyType == dsaKey) && (keyType == fortezzaKey)) &&
+ !((key->keyType == rsaKey) && (keyType == rsaPssKey))) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return 0;
}
@@ -59,10 +61,24 @@ SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
cx->hashalg = hashalg;
cx->signalg = signalg;
cx->key = key;
+ cx->params = params;
}
return cx;
}
+SGNContext *
+SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
+{
+ return sgn_NewContext(alg, NULL, key);
+}
+
+SGNContext *
+SGN_NewContextWithAlgorithmID(SECAlgorithmID *alg, SECKEYPrivateKey *key)
+{
+ SECOidTag tag = SECOID_GetAlgorithmTag(alg);
+ return sgn_NewContext(tag, &alg->parameters, key);
+}
+
void
SGN_DestroyContext(SGNContext *cx, PRBool freeit)
{
@@ -148,6 +164,7 @@ SGN_End(SGNContext *cx, SECItem *result)
result->data = 0;
digder.data = 0;
+ sigitem.data = 0;
/* Finish up digest function */
if (cx->hashcx == NULL) {
@@ -156,7 +173,8 @@ SGN_End(SGNContext *cx, SECItem *result)
}
(*cx->hashobj->end)(cx->hashcx, digest, &part1, sizeof(digest));
- if (privKey->keyType == rsaKey) {
+ if (privKey->keyType == rsaKey &&
+ cx->signalg != SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
@@ -200,26 +218,65 @@ SGN_End(SGNContext *cx, SECItem *result)
goto loser;
}
- rv = PK11_Sign(privKey, &sigitem, &digder);
- if (rv != SECSuccess) {
- PORT_Free(sigitem.data);
- sigitem.data = NULL;
- goto loser;
+ if (cx->signalg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+ CK_RSA_PKCS_PSS_PARAMS mech;
+ SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) };
+
+ PORT_Memset(&mech, 0, sizeof(mech));
+
+ if (cx->params && cx->params->data) {
+ SECKEYRSAPSSParams params;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ PORT_Memset(&params, 0, sizeof(params));
+ rv = SEC_QuickDERDecodeItem(arena, &params,
+ SECKEY_RSAPSSParamsTemplate,
+ cx->params);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sec_RSAPSSParamsToMechanism(&mech, &params);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ mech.hashAlg = CKM_SHA_1;
+ mech.mgf = CKG_MGF1_SHA1;
+ mech.sLen = digder.len;
+ }
+ rv = PK11_SignWithMechanism(privKey, CKM_RSA_PKCS_PSS, &mechItem,
+ &sigitem, &digder);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ rv = PK11_Sign(privKey, &sigitem, &digder);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
}
if ((cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) ||
(cx->signalg == SEC_OID_ANSIX962_EC_PUBLIC_KEY)) {
/* DSAU_EncodeDerSigWithLen works for DSA and ECDSA */
rv = DSAU_EncodeDerSigWithLen(result, &sigitem, sigitem.len);
- PORT_Free(sigitem.data);
if (rv != SECSuccess)
goto loser;
+ SECITEM_FreeItem(&sigitem, PR_FALSE);
} else {
result->len = sigitem.len;
result->data = sigitem.data;
}
loser:
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(&sigitem, PR_FALSE);
+ }
SGN_DestroyDigestInfo(di);
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
@@ -229,18 +286,14 @@ loser:
/************************************************************************/
-/*
-** Sign a block of data returning in result a bunch of bytes that are the
-** signature. Returns zero on success, an error code on failure.
-*/
-SECStatus
-SEC_SignData(SECItem *res, const unsigned char *buf, int len,
- SECKEYPrivateKey *pk, SECOidTag algid)
+static SECStatus
+sec_SignData(SECItem *res, const unsigned char *buf, int len,
+ SECKEYPrivateKey *pk, SECOidTag algid, SECItem *params)
{
SECStatus rv;
SGNContext *sgn;
- sgn = SGN_NewContext(algid, pk);
+ sgn = sgn_NewContext(algid, params, pk);
if (sgn == NULL)
return SECFailure;
@@ -260,6 +313,25 @@ loser:
return rv;
}
+/*
+** Sign a block of data returning in result a bunch of bytes that are the
+** signature. Returns zero on success, an error code on failure.
+*/
+SECStatus
+SEC_SignData(SECItem *res, const unsigned char *buf, int len,
+ SECKEYPrivateKey *pk, SECOidTag algid)
+{
+ return sec_SignData(res, buf, len, pk, algid, NULL);
+}
+
+SECStatus
+SEC_SignDataWithAlgorithmID(SECItem *res, const unsigned char *buf, int len,
+ SECKEYPrivateKey *pk, SECAlgorithmID *algid)
+{
+ SECOidTag tag = SECOID_GetAlgorithmTag(algid);
+ return sec_SignData(res, buf, len, pk, tag, &algid->parameters);
+}
+
/************************************************************************/
DERTemplate CERTSignedDataTemplate[] =
@@ -294,10 +366,10 @@ const SEC_ASN1Template CERT_SignedDataTemplate[] =
SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedDataTemplate)
-SECStatus
-SEC_DerSignData(PLArenaPool *arena, SECItem *result,
+static SECStatus
+sec_DerSignData(PLArenaPool *arena, SECItem *result,
const unsigned char *buf, int len, SECKEYPrivateKey *pk,
- SECOidTag algID)
+ SECOidTag algID, SECItem *params)
{
SECItem it;
CERTSignedData sd;
@@ -339,7 +411,7 @@ SEC_DerSignData(PLArenaPool *arena, SECItem *result,
}
/* Sign input buffer */
- rv = SEC_SignData(&it, buf, len, pk, algID);
+ rv = sec_SignData(&it, buf, len, pk, algID, params);
if (rv)
goto loser;
@@ -349,7 +421,7 @@ SEC_DerSignData(PLArenaPool *arena, SECItem *result,
sd.data.len = len;
sd.signature.data = it.data;
sd.signature.len = it.len << 3; /* convert to bit string */
- rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0);
+ rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, params);
if (rv)
goto loser;
@@ -363,6 +435,24 @@ loser:
}
SECStatus
+SEC_DerSignData(PLArenaPool *arena, SECItem *result,
+ const unsigned char *buf, int len, SECKEYPrivateKey *pk,
+ SECOidTag algID)
+{
+ return sec_DerSignData(arena, result, buf, len, pk, algID, NULL);
+}
+
+SECStatus
+SEC_DerSignDataWithAlgorithmID(PLArenaPool *arena, SECItem *result,
+ const unsigned char *buf, int len,
+ SECKEYPrivateKey *pk,
+ SECAlgorithmID *algID)
+{
+ SECOidTag tag = SECOID_GetAlgorithmTag(algID);
+ return sec_DerSignData(arena, result, buf, len, pk, tag, &algID->parameters);
+}
+
+SECStatus
SGN_Digest(SECKEYPrivateKey *privKey,
SECOidTag algtag, SECItem *result, SECItem *digest)
{
@@ -509,3 +599,243 @@ SEC_GetSignatureAlgorithmOidTag(KeyType keyType, SECOidTag hashAlgTag)
}
return sigTag;
}
+
+static SECItem *
+sec_CreateRSAPSSParameters(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag hashAlgTag,
+ const SECItem *params,
+ const SECKEYPrivateKey *key)
+{
+ SECKEYRSAPSSParams pssParams;
+ int modBytes, hashLength;
+ unsigned long saltLength;
+ PRBool defaultSHA1 = PR_FALSE;
+ SECStatus rv;
+
+ if (key->keyType != rsaKey && key->keyType != rsaPssKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ PORT_Memset(&pssParams, 0, sizeof(pssParams));
+
+ if (params && params->data) {
+ /* The parameters field should either be empty or contain
+ * valid RSA-PSS parameters */
+ PORT_Assert(!(params->len == 2 &&
+ params->data[0] == SEC_ASN1_NULL &&
+ params->data[1] == 0));
+ rv = SEC_QuickDERDecodeItem(arena, &pssParams,
+ SECKEY_RSAPSSParamsTemplate,
+ params);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ defaultSHA1 = PR_TRUE;
+ }
+
+ if (pssParams.trailerField.data) {
+ unsigned long trailerField;
+
+ rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.trailerField,
+ &trailerField);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ if (trailerField != 1) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ }
+
+ modBytes = PK11_GetPrivateModulusLen((SECKEYPrivateKey *)key);
+
+ /* Determine the hash algorithm to use, based on hashAlgTag and
+ * pssParams.hashAlg; there are four cases */
+ if (hashAlgTag != SEC_OID_UNKNOWN) {
+ SECOidTag tag = SEC_OID_UNKNOWN;
+
+ if (pssParams.hashAlg) {
+ tag = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+ } else if (defaultSHA1) {
+ tag = SEC_OID_SHA1;
+ }
+
+ if (tag != SEC_OID_UNKNOWN && tag != hashAlgTag) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ } else if (hashAlgTag == SEC_OID_UNKNOWN) {
+ if (pssParams.hashAlg) {
+ hashAlgTag = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+ } else if (defaultSHA1) {
+ hashAlgTag = SEC_OID_SHA1;
+ } else {
+ /* Find a suitable hash algorithm based on the NIST recommendation */
+ if (modBytes <= 384) { /* 128, in NIST 800-57, Part 1 */
+ hashAlgTag = SEC_OID_SHA256;
+ } else if (modBytes <= 960) { /* 192, NIST 800-57, Part 1 */
+ hashAlgTag = SEC_OID_SHA384;
+ } else {
+ hashAlgTag = SEC_OID_SHA512;
+ }
+ }
+ }
+
+ if (hashAlgTag != SEC_OID_SHA1 && hashAlgTag != SEC_OID_SHA224 &&
+ hashAlgTag != SEC_OID_SHA256 && hashAlgTag != SEC_OID_SHA384 &&
+ hashAlgTag != SEC_OID_SHA512) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ /* Now that the hash algorithm is decided, check if it matches the
+ * existing parameters if any */
+ if (pssParams.maskAlg) {
+ SECAlgorithmID maskHashAlg;
+
+ if (SECOID_GetAlgorithmTag(pssParams.maskAlg) != SEC_OID_PKCS1_MGF1) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ if (pssParams.maskAlg->parameters.data == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg));
+ rv = SEC_QuickDERDecodeItem(arena, &maskHashAlg,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+ &pssParams.maskAlg->parameters);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ /* Following the recommendation in RFC 4055, assume the hash
+ * algorithm identical to pssParam.hashAlg */
+ if (SECOID_GetAlgorithmTag(&maskHashAlg) != hashAlgTag) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+ } else if (defaultSHA1) {
+ if (hashAlgTag != SEC_OID_SHA1) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+ }
+
+ hashLength = HASH_ResultLenByOidTag(hashAlgTag);
+
+ if (pssParams.saltLength.data) {
+ rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.saltLength,
+ &saltLength);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ /* The specified salt length is too long */
+ if (saltLength > modBytes - hashLength - 2) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ } else if (defaultSHA1) {
+ saltLength = 20;
+ }
+
+ /* Fill in the parameters */
+ if (pssParams.hashAlg) {
+ if (hashAlgTag == SEC_OID_SHA1) {
+ /* Omit hashAlg if the the algorithm is SHA-1 (default) */
+ pssParams.hashAlg = NULL;
+ }
+ } else {
+ if (hashAlgTag != SEC_OID_SHA1) {
+ pssParams.hashAlg = PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID));
+ if (!pssParams.hashAlg) {
+ return NULL;
+ }
+ rv = SECOID_SetAlgorithmID(arena, pssParams.hashAlg, hashAlgTag,
+ NULL);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ }
+ }
+
+ if (pssParams.maskAlg) {
+ if (hashAlgTag == SEC_OID_SHA1) {
+ /* Omit maskAlg if the the algorithm is SHA-1 (default) */
+ pssParams.maskAlg = NULL;
+ }
+ } else {
+ if (hashAlgTag != SEC_OID_SHA1) {
+ SECItem *hashAlgItem;
+
+ PORT_Assert(pssParams.hashAlg != NULL);
+
+ hashAlgItem = SEC_ASN1EncodeItem(arena, NULL, pssParams.hashAlg,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
+ if (!hashAlgItem) {
+ return NULL;
+ }
+ pssParams.maskAlg = PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID));
+ if (!pssParams.maskAlg) {
+ return NULL;
+ }
+ rv = SECOID_SetAlgorithmID(arena, pssParams.maskAlg,
+ SEC_OID_PKCS1_MGF1, hashAlgItem);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ }
+ }
+
+ if (pssParams.saltLength.data) {
+ if (saltLength == 20) {
+ /* Omit the salt length if it is the default */
+ pssParams.saltLength.data = NULL;
+ }
+ } else {
+ /* Find a suitable length from the hash algorithm and modulus bits */
+ saltLength = PR_MIN(hashLength, modBytes - hashLength - 2);
+
+ if (saltLength != 20 &&
+ !SEC_ASN1EncodeInteger(arena, &pssParams.saltLength, saltLength)) {
+ return NULL;
+ }
+ }
+
+ if (pssParams.trailerField.data) {
+ /* Omit trailerField if the value is 1 (default) */
+ pssParams.trailerField.data = NULL;
+ }
+
+ return SEC_ASN1EncodeItem(arena, result,
+ &pssParams, SECKEY_RSAPSSParamsTemplate);
+}
+
+SECItem *
+SEC_CreateSignatureAlgorithmParameters(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag signAlgTag,
+ SECOidTag hashAlgTag,
+ const SECItem *params,
+ const SECKEYPrivateKey *key)
+{
+ switch (signAlgTag) {
+ case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
+ return sec_CreateRSAPSSParameters(arena, result,
+ hashAlgTag, params, key);
+
+ default:
+ if (params == NULL)
+ return NULL;
+ if (result == NULL)
+ result = SECITEM_AllocItem(arena, NULL, 0);
+ if (SECITEM_CopyItem(arena, result, params) != SECSuccess)
+ return NULL;
+ return result;
+ }
+}
diff --git a/security/nss/lib/cryptohi/secvfy.c b/security/nss/lib/cryptohi/secvfy.c
index 2ac21abd4..83c9c579d 100644
--- a/security/nss/lib/cryptohi/secvfy.c
+++ b/security/nss/lib/cryptohi/secvfy.c
@@ -136,6 +136,8 @@ struct VFYContextStr {
unsigned char dsasig[DSA_MAX_SIGNATURE_LEN];
/* the full ECDSA signature */
unsigned char ecdsasig[2 * MAX_ECKEY_LEN];
+ /* the full RSA signature, only used in RSA-PSS */
+ unsigned char rsasig[(RSA_MAX_MODULUS_BITS + 7) / 8];
} u;
unsigned int pkcs1RSADigestInfoLen;
/* the encoded DigestInfo from a RSA PKCS#1 signature */
@@ -148,6 +150,7 @@ struct VFYContextStr {
* VFY_CreateContext call. If false, the
* signature must be provided with a
* VFY_EndWithSignature call. */
+ SECItem *params;
};
static SECStatus
@@ -250,9 +253,38 @@ sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg,
*hashalg = SEC_OID_SHA1;
break;
case SEC_OID_PKCS1_RSA_ENCRYPTION:
- case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
*hashalg = SEC_OID_UNKNOWN; /* get it from the RSA signature */
break;
+ case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
+ if (param && param->data) {
+ SECKEYRSAPSSParams pssParam;
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return SECFailure;
+ }
+ PORT_Memset(&pssParam, 0, sizeof pssParam);
+ rv = SEC_QuickDERDecodeItem(arena, &pssParam,
+ SECKEY_RSAPSSParamsTemplate,
+ param);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return rv;
+ }
+ if (pssParam.hashAlg) {
+ *hashalg = SECOID_GetAlgorithmTag(pssParam.hashAlg);
+ } else {
+ *hashalg = SEC_OID_SHA1; /* default, SHA-1 */
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ /* only accept hash algorithms */
+ if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) {
+ /* error set by HASH_GetHashTypeByOidTag */
+ return SECFailure;
+ }
+ } else {
+ *hashalg = SEC_OID_SHA1; /* default, SHA-1 */
+ }
+ break;
case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION:
@@ -434,6 +466,20 @@ vfy_CreateContext(const SECKEYPublicKey *key, const SECItem *sig,
cx->key,
sig, wincx);
break;
+ case rsaPssKey:
+ sigLen = SECKEY_SignatureLen(key);
+ if (sigLen == 0) {
+ /* error set by SECKEY_SignatureLen */
+ rv = SECFailure;
+ break;
+ }
+ if (sig->len != sigLen) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ rv = SECFailure;
+ break;
+ }
+ PORT_Memcpy(cx->u.buffer, sig->data, sigLen);
+ break;
case dsaKey:
case ecKey:
sigLen = SECKEY_SignatureLen(key);
@@ -496,6 +542,7 @@ VFYContext *
VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, const SECItem *sig,
const SECAlgorithmID *sigAlgorithm, SECOidTag *hash, void *wincx)
{
+ VFYContext *cx;
SECOidTag encAlg, hashAlg;
SECStatus rv = sec_DecodeSigAlg(key,
SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm),
@@ -503,7 +550,13 @@ VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, const SECItem *sig,
if (rv != SECSuccess) {
return NULL;
}
- return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
+
+ cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
+ if (sigAlgorithm->parameters.data) {
+ cx->params = SECITEM_DupItem(&sigAlgorithm->parameters);
+ }
+
+ return cx;
}
void
@@ -520,6 +573,9 @@ VFY_DestroyContext(VFYContext *cx, PRBool freeit)
if (cx->pkcs1RSADigestInfo) {
PORT_Free(cx->pkcs1RSADigestInfo);
}
+ if (cx->params) {
+ SECITEM_FreeItem(cx->params, PR_TRUE);
+ }
if (freeit) {
PORT_ZFree(cx, sizeof(VFYContext));
}
@@ -562,7 +618,7 @@ VFY_EndWithSignature(VFYContext *cx, SECItem *sig)
{
unsigned char final[HASH_LENGTH_MAX];
unsigned part;
- SECItem hash, dsasig; /* dsasig is also used for ECDSA */
+ SECItem hash, rsasig, dsasig; /* dsasig is also used for ECDSA */
SECStatus rv;
if ((cx->hasSignature == PR_FALSE) && (sig == NULL)) {
@@ -598,25 +654,70 @@ VFY_EndWithSignature(VFYContext *cx, SECItem *sig)
return SECFailure;
}
break;
- case rsaKey: {
- SECItem digest;
- digest.data = final;
- digest.len = part;
- if (sig) {
- SECOidTag hashid;
- PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
- rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid,
- &cx->pkcs1RSADigestInfo,
- &cx->pkcs1RSADigestInfoLen,
- cx->key,
- sig, cx->wincx);
- PORT_Assert(cx->hashAlg == hashid);
+ case rsaKey:
+ if (cx->encAlg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+ CK_RSA_PKCS_PSS_PARAMS mech;
+ SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) };
+ SECKEYRSAPSSParams params;
+ PLArenaPool *arena;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return SECFailure;
+ }
+
+ PORT_Memset(&params, 0, sizeof(params));
+ rv = SEC_QuickDERDecodeItem(arena, &params,
+ SECKEY_RSAPSSParamsTemplate,
+ cx->params);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ rv = sec_RSAPSSParamsToMechanism(&mech, &params);
+ PORT_FreeArena(arena, PR_FALSE);
if (rv != SECSuccess) {
return SECFailure;
}
+ rsasig.data = cx->u.buffer;
+ rsasig.len = SECKEY_SignatureLen(cx->key);
+ if (rsasig.len == 0) {
+ return SECFailure;
+ }
+ if (sig) {
+ if (sig->len != rsasig.len) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ PORT_Memcpy(rsasig.data, sig->data, rsasig.len);
+ }
+ hash.data = final;
+ hash.len = part;
+ if (PK11_VerifyWithMechanism(cx->key, CKM_RSA_PKCS_PSS, &mechItem,
+ &rsasig, &hash, cx->wincx) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ } else {
+ SECItem digest;
+ digest.data = final;
+ digest.len = part;
+ if (sig) {
+ SECOidTag hashid;
+ PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
+ rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid,
+ &cx->pkcs1RSADigestInfo,
+ &cx->pkcs1RSADigestInfoLen,
+ cx->key,
+ sig, cx->wincx);
+ PORT_Assert(cx->hashAlg == hashid);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+ return verifyPKCS1DigestInfo(cx, &digest);
}
- return verifyPKCS1DigestInfo(cx, &digest);
- }
+ break;
default:
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure; /* shouldn't happen */
@@ -722,7 +823,7 @@ VFY_VerifyDigestWithAlgorithmID(const SECItem *digest,
static SECStatus
vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key,
const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg,
- SECOidTag *hash, void *wincx)
+ const SECItem *params, SECOidTag *hash, void *wincx)
{
SECStatus rv;
VFYContext *cx;
@@ -730,6 +831,9 @@ vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key,
cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
if (cx == NULL)
return SECFailure;
+ if (params) {
+ cx->params = SECITEM_DupItem(params);
+ }
rv = VFY_Begin(cx);
if (rv == SECSuccess) {
@@ -748,7 +852,7 @@ VFY_VerifyDataDirect(const unsigned char *buf, int len,
SECOidTag encAlg, SECOidTag hashAlg,
SECOidTag *hash, void *wincx)
{
- return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx);
+ return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, hash, wincx);
}
SECStatus
@@ -760,7 +864,7 @@ VFY_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key,
if (rv != SECSuccess) {
return rv;
}
- return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, wincx);
+ return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, NULL, wincx);
}
SECStatus
@@ -777,5 +881,6 @@ VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len,
if (rv != SECSuccess) {
return rv;
}
- return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx);
+ return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg,
+ &sigAlgorithm->parameters, hash, wincx);
}
diff --git a/security/nss/lib/dbm/src/h_page.c b/security/nss/lib/dbm/src/h_page.c
index bf1252aeb..e5623224b 100644
--- a/security/nss/lib/dbm/src/h_page.c
+++ b/security/nss/lib/dbm/src/h_page.c
@@ -426,6 +426,9 @@ ugly_split(HTAB *hashp, uint32 obucket, BUFHEAD *old_bufp,
last_bfp = NULL;
scopyto = (uint16)copyto; /* ANSI */
+ if (ino[0] < 1) {
+ return DATABASE_CORRUPTED_ERROR;
+ }
n = ino[0] - 1;
while (n < ino[0]) {
@@ -463,7 +466,13 @@ ugly_split(HTAB *hashp, uint32 obucket, BUFHEAD *old_bufp,
* Fix up the old page -- the extra 2 are the fields
* which contained the overflow information.
*/
+ if (ino[0] < (moved + 2)) {
+ return DATABASE_CORRUPTED_ERROR;
+ }
ino[0] -= (moved + 2);
+ if (scopyto < sizeof(uint16) * (ino[0] + 3)) {
+ return DATABASE_CORRUPTED_ERROR;
+ }
FREESPACE(ino) =
scopyto - sizeof(uint16) * (ino[0] + 3);
OFFSET(ino) = scopyto;
@@ -486,8 +495,14 @@ ugly_split(HTAB *hashp, uint32 obucket, BUFHEAD *old_bufp,
for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) {
cino = (char *)ino;
key.data = (uint8 *)cino + ino[n];
+ if (off < ino[n]) {
+ return DATABASE_CORRUPTED_ERROR;
+ }
key.size = off - ino[n];
val.data = (uint8 *)cino + ino[n + 1];
+ if (ino[n] < ino[n + 1]) {
+ return DATABASE_CORRUPTED_ERROR;
+ }
val.size = ino[n] - ino[n + 1];
off = ino[n + 1];
diff --git a/security/nss/lib/dbm/src/hash.c b/security/nss/lib/dbm/src/hash.c
index b80aad4d3..98b1c07c7 100644
--- a/security/nss/lib/dbm/src/hash.c
+++ b/security/nss/lib/dbm/src/hash.c
@@ -704,8 +704,7 @@ hash_put(
return (DBM_ERROR);
}
- rv = hash_access(hashp, flag == R_NOOVERWRITE ? HASH_PUTNEW
- : HASH_PUT,
+ rv = hash_access(hashp, flag == R_NOOVERWRITE ? HASH_PUTNEW : HASH_PUT,
(DBT *)key, (DBT *)data);
if (rv == DATABASE_CORRUPTED_ERROR) {
diff --git a/security/nss/lib/dev/devutil.c b/security/nss/lib/dev/devutil.c
index b8f82c810..42ce03c97 100644
--- a/security/nss/lib/dev/devutil.c
+++ b/security/nss/lib/dev/devutil.c
@@ -32,15 +32,21 @@ nssCryptokiObject_Create(
/* a failure here indicates a device error */
return (nssCryptokiObject *)NULL;
}
+ if (cert_template[0].ulValueLen == 0) {
+ nss_ZFreeIf(cert_template[1].pValue);
+ return (nssCryptokiObject *)NULL;
+ }
object = nss_ZNEW(NULL, nssCryptokiObject);
if (!object) {
+ nss_ZFreeIf(cert_template[0].pValue);
+ nss_ZFreeIf(cert_template[1].pValue);
return (nssCryptokiObject *)NULL;
}
object->handle = h;
object->token = nssToken_AddRef(t);
isTokenObject = (CK_BBOOL *)cert_template[0].pValue;
object->isTokenObject = *isTokenObject;
- nss_ZFreeIf(isTokenObject);
+ nss_ZFreeIf(cert_template[0].pValue);
NSS_CK_ATTRIBUTE_TO_UTF8(&cert_template[1], object->label);
return object;
}
diff --git a/security/nss/lib/freebl/Makefile b/security/nss/lib/freebl/Makefile
index 914a0119c..0b3daa275 100644
--- a/security/nss/lib/freebl/Makefile
+++ b/security/nss/lib/freebl/Makefile
@@ -110,7 +110,9 @@ endif
# NSS_X86_OR_X64 means the target is either x86 or x64
ifeq (,$(filter-out i386 x386 x86 x86_64,$(CPU_ARCH)))
DEFINES += -DNSS_X86_OR_X64
- CFLAGS += -mpclmul -maes
+ EXTRA_SRCS += gcm-x86.c aes-x86.c
+$(OBJDIR)/gcm-x86.o: CFLAGS += -mpclmul -maes
+$(OBJDIR)/aes-x86.o: CFLAGS += -mpclmul -maes
ifneq (,$(USE_64)$(USE_X32))
DEFINES += -DNSS_X64
else
@@ -490,8 +492,6 @@ else
endif # Solaris for non-sparc family CPUs
endif # target == SunO
-# poly1305-donna-x64-sse2-incremental-source.c requires __int128 support
-# in GCC 4.6.0.
ifdef USE_64
ifdef CC_IS_CLANG
HAVE_INT128_SUPPORT = 1
@@ -508,38 +508,41 @@ ifdef USE_64
endif
endif
+ifndef HAVE_INT128_SUPPORT
+ DEFINES += -DKRML_NOUINT128
+endif
+
ifndef NSS_DISABLE_CHACHAPOLY
ifeq ($(CPU_ARCH),x86_64)
ifdef HAVE_INT128_SUPPORT
- EXTRA_SRCS += poly1305-donna-x64-sse2-incremental-source.c
+ EXTRA_SRCS += Hacl_Poly1305_64.c
else
EXTRA_SRCS += poly1305.c
endif
ifneq (1,$(CC_IS_GCC))
EXTRA_SRCS += chacha20.c
+ VERIFIED_SRCS += Hacl_Chacha20.c
else
EXTRA_SRCS += chacha20_vec.c
endif
else
EXTRA_SRCS += poly1305.c
EXTRA_SRCS += chacha20.c
+ VERIFIED_SRCS += Hacl_Chacha20.c
endif # x86_64
endif # NSS_DISABLE_CHACHAPOLY
-ifeq (,$(filter-out i386 x386 x86 x86_64,$(CPU_ARCH)))
+ifeq (,$(filter-out i386 x386 x86 x86_64 aarch64,$(CPU_ARCH)))
# All intel architectures get the 64 bit version
# With custom uint128 if necessary (faster than generic 32 bit version).
ECL_SRCS += curve25519_64.c
+ VERIFIED_SRCS += Hacl_Curve25519.c FStar.c
else
# All non intel architectures get the generic 32 bit implementation (slow!)
ECL_SRCS += curve25519_32.c
endif
-ifndef HAVE_INT128_SUPPORT
- ECL_SRCS += uint128.c
-endif
-
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
@@ -563,12 +566,12 @@ rijndael_tables:
$(DEFINES) $(INCLUDES) $(OBJDIR)/libfreebl.a
$(OBJDIR)/make_rijndael_tab
-vpath %.h mpi ecl
-vpath %.c mpi ecl
+vpath %.h mpi ecl verified
+vpath %.c mpi ecl verified
vpath %.S mpi ecl
vpath %.s mpi ecl
vpath %.asm mpi ecl
-INCLUDES += -Impi -Iecl
+INCLUDES += -Impi -Iecl -Iverified
DEFINES += -DMP_API_COMPATIBLE
@@ -587,8 +590,6 @@ ECL_OBJS += $(addprefix $(OBJDIR)/$(PROG_PREFIX), $(ECL_USERS:.c=$(OBJ_SUFFIX)))
$(ECL_OBJS): $(ECL_HDRS)
-
-
$(OBJDIR)/sysrand$(OBJ_SUFFIX): sysrand.c unix_rand.c win_rand.c
$(OBJDIR)/$(PROG_PREFIX)mpprime$(OBJ_SUFFIX): primes.c
diff --git a/security/nss/lib/freebl/aes-x86.c b/security/nss/lib/freebl/aes-x86.c
new file mode 100644
index 000000000..830b4782f
--- /dev/null
+++ b/security/nss/lib/freebl/aes-x86.c
@@ -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/. */
+
+#ifdef FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+#include "rijndael.h"
+#include "secerr.h"
+
+#include <wmmintrin.h> /* aes-ni */
+
+#define EXPAND_KEY128(k, rcon, res) \
+ tmp_key = _mm_aeskeygenassist_si128(k, rcon); \
+ tmp_key = _mm_shuffle_epi32(tmp_key, 0xFF); \
+ tmp = _mm_xor_si128(k, _mm_slli_si128(k, 4)); \
+ tmp = _mm_xor_si128(tmp, _mm_slli_si128(tmp, 4)); \
+ tmp = _mm_xor_si128(tmp, _mm_slli_si128(tmp, 4)); \
+ res = _mm_xor_si128(tmp, tmp_key)
+
+static void
+native_key_expansion128(AESContext *cx, const unsigned char *key)
+{
+ __m128i *keySchedule = cx->keySchedule;
+ pre_align __m128i tmp_key post_align;
+ pre_align __m128i tmp post_align;
+ keySchedule[0] = _mm_loadu_si128((__m128i *)key);
+ EXPAND_KEY128(keySchedule[0], 0x01, keySchedule[1]);
+ EXPAND_KEY128(keySchedule[1], 0x02, keySchedule[2]);
+ EXPAND_KEY128(keySchedule[2], 0x04, keySchedule[3]);
+ EXPAND_KEY128(keySchedule[3], 0x08, keySchedule[4]);
+ EXPAND_KEY128(keySchedule[4], 0x10, keySchedule[5]);
+ EXPAND_KEY128(keySchedule[5], 0x20, keySchedule[6]);
+ EXPAND_KEY128(keySchedule[6], 0x40, keySchedule[7]);
+ EXPAND_KEY128(keySchedule[7], 0x80, keySchedule[8]);
+ EXPAND_KEY128(keySchedule[8], 0x1B, keySchedule[9]);
+ EXPAND_KEY128(keySchedule[9], 0x36, keySchedule[10]);
+}
+
+#define EXPAND_KEY192_PART1(res, k0, kt, rcon) \
+ tmp2 = _mm_slli_si128(k0, 4); \
+ tmp1 = _mm_xor_si128(k0, tmp2); \
+ tmp2 = _mm_slli_si128(tmp2, 4); \
+ tmp1 = _mm_xor_si128(_mm_xor_si128(tmp1, tmp2), _mm_slli_si128(tmp2, 4)); \
+ tmp2 = _mm_aeskeygenassist_si128(kt, rcon); \
+ res = _mm_xor_si128(tmp1, _mm_shuffle_epi32(tmp2, 0x55))
+
+#define EXPAND_KEY192_PART2(res, k1, k2) \
+ tmp2 = _mm_xor_si128(k1, _mm_slli_si128(k1, 4)); \
+ res = _mm_xor_si128(tmp2, _mm_shuffle_epi32(k2, 0xFF))
+
+#define EXPAND_KEY192(k0, res1, res2, res3, carry, rcon1, rcon2) \
+ EXPAND_KEY192_PART1(tmp3, k0, res1, rcon1); \
+ EXPAND_KEY192_PART2(carry, res1, tmp3); \
+ res1 = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(res1), \
+ _mm_castsi128_pd(tmp3), 0)); \
+ res2 = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(tmp3), \
+ _mm_castsi128_pd(carry), 1)); \
+ EXPAND_KEY192_PART1(res3, tmp3, carry, rcon2)
+
+static void
+native_key_expansion192(AESContext *cx, const unsigned char *key)
+{
+ __m128i *keySchedule = cx->keySchedule;
+ pre_align __m128i tmp1 post_align;
+ pre_align __m128i tmp2 post_align;
+ pre_align __m128i tmp3 post_align;
+ pre_align __m128i carry post_align;
+ keySchedule[0] = _mm_loadu_si128((__m128i *)key);
+ keySchedule[1] = _mm_loadu_si128((__m128i *)(key + 16));
+ EXPAND_KEY192(keySchedule[0], keySchedule[1], keySchedule[2],
+ keySchedule[3], carry, 0x1, 0x2);
+ EXPAND_KEY192_PART2(keySchedule[4], carry, keySchedule[3]);
+ EXPAND_KEY192(keySchedule[3], keySchedule[4], keySchedule[5],
+ keySchedule[6], carry, 0x4, 0x8);
+ EXPAND_KEY192_PART2(keySchedule[7], carry, keySchedule[6]);
+ EXPAND_KEY192(keySchedule[6], keySchedule[7], keySchedule[8],
+ keySchedule[9], carry, 0x10, 0x20);
+ EXPAND_KEY192_PART2(keySchedule[10], carry, keySchedule[9]);
+ EXPAND_KEY192(keySchedule[9], keySchedule[10], keySchedule[11],
+ keySchedule[12], carry, 0x40, 0x80);
+}
+
+#define EXPAND_KEY256_PART(res, rconx, k1x, k2x, X) \
+ tmp_key = _mm_shuffle_epi32(_mm_aeskeygenassist_si128(k2x, rconx), X); \
+ tmp2 = _mm_slli_si128(k1x, 4); \
+ tmp1 = _mm_xor_si128(k1x, tmp2); \
+ tmp2 = _mm_slli_si128(tmp2, 4); \
+ tmp1 = _mm_xor_si128(_mm_xor_si128(tmp1, tmp2), _mm_slli_si128(tmp2, 4)); \
+ res = _mm_xor_si128(tmp1, tmp_key);
+
+#define EXPAND_KEY256(res1, res2, k1, k2, rcon) \
+ EXPAND_KEY256_PART(res1, rcon, k1, k2, 0xFF); \
+ EXPAND_KEY256_PART(res2, 0x00, k2, res1, 0xAA)
+
+static void
+native_key_expansion256(AESContext *cx, const unsigned char *key)
+{
+ __m128i *keySchedule = cx->keySchedule;
+ pre_align __m128i tmp_key post_align;
+ pre_align __m128i tmp1 post_align;
+ pre_align __m128i tmp2 post_align;
+ keySchedule[0] = _mm_loadu_si128((__m128i *)key);
+ keySchedule[1] = _mm_loadu_si128((__m128i *)(key + 16));
+ EXPAND_KEY256(keySchedule[2], keySchedule[3], keySchedule[0],
+ keySchedule[1], 0x01);
+ EXPAND_KEY256(keySchedule[4], keySchedule[5], keySchedule[2],
+ keySchedule[3], 0x02);
+ EXPAND_KEY256(keySchedule[6], keySchedule[7], keySchedule[4],
+ keySchedule[5], 0x04);
+ EXPAND_KEY256(keySchedule[8], keySchedule[9], keySchedule[6],
+ keySchedule[7], 0x08);
+ EXPAND_KEY256(keySchedule[10], keySchedule[11], keySchedule[8],
+ keySchedule[9], 0x10);
+ EXPAND_KEY256(keySchedule[12], keySchedule[13], keySchedule[10],
+ keySchedule[11], 0x20);
+ EXPAND_KEY256_PART(keySchedule[14], 0x40, keySchedule[12],
+ keySchedule[13], 0xFF);
+}
+
+/*
+ * AES key expansion using aes-ni instructions.
+ */
+void
+rijndael_native_key_expansion(AESContext *cx, const unsigned char *key,
+ unsigned int Nk)
+{
+ switch (Nk) {
+ case 4:
+ native_key_expansion128(cx, key);
+ return;
+ case 6:
+ native_key_expansion192(cx, key);
+ return;
+ case 8:
+ native_key_expansion256(cx, key);
+ return;
+ default:
+ /* This shouldn't happen (checked by the caller). */
+ return;
+ }
+}
+
+void
+rijndael_native_encryptBlock(AESContext *cx,
+ unsigned char *output,
+ const unsigned char *input)
+{
+ int i;
+ pre_align __m128i m post_align = _mm_loadu_si128((__m128i *)input);
+ m = _mm_xor_si128(m, cx->keySchedule[0]);
+ for (i = 1; i < cx->Nr; ++i) {
+ m = _mm_aesenc_si128(m, cx->keySchedule[i]);
+ }
+ m = _mm_aesenclast_si128(m, cx->keySchedule[cx->Nr]);
+ _mm_storeu_si128((__m128i *)output, m);
+}
diff --git a/security/nss/lib/freebl/blake2b.c b/security/nss/lib/freebl/blake2b.c
new file mode 100644
index 000000000..4099c67e0
--- /dev/null
+++ b/security/nss/lib/freebl/blake2b.c
@@ -0,0 +1,430 @@
+/*
+ * blake2b.c - definitions for the blake2b hash function
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+
+#include "secerr.h"
+#include "blapi.h"
+#include "blake2b.h"
+#include "crypto_primitives.h"
+
+/**
+ * This contains the BLAKE2b initialization vectors.
+ */
+static const uint64_t iv[8] = {
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+/**
+ * This contains the table of permutations for blake2b compression function.
+ */
+static const uint8_t sigma[12][16] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+/**
+ * This function increments the blake2b ctx counter.
+ */
+void
+blake2b_IncrementCounter(BLAKE2BContext* ctx, const uint64_t inc)
+{
+ ctx->t[0] += inc;
+ ctx->t[1] += ctx->t[0] < inc;
+}
+
+/**
+ * This macro implements the blake2b mixing function which mixes two 8-byte
+ * words from the message into the hash.
+ */
+#define G(a, b, c, d, x, y) \
+ a += b + x; \
+ d = ROTR64(d ^ a, 32); \
+ c += d; \
+ b = ROTR64(b ^ c, 24); \
+ a += b + y; \
+ d = ROTR64(d ^ a, 16); \
+ c += d; \
+ b = ROTR64(b ^ c, 63)
+
+#define ROUND(i) \
+ G(v[0], v[4], v[8], v[12], m[sigma[i][0]], m[sigma[i][1]]); \
+ G(v[1], v[5], v[9], v[13], m[sigma[i][2]], m[sigma[i][3]]); \
+ G(v[2], v[6], v[10], v[14], m[sigma[i][4]], m[sigma[i][5]]); \
+ G(v[3], v[7], v[11], v[15], m[sigma[i][6]], m[sigma[i][7]]); \
+ G(v[0], v[5], v[10], v[15], m[sigma[i][8]], m[sigma[i][9]]); \
+ G(v[1], v[6], v[11], v[12], m[sigma[i][10]], m[sigma[i][11]]); \
+ G(v[2], v[7], v[8], v[13], m[sigma[i][12]], m[sigma[i][13]]); \
+ G(v[3], v[4], v[9], v[14], m[sigma[i][14]], m[sigma[i][15]])
+
+/**
+ * The blake2b compression function which takes a full 128-byte chunk of the
+ * input message and mixes it into the ongoing ctx array, i.e., permute the
+ * ctx while xoring in the block of data.
+ */
+void
+blake2b_Compress(BLAKE2BContext* ctx, const uint8_t* block)
+{
+ size_t i;
+ uint64_t v[16], m[16];
+
+ PORT_Memcpy(m, block, BLAKE2B_BLOCK_LENGTH);
+#if !defined(IS_LITTLE_ENDIAN)
+ for (i = 0; i < 16; ++i) {
+ m[i] = FREEBL_HTONLL(m[i]);
+ }
+#endif
+
+ PORT_Memcpy(v, ctx->h, 8 * 8);
+ PORT_Memcpy(v + 8, iv, 8 * 8);
+
+ v[12] ^= ctx->t[0];
+ v[13] ^= ctx->t[1];
+ v[14] ^= ctx->f;
+
+ ROUND(0);
+ ROUND(1);
+ ROUND(2);
+ ROUND(3);
+ ROUND(4);
+ ROUND(5);
+ ROUND(6);
+ ROUND(7);
+ ROUND(8);
+ ROUND(9);
+ ROUND(10);
+ ROUND(11);
+
+ for (i = 0; i < 8; i++) {
+ ctx->h[i] ^= v[i] ^ v[i + 8];
+ }
+}
+
+/**
+ * This function can be used for both keyed and unkeyed version.
+ */
+BLAKE2BContext*
+BLAKE2B_NewContext()
+{
+ return PORT_ZNew(BLAKE2BContext);
+}
+
+/**
+ * Zero and free the context and can be used for both keyed and unkeyed version.
+ */
+void
+BLAKE2B_DestroyContext(BLAKE2BContext* ctx, PRBool freeit)
+{
+ PORT_Memset(ctx, 0, sizeof(*ctx));
+ if (freeit) {
+ PORT_Free(ctx);
+ }
+}
+
+/**
+ * This function initializes blake2b ctx and can be used for both keyed and
+ * unkeyed version. It also checks ctx and sets error states.
+ */
+static SECStatus
+blake2b_Begin(BLAKE2BContext* ctx, uint8_t outlen, const uint8_t* key,
+ size_t keylen)
+{
+ PORT_Assert(ctx != NULL);
+ if (!ctx) {
+ goto failure;
+ }
+ if (outlen == 0 || outlen > BLAKE2B512_LENGTH) {
+ goto failure;
+ }
+ if (key && keylen > BLAKE2B_KEY_SIZE) {
+ goto failure;
+ }
+ /* Note: key can be null if it's unkeyed. */
+ if ((key == NULL && keylen > 0) || keylen > BLAKE2B_KEY_SIZE ||
+ (key != NULL && keylen == 0)) {
+ goto failure;
+ }
+
+ /* Mix key size(keylen) and desired hash length(outlen) into h0 */
+ uint64_t param = outlen ^ (keylen << 8) ^ (1 << 16) ^ (1 << 24);
+ PORT_Memcpy(ctx->h, iv, 8 * 8);
+ ctx->h[0] ^= param;
+ ctx->outlen = outlen;
+
+ /* This updates the context for only the keyed version */
+ if (keylen > 0 && keylen <= BLAKE2B_KEY_SIZE && key) {
+ uint8_t block[BLAKE2B_BLOCK_LENGTH] = { 0 };
+ PORT_Memcpy(block, key, keylen);
+ BLAKE2B_Update(ctx, block, BLAKE2B_BLOCK_LENGTH);
+ PORT_Memset(block, 0, BLAKE2B_BLOCK_LENGTH);
+ }
+
+ return SECSuccess;
+
+failure:
+ PORT_Memset(&ctx, 0, sizeof(ctx));
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+}
+
+SECStatus
+BLAKE2B_Begin(BLAKE2BContext* ctx)
+{
+ return blake2b_Begin(ctx, BLAKE2B512_LENGTH, NULL, 0);
+}
+
+SECStatus
+BLAKE2B_MAC_Begin(BLAKE2BContext* ctx, const PRUint8* key, const size_t keylen)
+{
+ PORT_Assert(key != NULL);
+ if (!key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return blake2b_Begin(ctx, BLAKE2B512_LENGTH, (const uint8_t*)key, keylen);
+}
+
+static void
+blake2b_IncrementCompress(BLAKE2BContext* ctx, size_t blockLength,
+ const unsigned char* input)
+{
+ blake2b_IncrementCounter(ctx, blockLength);
+ blake2b_Compress(ctx, input);
+}
+
+/**
+ * This function updates blake2b ctx and can be used for both keyed and unkeyed
+ * version.
+ */
+SECStatus
+BLAKE2B_Update(BLAKE2BContext* ctx, const unsigned char* in,
+ unsigned int inlen)
+{
+ size_t left = ctx->buflen;
+ size_t fill = BLAKE2B_BLOCK_LENGTH - left;
+
+ /* Nothing to do if there's nothing. */
+ if (inlen == 0) {
+ return SECSuccess;
+ }
+
+ PORT_Assert(ctx != NULL);
+ PORT_Assert(in != NULL);
+ PORT_Assert(left <= BLAKE2B_BLOCK_LENGTH);
+ if (!ctx || !in) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Is this a reused context? */
+ if (ctx->f) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (inlen > fill) {
+ if (ctx->buflen) {
+ /* There's some remaining data in ctx->buf that we have to prepend
+ * to in. */
+ PORT_Memcpy(ctx->buf + left, in, fill);
+ ctx->buflen = 0;
+ blake2b_IncrementCompress(ctx, BLAKE2B_BLOCK_LENGTH, ctx->buf);
+ in += fill;
+ inlen -= fill;
+ }
+ while (inlen > BLAKE2B_BLOCK_LENGTH) {
+ blake2b_IncrementCompress(ctx, BLAKE2B_BLOCK_LENGTH, in);
+ in += BLAKE2B_BLOCK_LENGTH;
+ inlen -= BLAKE2B_BLOCK_LENGTH;
+ }
+ }
+
+ /* Store the remaining data from in in ctx->buf to process later.
+ * Note that ctx->buflen can be BLAKE2B_BLOCK_LENGTH. We can't process that
+ * here because we have to update ctx->f before compressing the last block.
+ */
+ PORT_Assert(inlen <= BLAKE2B_BLOCK_LENGTH);
+ PORT_Memcpy(ctx->buf + ctx->buflen, in, inlen);
+ ctx->buflen += inlen;
+
+ return SECSuccess;
+}
+
+/**
+ * This function finalizes ctx, pads final block and stores hash.
+ * It can be used for both keyed and unkeyed version.
+ */
+SECStatus
+BLAKE2B_End(BLAKE2BContext* ctx, unsigned char* out,
+ unsigned int* digestLen, size_t maxDigestLen)
+{
+ size_t i;
+ unsigned int outlen = PR_MIN(BLAKE2B512_LENGTH, maxDigestLen);
+
+ /* Argument checks */
+ if (!ctx || !out) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Sanity check against outlen in context. */
+ if (ctx->outlen < outlen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Is this a reused context? */
+ if (ctx->f != 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Process the remaining data from ctx->buf (padded with 0). */
+ blake2b_IncrementCounter(ctx, ctx->buflen);
+ /* BLAKE2B_BLOCK_LENGTH - ctx->buflen can be 0. */
+ PORT_Memset(ctx->buf + ctx->buflen, 0, BLAKE2B_BLOCK_LENGTH - ctx->buflen);
+ ctx->f = UINT64_MAX;
+ blake2b_Compress(ctx, ctx->buf);
+
+ /* Write out the blake2b context(ctx). */
+ for (i = 0; i < outlen; ++i) {
+ out[i] = ctx->h[i / 8] >> ((i % 8) * 8);
+ }
+
+ if (digestLen) {
+ *digestLen = outlen;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+blake2b_HashBuf(uint8_t* output, const uint8_t* input, uint8_t outlen,
+ size_t inlen, const uint8_t* key, size_t keylen)
+{
+ SECStatus rv = SECFailure;
+ BLAKE2BContext ctx = { { 0 } };
+
+ if (inlen != 0) {
+ PORT_Assert(input != NULL);
+ if (input == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto done;
+ }
+ }
+
+ PORT_Assert(output != NULL);
+ if (output == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto done;
+ }
+
+ if (blake2b_Begin(&ctx, outlen, key, keylen) != SECSuccess) {
+ goto done;
+ }
+
+ if (BLAKE2B_Update(&ctx, input, inlen) != SECSuccess) {
+ goto done;
+ }
+
+ if (BLAKE2B_End(&ctx, output, NULL, outlen) != SECSuccess) {
+ goto done;
+ }
+ rv = SECSuccess;
+
+done:
+ PORT_Memset(&ctx, 0, sizeof ctx);
+ return rv;
+}
+
+SECStatus
+BLAKE2B_Hash(unsigned char* dest, const char* src)
+{
+ return blake2b_HashBuf(dest, (const unsigned char*)src, BLAKE2B512_LENGTH,
+ PORT_Strlen(src), NULL, 0);
+}
+
+SECStatus
+BLAKE2B_HashBuf(unsigned char* output, const unsigned char* input, PRUint32 inlen)
+{
+ return blake2b_HashBuf(output, input, BLAKE2B512_LENGTH, inlen, NULL, 0);
+}
+
+SECStatus
+BLAKE2B_MAC_HashBuf(unsigned char* output, const unsigned char* input,
+ unsigned int inlen, const unsigned char* key,
+ unsigned int keylen)
+{
+ PORT_Assert(key != NULL);
+ if (!key && keylen <= BLAKE2B_KEY_SIZE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return blake2b_HashBuf(output, input, BLAKE2B512_LENGTH, inlen, key, keylen);
+}
+
+unsigned int
+BLAKE2B_FlattenSize(BLAKE2BContext* ctx)
+{
+ return sizeof(BLAKE2BContext);
+}
+
+SECStatus
+BLAKE2B_Flatten(BLAKE2BContext* ctx, unsigned char* space)
+{
+ PORT_Assert(space != NULL);
+ if (!space) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ PORT_Memcpy(space, ctx, sizeof(BLAKE2BContext));
+ return SECSuccess;
+}
+
+BLAKE2BContext*
+BLAKE2B_Resurrect(unsigned char* space, void* arg)
+{
+ PORT_Assert(space != NULL);
+ if (!space) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ BLAKE2BContext* ctx = BLAKE2B_NewContext();
+ if (ctx == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ PORT_Memcpy(ctx, space, sizeof(BLAKE2BContext));
+ return ctx;
+}
+
+void
+BLAKE2B_Clone(BLAKE2BContext* dest, BLAKE2BContext* src)
+{
+ PORT_Assert(dest != NULL);
+ PORT_Assert(src != NULL);
+ if (!dest || !src) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+ PORT_Memcpy(dest, src, sizeof(BLAKE2BContext));
+}
diff --git a/security/nss/lib/freebl/blake2b.h b/security/nss/lib/freebl/blake2b.h
new file mode 100644
index 000000000..d19a49f0e
--- /dev/null
+++ b/security/nss/lib/freebl/blake2b.h
@@ -0,0 +1,23 @@
+/*
+ * blake2b.h - header file for blake2b hash function
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 BLAKE_H
+#define BLAKE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct Blake2bContextStr {
+ uint64_t h[8]; /* chained state */
+ uint64_t t[2]; /* total number of bytes */
+ uint64_t f; /* last block flag */
+ uint8_t buf[BLAKE2B_BLOCK_LENGTH]; /* input buffer */
+ size_t buflen; /* size of remaining bytes in buf */
+ size_t outlen; /* digest size */
+};
+
+#endif /* BLAKE_H */
diff --git a/security/nss/lib/freebl/blapi.h b/security/nss/lib/freebl/blapi.h
index 31e471ac4..ca2149972 100644
--- a/security/nss/lib/freebl/blapi.h
+++ b/security/nss/lib/freebl/blapi.h
@@ -1402,6 +1402,84 @@ TLS_P_hash(HASH_HashType hashAlg, const SECItem *secret, const char *label,
/******************************************/
/*
+** Implements the Blake2b hash function.
+*/
+
+/*
+** Hash a null terminated string "src" into "dest" using Blake2b
+*/
+extern SECStatus BLAKE2B_Hash(unsigned char *dest, const char *src);
+
+/*
+** Hash a non-null terminated string "src" into "dest" using Blake2b
+*/
+extern SECStatus BLAKE2B_HashBuf(unsigned char *output,
+ const unsigned char *input, PRUint32 inlen);
+
+extern SECStatus BLAKE2B_MAC_HashBuf(unsigned char *output,
+ const unsigned char *input,
+ unsigned int inlen,
+ const unsigned char *key,
+ unsigned int keylen);
+
+/*
+** Create a new Blake2b context
+*/
+extern BLAKE2BContext *BLAKE2B_NewContext();
+
+/*
+** Destroy a Blake2b secure hash context.
+** "ctx" the context
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void BLAKE2B_DestroyContext(BLAKE2BContext *ctx, PRBool freeit);
+
+/*
+** Reset a Blake2b context, preparing it for a fresh round of hashing
+*/
+extern SECStatus BLAKE2B_Begin(BLAKE2BContext *ctx);
+
+extern SECStatus BLAKE2B_MAC_Begin(BLAKE2BContext *ctx, const PRUint8 *key,
+ const size_t keylen);
+
+/*
+** Update the Blake hash function with more data.
+*/
+extern SECStatus BLAKE2B_Update(BLAKE2BContext *ctx, const unsigned char *in,
+ unsigned int inlen);
+
+/*
+** Finish the Blake hash function. Produce the digested results in "digest"
+*/
+extern SECStatus BLAKE2B_End(BLAKE2BContext *ctx, unsigned char *out,
+ unsigned int *digestLen, size_t maxDigestLen);
+
+/*
+ * Return the size of a buffer needed to flatten the Blake2b Context into
+ * "ctx" the context
+ * returns size;
+ */
+extern unsigned int BLAKE2B_FlattenSize(BLAKE2BContext *ctx);
+
+/*
+ * Flatten the Blake2b Context into a buffer:
+ * "ctx" the context
+ * "space" the buffer to flatten to
+ * returns status;
+ */
+extern SECStatus BLAKE2B_Flatten(BLAKE2BContext *ctx, unsigned char *space);
+
+/*
+ * Resurrect a flattened context into a Blake2b Context
+ * "space" the buffer of the flattend buffer
+ * "arg" ptr to void used by cryptographic resurrect
+ * returns resurected context
+ */
+extern BLAKE2BContext *BLAKE2B_Resurrect(unsigned char *space, void *arg);
+extern void BLAKE2B_Clone(BLAKE2BContext *dest, BLAKE2BContext *src);
+
+/******************************************/
+/*
** Pseudo Random Number Generation. FIPS compliance desirable.
*/
diff --git a/security/nss/lib/freebl/blapii.h b/security/nss/lib/freebl/blapii.h
index b1be7bedf..bcf62e9f3 100644
--- a/security/nss/lib/freebl/blapii.h
+++ b/security/nss/lib/freebl/blapii.h
@@ -22,8 +22,10 @@ typedef void (*freeblDestroyFunc)(void *cx, PRBool freeit);
SEC_BEGIN_PROTOS
+#ifndef NSS_FIPS_DISABLED
SECStatus BL_FIPSEntryOK(PRBool freeblOnly);
PRBool BL_POSTRan(PRBool freeblOnly);
+#endif
#if defined(XP_UNIX) && !defined(NO_FORK_CHECK)
diff --git a/security/nss/lib/freebl/blapit.h b/security/nss/lib/freebl/blapit.h
index 2a17b5f46..c718c6f27 100644
--- a/security/nss/lib/freebl/blapit.h
+++ b/security/nss/lib/freebl/blapit.h
@@ -91,25 +91,27 @@ typedef int __BLAPI_DEPRECATED __attribute__((deprecated));
/*
* Number of bytes each hash algorithm produces
*/
-#define MD2_LENGTH 16 /* Bytes */
-#define MD5_LENGTH 16 /* Bytes */
-#define SHA1_LENGTH 20 /* Bytes */
-#define SHA256_LENGTH 32 /* bytes */
-#define SHA384_LENGTH 48 /* bytes */
-#define SHA512_LENGTH 64 /* bytes */
+#define MD2_LENGTH 16 /* Bytes */
+#define MD5_LENGTH 16 /* Bytes */
+#define SHA1_LENGTH 20 /* Bytes */
+#define SHA256_LENGTH 32 /* bytes */
+#define SHA384_LENGTH 48 /* bytes */
+#define SHA512_LENGTH 64 /* bytes */
+#define BLAKE2B512_LENGTH 64 /* Bytes */
#define HASH_LENGTH_MAX SHA512_LENGTH
/*
* Input block size for each hash algorithm.
*/
-#define MD2_BLOCK_LENGTH 64 /* bytes */
-#define MD5_BLOCK_LENGTH 64 /* bytes */
-#define SHA1_BLOCK_LENGTH 64 /* bytes */
-#define SHA224_BLOCK_LENGTH 64 /* bytes */
-#define SHA256_BLOCK_LENGTH 64 /* bytes */
-#define SHA384_BLOCK_LENGTH 128 /* bytes */
-#define SHA512_BLOCK_LENGTH 128 /* bytes */
+#define MD2_BLOCK_LENGTH 64 /* bytes */
+#define MD5_BLOCK_LENGTH 64 /* bytes */
+#define SHA1_BLOCK_LENGTH 64 /* bytes */
+#define SHA224_BLOCK_LENGTH 64 /* bytes */
+#define SHA256_BLOCK_LENGTH 64 /* bytes */
+#define SHA384_BLOCK_LENGTH 128 /* bytes */
+#define SHA512_BLOCK_LENGTH 128 /* bytes */
+#define BLAKE2B_BLOCK_LENGTH 128 /* Bytes */
#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH
#define AES_KEY_WRAP_IV_BYTES 8
@@ -127,6 +129,8 @@ typedef int __BLAPI_DEPRECATED __attribute__((deprecated));
#define NSS_FREEBL_DEFAULT_CHUNKSIZE 2048
+#define BLAKE2B_KEY_SIZE 64
+
/*
* These values come from the initial key size limits from the PKCS #11
* module. They may be arbitrarily adjusted to any value freebl supports.
@@ -213,6 +217,7 @@ struct SHA512ContextStr;
struct AESKeyWrapContextStr;
struct SEEDContextStr;
struct ChaCha20Poly1305ContextStr;
+struct Blake2bContextStr;
typedef struct DESContextStr DESContext;
typedef struct RC2ContextStr RC2Context;
@@ -232,6 +237,7 @@ typedef struct SHA512ContextStr SHA384Context;
typedef struct AESKeyWrapContextStr AESKeyWrapContext;
typedef struct SEEDContextStr SEEDContext;
typedef struct ChaCha20Poly1305ContextStr ChaCha20Poly1305Context;
+typedef struct Blake2bContextStr BLAKE2BContext;
/***************************************************************************
** RSA Public and Private Key structures
diff --git a/security/nss/lib/freebl/chacha20.c b/security/nss/lib/freebl/chacha20.c
index f55d1e670..15ed67b5b 100644
--- a/security/nss/lib/freebl/chacha20.c
+++ b/security/nss/lib/freebl/chacha20.c
@@ -7,113 +7,13 @@
#include <string.h>
#include <stdio.h>
-#include "prtypes.h"
-#include "secport.h"
#include "chacha20.h"
-
-#if defined(_MSC_VER)
-#pragma intrinsic(_lrotl)
-#define ROTL32(x, n) _lrotl(x, n)
-#else
-#define ROTL32(x, n) ((x << n) | (x >> ((8 * sizeof x) - n)))
-#endif
-
-#define ROTATE(v, c) ROTL32((v), (c))
-
-#define U32TO8_LITTLE(p, v) \
- { \
- (p)[0] = ((v)) & 0xff; \
- (p)[1] = ((v) >> 8) & 0xff; \
- (p)[2] = ((v) >> 16) & 0xff; \
- (p)[3] = ((v) >> 24) & 0xff; \
- }
-#define U8TO32_LITTLE(p) \
- (((PRUint32)((p)[0])) | ((PRUint32)((p)[1]) << 8) | \
- ((PRUint32)((p)[2]) << 16) | ((PRUint32)((p)[3]) << 24))
-
-#define QUARTERROUND(x, a, b, c, d) \
- x[a] = x[a] + x[b]; \
- x[d] = ROTATE(x[d] ^ x[a], 16); \
- x[c] = x[c] + x[d]; \
- x[b] = ROTATE(x[b] ^ x[c], 12); \
- x[a] = x[a] + x[b]; \
- x[d] = ROTATE(x[d] ^ x[a], 8); \
- x[c] = x[c] + x[d]; \
- x[b] = ROTATE(x[b] ^ x[c], 7);
-
-static void
-ChaChaCore(unsigned char output[64], const PRUint32 input[16], int num_rounds)
-{
- PRUint32 x[16];
- int i;
-
- PORT_Memcpy(x, input, sizeof(PRUint32) * 16);
- for (i = num_rounds; i > 0; i -= 2) {
- QUARTERROUND(x, 0, 4, 8, 12)
- QUARTERROUND(x, 1, 5, 9, 13)
- QUARTERROUND(x, 2, 6, 10, 14)
- QUARTERROUND(x, 3, 7, 11, 15)
- QUARTERROUND(x, 0, 5, 10, 15)
- QUARTERROUND(x, 1, 6, 11, 12)
- QUARTERROUND(x, 2, 7, 8, 13)
- QUARTERROUND(x, 3, 4, 9, 14)
- }
-
- for (i = 0; i < 16; ++i) {
- x[i] = x[i] + input[i];
- }
- for (i = 0; i < 16; ++i) {
- U32TO8_LITTLE(output + 4 * i, x[i]);
- }
-}
-
-static const unsigned char sigma[16] = "expand 32-byte k";
+#include "verified/Hacl_Chacha20.h"
void
ChaCha20XOR(unsigned char *out, const unsigned char *in, unsigned int inLen,
const unsigned char key[32], const unsigned char nonce[12],
uint32_t counter)
{
- unsigned char block[64];
- PRUint32 input[16];
- unsigned int i;
-
- input[4] = U8TO32_LITTLE(key + 0);
- input[5] = U8TO32_LITTLE(key + 4);
- input[6] = U8TO32_LITTLE(key + 8);
- input[7] = U8TO32_LITTLE(key + 12);
-
- input[8] = U8TO32_LITTLE(key + 16);
- input[9] = U8TO32_LITTLE(key + 20);
- input[10] = U8TO32_LITTLE(key + 24);
- input[11] = U8TO32_LITTLE(key + 28);
-
- input[0] = U8TO32_LITTLE(sigma + 0);
- input[1] = U8TO32_LITTLE(sigma + 4);
- input[2] = U8TO32_LITTLE(sigma + 8);
- input[3] = U8TO32_LITTLE(sigma + 12);
-
- input[12] = counter;
- input[13] = U8TO32_LITTLE(nonce + 0);
- input[14] = U8TO32_LITTLE(nonce + 4);
- input[15] = U8TO32_LITTLE(nonce + 8);
-
- while (inLen >= 64) {
- ChaChaCore(block, input, 20);
- for (i = 0; i < 64; i++) {
- out[i] = in[i] ^ block[i];
- }
-
- input[12]++;
- inLen -= 64;
- in += 64;
- out += 64;
- }
-
- if (inLen > 0) {
- ChaChaCore(block, input, 20);
- for (i = 0; i < inLen; i++) {
- out[i] = in[i] ^ block[i];
- }
- }
+ Hacl_Chacha20_chacha20(out, (uint8_t *)in, inLen, (uint8_t *)key, (uint8_t *)nonce, counter);
}
diff --git a/security/nss/lib/freebl/chacha20poly1305.c b/security/nss/lib/freebl/chacha20poly1305.c
index cd265e1ff..991fa0ca3 100644
--- a/security/nss/lib/freebl/chacha20poly1305.c
+++ b/security/nss/lib/freebl/chacha20poly1305.c
@@ -14,7 +14,11 @@
#include "blapit.h"
#ifndef NSS_DISABLE_CHACHAPOLY
+#if defined(HAVE_INT128_SUPPORT) && (defined(NSS_X86_OR_X64) || defined(__aarch64__))
+#include "verified/Hacl_Poly1305_64.h"
+#else
#include "poly1305.h"
+#endif
#include "chacha20.h"
#include "chacha20poly1305.h"
#endif
@@ -22,6 +26,49 @@
/* Poly1305Do writes the Poly1305 authenticator of the given additional data
* and ciphertext to |out|. */
#ifndef NSS_DISABLE_CHACHAPOLY
+
+#if defined(HAVE_INT128_SUPPORT) && (defined(NSS_X86_OR_X64) || defined(__aarch64__))
+
+static void
+Poly1305PadUpdate(Hacl_Impl_Poly1305_64_State_poly1305_state state, unsigned char *block, const unsigned char *p, const unsigned int pLen)
+{
+ unsigned int pRemLen = pLen % 16;
+ Hacl_Poly1305_64_update(state, (uint8_t *)p, (pLen / 16));
+ if (pRemLen > 0) {
+ memcpy(block, p + (pLen - pRemLen), pRemLen);
+ Hacl_Poly1305_64_update(state, block, 1);
+ }
+}
+
+static void
+Poly1305Do(unsigned char *out, const unsigned char *ad, unsigned int adLen,
+ const unsigned char *ciphertext, unsigned int ciphertextLen,
+ const unsigned char key[32])
+{
+ uint64_t tmp1[6U] = { 0U };
+ Hacl_Impl_Poly1305_64_State_poly1305_state state = Hacl_Poly1305_64_mk_state(tmp1, tmp1 + 3);
+
+ unsigned char block[16] = { 0 };
+ Hacl_Poly1305_64_init(state, (uint8_t *)key);
+
+ Poly1305PadUpdate(state, block, ad, adLen);
+ memset(block, 0, 16);
+ Poly1305PadUpdate(state, block, ciphertext, ciphertextLen);
+
+ unsigned int i;
+ unsigned int j;
+ for (i = 0, j = adLen; i < 8; i++, j >>= 8) {
+ block[i] = j;
+ }
+ for (i = 8, j = ciphertextLen; i < 16; i++, j >>= 8) {
+ block[i] = j;
+ }
+
+ Hacl_Poly1305_64_update(state, block, 1);
+ Hacl_Poly1305_64_finish(state, out, (uint8_t *)(key + 16));
+}
+#else
+
static void
Poly1305Do(unsigned char *out, const unsigned char *ad, unsigned int adLen,
const unsigned char *ciphertext, unsigned int ciphertextLen,
@@ -56,7 +103,9 @@ Poly1305Do(unsigned char *out, const unsigned char *ad, unsigned int adLen,
Poly1305Update(&state, lengthBytes, sizeof(lengthBytes));
Poly1305Finish(&state, out);
}
-#endif
+
+#endif /* HAVE_INT128_SUPPORT */
+#endif /* NSS_DISABLE_CHACHAPOLY */
SECStatus
ChaCha20Poly1305_InitContext(ChaCha20Poly1305Context *ctx,
diff --git a/security/nss/lib/freebl/config.mk b/security/nss/lib/freebl/config.mk
index 918a66363..f15077096 100644
--- a/security/nss/lib/freebl/config.mk
+++ b/security/nss/lib/freebl/config.mk
@@ -90,7 +90,12 @@ EXTRA_SHARED_LIBS += \
endif
endif
+ifeq ($(OS_ARCH), Linux)
+CFLAGS += -std=gnu99
+endif
+
ifeq ($(OS_ARCH), Darwin)
+CFLAGS += -std=gnu99
EXTRA_SHARED_LIBS += -dylib_file @executable_path/libplc4.dylib:$(DIST)/lib/libplc4.dylib -dylib_file @executable_path/libplds4.dylib:$(DIST)/lib/libplds4.dylib
endif
diff --git a/security/nss/lib/freebl/crypto_primitives.c b/security/nss/lib/freebl/crypto_primitives.c
new file mode 100644
index 000000000..49c8ca5ca
--- /dev/null
+++ b/security/nss/lib/freebl/crypto_primitives.c
@@ -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/. */
+
+#ifdef FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+
+/* This file holds useful functions and macros for crypto code. */
+#include "crypto_primitives.h"
+
+/*
+ * FREEBL_HTONLL(x): swap bytes in a 64-bit integer.
+ */
+#if defined(__GNUC__) && (defined(__x86_64__) || defined(__x86_64))
+
+__inline__ PRUint64
+swap8b(PRUint64 value)
+{
+ __asm__("bswapq %0"
+ : "+r"(value));
+ return (value);
+}
+
+#elif !defined(_MSC_VER)
+
+PRUint64
+swap8b(PRUint64 x)
+{
+ PRUint64 t1 = x;
+ t1 = ((t1 & SHA_MASK8) << 8) | ((t1 >> 8) & SHA_MASK8);
+ t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16);
+ return (t1 >> 32) | (t1 << 32);
+}
+
+#endif
diff --git a/security/nss/lib/freebl/crypto_primitives.h b/security/nss/lib/freebl/crypto_primitives.h
new file mode 100644
index 000000000..f19601f4b
--- /dev/null
+++ b/security/nss/lib/freebl/crypto_primitives.h
@@ -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/. */
+
+/* This file holds useful functions and macros for crypto code. */
+
+#ifdef FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+
+#include <stdlib.h>
+#include "prtypes.h"
+
+/* Unfortunately this isn't always set when it should be. */
+#if defined(HAVE_LONG_LONG)
+
+/*
+ * ROTR64/ROTL64(x, n): rotate a 64-bit integer x by n bites to the right/left.
+ */
+#if defined(_MSC_VER)
+#pragma intrinsic(_rotr64, _rotl64)
+#define ROTR64(x, n) _rotr64((x), (n))
+#define ROTL64(x, n) _rotl64((x), (n))
+#else
+#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
+#define ROTL64(x, n) (((x) << (n)) | ((x) >> (64 - (n))))
+#endif
+
+/*
+ * FREEBL_HTONLL(x): swap bytes in a 64-bit integer.
+ */
+#if defined(_MSC_VER)
+
+#pragma intrinsic(_byteswap_uint64)
+#define FREEBL_HTONLL(x) _byteswap_uint64(x)
+
+#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__x86_64))
+
+PRUint64 swap8b(PRUint64 value);
+#define FREEBL_HTONLL(x) swap8b(x)
+
+#else
+
+#define SHA_MASK16 0x0000FFFF0000FFFFULL
+#define SHA_MASK8 0x00FF00FF00FF00FFULL
+PRUint64 swap8b(PRUint64 x);
+#define FREEBL_HTONLL(x) swap8b(x)
+
+#endif /* _MSC_VER */
+
+#endif /* HAVE_LONG_LONG */ \ No newline at end of file
diff --git a/security/nss/lib/freebl/det_rng.c b/security/nss/lib/freebl/det_rng.c
index 04fce30e8..53d48bc7c 100644
--- a/security/nss/lib/freebl/det_rng.c
+++ b/security/nss/lib/freebl/det_rng.c
@@ -8,19 +8,22 @@
#include "nssilock.h"
#include "seccomon.h"
#include "secerr.h"
+#include "prinit.h"
#define GLOBAL_BYTES_SIZE 100
static PRUint8 globalBytes[GLOBAL_BYTES_SIZE];
static unsigned long globalNumCalls = 0;
static PZLock *rng_lock = NULL;
+static PRCallOnceType coRNGInit;
+static const PRCallOnceType pristineCallOnce;
-SECStatus
-RNG_RNGInit(void)
+static PRStatus
+rng_init(void)
{
rng_lock = PZ_NewLock(nssILockOther);
if (!rng_lock) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
+ return PR_FAILURE;
}
/* --- LOCKED --- */
PZ_Lock(rng_lock);
@@ -28,6 +31,17 @@ RNG_RNGInit(void)
PZ_Unlock(rng_lock);
/* --- UNLOCKED --- */
+ return PR_SUCCESS;
+}
+
+SECStatus
+RNG_RNGInit(void)
+{
+ /* Allow only one call to initialize the context */
+ if (PR_CallOnce(&coRNGInit, rng_init) != PR_SUCCESS) {
+ return SECFailure;
+ }
+
return SECSuccess;
}
@@ -97,8 +111,11 @@ RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
void
RNG_RNGShutdown(void)
{
- PZ_DestroyLock(rng_lock);
- rng_lock = NULL;
+ if (rng_lock) {
+ PZ_DestroyLock(rng_lock);
+ rng_lock = NULL;
+ }
+ coRNGInit = pristineCallOnce;
}
/* Test functions are not implemented! */
diff --git a/security/nss/lib/freebl/ec.c b/security/nss/lib/freebl/ec.c
index 669c9b147..b28815ade 100644
--- a/security/nss/lib/freebl/ec.c
+++ b/security/nss/lib/freebl/ec.c
@@ -15,8 +15,6 @@
#include "ec.h"
#include "ecl.h"
-#ifndef NSS_DISABLE_ECC
-
static const ECMethod kMethods[] = {
{ ECCurve25519,
ec_Curve25519_pt_mul,
@@ -183,7 +181,6 @@ cleanup:
return rv;
}
-#endif /* NSS_DISABLE_ECC */
/* Generates a new EC key pair. The private key is a supplied
* value and the public key is the result of performing a scalar
@@ -194,7 +191,6 @@ ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey,
const unsigned char *privKeyBytes, int privKeyLen)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
PLArenaPool *arena;
ECPrivateKey *key;
mp_int k;
@@ -309,9 +305,6 @@ cleanup:
printf("ec_NewKey returning %s\n",
(rv == SECSuccess) ? "success" : "failure");
#endif
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
@@ -326,15 +319,10 @@ EC_NewKeyFromSeed(ECParams *ecParams, ECPrivateKey **privKey,
const unsigned char *seed, int seedlen)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
rv = ec_NewKey(ecParams, privKey, seed, seedlen);
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
-#ifndef NSS_DISABLE_ECC
/* Generate a random private key using the algorithm A.4.1 of ANSI X9.62,
* modified a la FIPS 186-2 Change Notice 1 to eliminate the bias in the
* random number generator.
@@ -391,7 +379,6 @@ cleanup:
}
return privKeyBytes;
}
-#endif /* NSS_DISABLE_ECC */
/* Generates a new EC key pair. The private key is a random value and
* the public key is the result of performing a scalar point multiplication
@@ -401,7 +388,6 @@ SECStatus
EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
int len;
unsigned char *privKeyBytes = NULL;
@@ -425,9 +411,6 @@ cleanup:
printf("EC_NewKey returning %s\n",
(rv == SECSuccess) ? "success" : "failure");
#endif
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
@@ -441,7 +424,6 @@ cleanup:
SECStatus
EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue)
{
-#ifndef NSS_DISABLE_ECC
mp_int Px, Py;
ECGroup *group = NULL;
SECStatus rv = SECFailure;
@@ -525,10 +507,6 @@ cleanup:
rv = SECFailure;
}
return rv;
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
- return SECFailure;
-#endif /* NSS_DISABLE_ECC */
}
/*
@@ -549,7 +527,6 @@ ECDH_Derive(SECItem *publicValue,
SECItem *derivedSecret)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
unsigned int len = 0;
SECItem pointQ = { siBuffer, NULL, 0 };
mp_int k; /* to hold the private value */
@@ -589,7 +566,11 @@ ECDH_Derive(SECItem *publicValue,
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
return SECFailure;
}
- return method->mul(derivedSecret, privateValue, publicValue);
+ rv = method->mul(derivedSecret, privateValue, publicValue);
+ if (rv != SECSuccess) {
+ SECITEM_ZfreeItem(derivedSecret, PR_FALSE);
+ }
+ return rv;
}
/*
@@ -654,9 +635,6 @@ cleanup:
if (pointQ.data) {
PORT_ZFree(pointQ.data, pointQ.len);
}
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
@@ -670,7 +648,6 @@ ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature,
const SECItem *digest, const unsigned char *kb, const int kblen)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
mp_int x1;
mp_int d, k; /* private key, random integer */
mp_int r, s; /* tuple (r, s) is the signature */
@@ -899,9 +876,6 @@ cleanup:
printf("ECDSA signing with seed %s\n",
(rv == SECSuccess) ? "succeeded" : "failed");
#endif
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
@@ -914,7 +888,6 @@ SECStatus
ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
int len;
unsigned char *kBytes = NULL;
@@ -941,9 +914,6 @@ cleanup:
printf("ECDSA signing %s\n",
(rv == SECSuccess) ? "succeeded" : "failed");
#endif
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
@@ -961,7 +931,6 @@ ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature,
const SECItem *digest)
{
SECStatus rv = SECFailure;
-#ifndef NSS_DISABLE_ECC
mp_int r_, s_; /* tuple (r', s') is received signature) */
mp_int c, u1, u2, v; /* intermediate values used in verification */
mp_int x1;
@@ -1161,9 +1130,6 @@ cleanup:
printf("ECDSA verification %s\n",
(rv == SECSuccess) ? "succeeded" : "failed");
#endif
-#else
- PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-#endif /* NSS_DISABLE_ECC */
return rv;
}
diff --git a/security/nss/lib/freebl/ecdecode.c b/security/nss/lib/freebl/ecdecode.c
index 54b3e111b..652ad42d5 100644
--- a/security/nss/lib/freebl/ecdecode.c
+++ b/security/nss/lib/freebl/ecdecode.c
@@ -2,8 +2,6 @@
* License, v. 2.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 NSS_DISABLE_ECC
-
#ifdef FREEBL_NO_DEPEND
#include "stubs.h"
#endif
@@ -252,5 +250,3 @@ EC_GetPointSize(const ECParams *params)
}
return curveParams->pointSize - 1;
}
-
-#endif /* NSS_DISABLE_ECC */
diff --git a/security/nss/lib/freebl/ecl/curve25519_64.c b/security/nss/lib/freebl/ecl/curve25519_64.c
index 65f6bd41b..a2e4296bb 100644
--- a/security/nss/lib/freebl/ecl/curve25519_64.c
+++ b/security/nss/lib/freebl/ecl/curve25519_64.c
@@ -2,513 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/*
- * Derived from public domain C code by Adan Langley and Daniel J. Bernstein
- */
-
-#include "uint128.h"
-
#include "ecl-priv.h"
-#include "mpi.h"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-typedef uint8_t u8;
-typedef uint64_t felem;
-
-/* Sum two numbers: output += in */
-static void
-fsum(felem *output, const felem *in)
-{
- unsigned i;
- for (i = 0; i < 5; ++i) {
- output[i] += in[i];
- }
-}
-
-/* Find the difference of two numbers: output = in - output
- * (note the order of the arguments!)
- */
-static void
-fdifference_backwards(felem *ioutput, const felem *iin)
-{
- static const int64_t twotothe51 = ((int64_t)1l << 51);
- const int64_t *in = (const int64_t *)iin;
- int64_t *out = (int64_t *)ioutput;
-
- out[0] = in[0] - out[0];
- out[1] = in[1] - out[1];
- out[2] = in[2] - out[2];
- out[3] = in[3] - out[3];
- out[4] = in[4] - out[4];
-
- // An arithmetic shift right of 63 places turns a positive number to 0 and a
- // negative number to all 1's. This gives us a bitmask that lets us avoid
- // side-channel prone branches.
- int64_t t;
-
-#define NEGCHAIN(a, b) \
- t = out[a] >> 63; \
- out[a] += twotothe51 & t; \
- out[b] -= 1 & t;
-
-#define NEGCHAIN19(a, b) \
- t = out[a] >> 63; \
- out[a] += twotothe51 & t; \
- out[b] -= 19 & t;
-
- NEGCHAIN(0, 1);
- NEGCHAIN(1, 2);
- NEGCHAIN(2, 3);
- NEGCHAIN(3, 4);
- NEGCHAIN19(4, 0);
- NEGCHAIN(0, 1);
- NEGCHAIN(1, 2);
- NEGCHAIN(2, 3);
- NEGCHAIN(3, 4);
-}
-
-/* Multiply a number by a scalar: output = in * scalar */
-static void
-fscalar_product(felem *output, const felem *in,
- const felem scalar)
-{
- uint128_t tmp, tmp2;
-
- tmp = mul6464(in[0], scalar);
- output[0] = mask51(tmp);
-
- tmp2 = mul6464(in[1], scalar);
- tmp = add128(tmp2, rshift128(tmp, 51));
- output[1] = mask51(tmp);
-
- tmp2 = mul6464(in[2], scalar);
- tmp = add128(tmp2, rshift128(tmp, 51));
- output[2] = mask51(tmp);
-
- tmp2 = mul6464(in[3], scalar);
- tmp = add128(tmp2, rshift128(tmp, 51));
- output[3] = mask51(tmp);
-
- tmp2 = mul6464(in[4], scalar);
- tmp = add128(tmp2, rshift128(tmp, 51));
- output[4] = mask51(tmp);
-
- output[0] += mask_lower(rshift128(tmp, 51)) * 19;
-}
-
-/* Multiply two numbers: output = in2 * in
- *
- * output must be distinct to both inputs. The inputs are reduced coefficient
- * form, the output is not.
- */
-static void
-fmul(felem *output, const felem *in2, const felem *in)
-{
- uint128_t t0, t1, t2, t3, t4, t5, t6, t7, t8;
-
- t0 = mul6464(in[0], in2[0]);
- t1 = add128(mul6464(in[1], in2[0]), mul6464(in[0], in2[1]));
- t2 = add128(add128(mul6464(in[0], in2[2]),
- mul6464(in[2], in2[0])),
- mul6464(in[1], in2[1]));
- t3 = add128(add128(add128(mul6464(in[0], in2[3]),
- mul6464(in[3], in2[0])),
- mul6464(in[1], in2[2])),
- mul6464(in[2], in2[1]));
- t4 = add128(add128(add128(add128(mul6464(in[0], in2[4]),
- mul6464(in[4], in2[0])),
- mul6464(in[3], in2[1])),
- mul6464(in[1], in2[3])),
- mul6464(in[2], in2[2]));
- t5 = add128(add128(add128(mul6464(in[4], in2[1]),
- mul6464(in[1], in2[4])),
- mul6464(in[2], in2[3])),
- mul6464(in[3], in2[2]));
- t6 = add128(add128(mul6464(in[4], in2[2]),
- mul6464(in[2], in2[4])),
- mul6464(in[3], in2[3]));
- t7 = add128(mul6464(in[3], in2[4]), mul6464(in[4], in2[3]));
- t8 = mul6464(in[4], in2[4]);
-
- t0 = add128(t0, mul12819(t5));
- t1 = add128(t1, mul12819(t6));
- t2 = add128(t2, mul12819(t7));
- t3 = add128(t3, mul12819(t8));
-
- t1 = add128(t1, rshift128(t0, 51));
- t0 = mask51full(t0);
- t2 = add128(t2, rshift128(t1, 51));
- t1 = mask51full(t1);
- t3 = add128(t3, rshift128(t2, 51));
- t4 = add128(t4, rshift128(t3, 51));
- t0 = add128(t0, mul12819(rshift128(t4, 51)));
- t1 = add128(t1, rshift128(t0, 51));
- t2 = mask51full(t2);
- t2 = add128(t2, rshift128(t1, 51));
-
- output[0] = mask51(t0);
- output[1] = mask51(t1);
- output[2] = mask_lower(t2);
- output[3] = mask51(t3);
- output[4] = mask51(t4);
-}
-
-static void
-fsquare(felem *output, const felem *in)
-{
- uint128_t t0, t1, t2, t3, t4, t5, t6, t7, t8;
-
- t0 = mul6464(in[0], in[0]);
- t1 = lshift128(mul6464(in[0], in[1]), 1);
- t2 = add128(lshift128(mul6464(in[0], in[2]), 1),
- mul6464(in[1], in[1]));
- t3 = add128(lshift128(mul6464(in[0], in[3]), 1),
- lshift128(mul6464(in[1], in[2]), 1));
- t4 = add128(add128(lshift128(mul6464(in[0], in[4]), 1),
- lshift128(mul6464(in[3], in[1]), 1)),
- mul6464(in[2], in[2]));
- t5 = add128(lshift128(mul6464(in[4], in[1]), 1),
- lshift128(mul6464(in[2], in[3]), 1));
- t6 = add128(lshift128(mul6464(in[4], in[2]), 1),
- mul6464(in[3], in[3]));
- t7 = lshift128(mul6464(in[3], in[4]), 1);
- t8 = mul6464(in[4], in[4]);
-
- t0 = add128(t0, mul12819(t5));
- t1 = add128(t1, mul12819(t6));
- t2 = add128(t2, mul12819(t7));
- t3 = add128(t3, mul12819(t8));
-
- t1 = add128(t1, rshift128(t0, 51));
- t0 = mask51full(t0);
- t2 = add128(t2, rshift128(t1, 51));
- t1 = mask51full(t1);
- t3 = add128(t3, rshift128(t2, 51));
- t4 = add128(t4, rshift128(t3, 51));
- t0 = add128(t0, mul12819(rshift128(t4, 51)));
- t1 = add128(t1, rshift128(t0, 51));
-
- output[0] = mask51(t0);
- output[1] = mask_lower(t1);
- output[2] = mask51(t2);
- output[3] = mask51(t3);
- output[4] = mask51(t4);
-}
-
-/* Take a 32-byte number and expand it into polynomial form */
-static void NO_SANITIZE_ALIGNMENT
-fexpand(felem *output, const u8 *in)
-{
- output[0] = *((const uint64_t *)(in)) & MASK51;
- output[1] = (*((const uint64_t *)(in + 6)) >> 3) & MASK51;
- output[2] = (*((const uint64_t *)(in + 12)) >> 6) & MASK51;
- output[3] = (*((const uint64_t *)(in + 19)) >> 1) & MASK51;
- output[4] = (*((const uint64_t *)(in + 24)) >> 12) & MASK51;
-}
-
-/* Take a fully reduced polynomial form number and contract it into a
- * 32-byte array
- */
-static void
-fcontract(u8 *output, const felem *input)
-{
- uint128_t t0 = init128x(input[0]);
- uint128_t t1 = init128x(input[1]);
- uint128_t t2 = init128x(input[2]);
- uint128_t t3 = init128x(input[3]);
- uint128_t t4 = init128x(input[4]);
- uint128_t tmp = init128x(19);
-
- t1 = add128(t1, rshift128(t0, 51));
- t0 = mask51full(t0);
- t2 = add128(t2, rshift128(t1, 51));
- t1 = mask51full(t1);
- t3 = add128(t3, rshift128(t2, 51));
- t2 = mask51full(t2);
- t4 = add128(t4, rshift128(t3, 51));
- t3 = mask51full(t3);
- t0 = add128(t0, mul12819(rshift128(t4, 51)));
- t4 = mask51full(t4);
-
- t1 = add128(t1, rshift128(t0, 51));
- t0 = mask51full(t0);
- t2 = add128(t2, rshift128(t1, 51));
- t1 = mask51full(t1);
- t3 = add128(t3, rshift128(t2, 51));
- t2 = mask51full(t2);
- t4 = add128(t4, rshift128(t3, 51));
- t3 = mask51full(t3);
- t0 = add128(t0, mul12819(rshift128(t4, 51)));
- t4 = mask51full(t4);
-
- /* now t is between 0 and 2^255-1, properly carried. */
- /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
-
- t0 = add128(t0, tmp);
-
- t1 = add128(t1, rshift128(t0, 51));
- t0 = mask51full(t0);
- t2 = add128(t2, rshift128(t1, 51));
- t1 = mask51full(t1);
- t3 = add128(t3, rshift128(t2, 51));
- t2 = mask51full(t2);
- t4 = add128(t4, rshift128(t3, 51));
- t3 = mask51full(t3);
- t0 = add128(t0, mul12819(rshift128(t4, 51)));
- t4 = mask51full(t4);
-
- /* now between 19 and 2^255-1 in both cases, and offset by 19. */
-
- t0 = add128(t0, init128x(0x8000000000000 - 19));
- tmp = init128x(0x8000000000000 - 1);
- t1 = add128(t1, tmp);
- t2 = add128(t2, tmp);
- t3 = add128(t3, tmp);
- t4 = add128(t4, tmp);
-
- /* now between 2^255 and 2^256-20, and offset by 2^255. */
-
- t1 = add128(t1, rshift128(t0, 51));
- t0 = mask51full(t0);
- t2 = add128(t2, rshift128(t1, 51));
- t1 = mask51full(t1);
- t3 = add128(t3, rshift128(t2, 51));
- t2 = mask51full(t2);
- t4 = add128(t4, rshift128(t3, 51));
- t3 = mask51full(t3);
- t4 = mask51full(t4);
-
- *((uint64_t *)(output)) = mask_lower(t0) | mask_lower(t1) << 51;
- *((uint64_t *)(output + 8)) = (mask_lower(t1) >> 13) | (mask_lower(t2) << 38);
- *((uint64_t *)(output + 16)) = (mask_lower(t2) >> 26) | (mask_lower(t3) << 25);
- *((uint64_t *)(output + 24)) = (mask_lower(t3) >> 39) | (mask_lower(t4) << 12);
-}
-
-/* Input: Q, Q', Q-Q'
- * Output: 2Q, Q+Q'
- *
- * x2 z3: long form
- * x3 z3: long form
- * x z: short form, destroyed
- * xprime zprime: short form, destroyed
- * qmqp: short form, preserved
- */
-static void
-fmonty(felem *x2, felem *z2, /* output 2Q */
- felem *x3, felem *z3, /* output Q + Q' */
- felem *x, felem *z, /* input Q */
- felem *xprime, felem *zprime, /* input Q' */
- const felem *qmqp /* input Q - Q' */)
-{
- felem origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5], zzprime[5],
- zzzprime[5];
-
- memcpy(origx, x, 5 * sizeof(felem));
- fsum(x, z);
- fdifference_backwards(z, origx); // does x - z
-
- memcpy(origxprime, xprime, sizeof(felem) * 5);
- fsum(xprime, zprime);
- fdifference_backwards(zprime, origxprime);
- fmul(xxprime, xprime, z);
- fmul(zzprime, x, zprime);
- memcpy(origxprime, xxprime, sizeof(felem) * 5);
- fsum(xxprime, zzprime);
- fdifference_backwards(zzprime, origxprime);
- fsquare(x3, xxprime);
- fsquare(zzzprime, zzprime);
- fmul(z3, zzzprime, qmqp);
-
- fsquare(xx, x);
- fsquare(zz, z);
- fmul(x2, xx, zz);
- fdifference_backwards(zz, xx); // does zz = xx - zz
- fscalar_product(zzz, zz, 121665);
- fsum(zzz, xx);
- fmul(z2, zz, zzz);
-}
-
-// -----------------------------------------------------------------------------
-// Maybe swap the contents of two felem arrays (@a and @b), each @len elements
-// long. Perform the swap iff @swap is non-zero.
-//
-// This function performs the swap without leaking any side-channel
-// information.
-// -----------------------------------------------------------------------------
-static void
-swap_conditional(felem *a, felem *b, unsigned len, felem iswap)
-{
- unsigned i;
- const felem swap = 1 + ~iswap;
-
- for (i = 0; i < len; ++i) {
- const felem x = swap & (a[i] ^ b[i]);
- a[i] ^= x;
- b[i] ^= x;
- }
-}
-
-/* Calculates nQ where Q is the x-coordinate of a point on the curve
- *
- * resultx/resultz: the x coordinate of the resulting curve point (short form)
- * n: a 32-byte number
- * q: a point of the curve (short form)
- */
-static void
-cmult(felem *resultx, felem *resultz, const u8 *n, const felem *q)
-{
- felem a[5] = { 0 }, b[5] = { 1 }, c[5] = { 1 }, d[5] = { 0 };
- felem *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
- felem e[5] = { 0 }, f[5] = { 1 }, g[5] = { 0 }, h[5] = { 1 };
- felem *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
-
- unsigned i, j;
-
- memcpy(nqpqx, q, sizeof(felem) * 5);
-
- for (i = 0; i < 32; ++i) {
- u8 byte = n[31 - i];
- for (j = 0; j < 8; ++j) {
- const felem bit = byte >> 7;
-
- swap_conditional(nqx, nqpqx, 5, bit);
- swap_conditional(nqz, nqpqz, 5, bit);
- fmonty(nqx2, nqz2, nqpqx2, nqpqz2, nqx, nqz, nqpqx, nqpqz, q);
- swap_conditional(nqx2, nqpqx2, 5, bit);
- swap_conditional(nqz2, nqpqz2, 5, bit);
-
- t = nqx;
- nqx = nqx2;
- nqx2 = t;
- t = nqz;
- nqz = nqz2;
- nqz2 = t;
- t = nqpqx;
- nqpqx = nqpqx2;
- nqpqx2 = t;
- t = nqpqz;
- nqpqz = nqpqz2;
- nqpqz2 = t;
-
- byte <<= 1;
- }
- }
-
- memcpy(resultx, nqx, sizeof(felem) * 5);
- memcpy(resultz, nqz, sizeof(felem) * 5);
-}
-
-// -----------------------------------------------------------------------------
-// Shamelessly copied from djb's code
-// -----------------------------------------------------------------------------
-static void
-crecip(felem *out, const felem *z)
-{
- felem z2[5];
- felem z9[5];
- felem z11[5];
- felem z2_5_0[5];
- felem z2_10_0[5];
- felem z2_20_0[5];
- felem z2_50_0[5];
- felem z2_100_0[5];
- felem t0[5];
- felem t1[5];
- int i;
-
- /* 2 */ fsquare(z2, z);
- /* 4 */ fsquare(t1, z2);
- /* 8 */ fsquare(t0, t1);
- /* 9 */ fmul(z9, t0, z);
- /* 11 */ fmul(z11, z9, z2);
- /* 22 */ fsquare(t0, z11);
- /* 2^5 - 2^0 = 31 */ fmul(z2_5_0, t0, z9);
-
- /* 2^6 - 2^1 */ fsquare(t0, z2_5_0);
- /* 2^7 - 2^2 */ fsquare(t1, t0);
- /* 2^8 - 2^3 */ fsquare(t0, t1);
- /* 2^9 - 2^4 */ fsquare(t1, t0);
- /* 2^10 - 2^5 */ fsquare(t0, t1);
- /* 2^10 - 2^0 */ fmul(z2_10_0, t0, z2_5_0);
-
- /* 2^11 - 2^1 */ fsquare(t0, z2_10_0);
- /* 2^12 - 2^2 */ fsquare(t1, t0);
- /* 2^20 - 2^10 */ for (i = 2; i < 10; i += 2) {
- fsquare(t0, t1);
- fsquare(t1, t0);
- }
- /* 2^20 - 2^0 */ fmul(z2_20_0, t1, z2_10_0);
-
- /* 2^21 - 2^1 */ fsquare(t0, z2_20_0);
- /* 2^22 - 2^2 */ fsquare(t1, t0);
- /* 2^40 - 2^20 */ for (i = 2; i < 20; i += 2) {
- fsquare(t0, t1);
- fsquare(t1, t0);
- }
- /* 2^40 - 2^0 */ fmul(t0, t1, z2_20_0);
-
- /* 2^41 - 2^1 */ fsquare(t1, t0);
- /* 2^42 - 2^2 */ fsquare(t0, t1);
- /* 2^50 - 2^10 */ for (i = 2; i < 10; i += 2) {
- fsquare(t1, t0);
- fsquare(t0, t1);
- }
- /* 2^50 - 2^0 */ fmul(z2_50_0, t0, z2_10_0);
-
- /* 2^51 - 2^1 */ fsquare(t0, z2_50_0);
- /* 2^52 - 2^2 */ fsquare(t1, t0);
- /* 2^100 - 2^50 */ for (i = 2; i < 50; i += 2) {
- fsquare(t0, t1);
- fsquare(t1, t0);
- }
- /* 2^100 - 2^0 */ fmul(z2_100_0, t1, z2_50_0);
-
- /* 2^101 - 2^1 */ fsquare(t1, z2_100_0);
- /* 2^102 - 2^2 */ fsquare(t0, t1);
- /* 2^200 - 2^100 */ for (i = 2; i < 100; i += 2) {
- fsquare(t1, t0);
- fsquare(t0, t1);
- }
- /* 2^200 - 2^0 */ fmul(t1, t0, z2_100_0);
-
- /* 2^201 - 2^1 */ fsquare(t0, t1);
- /* 2^202 - 2^2 */ fsquare(t1, t0);
- /* 2^250 - 2^50 */ for (i = 2; i < 50; i += 2) {
- fsquare(t0, t1);
- fsquare(t1, t0);
- }
- /* 2^250 - 2^0 */ fmul(t0, t1, z2_50_0);
-
- /* 2^251 - 2^1 */ fsquare(t1, t0);
- /* 2^252 - 2^2 */ fsquare(t0, t1);
- /* 2^253 - 2^3 */ fsquare(t1, t0);
- /* 2^254 - 2^4 */ fsquare(t0, t1);
- /* 2^255 - 2^5 */ fsquare(t1, t0);
- /* 2^255 - 21 */ fmul(out, t1, z11);
-}
+#include "../verified/Hacl_Curve25519.h"
SECStatus
-ec_Curve25519_mul(uint8_t *mypublic, const uint8_t *secret,
- const uint8_t *basepoint)
+ec_Curve25519_mul(uint8_t *mypublic, const uint8_t *secret, const uint8_t *basepoint)
{
- felem bp[5], x[5], z[5], zmone[5];
- uint8_t e[32];
- int i;
-
- for (i = 0; i < 32; ++i) {
- e[i] = secret[i];
- }
- e[0] &= 248;
- e[31] &= 127;
- e[31] |= 64;
- fexpand(bp, basepoint);
- cmult(x, z, e, bp);
- crecip(zmone, z);
- fmul(z, x, zmone);
- fcontract(mypublic, z);
-
+ // Note: this cast is safe because HaCl* state has a post-condition that only "mypublic" changed.
+ Hacl_Curve25519_crypto_scalarmult(mypublic, (uint8_t *)secret, (uint8_t *)basepoint);
return 0;
}
diff --git a/security/nss/lib/freebl/ecl/ecp_25519.c b/security/nss/lib/freebl/ecl/ecp_25519.c
index 1e7875fff..38bd34c50 100644
--- a/security/nss/lib/freebl/ecl/ecp_25519.c
+++ b/security/nss/lib/freebl/ecl/ecp_25519.c
@@ -115,5 +115,9 @@ ec_Curve25519_pt_mul(SECItem *X, SECItem *k, SECItem *P)
px = P->data;
}
- return ec_Curve25519_mul(X->data, k->data, px);
+ SECStatus rv = ec_Curve25519_mul(X->data, k->data, px);
+ if (NSS_SecureMemcmpZero(X->data, X->len) == 0) {
+ return SECFailure;
+ }
+ return rv;
}
diff --git a/security/nss/lib/freebl/exports.gyp b/security/nss/lib/freebl/exports.gyp
index aded6bfb6..ca0b6dafd 100644
--- a/security/nss/lib/freebl/exports.gyp
+++ b/security/nss/lib/freebl/exports.gyp
@@ -29,6 +29,7 @@
'files': [
'alghmac.h',
'blapi.h',
+ 'blake2b.h',
'chacha20poly1305.h',
'ec.h',
'ecl/ecl-curve.h',
diff --git a/security/nss/lib/freebl/fipsfreebl.c b/security/nss/lib/freebl/fipsfreebl.c
index 094513560..2328a677f 100644
--- a/security/nss/lib/freebl/fipsfreebl.c
+++ b/security/nss/lib/freebl/fipsfreebl.c
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* $Id: fipstest.c,v 1.31 2012/06/28 17:55:06 rrelyea%redhat.com Exp $ */
+#ifndef NSS_FIPS_DISABLED
#ifdef FREEBL_NO_DEPEND
#include "stubs.h"
#endif
@@ -15,9 +16,7 @@
#include "secerr.h"
#include "prtypes.h"
-#ifdef NSS_ENABLE_ECC
#include "ec.h" /* Required for ECDSA */
-#endif
/*
* different platforms have different ways of calling and initial entry point
@@ -1077,8 +1076,6 @@ rsa_loser:
return (SECFailure);
}
-#ifdef NSS_ENABLE_ECC
-
static SECStatus
freebl_fips_ECDSA_Test(ECParams *ecparams,
const PRUint8 *knownSignature,
@@ -1275,8 +1272,6 @@ freebl_fips_ECDSA_PowerUpSelfTest()
return (SECSuccess);
}
-#endif /* NSS_ENABLE_ECC */
-
static SECStatus
freebl_fips_DSA_PowerUpSelfTest(void)
{
@@ -1559,13 +1554,11 @@ freebl_fipsPowerUpSelfTest(unsigned int tests)
if (rv != SECSuccess)
return rv;
-#ifdef NSS_ENABLE_ECC
/* ECDSA Power-Up SelfTest(s). */
rv = freebl_fips_ECDSA_PowerUpSelfTest();
if (rv != SECSuccess)
return rv;
-#endif
}
/* Passed Power-Up SelfTest(s). */
return (SECSuccess);
@@ -1589,9 +1582,6 @@ static PRBool self_tests_freebl_ran = PR_FALSE;
static PRBool self_tests_ran = PR_FALSE;
static PRBool self_tests_freebl_success = PR_FALSE;
static PRBool self_tests_success = PR_FALSE;
-#if defined(DEBUG)
-static PRBool fips_mode_available = PR_FALSE;
-#endif
/*
* accessors for freebl
@@ -1644,7 +1634,6 @@ bl_startup_tests(void)
PORT_Assert(self_tests_freebl_ran == PR_FALSE);
PORT_Assert(self_tests_success == PR_FALSE);
- PORT_Assert(fips_mode_available == PR_FALSE);
self_tests_freebl_ran = PR_TRUE; /* we are running the tests */
self_tests_success = PR_FALSE; /* force it just in case */
self_tests_freebl_success = PR_FALSE; /* force it just in case */
@@ -1713,3 +1702,4 @@ BL_FIPSEntryOK(PRBool freebl_only)
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
+#endif
diff --git a/security/nss/lib/freebl/freebl.gyp b/security/nss/lib/freebl/freebl.gyp
index 8c0d0dcd5..8b6a546e7 100644
--- a/security/nss/lib/freebl/freebl.gyp
+++ b/security/nss/lib/freebl/freebl.gyp
@@ -23,6 +23,37 @@
]
},
{
+ 'target_name': 'gcm-aes-x86_c_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'gcm-x86.c', 'aes-x86.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports'
+ ],
+ # Enable isa option for pclmul and aes-ni; supported since gcc 4.4.
+ # This is only supported by x84/x64. It's not needed for Windows,
+ # unless clang-cl is used.
+ 'cflags_mozilla': [
+ '-mpclmul', '-maes'
+ ],
+ 'conditions': [
+ [ 'OS=="linux" or OS=="android" or OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd"', {
+ 'cflags': [
+ '-mpclmul', '-maes'
+ ],
+ }],
+ # macOS build doesn't use cflags.
+ [ 'OS=="mac"', {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-mpclmul', '-maes'
+ ],
+ },
+ }]
+ ]
+ },
+ {
'target_name': 'freebl',
'type': 'static_library',
'sources': [
@@ -45,6 +76,11 @@
'<(DEPTH)/exports.gyp:nss_exports',
],
'conditions': [
+ [ 'target_arch=="ia32" or target_arch=="x64"', {
+ 'dependencies': [
+ 'gcm-aes-x86_c_lib'
+ ],
+ }],
[ 'OS=="linux"', {
'defines!': [
'FREEBL_NO_DEPEND',
@@ -76,6 +112,11 @@
'<(DEPTH)/exports.gyp:nss_exports',
],
'conditions': [
+ [ 'target_arch=="ia32" or target_arch=="x64"', {
+ 'dependencies': [
+ 'gcm-aes-x86_c_lib'
+ ]
+ }],
[ 'OS!="linux" and OS!="android"', {
'conditions': [
[ 'moz_fold_libs==0', {
@@ -142,7 +183,8 @@
'target_defaults': {
'include_dirs': [
'mpi',
- 'ecl'
+ 'ecl',
+ 'verified',
],
'defines': [
'SHLIB_SUFFIX=\"<(dll_suffix)\"',
@@ -153,19 +195,12 @@
'MP_API_COMPATIBLE'
],
'conditions': [
- [ 'target_arch=="ia32" or target_arch=="x64"', {
- 'cflags_mozilla': [
- '-mpclmul',
- '-maes',
- ],
- }],
[ 'OS=="mac"', {
'xcode_settings': {
# I'm not sure since when this is supported.
# But I hope that doesn't matter. We also assume this is x86/x64.
'OTHER_CFLAGS': [
- '-mpclmul',
- '-maes',
+ '-std=gnu99',
],
},
}],
@@ -221,17 +256,24 @@
'HAVE_INT128_SUPPORT',
],
}, {
- 'sources': [
- 'ecl/uint128.c',
+ 'defines': [
+ 'KRML_NOUINT128',
],
}],
],
+ }, {
+ 'defines': [
+ 'KRML_NOUINT128',
+ ],
}],
[ 'OS=="linux"', {
'defines': [
'FREEBL_LOWHASH',
'FREEBL_NO_DEPEND',
],
+ 'cflags': [
+ '-std=gnu99',
+ ],
}],
[ 'OS=="linux" or OS=="android"', {
'conditions': [
@@ -259,14 +301,6 @@
'MP_USE_UINT_DIGIT',
],
}],
- [ 'target_arch=="ia32" or target_arch=="x64"', {
- 'cflags': [
- # enable isa option for pclmul am aes-ni; supported since gcc 4.4
- # This is only support by x84/x64. It's not needed for Windows.
- '-mpclmul',
- '-maes',
- ],
- }],
[ 'target_arch=="arm"', {
'defines': [
'MP_ASSEMBLY_MULTIPLY',
diff --git a/security/nss/lib/freebl/freebl_base.gypi b/security/nss/lib/freebl/freebl_base.gypi
index 027aa2702..44e28963b 100644
--- a/security/nss/lib/freebl/freebl_base.gypi
+++ b/security/nss/lib/freebl/freebl_base.gypi
@@ -8,8 +8,10 @@
'alghmac.c',
'arcfive.c',
'arcfour.c',
+ 'blake2b.c',
'camellia.c',
'chacha20poly1305.c',
+ 'crypto_primitives.c',
'ctr.c',
'cts.c',
'des.c',
@@ -98,10 +100,6 @@
],
}],
[ 'OS=="win"', {
- 'sources': [
- #TODO: building with mingw should not need this.
- 'ecl/uint128.c',
- ],
'libraries': [
'advapi32.lib',
],
@@ -132,29 +130,53 @@
}],
],
}],
- ['target_arch=="ia32" or target_arch=="x64"', {
+ ['target_arch=="ia32" or target_arch=="x64" or target_arch=="arm64" or target_arch=="aarch64"', {
'sources': [
- # All intel architectures get the 64 bit version
+ # All intel and 64-bit ARM architectures get the 64 bit version.
'ecl/curve25519_64.c',
+ 'verified/Hacl_Curve25519.c',
+ 'verified/FStar.c',
],
}, {
'sources': [
- # All non intel architectures get the generic 32 bit implementation (slow!)
+ # All other architectures get the generic 32 bit implementation (slow!)
'ecl/curve25519_32.c',
],
}],
- #TODO uint128.c
[ 'disable_chachapoly==0', {
'conditions': [
- [ 'OS!="win" and target_arch=="x64"', {
- 'sources': [
- 'chacha20_vec.c',
- 'poly1305-donna-x64-sse2-incremental-source.c',
+ [ 'OS!="win"', {
+ 'conditions': [
+ [ 'target_arch=="x64"', {
+ 'sources': [
+ 'chacha20_vec.c',
+ 'verified/Hacl_Poly1305_64.c',
+ ],
+ }, {
+ # !Windows & !x64
+ 'conditions': [
+ [ 'target_arch=="arm64" or target_arch=="aarch64"', {
+ 'sources': [
+ 'chacha20.c',
+ 'verified/Hacl_Chacha20.c',
+ 'verified/Hacl_Poly1305_64.c',
+ ],
+ }, {
+ # !Windows & !x64 & !arm64 & !aarch64
+ 'sources': [
+ 'chacha20.c',
+ 'verified/Hacl_Chacha20.c',
+ 'poly1305.c',
+ ],
+ }],
+ ],
+ }],
],
}, {
- # not x64
+ # Windows
'sources': [
'chacha20.c',
+ 'verified/Hacl_Chacha20.c',
'poly1305.c',
],
}],
diff --git a/security/nss/lib/freebl/gcm-x86.c b/security/nss/lib/freebl/gcm-x86.c
new file mode 100644
index 000000000..e34d63394
--- /dev/null
+++ b/security/nss/lib/freebl/gcm-x86.c
@@ -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/. */
+
+#ifdef FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+#include "gcm.h"
+#include "secerr.h"
+
+#include <wmmintrin.h> /* clmul */
+
+#define WRITE64(x, bytes) \
+ (bytes)[0] = (x) >> 56; \
+ (bytes)[1] = (x) >> 48; \
+ (bytes)[2] = (x) >> 40; \
+ (bytes)[3] = (x) >> 32; \
+ (bytes)[4] = (x) >> 24; \
+ (bytes)[5] = (x) >> 16; \
+ (bytes)[6] = (x) >> 8; \
+ (bytes)[7] = (x);
+
+SECStatus
+gcm_HashWrite_hw(gcmHashContext *ghash, unsigned char *outbuf)
+{
+ uint64_t tmp_out[2];
+ _mm_storeu_si128((__m128i *)tmp_out, ghash->x);
+ /* maxout must be larger than 16 byte (checked by the caller). */
+ WRITE64(tmp_out[0], outbuf + 8);
+ WRITE64(tmp_out[1], outbuf);
+ return SECSuccess;
+}
+
+SECStatus
+gcm_HashMult_hw(gcmHashContext *ghash, const unsigned char *buf,
+ unsigned int count)
+{
+ size_t i;
+ pre_align __m128i z_high post_align;
+ pre_align __m128i z_low post_align;
+ pre_align __m128i C post_align;
+ pre_align __m128i D post_align;
+ pre_align __m128i E post_align;
+ pre_align __m128i F post_align;
+ pre_align __m128i bin post_align;
+ pre_align __m128i Ci post_align;
+ pre_align __m128i tmp post_align;
+
+ for (i = 0; i < count; i++, buf += 16) {
+ bin = _mm_set_epi16(((uint16_t)buf[0] << 8) | buf[1],
+ ((uint16_t)buf[2] << 8) | buf[3],
+ ((uint16_t)buf[4] << 8) | buf[5],
+ ((uint16_t)buf[6] << 8) | buf[7],
+ ((uint16_t)buf[8] << 8) | buf[9],
+ ((uint16_t)buf[10] << 8) | buf[11],
+ ((uint16_t)buf[12] << 8) | buf[13],
+ ((uint16_t)buf[14] << 8) | buf[15]);
+ Ci = _mm_xor_si128(bin, ghash->x);
+
+ /* Do binary mult ghash->X = Ci * ghash->H. */
+ C = _mm_clmulepi64_si128(Ci, ghash->h, 0x00);
+ D = _mm_clmulepi64_si128(Ci, ghash->h, 0x11);
+ E = _mm_clmulepi64_si128(Ci, ghash->h, 0x01);
+ F = _mm_clmulepi64_si128(Ci, ghash->h, 0x10);
+ tmp = _mm_xor_si128(E, F);
+ z_high = _mm_xor_si128(tmp, _mm_slli_si128(D, 8));
+ z_high = _mm_unpackhi_epi64(z_high, D);
+ z_low = _mm_xor_si128(_mm_slli_si128(tmp, 8), C);
+ z_low = _mm_unpackhi_epi64(_mm_slli_si128(C, 8), z_low);
+
+ /* Shift one to the left (multiply by x) as gcm spec is stupid. */
+ C = _mm_slli_si128(z_low, 8);
+ E = _mm_srli_epi64(C, 63);
+ D = _mm_slli_si128(z_high, 8);
+ F = _mm_srli_epi64(D, 63);
+ /* Carry over */
+ C = _mm_srli_si128(z_low, 8);
+ D = _mm_srli_epi64(C, 63);
+ z_low = _mm_or_si128(_mm_slli_epi64(z_low, 1), E);
+ z_high = _mm_or_si128(_mm_or_si128(_mm_slli_epi64(z_high, 1), F), D);
+
+ /* Reduce */
+ C = _mm_slli_si128(z_low, 8);
+ /* D = z_low << 127 */
+ D = _mm_slli_epi64(C, 63);
+ /* E = z_low << 126 */
+ E = _mm_slli_epi64(C, 62);
+ /* F = z_low << 121 */
+ F = _mm_slli_epi64(C, 57);
+ /* z_low ^= (z_low << 127) ^ (z_low << 126) ^ (z_low << 121); */
+ z_low = _mm_xor_si128(_mm_xor_si128(_mm_xor_si128(z_low, D), E), F);
+ C = _mm_srli_si128(z_low, 8);
+ /* D = z_low >> 1 */
+ D = _mm_slli_epi64(C, 63);
+ D = _mm_or_si128(_mm_srli_epi64(z_low, 1), D);
+ /* E = z_low >> 2 */
+ E = _mm_slli_epi64(C, 62);
+ E = _mm_or_si128(_mm_srli_epi64(z_low, 2), E);
+ /* F = z_low >> 7 */
+ F = _mm_slli_epi64(C, 57);
+ F = _mm_or_si128(_mm_srli_epi64(z_low, 7), F);
+ /* ghash->x ^= z_low ^ (z_low >> 1) ^ (z_low >> 2) ^ (z_low >> 7); */
+ ghash->x = _mm_xor_si128(_mm_xor_si128(
+ _mm_xor_si128(_mm_xor_si128(z_high, z_low), D), E),
+ F);
+ }
+ return SECSuccess;
+}
+
+SECStatus
+gcm_HashInit_hw(gcmHashContext *ghash)
+{
+ ghash->ghash_mul = gcm_HashMult_hw;
+ ghash->x = _mm_setzero_si128();
+ /* MSVC requires __m64 to load epi64. */
+ ghash->h = _mm_set_epi32(ghash->h_high >> 32, (uint32_t)ghash->h_high,
+ ghash->h_low >> 32, (uint32_t)ghash->h_low);
+ ghash->hw = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+gcm_HashZeroX_hw(gcmHashContext *ghash)
+{
+ ghash->x = _mm_setzero_si128();
+ return SECSuccess;
+}
diff --git a/security/nss/lib/freebl/gcm.c b/security/nss/lib/freebl/gcm.c
index 0fdb0fd48..f1e16da78 100644
--- a/security/nss/lib/freebl/gcm.c
+++ b/security/nss/lib/freebl/gcm.c
@@ -17,18 +17,50 @@
#include <limits.h>
-#ifdef NSS_X86_OR_X64
-#include <wmmintrin.h> /* clmul */
-#endif
-
/* Forward declarations */
+SECStatus gcm_HashInit_hw(gcmHashContext *ghash);
+SECStatus gcm_HashWrite_hw(gcmHashContext *ghash, unsigned char *outbuf);
SECStatus gcm_HashMult_hw(gcmHashContext *ghash, const unsigned char *buf,
unsigned int count);
+SECStatus gcm_HashZeroX_hw(gcmHashContext *ghash);
SECStatus gcm_HashMult_sftw(gcmHashContext *ghash, const unsigned char *buf,
unsigned int count);
SECStatus gcm_HashMult_sftw32(gcmHashContext *ghash, const unsigned char *buf,
unsigned int count);
+/* Stub definitions for the above *_hw functions, which shouldn't be
+ * used unless NSS_X86_OR_X64 is defined */
+#ifndef NSS_X86_OR_X64
+SECStatus
+gcm_HashWrite_hw(gcmHashContext *ghash, unsigned char *outbuf)
+{
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+}
+
+SECStatus
+gcm_HashMult_hw(gcmHashContext *ghash, const unsigned char *buf,
+ unsigned int count)
+{
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+}
+
+SECStatus
+gcm_HashInit_hw(gcmHashContext *ghash)
+{
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+}
+
+SECStatus
+gcm_HashZeroX_hw(gcmHashContext *ghash)
+{
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+}
+#endif /* NSS_X86_OR_X64 */
+
uint64_t
get64(const unsigned char *bytes)
{
@@ -46,6 +78,8 @@ get64(const unsigned char *bytes)
SECStatus
gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, PRBool sw)
{
+ SECStatus rv = SECSuccess;
+
ghash->cLen = 0;
ghash->bufLen = 0;
PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf));
@@ -53,17 +87,7 @@ gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, PRBool sw)
ghash->h_low = get64(H + 8);
ghash->h_high = get64(H);
if (clmul_support() && !sw) {
-#ifdef NSS_X86_OR_X64
- ghash->ghash_mul = gcm_HashMult_hw;
- ghash->x = _mm_setzero_si128();
- /* MSVC requires __m64 to load epi64. */
- ghash->h = _mm_set_epi32(ghash->h_high >> 32, (uint32_t)ghash->h_high,
- ghash->h_low >> 32, (uint32_t)ghash->h_low);
- ghash->hw = PR_TRUE;
-#else
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
-#endif /* NSS_X86_OR_X64 */
+ rv = gcm_HashInit_hw(ghash);
} else {
/* We fall back to the software implementation if we can't use / don't
* want to use pclmul. */
@@ -75,7 +99,7 @@ gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, PRBool sw)
ghash->x_high = ghash->x_low = 0;
ghash->hw = PR_FALSE;
}
- return SECSuccess;
+ return rv;
}
#ifdef HAVE_INT128_SUPPORT
@@ -283,102 +307,17 @@ gcm_HashMult_sftw32(gcmHashContext *ghash, const unsigned char *buf,
}
#endif /* HAVE_INT128_SUPPORT */
-SECStatus
-gcm_HashMult_hw(gcmHashContext *ghash, const unsigned char *buf,
- unsigned int count)
-{
-#ifdef NSS_X86_OR_X64
- size_t i;
- pre_align __m128i z_high post_align;
- pre_align __m128i z_low post_align;
- pre_align __m128i C post_align;
- pre_align __m128i D post_align;
- pre_align __m128i E post_align;
- pre_align __m128i F post_align;
- pre_align __m128i bin post_align;
- pre_align __m128i Ci post_align;
- pre_align __m128i tmp post_align;
-
- for (i = 0; i < count; i++, buf += 16) {
- bin = _mm_set_epi16(((uint16_t)buf[0] << 8) | buf[1],
- ((uint16_t)buf[2] << 8) | buf[3],
- ((uint16_t)buf[4] << 8) | buf[5],
- ((uint16_t)buf[6] << 8) | buf[7],
- ((uint16_t)buf[8] << 8) | buf[9],
- ((uint16_t)buf[10] << 8) | buf[11],
- ((uint16_t)buf[12] << 8) | buf[13],
- ((uint16_t)buf[14] << 8) | buf[15]);
- Ci = _mm_xor_si128(bin, ghash->x);
-
- /* Do binary mult ghash->X = Ci * ghash->H. */
- C = _mm_clmulepi64_si128(Ci, ghash->h, 0x00);
- D = _mm_clmulepi64_si128(Ci, ghash->h, 0x11);
- E = _mm_clmulepi64_si128(Ci, ghash->h, 0x01);
- F = _mm_clmulepi64_si128(Ci, ghash->h, 0x10);
- tmp = _mm_xor_si128(E, F);
- z_high = _mm_xor_si128(tmp, _mm_slli_si128(D, 8));
- z_high = _mm_unpackhi_epi64(z_high, D);
- z_low = _mm_xor_si128(_mm_slli_si128(tmp, 8), C);
- z_low = _mm_unpackhi_epi64(_mm_slli_si128(C, 8), z_low);
-
- /* Shift one to the left (multiply by x) as gcm spec is stupid. */
- C = _mm_slli_si128(z_low, 8);
- E = _mm_srli_epi64(C, 63);
- D = _mm_slli_si128(z_high, 8);
- F = _mm_srli_epi64(D, 63);
- /* Carry over */
- C = _mm_srli_si128(z_low, 8);
- D = _mm_srli_epi64(C, 63);
- z_low = _mm_or_si128(_mm_slli_epi64(z_low, 1), E);
- z_high = _mm_or_si128(_mm_or_si128(_mm_slli_epi64(z_high, 1), F), D);
-
- /* Reduce */
- C = _mm_slli_si128(z_low, 8);
- /* D = z_low << 127 */
- D = _mm_slli_epi64(C, 63);
- /* E = z_low << 126 */
- E = _mm_slli_epi64(C, 62);
- /* F = z_low << 121 */
- F = _mm_slli_epi64(C, 57);
- /* z_low ^= (z_low << 127) ^ (z_low << 126) ^ (z_low << 121); */
- z_low = _mm_xor_si128(_mm_xor_si128(_mm_xor_si128(z_low, D), E), F);
- C = _mm_srli_si128(z_low, 8);
- /* D = z_low >> 1 */
- D = _mm_slli_epi64(C, 63);
- D = _mm_or_si128(_mm_srli_epi64(z_low, 1), D);
- /* E = z_low >> 2 */
- E = _mm_slli_epi64(C, 62);
- E = _mm_or_si128(_mm_srli_epi64(z_low, 2), E);
- /* F = z_low >> 7 */
- F = _mm_slli_epi64(C, 57);
- F = _mm_or_si128(_mm_srli_epi64(z_low, 7), F);
- /* ghash->x ^= z_low ^ (z_low >> 1) ^ (z_low >> 2) ^ (z_low >> 7); */
- ghash->x = _mm_xor_si128(_mm_xor_si128(
- _mm_xor_si128(_mm_xor_si128(z_high, z_low), D), E),
- F);
- }
- return SECSuccess;
-#else
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
-#endif /* NSS_X86_OR_X64 */
-}
-
static SECStatus
gcm_zeroX(gcmHashContext *ghash)
{
+ SECStatus rv = SECSuccess;
+
if (ghash->hw) {
-#ifdef NSS_X86_OR_X64
- ghash->x = _mm_setzero_si128();
- return SECSuccess;
-#else
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
-#endif /* NSS_X86_OR_X64 */
+ rv = gcm_HashZeroX_hw(ghash);
}
ghash->x_high = ghash->x_low = 0;
- return SECSuccess;
+ return rv;
}
/*
@@ -503,15 +442,10 @@ gcmHash_Final(gcmHashContext *ghash, unsigned char *outbuf,
}
if (ghash->hw) {
-#ifdef NSS_X86_OR_X64
- uint64_t tmp_out[2];
- _mm_storeu_si128((__m128i *)tmp_out, ghash->x);
- WRITE64(tmp_out[0], T + 8);
- WRITE64(tmp_out[1], T);
-#else
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
-#endif /* NSS_X86_OR_X64 */
+ rv = gcm_HashWrite_hw(ghash, T);
+ if (rv != SECSuccess) {
+ goto cleanup;
+ }
} else {
WRITE64(ghash->x_low, T + 8);
WRITE64(ghash->x_high, T);
@@ -595,14 +529,7 @@ GCM_CreateContext(void *context, freeblCipherFunc cipher,
if (gcm == NULL) {
return NULL;
}
- /* aligned_alloc is C11 so we have to do it the old way. */
- ghash = PORT_ZAlloc(sizeof(gcmHashContext) + 15);
- if (ghash == NULL) {
- PORT_SetError(SEC_ERROR_NO_MEMORY);
- goto loser;
- }
- ghash->mem = ghash;
- ghash = (gcmHashContext *)(((uintptr_t)ghash + 15) & ~(uintptr_t)0x0F);
+ ghash = PORT_ZNewAligned(gcmHashContext, 16, mem);
/* first plug in the ghash context */
gcm->ghash_context = ghash;
diff --git a/security/nss/lib/freebl/gcm.h b/security/nss/lib/freebl/gcm.h
index 0c707a081..42ef0f717 100644
--- a/security/nss/lib/freebl/gcm.h
+++ b/security/nss/lib/freebl/gcm.h
@@ -9,7 +9,21 @@
#include <stdint.h>
#ifdef NSS_X86_OR_X64
+/* GCC <= 4.8 doesn't support including emmintrin.h without enabling SSE2 */
+#if !defined(__clang__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+ (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 8))
+#pragma GCC push_options
+#pragma GCC target("sse2")
+#undef NSS_DISABLE_SSE2
+#define NSS_DISABLE_SSE2 1
+#endif /* GCC <= 4.8 */
+
#include <emmintrin.h> /* __m128i */
+
+#ifdef NSS_DISABLE_SSE2
+#undef NSS_DISABLE_SSE2
+#pragma GCC pop_options
+#endif /* NSS_DISABLE_SSE2 */
#endif
SEC_BEGIN_PROTOS
diff --git a/security/nss/lib/freebl/ldvector.c b/security/nss/lib/freebl/ldvector.c
index 2447a0c9f..d39965256 100644
--- a/security/nss/lib/freebl/ldvector.c
+++ b/security/nss/lib/freebl/ldvector.c
@@ -298,9 +298,25 @@ static const struct FREEBLVectorStr vector =
/* End of Version 3.018 */
- EC_GetPointSize
+ EC_GetPointSize,
/* End of Version 3.019 */
+
+ BLAKE2B_Hash,
+ BLAKE2B_HashBuf,
+ BLAKE2B_MAC_HashBuf,
+ BLAKE2B_NewContext,
+ BLAKE2B_DestroyContext,
+ BLAKE2B_Begin,
+ BLAKE2B_MAC_Begin,
+ BLAKE2B_Update,
+ BLAKE2B_End,
+ BLAKE2B_FlattenSize,
+ BLAKE2B_Flatten,
+ BLAKE2B_Resurrect
+
+ /* End of Version 3.020 */
+
};
const FREEBLVector*
@@ -320,8 +336,12 @@ FREEBL_GetVector(void)
return NULL;
}
#endif
- /* make sure the Full self tests have been run before continuing */
+
+#ifndef NSS_FIPS_DISABLED
+ /* In FIPS mode make sure the Full self tests have been run before
+ * continuing. */
BL_POSTRan(PR_FALSE);
+#endif
return &vector;
}
diff --git a/security/nss/lib/freebl/loader.c b/security/nss/lib/freebl/loader.c
index 792171b08..fe5e0a668 100644
--- a/security/nss/lib/freebl/loader.c
+++ b/security/nss/lib/freebl/loader.c
@@ -2124,3 +2124,114 @@ EC_GetPointSize(const ECParams *params)
return SECFailure;
return (vector->p_EC_GetPointSize)(params);
}
+
+SECStatus
+BLAKE2B_Hash(unsigned char *dest, const char *src)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_Hash)(dest, src);
+}
+
+SECStatus
+BLAKE2B_HashBuf(unsigned char *output, const unsigned char *input, PRUint32 inlen)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_HashBuf)(output, input, inlen);
+}
+
+SECStatus
+BLAKE2B_MAC_HashBuf(unsigned char *output, const unsigned char *input,
+ unsigned int inlen, const unsigned char *key,
+ unsigned int keylen)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_MAC_HashBuf)(output, input, inlen, key, keylen);
+}
+
+BLAKE2BContext *
+BLAKE2B_NewContext(void)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return NULL;
+ }
+ return (vector->p_BLAKE2B_NewContext)();
+}
+
+void
+BLAKE2B_DestroyContext(BLAKE2BContext *BLAKE2BContext, PRBool freeit)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return;
+ }
+ (vector->p_BLAKE2B_DestroyContext)(BLAKE2BContext, freeit);
+}
+
+SECStatus
+BLAKE2B_Begin(BLAKE2BContext *ctx)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_Begin)(ctx);
+}
+
+SECStatus
+BLAKE2B_MAC_Begin(BLAKE2BContext *ctx, const PRUint8 *key, const size_t keylen)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_MAC_Begin)(ctx, key, keylen);
+}
+
+SECStatus
+BLAKE2B_Update(BLAKE2BContext *ctx, const unsigned char *in, unsigned int inlen)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_Update)(ctx, in, inlen);
+}
+
+SECStatus
+BLAKE2B_End(BLAKE2BContext *ctx, unsigned char *out,
+ unsigned int *digestLen, size_t maxDigestLen)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_End)(ctx, out, digestLen, maxDigestLen);
+}
+
+unsigned int
+BLAKE2B_FlattenSize(BLAKE2BContext *ctx)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return 0;
+ }
+ return (vector->p_BLAKE2B_FlattenSize)(ctx);
+}
+
+SECStatus
+BLAKE2B_Flatten(BLAKE2BContext *ctx, unsigned char *space)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return SECFailure;
+ }
+ return (vector->p_BLAKE2B_Flatten)(ctx, space);
+}
+
+BLAKE2BContext *
+BLAKE2B_Resurrect(unsigned char *space, void *arg)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) {
+ return NULL;
+ }
+ return (vector->p_BLAKE2B_Resurrect)(space, arg);
+}
diff --git a/security/nss/lib/freebl/loader.h b/security/nss/lib/freebl/loader.h
index ed392cc47..ff10cf9ba 100644
--- a/security/nss/lib/freebl/loader.h
+++ b/security/nss/lib/freebl/loader.h
@@ -10,7 +10,7 @@
#include "blapi.h"
-#define FREEBL_VERSION 0x0313
+#define FREEBL_VERSION 0x0314
struct FREEBLVectorStr {
@@ -736,6 +736,29 @@ struct FREEBLVectorStr {
/* Version 3.019 came to here */
+ SECStatus (*p_BLAKE2B_Hash)(unsigned char *dest, const char *src);
+ SECStatus (*p_BLAKE2B_HashBuf)(unsigned char *output,
+ const unsigned char *input, PRUint32 inlen);
+ SECStatus (*p_BLAKE2B_MAC_HashBuf)(unsigned char *output,
+ const unsigned char *input,
+ unsigned int inlen,
+ const unsigned char *key,
+ unsigned int keylen);
+ BLAKE2BContext *(*p_BLAKE2B_NewContext)();
+ void (*p_BLAKE2B_DestroyContext)(BLAKE2BContext *ctx, PRBool freeit);
+ SECStatus (*p_BLAKE2B_Begin)(BLAKE2BContext *ctx);
+ SECStatus (*p_BLAKE2B_MAC_Begin)(BLAKE2BContext *ctx, const PRUint8 *key,
+ const size_t keylen);
+ SECStatus (*p_BLAKE2B_Update)(BLAKE2BContext *ctx, const unsigned char *in,
+ unsigned int inlen);
+ SECStatus (*p_BLAKE2B_End)(BLAKE2BContext *ctx, unsigned char *out,
+ unsigned int *digestLen, size_t maxDigestLen);
+ unsigned int (*p_BLAKE2B_FlattenSize)(BLAKE2BContext *ctx);
+ SECStatus (*p_BLAKE2B_Flatten)(BLAKE2BContext *ctx, unsigned char *space);
+ BLAKE2BContext *(*p_BLAKE2B_Resurrect)(unsigned char *space, void *arg);
+
+ /* Version 3.020 came to here */
+
/* Add new function pointers at the end of this struct and bump
* FREEBL_VERSION at the beginning of this file. */
};
diff --git a/security/nss/lib/freebl/manifest.mn b/security/nss/lib/freebl/manifest.mn
index bf8144218..e4c9ab0b7 100644
--- a/security/nss/lib/freebl/manifest.mn
+++ b/security/nss/lib/freebl/manifest.mn
@@ -1,10 +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/.
# NOTE: any ifdefs in this file must be defined on the gmake command line
-# (if anywhere). They cannot come from Makefile or config.mk
+# (if anywhere). They cannot come from Makefile or config.mk
CORE_DEPTH = ../..
@@ -75,7 +75,7 @@ DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\" \
-DSHLIB_VERSION=\"$(LIBRARY_VERSION)\" \
-DSOFTOKEN_SHLIB_VERSION=\"$(SOFTOKEN_LIBRARY_VERSION)\"
-REQUIRES =
+REQUIRES =
EXPORTS = \
blapit.h \
@@ -86,6 +86,7 @@ EXPORTS = \
PRIVATE_EXPORTS = \
alghmac.h \
+ blake2b.h \
blapi.h \
chacha20poly1305.h \
hmacct.h \
@@ -102,16 +103,13 @@ MPI_SRCS = mpprime.c mpmontg.c mplogic.c mpi.c mp_gf2m.c
ECL_HDRS = ecl-exp.h ecl.h ecp.h ecl-priv.h
-ifndef NSS_DISABLE_ECC
ECL_SRCS = ecl.c ecl_mult.c ecl_gf.c \
ecp_aff.c ecp_jac.c ecp_mont.c \
ec_naf.c ecp_jm.c ecp_256.c ecp_384.c ecp_521.c \
ecp_256_32.c ecp_25519.c
-else
-ECL_SRCS = $(NULL)
-endif
SHA_SRCS = sha_fast.c
MPCPU_SRCS = mpcpucache.c
+VERIFIED_SRCS = $(NULL)
CSRCS = \
freeblver.c \
@@ -126,6 +124,8 @@ CSRCS = \
alg2268.c \
arcfour.c \
arcfive.c \
+ crypto_primitives.c \
+ blake2b.c \
desblapi.c \
des.c \
drbg.c \
@@ -153,6 +153,7 @@ CSRCS = \
$(MPI_SRCS) \
$(MPCPU_SRCS) \
$(ECL_SRCS) \
+ $(VERIFIED_SRCS) \
$(STUBS_SRCS) \
$(LOWHASH_SRCS) \
$(EXTRA_SRCS) \
@@ -162,6 +163,7 @@ ALL_CSRCS := $(CSRCS)
ALL_HDRS = \
alghmac.h \
+ blake2b.h \
blapi.h \
blapit.h \
des.h \
@@ -178,12 +180,6 @@ ALL_HDRS = \
$(NULL)
-ifdef AES_GEN_TBL
-DEFINES += -DRIJNDAEL_GENERATE_TABLES
-else
-ifdef AES_GEN_TBL_M
-DEFINES += -DRIJNDAEL_GENERATE_TABLES_MACRO
-else
ifdef AES_GEN_VAL
DEFINES += -DRIJNDAEL_GENERATE_VALUES
else
@@ -193,5 +189,3 @@ else
DEFINES += -DRIJNDAEL_INCLUDE_TABLES
endif
endif
-endif
-endif
diff --git a/security/nss/lib/freebl/mpi/README b/security/nss/lib/freebl/mpi/README
index 776ba713a..cf4302758 100644
--- a/security/nss/lib/freebl/mpi/README
+++ b/security/nss/lib/freebl/mpi/README
@@ -53,7 +53,7 @@ to change are:
single digit. This is just a printf() format string, so you
can adjust it appropriately.
-(3) The macros DIGIT_MAX and MP_WORD_MAX, which specify the
+(3) The macros DIGIT_MAX and MP_WORD_MAX, which specify the
largest value expressible in an mp_digit and an mp_word,
respectively.
@@ -345,7 +345,7 @@ returns values of x and y satisfying Bezout's identity. This is used
by mp_invmod() to find modular inverses. However, if you do not need
these values, you will find that mp_gcd() is MUCH more efficient,
since it doesn't need all the intermediate values that mp_xgcd()
-requires in order to compute x and y.
+requires in order to compute x and y.
The mp_gcd() (and mp_xgcd()) functions use the binary (extended) GCD
algorithm due to Josef Stein.
@@ -361,7 +361,7 @@ mp_read_radix(mp, str, r) - convert a string in radix r to an mp_int
mp_read_raw(mp, s, len) - convert a string of bytes to an mp_int
mp_radix_size(mp, r) - return length of buffer needed by mp_toradix()
mp_raw_size(mp) - return length of buffer needed by mp_toraw()
-mp_toradix(mp, str, r) - convert an mp_int to a string of radix r
+mp_toradix(mp, str, r) - convert an mp_int to a string of radix r
digits
mp_toraw(mp, str) - convert an mp_int to a string of bytes
mp_tovalue(ch, r) - convert ch to its value when taken as
@@ -387,7 +387,7 @@ The mp_read_radix() and mp_toradix() functions support bases from 2 to
than this, you will need to write them yourself (that's why mp_div_d()
is provided, after all).
-Note: mp_read_radix() will accept as digits either capital or
+Note: mp_read_radix() will accept as digits either capital or
---- lower-case letters. However, the current implementation of
mp_toradix() only outputs upper-case letters, when writing
bases betwee 10 and 36. The underlying code supports using
@@ -448,14 +448,14 @@ Note: The mpp_random() and mpp_random_size() functions use the C
to change.
mpp_divis_vector(a, v, s, w) - is a divisible by any of the s digits
- in v? If so, let w be the index of
+ in v? If so, let w be the index of
that digit
mpp_divis_primes(a, np) - is a divisible by any of the first np
- primes? If so, set np to the prime
+ primes? If so, set np to the prime
which divided a.
-mpp_fermat(a, d) - test if w^a = w (mod a). If so,
+mpp_fermat(a, d) - test if w^a = w (mod a). If so,
returns MP_YES, otherwise MP_NO.
mpp_pprime(a, nt) - perform nt iterations of the Rabin-
@@ -486,7 +486,7 @@ The file 'mpi-config.h' defines several configurable parameters for
the library, which you can adjust to suit your application. At the
time of this writing, the available options are:
-MP_IOFUNC - Define true to include the mp_print() function,
+MP_IOFUNC - Define true to include the mp_print() function,
which is moderately useful for debugging. This
implicitly includes <stdio.h>.
@@ -502,21 +502,14 @@ MP_LOGTAB - If true, the file "logtab.h" is included, which
the library includes <math.h> and uses log(). This
typically forces you to link against math libraries.
-MP_MEMSET - If true, use memset() to zero buffers. If you run
- into weird alignment related bugs, set this to zero
- and an explicit loop will be used.
-
-MP_MEMCPY - If true, use memcpy() to copy buffers. If you run
- into weird alignment bugs, set this to zero and an
- explicit loop will be used.
MP_ARGCHK - Set to 0, 1, or 2. This defines how the argument
- checking macro, ARGCHK(), gets expanded. If this
- is set to zero, ARGCHK() expands to nothing; no
+ checking macro, ARGCHK(), gets expanded. If this
+ is set to zero, ARGCHK() expands to nothing; no
argument checks are performed. If this is 1, the
ARGCHK() macro expands to code that returns MP_BADARG
- or similar at runtime. If it is 2, ARGCHK() expands
- to an assert() call that aborts the program on a
+ or similar at runtime. If it is 2, ARGCHK() expands
+ to an assert() call that aborts the program on a
bad input.
MP_DEBUG - Turns on debugging output. This is probably not at
@@ -528,14 +521,14 @@ MP_DEFPREC - The default precision of a newly-created mp_int, in
the mp_set_prec() function, but this is its initial
value.
-MP_SQUARE - If this is set to a nonzero value, the mp_sqr()
+MP_SQUARE - If this is set to a nonzero value, the mp_sqr()
function will use an alternate algorithm that takes
advantage of the redundant inner product computation
when both multiplicands are identical. Unfortunately,
with some compilers this is actually SLOWER than just
calling mp_mul() with the same argument twice. So
if you set MP_SQUARE to zero, mp_sqr() will be expan-
- ded into a call to mp_mul(). This applies to all
+ ded into a call to mp_mul(). This applies to all
the uses of mp_sqr(), including mp_sqrmod() and the
internal calls to s_mp_sqr() inside mpi.c
@@ -568,7 +561,7 @@ CFLAGS=-ansi -pedantic -Wall -O2
If all goes well, the library should compile without warnings using
this combination. You should, of course, make whatever adjustments
-you find necessary.
+you find necessary.
The MPI library distribution comes with several additional programs
which are intended to demonstrate the use of the library, and provide
@@ -580,7 +573,7 @@ directory) for manipulating large numbers. These include:
basecvt.c A radix-conversion program, supporting bases from
2 to 64 inclusive.
-bbsrand.c A BBS (quadratic residue) pseudo-random number
+bbsrand.c A BBS (quadratic residue) pseudo-random number
generator. The file 'bbsrand.c' is just the driver
for the program; the real code lives in the files
'bbs_rand.h' and 'bbs_rand.c'
@@ -626,7 +619,7 @@ Acknowledgements:
----------------
The algorithms used in this library were drawn primarily from Volume
-2 of Donald Knuth's magnum opus, _The Art of Computer Programming_,
+2 of Donald Knuth's magnum opus, _The Art of Computer Programming_,
"Semi-Numerical Methods". Barrett's algorithm for modular reduction
came from Menezes, Oorschot, and Vanstone's _Handbook of Applied
Cryptography_, Chapter 14.
diff --git a/security/nss/lib/freebl/mpi/mpi-config.h b/security/nss/lib/freebl/mpi/mpi-config.h
index c6f72b206..0cc868a14 100644
--- a/security/nss/lib/freebl/mpi/mpi-config.h
+++ b/security/nss/lib/freebl/mpi/mpi-config.h
@@ -28,14 +28,6 @@
#define MP_LOGTAB 1 /* use table of logs instead of log()? */
#endif
-#ifndef MP_MEMSET
-#define MP_MEMSET 1 /* use memset() to zero buffers? */
-#endif
-
-#ifndef MP_MEMCPY
-#define MP_MEMCPY 1 /* use memcpy() to copy buffers? */
-#endif
-
#ifndef MP_ARGCHK
/*
0 = no parameter checks
diff --git a/security/nss/lib/freebl/mpi/mpi.c b/security/nss/lib/freebl/mpi/mpi.c
index f7784c8d9..ae404019d 100644
--- a/security/nss/lib/freebl/mpi/mpi.c
+++ b/security/nss/lib/freebl/mpi/mpi.c
@@ -2782,15 +2782,7 @@ s_mp_pad(mp_int *mp, mp_size min)
void
s_mp_setz(mp_digit *dp, mp_size count)
{
-#if MP_MEMSET == 0
- int ix;
-
- for (ix = 0; ix < count; ix++)
- dp[ix] = 0;
-#else
memset(dp, 0, count * sizeof(mp_digit));
-#endif
-
} /* end s_mp_setz() */
/* }}} */
@@ -2801,14 +2793,7 @@ s_mp_setz(mp_digit *dp, mp_size count)
void
s_mp_copy(const mp_digit *sp, mp_digit *dp, mp_size count)
{
-#if MP_MEMCPY == 0
- int ix;
-
- for (ix = 0; ix < count; ix++)
- dp[ix] = sp[ix];
-#else
memcpy(dp, sp, count * sizeof(mp_digit));
-#endif
} /* end s_mp_copy() */
/* }}} */
diff --git a/security/nss/lib/freebl/nsslowhash.c b/security/nss/lib/freebl/nsslowhash.c
index 5ed039689..22f97810f 100644
--- a/security/nss/lib/freebl/nsslowhash.c
+++ b/security/nss/lib/freebl/nsslowhash.c
@@ -22,6 +22,7 @@ struct NSSLOWHASHContextStr {
void *hashCtxt;
};
+#ifndef NSS_FIPS_DISABLED
static int
nsslow_GetFIPSEnabled(void)
{
@@ -40,9 +41,10 @@ nsslow_GetFIPSEnabled(void)
return 0;
if (d != '1')
return 0;
-#endif
+#endif /* LINUX */
return 1;
}
+#endif /* NSS_FIPS_DISABLED */
static NSSLOWInitContext dummyContext = { 0 };
static PRBool post_failed = PR_TRUE;
@@ -54,6 +56,7 @@ NSSLOW_Init(void)
(void)FREEBL_InitStubs();
#endif
+#ifndef NSS_FIPS_DISABLED
/* make sure the FIPS product is installed if we are trying to
* go into FIPS mode */
if (nsslow_GetFIPSEnabled()) {
@@ -63,6 +66,7 @@ NSSLOW_Init(void)
return NULL;
}
}
+#endif
post_failed = PR_FALSE;
return &dummyContext;
diff --git a/security/nss/lib/freebl/poly1305.h b/security/nss/lib/freebl/poly1305.h
index 0a463483f..125f49b3b 100644
--- a/security/nss/lib/freebl/poly1305.h
+++ b/security/nss/lib/freebl/poly1305.h
@@ -8,6 +8,8 @@
#ifndef FREEBL_POLY1305_H_
#define FREEBL_POLY1305_H_
+#include "stddef.h"
+
typedef unsigned char poly1305_state[512];
/* Poly1305Init sets up |state| so that it can be used to calculate an
diff --git a/security/nss/lib/freebl/rijndael.c b/security/nss/lib/freebl/rijndael.c
index e4ad60388..5de27de9c 100644
--- a/security/nss/lib/freebl/rijndael.c
+++ b/security/nss/lib/freebl/rijndael.c
@@ -27,16 +27,39 @@
#include "intel-gcm.h"
#endif /* INTEL_GCM */
+/* Forward declarations */
+void rijndael_native_key_expansion(AESContext *cx, const unsigned char *key,
+ unsigned int Nk);
+void rijndael_native_encryptBlock(AESContext *cx,
+ unsigned char *output,
+ const unsigned char *input);
+
+/* Stub definitions for the above rijndael_native_* functions, which
+ * shouldn't be used unless NSS_X86_OR_X64 is defined */
+#ifndef NSS_X86_OR_X64
+void
+rijndael_native_key_expansion(AESContext *cx, const unsigned char *key,
+ unsigned int Nk)
+{
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ PORT_Assert(0);
+}
+
+void
+rijndael_native_encryptBlock(AESContext *cx,
+ unsigned char *output,
+ const unsigned char *input)
+{
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ PORT_Assert(0);
+}
+#endif /* NSS_X86_OR_X64 */
+
/*
- * There are currently five ways to build this code, varying in performance
+ * There are currently three ways to build this code, varying in performance
* and code size.
*
* RIJNDAEL_INCLUDE_TABLES Include all tables from rijndael32.tab
- * RIJNDAEL_GENERATE_TABLES Generate tables on first
- * encryption/decryption, then store them;
- * use the function gfm
- * RIJNDAEL_GENERATE_TABLES_MACRO Same as above, but use macros to do
- * the generation
* RIJNDAEL_GENERATE_VALUES Do not store tables, generate the table
* values "on-the-fly", using gfm
* RIJNDAEL_GENERATE_VALUES_MACRO Same as above, but use macros
@@ -108,8 +131,7 @@
((a & 0x80) ? ((a << 1) ^ 0x1b) : (a << 1))
/* Choose GFM method (macros or function) */
-#if defined(RIJNDAEL_GENERATE_TABLES_MACRO) || \
- defined(RIJNDAEL_GENERATE_VALUES_MACRO)
+#if defined(RIJNDAEL_GENERATE_VALUES_MACRO)
/*
* Galois field GF(2**8) multipliers, in macro form
@@ -133,7 +155,7 @@
#define GFM0E(a) \
(GFM02(a) ^ GFM04(a) ^ GFM08(a)) /* a * 0E = a * (02 + 04 + 08) */
-#else /* RIJNDAEL_GENERATE_TABLES or RIJNDAEL_GENERATE_VALUES */
+#else /* RIJNDAEL_GENERATE_VALUES */
/* GF_MULTIPLY
*
@@ -244,7 +266,7 @@ gen_TInvXi(PRUint8 tx, PRUint8 i)
#define IMXC1(b) G_IMXC1(b)
#define IMXC2(b) G_IMXC2(b)
#define IMXC3(b) G_IMXC3(b)
-#elif defined(RIJNDAEL_GENERATE_VALUES_MACRO)
+#else /* RIJNDAEL_GENERATE_VALUES_MACRO */
/* generate values for the tables with macros */
#define T0(i) G_T0(i)
#define T1(i) G_T1(i)
@@ -258,84 +280,10 @@ gen_TInvXi(PRUint8 tx, PRUint8 i)
#define IMXC1(b) G_IMXC1(b)
#define IMXC2(b) G_IMXC2(b)
#define IMXC3(b) G_IMXC3(b)
-#else /* RIJNDAEL_GENERATE_TABLES or RIJNDAEL_GENERATE_TABLES_MACRO */
-/* Generate T and T**-1 table values and store, then index */
-/* The inverse mix column tables are still generated */
-#define T0(i) rijndaelTables->T0[i]
-#define T1(i) rijndaelTables->T1[i]
-#define T2(i) rijndaelTables->T2[i]
-#define T3(i) rijndaelTables->T3[i]
-#define TInv0(i) rijndaelTables->TInv0[i]
-#define TInv1(i) rijndaelTables->TInv1[i]
-#define TInv2(i) rijndaelTables->TInv2[i]
-#define TInv3(i) rijndaelTables->TInv3[i]
-#define IMXC0(b) G_IMXC0(b)
-#define IMXC1(b) G_IMXC1(b)
-#define IMXC2(b) G_IMXC2(b)
-#define IMXC3(b) G_IMXC3(b)
#endif /* choose T-table indexing method */
#endif /* not RIJNDAEL_INCLUDE_TABLES */
-#if defined(RIJNDAEL_GENERATE_TABLES) || \
- defined(RIJNDAEL_GENERATE_TABLES_MACRO)
-
-/* Code to generate and store the tables */
-
-struct rijndael_tables_str {
- PRUint32 T0[256];
- PRUint32 T1[256];
- PRUint32 T2[256];
- PRUint32 T3[256];
- PRUint32 TInv0[256];
- PRUint32 TInv1[256];
- PRUint32 TInv2[256];
- PRUint32 TInv3[256];
-};
-
-static struct rijndael_tables_str *rijndaelTables = NULL;
-static PRCallOnceType coRTInit = { 0, 0, 0 };
-static PRStatus
-init_rijndael_tables(void)
-{
- PRUint32 i;
- PRUint8 si01, si02, si03, si04, si08, si09, si0B, si0D, si0E;
- struct rijndael_tables_str *rts;
- rts = (struct rijndael_tables_str *)
- PORT_Alloc(sizeof(struct rijndael_tables_str));
- if (!rts)
- return PR_FAILURE;
- for (i = 0; i < 256; i++) {
- /* The forward values */
- si01 = SBOX(i);
- si02 = XTIME(si01);
- si03 = si02 ^ si01;
- rts->T0[i] = WORD4(si02, si01, si01, si03);
- rts->T1[i] = WORD4(si03, si02, si01, si01);
- rts->T2[i] = WORD4(si01, si03, si02, si01);
- rts->T3[i] = WORD4(si01, si01, si03, si02);
- /* The inverse values */
- si01 = SINV(i);
- si02 = XTIME(si01);
- si04 = XTIME(si02);
- si08 = XTIME(si04);
- si03 = si02 ^ si01;
- si09 = si08 ^ si01;
- si0B = si08 ^ si03;
- si0D = si09 ^ si04;
- si0E = si08 ^ si04 ^ si02;
- rts->TInv0[i] = WORD4(si0E, si09, si0D, si0B);
- rts->TInv1[i] = WORD4(si0B, si0E, si09, si0D);
- rts->TInv2[i] = WORD4(si0D, si0B, si0E, si09);
- rts->TInv3[i] = WORD4(si09, si0D, si0B, si0E);
- }
- /* wait until all the values are in to set */
- rijndaelTables = rts;
- return PR_SUCCESS;
-}
-
-#endif /* code to generate tables */
-
/**************************************************************************
*
* Stuff related to the Rijndael key schedule
@@ -389,162 +337,6 @@ rijndael_key_expansion7(AESContext *cx, const unsigned char *key, unsigned int N
}
}
-#if defined(NSS_X86_OR_X64)
-#define EXPAND_KEY128(k, rcon, res) \
- tmp_key = _mm_aeskeygenassist_si128(k, rcon); \
- tmp_key = _mm_shuffle_epi32(tmp_key, 0xFF); \
- tmp = _mm_xor_si128(k, _mm_slli_si128(k, 4)); \
- tmp = _mm_xor_si128(tmp, _mm_slli_si128(tmp, 4)); \
- tmp = _mm_xor_si128(tmp, _mm_slli_si128(tmp, 4)); \
- res = _mm_xor_si128(tmp, tmp_key)
-
-static void
-native_key_expansion128(AESContext *cx, const unsigned char *key)
-{
- __m128i *keySchedule = cx->keySchedule;
- pre_align __m128i tmp_key post_align;
- pre_align __m128i tmp post_align;
- keySchedule[0] = _mm_loadu_si128((__m128i *)key);
- EXPAND_KEY128(keySchedule[0], 0x01, keySchedule[1]);
- EXPAND_KEY128(keySchedule[1], 0x02, keySchedule[2]);
- EXPAND_KEY128(keySchedule[2], 0x04, keySchedule[3]);
- EXPAND_KEY128(keySchedule[3], 0x08, keySchedule[4]);
- EXPAND_KEY128(keySchedule[4], 0x10, keySchedule[5]);
- EXPAND_KEY128(keySchedule[5], 0x20, keySchedule[6]);
- EXPAND_KEY128(keySchedule[6], 0x40, keySchedule[7]);
- EXPAND_KEY128(keySchedule[7], 0x80, keySchedule[8]);
- EXPAND_KEY128(keySchedule[8], 0x1B, keySchedule[9]);
- EXPAND_KEY128(keySchedule[9], 0x36, keySchedule[10]);
-}
-
-#define EXPAND_KEY192_PART1(res, k0, kt, rcon) \
- tmp2 = _mm_slli_si128(k0, 4); \
- tmp1 = _mm_xor_si128(k0, tmp2); \
- tmp2 = _mm_slli_si128(tmp2, 4); \
- tmp1 = _mm_xor_si128(_mm_xor_si128(tmp1, tmp2), _mm_slli_si128(tmp2, 4)); \
- tmp2 = _mm_aeskeygenassist_si128(kt, rcon); \
- res = _mm_xor_si128(tmp1, _mm_shuffle_epi32(tmp2, 0x55))
-
-#define EXPAND_KEY192_PART2(res, k1, k2) \
- tmp2 = _mm_xor_si128(k1, _mm_slli_si128(k1, 4)); \
- res = _mm_xor_si128(tmp2, _mm_shuffle_epi32(k2, 0xFF))
-
-#define EXPAND_KEY192(k0, res1, res2, res3, carry, rcon1, rcon2) \
- EXPAND_KEY192_PART1(tmp3, k0, res1, rcon1); \
- EXPAND_KEY192_PART2(carry, res1, tmp3); \
- res1 = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(res1), \
- _mm_castsi128_pd(tmp3), 0)); \
- res2 = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(tmp3), \
- _mm_castsi128_pd(carry), 1)); \
- EXPAND_KEY192_PART1(res3, tmp3, carry, rcon2)
-
-static void
-native_key_expansion192(AESContext *cx, const unsigned char *key)
-{
- __m128i *keySchedule = cx->keySchedule;
- pre_align __m128i tmp1 post_align;
- pre_align __m128i tmp2 post_align;
- pre_align __m128i tmp3 post_align;
- pre_align __m128i carry post_align;
- keySchedule[0] = _mm_loadu_si128((__m128i *)key);
- keySchedule[1] = _mm_loadu_si128((__m128i *)(key + 16));
- EXPAND_KEY192(keySchedule[0], keySchedule[1], keySchedule[2],
- keySchedule[3], carry, 0x1, 0x2);
- EXPAND_KEY192_PART2(keySchedule[4], carry, keySchedule[3]);
- EXPAND_KEY192(keySchedule[3], keySchedule[4], keySchedule[5],
- keySchedule[6], carry, 0x4, 0x8);
- EXPAND_KEY192_PART2(keySchedule[7], carry, keySchedule[6]);
- EXPAND_KEY192(keySchedule[6], keySchedule[7], keySchedule[8],
- keySchedule[9], carry, 0x10, 0x20);
- EXPAND_KEY192_PART2(keySchedule[10], carry, keySchedule[9]);
- EXPAND_KEY192(keySchedule[9], keySchedule[10], keySchedule[11],
- keySchedule[12], carry, 0x40, 0x80);
-}
-
-#define EXPAND_KEY256_PART(res, rconx, k1x, k2x, X) \
- tmp_key = _mm_shuffle_epi32(_mm_aeskeygenassist_si128(k2x, rconx), X); \
- tmp2 = _mm_slli_si128(k1x, 4); \
- tmp1 = _mm_xor_si128(k1x, tmp2); \
- tmp2 = _mm_slli_si128(tmp2, 4); \
- tmp1 = _mm_xor_si128(_mm_xor_si128(tmp1, tmp2), _mm_slli_si128(tmp2, 4)); \
- res = _mm_xor_si128(tmp1, tmp_key);
-
-#define EXPAND_KEY256(res1, res2, k1, k2, rcon) \
- EXPAND_KEY256_PART(res1, rcon, k1, k2, 0xFF); \
- EXPAND_KEY256_PART(res2, 0x00, k2, res1, 0xAA)
-
-static void
-native_key_expansion256(AESContext *cx, const unsigned char *key)
-{
- __m128i *keySchedule = cx->keySchedule;
- pre_align __m128i tmp_key post_align;
- pre_align __m128i tmp1 post_align;
- pre_align __m128i tmp2 post_align;
- keySchedule[0] = _mm_loadu_si128((__m128i *)key);
- keySchedule[1] = _mm_loadu_si128((__m128i *)(key + 16));
- EXPAND_KEY256(keySchedule[2], keySchedule[3], keySchedule[0],
- keySchedule[1], 0x01);
- EXPAND_KEY256(keySchedule[4], keySchedule[5], keySchedule[2],
- keySchedule[3], 0x02);
- EXPAND_KEY256(keySchedule[6], keySchedule[7], keySchedule[4],
- keySchedule[5], 0x04);
- EXPAND_KEY256(keySchedule[8], keySchedule[9], keySchedule[6],
- keySchedule[7], 0x08);
- EXPAND_KEY256(keySchedule[10], keySchedule[11], keySchedule[8],
- keySchedule[9], 0x10);
- EXPAND_KEY256(keySchedule[12], keySchedule[13], keySchedule[10],
- keySchedule[11], 0x20);
- EXPAND_KEY256_PART(keySchedule[14], 0x40, keySchedule[12],
- keySchedule[13], 0xFF);
-}
-
-#endif /* NSS_X86_OR_X64 */
-
-/*
- * AES key expansion using aes-ni instructions.
- */
-static void
-native_key_expansion(AESContext *cx, const unsigned char *key, unsigned int Nk)
-{
-#ifdef NSS_X86_OR_X64
- switch (Nk) {
- case 4:
- native_key_expansion128(cx, key);
- return;
- case 6:
- native_key_expansion192(cx, key);
- return;
- case 8:
- native_key_expansion256(cx, key);
- return;
- default:
- /* This shouldn't happen. */
- PORT_Assert(0);
- }
-#else
- PORT_Assert(0);
-#endif /* NSS_X86_OR_X64 */
-}
-
-static void
-native_encryptBlock(AESContext *cx,
- unsigned char *output,
- const unsigned char *input)
-{
-#ifdef NSS_X86_OR_X64
- int i;
- pre_align __m128i m post_align = _mm_loadu_si128((__m128i *)input);
- m = _mm_xor_si128(m, cx->keySchedule[0]);
- for (i = 1; i < cx->Nr; ++i) {
- m = _mm_aesenc_si128(m, cx->keySchedule[i]);
- }
- m = _mm_aesenclast_si128(m, cx->keySchedule[cx->Nr]);
- _mm_storeu_si128((__m128i *)output, m);
-#else
- PORT_Assert(0);
-#endif /* NSS_X86_OR_X64 */
-}
-
/* rijndael_key_expansion
*
* Generate the expanded key from the key input by the user.
@@ -910,7 +702,7 @@ rijndael_encryptECB(AESContext *cx, unsigned char *output,
if (aesni_support()) {
/* Use hardware acceleration for normal AES parameters. */
- encryptor = &native_encryptBlock;
+ encryptor = &rijndael_native_encryptBlock;
} else {
encryptor = &rijndael_encryptBlock128;
}
@@ -1017,14 +809,7 @@ rijndael_decryptCBC(AESContext *cx, unsigned char *output,
AESContext *
AES_AllocateContext(void)
{
- /* aligned_alloc is C11 so we have to do it the old way. */
- AESContext *ctx = PORT_ZAlloc(sizeof(AESContext) + 15);
- if (ctx == NULL) {
- PORT_SetError(SEC_ERROR_NO_MEMORY);
- return NULL;
- }
- ctx->mem = ctx;
- return (AESContext *)(((uintptr_t)ctx + 15) & ~(uintptr_t)0x0F);
+ return PORT_ZNewAligned(AESContext, 16, mem);
}
/*
@@ -1107,22 +892,13 @@ aes_InitContext(AESContext *cx, const unsigned char *key, unsigned int keysize,
} else
#endif
{
-
-#if defined(RIJNDAEL_GENERATE_TABLES) || \
- defined(RIJNDAEL_GENERATE_TABLES_MACRO)
- if (rijndaelTables == NULL) {
- if (PR_CallOnce(&coRTInit, init_rijndael_tables) != PR_SUCCESS) {
- return SECFailure;
- }
- }
-#endif
/* Generate expanded key */
if (encrypt) {
if (use_hw_aes && (cx->mode == NSS_AES_GCM || cx->mode == NSS_AES ||
cx->mode == NSS_AES_CTR)) {
PORT_Assert(keysize == 16 || keysize == 24 || keysize == 32);
/* Prepare hardware key for normal AES parameters. */
- native_key_expansion(cx, key, Nk);
+ rijndael_native_key_expansion(cx, key, Nk);
} else {
rijndael_key_expansion(cx, key, Nk);
}
diff --git a/security/nss/lib/freebl/rijndael.h b/security/nss/lib/freebl/rijndael.h
index 1f4a8a9f7..1b63a323d 100644
--- a/security/nss/lib/freebl/rijndael.h
+++ b/security/nss/lib/freebl/rijndael.h
@@ -8,8 +8,22 @@
#include "blapii.h"
#include <stdint.h>
-#ifdef NSS_X86_OR_X64
-#include <wmmintrin.h> /* aes-ni */
+#if defined(NSS_X86_OR_X64)
+/* GCC <= 4.8 doesn't support including emmintrin.h without enabling SSE2 */
+#if !defined(__clang__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+ (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 8))
+#pragma GCC push_options
+#pragma GCC target("sse2")
+#undef NSS_DISABLE_SSE2
+#define NSS_DISABLE_SSE2 1
+#endif /* GCC <= 4.8 */
+
+#include <emmintrin.h> /* __m128i */
+
+#ifdef NSS_DISABLE_SSE2
+#undef NSS_DISABLE_SSE2
+#pragma GCC pop_options
+#endif /* NSS_DISABLE_SSE2 */
#endif
typedef void AESBlockFunc(AESContext *cx,
diff --git a/security/nss/lib/freebl/rsa.c b/security/nss/lib/freebl/rsa.c
index 7354d9317..a08636de6 100644
--- a/security/nss/lib/freebl/rsa.c
+++ b/security/nss/lib/freebl/rsa.c
@@ -276,7 +276,10 @@ RSAPrivateKey *
RSA_NewKey(int keySizeInBits, SECItem *publicExponent)
{
unsigned int primeLen;
- mp_int p, q, e, d;
+ mp_int p = { 0, 0, 0, NULL };
+ mp_int q = { 0, 0, 0, NULL };
+ mp_int e = { 0, 0, 0, NULL };
+ mp_int d = { 0, 0, 0, NULL };
int kiter;
int max_attempts;
mp_err err = MP_OKAY;
@@ -290,34 +293,46 @@ RSA_NewKey(int keySizeInBits, SECItem *publicExponent)
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return NULL;
}
- /* 1. Allocate arena & key */
+ /* 1. Set the public exponent and check if it's uneven and greater than 2.*/
+ MP_DIGITS(&e) = 0;
+ CHECK_MPI_OK(mp_init(&e));
+ SECITEM_TO_MPINT(*publicExponent, &e);
+ if (mp_iseven(&e) || !(mp_cmp_d(&e, 2) > 0)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto cleanup;
+ }
+#ifndef NSS_FIPS_DISABLED
+ /* Check that the exponent is not smaller than 65537 */
+ if (mp_cmp_d(&e, 0x10001) < 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto cleanup;
+ }
+#endif
+
+ /* 2. Allocate arena & key */
arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE);
if (!arena) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
- return NULL;
+ goto cleanup;
}
key = PORT_ArenaZNew(arena, RSAPrivateKey);
if (!key) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
- PORT_FreeArena(arena, PR_TRUE);
- return NULL;
+ goto cleanup;
}
key->arena = arena;
/* length of primes p and q (in bytes) */
primeLen = keySizeInBits / (2 * PR_BITS_PER_BYTE);
MP_DIGITS(&p) = 0;
MP_DIGITS(&q) = 0;
- MP_DIGITS(&e) = 0;
MP_DIGITS(&d) = 0;
CHECK_MPI_OK(mp_init(&p));
CHECK_MPI_OK(mp_init(&q));
- CHECK_MPI_OK(mp_init(&e));
CHECK_MPI_OK(mp_init(&d));
- /* 2. Set the version number (PKCS1 v1.5 says it should be zero) */
+ /* 3. Set the version number (PKCS1 v1.5 says it should be zero) */
SECITEM_AllocItem(arena, &key->version, 1);
key->version.data[0] = 0;
- /* 3. Set the public exponent */
- SECITEM_TO_MPINT(*publicExponent, &e);
+
kiter = 0;
max_attempts = 5 * (keySizeInBits / 2); /* FIPS 186-4 B.3.3 steps 4.7 and 5.8 */
do {
diff --git a/security/nss/lib/freebl/sha512.c b/security/nss/lib/freebl/sha512.c
index 528f884b2..c1cfb7376 100644
--- a/security/nss/lib/freebl/sha512.c
+++ b/security/nss/lib/freebl/sha512.c
@@ -19,6 +19,7 @@
#include "secport.h" /* for PORT_XXX */
#include "blapi.h"
#include "sha256.h" /* for struct SHA256ContextStr */
+#include "crypto_primitives.h"
/* ============= Common constants and defines ======================= */
@@ -648,15 +649,6 @@ SHA224_Clone(SHA224Context *dest, SHA224Context *src)
/* common #defines for SHA512 and SHA384 */
#if defined(HAVE_LONG_LONG)
-#if defined(_MSC_VER)
-#pragma intrinsic(_rotr64, _rotl64)
-#define ROTR64(x, n) _rotr64(x, n)
-#define ROTL64(x, n) _rotl64(x, n)
-#else
-#define ROTR64(x, n) ((x >> n) | (x << (64 - n)))
-#define ROTL64(x, n) ((x << n) | (x >> (64 - n)))
-#endif
-
#define S0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39))
#define S1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41))
#define s0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ SHR(x, 7))
@@ -670,36 +662,7 @@ SHA224_Clone(SHA224Context *dest, SHA224Context *src)
#define ULLC(hi, lo) 0x##hi##lo##ULL
#endif
-#if defined(IS_LITTLE_ENDIAN)
-#if defined(_MSC_VER)
-#pragma intrinsic(_byteswap_uint64)
-#define SHA_HTONLL(x) _byteswap_uint64(x)
-
-#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__x86_64))
-static __inline__ PRUint64
-swap8b(PRUint64 value)
-{
- __asm__("bswapq %0"
- : "+r"(value));
- return (value);
-}
-#define SHA_HTONLL(x) swap8b(x)
-
-#else
-#define SHA_MASK16 ULLC(0000FFFF, 0000FFFF)
-#define SHA_MASK8 ULLC(00FF00FF, 00FF00FF)
-static PRUint64
-swap8b(PRUint64 x)
-{
- PRUint64 t1 = x;
- t1 = ((t1 & SHA_MASK8) << 8) | ((t1 >> 8) & SHA_MASK8);
- t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16);
- return (t1 >> 32) | (t1 << 32);
-}
-#define SHA_HTONLL(x) swap8b(x)
-#endif
-#define BYTESWAP8(x) x = SHA_HTONLL(x)
-#endif /* defined(IS_LITTLE_ENDIAN) */
+#define BYTESWAP8(x) x = FREEBL_HTONLL(x)
#else /* no long long */
@@ -708,8 +671,8 @@ swap8b(PRUint64 x)
{ \
0x##lo##U, 0x##hi##U \
}
-#define SHA_HTONLL(x) (BYTESWAP4(x.lo), BYTESWAP4(x.hi), \
- x.hi ^= x.lo ^= x.hi ^= x.lo, x)
+#define FREEBL_HTONLL(x) (BYTESWAP4(x.lo), BYTESWAP4(x.hi), \
+ x.hi ^= x.lo ^= x.hi ^= x.lo, x)
#define BYTESWAP8(x) \
do { \
PRUint32 tmp; \
diff --git a/security/nss/lib/freebl/shvfy.c b/security/nss/lib/freebl/shvfy.c
index bd9cd1c94..98db4614b 100644
--- a/security/nss/lib/freebl/shvfy.c
+++ b/security/nss/lib/freebl/shvfy.c
@@ -19,6 +19,8 @@
#include "pqg.h"
#include "blapii.h"
+#ifndef NSS_FIPS_DISABLED
+
/*
* Most modern version of Linux support a speed optimization scheme where an
* application called prelink modifies programs and shared libraries to quickly
@@ -537,3 +539,23 @@ BLAPI_VerifySelf(const char *name)
}
return blapi_SHVerify(name, (PRFuncPtr)decodeInt, PR_TRUE);
}
+
+#else /* NSS_FIPS_DISABLED */
+
+PRBool
+BLAPI_SHVerifyFile(const char *shName)
+{
+ return PR_FALSE;
+}
+PRBool
+BLAPI_SHVerify(const char *name, PRFuncPtr addr)
+{
+ return PR_FALSE;
+}
+PRBool
+BLAPI_VerifySelf(const char *name)
+{
+ return PR_FALSE;
+}
+
+#endif /* NSS_FIPS_DISABLED */
diff --git a/security/nss/lib/freebl/stubs.c b/security/nss/lib/freebl/stubs.c
index 8e0784935..4d41ef975 100644
--- a/security/nss/lib/freebl/stubs.c
+++ b/security/nss/lib/freebl/stubs.c
@@ -38,6 +38,11 @@
#include <blapi.h>
#include <private/pprio.h>
+/* Android API < 21 doesn't define RTLD_NOLOAD */
+#ifndef RTLD_NOLOAD
+#define RTLD_NOLOAD 0
+#endif
+
#define FREEBL_NO_WEAK 1
#define WEAK __attribute__((weak))
@@ -136,6 +141,11 @@ STUB_DECLARE(int, PORT_GetError_Util, (void));
STUB_DECLARE(PLArenaPool *, PORT_NewArena_Util, (unsigned long chunksize));
STUB_DECLARE(void, PORT_SetError_Util, (int value));
STUB_DECLARE(void *, PORT_ZAlloc_Util, (size_t len));
+STUB_DECLARE(void *, PORT_ZAllocAligned_Util, (size_t bytes, size_t alignment,
+ void **mem));
+STUB_DECLARE(void *, PORT_ZAllocAlignedOffset_Util, (size_t bytes,
+ size_t alignment,
+ size_t offset));
STUB_DECLARE(void, PORT_ZFree_Util, (void *ptr, size_t len));
STUB_DECLARE(void, PR_Assert, (const char *s, const char *file, PRIntn ln));
@@ -174,11 +184,14 @@ STUB_DECLARE(void, SECITEM_FreeItem_Util, (SECItem * zap, PRBool freeit));
STUB_DECLARE(void, SECITEM_ZfreeItem_Util, (SECItem * zap, PRBool freeit));
STUB_DECLARE(SECOidTag, SECOID_FindOIDTag_Util, (const SECItem *oid));
STUB_DECLARE(int, NSS_SecureMemcmp, (const void *a, const void *b, size_t n));
+STUB_DECLARE(unsigned int, NSS_SecureMemcmpZero, (const void *mem, size_t n));
#define PORT_ZNew_stub(type) (type *)PORT_ZAlloc_stub(sizeof(type))
#define PORT_New_stub(type) (type *)PORT_Alloc_stub(sizeof(type))
#define PORT_ZNewArray_stub(type, num) \
(type *)PORT_ZAlloc_stub(sizeof(type) * (num))
+#define PORT_ZNewAligned_stub(type, alignment, mem) \
+ (type *)PORT_ZAllocAlignedOffset_stub(sizeof(type), alignment, offsetof(type, mem))
/*
* NOTE: in order to support hashing only the memory allocation stubs,
@@ -214,6 +227,52 @@ PORT_ZAlloc_stub(size_t len)
return ptr;
}
+/* aligned_alloc is C11. This is an alternative to get aligned memory. */
+extern void *
+PORT_ZAllocAligned_stub(size_t bytes, size_t alignment, void **mem)
+{
+ STUB_SAFE_CALL3(PORT_ZAllocAligned_Util, bytes, alignment, mem);
+
+ /* This only works if alignement is a power of 2. */
+ if ((alignment == 0) || (alignment & (alignment - 1))) {
+ return NULL;
+ }
+
+ size_t x = alignment - 1;
+ size_t len = (bytes ? bytes : 1) + x;
+
+ if (!mem) {
+ return NULL;
+ }
+
+ /* Always allocate a non-zero amount of bytes */
+ *mem = malloc(len);
+ if (!*mem) {
+ return NULL;
+ }
+
+ memset(*mem, 0, len);
+ return (void *)(((uintptr_t)*mem + x) & ~(uintptr_t)x);
+}
+
+extern void *
+PORT_ZAllocAlignedOffset_stub(size_t size, size_t alignment, size_t offset)
+{
+ STUB_SAFE_CALL3(PORT_ZAllocAlignedOffset_Util, size, alignment, offset);
+ if (offset > size) {
+ return NULL;
+ }
+
+ void *mem = NULL;
+ void *v = PORT_ZAllocAligned_stub(size, alignment, &mem);
+ if (!v) {
+ return NULL;
+ }
+
+ *((void **)((uintptr_t)v + offset)) = mem;
+ return v;
+}
+
extern void
PORT_ZFree_stub(void *ptr, size_t len)
{
@@ -590,6 +649,13 @@ NSS_SecureMemcmp_stub(const void *a, const void *b, size_t n)
abort();
}
+extern unsigned int
+NSS_SecureMemcmpZero_stub(const void *mem, size_t n)
+{
+ STUB_SAFE_CALL2(NSS_SecureMemcmpZero, mem, n);
+ abort();
+}
+
#ifdef FREEBL_NO_WEAK
static const char *nsprLibName = SHLIB_PREFIX "nspr4." SHLIB_SUFFIX;
@@ -642,6 +708,7 @@ freebl_InitNSSUtil(void *lib)
STUB_FETCH_FUNCTION(SECITEM_ZfreeItem_Util);
STUB_FETCH_FUNCTION(SECOID_FindOIDTag_Util);
STUB_FETCH_FUNCTION(NSS_SecureMemcmp);
+ STUB_FETCH_FUNCTION(NSS_SecureMemcmpZero);
return SECSuccess;
}
diff --git a/security/nss/lib/freebl/stubs.h b/security/nss/lib/freebl/stubs.h
index 25ec394ec..e63cf7a5d 100644
--- a/security/nss/lib/freebl/stubs.h
+++ b/security/nss/lib/freebl/stubs.h
@@ -30,6 +30,8 @@
#define PORT_SetError PORT_SetError_stub
#define PORT_ZAlloc PORT_ZAlloc_stub
#define PORT_ZFree PORT_ZFree_stub
+#define PORT_ZAllocAligned PORT_ZAllocAligned_stub
+#define PORT_ZAllocAlignedOffset PORT_ZAllocAlignedOffset_stub
#define SECITEM_AllocItem SECITEM_AllocItem_stub
#define SECITEM_CompareItem SECITEM_CompareItem_stub
@@ -38,6 +40,7 @@
#define SECITEM_ZfreeItem SECITEM_ZfreeItem_stub
#define SECOID_FindOIDTag SECOID_FindOIDTag_stub
#define NSS_SecureMemcmp NSS_SecureMemcmp_stub
+#define NSS_SecureMemcmpZero NSS_SecureMemcmpZero_stub
#define PR_Assert PR_Assert_stub
#define PR_Access PR_Access_stub
diff --git a/security/nss/lib/freebl/verified/FStar.c b/security/nss/lib/freebl/verified/FStar.c
new file mode 100644
index 000000000..4e5f6d50d
--- /dev/null
+++ b/security/nss/lib/freebl/verified/FStar.c
@@ -0,0 +1,255 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file was auto-generated by KreMLin! */
+
+#include "FStar.h"
+
+static uint64_t
+FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b)
+{
+ return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (uint32_t)63U;
+}
+
+static uint64_t
+FStar_UInt128_carry(uint64_t a, uint64_t b)
+{
+ return FStar_UInt128_constant_time_carry(a, b);
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return (
+ (FStar_UInt128_uint128){
+ .low = a.low + b.low,
+ .high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return (
+ (FStar_UInt128_uint128){
+ .low = a.low + b.low,
+ .high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return (
+ (FStar_UInt128_uint128){
+ .low = a.low - b.low,
+ .high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low) });
+}
+
+static FStar_UInt128_uint128
+FStar_UInt128_sub_mod_impl(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return (
+ (FStar_UInt128_uint128){
+ .low = a.low - b.low,
+ .high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return FStar_UInt128_sub_mod_impl(a, b);
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return ((FStar_UInt128_uint128){.low = a.low & b.low, .high = a.high & b.high });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return ((FStar_UInt128_uint128){.low = a.low ^ b.low, .high = a.high ^ b.high });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return ((FStar_UInt128_uint128){.low = a.low | b.low, .high = a.high | b.high });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_lognot(FStar_UInt128_uint128 a)
+{
+ return ((FStar_UInt128_uint128){.low = ~a.low, .high = ~a.high });
+}
+
+static uint32_t FStar_UInt128_u32_64 = (uint32_t)64U;
+
+static uint64_t
+FStar_UInt128_add_u64_shift_left(uint64_t hi, uint64_t lo, uint32_t s)
+{
+ return (hi << s) + (lo >> (FStar_UInt128_u32_64 - s));
+}
+
+static uint64_t
+FStar_UInt128_add_u64_shift_left_respec(uint64_t hi, uint64_t lo, uint32_t s)
+{
+ return FStar_UInt128_add_u64_shift_left(hi, lo, s);
+}
+
+static FStar_UInt128_uint128
+FStar_UInt128_shift_left_small(FStar_UInt128_uint128 a, uint32_t s)
+{
+ if (s == (uint32_t)0U)
+ return a;
+ else
+ return (
+ (FStar_UInt128_uint128){
+ .low = a.low << s,
+ .high = FStar_UInt128_add_u64_shift_left_respec(a.high, a.low, s) });
+}
+
+static FStar_UInt128_uint128
+FStar_UInt128_shift_left_large(FStar_UInt128_uint128 a, uint32_t s)
+{
+ return ((FStar_UInt128_uint128){.low = (uint64_t)0U, .high = a.low << (s - FStar_UInt128_u32_64) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s)
+{
+ if (s < FStar_UInt128_u32_64)
+ return FStar_UInt128_shift_left_small(a, s);
+ else
+ return FStar_UInt128_shift_left_large(a, s);
+}
+
+static uint64_t
+FStar_UInt128_add_u64_shift_right(uint64_t hi, uint64_t lo, uint32_t s)
+{
+ return (lo >> s) + (hi << (FStar_UInt128_u32_64 - s));
+}
+
+static uint64_t
+FStar_UInt128_add_u64_shift_right_respec(uint64_t hi, uint64_t lo, uint32_t s)
+{
+ return FStar_UInt128_add_u64_shift_right(hi, lo, s);
+}
+
+static FStar_UInt128_uint128
+FStar_UInt128_shift_right_small(FStar_UInt128_uint128 a, uint32_t s)
+{
+ if (s == (uint32_t)0U)
+ return a;
+ else
+ return (
+ (FStar_UInt128_uint128){
+ .low = FStar_UInt128_add_u64_shift_right_respec(a.high, a.low, s),
+ .high = a.high >> s });
+}
+
+static FStar_UInt128_uint128
+FStar_UInt128_shift_right_large(FStar_UInt128_uint128 a, uint32_t s)
+{
+ return ((FStar_UInt128_uint128){.low = a.high >> (s - FStar_UInt128_u32_64), .high = (uint64_t)0U });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s)
+{
+ if (s < FStar_UInt128_u32_64)
+ return FStar_UInt128_shift_right_small(a, s);
+ else
+ return FStar_UInt128_shift_right_large(a, s);
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return (
+ (FStar_UInt128_uint128){
+ .low = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high),
+ .high = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b)
+{
+ return (
+ (FStar_UInt128_uint128){
+ .low = (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)),
+ .high = (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_uint64_to_uint128(uint64_t a)
+{
+ return ((FStar_UInt128_uint128){.low = a, .high = (uint64_t)0U });
+}
+
+uint64_t
+FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a)
+{
+ return a.low;
+}
+
+static uint64_t FStar_UInt128_u64_l32_mask = (uint64_t)0xffffffffU;
+
+static uint64_t
+FStar_UInt128_u64_mod_32(uint64_t a)
+{
+ return a & FStar_UInt128_u64_l32_mask;
+}
+
+static uint32_t FStar_UInt128_u32_32 = (uint32_t)32U;
+
+static K___uint64_t_uint64_t_uint64_t_uint64_t
+FStar_UInt128_mul_wide_impl_t_(uint64_t x, uint64_t y)
+{
+ return (
+ (K___uint64_t_uint64_t_uint64_t_uint64_t){
+ .fst = FStar_UInt128_u64_mod_32(x),
+ .snd = FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y)),
+ .thd = x >> FStar_UInt128_u32_32,
+ .f3 = (x >> FStar_UInt128_u32_32) * FStar_UInt128_u64_mod_32(y) + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32) });
+}
+
+static uint64_t
+FStar_UInt128_u32_combine_(uint64_t hi, uint64_t lo)
+{
+ return lo + (hi << FStar_UInt128_u32_32);
+}
+
+static FStar_UInt128_uint128
+FStar_UInt128_mul_wide_impl(uint64_t x, uint64_t y)
+{
+ K___uint64_t_uint64_t_uint64_t_uint64_t scrut = FStar_UInt128_mul_wide_impl_t_(x, y);
+ uint64_t u1 = scrut.fst;
+ uint64_t w3 = scrut.snd;
+ uint64_t x_ = scrut.thd;
+ uint64_t t_ = scrut.f3;
+ return (
+ (FStar_UInt128_uint128){
+ .low = FStar_UInt128_u32_combine_(u1 * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32(t_),
+ w3),
+ .high = x_ * (y >> FStar_UInt128_u32_32) + (t_ >> FStar_UInt128_u32_32) +
+ ((u1 * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32(t_)) >> FStar_UInt128_u32_32) });
+}
+
+FStar_UInt128_uint128
+FStar_UInt128_mul_wide(uint64_t x, uint64_t y)
+{
+ return FStar_UInt128_mul_wide_impl(x, y);
+}
diff --git a/security/nss/lib/freebl/verified/FStar.h b/security/nss/lib/freebl/verified/FStar.h
new file mode 100644
index 000000000..7b105b8f2
--- /dev/null
+++ b/security/nss/lib/freebl/verified/FStar.h
@@ -0,0 +1,69 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file was auto-generated by KreMLin! */
+#ifndef __FStar_H
+#define __FStar_H
+
+#include "kremlib_base.h"
+
+typedef struct
+{
+ uint64_t low;
+ uint64_t high;
+} FStar_UInt128_uint128;
+
+typedef FStar_UInt128_uint128 FStar_UInt128_t;
+
+extern void FStar_UInt128_constant_time_carry_ok(uint64_t x0, uint64_t x1);
+
+FStar_UInt128_uint128 FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a);
+
+FStar_UInt128_uint128 FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s);
+
+FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s);
+
+FStar_UInt128_uint128 FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b);
+
+FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a);
+
+uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a);
+
+typedef struct
+{
+ uint64_t fst;
+ uint64_t snd;
+ uint64_t thd;
+ uint64_t f3;
+} K___uint64_t_uint64_t_uint64_t_uint64_t;
+
+FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y);
+#endif
diff --git a/security/nss/lib/freebl/verified/Hacl_Chacha20.c b/security/nss/lib/freebl/verified/Hacl_Chacha20.c
new file mode 100644
index 000000000..45a743035
--- /dev/null
+++ b/security/nss/lib/freebl/verified/Hacl_Chacha20.c
@@ -0,0 +1,270 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hacl_Chacha20.h"
+
+static void
+Hacl_Lib_LoadStore32_uint32s_from_le_bytes(uint32_t *output, uint8_t *input, uint32_t len)
+{
+ for (uint32_t i = (uint32_t)0U; i < len; i = i + (uint32_t)1U) {
+ uint8_t *x0 = input + (uint32_t)4U * i;
+ uint32_t inputi = load32_le(x0);
+ output[i] = inputi;
+ }
+}
+
+static void
+Hacl_Lib_LoadStore32_uint32s_to_le_bytes(uint8_t *output, uint32_t *input, uint32_t len)
+{
+ for (uint32_t i = (uint32_t)0U; i < len; i = i + (uint32_t)1U) {
+ uint32_t hd1 = input[i];
+ uint8_t *x0 = output + (uint32_t)4U * i;
+ store32_le(x0, hd1);
+ }
+}
+
+inline static uint32_t
+Hacl_Impl_Chacha20_rotate_left(uint32_t a, uint32_t s)
+{
+ return a << s | a >> ((uint32_t)32U - s);
+}
+
+inline static void
+Hacl_Impl_Chacha20_quarter_round(uint32_t *st, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+ uint32_t sa = st[a];
+ uint32_t sb0 = st[b];
+ st[a] = sa + sb0;
+ uint32_t sd = st[d];
+ uint32_t sa10 = st[a];
+ uint32_t sda = sd ^ sa10;
+ st[d] = Hacl_Impl_Chacha20_rotate_left(sda, (uint32_t)16U);
+ uint32_t sa0 = st[c];
+ uint32_t sb1 = st[d];
+ st[c] = sa0 + sb1;
+ uint32_t sd0 = st[b];
+ uint32_t sa11 = st[c];
+ uint32_t sda0 = sd0 ^ sa11;
+ st[b] = Hacl_Impl_Chacha20_rotate_left(sda0, (uint32_t)12U);
+ uint32_t sa2 = st[a];
+ uint32_t sb2 = st[b];
+ st[a] = sa2 + sb2;
+ uint32_t sd1 = st[d];
+ uint32_t sa12 = st[a];
+ uint32_t sda1 = sd1 ^ sa12;
+ st[d] = Hacl_Impl_Chacha20_rotate_left(sda1, (uint32_t)8U);
+ uint32_t sa3 = st[c];
+ uint32_t sb = st[d];
+ st[c] = sa3 + sb;
+ uint32_t sd2 = st[b];
+ uint32_t sa1 = st[c];
+ uint32_t sda2 = sd2 ^ sa1;
+ st[b] = Hacl_Impl_Chacha20_rotate_left(sda2, (uint32_t)7U);
+}
+
+inline static void
+Hacl_Impl_Chacha20_double_round(uint32_t *st)
+{
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)0U, (uint32_t)4U, (uint32_t)8U, (uint32_t)12U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)1U, (uint32_t)5U, (uint32_t)9U, (uint32_t)13U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)2U, (uint32_t)6U, (uint32_t)10U, (uint32_t)14U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)3U, (uint32_t)7U, (uint32_t)11U, (uint32_t)15U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)0U, (uint32_t)5U, (uint32_t)10U, (uint32_t)15U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)1U, (uint32_t)6U, (uint32_t)11U, (uint32_t)12U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)2U, (uint32_t)7U, (uint32_t)8U, (uint32_t)13U);
+ Hacl_Impl_Chacha20_quarter_round(st, (uint32_t)3U, (uint32_t)4U, (uint32_t)9U, (uint32_t)14U);
+}
+
+inline static void
+Hacl_Impl_Chacha20_rounds(uint32_t *st)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)10U; i = i + (uint32_t)1U)
+ Hacl_Impl_Chacha20_double_round(st);
+}
+
+inline static void
+Hacl_Impl_Chacha20_sum_states(uint32_t *st, uint32_t *st_)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)16U; i = i + (uint32_t)1U) {
+ uint32_t xi = st[i];
+ uint32_t yi = st_[i];
+ st[i] = xi + yi;
+ }
+}
+
+inline static void
+Hacl_Impl_Chacha20_copy_state(uint32_t *st, uint32_t *st_)
+{
+ memcpy(st, st_, (uint32_t)16U * sizeof st_[0U]);
+}
+
+inline static void
+Hacl_Impl_Chacha20_chacha20_core(uint32_t *k, uint32_t *st, uint32_t ctr)
+{
+ st[12U] = ctr;
+ Hacl_Impl_Chacha20_copy_state(k, st);
+ Hacl_Impl_Chacha20_rounds(k);
+ Hacl_Impl_Chacha20_sum_states(k, st);
+}
+
+inline static void
+Hacl_Impl_Chacha20_chacha20_block(uint8_t *stream_block, uint32_t *st, uint32_t ctr)
+{
+ uint32_t st_[16U] = { 0U };
+ Hacl_Impl_Chacha20_chacha20_core(st_, st, ctr);
+ Hacl_Lib_LoadStore32_uint32s_to_le_bytes(stream_block, st_, (uint32_t)16U);
+}
+
+inline static void
+Hacl_Impl_Chacha20_init(uint32_t *st, uint8_t *k, uint8_t *n1)
+{
+ uint32_t *stcst = st;
+ uint32_t *stk = st + (uint32_t)4U;
+ uint32_t *stc = st + (uint32_t)12U;
+ uint32_t *stn = st + (uint32_t)13U;
+ stcst[0U] = (uint32_t)0x61707865U;
+ stcst[1U] = (uint32_t)0x3320646eU;
+ stcst[2U] = (uint32_t)0x79622d32U;
+ stcst[3U] = (uint32_t)0x6b206574U;
+ Hacl_Lib_LoadStore32_uint32s_from_le_bytes(stk, k, (uint32_t)8U);
+ stc[0U] = (uint32_t)0U;
+ Hacl_Lib_LoadStore32_uint32s_from_le_bytes(stn, n1, (uint32_t)3U);
+}
+
+static void
+Hacl_Impl_Chacha20_update(uint8_t *output, uint8_t *plain, uint32_t *st, uint32_t ctr)
+{
+ uint32_t b[48U] = { 0U };
+ uint32_t *k = b;
+ uint32_t *ib = b + (uint32_t)16U;
+ uint32_t *ob = b + (uint32_t)32U;
+ Hacl_Impl_Chacha20_chacha20_core(k, st, ctr);
+ Hacl_Lib_LoadStore32_uint32s_from_le_bytes(ib, plain, (uint32_t)16U);
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)16U; i = i + (uint32_t)1U) {
+ uint32_t xi = ib[i];
+ uint32_t yi = k[i];
+ ob[i] = xi ^ yi;
+ }
+ Hacl_Lib_LoadStore32_uint32s_to_le_bytes(output, ob, (uint32_t)16U);
+}
+
+static void
+Hacl_Impl_Chacha20_update_last(
+ uint8_t *output,
+ uint8_t *plain,
+ uint32_t len,
+ uint32_t *st,
+ uint32_t ctr)
+{
+ uint8_t block[64U] = { 0U };
+ Hacl_Impl_Chacha20_chacha20_block(block, st, ctr);
+ uint8_t *mask = block;
+ for (uint32_t i = (uint32_t)0U; i < len; i = i + (uint32_t)1U) {
+ uint8_t xi = plain[i];
+ uint8_t yi = mask[i];
+ output[i] = xi ^ yi;
+ }
+}
+
+static void
+Hacl_Impl_Chacha20_chacha20_counter_mode_blocks(
+ uint8_t *output,
+ uint8_t *plain,
+ uint32_t num_blocks,
+ uint32_t *st,
+ uint32_t ctr)
+{
+ for (uint32_t i = (uint32_t)0U; i < num_blocks; i = i + (uint32_t)1U) {
+ uint8_t *b = plain + (uint32_t)64U * i;
+ uint8_t *o = output + (uint32_t)64U * i;
+ Hacl_Impl_Chacha20_update(o, b, st, ctr + i);
+ }
+}
+
+static void
+Hacl_Impl_Chacha20_chacha20_counter_mode(
+ uint8_t *output,
+ uint8_t *plain,
+ uint32_t len,
+ uint32_t *st,
+ uint32_t ctr)
+{
+ uint32_t blocks_len = len >> (uint32_t)6U;
+ uint32_t part_len = len & (uint32_t)0x3fU;
+ uint8_t *output_ = output;
+ uint8_t *plain_ = plain;
+ uint8_t *output__ = output + (uint32_t)64U * blocks_len;
+ uint8_t *plain__ = plain + (uint32_t)64U * blocks_len;
+ Hacl_Impl_Chacha20_chacha20_counter_mode_blocks(output_, plain_, blocks_len, st, ctr);
+ if (part_len > (uint32_t)0U)
+ Hacl_Impl_Chacha20_update_last(output__, plain__, part_len, st, ctr + blocks_len);
+}
+
+static void
+Hacl_Impl_Chacha20_chacha20(
+ uint8_t *output,
+ uint8_t *plain,
+ uint32_t len,
+ uint8_t *k,
+ uint8_t *n1,
+ uint32_t ctr)
+{
+ uint32_t buf[16U] = { 0U };
+ uint32_t *st = buf;
+ Hacl_Impl_Chacha20_init(st, k, n1);
+ Hacl_Impl_Chacha20_chacha20_counter_mode(output, plain, len, st, ctr);
+}
+
+void
+Hacl_Chacha20_chacha20_key_block(uint8_t *block, uint8_t *k, uint8_t *n1, uint32_t ctr)
+{
+ uint32_t buf[16U] = { 0U };
+ uint32_t *st = buf;
+ Hacl_Impl_Chacha20_init(st, k, n1);
+ Hacl_Impl_Chacha20_chacha20_block(block, st, ctr);
+}
+
+/*
+ This function implements Chacha20
+
+ val chacha20 :
+ output:uint8_p ->
+ plain:uint8_p{ disjoint output plain } ->
+ len:uint32_t{ v len = length output /\ v len = length plain } ->
+ key:uint8_p{ length key = 32 } ->
+ nonce:uint8_p{ length nonce = 12 } ->
+ ctr:uint32_t{ v ctr + length plain / 64 < pow2 32 } ->
+ Stack unit
+ (requires
+ fun h -> live h output /\ live h plain /\ live h nonce /\ live h key)
+ (ensures
+ fun h0 _ h1 ->
+ live h1 output /\ live h0 plain /\ modifies_1 output h0 h1 /\
+ live h0 nonce /\
+ live h0 key /\
+ h1.[ output ] ==
+ chacha20_encrypt_bytes h0.[ key ] h0.[ nonce ] (v ctr) h0.[ plain ])
+*/
+void
+Hacl_Chacha20_chacha20(
+ uint8_t *output,
+ uint8_t *plain,
+ uint32_t len,
+ uint8_t *k,
+ uint8_t *n1,
+ uint32_t ctr)
+{
+ Hacl_Impl_Chacha20_chacha20(output, plain, len, k, n1, ctr);
+}
diff --git a/security/nss/lib/freebl/verified/Hacl_Chacha20.h b/security/nss/lib/freebl/verified/Hacl_Chacha20.h
new file mode 100644
index 000000000..f97e44b74
--- /dev/null
+++ b/security/nss/lib/freebl/verified/Hacl_Chacha20.h
@@ -0,0 +1,81 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kremlib.h"
+#ifndef __Hacl_Chacha20_H
+#define __Hacl_Chacha20_H
+
+typedef uint32_t Hacl_Impl_Xor_Lemmas_u32;
+
+typedef uint8_t Hacl_Impl_Xor_Lemmas_u8;
+
+typedef uint8_t *Hacl_Lib_LoadStore32_uint8_p;
+
+typedef uint32_t Hacl_Impl_Chacha20_u32;
+
+typedef uint32_t Hacl_Impl_Chacha20_h32;
+
+typedef uint8_t *Hacl_Impl_Chacha20_uint8_p;
+
+typedef uint32_t *Hacl_Impl_Chacha20_state;
+
+typedef uint32_t Hacl_Impl_Chacha20_idx;
+
+typedef struct
+{
+ void *k;
+ void *n;
+} Hacl_Impl_Chacha20_log_t_;
+
+typedef void *Hacl_Impl_Chacha20_log_t;
+
+typedef uint32_t Hacl_Lib_Create_h32;
+
+typedef uint8_t *Hacl_Chacha20_uint8_p;
+
+typedef uint32_t Hacl_Chacha20_uint32_t;
+
+void Hacl_Chacha20_chacha20_key_block(uint8_t *block, uint8_t *k, uint8_t *n1, uint32_t ctr);
+
+/*
+ This function implements Chacha20
+
+ val chacha20 :
+ output:uint8_p ->
+ plain:uint8_p{ disjoint output plain } ->
+ len:uint32_t{ v len = length output /\ v len = length plain } ->
+ key:uint8_p{ length key = 32 } ->
+ nonce:uint8_p{ length nonce = 12 } ->
+ ctr:uint32_t{ v ctr + length plain / 64 < pow2 32 } ->
+ Stack unit
+ (requires
+ fun h -> live h output /\ live h plain /\ live h nonce /\ live h key)
+ (ensures
+ fun h0 _ h1 ->
+ live h1 output /\ live h0 plain /\ modifies_1 output h0 h1 /\
+ live h0 nonce /\
+ live h0 key /\
+ h1.[ output ] ==
+ chacha20_encrypt_bytes h0.[ key ] h0.[ nonce ] (v ctr) h0.[ plain ])
+*/
+void
+Hacl_Chacha20_chacha20(
+ uint8_t *output,
+ uint8_t *plain,
+ uint32_t len,
+ uint8_t *k,
+ uint8_t *n1,
+ uint32_t ctr);
+#endif
diff --git a/security/nss/lib/freebl/verified/Hacl_Curve25519.c b/security/nss/lib/freebl/verified/Hacl_Curve25519.c
new file mode 100644
index 000000000..f2dcddc57
--- /dev/null
+++ b/security/nss/lib/freebl/verified/Hacl_Curve25519.c
@@ -0,0 +1,845 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hacl_Curve25519.h"
+
+static void
+Hacl_Bignum_Modulo_carry_top(uint64_t *b)
+{
+ uint64_t b4 = b[4U];
+ uint64_t b0 = b[0U];
+ uint64_t b4_ = b4 & (uint64_t)0x7ffffffffffffU;
+ uint64_t b0_ = b0 + (uint64_t)19U * (b4 >> (uint32_t)51U);
+ b[4U] = b4_;
+ b[0U] = b0_;
+}
+
+inline static void
+Hacl_Bignum_Fproduct_copy_from_wide_(uint64_t *output, FStar_UInt128_t *input)
+{
+ {
+ FStar_UInt128_t xi = input[0U];
+ output[0U] = FStar_UInt128_uint128_to_uint64(xi);
+ }
+ {
+ FStar_UInt128_t xi = input[1U];
+ output[1U] = FStar_UInt128_uint128_to_uint64(xi);
+ }
+ {
+ FStar_UInt128_t xi = input[2U];
+ output[2U] = FStar_UInt128_uint128_to_uint64(xi);
+ }
+ {
+ FStar_UInt128_t xi = input[3U];
+ output[3U] = FStar_UInt128_uint128_to_uint64(xi);
+ }
+ {
+ FStar_UInt128_t xi = input[4U];
+ output[4U] = FStar_UInt128_uint128_to_uint64(xi);
+ }
+}
+
+inline static void
+Hacl_Bignum_Fproduct_sum_scalar_multiplication_(
+ FStar_UInt128_t *output,
+ uint64_t *input,
+ uint64_t s)
+{
+ {
+ FStar_UInt128_t xi = output[0U];
+ uint64_t yi = input[0U];
+ output[0U] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s));
+ }
+ {
+ FStar_UInt128_t xi = output[1U];
+ uint64_t yi = input[1U];
+ output[1U] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s));
+ }
+ {
+ FStar_UInt128_t xi = output[2U];
+ uint64_t yi = input[2U];
+ output[2U] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s));
+ }
+ {
+ FStar_UInt128_t xi = output[3U];
+ uint64_t yi = input[3U];
+ output[3U] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s));
+ }
+ {
+ FStar_UInt128_t xi = output[4U];
+ uint64_t yi = input[4U];
+ output[4U] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s));
+ }
+}
+
+inline static void
+Hacl_Bignum_Fproduct_carry_wide_(FStar_UInt128_t *tmp)
+{
+ {
+ uint32_t ctr = (uint32_t)0U;
+ FStar_UInt128_t tctr = tmp[ctr];
+ FStar_UInt128_t tctrp1 = tmp[ctr + (uint32_t)1U];
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(tctr) & (uint64_t)0x7ffffffffffffU;
+ FStar_UInt128_t c = FStar_UInt128_shift_right(tctr, (uint32_t)51U);
+ tmp[ctr] = FStar_UInt128_uint64_to_uint128(r0);
+ tmp[ctr + (uint32_t)1U] = FStar_UInt128_add(tctrp1, c);
+ }
+ {
+ uint32_t ctr = (uint32_t)1U;
+ FStar_UInt128_t tctr = tmp[ctr];
+ FStar_UInt128_t tctrp1 = tmp[ctr + (uint32_t)1U];
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(tctr) & (uint64_t)0x7ffffffffffffU;
+ FStar_UInt128_t c = FStar_UInt128_shift_right(tctr, (uint32_t)51U);
+ tmp[ctr] = FStar_UInt128_uint64_to_uint128(r0);
+ tmp[ctr + (uint32_t)1U] = FStar_UInt128_add(tctrp1, c);
+ }
+ {
+ uint32_t ctr = (uint32_t)2U;
+ FStar_UInt128_t tctr = tmp[ctr];
+ FStar_UInt128_t tctrp1 = tmp[ctr + (uint32_t)1U];
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(tctr) & (uint64_t)0x7ffffffffffffU;
+ FStar_UInt128_t c = FStar_UInt128_shift_right(tctr, (uint32_t)51U);
+ tmp[ctr] = FStar_UInt128_uint64_to_uint128(r0);
+ tmp[ctr + (uint32_t)1U] = FStar_UInt128_add(tctrp1, c);
+ }
+ {
+ uint32_t ctr = (uint32_t)3U;
+ FStar_UInt128_t tctr = tmp[ctr];
+ FStar_UInt128_t tctrp1 = tmp[ctr + (uint32_t)1U];
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(tctr) & (uint64_t)0x7ffffffffffffU;
+ FStar_UInt128_t c = FStar_UInt128_shift_right(tctr, (uint32_t)51U);
+ tmp[ctr] = FStar_UInt128_uint64_to_uint128(r0);
+ tmp[ctr + (uint32_t)1U] = FStar_UInt128_add(tctrp1, c);
+ }
+}
+
+inline static void
+Hacl_Bignum_Fmul_shift_reduce(uint64_t *output)
+{
+ uint64_t tmp = output[4U];
+ {
+ uint32_t ctr = (uint32_t)5U - (uint32_t)0U - (uint32_t)1U;
+ uint64_t z = output[ctr - (uint32_t)1U];
+ output[ctr] = z;
+ }
+ {
+ uint32_t ctr = (uint32_t)5U - (uint32_t)1U - (uint32_t)1U;
+ uint64_t z = output[ctr - (uint32_t)1U];
+ output[ctr] = z;
+ }
+ {
+ uint32_t ctr = (uint32_t)5U - (uint32_t)2U - (uint32_t)1U;
+ uint64_t z = output[ctr - (uint32_t)1U];
+ output[ctr] = z;
+ }
+ {
+ uint32_t ctr = (uint32_t)5U - (uint32_t)3U - (uint32_t)1U;
+ uint64_t z = output[ctr - (uint32_t)1U];
+ output[ctr] = z;
+ }
+ output[0U] = tmp;
+ uint64_t b0 = output[0U];
+ output[0U] = (uint64_t)19U * b0;
+}
+
+static void
+Hacl_Bignum_Fmul_mul_shift_reduce_(FStar_UInt128_t *output, uint64_t *input, uint64_t *input21)
+{
+ {
+ uint64_t input2i = input21[0U];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+ Hacl_Bignum_Fmul_shift_reduce(input);
+ }
+ {
+ uint64_t input2i = input21[1U];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+ Hacl_Bignum_Fmul_shift_reduce(input);
+ }
+ {
+ uint64_t input2i = input21[2U];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+ Hacl_Bignum_Fmul_shift_reduce(input);
+ }
+ {
+ uint64_t input2i = input21[3U];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+ Hacl_Bignum_Fmul_shift_reduce(input);
+ }
+ uint32_t i = (uint32_t)4U;
+ uint64_t input2i = input21[i];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+}
+
+inline static void
+Hacl_Bignum_Fmul_fmul(uint64_t *output, uint64_t *input, uint64_t *input21)
+{
+ uint64_t tmp[5U] = { 0U };
+ memcpy(tmp, input, (uint32_t)5U * sizeof input[0U]);
+ KRML_CHECK_SIZE(FStar_UInt128_uint64_to_uint128((uint64_t)0U), (uint32_t)5U);
+ FStar_UInt128_t t[5U];
+ for (uint32_t _i = 0U; _i < (uint32_t)5U; ++_i)
+ t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U);
+ Hacl_Bignum_Fmul_mul_shift_reduce_(t, tmp, input21);
+ Hacl_Bignum_Fproduct_carry_wide_(t);
+ FStar_UInt128_t b4 = t[4U];
+ FStar_UInt128_t b0 = t[0U];
+ FStar_UInt128_t
+ b4_ = FStar_UInt128_logand(b4, FStar_UInt128_uint64_to_uint128((uint64_t)0x7ffffffffffffU));
+ FStar_UInt128_t
+ b0_ =
+ FStar_UInt128_add(b0,
+ FStar_UInt128_mul_wide((uint64_t)19U,
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b4, (uint32_t)51U))));
+ t[4U] = b4_;
+ t[0U] = b0_;
+ Hacl_Bignum_Fproduct_copy_from_wide_(output, t);
+ uint64_t i0 = output[0U];
+ uint64_t i1 = output[1U];
+ uint64_t i0_ = i0 & (uint64_t)0x7ffffffffffffU;
+ uint64_t i1_ = i1 + (i0 >> (uint32_t)51U);
+ output[0U] = i0_;
+ output[1U] = i1_;
+}
+
+inline static void
+Hacl_Bignum_Fsquare_fsquare__(FStar_UInt128_t *tmp, uint64_t *output)
+{
+ uint64_t r0 = output[0U];
+ uint64_t r1 = output[1U];
+ uint64_t r2 = output[2U];
+ uint64_t r3 = output[3U];
+ uint64_t r4 = output[4U];
+ uint64_t d0 = r0 * (uint64_t)2U;
+ uint64_t d1 = r1 * (uint64_t)2U;
+ uint64_t d2 = r2 * (uint64_t)2U * (uint64_t)19U;
+ uint64_t d419 = r4 * (uint64_t)19U;
+ uint64_t d4 = d419 * (uint64_t)2U;
+ FStar_UInt128_t
+ s0 =
+ FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(r0, r0),
+ FStar_UInt128_mul_wide(d4, r1)),
+ FStar_UInt128_mul_wide(d2, r3));
+ FStar_UInt128_t
+ s1 =
+ FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r1),
+ FStar_UInt128_mul_wide(d4, r2)),
+ FStar_UInt128_mul_wide(r3 * (uint64_t)19U, r3));
+ FStar_UInt128_t
+ s2 =
+ FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r2),
+ FStar_UInt128_mul_wide(r1, r1)),
+ FStar_UInt128_mul_wide(d4, r3));
+ FStar_UInt128_t
+ s3 =
+ FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r3),
+ FStar_UInt128_mul_wide(d1, r2)),
+ FStar_UInt128_mul_wide(r4, d419));
+ FStar_UInt128_t
+ s4 =
+ FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r4),
+ FStar_UInt128_mul_wide(d1, r3)),
+ FStar_UInt128_mul_wide(r2, r2));
+ tmp[0U] = s0;
+ tmp[1U] = s1;
+ tmp[2U] = s2;
+ tmp[3U] = s3;
+ tmp[4U] = s4;
+}
+
+inline static void
+Hacl_Bignum_Fsquare_fsquare_(FStar_UInt128_t *tmp, uint64_t *output)
+{
+ Hacl_Bignum_Fsquare_fsquare__(tmp, output);
+ Hacl_Bignum_Fproduct_carry_wide_(tmp);
+ FStar_UInt128_t b4 = tmp[4U];
+ FStar_UInt128_t b0 = tmp[0U];
+ FStar_UInt128_t
+ b4_ = FStar_UInt128_logand(b4, FStar_UInt128_uint64_to_uint128((uint64_t)0x7ffffffffffffU));
+ FStar_UInt128_t
+ b0_ =
+ FStar_UInt128_add(b0,
+ FStar_UInt128_mul_wide((uint64_t)19U,
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b4, (uint32_t)51U))));
+ tmp[4U] = b4_;
+ tmp[0U] = b0_;
+ Hacl_Bignum_Fproduct_copy_from_wide_(output, tmp);
+ uint64_t i0 = output[0U];
+ uint64_t i1 = output[1U];
+ uint64_t i0_ = i0 & (uint64_t)0x7ffffffffffffU;
+ uint64_t i1_ = i1 + (i0 >> (uint32_t)51U);
+ output[0U] = i0_;
+ output[1U] = i1_;
+}
+
+static void
+Hacl_Bignum_Fsquare_fsquare_times_(uint64_t *input, FStar_UInt128_t *tmp, uint32_t count1)
+{
+ Hacl_Bignum_Fsquare_fsquare_(tmp, input);
+ for (uint32_t i = (uint32_t)1U; i < count1; i = i + (uint32_t)1U)
+ Hacl_Bignum_Fsquare_fsquare_(tmp, input);
+}
+
+inline static void
+Hacl_Bignum_Fsquare_fsquare_times(uint64_t *output, uint64_t *input, uint32_t count1)
+{
+ KRML_CHECK_SIZE(FStar_UInt128_uint64_to_uint128((uint64_t)0U), (uint32_t)5U);
+ FStar_UInt128_t t[5U];
+ for (uint32_t _i = 0U; _i < (uint32_t)5U; ++_i)
+ t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U);
+ memcpy(output, input, (uint32_t)5U * sizeof input[0U]);
+ Hacl_Bignum_Fsquare_fsquare_times_(output, t, count1);
+}
+
+inline static void
+Hacl_Bignum_Fsquare_fsquare_times_inplace(uint64_t *output, uint32_t count1)
+{
+ KRML_CHECK_SIZE(FStar_UInt128_uint64_to_uint128((uint64_t)0U), (uint32_t)5U);
+ FStar_UInt128_t t[5U];
+ for (uint32_t _i = 0U; _i < (uint32_t)5U; ++_i)
+ t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U);
+ Hacl_Bignum_Fsquare_fsquare_times_(output, t, count1);
+}
+
+inline static void
+Hacl_Bignum_Crecip_crecip(uint64_t *out, uint64_t *z)
+{
+ uint64_t buf[20U] = { 0U };
+ uint64_t *a = buf;
+ uint64_t *t00 = buf + (uint32_t)5U;
+ uint64_t *b0 = buf + (uint32_t)10U;
+ Hacl_Bignum_Fsquare_fsquare_times(a, z, (uint32_t)1U);
+ Hacl_Bignum_Fsquare_fsquare_times(t00, a, (uint32_t)2U);
+ Hacl_Bignum_Fmul_fmul(b0, t00, z);
+ Hacl_Bignum_Fmul_fmul(a, b0, a);
+ Hacl_Bignum_Fsquare_fsquare_times(t00, a, (uint32_t)1U);
+ Hacl_Bignum_Fmul_fmul(b0, t00, b0);
+ Hacl_Bignum_Fsquare_fsquare_times(t00, b0, (uint32_t)5U);
+ uint64_t *t01 = buf + (uint32_t)5U;
+ uint64_t *b1 = buf + (uint32_t)10U;
+ uint64_t *c0 = buf + (uint32_t)15U;
+ Hacl_Bignum_Fmul_fmul(b1, t01, b1);
+ Hacl_Bignum_Fsquare_fsquare_times(t01, b1, (uint32_t)10U);
+ Hacl_Bignum_Fmul_fmul(c0, t01, b1);
+ Hacl_Bignum_Fsquare_fsquare_times(t01, c0, (uint32_t)20U);
+ Hacl_Bignum_Fmul_fmul(t01, t01, c0);
+ Hacl_Bignum_Fsquare_fsquare_times_inplace(t01, (uint32_t)10U);
+ Hacl_Bignum_Fmul_fmul(b1, t01, b1);
+ Hacl_Bignum_Fsquare_fsquare_times(t01, b1, (uint32_t)50U);
+ uint64_t *a0 = buf;
+ uint64_t *t0 = buf + (uint32_t)5U;
+ uint64_t *b = buf + (uint32_t)10U;
+ uint64_t *c = buf + (uint32_t)15U;
+ Hacl_Bignum_Fmul_fmul(c, t0, b);
+ Hacl_Bignum_Fsquare_fsquare_times(t0, c, (uint32_t)100U);
+ Hacl_Bignum_Fmul_fmul(t0, t0, c);
+ Hacl_Bignum_Fsquare_fsquare_times_inplace(t0, (uint32_t)50U);
+ Hacl_Bignum_Fmul_fmul(t0, t0, b);
+ Hacl_Bignum_Fsquare_fsquare_times_inplace(t0, (uint32_t)5U);
+ Hacl_Bignum_Fmul_fmul(out, t0, a0);
+}
+
+inline static void
+Hacl_Bignum_fsum(uint64_t *a, uint64_t *b)
+{
+ {
+ uint64_t xi = a[0U];
+ uint64_t yi = b[0U];
+ a[0U] = xi + yi;
+ }
+ {
+ uint64_t xi = a[1U];
+ uint64_t yi = b[1U];
+ a[1U] = xi + yi;
+ }
+ {
+ uint64_t xi = a[2U];
+ uint64_t yi = b[2U];
+ a[2U] = xi + yi;
+ }
+ {
+ uint64_t xi = a[3U];
+ uint64_t yi = b[3U];
+ a[3U] = xi + yi;
+ }
+ {
+ uint64_t xi = a[4U];
+ uint64_t yi = b[4U];
+ a[4U] = xi + yi;
+ }
+}
+
+inline static void
+Hacl_Bignum_fdifference(uint64_t *a, uint64_t *b)
+{
+ uint64_t tmp[5U] = { 0U };
+ memcpy(tmp, b, (uint32_t)5U * sizeof b[0U]);
+ uint64_t b0 = tmp[0U];
+ uint64_t b1 = tmp[1U];
+ uint64_t b2 = tmp[2U];
+ uint64_t b3 = tmp[3U];
+ uint64_t b4 = tmp[4U];
+ tmp[0U] = b0 + (uint64_t)0x3fffffffffff68U;
+ tmp[1U] = b1 + (uint64_t)0x3ffffffffffff8U;
+ tmp[2U] = b2 + (uint64_t)0x3ffffffffffff8U;
+ tmp[3U] = b3 + (uint64_t)0x3ffffffffffff8U;
+ tmp[4U] = b4 + (uint64_t)0x3ffffffffffff8U;
+ {
+ uint64_t xi = a[0U];
+ uint64_t yi = tmp[0U];
+ a[0U] = yi - xi;
+ }
+ {
+ uint64_t xi = a[1U];
+ uint64_t yi = tmp[1U];
+ a[1U] = yi - xi;
+ }
+ {
+ uint64_t xi = a[2U];
+ uint64_t yi = tmp[2U];
+ a[2U] = yi - xi;
+ }
+ {
+ uint64_t xi = a[3U];
+ uint64_t yi = tmp[3U];
+ a[3U] = yi - xi;
+ }
+ {
+ uint64_t xi = a[4U];
+ uint64_t yi = tmp[4U];
+ a[4U] = yi - xi;
+ }
+}
+
+inline static void
+Hacl_Bignum_fscalar(uint64_t *output, uint64_t *b, uint64_t s)
+{
+ KRML_CHECK_SIZE(FStar_UInt128_uint64_to_uint128((uint64_t)0U), (uint32_t)5U);
+ FStar_UInt128_t tmp[5U];
+ for (uint32_t _i = 0U; _i < (uint32_t)5U; ++_i)
+ tmp[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U);
+ {
+ uint64_t xi = b[0U];
+ tmp[0U] = FStar_UInt128_mul_wide(xi, s);
+ }
+ {
+ uint64_t xi = b[1U];
+ tmp[1U] = FStar_UInt128_mul_wide(xi, s);
+ }
+ {
+ uint64_t xi = b[2U];
+ tmp[2U] = FStar_UInt128_mul_wide(xi, s);
+ }
+ {
+ uint64_t xi = b[3U];
+ tmp[3U] = FStar_UInt128_mul_wide(xi, s);
+ }
+ {
+ uint64_t xi = b[4U];
+ tmp[4U] = FStar_UInt128_mul_wide(xi, s);
+ }
+ Hacl_Bignum_Fproduct_carry_wide_(tmp);
+ FStar_UInt128_t b4 = tmp[4U];
+ FStar_UInt128_t b0 = tmp[0U];
+ FStar_UInt128_t
+ b4_ = FStar_UInt128_logand(b4, FStar_UInt128_uint64_to_uint128((uint64_t)0x7ffffffffffffU));
+ FStar_UInt128_t
+ b0_ =
+ FStar_UInt128_add(b0,
+ FStar_UInt128_mul_wide((uint64_t)19U,
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b4, (uint32_t)51U))));
+ tmp[4U] = b4_;
+ tmp[0U] = b0_;
+ Hacl_Bignum_Fproduct_copy_from_wide_(output, tmp);
+}
+
+inline static void
+Hacl_Bignum_fmul(uint64_t *output, uint64_t *a, uint64_t *b)
+{
+ Hacl_Bignum_Fmul_fmul(output, a, b);
+}
+
+inline static void
+Hacl_Bignum_crecip(uint64_t *output, uint64_t *input)
+{
+ Hacl_Bignum_Crecip_crecip(output, input);
+}
+
+static void
+Hacl_EC_Point_swap_conditional_step(uint64_t *a, uint64_t *b, uint64_t swap1, uint32_t ctr)
+{
+ uint32_t i = ctr - (uint32_t)1U;
+ uint64_t ai = a[i];
+ uint64_t bi = b[i];
+ uint64_t x = swap1 & (ai ^ bi);
+ uint64_t ai1 = ai ^ x;
+ uint64_t bi1 = bi ^ x;
+ a[i] = ai1;
+ b[i] = bi1;
+}
+
+static void
+Hacl_EC_Point_swap_conditional_(uint64_t *a, uint64_t *b, uint64_t swap1, uint32_t ctr)
+{
+ if (!(ctr == (uint32_t)0U)) {
+ Hacl_EC_Point_swap_conditional_step(a, b, swap1, ctr);
+ uint32_t i = ctr - (uint32_t)1U;
+ Hacl_EC_Point_swap_conditional_(a, b, swap1, i);
+ }
+}
+
+static void
+Hacl_EC_Point_swap_conditional(uint64_t *a, uint64_t *b, uint64_t iswap)
+{
+ uint64_t swap1 = (uint64_t)0U - iswap;
+ Hacl_EC_Point_swap_conditional_(a, b, swap1, (uint32_t)5U);
+ Hacl_EC_Point_swap_conditional_(a + (uint32_t)5U, b + (uint32_t)5U, swap1, (uint32_t)5U);
+}
+
+static void
+Hacl_EC_Point_copy(uint64_t *output, uint64_t *input)
+{
+ memcpy(output, input, (uint32_t)5U * sizeof input[0U]);
+ memcpy(output + (uint32_t)5U,
+ input + (uint32_t)5U,
+ (uint32_t)5U * sizeof(input + (uint32_t)5U)[0U]);
+}
+
+static void
+Hacl_EC_AddAndDouble_fmonty(
+ uint64_t *pp,
+ uint64_t *ppq,
+ uint64_t *p,
+ uint64_t *pq,
+ uint64_t *qmqp)
+{
+ uint64_t *qx = qmqp;
+ uint64_t *x2 = pp;
+ uint64_t *z2 = pp + (uint32_t)5U;
+ uint64_t *x3 = ppq;
+ uint64_t *z3 = ppq + (uint32_t)5U;
+ uint64_t *x = p;
+ uint64_t *z = p + (uint32_t)5U;
+ uint64_t *xprime = pq;
+ uint64_t *zprime = pq + (uint32_t)5U;
+ uint64_t buf[40U] = { 0U };
+ uint64_t *origx = buf;
+ uint64_t *origxprime = buf + (uint32_t)5U;
+ uint64_t *xxprime0 = buf + (uint32_t)25U;
+ uint64_t *zzprime0 = buf + (uint32_t)30U;
+ memcpy(origx, x, (uint32_t)5U * sizeof x[0U]);
+ Hacl_Bignum_fsum(x, z);
+ Hacl_Bignum_fdifference(z, origx);
+ memcpy(origxprime, xprime, (uint32_t)5U * sizeof xprime[0U]);
+ Hacl_Bignum_fsum(xprime, zprime);
+ Hacl_Bignum_fdifference(zprime, origxprime);
+ Hacl_Bignum_fmul(xxprime0, xprime, z);
+ Hacl_Bignum_fmul(zzprime0, x, zprime);
+ uint64_t *origxprime0 = buf + (uint32_t)5U;
+ uint64_t *xx0 = buf + (uint32_t)15U;
+ uint64_t *zz0 = buf + (uint32_t)20U;
+ uint64_t *xxprime = buf + (uint32_t)25U;
+ uint64_t *zzprime = buf + (uint32_t)30U;
+ uint64_t *zzzprime = buf + (uint32_t)35U;
+ memcpy(origxprime0, xxprime, (uint32_t)5U * sizeof xxprime[0U]);
+ Hacl_Bignum_fsum(xxprime, zzprime);
+ Hacl_Bignum_fdifference(zzprime, origxprime0);
+ Hacl_Bignum_Fsquare_fsquare_times(x3, xxprime, (uint32_t)1U);
+ Hacl_Bignum_Fsquare_fsquare_times(zzzprime, zzprime, (uint32_t)1U);
+ Hacl_Bignum_fmul(z3, zzzprime, qx);
+ Hacl_Bignum_Fsquare_fsquare_times(xx0, x, (uint32_t)1U);
+ Hacl_Bignum_Fsquare_fsquare_times(zz0, z, (uint32_t)1U);
+ uint64_t *zzz = buf + (uint32_t)10U;
+ uint64_t *xx = buf + (uint32_t)15U;
+ uint64_t *zz = buf + (uint32_t)20U;
+ Hacl_Bignum_fmul(x2, xx, zz);
+ Hacl_Bignum_fdifference(zz, xx);
+ uint64_t scalar = (uint64_t)121665U;
+ Hacl_Bignum_fscalar(zzz, zz, scalar);
+ Hacl_Bignum_fsum(zzz, xx);
+ Hacl_Bignum_fmul(z2, zzz, zz);
+}
+
+static void
+Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(
+ uint64_t *nq,
+ uint64_t *nqpq,
+ uint64_t *nq2,
+ uint64_t *nqpq2,
+ uint64_t *q,
+ uint8_t byt)
+{
+ uint64_t bit = (uint64_t)(byt >> (uint32_t)7U);
+ Hacl_EC_Point_swap_conditional(nq, nqpq, bit);
+ Hacl_EC_AddAndDouble_fmonty(nq2, nqpq2, nq, nqpq, q);
+ uint64_t bit0 = (uint64_t)(byt >> (uint32_t)7U);
+ Hacl_EC_Point_swap_conditional(nq2, nqpq2, bit0);
+}
+
+static void
+Hacl_EC_Ladder_SmallLoop_cmult_small_loop_double_step(
+ uint64_t *nq,
+ uint64_t *nqpq,
+ uint64_t *nq2,
+ uint64_t *nqpq2,
+ uint64_t *q,
+ uint8_t byt)
+{
+ Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt);
+ uint8_t byt1 = byt << (uint32_t)1U;
+ Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1);
+}
+
+static void
+Hacl_EC_Ladder_SmallLoop_cmult_small_loop(
+ uint64_t *nq,
+ uint64_t *nqpq,
+ uint64_t *nq2,
+ uint64_t *nqpq2,
+ uint64_t *q,
+ uint8_t byt,
+ uint32_t i)
+{
+ if (!(i == (uint32_t)0U)) {
+ uint32_t i_ = i - (uint32_t)1U;
+ Hacl_EC_Ladder_SmallLoop_cmult_small_loop_double_step(nq, nqpq, nq2, nqpq2, q, byt);
+ uint8_t byt_ = byt << (uint32_t)2U;
+ Hacl_EC_Ladder_SmallLoop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, byt_, i_);
+ }
+}
+
+static void
+Hacl_EC_Ladder_BigLoop_cmult_big_loop(
+ uint8_t *n1,
+ uint64_t *nq,
+ uint64_t *nqpq,
+ uint64_t *nq2,
+ uint64_t *nqpq2,
+ uint64_t *q,
+ uint32_t i)
+{
+ if (!(i == (uint32_t)0U)) {
+ uint32_t i1 = i - (uint32_t)1U;
+ uint8_t byte = n1[i1];
+ Hacl_EC_Ladder_SmallLoop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, byte, (uint32_t)4U);
+ Hacl_EC_Ladder_BigLoop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, i1);
+ }
+}
+
+static void
+Hacl_EC_Ladder_cmult(uint64_t *result, uint8_t *n1, uint64_t *q)
+{
+ uint64_t point_buf[40U] = { 0U };
+ uint64_t *nq = point_buf;
+ uint64_t *nqpq = point_buf + (uint32_t)10U;
+ uint64_t *nq2 = point_buf + (uint32_t)20U;
+ uint64_t *nqpq2 = point_buf + (uint32_t)30U;
+ Hacl_EC_Point_copy(nqpq, q);
+ nq[0U] = (uint64_t)1U;
+ Hacl_EC_Ladder_BigLoop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, (uint32_t)32U);
+ Hacl_EC_Point_copy(result, nq);
+}
+
+static void
+Hacl_EC_Format_fexpand(uint64_t *output, uint8_t *input)
+{
+ uint64_t i0 = load64_le(input);
+ uint8_t *x00 = input + (uint32_t)6U;
+ uint64_t i1 = load64_le(x00);
+ uint8_t *x01 = input + (uint32_t)12U;
+ uint64_t i2 = load64_le(x01);
+ uint8_t *x02 = input + (uint32_t)19U;
+ uint64_t i3 = load64_le(x02);
+ uint8_t *x0 = input + (uint32_t)24U;
+ uint64_t i4 = load64_le(x0);
+ uint64_t output0 = i0 & (uint64_t)0x7ffffffffffffU;
+ uint64_t output1 = i1 >> (uint32_t)3U & (uint64_t)0x7ffffffffffffU;
+ uint64_t output2 = i2 >> (uint32_t)6U & (uint64_t)0x7ffffffffffffU;
+ uint64_t output3 = i3 >> (uint32_t)1U & (uint64_t)0x7ffffffffffffU;
+ uint64_t output4 = i4 >> (uint32_t)12U & (uint64_t)0x7ffffffffffffU;
+ output[0U] = output0;
+ output[1U] = output1;
+ output[2U] = output2;
+ output[3U] = output3;
+ output[4U] = output4;
+}
+
+static void
+Hacl_EC_Format_fcontract_first_carry_pass(uint64_t *input)
+{
+ uint64_t t0 = input[0U];
+ uint64_t t1 = input[1U];
+ uint64_t t2 = input[2U];
+ uint64_t t3 = input[3U];
+ uint64_t t4 = input[4U];
+ uint64_t t1_ = t1 + (t0 >> (uint32_t)51U);
+ uint64_t t0_ = t0 & (uint64_t)0x7ffffffffffffU;
+ uint64_t t2_ = t2 + (t1_ >> (uint32_t)51U);
+ uint64_t t1__ = t1_ & (uint64_t)0x7ffffffffffffU;
+ uint64_t t3_ = t3 + (t2_ >> (uint32_t)51U);
+ uint64_t t2__ = t2_ & (uint64_t)0x7ffffffffffffU;
+ uint64_t t4_ = t4 + (t3_ >> (uint32_t)51U);
+ uint64_t t3__ = t3_ & (uint64_t)0x7ffffffffffffU;
+ input[0U] = t0_;
+ input[1U] = t1__;
+ input[2U] = t2__;
+ input[3U] = t3__;
+ input[4U] = t4_;
+}
+
+static void
+Hacl_EC_Format_fcontract_first_carry_full(uint64_t *input)
+{
+ Hacl_EC_Format_fcontract_first_carry_pass(input);
+ Hacl_Bignum_Modulo_carry_top(input);
+}
+
+static void
+Hacl_EC_Format_fcontract_second_carry_pass(uint64_t *input)
+{
+ uint64_t t0 = input[0U];
+ uint64_t t1 = input[1U];
+ uint64_t t2 = input[2U];
+ uint64_t t3 = input[3U];
+ uint64_t t4 = input[4U];
+ uint64_t t1_ = t1 + (t0 >> (uint32_t)51U);
+ uint64_t t0_ = t0 & (uint64_t)0x7ffffffffffffU;
+ uint64_t t2_ = t2 + (t1_ >> (uint32_t)51U);
+ uint64_t t1__ = t1_ & (uint64_t)0x7ffffffffffffU;
+ uint64_t t3_ = t3 + (t2_ >> (uint32_t)51U);
+ uint64_t t2__ = t2_ & (uint64_t)0x7ffffffffffffU;
+ uint64_t t4_ = t4 + (t3_ >> (uint32_t)51U);
+ uint64_t t3__ = t3_ & (uint64_t)0x7ffffffffffffU;
+ input[0U] = t0_;
+ input[1U] = t1__;
+ input[2U] = t2__;
+ input[3U] = t3__;
+ input[4U] = t4_;
+}
+
+static void
+Hacl_EC_Format_fcontract_second_carry_full(uint64_t *input)
+{
+ Hacl_EC_Format_fcontract_second_carry_pass(input);
+ Hacl_Bignum_Modulo_carry_top(input);
+ uint64_t i0 = input[0U];
+ uint64_t i1 = input[1U];
+ uint64_t i0_ = i0 & (uint64_t)0x7ffffffffffffU;
+ uint64_t i1_ = i1 + (i0 >> (uint32_t)51U);
+ input[0U] = i0_;
+ input[1U] = i1_;
+}
+
+static void
+Hacl_EC_Format_fcontract_trim(uint64_t *input)
+{
+ uint64_t a0 = input[0U];
+ uint64_t a1 = input[1U];
+ uint64_t a2 = input[2U];
+ uint64_t a3 = input[3U];
+ uint64_t a4 = input[4U];
+ uint64_t mask0 = FStar_UInt64_gte_mask(a0, (uint64_t)0x7ffffffffffedU);
+ uint64_t mask1 = FStar_UInt64_eq_mask(a1, (uint64_t)0x7ffffffffffffU);
+ uint64_t mask2 = FStar_UInt64_eq_mask(a2, (uint64_t)0x7ffffffffffffU);
+ uint64_t mask3 = FStar_UInt64_eq_mask(a3, (uint64_t)0x7ffffffffffffU);
+ uint64_t mask4 = FStar_UInt64_eq_mask(a4, (uint64_t)0x7ffffffffffffU);
+ uint64_t mask = (((mask0 & mask1) & mask2) & mask3) & mask4;
+ uint64_t a0_ = a0 - ((uint64_t)0x7ffffffffffedU & mask);
+ uint64_t a1_ = a1 - ((uint64_t)0x7ffffffffffffU & mask);
+ uint64_t a2_ = a2 - ((uint64_t)0x7ffffffffffffU & mask);
+ uint64_t a3_ = a3 - ((uint64_t)0x7ffffffffffffU & mask);
+ uint64_t a4_ = a4 - ((uint64_t)0x7ffffffffffffU & mask);
+ input[0U] = a0_;
+ input[1U] = a1_;
+ input[2U] = a2_;
+ input[3U] = a3_;
+ input[4U] = a4_;
+}
+
+static void
+Hacl_EC_Format_fcontract_store(uint8_t *output, uint64_t *input)
+{
+ uint64_t t0 = input[0U];
+ uint64_t t1 = input[1U];
+ uint64_t t2 = input[2U];
+ uint64_t t3 = input[3U];
+ uint64_t t4 = input[4U];
+ uint64_t o0 = t1 << (uint32_t)51U | t0;
+ uint64_t o1 = t2 << (uint32_t)38U | t1 >> (uint32_t)13U;
+ uint64_t o2 = t3 << (uint32_t)25U | t2 >> (uint32_t)26U;
+ uint64_t o3 = t4 << (uint32_t)12U | t3 >> (uint32_t)39U;
+ uint8_t *b0 = output;
+ uint8_t *b1 = output + (uint32_t)8U;
+ uint8_t *b2 = output + (uint32_t)16U;
+ uint8_t *b3 = output + (uint32_t)24U;
+ store64_le(b0, o0);
+ store64_le(b1, o1);
+ store64_le(b2, o2);
+ store64_le(b3, o3);
+}
+
+static void
+Hacl_EC_Format_fcontract(uint8_t *output, uint64_t *input)
+{
+ Hacl_EC_Format_fcontract_first_carry_full(input);
+ Hacl_EC_Format_fcontract_second_carry_full(input);
+ Hacl_EC_Format_fcontract_trim(input);
+ Hacl_EC_Format_fcontract_store(output, input);
+}
+
+static void
+Hacl_EC_Format_scalar_of_point(uint8_t *scalar, uint64_t *point)
+{
+ uint64_t *x = point;
+ uint64_t *z = point + (uint32_t)5U;
+ uint64_t buf[10U] = { 0U };
+ uint64_t *zmone = buf;
+ uint64_t *sc = buf + (uint32_t)5U;
+ Hacl_Bignum_crecip(zmone, z);
+ Hacl_Bignum_fmul(sc, x, zmone);
+ Hacl_EC_Format_fcontract(scalar, sc);
+}
+
+void
+Hacl_EC_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint)
+{
+ uint64_t buf0[10U] = { 0U };
+ uint64_t *x0 = buf0;
+ uint64_t *z = buf0 + (uint32_t)5U;
+ Hacl_EC_Format_fexpand(x0, basepoint);
+ z[0U] = (uint64_t)1U;
+ uint64_t *q = buf0;
+ uint8_t e[32U] = { 0U };
+ memcpy(e, secret, (uint32_t)32U * sizeof secret[0U]);
+ uint8_t e0 = e[0U];
+ uint8_t e31 = e[31U];
+ uint8_t e01 = e0 & (uint8_t)248U;
+ uint8_t e311 = e31 & (uint8_t)127U;
+ uint8_t e312 = e311 | (uint8_t)64U;
+ e[0U] = e01;
+ e[31U] = e312;
+ uint8_t *scalar = e;
+ uint64_t buf[15U] = { 0U };
+ uint64_t *nq = buf;
+ uint64_t *x = nq;
+ x[0U] = (uint64_t)1U;
+ Hacl_EC_Ladder_cmult(nq, scalar, q);
+ Hacl_EC_Format_scalar_of_point(mypublic, nq);
+}
+
+void
+Hacl_Curve25519_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint)
+{
+ Hacl_EC_crypto_scalarmult(mypublic, secret, basepoint);
+}
diff --git a/security/nss/lib/freebl/verified/Hacl_Curve25519.h b/security/nss/lib/freebl/verified/Hacl_Curve25519.h
new file mode 100644
index 000000000..0e443f177
--- /dev/null
+++ b/security/nss/lib/freebl/verified/Hacl_Curve25519.h
@@ -0,0 +1,57 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kremlib.h"
+#ifndef __Hacl_Curve25519_H
+#define __Hacl_Curve25519_H
+
+typedef uint64_t Hacl_Bignum_Constants_limb;
+
+typedef FStar_UInt128_t Hacl_Bignum_Constants_wide;
+
+typedef uint64_t Hacl_Bignum_Parameters_limb;
+
+typedef FStar_UInt128_t Hacl_Bignum_Parameters_wide;
+
+typedef uint32_t Hacl_Bignum_Parameters_ctr;
+
+typedef uint64_t *Hacl_Bignum_Parameters_felem;
+
+typedef FStar_UInt128_t *Hacl_Bignum_Parameters_felem_wide;
+
+typedef void *Hacl_Bignum_Parameters_seqelem;
+
+typedef void *Hacl_Bignum_Parameters_seqelem_wide;
+
+typedef FStar_UInt128_t Hacl_Bignum_Wide_t;
+
+typedef uint64_t Hacl_Bignum_Limb_t;
+
+extern void Hacl_Bignum_lemma_diff(Prims_int x0, Prims_int x1, Prims_pos x2);
+
+typedef uint64_t *Hacl_EC_Point_point;
+
+typedef uint8_t *Hacl_EC_Ladder_SmallLoop_uint8_p;
+
+typedef uint8_t *Hacl_EC_Ladder_uint8_p;
+
+typedef uint8_t *Hacl_EC_Format_uint8_p;
+
+void Hacl_EC_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint);
+
+typedef uint8_t *Hacl_Curve25519_uint8_p;
+
+void Hacl_Curve25519_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint);
+#endif
diff --git a/security/nss/lib/freebl/verified/Hacl_Poly1305_64.c b/security/nss/lib/freebl/verified/Hacl_Poly1305_64.c
new file mode 100644
index 000000000..984031ae2
--- /dev/null
+++ b/security/nss/lib/freebl/verified/Hacl_Poly1305_64.c
@@ -0,0 +1,485 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hacl_Poly1305_64.h"
+
+inline static void
+Hacl_Bignum_Modulo_reduce(uint64_t *b)
+{
+ uint64_t b0 = b[0U];
+ b[0U] = (b0 << (uint32_t)4U) + (b0 << (uint32_t)2U);
+}
+
+inline static void
+Hacl_Bignum_Modulo_carry_top(uint64_t *b)
+{
+ uint64_t b2 = b[2U];
+ uint64_t b0 = b[0U];
+ uint64_t b2_42 = b2 >> (uint32_t)42U;
+ b[2U] = b2 & (uint64_t)0x3ffffffffffU;
+ b[0U] = (b2_42 << (uint32_t)2U) + b2_42 + b0;
+}
+
+inline static void
+Hacl_Bignum_Modulo_carry_top_wide(FStar_UInt128_t *b)
+{
+ FStar_UInt128_t b2 = b[2U];
+ FStar_UInt128_t b0 = b[0U];
+ FStar_UInt128_t
+ b2_ = FStar_UInt128_logand(b2, FStar_UInt128_uint64_to_uint128((uint64_t)0x3ffffffffffU));
+ uint64_t b2_42 = FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b2, (uint32_t)42U));
+ FStar_UInt128_t
+ b0_ = FStar_UInt128_add(b0, FStar_UInt128_uint64_to_uint128((b2_42 << (uint32_t)2U) + b2_42));
+ b[2U] = b2_;
+ b[0U] = b0_;
+}
+
+inline static void
+Hacl_Bignum_Fproduct_copy_from_wide_(uint64_t *output, FStar_UInt128_t *input)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)3U; i = i + (uint32_t)1U) {
+ FStar_UInt128_t xi = input[i];
+ output[i] = FStar_UInt128_uint128_to_uint64(xi);
+ }
+}
+
+inline static void
+Hacl_Bignum_Fproduct_sum_scalar_multiplication_(
+ FStar_UInt128_t *output,
+ uint64_t *input,
+ uint64_t s)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)3U; i = i + (uint32_t)1U) {
+ FStar_UInt128_t xi = output[i];
+ uint64_t yi = input[i];
+ output[i] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s));
+ }
+}
+
+inline static void
+Hacl_Bignum_Fproduct_carry_wide_(FStar_UInt128_t *tmp)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)2U; i = i + (uint32_t)1U) {
+ uint32_t ctr = i;
+ FStar_UInt128_t tctr = tmp[ctr];
+ FStar_UInt128_t tctrp1 = tmp[ctr + (uint32_t)1U];
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(tctr) & (uint64_t)0xfffffffffffU;
+ FStar_UInt128_t c = FStar_UInt128_shift_right(tctr, (uint32_t)44U);
+ tmp[ctr] = FStar_UInt128_uint64_to_uint128(r0);
+ tmp[ctr + (uint32_t)1U] = FStar_UInt128_add(tctrp1, c);
+ }
+}
+
+inline static void
+Hacl_Bignum_Fproduct_carry_limb_(uint64_t *tmp)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)2U; i = i + (uint32_t)1U) {
+ uint32_t ctr = i;
+ uint64_t tctr = tmp[ctr];
+ uint64_t tctrp1 = tmp[ctr + (uint32_t)1U];
+ uint64_t r0 = tctr & (uint64_t)0xfffffffffffU;
+ uint64_t c = tctr >> (uint32_t)44U;
+ tmp[ctr] = r0;
+ tmp[ctr + (uint32_t)1U] = tctrp1 + c;
+ }
+}
+
+inline static void
+Hacl_Bignum_Fmul_shift_reduce(uint64_t *output)
+{
+ uint64_t tmp = output[2U];
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)2U; i = i + (uint32_t)1U) {
+ uint32_t ctr = (uint32_t)3U - i - (uint32_t)1U;
+ uint64_t z = output[ctr - (uint32_t)1U];
+ output[ctr] = z;
+ }
+ output[0U] = tmp;
+ Hacl_Bignum_Modulo_reduce(output);
+}
+
+static void
+Hacl_Bignum_Fmul_mul_shift_reduce_(FStar_UInt128_t *output, uint64_t *input, uint64_t *input2)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)2U; i = i + (uint32_t)1U) {
+ uint64_t input2i = input2[i];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+ Hacl_Bignum_Fmul_shift_reduce(input);
+ }
+ uint32_t i = (uint32_t)2U;
+ uint64_t input2i = input2[i];
+ Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i);
+}
+
+inline static void
+Hacl_Bignum_Fmul_fmul(uint64_t *output, uint64_t *input, uint64_t *input2)
+{
+ uint64_t tmp[3U] = { 0U };
+ memcpy(tmp, input, (uint32_t)3U * sizeof input[0U]);
+ KRML_CHECK_SIZE(FStar_UInt128_uint64_to_uint128((uint64_t)0U), (uint32_t)3U);
+ FStar_UInt128_t t[3U];
+ for (uint32_t _i = 0U; _i < (uint32_t)3U; ++_i)
+ t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U);
+ Hacl_Bignum_Fmul_mul_shift_reduce_(t, tmp, input2);
+ Hacl_Bignum_Fproduct_carry_wide_(t);
+ Hacl_Bignum_Modulo_carry_top_wide(t);
+ Hacl_Bignum_Fproduct_copy_from_wide_(output, t);
+ uint64_t i0 = output[0U];
+ uint64_t i1 = output[1U];
+ uint64_t i0_ = i0 & (uint64_t)0xfffffffffffU;
+ uint64_t i1_ = i1 + (i0 >> (uint32_t)44U);
+ output[0U] = i0_;
+ output[1U] = i1_;
+}
+
+inline static void
+Hacl_Bignum_AddAndMultiply_add_and_multiply(uint64_t *acc, uint64_t *block, uint64_t *r)
+{
+ for (uint32_t i = (uint32_t)0U; i < (uint32_t)3U; i = i + (uint32_t)1U) {
+ uint64_t xi = acc[i];
+ uint64_t yi = block[i];
+ acc[i] = xi + yi;
+ }
+ Hacl_Bignum_Fmul_fmul(acc, acc, r);
+}
+
+inline static void
+Hacl_Impl_Poly1305_64_poly1305_update(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m)
+{
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut0 = st;
+ uint64_t *h = scrut0.h;
+ uint64_t *acc = h;
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *r = scrut.r;
+ uint64_t *r3 = r;
+ uint64_t tmp[3U] = { 0U };
+ FStar_UInt128_t m0 = load128_le(m);
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(m0) & (uint64_t)0xfffffffffffU;
+ uint64_t
+ r1 =
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(m0, (uint32_t)44U)) & (uint64_t)0xfffffffffffU;
+ uint64_t r2 = FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(m0, (uint32_t)88U));
+ tmp[0U] = r0;
+ tmp[1U] = r1;
+ tmp[2U] = r2;
+ uint64_t b2 = tmp[2U];
+ uint64_t b2_ = (uint64_t)0x10000000000U | b2;
+ tmp[2U] = b2_;
+ Hacl_Bignum_AddAndMultiply_add_and_multiply(acc, tmp, r3);
+}
+
+inline static void
+Hacl_Impl_Poly1305_64_poly1305_process_last_block_(
+ uint8_t *block,
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint64_t rem_)
+{
+ uint64_t tmp[3U] = { 0U };
+ FStar_UInt128_t m0 = load128_le(block);
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(m0) & (uint64_t)0xfffffffffffU;
+ uint64_t
+ r1 =
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(m0, (uint32_t)44U)) & (uint64_t)0xfffffffffffU;
+ uint64_t r2 = FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(m0, (uint32_t)88U));
+ tmp[0U] = r0;
+ tmp[1U] = r1;
+ tmp[2U] = r2;
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut0 = st;
+ uint64_t *h = scrut0.h;
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *r = scrut.r;
+ Hacl_Bignum_AddAndMultiply_add_and_multiply(h, tmp, r);
+}
+
+inline static void
+Hacl_Impl_Poly1305_64_poly1305_process_last_block(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint64_t rem_)
+{
+ uint8_t zero1 = (uint8_t)0U;
+ KRML_CHECK_SIZE(zero1, (uint32_t)16U);
+ uint8_t block[16U];
+ for (uint32_t _i = 0U; _i < (uint32_t)16U; ++_i)
+ block[_i] = zero1;
+ uint32_t i0 = (uint32_t)rem_;
+ uint32_t i = (uint32_t)rem_;
+ memcpy(block, m, i * sizeof m[0U]);
+ block[i0] = (uint8_t)1U;
+ Hacl_Impl_Poly1305_64_poly1305_process_last_block_(block, st, m, rem_);
+}
+
+static void
+Hacl_Impl_Poly1305_64_poly1305_last_pass(uint64_t *acc)
+{
+ Hacl_Bignum_Fproduct_carry_limb_(acc);
+ Hacl_Bignum_Modulo_carry_top(acc);
+ uint64_t a0 = acc[0U];
+ uint64_t a10 = acc[1U];
+ uint64_t a20 = acc[2U];
+ uint64_t a0_ = a0 & (uint64_t)0xfffffffffffU;
+ uint64_t r0 = a0 >> (uint32_t)44U;
+ uint64_t a1_ = (a10 + r0) & (uint64_t)0xfffffffffffU;
+ uint64_t r1 = (a10 + r0) >> (uint32_t)44U;
+ uint64_t a2_ = a20 + r1;
+ acc[0U] = a0_;
+ acc[1U] = a1_;
+ acc[2U] = a2_;
+ Hacl_Bignum_Modulo_carry_top(acc);
+ uint64_t i0 = acc[0U];
+ uint64_t i1 = acc[1U];
+ uint64_t i0_ = i0 & (uint64_t)0xfffffffffffU;
+ uint64_t i1_ = i1 + (i0 >> (uint32_t)44U);
+ acc[0U] = i0_;
+ acc[1U] = i1_;
+ uint64_t a00 = acc[0U];
+ uint64_t a1 = acc[1U];
+ uint64_t a2 = acc[2U];
+ uint64_t mask0 = FStar_UInt64_gte_mask(a00, (uint64_t)0xffffffffffbU);
+ uint64_t mask1 = FStar_UInt64_eq_mask(a1, (uint64_t)0xfffffffffffU);
+ uint64_t mask2 = FStar_UInt64_eq_mask(a2, (uint64_t)0x3ffffffffffU);
+ uint64_t mask = (mask0 & mask1) & mask2;
+ uint64_t a0_0 = a00 - ((uint64_t)0xffffffffffbU & mask);
+ uint64_t a1_0 = a1 - ((uint64_t)0xfffffffffffU & mask);
+ uint64_t a2_0 = a2 - ((uint64_t)0x3ffffffffffU & mask);
+ acc[0U] = a0_0;
+ acc[1U] = a1_0;
+ acc[2U] = a2_0;
+}
+
+static Hacl_Impl_Poly1305_64_State_poly1305_state
+Hacl_Impl_Poly1305_64_mk_state(uint64_t *r, uint64_t *h)
+{
+ return ((Hacl_Impl_Poly1305_64_State_poly1305_state){.r = r, .h = h });
+}
+
+static void
+Hacl_Standalone_Poly1305_64_poly1305_blocks(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint64_t len1)
+{
+ if (!(len1 == (uint64_t)0U)) {
+ uint8_t *block = m;
+ uint8_t *tail1 = m + (uint32_t)16U;
+ Hacl_Impl_Poly1305_64_poly1305_update(st, block);
+ uint64_t len2 = len1 - (uint64_t)1U;
+ Hacl_Standalone_Poly1305_64_poly1305_blocks(st, tail1, len2);
+ }
+}
+
+static void
+Hacl_Standalone_Poly1305_64_poly1305_partial(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *input,
+ uint64_t len1,
+ uint8_t *kr)
+{
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *r = scrut.r;
+ uint64_t *x0 = r;
+ FStar_UInt128_t k1 = load128_le(kr);
+ FStar_UInt128_t
+ k_clamped =
+ FStar_UInt128_logand(k1,
+ FStar_UInt128_logor(FStar_UInt128_shift_left(FStar_UInt128_uint64_to_uint128((uint64_t)0x0ffffffc0ffffffcU),
+ (uint32_t)64U),
+ FStar_UInt128_uint64_to_uint128((uint64_t)0x0ffffffc0fffffffU)));
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(k_clamped) & (uint64_t)0xfffffffffffU;
+ uint64_t
+ r1 =
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(k_clamped, (uint32_t)44U)) & (uint64_t)0xfffffffffffU;
+ uint64_t
+ r2 = FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(k_clamped, (uint32_t)88U));
+ x0[0U] = r0;
+ x0[1U] = r1;
+ x0[2U] = r2;
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut0 = st;
+ uint64_t *h = scrut0.h;
+ uint64_t *x00 = h;
+ x00[0U] = (uint64_t)0U;
+ x00[1U] = (uint64_t)0U;
+ x00[2U] = (uint64_t)0U;
+ Hacl_Standalone_Poly1305_64_poly1305_blocks(st, input, len1);
+}
+
+static void
+Hacl_Standalone_Poly1305_64_poly1305_complete(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint64_t len1,
+ uint8_t *k1)
+{
+ uint8_t *kr = k1;
+ uint64_t len16 = len1 >> (uint32_t)4U;
+ uint64_t rem16 = len1 & (uint64_t)0xfU;
+ uint8_t *part_input = m;
+ uint8_t *last_block = m + (uint32_t)((uint64_t)16U * len16);
+ Hacl_Standalone_Poly1305_64_poly1305_partial(st, part_input, len16, kr);
+ if (!(rem16 == (uint64_t)0U))
+ Hacl_Impl_Poly1305_64_poly1305_process_last_block(st, last_block, rem16);
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *h = scrut.h;
+ uint64_t *acc = h;
+ Hacl_Impl_Poly1305_64_poly1305_last_pass(acc);
+}
+
+static void
+Hacl_Standalone_Poly1305_64_crypto_onetimeauth_(
+ uint8_t *output,
+ uint8_t *input,
+ uint64_t len1,
+ uint8_t *k1)
+{
+ uint64_t buf[6U] = { 0U };
+ uint64_t *r = buf;
+ uint64_t *h = buf + (uint32_t)3U;
+ Hacl_Impl_Poly1305_64_State_poly1305_state st = Hacl_Impl_Poly1305_64_mk_state(r, h);
+ uint8_t *key_s = k1 + (uint32_t)16U;
+ Hacl_Standalone_Poly1305_64_poly1305_complete(st, input, len1, k1);
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *h3 = scrut.h;
+ uint64_t *acc = h3;
+ FStar_UInt128_t k_ = load128_le(key_s);
+ uint64_t h0 = acc[0U];
+ uint64_t h1 = acc[1U];
+ uint64_t h2 = acc[2U];
+ FStar_UInt128_t
+ acc_ =
+ FStar_UInt128_logor(FStar_UInt128_shift_left(FStar_UInt128_uint64_to_uint128(h2
+ << (uint32_t)24U |
+ h1 >> (uint32_t)20U),
+ (uint32_t)64U),
+ FStar_UInt128_uint64_to_uint128(h1 << (uint32_t)44U | h0));
+ FStar_UInt128_t mac_ = FStar_UInt128_add_mod(acc_, k_);
+ store128_le(output, mac_);
+}
+
+static void
+Hacl_Standalone_Poly1305_64_crypto_onetimeauth(
+ uint8_t *output,
+ uint8_t *input,
+ uint64_t len1,
+ uint8_t *k1)
+{
+ Hacl_Standalone_Poly1305_64_crypto_onetimeauth_(output, input, len1, k1);
+}
+
+Hacl_Impl_Poly1305_64_State_poly1305_state
+Hacl_Poly1305_64_mk_state(uint64_t *r, uint64_t *acc)
+{
+ return Hacl_Impl_Poly1305_64_mk_state(r, acc);
+}
+
+void
+Hacl_Poly1305_64_init(Hacl_Impl_Poly1305_64_State_poly1305_state st, uint8_t *k1)
+{
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *r = scrut.r;
+ uint64_t *x0 = r;
+ FStar_UInt128_t k10 = load128_le(k1);
+ FStar_UInt128_t
+ k_clamped =
+ FStar_UInt128_logand(k10,
+ FStar_UInt128_logor(FStar_UInt128_shift_left(FStar_UInt128_uint64_to_uint128((uint64_t)0x0ffffffc0ffffffcU),
+ (uint32_t)64U),
+ FStar_UInt128_uint64_to_uint128((uint64_t)0x0ffffffc0fffffffU)));
+ uint64_t r0 = FStar_UInt128_uint128_to_uint64(k_clamped) & (uint64_t)0xfffffffffffU;
+ uint64_t
+ r1 =
+ FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(k_clamped, (uint32_t)44U)) & (uint64_t)0xfffffffffffU;
+ uint64_t
+ r2 = FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(k_clamped, (uint32_t)88U));
+ x0[0U] = r0;
+ x0[1U] = r1;
+ x0[2U] = r2;
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut0 = st;
+ uint64_t *h = scrut0.h;
+ uint64_t *x00 = h;
+ x00[0U] = (uint64_t)0U;
+ x00[1U] = (uint64_t)0U;
+ x00[2U] = (uint64_t)0U;
+}
+
+void
+Hacl_Poly1305_64_update_block(Hacl_Impl_Poly1305_64_State_poly1305_state st, uint8_t *m)
+{
+ Hacl_Impl_Poly1305_64_poly1305_update(st, m);
+}
+
+void
+Hacl_Poly1305_64_update(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint32_t num_blocks)
+{
+ if (!(num_blocks == (uint32_t)0U)) {
+ uint8_t *block = m;
+ uint8_t *m_ = m + (uint32_t)16U;
+ uint32_t n1 = num_blocks - (uint32_t)1U;
+ Hacl_Poly1305_64_update_block(st, block);
+ Hacl_Poly1305_64_update(st, m_, n1);
+ }
+}
+
+void
+Hacl_Poly1305_64_update_last(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint32_t len1)
+{
+ if (!((uint64_t)len1 == (uint64_t)0U))
+ Hacl_Impl_Poly1305_64_poly1305_process_last_block(st, m, (uint64_t)len1);
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *h = scrut.h;
+ uint64_t *acc = h;
+ Hacl_Impl_Poly1305_64_poly1305_last_pass(acc);
+}
+
+void
+Hacl_Poly1305_64_finish(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *mac,
+ uint8_t *k1)
+{
+ Hacl_Impl_Poly1305_64_State_poly1305_state scrut = st;
+ uint64_t *h = scrut.h;
+ uint64_t *acc = h;
+ FStar_UInt128_t k_ = load128_le(k1);
+ uint64_t h0 = acc[0U];
+ uint64_t h1 = acc[1U];
+ uint64_t h2 = acc[2U];
+ FStar_UInt128_t
+ acc_ =
+ FStar_UInt128_logor(FStar_UInt128_shift_left(FStar_UInt128_uint64_to_uint128(h2
+ << (uint32_t)24U |
+ h1 >> (uint32_t)20U),
+ (uint32_t)64U),
+ FStar_UInt128_uint64_to_uint128(h1 << (uint32_t)44U | h0));
+ FStar_UInt128_t mac_ = FStar_UInt128_add_mod(acc_, k_);
+ store128_le(mac, mac_);
+}
+
+void
+Hacl_Poly1305_64_crypto_onetimeauth(
+ uint8_t *output,
+ uint8_t *input,
+ uint64_t len1,
+ uint8_t *k1)
+{
+ Hacl_Standalone_Poly1305_64_crypto_onetimeauth(output, input, len1, k1);
+}
diff --git a/security/nss/lib/freebl/verified/Hacl_Poly1305_64.h b/security/nss/lib/freebl/verified/Hacl_Poly1305_64.h
new file mode 100644
index 000000000..0aa9a0de3
--- /dev/null
+++ b/security/nss/lib/freebl/verified/Hacl_Poly1305_64.h
@@ -0,0 +1,99 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kremlib.h"
+#ifndef __Hacl_Poly1305_64_H
+#define __Hacl_Poly1305_64_H
+
+typedef uint64_t Hacl_Bignum_Constants_limb;
+
+typedef FStar_UInt128_t Hacl_Bignum_Constants_wide;
+
+typedef FStar_UInt128_t Hacl_Bignum_Wide_t;
+
+typedef uint64_t Hacl_Bignum_Limb_t;
+
+typedef void *Hacl_Impl_Poly1305_64_State_log_t;
+
+typedef uint8_t *Hacl_Impl_Poly1305_64_State_uint8_p;
+
+typedef uint64_t *Hacl_Impl_Poly1305_64_State_bigint;
+
+typedef void *Hacl_Impl_Poly1305_64_State_seqelem;
+
+typedef uint64_t *Hacl_Impl_Poly1305_64_State_elemB;
+
+typedef uint8_t *Hacl_Impl_Poly1305_64_State_wordB;
+
+typedef uint8_t *Hacl_Impl_Poly1305_64_State_wordB_16;
+
+typedef struct
+{
+ uint64_t *r;
+ uint64_t *h;
+} Hacl_Impl_Poly1305_64_State_poly1305_state;
+
+typedef void *Hacl_Impl_Poly1305_64_log_t;
+
+typedef uint64_t *Hacl_Impl_Poly1305_64_bigint;
+
+typedef uint8_t *Hacl_Impl_Poly1305_64_uint8_p;
+
+typedef uint64_t *Hacl_Impl_Poly1305_64_elemB;
+
+typedef uint8_t *Hacl_Impl_Poly1305_64_wordB;
+
+typedef uint8_t *Hacl_Impl_Poly1305_64_wordB_16;
+
+typedef uint8_t *Hacl_Poly1305_64_uint8_p;
+
+typedef uint64_t Hacl_Poly1305_64_uint64_t;
+
+typedef uint8_t *Hacl_Poly1305_64_key;
+
+typedef Hacl_Impl_Poly1305_64_State_poly1305_state Hacl_Poly1305_64_state;
+
+Hacl_Impl_Poly1305_64_State_poly1305_state
+Hacl_Poly1305_64_mk_state(uint64_t *r, uint64_t *acc);
+
+void Hacl_Poly1305_64_init(Hacl_Impl_Poly1305_64_State_poly1305_state st, uint8_t *k1);
+
+void Hacl_Poly1305_64_update_block(Hacl_Impl_Poly1305_64_State_poly1305_state st, uint8_t *m);
+
+void
+Hacl_Poly1305_64_update(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint32_t num_blocks);
+
+void
+Hacl_Poly1305_64_update_last(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *m,
+ uint32_t len1);
+
+void
+Hacl_Poly1305_64_finish(
+ Hacl_Impl_Poly1305_64_State_poly1305_state st,
+ uint8_t *mac,
+ uint8_t *k1);
+
+void
+Hacl_Poly1305_64_crypto_onetimeauth(
+ uint8_t *output,
+ uint8_t *input,
+ uint64_t len1,
+ uint8_t *k1);
+#endif
diff --git a/security/nss/lib/freebl/verified/kremlib.h b/security/nss/lib/freebl/verified/kremlib.h
new file mode 100644
index 000000000..c12164e74
--- /dev/null
+++ b/security/nss/lib/freebl/verified/kremlib.h
@@ -0,0 +1,672 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __KREMLIB_H
+#define __KREMLIB_H
+
+#include "kremlib_base.h"
+
+/* For tests only: we might need this function to be forward-declared, because
+ * the dependency on WasmSupport appears very late, after SimplifyWasm, and
+ * sadly, after the topological order has been done. */
+void WasmSupport_check_buffer_size(uint32_t s);
+
+/******************************************************************************/
+/* Stubs to ease compilation of non-Low* code */
+/******************************************************************************/
+
+/* Some types that KreMLin has no special knowledge of; many of them appear in
+ * signatures of ghost functions, meaning that it suffices to give them (any)
+ * definition. */
+typedef void *FStar_Seq_Base_seq, *Prims_prop, *FStar_HyperStack_mem,
+ *FStar_Set_set, *Prims_st_pre_h, *FStar_Heap_heap, *Prims_all_pre_h,
+ *FStar_TSet_set, *Prims_list, *FStar_Map_t, *FStar_UInt63_t_,
+ *FStar_Int63_t_, *FStar_UInt63_t, *FStar_Int63_t, *FStar_UInt_uint_t,
+ *FStar_Int_int_t, *FStar_HyperStack_stackref, *FStar_Bytes_bytes,
+ *FStar_HyperHeap_rid, *FStar_Heap_aref, *FStar_Monotonic_Heap_heap,
+ *FStar_Monotonic_Heap_aref, *FStar_Monotonic_HyperHeap_rid,
+ *FStar_Monotonic_HyperStack_mem, *FStar_Char_char_;
+
+typedef const char *Prims_string;
+
+/* For "bare" targets that do not have a C stdlib, the user might want to use
+ * [-add-include '"mydefinitions.h"'] and override these. */
+#ifndef KRML_HOST_PRINTF
+#define KRML_HOST_PRINTF printf
+#endif
+
+#ifndef KRML_HOST_EXIT
+#define KRML_HOST_EXIT exit
+#endif
+
+#ifndef KRML_HOST_MALLOC
+#define KRML_HOST_MALLOC malloc
+#endif
+
+/* In statement position, exiting is easy. */
+#define KRML_EXIT \
+ do { \
+ KRML_HOST_PRINTF("Unimplemented function at %s:%d\n", __FILE__, __LINE__); \
+ KRML_HOST_EXIT(254); \
+ } while (0)
+
+/* In expression position, use the comma-operator and a malloc to return an
+ * expression of the right size. KreMLin passes t as the parameter to the macro.
+ */
+#define KRML_EABORT(t, msg) \
+ (KRML_HOST_PRINTF("KreMLin abort at %s:%d\n%s\n", __FILE__, __LINE__, msg), \
+ KRML_HOST_EXIT(255), *((t *)KRML_HOST_MALLOC(sizeof(t))))
+
+/* In FStar.Buffer.fst, the size of arrays is uint32_t, but it's a number of
+ * *elements*. Do an ugly, run-time check (some of which KreMLin can eliminate).
+ */
+#define KRML_CHECK_SIZE(elt, size) \
+ if (((size_t)size) > SIZE_MAX / sizeof(elt)) { \
+ KRML_HOST_PRINTF( \
+ "Maximum allocatable size exceeded, aborting before overflow at " \
+ "%s:%d\n", \
+ __FILE__, __LINE__); \
+ KRML_HOST_EXIT(253); \
+ }
+
+/* A series of GCC atrocities to trace function calls (kremlin's [-d c-calls]
+ * option). Useful when trying to debug, say, Wasm, to compare traces. */
+/* clang-format off */
+#ifdef __GNUC__
+#define KRML_FORMAT(X) _Generic((X), \
+ uint8_t : "0x%08" PRIx8, \
+ uint16_t: "0x%08" PRIx16, \
+ uint32_t: "0x%08" PRIx32, \
+ uint64_t: "0x%08" PRIx64, \
+ int8_t : "0x%08" PRIx8, \
+ int16_t : "0x%08" PRIx16, \
+ int32_t : "0x%08" PRIx32, \
+ int64_t : "0x%08" PRIx64, \
+ default : "%s")
+
+#define KRML_FORMAT_ARG(X) _Generic((X), \
+ uint8_t : X, \
+ uint16_t: X, \
+ uint32_t: X, \
+ uint64_t: X, \
+ int8_t : X, \
+ int16_t : X, \
+ int32_t : X, \
+ int64_t : X, \
+ default : "unknown")
+/* clang-format on */
+
+#define KRML_DEBUG_RETURN(X) \
+ ({ \
+ __auto_type _ret = (X); \
+ KRML_HOST_PRINTF("returning: "); \
+ KRML_HOST_PRINTF(KRML_FORMAT(_ret), KRML_FORMAT_ARG(_ret)); \
+ KRML_HOST_PRINTF(" \n"); \
+ _ret; \
+ })
+#endif
+
+#define FStar_Buffer_eqb(b1, b2, n) \
+ (memcmp((b1), (b2), (n) * sizeof((b1)[0])) == 0)
+
+/* Stubs to make ST happy. Important note: you must generate a use of the macro
+ * argument, otherwise, you may have FStar_ST_recall(f) as the only use of f;
+ * KreMLin will think that this is a valid use, but then the C compiler, after
+ * macro expansion, will error out. */
+#define FStar_HyperHeap_root 0
+#define FStar_Pervasives_Native_fst(x) (x).fst
+#define FStar_Pervasives_Native_snd(x) (x).snd
+#define FStar_Seq_Base_createEmpty(x) 0
+#define FStar_Seq_Base_create(len, init) 0
+#define FStar_Seq_Base_upd(s, i, e) 0
+#define FStar_Seq_Base_eq(l1, l2) 0
+#define FStar_Seq_Base_length(l1) 0
+#define FStar_Seq_Base_append(x, y) 0
+#define FStar_Seq_Base_slice(x, y, z) 0
+#define FStar_Seq_Properties_snoc(x, y) 0
+#define FStar_Seq_Properties_cons(x, y) 0
+#define FStar_Seq_Base_index(x, y) 0
+#define FStar_HyperStack_is_eternal_color(x) 0
+#define FStar_Monotonic_HyperHeap_root 0
+#define FStar_Buffer_to_seq_full(x) 0
+#define FStar_Buffer_recall(x)
+#define FStar_HyperStack_ST_op_Colon_Equals(x, v) KRML_EXIT
+#define FStar_HyperStack_ST_op_Bang(x) 0
+#define FStar_HyperStack_ST_salloc(x) 0
+#define FStar_HyperStack_ST_ralloc(x, y) 0
+#define FStar_HyperStack_ST_new_region(x) (0)
+#define FStar_Monotonic_RRef_m_alloc(x) \
+ { \
+ 0 \
+ }
+
+#define FStar_HyperStack_ST_recall(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#define FStar_HyperStack_ST_recall_region(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#define FStar_Monotonic_RRef_m_recall(x1, x2) \
+ do { \
+ (void)(x1); \
+ (void)(x2); \
+ } while (0)
+
+#define FStar_Monotonic_RRef_m_write(x1, x2, x3, x4, x5) \
+ do { \
+ (void)(x1); \
+ (void)(x2); \
+ (void)(x3); \
+ (void)(x4); \
+ (void)(x5); \
+ } while (0)
+
+/******************************************************************************/
+/* Endian-ness macros that can only be implemented in C */
+/******************************************************************************/
+
+/* ... for Linux */
+#if defined(__linux__) || defined(__CYGWIN__)
+#include <endian.h>
+
+/* ... for OSX */
+#elif defined(__APPLE__)
+#include <libkern/OSByteOrder.h>
+#define htole64(x) OSSwapHostToLittleInt64(x)
+#define le64toh(x) OSSwapLittleToHostInt64(x)
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define be16toh(x) OSSwapBigToHostInt16(x)
+
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+
+/* ... for Solaris */
+#elif defined(__sun__)
+#include <sys/byteorder.h>
+#define htole64(x) LE_64(x)
+#define le64toh(x) LE_64(x)
+#define htobe64(x) BE_64(x)
+#define be64toh(x) BE_64(x)
+
+#define htole16(x) LE_16(x)
+#define le16toh(x) LE_16(x)
+#define htobe16(x) BE_16(x)
+#define be16toh(x) BE_16(x)
+
+#define htole32(x) LE_32(x)
+#define le32toh(x) LE_32(x)
+#define htobe32(x) BE_32(x)
+#define be32toh(x) BE_32(x)
+
+/* ... for the BSDs */
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+#include <sys/endian.h>
+#elif defined(__OpenBSD__)
+#include <endian.h>
+
+/* ... for Windows (MSVC)... not targeting XBOX 360! */
+#elif defined(_MSC_VER)
+
+#include <stdlib.h>
+#define htobe16(x) _byteswap_ushort(x)
+#define htole16(x) (x)
+#define be16toh(x) _byteswap_ushort(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) _byteswap_ulong(x)
+#define htole32(x) (x)
+#define be32toh(x) _byteswap_ulong(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) _byteswap_uint64(x)
+#define htole64(x) (x)
+#define be64toh(x) _byteswap_uint64(x)
+#define le64toh(x) (x)
+
+/* ... for Windows (GCC-like, e.g. mingw or clang) */
+#elif (defined(_WIN32) || defined(_WIN64)) && \
+ (defined(__GNUC__) || defined(__clang__))
+
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+
+/* ... generic big-endian fallback code */
+#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+/* byte swapping code inspired by:
+ * https://github.com/rweather/arduinolibs/blob/master/libraries/Crypto/utility/EndianUtil.h
+ * */
+
+#define htobe32(x) (x)
+#define be32toh(x) (x)
+#define htole32(x) \
+ (__extension__({ \
+ uint32_t _temp = (x); \
+ ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \
+ ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \
+ }))
+#define le32toh(x) (htole32((x)))
+
+#define htobe64(x) (x)
+#define be64toh(x) (x)
+#define htole64(x) \
+ (__extension__({ \
+ uint64_t __temp = (x); \
+ uint32_t __low = htobe32((uint32_t)__temp); \
+ uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \
+ (((uint64_t)__low) << 32) | __high; \
+ }))
+#define le64toh(x) (htole64((x)))
+
+/* ... generic little-endian fallback code */
+#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+#define htole32(x) (x)
+#define le32toh(x) (x)
+#define htobe32(x) \
+ (__extension__({ \
+ uint32_t _temp = (x); \
+ ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \
+ ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \
+ }))
+#define be32toh(x) (htobe32((x)))
+
+#define htole64(x) (x)
+#define le64toh(x) (x)
+#define htobe64(x) \
+ (__extension__({ \
+ uint64_t __temp = (x); \
+ uint32_t __low = htobe32((uint32_t)__temp); \
+ uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \
+ (((uint64_t)__low) << 32) | __high; \
+ }))
+#define be64toh(x) (htobe64((x)))
+
+/* ... couldn't determine endian-ness of the target platform */
+#else
+#error "Please define __BYTE_ORDER__!"
+
+#endif /* defined(__linux__) || ... */
+
+/* Loads and stores. These avoid undefined behavior due to unaligned memory
+ * accesses, via memcpy. */
+
+inline static uint16_t
+load16(uint8_t *b)
+{
+ uint16_t x;
+ memcpy(&x, b, 2);
+ return x;
+}
+
+inline static uint32_t
+load32(uint8_t *b)
+{
+ uint32_t x;
+ memcpy(&x, b, 4);
+ return x;
+}
+
+inline static uint64_t
+load64(uint8_t *b)
+{
+ uint64_t x;
+ memcpy(&x, b, 8);
+ return x;
+}
+
+inline static void
+store16(uint8_t *b, uint16_t i)
+{
+ memcpy(b, &i, 2);
+}
+
+inline static void
+store32(uint8_t *b, uint32_t i)
+{
+ memcpy(b, &i, 4);
+}
+
+inline static void
+store64(uint8_t *b, uint64_t i)
+{
+ memcpy(b, &i, 8);
+}
+
+#define load16_le(b) (le16toh(load16(b)))
+#define store16_le(b, i) (store16(b, htole16(i)))
+#define load16_be(b) (be16toh(load16(b)))
+#define store16_be(b, i) (store16(b, htobe16(i)))
+
+#define load32_le(b) (le32toh(load32(b)))
+#define store32_le(b, i) (store32(b, htole32(i)))
+#define load32_be(b) (be32toh(load32(b)))
+#define store32_be(b, i) (store32(b, htobe32(i)))
+
+#define load64_le(b) (le64toh(load64(b)))
+#define store64_le(b, i) (store64(b, htole64(i)))
+#define load64_be(b) (be64toh(load64(b)))
+#define store64_be(b, i) (store64(b, htobe64(i)))
+
+/******************************************************************************/
+/* Checked integers to ease the compilation of non-Low* code */
+/******************************************************************************/
+
+typedef int32_t Prims_pos, Prims_nat, Prims_nonzero, Prims_int,
+ krml_checked_int_t;
+
+inline static bool
+Prims_op_GreaterThanOrEqual(int32_t x, int32_t y)
+{
+ return x >= y;
+}
+
+inline static bool
+Prims_op_LessThanOrEqual(int32_t x, int32_t y)
+{
+ return x <= y;
+}
+
+inline static bool
+Prims_op_GreaterThan(int32_t x, int32_t y)
+{
+ return x > y;
+}
+
+inline static bool
+Prims_op_LessThan(int32_t x, int32_t y)
+{
+ return x < y;
+}
+
+#define RETURN_OR(x) \
+ do { \
+ int64_t __ret = x; \
+ if (__ret < INT32_MIN || INT32_MAX < __ret) { \
+ KRML_HOST_PRINTF("Prims.{int,nat,pos} integer overflow at %s:%d\n", \
+ __FILE__, __LINE__); \
+ KRML_HOST_EXIT(252); \
+ } \
+ return (int32_t)__ret; \
+ } while (0)
+
+inline static int32_t
+Prims_pow2(int32_t x)
+{
+ RETURN_OR((int64_t)1 << (int64_t)x);
+}
+
+inline static int32_t
+Prims_op_Multiply(int32_t x, int32_t y)
+{
+ RETURN_OR((int64_t)x * (int64_t)y);
+}
+
+inline static int32_t
+Prims_op_Addition(int32_t x, int32_t y)
+{
+ RETURN_OR((int64_t)x + (int64_t)y);
+}
+
+inline static int32_t
+Prims_op_Subtraction(int32_t x, int32_t y)
+{
+ RETURN_OR((int64_t)x - (int64_t)y);
+}
+
+inline static int32_t
+Prims_op_Division(int32_t x, int32_t y)
+{
+ RETURN_OR((int64_t)x / (int64_t)y);
+}
+
+inline static int32_t
+Prims_op_Modulus(int32_t x, int32_t y)
+{
+ RETURN_OR((int64_t)x % (int64_t)y);
+}
+
+inline static int8_t
+FStar_UInt8_uint_to_t(int8_t x)
+{
+ return x;
+}
+inline static int16_t
+FStar_UInt16_uint_to_t(int16_t x)
+{
+ return x;
+}
+inline static int32_t
+FStar_UInt32_uint_to_t(int32_t x)
+{
+ return x;
+}
+inline static int64_t
+FStar_UInt64_uint_to_t(int64_t x)
+{
+ return x;
+}
+
+inline static int8_t
+FStar_UInt8_v(int8_t x)
+{
+ return x;
+}
+inline static int16_t
+FStar_UInt16_v(int16_t x)
+{
+ return x;
+}
+inline static int32_t
+FStar_UInt32_v(int32_t x)
+{
+ return x;
+}
+inline static int64_t
+FStar_UInt64_v(int64_t x)
+{
+ return x;
+}
+
+/* Platform-specific 128-bit arithmetic. These are static functions in a header,
+ * so that each translation unit gets its own copy and the C compiler can
+ * optimize. */
+#ifndef KRML_NOUINT128
+typedef unsigned __int128 FStar_UInt128_t, FStar_UInt128_t_, uint128_t;
+
+static inline void
+print128(const char *where, uint128_t n)
+{
+ KRML_HOST_PRINTF("%s: [%" PRIu64 ",%" PRIu64 "]\n", where,
+ (uint64_t)(n >> 64), (uint64_t)n);
+}
+
+static inline uint128_t
+load128_le(uint8_t *b)
+{
+ uint128_t l = (uint128_t)load64_le(b);
+ uint128_t h = (uint128_t)load64_le(b + 8);
+ return (h << 64 | l);
+}
+
+static inline void
+store128_le(uint8_t *b, uint128_t n)
+{
+ store64_le(b, (uint64_t)n);
+ store64_le(b + 8, (uint64_t)(n >> 64));
+}
+
+static inline uint128_t
+load128_be(uint8_t *b)
+{
+ uint128_t h = (uint128_t)load64_be(b);
+ uint128_t l = (uint128_t)load64_be(b + 8);
+ return (h << 64 | l);
+}
+
+static inline void
+store128_be(uint8_t *b, uint128_t n)
+{
+ store64_be(b, (uint64_t)(n >> 64));
+ store64_be(b + 8, (uint64_t)n);
+}
+
+#define FStar_UInt128_add(x, y) ((x) + (y))
+#define FStar_UInt128_mul(x, y) ((x) * (y))
+#define FStar_UInt128_add_mod(x, y) ((x) + (y))
+#define FStar_UInt128_sub(x, y) ((x) - (y))
+#define FStar_UInt128_sub_mod(x, y) ((x) - (y))
+#define FStar_UInt128_logand(x, y) ((x) & (y))
+#define FStar_UInt128_logor(x, y) ((x) | (y))
+#define FStar_UInt128_logxor(x, y) ((x) ^ (y))
+#define FStar_UInt128_lognot(x) (~(x))
+#define FStar_UInt128_shift_left(x, y) ((x) << (y))
+#define FStar_UInt128_shift_right(x, y) ((x) >> (y))
+#define FStar_UInt128_uint64_to_uint128(x) ((uint128_t)(x))
+#define FStar_UInt128_uint128_to_uint64(x) ((uint64_t)(x))
+#define FStar_UInt128_mul_wide(x, y) ((uint128_t)(x) * (y))
+#define FStar_UInt128_op_Hat_Hat(x, y) ((x) ^ (y))
+
+static inline uint128_t
+FStar_UInt128_eq_mask(uint128_t x, uint128_t y)
+{
+ uint64_t mask =
+ FStar_UInt64_eq_mask((uint64_t)(x >> 64), (uint64_t)(y >> 64)) &
+ FStar_UInt64_eq_mask(x, y);
+ return ((uint128_t)mask) << 64 | mask;
+}
+
+static inline uint128_t
+FStar_UInt128_gte_mask(uint128_t x, uint128_t y)
+{
+ uint64_t mask =
+ (FStar_UInt64_gte_mask(x >> 64, y >> 64) &
+ ~(FStar_UInt64_eq_mask(x >> 64, y >> 64))) |
+ (FStar_UInt64_eq_mask(x >> 64, y >> 64) & FStar_UInt64_gte_mask(x, y));
+ return ((uint128_t)mask) << 64 | mask;
+}
+
+#else /* !defined(KRML_NOUINT128) */
+
+/* This is a bad circular dependency... should fix it properly. */
+#include "FStar.h"
+
+typedef FStar_UInt128_uint128 FStar_UInt128_t_, uint128_t;
+
+/* A series of definitions written using pointers. */
+static inline void
+print128_(const char *where, uint128_t *n)
+{
+ KRML_HOST_PRINTF("%s: [0x%08" PRIx64 ",0x%08" PRIx64 "]\n", where, n->high, n->low);
+}
+
+static inline void
+load128_le_(uint8_t *b, uint128_t *r)
+{
+ r->low = load64_le(b);
+ r->high = load64_le(b + 8);
+}
+
+static inline void
+store128_le_(uint8_t *b, uint128_t *n)
+{
+ store64_le(b, n->low);
+ store64_le(b + 8, n->high);
+}
+
+static inline void
+load128_be_(uint8_t *b, uint128_t *r)
+{
+ r->high = load64_be(b);
+ r->low = load64_be(b + 8);
+}
+
+static inline void
+store128_be_(uint8_t *b, uint128_t *n)
+{
+ store64_be(b, n->high);
+ store64_be(b + 8, n->low);
+}
+
+#ifndef KRML_NOSTRUCT_PASSING
+
+static inline void
+print128(const char *where, uint128_t n)
+{
+ print128_(where, &n);
+}
+
+static inline uint128_t
+load128_le(uint8_t *b)
+{
+ uint128_t r;
+ load128_le_(b, &r);
+ return r;
+}
+
+static inline void
+store128_le(uint8_t *b, uint128_t n)
+{
+ store128_le_(b, &n);
+}
+
+static inline uint128_t
+load128_be(uint8_t *b)
+{
+ uint128_t r;
+ load128_be_(b, &r);
+ return r;
+}
+
+static inline void
+store128_be(uint8_t *b, uint128_t n)
+{
+ store128_be_(b, &n);
+}
+
+#else /* !defined(KRML_STRUCT_PASSING) */
+
+#define print128 print128_
+#define load128_le load128_le_
+#define store128_le store128_le_
+#define load128_be load128_be_
+#define store128_be store128_be_
+
+#endif /* KRML_STRUCT_PASSING */
+#endif /* KRML_UINT128 */
+#endif /* __KREMLIB_H */
diff --git a/security/nss/lib/freebl/verified/kremlib_base.h b/security/nss/lib/freebl/verified/kremlib_base.h
new file mode 100644
index 000000000..61bac11d4
--- /dev/null
+++ b/security/nss/lib/freebl/verified/kremlib_base.h
@@ -0,0 +1,191 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __KREMLIB_BASE_H
+#define __KREMLIB_BASE_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/******************************************************************************/
+/* Some macros to ease compatibility */
+/******************************************************************************/
+
+/* Define __cdecl and friends when using GCC, so that we can safely compile code
+ * that contains __cdecl on all platforms. Note that this is in a separate
+ * header so that Dafny-generated code can include just this file. */
+#ifndef _MSC_VER
+/* Use the gcc predefined macros if on a platform/architectures that set them.
+ * Otherwise define them to be empty. */
+#ifndef __cdecl
+#define __cdecl
+#endif
+#ifndef __stdcall
+#define __stdcall
+#endif
+#ifndef __fastcall
+#define __fastcall
+#endif
+#endif
+
+#ifdef __GNUC__
+#define inline __inline__
+#endif
+
+/* GCC-specific attribute syntax; everyone else gets the standard C inline
+ * attribute. */
+#ifdef __GNU_C__
+#ifndef __clang__
+#define force_inline inline __attribute__((always_inline))
+#else
+#define force_inline inline
+#endif
+#else
+#define force_inline inline
+#endif
+
+/******************************************************************************/
+/* Implementing C.fst */
+/******************************************************************************/
+
+/* Uppercase issue; we have to define lowercase versions of the C macros (as we
+ * have no way to refer to an uppercase *variable* in F*). */
+extern int exit_success;
+extern int exit_failure;
+
+/* This one allows the user to write C.EXIT_SUCCESS. */
+typedef int exit_code;
+
+void print_string(const char *s);
+void print_bytes(uint8_t *b, uint32_t len);
+
+/* The universal null pointer defined in C.Nullity.fst */
+#define C_Nullity_null(X) 0
+
+/* If some globals need to be initialized before the main, then kremlin will
+ * generate and try to link last a function with this type: */
+void kremlinit_globals(void);
+
+/******************************************************************************/
+/* Implementation of machine integers (possibly of 128-bit integers) */
+/******************************************************************************/
+
+/* Integer types */
+typedef uint64_t FStar_UInt64_t, FStar_UInt64_t_;
+typedef int64_t FStar_Int64_t, FStar_Int64_t_;
+typedef uint32_t FStar_UInt32_t, FStar_UInt32_t_;
+typedef int32_t FStar_Int32_t, FStar_Int32_t_;
+typedef uint16_t FStar_UInt16_t, FStar_UInt16_t_;
+typedef int16_t FStar_Int16_t, FStar_Int16_t_;
+typedef uint8_t FStar_UInt8_t, FStar_UInt8_t_;
+typedef int8_t FStar_Int8_t, FStar_Int8_t_;
+
+static inline uint32_t
+rotate32_left(uint32_t x, uint32_t n)
+{
+ /* assert (n<32); */
+ return (x << n) | (x >> (32 - n));
+}
+static inline uint32_t
+rotate32_right(uint32_t x, uint32_t n)
+{
+ /* assert (n<32); */
+ return (x >> n) | (x << (32 - n));
+}
+
+/* Constant time comparisons */
+static inline uint8_t
+FStar_UInt8_eq_mask(uint8_t x, uint8_t y)
+{
+ x = ~(x ^ y);
+ x &= x << 4;
+ x &= x << 2;
+ x &= x << 1;
+ return (int8_t)x >> 7;
+}
+
+static inline uint8_t
+FStar_UInt8_gte_mask(uint8_t x, uint8_t y)
+{
+ return ~(uint8_t)(((int32_t)x - y) >> 31);
+}
+
+static inline uint16_t
+FStar_UInt16_eq_mask(uint16_t x, uint16_t y)
+{
+ x = ~(x ^ y);
+ x &= x << 8;
+ x &= x << 4;
+ x &= x << 2;
+ x &= x << 1;
+ return (int16_t)x >> 15;
+}
+
+static inline uint16_t
+FStar_UInt16_gte_mask(uint16_t x, uint16_t y)
+{
+ return ~(uint16_t)(((int32_t)x - y) >> 31);
+}
+
+static inline uint32_t
+FStar_UInt32_eq_mask(uint32_t x, uint32_t y)
+{
+ x = ~(x ^ y);
+ x &= x << 16;
+ x &= x << 8;
+ x &= x << 4;
+ x &= x << 2;
+ x &= x << 1;
+ return ((int32_t)x) >> 31;
+}
+
+static inline uint32_t
+FStar_UInt32_gte_mask(uint32_t x, uint32_t y)
+{
+ return ~((uint32_t)(((int64_t)x - y) >> 63));
+}
+
+static inline uint64_t
+FStar_UInt64_eq_mask(uint64_t x, uint64_t y)
+{
+ x = ~(x ^ y);
+ x &= x << 32;
+ x &= x << 16;
+ x &= x << 8;
+ x &= x << 4;
+ x &= x << 2;
+ x &= x << 1;
+ return ((int64_t)x) >> 63;
+}
+
+static inline uint64_t
+FStar_UInt64_gte_mask(uint64_t x, uint64_t y)
+{
+ uint64_t low63 =
+ ~((uint64_t)((int64_t)((int64_t)(x & UINT64_C(0x7fffffffffffffff)) -
+ (int64_t)(y & UINT64_C(0x7fffffffffffffff))) >>
+ 63));
+ uint64_t high_bit =
+ ~((uint64_t)((int64_t)((int64_t)(x & UINT64_C(0x8000000000000000)) -
+ (int64_t)(y & UINT64_C(0x8000000000000000))) >>
+ 63));
+ return low63 & high_bit;
+}
+
+#endif
diff --git a/security/nss/lib/freebl/verified/specs/Spec.CTR.fst b/security/nss/lib/freebl/verified/specs/Spec.CTR.fst
new file mode 100644
index 000000000..e411cd353
--- /dev/null
+++ b/security/nss/lib/freebl/verified/specs/Spec.CTR.fst
@@ -0,0 +1,98 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module Spec.CTR
+
+module ST = FStar.HyperStack.ST
+
+open FStar.Mul
+open FStar.Seq
+open Spec.Lib
+
+#reset-options "--initial_fuel 0 --max_fuel 0 --initial_ifuel 0 --max_ifuel 0"
+
+type block_cipher_ctx = {
+ keylen: nat ;
+ blocklen: (x:nat{x>0});
+ noncelen: nat;
+ counterbits: nat;
+ incr: pos}
+
+type key (c:block_cipher_ctx) = lbytes c.keylen
+type nonce (c:block_cipher_ctx) = lbytes c.noncelen
+type block (c:block_cipher_ctx) = lbytes (c.blocklen*c.incr)
+type counter (c:block_cipher_ctx) = UInt.uint_t c.counterbits
+type block_cipher (c:block_cipher_ctx) = key c -> nonce c -> counter c -> block c
+
+val xor: #len:nat -> x:lbytes len -> y:lbytes len -> Tot (lbytes len)
+let xor #len x y = map2 FStar.UInt8.(fun x y -> x ^^ y) x y
+
+
+val counter_mode_blocks:
+ ctx: block_cipher_ctx ->
+ bc: block_cipher ctx ->
+ k:key ctx -> n:nonce ctx -> c:counter ctx ->
+ plain:seq UInt8.t{c + ctx.incr * (length plain / ctx.blocklen) < pow2 ctx.counterbits /\
+ length plain % (ctx.blocklen * ctx.incr) = 0} ->
+ Tot (lbytes (length plain))
+ (decreases (length plain))
+#reset-options "--z3rlimit 200 --max_fuel 0"
+let rec counter_mode_blocks ctx block_enc key nonce counter plain =
+ let len = length plain in
+ let len' = len / (ctx.blocklen * ctx.incr) in
+ Math.Lemmas.lemma_div_mod len (ctx.blocklen * ctx.incr) ;
+ if len = 0 then Seq.createEmpty #UInt8.t
+ else (
+ let prefix, block = split plain (len - ctx.blocklen * ctx.incr) in
+ (* TODO: move to a single lemma for clarify *)
+ Math.Lemmas.lemma_mod_plus (length prefix) 1 (ctx.blocklen * ctx.incr);
+ Math.Lemmas.lemma_div_le (length prefix) len ctx.blocklen;
+ Spec.CTR.Lemmas.lemma_div len (ctx.blocklen * ctx.incr);
+ (* End TODO *)
+ let cipher = counter_mode_blocks ctx block_enc key nonce counter prefix in
+ let mask = block_enc key nonce (counter + (len / ctx.blocklen - 1) * ctx.incr) in
+ let eb = xor block mask in
+ cipher @| eb
+ )
+
+
+val counter_mode:
+ ctx: block_cipher_ctx ->
+ bc: block_cipher ctx ->
+ k:key ctx -> n:nonce ctx -> c:counter ctx ->
+ plain:seq UInt8.t{c + ctx.incr * (length plain / ctx.blocklen) < pow2 ctx.counterbits} ->
+ Tot (lbytes (length plain))
+ (decreases (length plain))
+#reset-options "--z3rlimit 200 --max_fuel 0"
+let counter_mode ctx block_enc key nonce counter plain =
+ let len = length plain in
+ let blocks_len = (ctx.incr * ctx.blocklen) * (len / (ctx.blocklen * ctx.incr)) in
+ let part_len = len % (ctx.blocklen * ctx.incr) in
+ (* TODO: move to a single lemma for clarify *)
+ Math.Lemmas.lemma_div_mod len (ctx.blocklen * ctx.incr);
+ Math.Lemmas.multiple_modulo_lemma (len / (ctx.blocklen * ctx.incr)) (ctx.blocklen * ctx.incr);
+ Math.Lemmas.lemma_div_le (blocks_len) len ctx.blocklen;
+ (* End TODO *)
+ let blocks, last_block = split plain blocks_len in
+ let cipher_blocks = counter_mode_blocks ctx block_enc key nonce counter blocks in
+ let cipher_last_block =
+ if part_len > 0
+ then (* encrypt final partial block(s) *)
+ let mask = block_enc key nonce (counter+ctx.incr*(length plain / ctx.blocklen)) in
+ let mask = slice mask 0 part_len in
+ assert(length last_block = part_len);
+ xor #part_len last_block mask
+ else createEmpty in
+ cipher_blocks @| cipher_last_block
diff --git a/security/nss/lib/freebl/verified/specs/Spec.Chacha20.fst b/security/nss/lib/freebl/verified/specs/Spec.Chacha20.fst
new file mode 100644
index 000000000..0bdc69725
--- /dev/null
+++ b/security/nss/lib/freebl/verified/specs/Spec.Chacha20.fst
@@ -0,0 +1,169 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module Spec.Chacha20
+
+module ST = FStar.HyperStack.ST
+
+open FStar.Mul
+open FStar.Seq
+open FStar.UInt32
+open FStar.Endianness
+open Spec.Lib
+open Spec.Chacha20.Lemmas
+open Seq.Create
+
+#set-options "--max_fuel 0 --z3rlimit 100"
+
+(* Constants *)
+let keylen = 32 (* in bytes *)
+let blocklen = 64 (* in bytes *)
+let noncelen = 12 (* in bytes *)
+
+type key = lbytes keylen
+type block = lbytes blocklen
+type nonce = lbytes noncelen
+type counter = UInt.uint_t 32
+
+// using @ as a functional substitute for ;
+// internally, blocks are represented as 16 x 4-byte integers
+type state = m:seq UInt32.t {length m = 16}
+type idx = n:nat{n < 16}
+type shuffle = state -> Tot state
+
+let line (a:idx) (b:idx) (d:idx) (s:t{0 < v s /\ v s < 32}) (m:state) : Tot state =
+ let m = m.[a] <- (m.[a] +%^ m.[b]) in
+ let m = m.[d] <- ((m.[d] ^^ m.[a]) <<< s) in m
+
+let quarter_round a b c d : shuffle =
+ line a b d 16ul @
+ line c d b 12ul @
+ line a b d 8ul @
+ line c d b 7ul
+
+let column_round : shuffle =
+ quarter_round 0 4 8 12 @
+ quarter_round 1 5 9 13 @
+ quarter_round 2 6 10 14 @
+ quarter_round 3 7 11 15
+
+let diagonal_round : shuffle =
+ quarter_round 0 5 10 15 @
+ quarter_round 1 6 11 12 @
+ quarter_round 2 7 8 13 @
+ quarter_round 3 4 9 14
+
+let double_round: shuffle =
+ column_round @ diagonal_round (* 2 rounds *)
+
+let rounds : shuffle =
+ iter 10 double_round (* 20 rounds *)
+
+let chacha20_core (s:state) : Tot state =
+ let s' = rounds s in
+ Spec.Loops.seq_map2 (fun x y -> x +%^ y) s' s
+
+(* state initialization *)
+let c0 = 0x61707865ul
+let c1 = 0x3320646eul
+let c2 = 0x79622d32ul
+let c3 = 0x6b206574ul
+
+let setup (k:key) (n:nonce) (c:counter): Tot state =
+ create_4 c0 c1 c2 c3 @|
+ uint32s_from_le 8 k @|
+ create_1 (UInt32.uint_to_t c) @|
+ uint32s_from_le 3 n
+
+let chacha20_block (k:key) (n:nonce) (c:counter): Tot block =
+ let st = setup k n c in
+ let st' = chacha20_core st in
+ uint32s_to_le 16 st'
+
+let chacha20_ctx: Spec.CTR.block_cipher_ctx =
+ let open Spec.CTR in
+ {
+ keylen = keylen;
+ blocklen = blocklen;
+ noncelen = noncelen;
+ counterbits = 32;
+ incr = 1
+ }
+
+let chacha20_cipher: Spec.CTR.block_cipher chacha20_ctx = chacha20_block
+
+let chacha20_encrypt_bytes key nonce counter m =
+ Spec.CTR.counter_mode chacha20_ctx chacha20_cipher key nonce counter m
+
+
+unfold let test_plaintext = [
+ 0x4cuy; 0x61uy; 0x64uy; 0x69uy; 0x65uy; 0x73uy; 0x20uy; 0x61uy;
+ 0x6euy; 0x64uy; 0x20uy; 0x47uy; 0x65uy; 0x6euy; 0x74uy; 0x6cuy;
+ 0x65uy; 0x6duy; 0x65uy; 0x6euy; 0x20uy; 0x6fuy; 0x66uy; 0x20uy;
+ 0x74uy; 0x68uy; 0x65uy; 0x20uy; 0x63uy; 0x6cuy; 0x61uy; 0x73uy;
+ 0x73uy; 0x20uy; 0x6fuy; 0x66uy; 0x20uy; 0x27uy; 0x39uy; 0x39uy;
+ 0x3auy; 0x20uy; 0x49uy; 0x66uy; 0x20uy; 0x49uy; 0x20uy; 0x63uy;
+ 0x6fuy; 0x75uy; 0x6cuy; 0x64uy; 0x20uy; 0x6fuy; 0x66uy; 0x66uy;
+ 0x65uy; 0x72uy; 0x20uy; 0x79uy; 0x6fuy; 0x75uy; 0x20uy; 0x6fuy;
+ 0x6euy; 0x6cuy; 0x79uy; 0x20uy; 0x6fuy; 0x6euy; 0x65uy; 0x20uy;
+ 0x74uy; 0x69uy; 0x70uy; 0x20uy; 0x66uy; 0x6fuy; 0x72uy; 0x20uy;
+ 0x74uy; 0x68uy; 0x65uy; 0x20uy; 0x66uy; 0x75uy; 0x74uy; 0x75uy;
+ 0x72uy; 0x65uy; 0x2cuy; 0x20uy; 0x73uy; 0x75uy; 0x6euy; 0x73uy;
+ 0x63uy; 0x72uy; 0x65uy; 0x65uy; 0x6euy; 0x20uy; 0x77uy; 0x6fuy;
+ 0x75uy; 0x6cuy; 0x64uy; 0x20uy; 0x62uy; 0x65uy; 0x20uy; 0x69uy;
+ 0x74uy; 0x2euy
+]
+
+unfold let test_ciphertext = [
+ 0x6euy; 0x2euy; 0x35uy; 0x9auy; 0x25uy; 0x68uy; 0xf9uy; 0x80uy;
+ 0x41uy; 0xbauy; 0x07uy; 0x28uy; 0xdduy; 0x0duy; 0x69uy; 0x81uy;
+ 0xe9uy; 0x7euy; 0x7auy; 0xecuy; 0x1duy; 0x43uy; 0x60uy; 0xc2uy;
+ 0x0auy; 0x27uy; 0xafuy; 0xccuy; 0xfduy; 0x9fuy; 0xaeuy; 0x0buy;
+ 0xf9uy; 0x1buy; 0x65uy; 0xc5uy; 0x52uy; 0x47uy; 0x33uy; 0xabuy;
+ 0x8fuy; 0x59uy; 0x3duy; 0xabuy; 0xcduy; 0x62uy; 0xb3uy; 0x57uy;
+ 0x16uy; 0x39uy; 0xd6uy; 0x24uy; 0xe6uy; 0x51uy; 0x52uy; 0xabuy;
+ 0x8fuy; 0x53uy; 0x0cuy; 0x35uy; 0x9fuy; 0x08uy; 0x61uy; 0xd8uy;
+ 0x07uy; 0xcauy; 0x0duy; 0xbfuy; 0x50uy; 0x0duy; 0x6auy; 0x61uy;
+ 0x56uy; 0xa3uy; 0x8euy; 0x08uy; 0x8auy; 0x22uy; 0xb6uy; 0x5euy;
+ 0x52uy; 0xbcuy; 0x51uy; 0x4duy; 0x16uy; 0xccuy; 0xf8uy; 0x06uy;
+ 0x81uy; 0x8cuy; 0xe9uy; 0x1auy; 0xb7uy; 0x79uy; 0x37uy; 0x36uy;
+ 0x5auy; 0xf9uy; 0x0buy; 0xbfuy; 0x74uy; 0xa3uy; 0x5buy; 0xe6uy;
+ 0xb4uy; 0x0buy; 0x8euy; 0xeduy; 0xf2uy; 0x78uy; 0x5euy; 0x42uy;
+ 0x87uy; 0x4duy
+]
+
+unfold let test_key = [
+ 0uy; 1uy; 2uy; 3uy; 4uy; 5uy; 6uy; 7uy;
+ 8uy; 9uy; 10uy; 11uy; 12uy; 13uy; 14uy; 15uy;
+ 16uy; 17uy; 18uy; 19uy; 20uy; 21uy; 22uy; 23uy;
+ 24uy; 25uy; 26uy; 27uy; 28uy; 29uy; 30uy; 31uy
+ ]
+unfold let test_nonce = [
+ 0uy; 0uy; 0uy; 0uy; 0uy; 0uy; 0uy; 0x4auy; 0uy; 0uy; 0uy; 0uy
+ ]
+
+unfold let test_counter = 1
+
+let test() =
+ assert_norm(List.Tot.length test_plaintext = 114);
+ assert_norm(List.Tot.length test_ciphertext = 114);
+ assert_norm(List.Tot.length test_key = 32);
+ assert_norm(List.Tot.length test_nonce = 12);
+ let test_plaintext = createL test_plaintext in
+ let test_ciphertext = createL test_ciphertext in
+ let test_key = createL test_key in
+ let test_nonce = createL test_nonce in
+ chacha20_encrypt_bytes test_key test_nonce test_counter test_plaintext
+ = test_ciphertext
diff --git a/security/nss/lib/freebl/verified/specs/Spec.Curve25519.fst b/security/nss/lib/freebl/verified/specs/Spec.Curve25519.fst
new file mode 100644
index 000000000..af4035b09
--- /dev/null
+++ b/security/nss/lib/freebl/verified/specs/Spec.Curve25519.fst
@@ -0,0 +1,168 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module Spec.Curve25519
+
+module ST = FStar.HyperStack.ST
+
+open FStar.Mul
+open FStar.Seq
+open FStar.UInt8
+open FStar.Endianness
+open Spec.Lib
+open Spec.Curve25519.Lemmas
+
+#reset-options "--initial_fuel 0 --max_fuel 0 --z3rlimit 20"
+
+(* Field types and parameters *)
+let prime = pow2 255 - 19
+type elem : Type0 = e:int{e >= 0 /\ e < prime}
+let fadd e1 e2 = (e1 + e2) % prime
+let fsub e1 e2 = (e1 - e2) % prime
+let fmul e1 e2 = (e1 * e2) % prime
+let zero : elem = 0
+let one : elem = 1
+let ( +@ ) = fadd
+let ( *@ ) = fmul
+
+(** Exponentiation *)
+let rec ( ** ) (e:elem) (n:pos) : Tot elem (decreases n) =
+ if n = 1 then e
+ else
+ if n % 2 = 0 then op_Star_Star (e `fmul` e) (n / 2)
+ else e `fmul` (op_Star_Star (e `fmul` e) ((n-1)/2))
+
+(* Type aliases *)
+type scalar = lbytes 32
+type serialized_point = lbytes 32
+type proj_point = | Proj: x:elem -> z:elem -> proj_point
+
+let decodeScalar25519 (k:scalar) =
+ let k = k.[0] <- (k.[0] &^ 248uy) in
+ let k = k.[31] <- ((k.[31] &^ 127uy) |^ 64uy) in k
+
+let decodePoint (u:serialized_point) =
+ (little_endian u % pow2 255) % prime
+
+let add_and_double qx nq nqp1 =
+ let x_1 = qx in
+ let x_2, z_2 = nq.x, nq.z in
+ let x_3, z_3 = nqp1.x, nqp1.z in
+ let a = x_2 `fadd` z_2 in
+ let aa = a**2 in
+ let b = x_2 `fsub` z_2 in
+ let bb = b**2 in
+ let e = aa `fsub` bb in
+ let c = x_3 `fadd` z_3 in
+ let d = x_3 `fsub` z_3 in
+ let da = d `fmul` a in
+ let cb = c `fmul` b in
+ let x_3 = (da `fadd` cb)**2 in
+ let z_3 = x_1 `fmul` ((da `fsub` cb)**2) in
+ let x_2 = aa `fmul` bb in
+ let z_2 = e `fmul` (aa `fadd` (121665 `fmul` e)) in
+ Proj x_2 z_2, Proj x_3 z_3
+
+let ith_bit (k:scalar) (i:nat{i < 256}) =
+ let q = i / 8 in let r = i % 8 in
+ (v (k.[q]) / pow2 r) % 2
+
+let rec montgomery_ladder_ (init:elem) x xp1 (k:scalar) (ctr:nat{ctr<=256})
+ : Tot proj_point (decreases ctr) =
+ if ctr = 0 then x
+ else (
+ let ctr' = ctr - 1 in
+ let (x', xp1') =
+ if ith_bit k ctr' = 1 then (
+ let nqp2, nqp1 = add_and_double init xp1 x in
+ nqp1, nqp2
+ ) else add_and_double init x xp1 in
+ montgomery_ladder_ init x' xp1' k ctr'
+ )
+
+let montgomery_ladder (init:elem) (k:scalar) : Tot proj_point =
+ montgomery_ladder_ init (Proj one zero) (Proj init one) k 256
+
+let encodePoint (p:proj_point) : Tot serialized_point =
+ let p = p.x `fmul` (p.z ** (prime - 2)) in
+ little_bytes 32ul p
+
+let scalarmult (k:scalar) (u:serialized_point) : Tot serialized_point =
+ let k = decodeScalar25519 k in
+ let u = decodePoint u in
+ let res = montgomery_ladder u k in
+ encodePoint res
+
+
+(* ********************* *)
+(* RFC 7748 Test Vectors *)
+(* ********************* *)
+
+let scalar1 = [
+ 0xa5uy; 0x46uy; 0xe3uy; 0x6buy; 0xf0uy; 0x52uy; 0x7cuy; 0x9duy;
+ 0x3buy; 0x16uy; 0x15uy; 0x4buy; 0x82uy; 0x46uy; 0x5euy; 0xdduy;
+ 0x62uy; 0x14uy; 0x4cuy; 0x0auy; 0xc1uy; 0xfcuy; 0x5auy; 0x18uy;
+ 0x50uy; 0x6auy; 0x22uy; 0x44uy; 0xbauy; 0x44uy; 0x9auy; 0xc4uy
+]
+
+let scalar2 = [
+ 0x4buy; 0x66uy; 0xe9uy; 0xd4uy; 0xd1uy; 0xb4uy; 0x67uy; 0x3cuy;
+ 0x5auy; 0xd2uy; 0x26uy; 0x91uy; 0x95uy; 0x7duy; 0x6auy; 0xf5uy;
+ 0xc1uy; 0x1buy; 0x64uy; 0x21uy; 0xe0uy; 0xeauy; 0x01uy; 0xd4uy;
+ 0x2cuy; 0xa4uy; 0x16uy; 0x9euy; 0x79uy; 0x18uy; 0xbauy; 0x0duy
+]
+
+let input1 = [
+ 0xe6uy; 0xdbuy; 0x68uy; 0x67uy; 0x58uy; 0x30uy; 0x30uy; 0xdbuy;
+ 0x35uy; 0x94uy; 0xc1uy; 0xa4uy; 0x24uy; 0xb1uy; 0x5fuy; 0x7cuy;
+ 0x72uy; 0x66uy; 0x24uy; 0xecuy; 0x26uy; 0xb3uy; 0x35uy; 0x3buy;
+ 0x10uy; 0xa9uy; 0x03uy; 0xa6uy; 0xd0uy; 0xabuy; 0x1cuy; 0x4cuy
+]
+
+let input2 = [
+ 0xe5uy; 0x21uy; 0x0fuy; 0x12uy; 0x78uy; 0x68uy; 0x11uy; 0xd3uy;
+ 0xf4uy; 0xb7uy; 0x95uy; 0x9duy; 0x05uy; 0x38uy; 0xaeuy; 0x2cuy;
+ 0x31uy; 0xdbuy; 0xe7uy; 0x10uy; 0x6fuy; 0xc0uy; 0x3cuy; 0x3euy;
+ 0xfcuy; 0x4cuy; 0xd5uy; 0x49uy; 0xc7uy; 0x15uy; 0xa4uy; 0x93uy
+]
+
+let expected1 = [
+ 0xc3uy; 0xdauy; 0x55uy; 0x37uy; 0x9duy; 0xe9uy; 0xc6uy; 0x90uy;
+ 0x8euy; 0x94uy; 0xeauy; 0x4duy; 0xf2uy; 0x8duy; 0x08uy; 0x4fuy;
+ 0x32uy; 0xecuy; 0xcfuy; 0x03uy; 0x49uy; 0x1cuy; 0x71uy; 0xf7uy;
+ 0x54uy; 0xb4uy; 0x07uy; 0x55uy; 0x77uy; 0xa2uy; 0x85uy; 0x52uy
+]
+let expected2 = [
+ 0x95uy; 0xcbuy; 0xdeuy; 0x94uy; 0x76uy; 0xe8uy; 0x90uy; 0x7duy;
+ 0x7auy; 0xaduy; 0xe4uy; 0x5cuy; 0xb4uy; 0xb8uy; 0x73uy; 0xf8uy;
+ 0x8buy; 0x59uy; 0x5auy; 0x68uy; 0x79uy; 0x9fuy; 0xa1uy; 0x52uy;
+ 0xe6uy; 0xf8uy; 0xf7uy; 0x64uy; 0x7auy; 0xacuy; 0x79uy; 0x57uy
+]
+
+let test () =
+ assert_norm(List.Tot.length scalar1 = 32);
+ assert_norm(List.Tot.length scalar2 = 32);
+ assert_norm(List.Tot.length input1 = 32);
+ assert_norm(List.Tot.length input2 = 32);
+ assert_norm(List.Tot.length expected1 = 32);
+ assert_norm(List.Tot.length expected2 = 32);
+ let scalar1 = createL scalar1 in
+ let scalar2 = createL scalar2 in
+ let input1 = createL input1 in
+ let input2 = createL input2 in
+ let expected1 = createL expected1 in
+ let expected2 = createL expected2 in
+ scalarmult scalar1 input1 = expected1
+ && scalarmult scalar2 input2 = expected2
diff --git a/security/nss/lib/freebl/verified/specs/Spec.Poly1305.fst b/security/nss/lib/freebl/verified/specs/Spec.Poly1305.fst
new file mode 100644
index 000000000..f9d8a4cb2
--- /dev/null
+++ b/security/nss/lib/freebl/verified/specs/Spec.Poly1305.fst
@@ -0,0 +1,107 @@
+/* Copyright 2016-2017 INRIA and Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module Spec.Poly1305
+
+module ST = FStar.HyperStack.ST
+
+open FStar.Math.Lib
+open FStar.Mul
+open FStar.Seq
+open FStar.UInt8
+open FStar.Endianness
+open Spec.Poly1305.Lemmas
+
+#set-options "--initial_fuel 0 --max_fuel 0 --initial_ifuel 0 --max_ifuel 0"
+
+(* Field types and parameters *)
+let prime = pow2 130 - 5
+type elem = e:int{e >= 0 /\ e < prime}
+let fadd (e1:elem) (e2:elem) = (e1 + e2) % prime
+let fmul (e1:elem) (e2:elem) = (e1 * e2) % prime
+let zero : elem = 0
+let one : elem = 1
+let op_Plus_At = fadd
+let op_Star_At = fmul
+(* Type aliases *)
+let op_Amp_Bar = UInt.logand #128
+type word = w:bytes{length w <= 16}
+type word_16 = w:bytes{length w = 16}
+type tag = word_16
+type key = lbytes 32
+type text = seq word
+
+(* Specification code *)
+let encode (w:word) =
+ (pow2 (8 * length w)) `fadd` (little_endian w)
+
+let rec poly (txt:text) (r:e:elem) : Tot elem (decreases (length txt)) =
+ if length txt = 0 then zero
+ else
+ let a = poly (Seq.tail txt) r in
+ let n = encode (Seq.head txt) in
+ (n `fadd` a) `fmul` r
+
+let encode_r (rb:word_16) =
+ (little_endian rb) &| 0x0ffffffc0ffffffc0ffffffc0fffffff
+
+let finish (a:elem) (s:word_16) : Tot tag =
+ let n = (a + little_endian s) % pow2 128 in
+ little_bytes 16ul n
+
+let rec encode_bytes (txt:bytes) : Tot text (decreases (length txt)) =
+ if length txt = 0 then createEmpty
+ else
+ let w, txt = split txt (min (length txt) 16) in
+ append_last (encode_bytes txt) w
+
+let poly1305 (msg:bytes) (k:key) : Tot tag =
+ let text = encode_bytes msg in
+ let r = encode_r (slice k 0 16) in
+ let s = slice k 16 32 in
+ finish (poly text r) s
+
+
+(* ********************* *)
+(* RFC 7539 Test Vectors *)
+(* ********************* *)
+
+#reset-options "--initial_fuel 0 --max_fuel 0 --z3rlimit 20"
+
+unfold let msg = [
+ 0x43uy; 0x72uy; 0x79uy; 0x70uy; 0x74uy; 0x6fuy; 0x67uy; 0x72uy;
+ 0x61uy; 0x70uy; 0x68uy; 0x69uy; 0x63uy; 0x20uy; 0x46uy; 0x6fuy;
+ 0x72uy; 0x75uy; 0x6duy; 0x20uy; 0x52uy; 0x65uy; 0x73uy; 0x65uy;
+ 0x61uy; 0x72uy; 0x63uy; 0x68uy; 0x20uy; 0x47uy; 0x72uy; 0x6fuy;
+ 0x75uy; 0x70uy ]
+
+unfold let k = [
+ 0x85uy; 0xd6uy; 0xbeuy; 0x78uy; 0x57uy; 0x55uy; 0x6duy; 0x33uy;
+ 0x7fuy; 0x44uy; 0x52uy; 0xfeuy; 0x42uy; 0xd5uy; 0x06uy; 0xa8uy;
+ 0x01uy; 0x03uy; 0x80uy; 0x8auy; 0xfbuy; 0x0duy; 0xb2uy; 0xfduy;
+ 0x4auy; 0xbfuy; 0xf6uy; 0xafuy; 0x41uy; 0x49uy; 0xf5uy; 0x1buy ]
+
+unfold let expected = [
+ 0xa8uy; 0x06uy; 0x1duy; 0xc1uy; 0x30uy; 0x51uy; 0x36uy; 0xc6uy;
+ 0xc2uy; 0x2buy; 0x8buy; 0xafuy; 0x0cuy; 0x01uy; 0x27uy; 0xa9uy ]
+
+let test () : Tot bool =
+ assert_norm(List.Tot.length msg = 34);
+ assert_norm(List.Tot.length k = 32);
+ assert_norm(List.Tot.length expected = 16);
+ let msg = createL msg in
+ let k = createL k in
+ let expected = createL expected in
+ poly1305 msg k = expected
diff --git a/security/nss/lib/nss/nss.def b/security/nss/lib/nss/nss.def
index e1453cc84..4f0ade4d0 100644
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1115,3 +1115,21 @@ PK11_GetTokenURI;
;+ local:
;+ *;
;+};
+;+NSS_3.33 { # NSS 3.33 release
+;+ global:
+CERT_FindCertByIssuerAndSNCX;
+CERT_FindCertByNicknameOrEmailAddrCX;
+CERT_FindCertByNicknameOrEmailAddrForUsageCX;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.34 { # NSS 3.34 release
+;+ global:
+PK11_CreateManagedGenericObject;
+SGN_NewContextWithAlgorithmID;
+SEC_SignDataWithAlgorithmID;
+SEC_DerSignDataWithAlgorithmID;
+SEC_CreateSignatureAlgorithmParameters;
+;+ local:
+;+ *;
+;+};
diff --git a/security/nss/lib/nss/nss.h b/security/nss/lib/nss/nss.h
index 8238faca7..62cf36730 100644
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -22,10 +22,10 @@
* The format of the version string should be
* "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
*/
-#define NSS_VERSION "3.32.1" _NSS_CUSTOMIZED
+#define NSS_VERSION "3.35" _NSS_CUSTOMIZED
#define NSS_VMAJOR 3
-#define NSS_VMINOR 32
-#define NSS_VPATCH 1
+#define NSS_VMINOR 35
+#define NSS_VPATCH 0
#define NSS_VBUILD 0
#define NSS_BETA PR_FALSE
@@ -291,6 +291,15 @@ SECStatus NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData);
#define NSS_DTLS_VERSION_MIN_POLICY 0x00a
#define NSS_DTLS_VERSION_MAX_POLICY 0x00b
+/* Until NSS 3.30, the PKCS#12 implementation used BMPString encoding
+ * for all passwords. This changed to use UTF-8 for non-PKCS#12 PBEs
+ * in NSS 3.31.
+ *
+ * For backward compatibility, this option reverts the behavior to the
+ * old NSS versions. This option might be removed in the future NSS
+ * releases; don't rely on it. */
+#define __NSS_PKCS12_DECODE_FORCE_UNICODE 0x00c
+
/*
* Set and get global options for the NSS library.
*/
diff --git a/security/nss/lib/nss/nssoptions.c b/security/nss/lib/nss/nssoptions.c
index fc97d6278..1339cede8 100644
--- a/security/nss/lib/nss/nssoptions.c
+++ b/security/nss/lib/nss/nssoptions.c
@@ -23,6 +23,7 @@ struct nssOps {
PRInt32 tlsVersionMaxPolicy;
PRInt32 dtlsVersionMinPolicy;
PRInt32 dtlsVersionMaxPolicy;
+ PRInt32 pkcs12DecodeForceUnicode;
};
static struct nssOps nss_ops = {
@@ -33,6 +34,7 @@ static struct nssOps nss_ops = {
0xffff, /* set TLS max to more than the largest legal SSL value */
1,
0xffff,
+ PR_FALSE
};
SECStatus
@@ -62,6 +64,9 @@ NSS_OptionSet(PRInt32 which, PRInt32 value)
case NSS_DTLS_VERSION_MAX_POLICY:
nss_ops.dtlsVersionMaxPolicy = value;
break;
+ case __NSS_PKCS12_DECODE_FORCE_UNICODE:
+ nss_ops.pkcs12DecodeForceUnicode = value;
+ break;
default:
rv = SECFailure;
}
@@ -96,6 +101,9 @@ NSS_OptionGet(PRInt32 which, PRInt32 *value)
case NSS_DTLS_VERSION_MAX_POLICY:
*value = nss_ops.dtlsVersionMaxPolicy;
break;
+ case __NSS_PKCS12_DECODE_FORCE_UNICODE:
+ *value = nss_ops.pkcs12DecodeForceUnicode;
+ break;
default:
rv = SECFailure;
}
diff --git a/security/nss/lib/nss/utilwrap.c b/security/nss/lib/nss/utilwrap.c
index 938d95c0f..48e147d88 100644
--- a/security/nss/lib/nss/utilwrap.c
+++ b/security/nss/lib/nss/utilwrap.c
@@ -75,6 +75,8 @@
#undef PORT_UCS2_ASCIIConversion
#undef PORT_UCS2_UTF8Conversion
#undef PORT_ZAlloc
+#undef PORT_ZAllocAligned
+#undef PORT_ZAllocAlignedOffset
#undef PORT_ZFree
#undef SEC_ASN1Decode
#undef SEC_ASN1DecodeInteger
@@ -144,6 +146,18 @@ PORT_ZAlloc(size_t bytes)
return PORT_ZAlloc_Util(bytes);
}
+void *
+PORT_ZAllocAligned(size_t bytes, size_t alignment, void **mem)
+{
+ return PORT_ZAllocAligned_Util(bytes, alignment, mem);
+}
+
+void *
+PORT_ZAllocAlignedOffset(size_t bytes, size_t alignment, size_t offset)
+{
+ return PORT_ZAllocAlignedOffset_Util(bytes, alignment, offset);
+}
+
void
PORT_Free(void *ptr)
{
diff --git a/security/nss/lib/pk11wrap/pk11load.c b/security/nss/lib/pk11wrap/pk11load.c
index 91339fad8..d1f6ec442 100644
--- a/security/nss/lib/pk11wrap/pk11load.c
+++ b/security/nss/lib/pk11wrap/pk11load.c
@@ -64,8 +64,7 @@ secmodUnlockMutext(CK_VOID_PTR mutext)
static SECMODModuleID nextModuleID = 1;
static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
secmodCreateMutext, secmodDestroyMutext, secmodLockMutext,
- secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS |
- CKF_OS_LOCKING_OK,
+ secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS | CKF_OS_LOCKING_OK,
NULL
};
static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = {
diff --git a/security/nss/lib/pk11wrap/pk11merge.c b/security/nss/lib/pk11wrap/pk11merge.c
index 8c4c5129a..b2101b819 100644
--- a/security/nss/lib/pk11wrap/pk11merge.c
+++ b/security/nss/lib/pk11wrap/pk11merge.c
@@ -68,8 +68,11 @@ pk11_copyAttributes(PLArenaPool *arena,
copyTemplate, copyTemplateCount);
/* if we have missing attributes, just skip them and create the object */
if (crv == CKR_ATTRIBUTE_TYPE_INVALID) {
- int i, j;
+ CK_ULONG i, j;
newTemplate = PORT_NewArray(CK_ATTRIBUTE, copyTemplateCount);
+ if (!newTemplate) {
+ return SECFailure;
+ }
/* remove the unknown attributes. If we don't have enough attributes
* PK11_CreateNewObject() will fail */
for (i = 0, j = 0; i < copyTemplateCount; i++) {
@@ -1258,6 +1261,7 @@ pk11_newMergeLogNode(PLArenaPool *arena,
/* initialize it */
obj->slot = slot;
obj->objectID = id;
+ obj->owner = PR_FALSE;
newLog->object = obj;
newLog->error = error;
diff --git a/security/nss/lib/pk11wrap/pk11obj.c b/security/nss/lib/pk11wrap/pk11obj.c
index 47c56154d..b97caddd4 100644
--- a/security/nss/lib/pk11wrap/pk11obj.c
+++ b/security/nss/lib/pk11wrap/pk11obj.c
@@ -201,7 +201,6 @@ PK11_GetAttributes(PLArenaPool *arena, PK11SlotInfo *slot,
/* make pedantic happy... note that it's only used arena != NULL */
void *mark = NULL;
CK_RV crv;
- PORT_Assert(slot->session != CK_INVALID_SESSION);
if (slot->session == CK_INVALID_SESSION)
return CKR_SESSION_HANDLE_INVALID;
@@ -1506,6 +1505,7 @@ PK11_FindGenericObjects(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass)
/* initialize it */
obj->slot = PK11_ReferenceSlot(slot);
obj->objectID = objectIDs[i];
+ obj->owner = PR_FALSE;
obj->next = NULL;
obj->prev = NULL;
@@ -1586,6 +1586,9 @@ PK11_DestroyGenericObject(PK11GenericObject *object)
PK11_UnlinkGenericObject(object);
if (object->slot) {
+ if (object->owner) {
+ PK11_DestroyObject(object->slot, object->objectID);
+ }
PK11_FreeSlot(object->slot);
}
PORT_Free(object);
@@ -1627,8 +1630,9 @@ PK11_DestroyGenericObjects(PK11GenericObject *objects)
* Hand Create a new object and return the Generic object for our new object.
*/
PK11GenericObject *
-PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
- int count, PRBool token)
+pk11_CreateGenericObjectHelper(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token, PRBool owner)
{
CK_OBJECT_HANDLE objectID;
PK11GenericObject *obj;
@@ -1652,11 +1656,40 @@ PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
/* initialize it */
obj->slot = PK11_ReferenceSlot(slot);
obj->objectID = objectID;
+ obj->owner = owner;
obj->next = NULL;
obj->prev = NULL;
return obj;
}
+/* This is the classic interface. Applications would call this function to
+ * create new object that would not be destroyed later. This lead to resource
+ * leaks (and thus memory leaks in the PKCS #11 module). To solve this we have
+ * a new interface that automatically marks objects created on the fly to be
+ * destroyed later.
+ * The old interface is preserved because applications like Mozilla purposefully
+ * leak the reference to be found later with PK11_FindGenericObjects. New
+ * applications should use the new interface PK11_CreateManagedGenericObject */
+PK11GenericObject *
+PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token)
+{
+ return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
+ PR_FALSE);
+}
+
+/* Use this interface. It will automatically destroy any temporary objects
+ * (token = PR_FALSE) when the PK11GenericObject is freed. Permanent objects still
+ * need to be destroyed by hand with PK11_DestroyTokenObject.
+ */
+PK11GenericObject *
+PK11_CreateManagedGenericObject(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate, int count, PRBool token)
+{
+ return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
+ !token);
+}
+
/*
* Change an attribute on a raw object
*/
diff --git a/security/nss/lib/pk11wrap/pk11pars.c b/security/nss/lib/pk11wrap/pk11pars.c
index ee20789cc..fc30222b3 100644
--- a/security/nss/lib/pk11wrap/pk11pars.c
+++ b/security/nss/lib/pk11wrap/pk11pars.c
@@ -413,8 +413,7 @@ static const policyFlagDef policyFlagList[] = {
/* add other signatures in the future */
{ CIPHER_NAME("SIGNATURE"), NSS_USE_ALG_IN_CERT_SIGNATURE },
/* enable everything */
- { CIPHER_NAME("ALL"), NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX |
- NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("ALL"), NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
{ CIPHER_NAME("NONE"), 0 }
};
diff --git a/security/nss/lib/pk11wrap/pk11pbe.c b/security/nss/lib/pk11wrap/pk11pbe.c
index bea9333f6..5f68f399e 100644
--- a/security/nss/lib/pk11wrap/pk11pbe.c
+++ b/security/nss/lib/pk11wrap/pk11pbe.c
@@ -367,7 +367,24 @@ sec_pkcs5v2_key_length(SECAlgorithmID *algid, SECAlgorithmID *cipherAlgId)
cipherAlg = SECOID_GetAlgorithmTag(cipherAlgId);
if (sec_pkcs5_is_algorithm_v2_aes_algorithm(cipherAlg)) {
- length = sec_pkcs5v2_aes_key_length(cipherAlg);
+ /* Previously, the PKCS#12 files created with the old NSS
+ * releases encoded the maximum key size of AES (that is 32)
+ * in the keyLength field of PBKDF2-params. That resulted in
+ * always performing AES-256 even if AES-128-CBC or
+ * AES-192-CBC is specified in the encryptionScheme field of
+ * PBES2-params. This is wrong, but for compatibility reasons,
+ * check the keyLength field and use the value if it is 32.
+ */
+ if (p5_param.keyLength.data != NULL) {
+ length = DER_GetInteger(&p5_param.keyLength);
+ }
+ /* If the keyLength field is present and contains a value
+ * other than 32, that means the file is created outside of
+ * NSS, which we don't care about. Note that the following
+ * also handles the case when the field is absent. */
+ if (length != 32) {
+ length = sec_pkcs5v2_aes_key_length(cipherAlg);
+ }
} else if (p5_param.keyLength.data != NULL) {
length = DER_GetInteger(&p5_param.keyLength);
} else {
diff --git a/security/nss/lib/pk11wrap/pk11pk12.c b/security/nss/lib/pk11wrap/pk11pk12.c
index d753b87e5..035143af8 100644
--- a/security/nss/lib/pk11wrap/pk11pk12.c
+++ b/security/nss/lib/pk11wrap/pk11pk12.c
@@ -153,7 +153,6 @@ const SEC_ASN1Template SECKEY_DHPrivateKeyExportTemplate[] = {
{ SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.prime) },
};
-#ifndef NSS_DISABLE_ECC
SEC_ASN1_MKSUB(SEC_BitStringTemplate)
SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
@@ -178,7 +177,6 @@ const SEC_ASN1Template SECKEY_ECPrivateKeyExportTemplate[] = {
SEC_ASN1_SUB(SEC_BitStringTemplate) },
{ 0 }
};
-#endif /* NSS_DISABLE_ECC */
const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
@@ -346,16 +344,13 @@ PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk,
switch (lpk->keyType) {
case rsaKey:
keyType = CKK_RSA;
- PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ? &cktrue
- : &ckfalse,
+ PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
- PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ? &cktrue
- : &ckfalse,
+ PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
- PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
- : &ckfalse,
+ PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
@@ -482,7 +477,6 @@ PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk,
lpk->u.dh.privateValue.len);
attrs++;
break;
-#ifndef NSS_DISABLE_ECC
case ecKey:
keyType = CKK_EC;
if (lpk->u.ec.publicValue.len == 0) {
@@ -494,8 +488,7 @@ PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk,
lpk->u.ec.publicValue.len);
attrs++;
}
- PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
- : &ckfalse,
+ PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
@@ -503,8 +496,7 @@ PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk,
: &ckfalse,
sizeof(CK_BBOOL));
attrs++;
- PK11_SETATTRS(attrs, CKA_DERIVE, (keyUsage & KU_KEY_AGREEMENT) ? &cktrue
- : &ckfalse,
+ PK11_SETATTRS(attrs, CKA_DERIVE, (keyUsage & KU_KEY_AGREEMENT) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
ck_id = PK11_MakeIDFromPubKey(&lpk->u.ec.publicValue);
@@ -525,7 +517,6 @@ PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk,
lpk->u.ec.publicValue.len);
attrs++;
break;
-#endif /* NSS_DISABLE_ECC */
default:
PORT_SetError(SEC_ERROR_BAD_KEY);
goto loser;
@@ -606,7 +597,6 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
paramDest = NULL;
lpk->keyType = dhKey;
break;
-#ifndef NSS_DISABLE_ECC
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
prepare_ec_priv_key_export_for_asn1(lpk);
keyTemplate = SECKEY_ECPrivateKeyExportTemplate;
@@ -614,7 +604,6 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
paramDest = NULL;
lpk->keyType = ecKey;
break;
-#endif /* NSS_DISABLE_ECC */
default:
keyTemplate = NULL;
@@ -633,7 +622,6 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
goto loser;
}
-#ifndef NSS_DISABLE_ECC
if (lpk->keyType == ecKey) {
/* Convert length in bits to length in bytes. */
lpk->u.ec.publicValue.len >>= 3;
@@ -645,7 +633,6 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
goto loser;
}
}
-#endif /* NSS_DISABLE_ECC */
if (paramDest && paramTemplate) {
rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate,
diff --git a/security/nss/lib/pk11wrap/pk11pub.h b/security/nss/lib/pk11wrap/pk11pub.h
index edfe82f5a..dbd8da092 100644
--- a/security/nss/lib/pk11wrap/pk11pub.h
+++ b/security/nss/lib/pk11wrap/pk11pub.h
@@ -831,6 +831,10 @@ SECStatus PK11_LinkGenericObject(PK11GenericObject *list,
PK11GenericObject *object);
SECStatus PK11_DestroyGenericObjects(PK11GenericObject *object);
SECStatus PK11_DestroyGenericObject(PK11GenericObject *object);
+PK11GenericObject *PK11_CreateManagedGenericObject(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token);
+/* deprecated */
PK11GenericObject *PK11_CreateGenericObject(PK11SlotInfo *slot,
const CK_ATTRIBUTE *pTemplate,
int count, PRBool token);
diff --git a/security/nss/lib/pk11wrap/pk11skey.c b/security/nss/lib/pk11wrap/pk11skey.c
index 1ef53e1d7..cf2a40a2f 100644
--- a/security/nss/lib/pk11wrap/pk11skey.c
+++ b/security/nss/lib/pk11wrap/pk11skey.c
@@ -182,6 +182,10 @@ PK11_FreeSymKey(PK11SymKey *symKey)
PK11SlotInfo *slot;
PRBool freeit = PR_TRUE;
+ if (!symKey) {
+ return;
+ }
+
if (PR_ATOMIC_DECREMENT(&symKey->refCount) == 0) {
PK11SymKey *parent = symKey->parent;
diff --git a/security/nss/lib/pk11wrap/pk11slot.c b/security/nss/lib/pk11wrap/pk11slot.c
index 0a6ed6c08..c39abe17e 100644
--- a/security/nss/lib/pk11wrap/pk11slot.c
+++ b/security/nss/lib/pk11wrap/pk11slot.c
@@ -1182,7 +1182,7 @@ PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts)
/* set the slot flags to the current token values */
slot->series++; /* allow other objects to detect that the
- * slot is different */
+ * slot is different */
slot->flags = slot->tokenInfo.flags;
slot->needLogin = ((slot->tokenInfo.flags & CKF_LOGIN_REQUIRED) ? PR_TRUE : PR_FALSE);
slot->readOnly = ((slot->tokenInfo.flags & CKF_WRITE_PROTECTED) ? PR_TRUE : PR_FALSE);
@@ -1471,6 +1471,9 @@ PK11_InitSlot(SECMODModule *mod, CK_SLOT_ID slotID, PK11SlotInfo *slot)
slot->hasRootCerts = PR_TRUE;
}
}
+ if ((slotInfo.flags & CKF_USER_PIN_INITIALIZED) != 0) {
+ slot->flags |= CKF_USER_PIN_INITIALIZED;
+ }
}
/*********************************************************************
diff --git a/security/nss/lib/pk11wrap/pk11util.c b/security/nss/lib/pk11wrap/pk11util.c
index a962e9bb3..e316f1f1a 100644
--- a/security/nss/lib/pk11wrap/pk11util.c
+++ b/security/nss/lib/pk11wrap/pk11util.c
@@ -437,6 +437,11 @@ SECMOD_DeleteInternalModule(const char *name)
return rv;
}
+#ifdef NSS_FIPS_DISABLED
+ PORT_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR);
+ return rv;
+#endif
+
SECMOD_GetWriteLock(moduleLock);
for (mlpp = &modules, mlp = modules;
mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
@@ -955,7 +960,11 @@ SECMOD_DestroyModuleList(SECMODModuleList *list)
PRBool
SECMOD_CanDeleteInternalModule(void)
{
+#ifdef NSS_FIPS_DISABLED
+ return PR_FALSE;
+#else
return (PRBool)(pendingModule == NULL);
+#endif
}
/*
diff --git a/security/nss/lib/pk11wrap/secmodti.h b/security/nss/lib/pk11wrap/secmodti.h
index 63c207929..260e6387d 100644
--- a/security/nss/lib/pk11wrap/secmodti.h
+++ b/security/nss/lib/pk11wrap/secmodti.h
@@ -175,6 +175,7 @@ struct PK11GenericObjectStr {
PK11GenericObject *next;
PK11SlotInfo *slot;
CK_OBJECT_HANDLE objectID;
+ PRBool owner;
};
#define MAX_TEMPL_ATTRS 16 /* maximum attributes in template */
diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c
index 57333ac37..dfe7015df 100644
--- a/security/nss/lib/pkcs12/p12d.c
+++ b/security/nss/lib/pkcs12/p12d.c
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nssrenam.h"
+#include "nss.h"
#include "p12t.h"
#include "p12.h"
#include "plarena.h"
@@ -126,6 +127,7 @@ struct SEC_PKCS12DecoderContextStr {
SECKEYGetPasswordKey pwfn;
void *pwfnarg;
PRBool swapUnicodeBytes;
+ PRBool forceUnicode;
/* import information */
PRBool bagsVerified;
@@ -192,8 +194,18 @@ sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid)
}
algorithm = SECOID_GetAlgorithmTag(algid);
- if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm, p12dcx->pwitem))
- return NULL;
+
+ if (p12dcx->forceUnicode) {
+ if (SECITEM_CopyItem(NULL, &pwitem, p12dcx->pwitem) != SECSuccess) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ } else {
+ if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm, p12dcx->pwitem)) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ }
bulkKey = PK11_PBEKeyGen(slot, algid, &pwitem, PR_FALSE, p12dcx->wincx);
/* some tokens can't generate PBE keys on their own, generate the
@@ -1164,6 +1176,8 @@ SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx,
{
SEC_PKCS12DecoderContext *p12dcx;
PLArenaPool *arena;
+ PRInt32 forceUnicode = PR_FALSE;
+ SECStatus rv;
arena = PORT_NewArena(2048); /* different size? */
if (!arena) {
@@ -1196,6 +1210,11 @@ SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx,
#else
p12dcx->swapUnicodeBytes = PR_FALSE;
#endif
+ rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ p12dcx->forceUnicode = forceUnicode;
p12dcx->errorValue = 0;
p12dcx->error = PR_FALSE;
@@ -2428,7 +2447,7 @@ sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey, KeyType *type);
static SECStatus
sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECKEYPublicKey *pubKey,
unsigned int keyUsage,
- SECItem *nickName, void *wincx)
+ SECItem *nickName, PRBool forceUnicode, void *wincx)
{
SECStatus rv;
SECItem *publicValue = NULL;
@@ -2466,9 +2485,21 @@ sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECKEYPublicKey *pubKey,
&key->safeBagContent.pkcs8ShroudedKeyBag->algorithm;
SECOidTag algorithm = SECOID_GetAlgorithmTag(algid);
- if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm,
- key->pwitem))
- return SECFailure;
+ if (forceUnicode) {
+ if (SECITEM_CopyItem(NULL, &pwitem, key->pwitem) != SECSuccess) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ return SECFailure;
+ }
+ } else {
+ if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm,
+ key->pwitem)) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ return SECFailure;
+ }
+ }
+
rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot,
key->safeBagContent.pkcs8ShroudedKeyBag,
&pwitem, nickName, publicValue,
@@ -2923,7 +2954,8 @@ sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey,
* two passes in sec_pkcs12_validate_bags.
*/
static SECStatus
-sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, void *wincx)
+sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, PRBool forceUnicode,
+ void *wincx)
{
sec_PKCS12SafeBag **keyList;
int i;
@@ -2976,7 +3008,8 @@ sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, void *wincx)
key->problem = PR_TRUE;
rv = SECFailure;
} else {
- rv = sec_pkcs12_add_key(key, pubKey, keyUsage, nickName, wincx);
+ rv = sec_pkcs12_add_key(key, pubKey, keyUsage, nickName,
+ forceUnicode, wincx);
}
if (pubKey) {
SECKEY_DestroyPublicKey(pubKey);
@@ -3053,6 +3086,9 @@ sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, void *wincx)
SECStatus
SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx)
{
+ PRBool forceUnicode = PR_FALSE;
+ SECStatus rv;
+
if (!p12dcx || p12dcx->error) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
@@ -3062,7 +3098,16 @@ SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx)
return SECFailure;
}
- return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx);
+ /* We need to check the option here as well as in
+ * SEC_PKCS12DecoderStart, because different PBE's could be used
+ * for PKCS #7 and PKCS #8 */
+ rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return sec_pkcs12_install_bags(p12dcx->safeBags, forceUnicode,
+ p12dcx->wincx);
}
PRBool
diff --git a/security/nss/lib/pkcs12/p12local.c b/security/nss/lib/pkcs12/p12local.c
index a94c08be1..53e3aa6bb 100644
--- a/security/nss/lib/pkcs12/p12local.c
+++ b/security/nss/lib/pkcs12/p12local.c
@@ -267,8 +267,7 @@ sec_pkcs12_generate_key_from_password(SECOidTag algorithm,
return NULL;
}
- pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) *
- (salt->len + password->len));
+ pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) * (salt->len + password->len));
if (pre_hash == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
diff --git a/security/nss/lib/pkcs7/p7create.c b/security/nss/lib/pkcs7/p7create.c
index 96ada5c0f..a79d5aa26 100644
--- a/security/nss/lib/pkcs7/p7create.c
+++ b/security/nss/lib/pkcs7/p7create.c
@@ -18,7 +18,13 @@
#include "secder.h"
#include "secpkcs5.h"
-const int NSS_PBE_DEFAULT_ITERATION_COUNT = 2000; /* used in p12e.c too */
+const int NSS_PBE_DEFAULT_ITERATION_COUNT = /* used in p12e.c too */
+#ifdef DEBUG
+ 10000
+#else
+ 1000000
+#endif
+ ;
static SECStatus
sec_pkcs7_init_content_info(SEC_PKCS7ContentInfo *cinfo, PLArenaPool *poolp,
diff --git a/security/nss/lib/pki/pki3hack.c b/security/nss/lib/pki/pki3hack.c
index 548853970..fb3110a23 100644
--- a/security/nss/lib/pki/pki3hack.c
+++ b/security/nss/lib/pki/pki3hack.c
@@ -180,16 +180,18 @@ STAN_RemoveModuleFromDefaultTrustDomain(
NSSTrustDomain *td;
int i;
td = STAN_GetDefaultTrustDomain();
- NSSRWLock_LockWrite(td->tokensLock);
for (i = 0; i < module->slotCount; i++) {
token = PK11Slot_GetNSSToken(module->slots[i]);
if (token) {
nssToken_NotifyCertsNotVisible(token);
+ NSSRWLock_LockWrite(td->tokensLock);
nssList_Remove(td->tokenList, token);
+ NSSRWLock_UnlockWrite(td->tokensLock);
PK11Slot_SetNSSToken(module->slots[i], NULL);
nssToken_Destroy(token);
}
}
+ NSSRWLock_LockWrite(td->tokensLock);
nssListIterator_Destroy(td->tokens);
td->tokens = nssList_CreateIterator(td->tokenList);
NSSRWLock_UnlockWrite(td->tokensLock);
diff --git a/security/nss/lib/smime/cmsdecode.c b/security/nss/lib/smime/cmsdecode.c
index d96511171..62b4ebfe5 100644
--- a/security/nss/lib/smime/cmsdecode.c
+++ b/security/nss/lib/smime/cmsdecode.c
@@ -87,8 +87,7 @@ nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
/* XXX error handling: need to set p7dcx->error */
#ifdef CMSDEBUG
- fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before"
- : "after",
+ fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after",
dest, depth);
#endif
diff --git a/security/nss/lib/smime/cmsencode.c b/security/nss/lib/smime/cmsencode.c
index a4414e008..0d723e865 100644
--- a/security/nss/lib/smime/cmsencode.c
+++ b/security/nss/lib/smime/cmsencode.c
@@ -134,8 +134,7 @@ nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
rootcinfo = &(p7ecx->cmsg->contentInfo);
#ifdef CMSDEBUG
- fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before"
- : "after",
+ fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after",
dest, depth);
#endif
diff --git a/security/nss/lib/softoken/fipstest.c b/security/nss/lib/softoken/fipstest.c
index 3563bd2d2..0cca74d6e 100644
--- a/security/nss/lib/softoken/fipstest.c
+++ b/security/nss/lib/softoken/fipstest.c
@@ -5,6 +5,7 @@
* License, v. 2.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 NSS_FIPS_DISABLED
#include "seccomon.h"
#include "blapi.h"
#include "softoken.h"
@@ -652,3 +653,11 @@ sftk_FIPSEntryOK()
}
return CKR_OK;
}
+#else
+#include "pkcs11t.h"
+CK_RV
+sftk_FIPSEntryOK()
+{
+ return CKR_DEVICE_ERROR;
+}
+#endif /* NSS_FIPS_DISABLED */
diff --git a/security/nss/lib/softoken/fipstokn.c b/security/nss/lib/softoken/fipstokn.c
index fd4fd4207..ca7d7998a 100644
--- a/security/nss/lib/softoken/fipstokn.c
+++ b/security/nss/lib/softoken/fipstokn.c
@@ -540,7 +540,10 @@ FC_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
crv = NSC_GetTokenInfo(slotID, pInfo);
if (crv == CKR_OK) {
- if ((pInfo->flags & CKF_LOGIN_REQUIRED) == 0) {
+ /* use the global database to figure out if we are running in
+ * FIPS 140 Level 1 or Level 2 */
+ if (slotID == FIPS_SLOT_ID &&
+ (pInfo->flags & CKF_LOGIN_REQUIRED) == 0) {
isLevel2 = PR_FALSE;
}
}
@@ -616,7 +619,8 @@ FC_InitPIN(CK_SESSION_HANDLE hSession,
* we need to make sure the pin meets FIPS requirements */
if ((ulPinLen == 0) || ((rv = sftk_newPinCheck(pPin, ulPinLen)) == CKR_OK)) {
rv = NSC_InitPIN(hSession, pPin, ulPinLen);
- if (rv == CKR_OK) {
+ if ((rv == CKR_OK) &&
+ (sftk_SlotIDFromSessionHandle(hSession) == FIPS_SLOT_ID)) {
isLevel2 = (ulPinLen > 0) ? PR_TRUE : PR_FALSE;
}
}
@@ -644,7 +648,8 @@ FC_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin,
if ((rv = sftk_fipsCheck()) == CKR_OK &&
(rv = sftk_newPinCheck(pNewPin, usNewLen)) == CKR_OK) {
rv = NSC_SetPIN(hSession, pOldPin, usOldLen, pNewPin, usNewLen);
- if (rv == CKR_OK) {
+ if ((rv == CKR_OK) &&
+ (sftk_SlotIDFromSessionHandle(hSession) == FIPS_SLOT_ID)) {
/* if we set the password in level1 we now go
* to level2. NOTE: we don't allow the user to
* go from level2 to level1 */
@@ -705,11 +710,23 @@ FC_GetSessionInfo(CK_SESSION_HANDLE hSession,
rv = NSC_GetSessionInfo(hSession, pInfo);
if (rv == CKR_OK) {
- if ((isLoggedIn) && (pInfo->state == CKS_RO_PUBLIC_SESSION)) {
- pInfo->state = CKS_RO_USER_FUNCTIONS;
- }
- if ((isLoggedIn) && (pInfo->state == CKS_RW_PUBLIC_SESSION)) {
- pInfo->state = CKS_RW_USER_FUNCTIONS;
+ /* handle the case where the auxilary slot doesn't require login.
+ * piggy back on the main token's login state */
+ if (isLoggedIn &&
+ ((pInfo->state == CKS_RO_PUBLIC_SESSION) ||
+ (pInfo->state == CKS_RW_PUBLIC_SESSION))) {
+ CK_RV crv;
+ CK_TOKEN_INFO tInfo;
+ crv = NSC_GetTokenInfo(sftk_SlotIDFromSessionHandle(hSession),
+ &tInfo);
+ /* if the token doesn't login, use our global login state */
+ if ((crv == CKR_OK) && ((tInfo.flags & CKF_LOGIN_REQUIRED) == 0)) {
+ if (pInfo->state == CKS_RO_PUBLIC_SESSION) {
+ pInfo->state = CKS_RO_USER_FUNCTIONS;
+ } else {
+ pInfo->state = CKS_RW_USER_FUNCTIONS;
+ }
+ }
}
}
return rv;
diff --git a/security/nss/lib/softoken/legacydb/keydb.c b/security/nss/lib/softoken/legacydb/keydb.c
index 178e333ec..b4aa7754b 100644
--- a/security/nss/lib/softoken/legacydb/keydb.c
+++ b/security/nss/lib/softoken/legacydb/keydb.c
@@ -1137,12 +1137,10 @@ nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle, NSSLOWCERTCertificate *cer
namekey.data = pubkey->u.dh.publicValue.data;
namekey.size = pubkey->u.dh.publicValue.len;
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
namekey.data = pubkey->u.ec.publicValue.data;
namekey.size = pubkey->u.ec.publicValue.len;
break;
-#endif /* NSS_DISABLE_ECC */
default:
/* XXX We don't do Fortezza or DH yet. */
return PR_FALSE;
@@ -1467,12 +1465,10 @@ seckey_encrypt_private_key(PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk,
SECItem *der_item = NULL;
SECItem *cipherText = NULL;
SECItem *dummy = NULL;
-#ifndef NSS_DISABLE_ECC
#ifdef EC_DEBUG
SECItem *fordebug = NULL;
#endif
int savelen;
-#endif
temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
if (temparena == NULL)
@@ -1548,7 +1544,6 @@ seckey_encrypt_private_key(PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk,
goto loser;
}
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
lg_prepare_low_ec_priv_key_for_asn1(pk);
/* Public value is encoded as a bit string so adjust length
@@ -1589,7 +1584,6 @@ seckey_encrypt_private_key(PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk,
#endif
break;
-#endif /* NSS_DISABLE_ECC */
default:
/* We don't support DH or Fortezza private keys yet */
PORT_Assert(PR_FALSE);
@@ -1809,7 +1803,6 @@ seckey_decrypt_private_key(SECItem *epki,
lg_nsslowkey_DHPrivateKeyTemplate,
&newPrivateKey);
break;
-#ifndef NSS_DISABLE_ECC
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
pk->keyType = NSSLOWKEYECKey;
lg_prepare_low_ec_priv_key_for_asn1(pk);
@@ -1849,7 +1842,6 @@ seckey_decrypt_private_key(SECItem *epki,
}
break;
-#endif /* NSS_DISABLE_ECC */
default:
rv = SECFailure;
break;
diff --git a/security/nss/lib/softoken/legacydb/lgattr.c b/security/nss/lib/softoken/legacydb/lgattr.c
index 5c2cbdbc6..542b0c968 100644
--- a/security/nss/lib/softoken/legacydb/lgattr.c
+++ b/security/nss/lib/softoken/legacydb/lgattr.c
@@ -133,7 +133,7 @@ lg_CopyAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE_TYPE type,
attr->ulValueLen = (CK_ULONG)-1;
return CKR_BUFFER_TOO_SMALL;
}
- if (value != NULL) {
+ if (len > 0 && value != NULL) {
PORT_Memcpy(attr->pValue, value, len);
}
attr->ulValueLen = len;
@@ -421,11 +421,9 @@ lg_GetPubItem(NSSLOWKEYPublicKey *pubKey)
case NSSLOWKEYDHKey:
pubItem = &pubKey->u.dh.publicValue;
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
pubItem = &pubKey->u.ec.publicValue;
break;
-#endif /* NSS_DISABLE_ECC */
default:
break;
}
@@ -544,7 +542,6 @@ lg_FindDHPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
return lg_invalidAttribute(attribute);
}
-#ifndef NSS_DISABLE_ECC
static CK_RV
lg_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
CK_ATTRIBUTE *attribute)
@@ -594,7 +591,6 @@ lg_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
}
return lg_invalidAttribute(attribute);
}
-#endif /* NSS_DISABLE_ECC */
static CK_RV
lg_FindPublicKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
@@ -645,10 +641,8 @@ lg_FindPublicKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
return lg_FindDSAPublicKeyAttribute(key, type, attribute);
case NSSLOWKEYDHKey:
return lg_FindDHPublicKeyAttribute(key, type, attribute);
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
return lg_FindECPublicKeyAttribute(key, type, attribute);
-#endif /* NSS_DISABLE_ECC */
default:
break;
}
@@ -935,7 +929,6 @@ lg_FindDHPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
return lg_invalidAttribute(attribute);
}
-#ifndef NSS_DISABLE_ECC
static CK_RV
lg_FindECPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
CK_ATTRIBUTE *attribute, SDB *sdbpw)
@@ -973,7 +966,6 @@ lg_FindECPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
}
return lg_invalidAttribute(attribute);
}
-#endif /* NSS_DISABLE_ECC */
static CK_RV
lg_FindPrivateKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
@@ -1020,10 +1012,8 @@ lg_FindPrivateKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
return lg_FindDSAPrivateKeyAttribute(key, type, attribute, obj->sdb);
case NSSLOWKEYDHKey:
return lg_FindDHPrivateKeyAttribute(key, type, attribute, obj->sdb);
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
return lg_FindECPrivateKeyAttribute(key, type, attribute, obj->sdb);
-#endif /* NSS_DISABLE_ECC */
default:
break;
}
diff --git a/security/nss/lib/softoken/legacydb/lgcreate.c b/security/nss/lib/softoken/legacydb/lgcreate.c
index a0d2b2e57..f2b2aa634 100644
--- a/security/nss/lib/softoken/legacydb/lgcreate.c
+++ b/security/nss/lib/softoken/legacydb/lgcreate.c
@@ -398,21 +398,17 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
NSSLOWKEYPrivateKey *priv;
SECItem pubKeySpace = { siBuffer, NULL, 0 };
SECItem *pubKey;
-#ifndef NSS_DISABLE_ECC
SECItem pubKey2Space = { siBuffer, NULL, 0 };
PLArenaPool *arena = NULL;
-#endif /* NSS_DISABLE_ECC */
NSSLOWKEYDBHandle *keyHandle = NULL;
switch (key_type) {
case CKK_RSA:
pubKeyAttr = CKA_MODULUS;
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
pubKeyAttr = CKA_EC_POINT;
break;
-#endif /* NSS_DISABLE_ECC */
case CKK_DSA:
case CKK_DH:
break;
@@ -425,7 +421,6 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
if (crv != CKR_OK)
return crv;
-#ifndef NSS_DISABLE_ECC
if (key_type == CKK_EC) {
SECStatus rv;
/*
@@ -448,7 +443,6 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
pubKey = &pubKey2Space;
}
}
-#endif /* NSS_DISABLE_ECC */
PORT_Assert(pubKey->data);
if (pubKey->data == NULL) {
@@ -469,14 +463,12 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
/* make sure the associated private key already exists */
/* only works if we are logged in */
priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey, sdb /*password*/);
-#ifndef NSS_DISABLE_ECC
if (priv == NULL && pubKey == &pubKey2Space) {
/* no match on the decoded key, match the original pubkey */
pubKey = &pubKeySpace;
priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey,
sdb /*password*/);
}
-#endif
if (priv == NULL) {
/* the legacy database can only 'store' public keys which already
* have their corresponding private keys in the database */
@@ -490,10 +482,9 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
done:
PORT_Free(pubKeySpace.data);
-#ifndef NSS_DISABLE_ECC
- if (arena)
+ if (arena) {
PORT_FreeArena(arena, PR_FALSE);
-#endif
+ }
return crv;
}
@@ -613,7 +604,6 @@ lg_mkPrivKey(SDB *sdb, const CK_ATTRIBUTE *templ, CK_ULONG count,
}
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
privKey->keyType = NSSLOWKEYECKey;
crv = lg_Attribute2SSecItem(arena, CKA_EC_PARAMS, templ, count,
@@ -646,7 +636,6 @@ lg_mkPrivKey(SDB *sdb, const CK_ATTRIBUTE *templ, CK_ULONG count,
if (rv != SECSuccess)
crv = CKR_HOST_MEMORY;
break;
-#endif /* NSS_DISABLE_ECC */
default:
crv = CKR_KEY_TYPE_INCONSISTENT;
diff --git a/security/nss/lib/softoken/legacydb/lgfips.c b/security/nss/lib/softoken/legacydb/lgfips.c
index b017424db..b991dcf8e 100644
--- a/security/nss/lib/softoken/legacydb/lgfips.c
+++ b/security/nss/lib/softoken/legacydb/lgfips.c
@@ -6,6 +6,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* $Id: fipstest.c,v 1.31 2012/06/28 17:55:06 rrelyea%redhat.com Exp $ */
+#ifndef NSS_FIPS_DISABLED
+
#include "seccomon.h"
#include "lgdb.h"
#include "blapi.h"
@@ -113,3 +115,5 @@ lg_FIPSEntryOK()
#endif
return lg_self_tests_success;
}
+
+#endif /* NSS_FIPS_DISABLED */
diff --git a/security/nss/lib/softoken/legacydb/lginit.c b/security/nss/lib/softoken/legacydb/lginit.c
index 6913eea50..4f0b53f52 100644
--- a/security/nss/lib/softoken/legacydb/lginit.c
+++ b/security/nss/lib/softoken/legacydb/lginit.c
@@ -586,11 +586,15 @@ legacy_Open(const char *configdir, const char *certPrefix,
#define NSS_VERSION_VARIABLE __nss_dbm_version
#include "verref.h"
+#ifndef NSS_FIPS_DISABLED
if (flags & SDB_FIPS) {
+ /* We shouldn't get here when FIPS is not enabled on the database. But
+ * we also don't care when this NSS build doesn't support FIPS. */
if (!lg_FIPSEntryOK()) {
return CKR_DEVICE_ERROR;
}
}
+#endif
rv = SECOID_Init();
if (SECSuccess != rv) {
diff --git a/security/nss/lib/softoken/legacydb/lowcert.c b/security/nss/lib/softoken/legacydb/lowcert.c
index 2906120ee..5a349f0aa 100644
--- a/security/nss/lib/softoken/legacydb/lowcert.c
+++ b/security/nss/lib/softoken/legacydb/lowcert.c
@@ -823,7 +823,6 @@ nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *cert)
if (rv == SECSuccess)
return pubk;
break;
-#ifndef NSS_DISABLE_ECC
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
pubk->keyType = NSSLOWKEYECKey;
/* Since PKCS#11 directly takes the DER encoding of EC params
@@ -845,7 +844,6 @@ nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *cert)
if (rv == SECSuccess)
return pubk;
break;
-#endif /* NSS_DISABLE_ECC */
default:
rv = SECFailure;
break;
diff --git a/security/nss/lib/softoken/legacydb/lowkey.c b/security/nss/lib/softoken/legacydb/lowkey.c
index 7de4197a1..a9b7cce3d 100644
--- a/security/nss/lib/softoken/legacydb/lowkey.c
+++ b/security/nss/lib/softoken/legacydb/lowkey.c
@@ -99,8 +99,6 @@ const SEC_ASN1Template lg_nsslowkey_DHPrivateKeyTemplate[] = {
{ 0 }
};
-#ifndef NSS_DISABLE_ECC
-
/* NOTE: The SECG specification allows the private key structure
* to contain curve parameters but recommends that they be stored
* in the PrivateKeyAlgorithmIdentifier field of the PrivateKeyInfo
@@ -193,7 +191,6 @@ LGEC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
loser:
return SECFailure;
}
-#endif /* NSS_DISABLE_ECC */
/*
* See bugzilla bug 125359
* Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
@@ -243,7 +240,6 @@ lg_prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
key->u.dh.privateValue.type = siUnsignedInteger;
}
-#ifndef NSS_DISABLE_ECC
void
lg_prepare_low_ecparams_for_asn1(ECParams *params)
{
@@ -260,7 +256,6 @@ lg_prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
key->u.ec.privateValue.type = siUnsignedInteger;
key->u.ec.publicValue.type = siUnsignedInteger;
}
-#endif /* NSS_DISABLE_ECC */
void
lg_nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
@@ -362,7 +357,6 @@ lg_nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
return pubk;
}
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPublicKey));
@@ -383,7 +377,6 @@ lg_nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
return pubk;
}
break;
-#endif /* NSS_DISABLE_ECC */
/* No Fortezza in Low Key implementations (Fortezza keys aren't
* stored in our data base */
default:
diff --git a/security/nss/lib/softoken/legacydb/lowkeyi.h b/security/nss/lib/softoken/legacydb/lowkeyi.h
index 5136b56a5..4a5bcfa91 100644
--- a/security/nss/lib/softoken/legacydb/lowkeyi.h
+++ b/security/nss/lib/softoken/legacydb/lowkeyi.h
@@ -26,10 +26,8 @@ extern void lg_prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
extern void lg_prepare_low_pqg_params_for_asn1(PQGParams *params);
extern void lg_prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
extern void lg_prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
-#ifndef NSS_DISABLE_ECC
extern void lg_prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
extern void lg_prepare_low_ecparams_for_asn1(ECParams *params);
-#endif /* NSS_DISABLE_ECC */
typedef char *(*NSSLOWKEYDBNameFunc)(void *arg, int dbVersion);
@@ -134,7 +132,6 @@ extern char *
nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle,
SECItem *modulus, SDB *sdb);
-#ifndef NSS_DISABLE_ECC
/*
* smaller version of EC_FillParams. In this code, we only need
* oid and DER data.
@@ -145,7 +142,7 @@ SECStatus LGEC_FillParams(PLArenaPool *arena, const SECItem *encodedParams,
/* Copy all of the fields from srcParams into dstParams */
SECStatus LGEC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
const ECParams *srcParams);
-#endif
+
SEC_END_PROTOS
#endif /* _LOWKEYI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/lowkeyti.h b/security/nss/lib/softoken/legacydb/lowkeyti.h
index ef92689e0..2fd5d4e29 100644
--- a/security/nss/lib/softoken/legacydb/lowkeyti.h
+++ b/security/nss/lib/softoken/legacydb/lowkeyti.h
@@ -42,10 +42,8 @@ extern const SEC_ASN1Template lg_nsslowkey_RSAPrivateKeyTemplate2[];
extern const SEC_ASN1Template lg_nsslowkey_DSAPrivateKeyTemplate[];
extern const SEC_ASN1Template lg_nsslowkey_DHPrivateKeyTemplate[];
extern const SEC_ASN1Template lg_nsslowkey_DHPrivateKeyExportTemplate[];
-#ifndef NSS_DISABLE_ECC
#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION 1 /* as per SECG 1 C.4 */
extern const SEC_ASN1Template lg_nsslowkey_ECPrivateKeyTemplate[];
-#endif /* NSS_DISABLE_ECC */
extern const SEC_ASN1Template lg_nsslowkey_PrivateKeyInfoTemplate[];
extern const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[];
diff --git a/security/nss/lib/softoken/legacydb/pcertdb.c b/security/nss/lib/softoken/legacydb/pcertdb.c
index f1444bf04..2e8b650ee 100644
--- a/security/nss/lib/softoken/legacydb/pcertdb.c
+++ b/security/nss/lib/softoken/legacydb/pcertdb.c
@@ -1854,6 +1854,8 @@ DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
entry->subjectName.len],
entry->smimeOptions.len);
+ } else {
+ entry->smimeOptions.data = NULL;
}
if (entry->optionsDate.len) {
entry->optionsDate.data =
@@ -1868,6 +1870,8 @@ DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
entry->subjectName.len +
entry->smimeOptions.len],
entry->optionsDate.len);
+ } else {
+ entry->optionsDate.data = NULL;
}
/* both options and options date must either exist or not exist */
@@ -2014,7 +2018,7 @@ nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
- certDBEntrySMime *entry;
+ certDBEntrySMime *entry = NULL;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
@@ -2031,8 +2035,8 @@ nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
goto loser;
}
- entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
- sizeof(certDBEntrySMime));
+ entry = (certDBEntrySMime *)PORT_ArenaZAlloc(arena,
+ sizeof(certDBEntrySMime));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
diff --git a/security/nss/lib/softoken/lowkey.c b/security/nss/lib/softoken/lowkey.c
index 73b1dc971..295d55f40 100644
--- a/security/nss/lib/softoken/lowkey.c
+++ b/security/nss/lib/softoken/lowkey.c
@@ -8,10 +8,7 @@
#include "base64.h"
#include "secasn1.h"
#include "secerr.h"
-
-#ifndef NSS_DISABLE_ECC
#include "softoken.h"
-#endif
SEC_ASN1_MKSUB(SEC_AnyTemplate)
SEC_ASN1_MKSUB(SEC_BitStringTemplate)
@@ -90,8 +87,6 @@ const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[] = {
{ 0 }
};
-#ifndef NSS_DISABLE_ECC
-
/* NOTE: The SECG specification allows the private key structure
* to contain curve parameters but recommends that they be stored
* in the PrivateKeyAlgorithmIdentifier field of the PrivateKeyInfo
@@ -117,7 +112,6 @@ const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[] = {
SEC_ASN1_SUB(SEC_BitStringTemplate) },
{ 0 }
};
-#endif /* NSS_DISABLE_ECC */
/*
* See bugzilla bug 125359
* Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
@@ -173,7 +167,6 @@ prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
key->u.dh.privateValue.type = siUnsignedInteger;
}
-#ifndef NSS_DISABLE_ECC
void
prepare_low_ecparams_for_asn1(ECParams *params)
{
@@ -190,7 +183,6 @@ prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
key->u.ec.privateValue.type = siUnsignedInteger;
key->u.ec.publicValue.type = siUnsignedInteger;
}
-#endif /* NSS_DISABLE_ECC */
void
nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
@@ -325,7 +317,6 @@ nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
return pubk;
}
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPublicKey));
@@ -346,7 +337,6 @@ nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
return pubk;
}
break;
-#endif /* NSS_DISABLE_ECC */
/* No Fortezza in Low Key implementations (Fortezza keys aren't
* stored in our data base */
default:
@@ -463,7 +453,6 @@ nsslowkey_CopyPrivateKey(NSSLOWKEYPrivateKey *privKey)
if (rv != SECSuccess)
break;
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.version),
&(privKey->u.ec.version));
@@ -484,7 +473,6 @@ nsslowkey_CopyPrivateKey(NSSLOWKEYPrivateKey *privKey)
if (rv != SECSuccess)
break;
break;
-#endif /* NSS_DISABLE_ECC */
default:
rv = SECFailure;
}
diff --git a/security/nss/lib/softoken/lowkeyi.h b/security/nss/lib/softoken/lowkeyi.h
index a5878c2f6..f9ba3a75f 100644
--- a/security/nss/lib/softoken/lowkeyi.h
+++ b/security/nss/lib/softoken/lowkeyi.h
@@ -25,10 +25,8 @@ extern void prepare_low_pqg_params_for_asn1(PQGParams *params);
extern void prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
extern void prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key);
extern void prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
-#ifndef NSS_DISABLE_ECC
extern void prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
extern void prepare_low_ecparams_for_asn1(ECParams *params);
-#endif /* NSS_DISABLE_ECC */
/*
** Destroy a private key object.
diff --git a/security/nss/lib/softoken/lowkeyti.h b/security/nss/lib/softoken/lowkeyti.h
index 2ef16405f..c048b33e7 100644
--- a/security/nss/lib/softoken/lowkeyti.h
+++ b/security/nss/lib/softoken/lowkeyti.h
@@ -20,10 +20,8 @@ extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[];
extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[];
extern const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[];
extern const SEC_ASN1Template nsslowkey_DHPrivateKeyExportTemplate[];
-#ifndef NSS_DISABLE_ECC
#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION 1 /* as per SECG 1 C.4 */
extern const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[];
-#endif /* NSS_DISABLE_ECC */
extern const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[];
extern const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[];
diff --git a/security/nss/lib/softoken/pkcs11.c b/security/nss/lib/softoken/pkcs11.c
index a594fd501..77882a274 100644
--- a/security/nss/lib/softoken/pkcs11.c
+++ b/security/nss/lib/softoken/pkcs11.c
@@ -282,13 +282,11 @@ static const struct mechanismList mechanisms[] = {
/* no diffie hellman yet */
{ CKM_DH_PKCS_KEY_PAIR_GEN, { DH_MIN_P_BITS, DH_MAX_P_BITS, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
{ CKM_DH_PKCS_DERIVE, { DH_MIN_P_BITS, DH_MAX_P_BITS, CKF_DERIVE }, PR_TRUE },
-#ifndef NSS_DISABLE_ECC
/* -------------------- Elliptic Curve Operations --------------------- */
{ CKM_EC_KEY_PAIR_GEN, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_GENERATE_KEY_PAIR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDH1_DERIVE, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_DERIVE | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA_SHA1, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
-#endif /* NSS_DISABLE_ECC */
/* ------------------------- RC2 Operations --------------------------- */
{ CKM_RC2_KEY_GEN, { 1, 128, CKF_GENERATE }, PR_TRUE },
{ CKM_RC2_ECB, { 1, 128, CKF_EN_DE_WR_UN }, PR_TRUE },
@@ -423,11 +421,20 @@ static const struct mechanismList mechanisms[] = {
#endif
/* --------------------- Secret Key Operations ------------------------ */
{ CKM_GENERIC_SECRET_KEY_GEN, { 1, 32, CKF_GENERATE }, PR_TRUE },
- { CKM_CONCATENATE_BASE_AND_KEY, { 1, 32, CKF_GENERATE }, PR_FALSE },
- { CKM_CONCATENATE_BASE_AND_DATA, { 1, 32, CKF_GENERATE }, PR_FALSE },
- { CKM_CONCATENATE_DATA_AND_BASE, { 1, 32, CKF_GENERATE }, PR_FALSE },
- { CKM_XOR_BASE_AND_DATA, { 1, 32, CKF_GENERATE }, PR_FALSE },
+ { CKM_CONCATENATE_BASE_AND_KEY, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_CONCATENATE_BASE_AND_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_CONCATENATE_DATA_AND_BASE, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_XOR_BASE_AND_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_EXTRACT_KEY_FROM_KEY, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_DES3_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_DES3_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_AES_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_AES_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_CAMELLIA_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_CAMELLIA_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_SEED_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+ { CKM_SEED_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
+
/* ---------------------- SSL Key Derivations ------------------------- */
{ CKM_SSL3_PRE_MASTER_KEY_GEN, { 48, 48, CKF_GENERATE }, PR_FALSE },
{ CKM_SSL3_MASTER_KEY_DERIVE, { 48, 48, CKF_DERIVE }, PR_FALSE },
@@ -931,7 +938,6 @@ sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object,
recover = CK_FALSE;
wrap = CK_FALSE;
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
if (!sftk_hasAttribute(object, CKA_EC_PARAMS)) {
return CKR_TEMPLATE_INCOMPLETE;
@@ -945,7 +951,6 @@ sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object,
recover = CK_FALSE;
wrap = CK_FALSE;
break;
-#endif /* NSS_DISABLE_ECC */
default:
return CKR_ATTRIBUTE_VALUE_INVALID;
}
@@ -1114,7 +1119,6 @@ sftk_handlePrivateKeyObject(SFTKSession *session, SFTKObject *object, CK_KEY_TYP
recover = CK_FALSE;
wrap = CK_FALSE;
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
if (!sftk_hasAttribute(object, CKA_EC_PARAMS)) {
return CKR_TEMPLATE_INCOMPLETE;
@@ -1127,7 +1131,6 @@ sftk_handlePrivateKeyObject(SFTKSession *session, SFTKObject *object, CK_KEY_TYP
recover = CK_FALSE;
wrap = CK_FALSE;
break;
-#endif /* NSS_DISABLE_ECC */
case CKK_NSS_JPAKE_ROUND1:
if (!sftk_hasAttribute(object, CKA_PRIME) ||
!sftk_hasAttribute(object, CKA_SUBPRIME) ||
@@ -1778,7 +1781,6 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type,
crv = sftk_Attribute2SSecItem(arena, &pubKey->u.dh.publicValue,
object, CKA_VALUE);
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
pubKey->keyType = NSSLOWKEYECKey;
crv = sftk_Attribute2SSecItem(arena,
@@ -1837,7 +1839,6 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type,
crv = CKR_ATTRIBUTE_VALUE_INVALID;
}
break;
-#endif /* NSS_DISABLE_ECC */
default:
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
@@ -1947,7 +1948,6 @@ sftk_mkPrivKey(SFTKObject *object, CK_KEY_TYPE key_type, CK_RV *crvp)
* if we don't set it explicitly */
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
privKey->keyType = NSSLOWKEYECKey;
crv = sftk_Attribute2SSecItem(arena,
@@ -1992,7 +1992,6 @@ sftk_mkPrivKey(SFTKObject *object, CK_KEY_TYPE key_type, CK_RV *crvp)
#endif
}
break;
-#endif /* NSS_DISABLE_ECC */
default:
crv = CKR_KEY_TYPE_INCONSISTENT;
@@ -2365,17 +2364,22 @@ sftk_SlotFromID(CK_SLOT_ID slotID, PRBool all)
return slot;
}
-SFTKSlot *
-sftk_SlotFromSessionHandle(CK_SESSION_HANDLE handle)
+CK_SLOT_ID
+sftk_SlotIDFromSessionHandle(CK_SESSION_HANDLE handle)
{
CK_ULONG slotIDIndex = (handle >> 24) & 0x7f;
CK_ULONG moduleIndex = (handle >> 31) & 1;
if (slotIDIndex >= nscSlotCount[moduleIndex]) {
- return NULL;
+ return (CK_SLOT_ID)-1;
}
+ return nscSlotList[moduleIndex][slotIDIndex];
+}
- return sftk_SlotFromID(nscSlotList[moduleIndex][slotIDIndex], PR_FALSE);
+SFTKSlot *
+sftk_SlotFromSessionHandle(CK_SESSION_HANDLE handle)
+{
+ return sftk_SlotFromID(sftk_SlotIDFromSessionHandle(handle), PR_FALSE);
}
static CK_RV
@@ -3305,6 +3309,15 @@ NSC_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
}
}
+ /* If there is no key database, this is for example the case when NSS was
+ * initialized with NSS_NoDbInit(), then there won't be any point in
+ * requesting a PIN. Set the CKF_USER_PIN_INITIALIZED bit so that
+ * PK11_NeedUserInit() doesn't indicate that a PIN is needed.
+ */
+ if (slot->keyDB == NULL) {
+ pInfo->flags |= CKF_USER_PIN_INITIALIZED;
+ }
+
/* ok we really should read it out of the keydb file. */
/* pInfo->hardwareVersion.major = NSSLOWKEY_DB_FILE_VERSION; */
pInfo->hardwareVersion.major = SOFTOKEN_VMAJOR;
@@ -3566,7 +3579,6 @@ NSC_InitToken(CK_SLOT_ID slotID, CK_CHAR_PTR pPin,
{
SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
SFTKDBHandle *handle;
- SFTKDBHandle *certHandle;
SECStatus rv;
unsigned int i;
SFTKObject *object;
@@ -3614,19 +3626,16 @@ NSC_InitToken(CK_SLOT_ID slotID, CK_CHAR_PTR pPin,
}
rv = sftkdb_ResetKeyDB(handle);
+ /* clear the password */
+ sftkdb_ClearPassword(handle);
+ /* update slot->needLogin (should be true now since no password is set) */
+ sftk_checkNeedLogin(slot, handle);
sftk_freeDB(handle);
if (rv != SECSuccess) {
return CKR_DEVICE_ERROR;
}
- /* finally mark all the user certs as non-user certs */
- certHandle = sftk_getCertDB(slot);
- if (certHandle == NULL)
- return CKR_OK;
-
- sftk_freeDB(certHandle);
-
- return CKR_OK; /*is this the right function for not implemented*/
+ return CKR_OK;
}
/* NSC_InitPIN initializes the normal user's PIN. */
@@ -3792,7 +3801,10 @@ NSC_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin,
/* Now update our local copy of the pin */
if (rv == SECSuccess) {
+ PZ_Lock(slot->slotLock);
slot->needLogin = (PRBool)(ulNewLen != 0);
+ slot->isLoggedIn = (PRBool)(sftkdb_PWCached(handle) == SECSuccess);
+ PZ_Unlock(slot->slotLock);
/* Reset login flags. */
if (ulNewLen == 0) {
PRBool tokenRemoved = PR_FALSE;
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c
index 0234aa431..d675d7331 100644
--- a/security/nss/lib/softoken/pkcs11c.c
+++ b/security/nss/lib/softoken/pkcs11c.c
@@ -65,7 +65,6 @@ sftk_Null(void *data, PRBool freeit)
return;
}
-#ifndef NSS_DISABLE_ECC
#ifdef EC_DEBUG
#define SEC_PRINT(str1, str2, num, sitem) \
printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
@@ -78,7 +77,6 @@ sftk_Null(void *data, PRBool freeit)
#undef EC_DEBUG
#define SEC_PRINT(a, b, c, d)
#endif
-#endif /* NSS_DISABLE_ECC */
/*
* free routines.... Free local type allocated data, and convert
@@ -124,7 +122,6 @@ sftk_MapCryptError(int error)
return CKR_KEY_SIZE_RANGE; /* the closest error code */
case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM:
return CKR_TEMPLATE_INCONSISTENT;
- /* EC functions set this error if NSS_DISABLE_ECC is defined */
case SEC_ERROR_UNSUPPORTED_KEYALG:
return CKR_MECHANISM_INVALID;
case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE:
@@ -1527,8 +1524,7 @@ NSC_DecryptUpdate(CK_SESSION_HANDLE hSession,
maxout -= padoutlen;
}
/* now save the final block for the next decrypt or the final */
- PORT_Memcpy(context->padBuf, &pEncryptedPart[ulEncryptedPartLen -
- context->blockSize],
+ PORT_Memcpy(context->padBuf, &pEncryptedPart[ulEncryptedPartLen - context->blockSize],
context->blockSize);
context->padDataLength = context->blockSize;
ulEncryptedPartLen -= context->padDataLength;
@@ -2417,7 +2413,6 @@ nsc_DSA_Sign_Stub(void *ctx, void *sigBuf,
return rv;
}
-#ifndef NSS_DISABLE_ECC
static SECStatus
nsc_ECDSAVerifyStub(void *ctx, void *sigBuf, unsigned int sigLen,
void *dataBuf, unsigned int dataLen)
@@ -2452,7 +2447,6 @@ nsc_ECDSASignStub(void *ctx, void *sigBuf,
*sigLen = signature.len;
return rv;
}
-#endif /* NSS_DISABLE_ECC */
/* NSC_SignInit setups up the signing operations. There are three basic
* types of signing:
@@ -2612,7 +2606,6 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
break;
-#ifndef NSS_DISABLE_ECC
case CKM_ECDSA_SHA1:
context->multi = PR_TRUE;
crv = sftk_doSubSHA1(context);
@@ -2635,7 +2628,6 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
context->maxLen = MAX_ECKEY_LEN * 2;
break;
-#endif /* NSS_DISABLE_ECC */
#define INIT_HMAC_MECH(mmm) \
case CKM_##mmm##_HMAC_GENERAL: \
@@ -3303,7 +3295,6 @@ NSC_VerifyInit(CK_SESSION_HANDLE hSession,
context->verify = (SFTKVerify)nsc_DSA_Verify_Stub;
context->destroy = sftk_Null;
break;
-#ifndef NSS_DISABLE_ECC
case CKM_ECDSA_SHA1:
context->multi = PR_TRUE;
crv = sftk_doSubSHA1(context);
@@ -3324,7 +3315,6 @@ NSC_VerifyInit(CK_SESSION_HANDLE hSession,
context->verify = (SFTKVerify)nsc_ECDSAVerifyStub;
context->destroy = sftk_Null;
break;
-#endif /* NSS_DISABLE_ECC */
INIT_HMAC_MECH(MD2)
INIT_HMAC_MECH(MD5)
@@ -4624,12 +4614,10 @@ sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession,
pairwise_digest_length = subPrimeLen;
mech.mechanism = CKM_DSA;
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
signature_length = MAX_ECKEY_LEN * 2;
mech.mechanism = CKM_ECDSA;
break;
-#endif
default:
return CKR_DEVICE_ERROR;
}
@@ -4746,12 +4734,10 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hSession,
/* Diffie Hellman */
DHPrivateKey *dhPriv;
-#ifndef NSS_DISABLE_ECC
/* Elliptic Curve Cryptography */
SECItem ecEncodedParams; /* DER Encoded parameters */
ECPrivateKey *ecPriv;
ECParams *ecParams;
-#endif /* NSS_DISABLE_ECC */
CHECK_FORK();
@@ -5097,7 +5083,6 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hSession,
PORT_FreeArena(dhPriv->arena, PR_TRUE);
break;
-#ifndef NSS_DISABLE_ECC
case CKM_EC_KEY_PAIR_GEN:
sftk_DeleteAttributeType(privateKey, CKA_EC_PARAMS);
sftk_DeleteAttributeType(privateKey, CKA_VALUE);
@@ -5166,7 +5151,6 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hSession,
/* should zeroize, since this function doesn't. */
PORT_FreeArena(ecPriv->ecParams.arena, PR_TRUE);
break;
-#endif /* NSS_DISABLE_ECC */
default:
crv = CKR_MECHANISM_INVALID;
@@ -5296,12 +5280,10 @@ sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp)
void *dummy, *param = NULL;
SECStatus rv = SECSuccess;
SECItem *encodedKey = NULL;
-#ifndef NSS_DISABLE_ECC
#ifdef EC_DEBUG
SECItem *fordebug;
#endif
int savelen;
-#endif
if (!key) {
*crvp = CKR_KEY_HANDLE_INVALID; /* really can't happen */
@@ -5353,7 +5335,6 @@ sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp)
nsslowkey_PQGParamsTemplate);
algorithm = SEC_OID_ANSIX9_DSA_SIGNATURE;
break;
-#ifndef NSS_DISABLE_ECC
case NSSLOWKEYECKey:
prepare_low_ec_priv_key_for_asn1(lk);
/* Public value is encoded as a bit string so adjust length
@@ -5382,7 +5363,6 @@ sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp)
algorithm = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
break;
-#endif /* NSS_DISABLE_ECC */
case NSSLOWKEYDHKey:
default:
dummy = NULL;
@@ -5641,8 +5621,7 @@ sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
prepare_low_dsa_priv_key_export_for_asn1(lpk);
prepare_low_pqg_params_for_asn1(&lpk->u.dsa.params);
break;
-/* case NSSLOWKEYDHKey: */
-#ifndef NSS_DISABLE_ECC
+ /* case NSSLOWKEYDHKey: */
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
keyTemplate = nsslowkey_ECPrivateKeyTemplate;
paramTemplate = NULL;
@@ -5651,7 +5630,6 @@ sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
prepare_low_ec_priv_key_for_asn1(lpk);
prepare_low_ecparams_for_asn1(&lpk->u.ec.ecParams);
break;
-#endif /* NSS_DISABLE_ECC */
default:
keyTemplate = NULL;
paramTemplate = NULL;
@@ -5666,7 +5644,6 @@ sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
/* decode the private key and any algorithm parameters */
rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
-#ifndef NSS_DISABLE_ECC
if (lpk->keyType == NSSLOWKEYECKey) {
/* convert length in bits to length in bytes */
lpk->u.ec.publicValue.len >>= 3;
@@ -5677,7 +5654,6 @@ sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
goto loser;
}
}
-#endif /* NSS_DISABLE_ECC */
if (rv != SECSuccess) {
goto loser;
@@ -5790,8 +5766,7 @@ sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
keyType = CKK_DH;
break;
#endif
-/* what about fortezza??? */
-#ifndef NSS_DISABLE_ECC
+ /* what about fortezza??? */
case NSSLOWKEYECKey:
keyType = CKK_EC;
crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK : CKR_KEY_TYPE_INCONSISTENT;
@@ -5823,7 +5798,6 @@ sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
break;
/* XXX Do we need to decode the EC Params here ?? */
break;
-#endif /* NSS_DISABLE_ECC */
default:
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
@@ -6153,7 +6127,6 @@ sftk_MapKeySize(CK_KEY_TYPE keyType)
return 0;
}
-#ifndef NSS_DISABLE_ECC
/* Inputs:
* key_len: Length of derived key to be generated.
* SharedSecret: a shared secret that is the output of a key agreement primitive.
@@ -6266,7 +6239,43 @@ sftk_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len,
else
return CKR_MECHANISM_INVALID;
}
-#endif /* NSS_DISABLE_ECC */
+
+/*
+ * Handle the derive from a block encryption cipher
+ */
+CK_RV
+sftk_DeriveEncrypt(SFTKCipher encrypt, void *cipherInfo,
+ int blockSize, SFTKObject *key, CK_ULONG keySize,
+ unsigned char *data, CK_ULONG len)
+{
+ /* large enough for a 512-bit key */
+ unsigned char tmpdata[SFTK_MAX_DERIVE_KEY_SIZE];
+ SECStatus rv;
+ unsigned int outLen;
+ CK_RV crv;
+
+ if ((len % blockSize) != 0) {
+ return CKR_MECHANISM_PARAM_INVALID;
+ }
+ if (len > SFTK_MAX_DERIVE_KEY_SIZE) {
+ return CKR_MECHANISM_PARAM_INVALID;
+ }
+ if (keySize && (len < keySize)) {
+ return CKR_MECHANISM_PARAM_INVALID;
+ }
+ if (keySize == 0) {
+ keySize = len;
+ }
+
+ rv = (*encrypt)(cipherInfo, &tmpdata, &outLen, len, data, len);
+ if (rv != SECSuccess) {
+ crv = sftk_MapCryptError(PORT_GetError());
+ return crv;
+ }
+
+ crv = sftk_forceAttribute(key, CKA_VALUE, tmpdata, keySize);
+ return crv;
+}
/*
* SSL Key generation given pre master secret
@@ -6926,6 +6935,172 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession,
break;
}
+ case CKM_DES3_ECB_ENCRYPT_DATA:
+ case CKM_DES3_CBC_ENCRYPT_DATA: {
+ void *cipherInfo;
+ unsigned char des3key[MAX_DES3_KEY_SIZE];
+ CK_DES_CBC_ENCRYPT_DATA_PARAMS *desEncryptPtr;
+ int mode;
+ unsigned char *iv;
+ unsigned char *data;
+ CK_ULONG len;
+
+ if (mechanism == CKM_DES3_ECB_ENCRYPT_DATA) {
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)
+ pMechanism->pParameter;
+ mode = NSS_DES_EDE3;
+ iv = NULL;
+ data = stringPtr->pData;
+ len = stringPtr->ulLen;
+ } else {
+ mode = NSS_DES_EDE3_CBC;
+ desEncryptPtr =
+ (CK_DES_CBC_ENCRYPT_DATA_PARAMS *)
+ pMechanism->pParameter;
+ iv = desEncryptPtr->iv;
+ data = desEncryptPtr->pData;
+ len = desEncryptPtr->length;
+ }
+ if (att->attrib.ulValueLen == 16) {
+ PORT_Memcpy(des3key, att->attrib.pValue, 16);
+ PORT_Memcpy(des3key + 16, des3key, 8);
+ } else if (att->attrib.ulValueLen == 24) {
+ PORT_Memcpy(des3key, att->attrib.pValue, 24);
+ } else {
+ crv = CKR_KEY_SIZE_RANGE;
+ break;
+ }
+ cipherInfo = DES_CreateContext(des3key, iv, mode, PR_TRUE);
+ PORT_Memset(des3key, 0, 24);
+ if (cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ crv = sftk_DeriveEncrypt((SFTKCipher)DES_Encrypt,
+ cipherInfo, 8, key, keySize,
+ data, len);
+ DES_DestroyContext(cipherInfo, PR_TRUE);
+ break;
+ }
+
+ case CKM_AES_ECB_ENCRYPT_DATA:
+ case CKM_AES_CBC_ENCRYPT_DATA: {
+ void *cipherInfo;
+ CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr;
+ int mode;
+ unsigned char *iv;
+ unsigned char *data;
+ CK_ULONG len;
+
+ if (mechanism == CKM_AES_ECB_ENCRYPT_DATA) {
+ mode = NSS_AES;
+ iv = NULL;
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter;
+ data = stringPtr->pData;
+ len = stringPtr->ulLen;
+ } else {
+ aesEncryptPtr =
+ (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)pMechanism->pParameter;
+ mode = NSS_AES_CBC;
+ iv = aesEncryptPtr->iv;
+ data = aesEncryptPtr->pData;
+ len = aesEncryptPtr->length;
+ }
+
+ cipherInfo = AES_CreateContext((unsigned char *)att->attrib.pValue,
+ iv, mode, PR_TRUE,
+ att->attrib.ulValueLen, 16);
+ if (cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ crv = sftk_DeriveEncrypt((SFTKCipher)AES_Encrypt,
+ cipherInfo, 16, key, keySize,
+ data, len);
+ AES_DestroyContext(cipherInfo, PR_TRUE);
+ break;
+ }
+
+ case CKM_CAMELLIA_ECB_ENCRYPT_DATA:
+ case CKM_CAMELLIA_CBC_ENCRYPT_DATA: {
+ void *cipherInfo;
+ CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr;
+ int mode;
+ unsigned char *iv;
+ unsigned char *data;
+ CK_ULONG len;
+
+ if (mechanism == CKM_CAMELLIA_ECB_ENCRYPT_DATA) {
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)
+ pMechanism->pParameter;
+ aesEncryptPtr = NULL;
+ mode = NSS_CAMELLIA;
+ data = stringPtr->pData;
+ len = stringPtr->ulLen;
+ iv = NULL;
+ } else {
+ stringPtr = NULL;
+ aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)
+ pMechanism->pParameter;
+ mode = NSS_CAMELLIA_CBC;
+ iv = aesEncryptPtr->iv;
+ data = aesEncryptPtr->pData;
+ len = aesEncryptPtr->length;
+ }
+
+ cipherInfo = Camellia_CreateContext((unsigned char *)att->attrib.pValue,
+ iv, mode, PR_TRUE,
+ att->attrib.ulValueLen);
+ if (cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ crv = sftk_DeriveEncrypt((SFTKCipher)Camellia_Encrypt,
+ cipherInfo, 16, key, keySize,
+ data, len);
+ Camellia_DestroyContext(cipherInfo, PR_TRUE);
+ break;
+ }
+
+ case CKM_SEED_ECB_ENCRYPT_DATA:
+ case CKM_SEED_CBC_ENCRYPT_DATA: {
+ void *cipherInfo;
+ CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr;
+ int mode;
+ unsigned char *iv;
+ unsigned char *data;
+ CK_ULONG len;
+
+ if (mechanism == CKM_SEED_ECB_ENCRYPT_DATA) {
+ mode = NSS_SEED;
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)
+ pMechanism->pParameter;
+ aesEncryptPtr = NULL;
+ data = stringPtr->pData;
+ len = stringPtr->ulLen;
+ iv = NULL;
+ } else {
+ mode = NSS_SEED_CBC;
+ aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)
+ pMechanism->pParameter;
+ iv = aesEncryptPtr->iv;
+ data = aesEncryptPtr->pData;
+ len = aesEncryptPtr->length;
+ }
+
+ cipherInfo = SEED_CreateContext((unsigned char *)att->attrib.pValue,
+ iv, mode, PR_TRUE);
+ if (cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ crv = sftk_DeriveEncrypt((SFTKCipher)SEED_Encrypt,
+ cipherInfo, 16, key, keySize,
+ data, len);
+ SEED_DestroyContext(cipherInfo, PR_TRUE);
+ break;
+ }
+
case CKM_CONCATENATE_BASE_AND_KEY: {
SFTKObject *newKey;
@@ -7242,7 +7417,6 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession,
break;
}
-#ifndef NSS_DISABLE_ECC
case CKM_ECDH1_DERIVE:
case CKM_ECDH1_COFACTOR_DERIVE: {
SECItem ecScalar, ecPoint;
@@ -7382,7 +7556,6 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession,
}
break;
}
-#endif /* NSS_DISABLE_ECC */
/* See RFC 5869 and CK_NSS_HKDFParams for documentation. */
case CKM_NSS_HKDF_SHA1:
diff --git a/security/nss/lib/softoken/pkcs11i.h b/security/nss/lib/softoken/pkcs11i.h
index c5f21c30a..7e57dc5e5 100644
--- a/security/nss/lib/softoken/pkcs11i.h
+++ b/security/nss/lib/softoken/pkcs11i.h
@@ -667,6 +667,7 @@ extern CK_RV sftk_handleObject(SFTKObject *object, SFTKSession *session);
extern SFTKSlot *sftk_SlotFromID(CK_SLOT_ID slotID, PRBool all);
extern SFTKSlot *sftk_SlotFromSessionHandle(CK_SESSION_HANDLE handle);
+extern CK_SLOT_ID sftk_SlotIDFromSessionHandle(CK_SESSION_HANDLE handle);
extern SFTKSession *sftk_SessionFromHandle(CK_SESSION_HANDLE handle);
extern void sftk_FreeSession(SFTKSession *session);
extern SFTKSession *sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify,
diff --git a/security/nss/lib/softoken/pkcs11u.c b/security/nss/lib/softoken/pkcs11u.c
index c51211b6c..27e411759 100644
--- a/security/nss/lib/softoken/pkcs11u.c
+++ b/security/nss/lib/softoken/pkcs11u.c
@@ -1261,13 +1261,11 @@ static const CK_ATTRIBUTE_TYPE dhPubKeyAttrs[] = {
};
static const CK_ULONG dhPubKeyAttrsCount =
sizeof(dhPubKeyAttrs) / sizeof(dhPubKeyAttrs[0]);
-#ifndef NSS_DISABLE_ECC
static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = {
CKA_EC_PARAMS, CKA_EC_POINT
};
static const CK_ULONG ecPubKeyAttrsCount =
sizeof(ecPubKeyAttrs) / sizeof(ecPubKeyAttrs[0]);
-#endif
static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = {
CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT,
@@ -1294,13 +1292,11 @@ static const CK_ATTRIBUTE_TYPE dhPrivKeyAttrs[] = {
};
static const CK_ULONG dhPrivKeyAttrsCount =
sizeof(dhPrivKeyAttrs) / sizeof(dhPrivKeyAttrs[0]);
-#ifndef NSS_DISABLE_ECC
static const CK_ATTRIBUTE_TYPE ecPrivKeyAttrs[] = {
CKA_EC_PARAMS, CKA_VALUE
};
static const CK_ULONG ecPrivKeyAttrsCount =
sizeof(ecPrivKeyAttrs) / sizeof(ecPrivKeyAttrs[0]);
-#endif
static const CK_ATTRIBUTE_TYPE certAttrs[] = {
CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER
@@ -1405,12 +1401,10 @@ stfk_CopyTokenPrivateKey(SFTKObject *destObject, SFTKTokenObject *src_to)
crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs,
dhPrivKeyAttrsCount);
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs,
ecPrivKeyAttrsCount);
break;
-#endif
default:
crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
* of token keys into our database. */
@@ -1467,12 +1461,10 @@ stfk_CopyTokenPublicKey(SFTKObject *destObject, SFTKTokenObject *src_to)
crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs,
dhPubKeyAttrsCount);
break;
-#ifndef NSS_DISABLE_ECC
case CKK_EC:
crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs,
ecPubKeyAttrsCount);
break;
-#endif
default:
crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
* of token keys into our database. */
diff --git a/security/nss/lib/softoken/sdb.c b/security/nss/lib/softoken/sdb.c
index 8690df34c..96717cb26 100644
--- a/security/nss/lib/softoken/sdb.c
+++ b/security/nss/lib/softoken/sdb.c
@@ -37,6 +37,7 @@
#elif defined(XP_UNIX)
#include <unistd.h>
#endif
+#include "utilpars.h"
#ifdef SQLITE_UNSAFE_THREADS
#include "prlock.h"
@@ -190,6 +191,34 @@ sdb_done(int err, int *count)
return 0;
}
+#if defined(_WIN32)
+/*
+ * NSPR functions and narrow CRT functions do not handle UTF-8 file paths that
+ * sqlite3 expects.
+ */
+
+static int
+sdb_chmod(const char *filename, int pmode)
+{
+ int result;
+
+ if (!filename) {
+ return -1;
+ }
+
+ wchar_t *filenameWide = _NSSUTIL_UTF8ToWide(filename);
+ if (!filenameWide) {
+ return -1;
+ }
+ result = _wchmod(filenameWide, pmode);
+ PORT_Free(filenameWide);
+
+ return result;
+}
+#else
+#define sdb_chmod(filename, pmode) chmod((filename), (pmode))
+#endif
+
/*
* find out where sqlite stores the temp tables. We do this by replicating
* the logic from sqlite.
@@ -1600,7 +1629,7 @@ loser:
return error;
}
-static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;";
+static const char RESET_CMD[] = "DELETE FROM %s;";
CK_RV
sdb_Reset(SDB *sdb)
{
@@ -1621,17 +1650,19 @@ sdb_Reset(SDB *sdb)
goto loser;
}
- /* delete the key table */
- newStr = sqlite3_mprintf(RESET_CMD, sdb_p->table);
- if (newStr == NULL) {
- error = CKR_HOST_MEMORY;
- goto loser;
- }
- sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
- sqlite3_free(newStr);
+ if (tableExists(sqlDB, sdb_p->table)) {
+ /* delete the contents of the key table */
+ newStr = sqlite3_mprintf(RESET_CMD, sdb_p->table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
- if (sqlerr != SQLITE_OK)
- goto loser;
+ if (sqlerr != SQLITE_OK)
+ goto loser;
+ }
/* delete the password entry table */
sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;",
@@ -1737,7 +1768,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
* sqlite3 will always create it.
*/
LOCK_SQLITE();
- create = (PR_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
+ create = (_NSSUTIL_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
if ((flags == SDB_RDONLY) && create) {
error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
goto loser;
@@ -1754,7 +1785,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
*
* NO NSPR call for chmod? :(
*/
- if (create && chmod(dbname, 0600) != 0) {
+ if (create && sdb_chmod(dbname, 0600) != 0) {
error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
goto loser;
}
@@ -1866,30 +1897,29 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
* so we use it for the cache (see sdb_buildCache for how it's done).*/
/*
- * we decide whether or not to use the cache based on the following input.
- *
- * NSS_SDB_USE_CACHE environment variable is non-existant or set to
- * anything other than "no" or "yes" ("auto", for instance).
- * This is the normal case. NSS will measure the performance of access
- * to the temp database versus the access to the users passed in
- * database location. If the temp database location is "significantly"
- * faster we will use the cache.
- *
- * NSS_SDB_USE_CACHE environment variable is set to "no": cache will not
- * be used.
- *
- * NSS_SDB_USE_CACHE environment variable is set to "yes": cache will
- * always be used.
- *
- * It is expected that most applications would use the "auto" selection,
- * the environment variable is primarily to simplify testing, and to
- * correct potential corner cases where */
+ * we decide whether or not to use the cache based on the following input.
+ *
+ * NSS_SDB_USE_CACHE environment variable is set to anything other than
+ * "yes" or "no" (for instance, "auto"): NSS will measure the performance
+ * of access to the temp database versus the access to the user's
+ * passed-in database location. If the temp database location is
+ * "significantly" faster we will use the cache.
+ *
+ * NSS_SDB_USE_CACHE environment variable is nonexistent or set to "no":
+ * cache will not be used.
+ *
+ * NSS_SDB_USE_CACHE environment variable is set to "yes": cache will
+ * always be used.
+ *
+ * It is expected that most applications will not need this feature, and
+ * thus it is disabled by default.
+ */
env = PR_GetEnvSecure("NSS_SDB_USE_CACHE");
- if (env && PORT_Strcasecmp(env, "no") == 0) {
+ if (!env || PORT_Strcasecmp(env, "no") == 0) {
enableCache = PR_FALSE;
- } else if (env && PORT_Strcasecmp(env, "yes") == 0) {
+ } else if (PORT_Strcasecmp(env, "yes") == 0) {
enableCache = PR_TRUE;
} else {
char *tempDir = NULL;
@@ -2035,10 +2065,11 @@ s_open(const char *directory, const char *certPrefix, const char *keyPrefix,
{
char *env;
env = PR_GetEnvSecure("NSS_SDB_USE_CACHE");
- /* If the environment variable is set to yes or no, sdb_init() will
- * ignore the value of accessOps, and we can skip the measuring.*/
- if (!env || ((PORT_Strcasecmp(env, "no") != 0) &&
- (PORT_Strcasecmp(env, "yes") != 0))) {
+ /* If the environment variable is undefined or set to yes or no,
+ * sdb_init() will ignore the value of accessOps, and we can skip the
+ * measuring.*/
+ if (env && PORT_Strcasecmp(env, "no") != 0 &&
+ PORT_Strcasecmp(env, "yes") != 0) {
accessOps = sdb_measureAccess(directory);
}
}
diff --git a/security/nss/lib/softoken/sdb.h b/security/nss/lib/softoken/sdb.h
index 04b873e02..8ff254bf7 100644
--- a/security/nss/lib/softoken/sdb.h
+++ b/security/nss/lib/softoken/sdb.h
@@ -83,6 +83,10 @@ CK_RV s_open(const char *directory, const char *certPrefix,
int flags, SDB **certdb, SDB **keydb, int *newInit);
CK_RV s_shutdown();
+#if defined(_WIN32)
+wchar_t *sdb_UTF8ToWide(const char *buf);
+#endif
+
/* flags */
#define SDB_RDONLY 1
#define SDB_RDWR 2
diff --git a/security/nss/lib/softoken/sftkdb.c b/security/nss/lib/softoken/sftkdb.c
index 52e516117..2ae084068 100644
--- a/security/nss/lib/softoken/sftkdb.c
+++ b/security/nss/lib/softoken/sftkdb.c
@@ -28,6 +28,9 @@
#include "utilpars.h"
#include "secerr.h"
#include "softoken.h"
+#if defined(_WIN32)
+#include <windows.h>
+#endif
/*
* We want all databases to have the same binary representation independent of
@@ -40,7 +43,7 @@
*/
#define BBP 8
-static PRBool
+PRBool
sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type)
{
switch (type) {
@@ -1370,7 +1373,8 @@ sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object,
}
/* make sure we don't have attributes that conflict with the existing DB */
- crv = sftkdb_checkConflicts(db, object->objclass, template, count, objectID);
+ crv = sftkdb_checkConflicts(db, object->objclass, ntemplate, count,
+ objectID);
if (crv != CKR_OK) {
goto loser;
}
@@ -1386,8 +1390,8 @@ sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object,
goto loser;
}
inTransaction = PR_TRUE;
- crv = sftkdb_setAttributeValue(arena, handle, db,
- objectID, template, count);
+ crv = sftkdb_setAttributeValue(arena, handle, db, objectID, ntemplate,
+ count);
if (crv != CKR_OK) {
goto loser;
}
@@ -2311,6 +2315,13 @@ loser:
crv = (*handle->update->sdb_GetMetaData)(handle->update, "password",
&item1, &item2);
if (crv != CKR_OK) {
+ /* if we get here, neither the source, nor the target has been initialized
+ * with a password entry. Create a metadata table now so that we don't
+ * mistake this for a partially updated database */
+ item1.data[0] = 0;
+ item2.data[0] = 0;
+ item1.len = item2.len = 1;
+ crv = (*handle->db->sdb_PutMetaData)(handle->db, "empty", &item1, &item2);
goto done;
}
crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1,
@@ -2501,6 +2512,53 @@ sftk_oldVersionExists(const char *dir, int version)
return PR_FALSE;
}
+#if defined(_WIN32)
+/*
+ * Convert an sdb path (encoded in UTF-8) to a legacy path (encoded in the
+ * current system codepage). Fails if the path contains a character outside
+ * the current system codepage.
+ */
+static char *
+sftk_legacyPathFromSDBPath(const char *confdir)
+{
+ wchar_t *confdirWide;
+ DWORD size;
+ char *nconfdir;
+ BOOL unmappable;
+
+ if (!confdir) {
+ return NULL;
+ }
+ confdirWide = _NSSUTIL_UTF8ToWide(confdir);
+ if (!confdirWide) {
+ return NULL;
+ }
+
+ size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, confdirWide, -1,
+ NULL, 0, NULL, &unmappable);
+ if (size == 0 || unmappable) {
+ PORT_Free(confdirWide);
+ return NULL;
+ }
+ nconfdir = PORT_Alloc(sizeof(char) * size);
+ if (!nconfdir) {
+ PORT_Free(confdirWide);
+ return NULL;
+ }
+ size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, confdirWide, -1,
+ nconfdir, size, NULL, &unmappable);
+ PORT_Free(confdirWide);
+ if (size == 0 || unmappable) {
+ PORT_Free(nconfdir);
+ return NULL;
+ }
+
+ return nconfdir;
+}
+#else
+#define sftk_legacyPathFromSDBPath(confdir) PORT_Strdup((confdir))
+#endif
+
static PRBool
sftk_hasLegacyDB(const char *confdir, const char *certPrefix,
const char *keyPrefix, int certVersion, int keyVersion)
@@ -2560,6 +2618,7 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
int flags = SDB_RDONLY;
PRBool newInit = PR_FALSE;
PRBool needUpdate = PR_FALSE;
+ char *nconfdir = NULL;
if (!readOnly) {
flags = SDB_CREATE;
@@ -2598,11 +2657,14 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
* the exists.
*/
if (crv != CKR_OK) {
- if (((flags & SDB_RDONLY) == SDB_RDONLY) &&
- sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
+ if ((flags & SDB_RDONLY) == SDB_RDONLY) {
+ nconfdir = sftk_legacyPathFromSDBPath(confdir);
+ }
+ if (nconfdir &&
+ sftk_hasLegacyDB(nconfdir, certPrefix, keyPrefix, 8, 3)) {
/* we have legacy databases, if we failed to open the new format
* DB's read only, just use the legacy ones */
- crv = sftkdbCall_open(confdir, certPrefix,
+ crv = sftkdbCall_open(nconfdir, certPrefix,
keyPrefix, 8, 3, flags,
noCertDB ? NULL : &certSDB, noKeyDB ? NULL : &keySDB);
}
@@ -2631,7 +2693,10 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
/* if the new format DB was also a newly created DB, and we
* succeeded, then need to update that new database with data
* from the existing legacy DB */
- if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
+ nconfdir = sftk_legacyPathFromSDBPath(confdir);
+ if (nconfdir &&
+ sftk_hasLegacyDB(nconfdir, certPrefix, keyPrefix, 8, 3)) {
+ confdir = nconfdir;
needUpdate = PR_TRUE;
}
}
@@ -2704,6 +2769,9 @@ done:
if (appName) {
PORT_Free(appName);
}
+ if (nconfdir) {
+ PORT_Free(nconfdir);
+ }
return forceOpen ? CKR_OK : crv;
}
diff --git a/security/nss/lib/softoken/sftkdbti.h b/security/nss/lib/softoken/sftkdbti.h
index 4942e1b12..7b1db4560 100644
--- a/security/nss/lib/softoken/sftkdbti.h
+++ b/security/nss/lib/softoken/sftkdbti.h
@@ -49,6 +49,7 @@ SECStatus sftkdb_VerifyAttribute(SECItem *passKey,
CK_ATTRIBUTE_TYPE attrType,
SECItem *plainText, SECItem *sigText);
+PRBool sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type);
void sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value);
CK_RV sftkdb_Update(SFTKDBHandle *handle, SECItem *key);
CK_RV sftkdb_PutAttributeSignature(SFTKDBHandle *handle,
diff --git a/security/nss/lib/softoken/sftkpwd.c b/security/nss/lib/softoken/sftkpwd.c
index 0b8c91bfd..e0d2df9ab 100644
--- a/security/nss/lib/softoken/sftkpwd.c
+++ b/security/nss/lib/softoken/sftkpwd.c
@@ -926,6 +926,13 @@ sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle,
continue;
}
+ if (authAttrs[i].ulValueLen == sizeof(CK_ULONG) &&
+ sftkdb_isULONGAttribute(authAttrs[i].type)) {
+ CK_ULONG value = *(CK_ULONG *)authAttrs[i].pValue;
+ sftk_ULong2SDBULong(authAttrs[i].pValue, value);
+ authAttrs[i].ulValueLen = SDB_ULONG_SIZE;
+ }
+
plainText.data = authAttrs[i].pValue;
plainText.len = authAttrs[i].ulValueLen;
rv = sftkdb_SignAttribute(arena, newKey, id,
diff --git a/security/nss/lib/softoken/softkver.h b/security/nss/lib/softoken/softkver.h
index fb2e5bda5..9fd99a8e0 100644
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -8,11 +8,7 @@
#ifndef _SOFTKVER_H_
#define _SOFTKVER_H_
-#ifndef NSS_DISABLE_ECC
#define SOFTOKEN_ECC_STRING " Basic ECC"
-#else
-#define SOFTOKEN_ECC_STRING ""
-#endif
/*
* Softoken's major version, minor version, patch level, build number,
@@ -21,10 +17,10 @@
* The format of the version string should be
* "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
*/
-#define SOFTOKEN_VERSION "3.32.1" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION "3.35" SOFTOKEN_ECC_STRING
#define SOFTOKEN_VMAJOR 3
-#define SOFTOKEN_VMINOR 32
-#define SOFTOKEN_VPATCH 1
+#define SOFTOKEN_VMINOR 35
+#define SOFTOKEN_VPATCH 0
#define SOFTOKEN_VBUILD 0
#define SOFTOKEN_BETA PR_FALSE
diff --git a/security/nss/lib/softoken/softoknt.h b/security/nss/lib/softoken/softoknt.h
index 071689842..03c92361c 100644
--- a/security/nss/lib/softoken/softoknt.h
+++ b/security/nss/lib/softoken/softoknt.h
@@ -9,6 +9,9 @@
#define _SOFTOKNT_H_
#define NSS_SOFTOKEN_DEFAULT_CHUNKSIZE 2048
+#define DES_BLOCK_SIZE 8 /* bytes */
+#define MAX_DES3_KEY_SIZE 24 /* DES_BLOCK_SIZE * 3 */
+#define SFTK_MAX_DERIVE_KEY_SIZE 64
/*
* FIPS 140-2 auditing
diff --git a/security/nss/lib/ssl/SSLerrs.h b/security/nss/lib/ssl/SSLerrs.h
index b73fb6bd0..c95fe661a 100644
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -473,8 +473,7 @@ ER3(SSL_ERROR_RX_MALFORMED_PRE_SHARED_KEY, (SSL_ERROR_BASE + 147),
ER3(SSL_ERROR_RX_MALFORMED_EARLY_DATA, (SSL_ERROR_BASE + 148),
"SSL received an invalid EarlyData extension.")
-ER3(SSL_ERROR_END_OF_EARLY_DATA_ALERT, (SSL_ERROR_BASE + 149),
- "SSL received an unexpected end of early data alert.")
+UNUSED_ERROR(149)
ER3(SSL_ERROR_MISSING_ALPN_EXTENSION, (SSL_ERROR_BASE + 150),
"SSL didn't receive an expected ALPN extension.")
@@ -511,3 +510,33 @@ ER3(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA, (SSL_ERROR_BASE + 160),
ER3(SSL_ERROR_TOO_MUCH_EARLY_DATA, (SSL_ERROR_BASE + 161),
"SSL received more early data than permitted.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA, (SSL_ERROR_BASE + 162),
+ "SSL received an unexpected End of Early Data message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA, (SSL_ERROR_BASE + 163),
+ "SSL received a malformed End of Early Data message.")
+
+ER3(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, (SSL_ERROR_BASE + 164),
+ "An experimental API was called, but not supported.")
+
+ER3(SSL_ERROR_APPLICATION_ABORT, (SSL_ERROR_BASE + 165),
+ "SSL handshake aborted by the application.")
+
+ER3(SSL_ERROR_APP_CALLBACK_ERROR, (SSL_ERROR_BASE + 166),
+ "An application callback produced an invalid response.")
+
+ER3(SSL_ERROR_NO_TIMERS_ERROR, (SSL_ERROR_BASE + 167),
+ "No timers are currently running.")
+
+ER3(SSL_ERROR_MISSING_COOKIE_EXTENSION, (SSL_ERROR_BASE + 168),
+ "A second ClientHello was received without a cookie extension.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, (SSL_ERROR_BASE + 169),
+ "SSL received an unexpected key update message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_KEY_UPDATE, (SSL_ERROR_BASE + 170),
+ "SSL received a malformed key update message.")
+
+ER3(SSL_ERROR_TOO_MANY_KEY_UPDATES, (SSL_ERROR_BASE + 171),
+ "SSL attempted too many key updates.")
diff --git a/security/nss/lib/ssl/authcert.c b/security/nss/lib/ssl/authcert.c
index 88c7c084a..2765c8342 100644
--- a/security/nss/lib/ssl/authcert.c
+++ b/security/nss/lib/ssl/authcert.c
@@ -17,6 +17,7 @@
#include "nss.h"
#include "ssl.h"
#include "pk11func.h" /* for PK11_ function calls */
+#include "sslimpl.h"
/*
* This callback used by SSL to pull client sertificate upon
@@ -63,7 +64,7 @@ NSS_GetClientAuthData(void *arg,
if (!cert)
continue;
/* Only check unexpired certs */
- if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) !=
+ if (CERT_CheckCertValidTimes(cert, ssl_TimeUsec(), PR_TRUE) !=
secCertTimeValid) {
CERT_DestroyCertificate(cert);
continue;
diff --git a/security/nss/lib/ssl/config.mk b/security/nss/lib/ssl/config.mk
index c8b053cab..d13613f78 100644
--- a/security/nss/lib/ssl/config.mk
+++ b/security/nss/lib/ssl/config.mk
@@ -57,11 +57,6 @@ endif
endif
-ifdef NSS_SSL_ENABLE_ZLIB
-DEFINES += -DNSS_SSL_ENABLE_ZLIB
-include $(CORE_DEPTH)/coreconf/zlib.mk
-endif
-
ifdef NSS_DISABLE_TLS_1_3
DEFINES += -DNSS_DISABLE_TLS_1_3
endif
diff --git a/security/nss/lib/ssl/dtls13con.c b/security/nss/lib/ssl/dtls13con.c
new file mode 100644
index 000000000..aba0f62ab
--- /dev/null
+++ b/security/nss/lib/ssl/dtls13con.c
@@ -0,0 +1,457 @@
+/* -*- 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/. */
+
+/*
+ * DTLS 1.3 Protocol
+ */
+
+#include "ssl.h"
+#include "sslimpl.h"
+#include "sslproto.h"
+
+/* DTLS 1.3 Record map for ACK processing.
+ * This represents a single fragment, so a record which includes
+ * multiple fragments will have one entry for each fragment on the
+ * sender. We use the same structure on the receiver for convenience
+ * but the only value we actually use is |record|.
+ */
+typedef struct DTLSHandshakeRecordEntryStr {
+ PRCList link;
+ PRUint16 messageSeq; /* The handshake message sequence number. */
+ PRUint32 offset; /* The offset into the handshake message. */
+ PRUint32 length; /* The length of the fragment. */
+ sslSequenceNumber record; /* The record (includes epoch). */
+ PRBool acked; /* Has this packet been acked. */
+} DTLSHandshakeRecordEntry;
+
+/* Combine the epoch and sequence number into a single value. */
+static inline sslSequenceNumber
+dtls_CombineSequenceNumber(DTLSEpoch epoch, sslSequenceNumber seqNum)
+{
+ PORT_Assert(seqNum <= RECORD_SEQ_MAX);
+ return ((sslSequenceNumber)epoch << 48) | seqNum;
+}
+
+SECStatus
+dtls13_RememberFragment(sslSocket *ss,
+ PRCList *list,
+ PRUint32 sequence,
+ PRUint32 offset,
+ PRUint32 length,
+ DTLSEpoch epoch,
+ sslSequenceNumber record)
+{
+ DTLSHandshakeRecordEntry *entry;
+
+ PORT_Assert(IS_DTLS(ss));
+ /* We should never send an empty fragment with offset > 0. */
+ PORT_Assert(length || !offset);
+
+ if (!tls13_MaybeTls13(ss)) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(20, ("%d: SSL3[%d]: %s remembering %s record=%llx msg=%d offset=%d",
+ SSL_GETPID(), ss->fd,
+ SSL_ROLE(ss),
+ list == &ss->ssl3.hs.dtlsSentHandshake ? "sent" : "received",
+ dtls_CombineSequenceNumber(epoch, record), sequence, offset));
+
+ entry = PORT_ZAlloc(sizeof(DTLSHandshakeRecordEntry));
+ if (!entry) {
+ return SECFailure;
+ }
+
+ entry->messageSeq = sequence;
+ entry->offset = offset;
+ entry->length = length;
+ entry->record = dtls_CombineSequenceNumber(epoch, record);
+ entry->acked = PR_FALSE;
+
+ PR_APPEND_LINK(&entry->link, list);
+
+ return SECSuccess;
+}
+
+SECStatus
+dtls13_SendAck(sslSocket *ss)
+{
+ sslBuffer buf = SSL_BUFFER_EMPTY;
+ SECStatus rv = SECSuccess;
+ PRCList *cursor;
+ PRInt32 sent;
+
+ SSL_TRC(10, ("%d: SSL3[%d]: Sending ACK",
+ SSL_GETPID(), ss->fd));
+
+ for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsRcvdHandshake);
+ cursor != &ss->ssl3.hs.dtlsRcvdHandshake;
+ cursor = PR_NEXT_LINK(cursor)) {
+ DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor;
+
+ SSL_TRC(10, ("%d: SSL3[%d]: ACK for record=%llx",
+ SSL_GETPID(), ss->fd, entry->record));
+ rv = sslBuffer_AppendNumber(&buf, entry->record, 8);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ ssl_GetXmitBufLock(ss);
+ sent = ssl3_SendRecord(ss, NULL, content_ack,
+ buf.buf, buf.len, 0);
+ ssl_ReleaseXmitBufLock(ss);
+ if (sent != buf.len) {
+ rv = SECFailure;
+ if (sent != -1) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+ }
+
+loser:
+ sslBuffer_Clear(&buf);
+ return rv;
+}
+
+void
+dtls13_SendAckCb(sslSocket *ss)
+{
+ if (!IS_DTLS(ss)) {
+ return;
+ }
+ (void)dtls13_SendAck(ss);
+}
+
+/* Zero length messages are very simple to check. */
+static PRBool
+dtls_IsEmptyMessageAcknowledged(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset)
+{
+ PRCList *cursor;
+
+ for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake);
+ cursor != &ss->ssl3.hs.dtlsSentHandshake;
+ cursor = PR_NEXT_LINK(cursor)) {
+ DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor;
+ if (!entry->acked || msgSeq != entry->messageSeq) {
+ continue;
+ }
+ /* Empty fragments are always offset 0. */
+ if (entry->length == 0) {
+ PORT_Assert(!entry->offset);
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+/* Take a range starting at |*start| and that start forwards based on the
+ * contents of the acknowedgement in |entry|. Only move if the acknowledged
+ * range overlaps |*start|. Return PR_TRUE if it moves. */
+static PRBool
+dtls_MoveUnackedStartForward(DTLSHandshakeRecordEntry *entry, PRUint32 *start)
+{
+ /* This entry starts too late. */
+ if (*start < entry->offset) {
+ return PR_FALSE;
+ }
+ /* This entry ends too early. */
+ if (*start >= entry->offset + entry->length) {
+ return PR_FALSE;
+ }
+ *start = entry->offset + entry->length;
+ return PR_TRUE;
+}
+
+/* Take a range ending at |*end| and move that end backwards based on the
+ * contents of the acknowedgement in |entry|. Only move if the acknowledged
+ * range overlaps |*end|. Return PR_TRUE if it moves. */
+static PRBool
+dtls_MoveUnackedEndBackward(DTLSHandshakeRecordEntry *entry, PRUint32 *end)
+{
+ /* This entry ends too early. */
+ if (*end > entry->offset + entry->length) {
+ return PR_FALSE;
+ }
+ /* This entry starts too late. */
+ if (*end <= entry->offset) {
+ return PR_FALSE;
+ }
+ *end = entry->offset;
+ return PR_TRUE;
+}
+
+/* Get the next contiguous range of unacknowledged bytes from the handshake
+ * message identified by |msgSeq|. The search starts at the offset in |offset|.
+ * |len| contains the full length of the message.
+ *
+ * Returns PR_TRUE if there is an unacknowledged range. In this case, values at
+ * |start| and |end| are modified to contain the range.
+ *
+ * Returns PR_FALSE if the message is entirely acknowledged from |offset|
+ * onwards.
+ */
+PRBool
+dtls_NextUnackedRange(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset,
+ PRUint32 len, PRUint32 *startOut, PRUint32 *endOut)
+{
+ PRCList *cur_p;
+ PRBool done = PR_FALSE;
+ DTLSHandshakeRecordEntry *entry;
+ PRUint32 start;
+ PRUint32 end;
+
+ PORT_Assert(IS_DTLS(ss));
+
+ *startOut = offset;
+ *endOut = len;
+ if (!tls13_MaybeTls13(ss)) {
+ return PR_TRUE;
+ }
+
+ /* The message is empty. Use a simple search. */
+ if (!len) {
+ PORT_Assert(!offset);
+ return !dtls_IsEmptyMessageAcknowledged(ss, msgSeq, offset);
+ }
+
+ /* This iterates multiple times over the acknowledgments and only terminates
+ * when an entire iteration happens without start or end moving. If that
+ * happens without start and end crossing each other, then there is a range
+ * of unacknowledged data. If they meet, then the message is fully
+ * acknowledged. */
+ start = offset;
+ end = len;
+ while (!done) {
+ done = PR_TRUE;
+ for (cur_p = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake);
+ cur_p != &ss->ssl3.hs.dtlsSentHandshake;
+ cur_p = PR_NEXT_LINK(cur_p)) {
+ entry = (DTLSHandshakeRecordEntry *)cur_p;
+ if (!entry->acked || msgSeq != entry->messageSeq) {
+ continue;
+ }
+
+ if (dtls_MoveUnackedStartForward(entry, &start) ||
+ dtls_MoveUnackedEndBackward(entry, &end)) {
+ if (start >= end) {
+ /* The message is all acknowledged. */
+ return PR_FALSE;
+ }
+ /* Start over again and keep going until we don't move either
+ * start or end. */
+ done = PR_FALSE;
+ break;
+ }
+ }
+ }
+ PORT_Assert(start < end);
+
+ *startOut = start;
+ *endOut = end;
+ return PR_TRUE;
+}
+
+SECStatus
+dtls13_SetupAcks(sslSocket *ss)
+{
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ if (ss->ssl3.hs.endOfFlight) {
+ dtls_CancelTimer(ss, ss->ssl3.hs.ackTimer);
+
+ if (ss->ssl3.hs.ws == idle_handshake && ss->sec.isServer) {
+ SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, sending ACK",
+ SSL_GETPID(), ss->fd));
+ return dtls13_SendAck(ss);
+ }
+ return SECSuccess;
+ }
+
+ /* We need to send an ACK. */
+ if (!ss->ssl3.hs.ackTimer->cb) {
+ /* We're not armed, so arm. */
+ SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, arming ack timer",
+ SSL_GETPID(), ss->fd));
+ return dtls_StartTimer(ss, ss->ssl3.hs.ackTimer,
+ DTLS_RETRANSMIT_INITIAL_MS / 4,
+ dtls13_SendAckCb);
+ }
+ /* The ack timer is already armed, so just return. */
+ return SECSuccess;
+}
+
+/*
+ * Special case processing for out-of-epoch records.
+ * This can only handle ACKs for now and everything else generates
+ * an error. In future, may also handle KeyUpdate.
+ *
+ * The error checking here is as follows:
+ *
+ * - If it's not encrypted, out of epoch stuff is just discarded.
+ * - If it's encrypted, out of epoch stuff causes an error.
+ */
+SECStatus
+dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec,
+ SSL3ContentType rType,
+ sslBuffer *databuf)
+{
+ SECStatus rv;
+ sslBuffer buf = *databuf;
+
+ databuf->len = 0; /* Discard data whatever happens. */
+ PORT_Assert(IS_DTLS(ss));
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ /* Can't happen, but double check. */
+ if (!IS_DTLS(ss) || (ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
+ tls13_FatalError(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+ SSL_TRC(10, ("%d: DTLS13[%d]: handle out of epoch record: type=%d", SSL_GETPID(),
+ ss->fd, rType));
+
+ if (rType == content_ack) {
+ ssl_GetSSL3HandshakeLock(ss);
+ rv = dtls13_HandleAck(ss, &buf);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ PORT_Assert(databuf->len == 0);
+ return rv;
+ }
+
+ switch (spec->epoch) {
+ case TrafficKeyClearText:
+ /* Drop. */
+ return SECSuccess;
+
+ case TrafficKeyHandshake:
+ /* Drop out of order handshake messages, but if we are the
+ * server, we might have processed the client's Finished and
+ * moved on to application data keys, but the client has
+ * retransmitted Finished (e.g., because our ACK got lost.)
+ * We just retransmit the previous Finished to let the client
+ * complete. */
+ if (rType == content_handshake) {
+ if ((ss->sec.isServer) &&
+ (ss->ssl3.hs.ws == idle_handshake)) {
+ PORT_Assert(dtls_TimerActive(ss, ss->ssl3.hs.hdTimer));
+ return dtls13_SendAck(ss);
+ }
+ return SECSuccess;
+ }
+
+ /* This isn't a handshake record, so shouldn't be encrypted
+ * under the handshake key. */
+ break;
+
+ default:
+ /* Any other epoch is forbidden. */
+ break;
+ }
+
+ SSL_TRC(10, ("%d: SSL3[%d]: unexpected out of epoch record type %d", SSL_GETPID(),
+ ss->fd, rType));
+
+ (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
+ return SECFailure;
+}
+
+SECStatus
+dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf)
+{
+ PRUint8 *b = databuf->buf;
+ PRUint32 l = databuf->len;
+ SECStatus rv;
+
+ /* Ensure we don't loop. */
+ databuf->len = 0;
+
+ PORT_Assert(IS_DTLS(ss));
+ if (!tls13_MaybeTls13(ss)) {
+ tls13_FatalError(ss, SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, illegal_parameter);
+ return SECSuccess;
+ }
+
+ SSL_TRC(10, ("%d: SSL3[%d]: Handling ACK", SSL_GETPID(), ss->fd));
+ while (l > 0) {
+ PRUint64 seq;
+ PRCList *cursor;
+
+ rv = ssl3_ConsumeHandshakeNumber64(ss, &seq, 8, &b, &l);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake);
+ cursor != &ss->ssl3.hs.dtlsSentHandshake;
+ cursor = PR_NEXT_LINK(cursor)) {
+ DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor;
+
+ if (entry->record == seq) {
+ SSL_TRC(10, (
+ "%d: SSL3[%d]: Marking record=%llx message %d offset %d length=%d as ACKed",
+ SSL_GETPID(), ss->fd,
+ seq, entry->messageSeq, entry->offset, entry->length));
+ entry->acked = PR_TRUE;
+ }
+ }
+ }
+
+ /* Try to flush. */
+ rv = dtls_TransmitMessageFlight(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Reset the retransmit timer. */
+ if (ss->ssl3.hs.rtTimer->cb) {
+ (void)dtls_RestartTimer(ss, ss->ssl3.hs.rtTimer);
+ }
+
+ /* If there are no more messages to send, cleanup. */
+ if (PR_CLIST_IS_EMPTY(&ss->ssl3.hs.lastMessageFlight)) {
+ SSL_TRC(10, ("%d: SSL3[%d]: No more unacked handshake messages",
+ SSL_GETPID(), ss->fd));
+
+ dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
+ ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL);
+ /* If the handshake is finished, and we're the client then
+ * also clean up the handshake read cipher spec. Any ACKs
+ * we receive will be with the application data cipher spec.
+ * The server needs to keep the handshake cipher spec around
+ * for the holddown period to process retransmitted Finisheds.
+ */
+ if (!ss->sec.isServer && (ss->ssl3.hs.ws == idle_handshake)) {
+ ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead,
+ TrafficKeyHandshake);
+ }
+ }
+ return SECSuccess;
+}
+
+/* Clean up the read timer for the handshake cipher suites on the
+ * server.
+ *
+ * In DTLS 1.3, the client speaks last (Finished), and will retransmit
+ * until the server ACKs that message (using application data cipher
+ * suites). I.e.,
+ *
+ * - The client uses the retransmit timer and retransmits using the
+ * saved write handshake cipher suite.
+ * - The server keeps the saved read handshake cipher suite around
+ * for the holddown period in case it needs to read the Finished.
+ *
+ * After the holddown period, the server assumes the client is happy
+ * and discards the handshake read cipher suite.
+ */
+void
+dtls13_HolddownTimerCb(sslSocket *ss)
+{
+ SSL_TRC(10, ("%d: SSL3[%d]: holddown timer fired",
+ SSL_GETPID(), ss->fd));
+ ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyHandshake);
+ ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
+}
diff --git a/security/nss/lib/ssl/dtls13con.h b/security/nss/lib/ssl/dtls13con.h
new file mode 100644
index 000000000..bf14d3bd2
--- /dev/null
+++ b/security/nss/lib/ssl/dtls13con.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __dtls13con_h_
+#define __dtls13con_h_
+
+SECStatus dtls13_RememberFragment(sslSocket *ss, PRCList *list,
+ PRUint32 sequence, PRUint32 offset,
+ PRUint32 length, DTLSEpoch epoch,
+ sslSequenceNumber record);
+PRBool dtls_NextUnackedRange(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset,
+ PRUint32 len, PRUint32 *startOut, PRUint32 *endOut);
+SECStatus dtls13_SetupAcks(sslSocket *ss);
+SECStatus dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec,
+ SSL3ContentType rType,
+ sslBuffer *databuf);
+SECStatus dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf);
+
+SECStatus dtls13_SendAck(sslSocket *ss);
+void dtls13_SendAckCb(sslSocket *ss);
+void dtls13_HolddownTimerCb(sslSocket *ss);
+void dtls_ReceivedFirstMessageInFlight(sslSocket *ss);
+
+#endif
diff --git a/security/nss/lib/ssl/dtlscon.c b/security/nss/lib/ssl/dtlscon.c
index fbd1779db..2f335f924 100644
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -10,16 +10,17 @@
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
+#include "dtls13con.h"
#ifndef PR_ARRAY_SIZE
#define PR_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
-static SECStatus dtls_TransmitMessageFlight(sslSocket *ss);
static SECStatus dtls_StartRetransmitTimer(sslSocket *ss);
static void dtls_RetransmitTimerExpiredCb(sslSocket *ss);
static SECStatus dtls_SendSavedWriteData(sslSocket *ss);
static void dtls_FinishedTimerCb(sslSocket *ss);
+static void dtls_CancelAllTimers(sslSocket *ss);
/* -28 adjusts for the IP/UDP header */
static const PRUint16 COMMON_MTU_VALUES[] = {
@@ -30,6 +31,9 @@ static const PRUint16 COMMON_MTU_VALUES[] = {
};
#define DTLS_COOKIE_BYTES 32
+/* Maximum DTLS expansion = header + IV + max CBC padding +
+ * maximum MAC. */
+#define DTLS_MAX_EXPANSION (DTLS_RECORD_HEADER_LENGTH + 16 + 16 + 32)
/* List copied from ssl3con.c:cipherSuites */
static const ssl3CipherSuite nonDTLSSuites[] = {
@@ -119,9 +123,9 @@ static DTLSQueuedMessage *
dtls_AllocQueuedMessage(ssl3CipherSpec *cwSpec, SSL3ContentType type,
const unsigned char *data, PRUint32 len)
{
- DTLSQueuedMessage *msg = NULL;
+ DTLSQueuedMessage *msg;
- msg = PORT_ZAlloc(sizeof(DTLSQueuedMessage));
+ msg = PORT_ZNew(DTLSQueuedMessage);
if (!msg)
return NULL;
@@ -137,7 +141,7 @@ dtls_AllocQueuedMessage(ssl3CipherSpec *cwSpec, SSL3ContentType type,
msg->type = type;
/* Safe if we are < 1.3, since the refct is
* already very high. */
- tls13_CipherSpecAddRef(cwSpec);
+ ssl_CipherSpecAddRef(cwSpec);
return msg;
}
@@ -155,7 +159,7 @@ dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg)
/* Safe if we are < 1.3, since the refct is
* already very high. */
- tls13_CipherSpecRelease(msg->cwSpec);
+ ssl_CipherSpecRelease(msg->cwSpec);
PORT_ZFree(msg->data, msg->len);
PORT_Free(msg);
}
@@ -184,37 +188,38 @@ dtls_FreeHandshakeMessages(PRCList *list)
static SECStatus
dtls_RetransmitDetected(sslSocket *ss)
{
+ dtlsTimer *timer = ss->ssl3.hs.rtTimer;
SECStatus rv = SECSuccess;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- if (ss->ssl3.hs.rtTimerCb == dtls_RetransmitTimerExpiredCb) {
+ if (timer->cb == dtls_RetransmitTimerExpiredCb) {
/* Check to see if we retransmitted recently. If so,
* suppress the triggered retransmit. This avoids
* retransmit wars after packet loss.
* This is not in RFC 5346 but it should be.
*/
- if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
- (ss->ssl3.hs.rtTimeoutMs / 4)) {
+ if ((PR_IntervalNow() - timer->started) >
+ (timer->timeout / 4)) {
SSL_TRC(30,
("%d: SSL3[%d]: Shortcutting retransmit timer",
SSL_GETPID(), ss->fd));
/* Cancel the timer and call the CB,
* which re-arms the timer */
- dtls_CancelTimer(ss);
+ dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
dtls_RetransmitTimerExpiredCb(ss);
} else {
SSL_TRC(30,
("%d: SSL3[%d]: Ignoring retransmission: "
"last retransmission %dms ago, suppressed for %dms",
SSL_GETPID(), ss->fd,
- PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted,
- ss->ssl3.hs.rtTimeoutMs / 4));
+ PR_IntervalNow() - timer->started,
+ timer->timeout / 4));
}
- } else if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb) {
+ } else if (timer->cb == dtls_FinishedTimerCb) {
SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected in holddown",
SSL_GETPID(), ss->fd));
/* Retransmit the messages and re-arm the timer
@@ -222,14 +227,14 @@ dtls_RetransmitDetected(sslSocket *ss)
* The spec isn't clear and my reasoning is that this
* may be a re-ordered packet rather than slowness,
* so let's be aggressive. */
- dtls_CancelTimer(ss);
+ dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
rv = dtls_TransmitMessageFlight(ss);
if (rv == SECSuccess) {
rv = dtls_StartHolddownTimer(ss);
}
} else {
- PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
+ PORT_Assert(timer->cb == NULL);
/* ... and ignore it. */
}
return rv;
@@ -238,19 +243,8 @@ dtls_RetransmitDetected(sslSocket *ss)
static SECStatus
dtls_HandleHandshakeMessage(sslSocket *ss, PRUint8 *data, PRBool last)
{
-
- /* At this point we are advancing our state machine, so we can free our last
- * flight of messages. */
- dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
ss->ssl3.hs.recvdHighWater = -1;
- /* Reset the timer to the initial value if the retry counter
- * is 0, per Sec. 4.2.4.1 */
- dtls_CancelTimer(ss);
- if (ss->ssl3.hs.rtRetries == 0) {
- ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
- }
-
return ssl3_HandleHandshakeMessage(ss, data, ss->ssl3.hs.msg_len,
last);
}
@@ -273,7 +267,8 @@ dtls_HandleHandshakeMessage(sslSocket *ss, PRUint8 *data, PRBool last)
#define OFFSET_MASK(o) (1 << (o % 8))
SECStatus
-dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
+dtls_HandleHandshake(sslSocket *ss, DTLSEpoch epoch, sslSequenceNumber seqNum,
+ sslBuffer *origBuf)
{
/* XXX OK for now.
* This doesn't work properly with asynchronous certificate validation.
@@ -283,6 +278,9 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
*/
sslBuffer buf = *origBuf;
SECStatus rv = SECSuccess;
+ PRBool discarded = PR_FALSE;
+
+ ss->ssl3.hs.endOfFlight = PR_FALSE;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -298,7 +296,7 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
if (buf.len < 12) {
PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
rv = SECFailure;
- break;
+ goto loser;
}
/* Parse the header */
@@ -323,14 +321,28 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
if (buf.len < fragment_length) {
PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
rv = SECFailure;
- break;
+ goto loser;
}
/* Sanity check the packet contents */
if ((fragment_length + fragment_offset) > message_length) {
PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
rv = SECFailure;
- break;
+ goto loser;
+ }
+
+ /* If we're a server and we receive what appears to be a retried
+ * ClientHello, and we are expecting a ClientHello, move the receive
+ * sequence number forward. This allows for a retried ClientHello if we
+ * send a stateless HelloRetryRequest. */
+ if (message_seq > ss->ssl3.hs.recvMessageSeq &&
+ message_seq == 1 &&
+ fragment_offset == 0 &&
+ ss->ssl3.hs.ws == wait_client_hello &&
+ (SSLHandshakeType)type == ssl_hs_client_hello) {
+ SSL_TRC(5, ("%d: DTLS[%d]: Received apparent 2nd ClientHello",
+ SSL_GETPID(), ss->fd));
+ ss->ssl3.hs.recvMessageSeq = 1;
}
/* There are three ways we could not be ready for this packet.
@@ -346,20 +358,20 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
(fragment_offset == 0) &&
(fragment_length == message_length)) {
/* Complete next message. Process immediately */
- ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
+ ss->ssl3.hs.msg_type = (SSLHandshakeType)type;
ss->ssl3.hs.msg_len = message_length;
rv = dtls_HandleHandshakeMessage(ss, buf.buf,
buf.len == fragment_length);
if (rv == SECFailure) {
- break; /* Discard the remainder of the record. */
+ goto loser;
}
} else {
if (message_seq < ss->ssl3.hs.recvMessageSeq) {
/* Case 3: we do an immediate retransmit if we're
* in a waiting state. */
rv = dtls_RetransmitDetected(ss);
- break;
+ goto loser;
} else if (message_seq > ss->ssl3.hs.recvMessageSeq) {
/* Case 2
*
@@ -369,7 +381,12 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
*
* XXX OK for now. Maybe do something smarter at some point?
*/
+ SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, discarding handshake message",
+ SSL_GETPID(), ss->fd));
+ discarded = PR_TRUE;
} else {
+ PRInt32 end = fragment_offset + fragment_length;
+
/* Case 1
*
* Buffer the fragment for reassembly
@@ -380,18 +397,18 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, message_length);
if (rv != SECSuccess)
- break;
+ goto loser;
/* Make room for the fragment map */
rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments,
map_length);
if (rv != SECSuccess)
- break;
+ goto loser;
/* Reset the reassembly map */
ss->ssl3.hs.recvdHighWater = 0;
PORT_Memset(ss->ssl3.hs.recvdFragments.buf, 0,
ss->ssl3.hs.recvdFragments.space);
- ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
+ ss->ssl3.hs.msg_type = (SSLHandshakeType)type;
ss->ssl3.hs.msg_len = message_length;
}
@@ -403,14 +420,14 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
ss->ssl3.hs.recvdHighWater = -1;
PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
rv = SECFailure;
- break;
+ goto loser;
}
- /* Now copy this fragment into the buffer */
- PORT_Assert((fragment_offset + fragment_length) <=
- ss->ssl3.hs.msg_body.space);
- PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset,
- buf.buf, fragment_length);
+ /* Now copy this fragment into the buffer. */
+ if (end > ss->ssl3.hs.recvdHighWater) {
+ PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset,
+ buf.buf, fragment_length);
+ }
/* This logic is a bit tricky. We have two values for
* reassembly state:
@@ -426,12 +443,11 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
if (fragment_offset <= (unsigned int)ss->ssl3.hs.recvdHighWater) {
/* Either this is the adjacent fragment or an overlapping
* fragment */
- ss->ssl3.hs.recvdHighWater = fragment_offset +
- fragment_length;
+ if (end > ss->ssl3.hs.recvdHighWater) {
+ ss->ssl3.hs.recvdHighWater = end;
+ }
} else {
- for (offset = fragment_offset;
- offset < fragment_offset + fragment_length;
- offset++) {
+ for (offset = fragment_offset; offset < end; offset++) {
ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] |=
OFFSET_MASK(offset);
}
@@ -457,7 +473,7 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
buf.len == fragment_length);
if (rv == SECFailure) {
- break; /* Discard the rest of the record. */
+ goto loser;
}
}
}
@@ -467,6 +483,26 @@ dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
buf.len -= fragment_length;
}
+ // This should never happen, but belt and suspenders.
+ if (rv == SECFailure) {
+ PORT_Assert(0);
+ goto loser;
+ }
+
+ /* If we processed all the fragments in this message, then mark it as remembered.
+ * TODO(ekr@rtfm.com): Store out of order messages for DTLS 1.3 so ACKs work
+ * better. Bug 1392620.*/
+ if (!discarded && tls13_MaybeTls13(ss)) {
+ rv = dtls13_RememberFragment(ss, &ss->ssl3.hs.dtlsRcvdHandshake,
+ 0, 0, 0, epoch, seqNum);
+ }
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = dtls13_SetupAcks(ss);
+
+loser:
origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */
/* XXX OK for now. In future handle rv == SECWouldBlock safely in order
@@ -560,6 +596,8 @@ dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
if (!(flags & ssl_SEND_FLAG_NO_RETRANSMIT)) {
rv = dtls_StartRetransmitTimer(ss);
+ } else {
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
}
}
@@ -576,7 +614,7 @@ static void
dtls_RetransmitTimerExpiredCb(sslSocket *ss)
{
SECStatus rv;
-
+ dtlsTimer *timer = ss->ssl3.hs.rtTimer;
ss->ssl3.hs.rtRetries++;
if (!(ss->ssl3.hs.rtRetries % 3)) {
@@ -589,175 +627,239 @@ dtls_RetransmitTimerExpiredCb(sslSocket *ss)
rv = dtls_TransmitMessageFlight(ss);
if (rv == SECSuccess) {
/* Re-arm the timer */
- ss->ssl3.hs.rtTimeoutMs *= 2;
- if (ss->ssl3.hs.rtTimeoutMs > DTLS_RETRANSMIT_MAX_MS) {
- ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_MAX_MS;
+ timer->timeout *= 2;
+ if (timer->timeout > DTLS_RETRANSMIT_MAX_MS) {
+ timer->timeout = DTLS_RETRANSMIT_MAX_MS;
}
- ss->ssl3.hs.rtTimerStarted = PR_IntervalNow();
- ss->ssl3.hs.rtTimerCb = dtls_RetransmitTimerExpiredCb;
+ timer->started = PR_IntervalNow();
+ timer->cb = dtls_RetransmitTimerExpiredCb;
SSL_TRC(30,
("%d: SSL3[%d]: Retransmit #%d, next in %d",
SSL_GETPID(), ss->fd,
- ss->ssl3.hs.rtRetries, ss->ssl3.hs.rtTimeoutMs));
+ ss->ssl3.hs.rtRetries, timer->timeout));
}
/* else: OK for now. In future maybe signal the stack that we couldn't
* transmit. For now, let the read handle any real network errors */
}
+#define DTLS_HS_HDR_LEN 12
+#define DTLS_MIN_FRAGMENT (DTLS_HS_HDR_LEN + 1 + DTLS_MAX_EXPANSION)
+
+/* Encrypt and encode a handshake message fragment. Flush the data out to the
+ * network if there is insufficient space for any fragment. */
+static SECStatus
+dtls_SendFragment(sslSocket *ss, DTLSQueuedMessage *msg, PRUint8 *data,
+ unsigned int len)
+{
+ PRInt32 sent;
+ SECStatus rv;
+
+ PRINT_BUF(40, (ss, "dtls_SendFragment", data, len));
+ sent = ssl3_SendRecord(ss, msg->cwSpec, msg->type, data, len,
+ ssl_SEND_FLAG_FORCE_INTO_BUFFER);
+ if (sent != len) {
+ if (sent != -1) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+ return SECFailure;
+ }
+
+ /* If another fragment won't fit, flush. */
+ if (ss->ssl3.mtu < ss->pendingBuf.len + DTLS_MIN_FRAGMENT) {
+ SSL_TRC(20, ("%d: DTLS[%d]: dtls_SendFragment: flush",
+ SSL_GETPID(), ss->fd));
+ rv = dtls_SendSavedWriteData(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+ return SECSuccess;
+}
+
+/* Fragment a handshake message into multiple records and send them. */
+static SECStatus
+dtls_FragmentHandshake(sslSocket *ss, DTLSQueuedMessage *msg)
+{
+ PRBool fragmentWritten = PR_FALSE;
+ PRUint16 msgSeq;
+ PRUint8 *fragment;
+ PRUint32 fragmentOffset = 0;
+ PRUint32 fragmentLen;
+ const PRUint8 *content = msg->data + DTLS_HS_HDR_LEN;
+ PRUint32 contentLen = msg->len - DTLS_HS_HDR_LEN;
+ SECStatus rv;
+
+ /* The headers consume 12 bytes so the smallest possible message (i.e., an
+ * empty one) is 12 bytes. */
+ PORT_Assert(msg->len >= DTLS_HS_HDR_LEN);
+
+ /* DTLS only supports fragmenting handshaking messages. */
+ PORT_Assert(msg->type == content_handshake);
+
+ msgSeq = (msg->data[4] << 8) | msg->data[5];
+
+ /* do {} while() so that empty messages are sent at least once. */
+ do {
+ PRUint8 buf[DTLS_MAX_MTU]; /* >= than largest plausible MTU */
+ PRBool hasUnackedRange;
+ PRUint32 end;
+
+ hasUnackedRange = dtls_NextUnackedRange(ss, msgSeq,
+ fragmentOffset, contentLen,
+ &fragmentOffset, &end);
+ if (!hasUnackedRange) {
+ SSL_TRC(20, ("%d: SSL3[%d]: FragmentHandshake %d: all acknowledged",
+ SSL_GETPID(), ss->fd, msgSeq));
+ break;
+ }
+
+ SSL_TRC(20, ("%d: SSL3[%d]: FragmentHandshake %d: unacked=%u-%u",
+ SSL_GETPID(), ss->fd, msgSeq, fragmentOffset, end));
+
+ /* Cut down to the data we have available. */
+ PORT_Assert(fragmentOffset <= contentLen);
+ PORT_Assert(fragmentOffset <= end);
+ PORT_Assert(end <= contentLen);
+ fragmentLen = PR_MIN(end, contentLen) - fragmentOffset;
+
+ /* Reduce to the space remaining in the MTU. Allow for any existing
+ * messages, record expansion, and the handshake header. */
+ fragmentLen = PR_MIN(fragmentLen,
+ ss->ssl3.mtu - /* MTU estimate. */
+ ss->pendingBuf.len - /* Less unsent records. */
+ DTLS_MAX_EXPANSION - /* Allow for expansion. */
+ DTLS_HS_HDR_LEN); /* + handshake header. */
+ PORT_Assert(fragmentLen > 0 || fragmentOffset == 0);
+
+ /* Make totally sure that we will fit in the buffer. This should be
+ * impossible; DTLS_MAX_MTU should always be more than ss->ssl3.mtu. */
+ if (fragmentLen >= (DTLS_MAX_MTU - DTLS_HS_HDR_LEN)) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if (fragmentLen == contentLen) {
+ fragment = msg->data;
+ } else {
+ sslBuffer tmp = SSL_BUFFER_FIXED(buf, sizeof(buf));
+
+ /* Construct an appropriate-sized fragment */
+ /* Type, length, sequence */
+ rv = sslBuffer_Append(&tmp, msg->data, 6);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* Offset. */
+ rv = sslBuffer_AppendNumber(&tmp, fragmentOffset, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* Length. */
+ rv = sslBuffer_AppendNumber(&tmp, fragmentLen, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* Data. */
+ rv = sslBuffer_Append(&tmp, content + fragmentOffset, fragmentLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ fragment = SSL_BUFFER_BASE(&tmp);
+ }
+
+ /* Record that we are sending first, because encrypting
+ * increments the sequence number. */
+ rv = dtls13_RememberFragment(ss, &ss->ssl3.hs.dtlsSentHandshake,
+ msgSeq, fragmentOffset, fragmentLen,
+ msg->cwSpec->epoch,
+ msg->cwSpec->seqNum);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = dtls_SendFragment(ss, msg, fragment,
+ fragmentLen + DTLS_HS_HDR_LEN);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ fragmentWritten = PR_TRUE;
+ fragmentOffset += fragmentLen;
+ } while (fragmentOffset < contentLen);
+
+ if (!fragmentWritten) {
+ /* Nothing was written if we got here, so the whole message must have
+ * been acknowledged. Discard it. */
+ SSL_TRC(10, ("%d: SSL3[%d]: FragmentHandshake %d: removed",
+ SSL_GETPID(), ss->fd, msgSeq));
+ PR_REMOVE_LINK(&msg->link);
+ dtls_FreeHandshakeMessage(msg);
+ }
+
+ return SECSuccess;
+}
+
/* Transmit a flight of handshake messages, stuffing them
- * into as few records as seems reasonable
+ * into as few records as seems reasonable.
+ *
+ * TODO: Space separate UDP packets out a little.
*
* Called from:
* dtls_FlushHandshake()
* dtls_RetransmitTimerExpiredCb()
*/
-static SECStatus
+SECStatus
dtls_TransmitMessageFlight(sslSocket *ss)
{
SECStatus rv = SECSuccess;
PRCList *msg_p;
- PRUint16 room_left = ss->ssl3.mtu;
- PRInt32 sent;
+
+ SSL_TRC(10, ("%d: SSL3[%d]: dtls_TransmitMessageFlight",
+ SSL_GETPID(), ss->fd));
ssl_GetXmitBufLock(ss);
ssl_GetSpecReadLock(ss);
- /* DTLS does not buffer its handshake messages in
- * ss->pendingBuf, but rather in the lastMessageFlight
- * structure. This is just a sanity check that
- * some programming error hasn't inadvertantly
- * stuffed something in ss->pendingBuf
+ /* DTLS does not buffer its handshake messages in ss->pendingBuf, but rather
+ * in the lastMessageFlight structure. This is just a sanity check that some
+ * programming error hasn't inadvertantly stuffed something in
+ * ss->pendingBuf. This function uses ss->pendingBuf temporarily and it
+ * needs to be empty to start.
*/
PORT_Assert(!ss->pendingBuf.len);
+
for (msg_p = PR_LIST_HEAD(&ss->ssl3.hs.lastMessageFlight);
- msg_p != &ss->ssl3.hs.lastMessageFlight;
- msg_p = PR_NEXT_LINK(msg_p)) {
+ msg_p != &ss->ssl3.hs.lastMessageFlight;) {
DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p;
- /* The logic here is:
- *
- * 1. If this is a message that will not fit into the remaining
- * space, then flush.
- * 2. If the message will now fit into the remaining space,
- * encrypt, buffer, and loop.
- * 3. If the message will not fit, then fragment.
- *
- * At the end of the function, flush.
- */
- if ((msg->len + SSL3_BUFFER_FUDGE) > room_left) {
- /* The message will not fit into the remaining space, so flush */
- rv = dtls_SendSavedWriteData(ss);
- if (rv != SECSuccess)
- break;
-
- room_left = ss->ssl3.mtu;
- }
+ /* Move the pointer forward so that the functions below are free to
+ * remove messages from the list. */
+ msg_p = PR_NEXT_LINK(msg_p);
- if ((msg->len + SSL3_BUFFER_FUDGE) <= room_left) {
- /* The message will fit, so encrypt and then continue with the
- * next packet */
- sent = ssl3_SendRecord(ss, msg->cwSpec, msg->type,
- msg->data, msg->len,
- ssl_SEND_FLAG_FORCE_INTO_BUFFER);
- if (sent != msg->len) {
- rv = SECFailure;
- if (sent != -1) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- }
- break;
- }
+ /* Note: This function fragments messages so that each record is close
+ * to full. This produces fewer records, but it means that messages can
+ * be quite fragmented. Adding an extra flush here would push new
+ * messages into new records and reduce fragmentation. */
- room_left = ss->ssl3.mtu - ss->pendingBuf.len;
+ if (msg->type == content_handshake) {
+ rv = dtls_FragmentHandshake(ss, msg);
} else {
- /* The message will not fit, so fragment.
- *
- * XXX OK for now. Arrange to coalesce the last fragment
- * of this message with the next message if possible.
- * That would be more efficient.
- */
- PRUint32 fragment_offset = 0;
- unsigned char fragment[DTLS_MAX_MTU]; /* >= than largest
- * plausible MTU */
-
- /* Assert that we have already flushed */
- PORT_Assert(room_left == ss->ssl3.mtu);
-
- /* Case 3: We now need to fragment this message
- * DTLS only supports fragmenting handshaking messages */
- PORT_Assert(msg->type == content_handshake);
-
- /* The headers consume 12 bytes so the smalles possible
- * message (i.e., an empty one) is 12 bytes
- */
- PORT_Assert(msg->len >= 12);
-
- while ((fragment_offset + 12) < msg->len) {
- PRUint32 fragment_len;
- const unsigned char *content = msg->data + 12;
- PRUint32 content_len = msg->len - 12;
-
- /* The reason we use 8 here is that that's the length of
- * the new DTLS data that we add to the header */
- fragment_len = PR_MIN((PRUint32)room_left - (SSL3_BUFFER_FUDGE + 8),
- content_len - fragment_offset);
- PORT_Assert(fragment_len < DTLS_MAX_MTU - 12);
- /* Make totally sure that we are within the buffer.
- * Note that the only way that fragment len could get
- * adjusted here is if
- *
- * (a) we are in release mode so the PORT_Assert is compiled out
- * (b) either the MTU table is inconsistent with DTLS_MAX_MTU
- * or ss->ssl3.mtu has become corrupt.
- */
- fragment_len = PR_MIN(fragment_len, DTLS_MAX_MTU - 12);
-
- /* Construct an appropriate-sized fragment */
- /* Type, length, sequence */
- PORT_Memcpy(fragment, msg->data, 6);
-
- /* Offset */
- fragment[6] = (fragment_offset >> 16) & 0xff;
- fragment[7] = (fragment_offset >> 8) & 0xff;
- fragment[8] = (fragment_offset)&0xff;
-
- /* Fragment length */
- fragment[9] = (fragment_len >> 16) & 0xff;
- fragment[10] = (fragment_len >> 8) & 0xff;
- fragment[11] = (fragment_len)&0xff;
-
- PORT_Memcpy(fragment + 12, content + fragment_offset,
- fragment_len);
-
- /*
- * Send the record. We do this in two stages
- * 1. Encrypt
- */
- sent = ssl3_SendRecord(ss, msg->cwSpec, msg->type,
- fragment, fragment_len + 12,
- ssl_SEND_FLAG_FORCE_INTO_BUFFER);
- if (sent != (fragment_len + 12)) {
- rv = SECFailure;
- if (sent != -1) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- }
- break;
- }
-
- /* 2. Flush */
- rv = dtls_SendSavedWriteData(ss);
- if (rv != SECSuccess)
- break;
-
- fragment_offset += fragment_len;
- }
+ PORT_Assert(!tls13_MaybeTls13(ss));
+ rv = dtls_SendFragment(ss, msg, msg->data, msg->len);
+ }
+ if (rv != SECSuccess) {
+ break;
}
}
- /* Finally, we need to flush */
- if (rv == SECSuccess)
+ /* Finally, flush any data that wasn't flushed already. */
+ if (rv == SECSuccess) {
rv = dtls_SendSavedWriteData(ss);
+ }
/* Give up the locks */
ssl_ReleaseSpecReadLock(ss);
@@ -796,23 +898,59 @@ dtls_SendSavedWriteData(sslSocket *ss)
return SECSuccess;
}
-static SECStatus
-dtls_StartTimer(sslSocket *ss, PRUint32 time, DTLSTimerCb cb)
+void
+dtls_InitTimers(sslSocket *ss)
{
- PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
+ unsigned int i;
+ dtlsTimer **timers[PR_ARRAY_SIZE(ss->ssl3.hs.timers)] = {
+ &ss->ssl3.hs.rtTimer,
+ &ss->ssl3.hs.ackTimer,
+ &ss->ssl3.hs.hdTimer
+ };
+ static const char *timerLabels[] = {
+ "retransmit", "ack", "holddown"
+ };
+
+ PORT_Assert(PR_ARRAY_SIZE(timers) == PR_ARRAY_SIZE(timerLabels));
+ for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+ *timers[i] = &ss->ssl3.hs.timers[i];
+ ss->ssl3.hs.timers[i].label = timerLabels[i];
+ }
+}
- ss->ssl3.hs.rtRetries = 0;
- ss->ssl3.hs.rtTimerStarted = PR_IntervalNow();
- ss->ssl3.hs.rtTimeoutMs = time;
- ss->ssl3.hs.rtTimerCb = cb;
+SECStatus
+dtls_StartTimer(sslSocket *ss, dtlsTimer *timer, PRUint32 time, DTLSTimerCb cb)
+{
+ PORT_Assert(timer->cb == NULL);
+
+ SSL_TRC(10, ("%d: SSL3[%d]: %s dtls_StartTimer %s timeout=%d",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss), timer->label, time));
+
+ timer->started = PR_IntervalNow();
+ timer->timeout = time;
+ timer->cb = cb;
return SECSuccess;
}
+SECStatus
+dtls_RestartTimer(sslSocket *ss, dtlsTimer *timer)
+{
+ timer->started = PR_IntervalNow();
+ return SECSuccess;
+}
+
+PRBool
+dtls_TimerActive(sslSocket *ss, dtlsTimer *timer)
+{
+ return timer->cb != NULL;
+}
/* Start a timer for retransmission. */
static SECStatus
dtls_StartRetransmitTimer(sslSocket *ss)
{
- return dtls_StartTimer(ss, DTLS_RETRANSMIT_INITIAL_MS,
+ ss->ssl3.hs.rtRetries = 0;
+ return dtls_StartTimer(ss, ss->ssl3.hs.rtTimer,
+ DTLS_RETRANSMIT_INITIAL_MS,
dtls_RetransmitTimerExpiredCb);
}
@@ -820,7 +958,9 @@ dtls_StartRetransmitTimer(sslSocket *ss)
SECStatus
dtls_StartHolddownTimer(sslSocket *ss)
{
- return dtls_StartTimer(ss, DTLS_RETRANSMIT_FINISHED_MS,
+ ss->ssl3.hs.rtRetries = 0;
+ return dtls_StartTimer(ss, ss->ssl3.hs.rtTimer,
+ DTLS_RETRANSMIT_FINISHED_MS,
dtls_FinishedTimerCb);
}
@@ -831,11 +971,25 @@ dtls_StartHolddownTimer(sslSocket *ss)
* dtls_CheckTimer()
*/
void
-dtls_CancelTimer(sslSocket *ss)
+dtls_CancelTimer(sslSocket *ss, dtlsTimer *timer)
{
+ SSL_TRC(30, ("%d: SSL3[%d]: %s dtls_CancelTimer %s",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss),
+ timer->label));
+
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
- ss->ssl3.hs.rtTimerCb = NULL;
+ timer->cb = NULL;
+}
+
+static void
+dtls_CancelAllTimers(sslSocket *ss)
+{
+ unsigned int i;
+
+ for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+ dtls_CancelTimer(ss, &ss->ssl3.hs.timers[i]);
+ }
}
/* Check the pending timer and fire the callback if it expired
@@ -845,22 +999,33 @@ dtls_CancelTimer(sslSocket *ss)
void
dtls_CheckTimer(sslSocket *ss)
{
+ unsigned int i;
+ SSL_TRC(30, ("%d: SSL3[%d]: dtls_CheckTimer (%s)",
+ SSL_GETPID(), ss->fd, ss->sec.isServer ? "server" : "client"));
+
ssl_GetSSL3HandshakeLock(ss);
- if (!ss->ssl3.hs.rtTimerCb) {
- ssl_ReleaseSSL3HandshakeLock(ss);
- return;
- }
- if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
- PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs)) {
- /* Timer has expired */
- DTLSTimerCb cb = ss->ssl3.hs.rtTimerCb;
+ for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+ dtlsTimer *timer = &ss->ssl3.hs.timers[i];
+ if (!timer->cb) {
+ continue;
+ }
+
+ if ((PR_IntervalNow() - timer->started) >=
+ PR_MillisecondsToInterval(timer->timeout)) {
+ /* Timer has expired */
+ DTLSTimerCb cb = timer->cb;
+
+ SSL_TRC(10, ("%d: SSL3[%d]: %s firing timer %s",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss),
+ timer->label));
- /* Cancel the timer so that we can call the CB safely */
- dtls_CancelTimer(ss);
+ /* Cancel the timer so that we can call the CB safely */
+ dtls_CancelTimer(ss, timer);
- /* Now call the CB */
- cb(ss);
+ /* Now call the CB */
+ cb(ss);
+ }
}
ssl_ReleaseSSL3HandshakeLock(ss);
}
@@ -874,9 +1039,6 @@ static void
dtls_FinishedTimerCb(sslSocket *ss)
{
dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
- }
}
/* Cancel the Finished hold-down timer and destroy the
@@ -895,8 +1057,8 @@ dtls_RehandshakeCleanup(sslSocket *ss)
return;
}
PORT_Assert((ss->version < SSL_LIBRARY_VERSION_TLS_1_3));
- dtls_CancelTimer(ss);
- ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
+ dtls_CancelAllTimers(ss);
+ dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
ss->ssl3.hs.sendMessageSeq = 0;
ss->ssl3.hs.recvMessageSeq = 0;
}
@@ -959,6 +1121,8 @@ dtls_HandleHelloVerifyRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
goto alert_loser;
}
+ dtls_ReceivedFirstMessageInFlight(ss);
+
/* The version.
*
* RFC 4347 required that you verify that the server versions
@@ -1103,27 +1267,53 @@ SECStatus
DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
{
sslSocket *ss = NULL;
- PRIntervalTime elapsed;
- PRIntervalTime desired;
+ PRBool found = PR_FALSE;
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime to;
+ unsigned int i;
+
+ *timeout = PR_INTERVAL_NO_TIMEOUT;
ss = ssl_FindSocket(socket);
- if (!ss)
+ if (!ss) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
+ }
- if (!IS_DTLS(ss))
+ if (!IS_DTLS(ss)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
+ }
- if (!ss->ssl3.hs.rtTimerCb)
- return SECFailure;
+ for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+ PRIntervalTime elapsed;
+ PRIntervalTime desired;
+ dtlsTimer *timer = &ss->ssl3.hs.timers[i];
- elapsed = PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted;
- desired = PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs);
- if (elapsed > desired) {
- /* Timer expired */
- *timeout = PR_INTERVAL_NO_WAIT;
- } else {
- *timeout = desired - elapsed;
+ if (!timer->cb) {
+ continue;
+ }
+ found = PR_TRUE;
+
+ elapsed = now - timer->started;
+ desired = PR_MillisecondsToInterval(timer->timeout);
+ if (elapsed > desired) {
+ /* Timer expired */
+ *timeout = PR_INTERVAL_NO_WAIT;
+ return SECSuccess;
+ } else {
+ to = desired - elapsed;
+ }
+
+ if (*timeout > to) {
+ *timeout = to;
+ }
+ }
+
+ if (!found) {
+ PORT_SetError(SSL_ERROR_NO_TIMERS_FOUND);
+ return SECFailure;
}
return SECSuccess;
@@ -1137,72 +1327,50 @@ DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
* seems like a good tradeoff for implementation effort and is
* consistent with the guidance of RFC 6347 Sections 4.1 and 4.2.4.1.
*
- * If the packet is not relevant, this function returns PR_FALSE.
- * If the packet is relevant, this function returns PR_TRUE
- * and sets |*seqNum| to the packet sequence number.
+ * If the packet is not relevant, this function returns PR_FALSE. If the packet
+ * is relevant, this function returns PR_TRUE and sets |*seqNumOut| to the
+ * packet sequence number (removing the epoch).
*/
PRBool
-dtls_IsRelevant(sslSocket *ss, const SSL3Ciphertext *cText,
- PRBool *sameEpoch, PRUint64 *seqNum)
+dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *spec,
+ const SSL3Ciphertext *cText,
+ sslSequenceNumber *seqNumOut)
{
- const ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
- DTLSEpoch epoch;
- sslSequenceNumber dtls_seq_num;
-
- epoch = cText->seq_num >> 48;
- *sameEpoch = crSpec->epoch == epoch;
- if (!*sameEpoch) {
- SSL_DBG(("%d: SSL3[%d]: dtls_IsRelevant, received packet "
- "from irrelevant epoch %d",
- SSL_GETPID(), ss->fd, epoch));
- return PR_FALSE;
- }
-
- dtls_seq_num = cText->seq_num & RECORD_SEQ_MAX;
- if (dtls_RecordGetRecvd(&crSpec->recvdRecords, dtls_seq_num) != 0) {
- SSL_DBG(("%d: SSL3[%d]: dtls_IsRelevant, rejecting "
- "potentially replayed packet",
- SSL_GETPID(), ss->fd));
+ sslSequenceNumber seqNum = cText->seq_num & RECORD_SEQ_MASK;
+ if (dtls_RecordGetRecvd(&spec->recvdRecords, seqNum) != 0) {
+ SSL_TRC(10, ("%d: SSL3[%d]: dtls_IsRelevant, rejecting "
+ "potentially replayed packet",
+ SSL_GETPID(), ss->fd));
return PR_FALSE;
}
- *seqNum = dtls_seq_num;
+ *seqNumOut = seqNum;
return PR_TRUE;
}
-/* In TLS 1.3, a client that receives a retransmission of the server's first
- * flight will reject that message and discard it (see dtls_IsRelevant() above).
- * However, we need to trigger retransmission to prevent loss of the client's
- * last flight from causing the connection to fail.
- *
- * This only triggers for a retransmitted ServerHello. Other (encrypted)
- * handshake messages do not trigger retransmission, so we are a little more
- * exposed to loss than is ideal.
- *
- * Note: This isn't an issue in earlier versions because the second-to-last
- * flight (sent by the server) includes the Finished message, which is not
- * dropped because it has the same epoch that the client currently expects.
- */
-SECStatus
-dtls_MaybeRetransmitHandshake(sslSocket *ss, const SSL3Ciphertext *cText,
- PRBool sameEpoch)
+void
+dtls_ReceivedFirstMessageInFlight(sslSocket *ss)
{
- SECStatus rv = SECSuccess;
- DTLSEpoch messageEpoch = cText->seq_num >> 48;
-
- /* Drop messages from other epochs if we are ignoring things. */
- if (!sameEpoch && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
- return SECSuccess;
- }
+ if (!IS_DTLS(ss))
+ return;
- if (!ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
- messageEpoch == 0 && cText->type == content_handshake) {
- ssl_GetSSL3HandshakeLock(ss);
- if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb &&
- ss->ssl3.hs.ws == idle_handshake) {
- rv = dtls_RetransmitDetected(ss);
+ /* At this point we are advancing our state machine, so we can free our last
+ * flight of messages. */
+ if (ss->ssl3.hs.ws != idle_handshake ||
+ ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ /* We need to keep our last flight around in DTLS 1.2 and below,
+ * so we can retransmit it in response to other people's
+ * retransmits. */
+ dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
+
+ /* Reset the timer to the initial value if the retry counter
+ * is 0, per RFC 6347, Sec. 4.2.4.1 */
+ dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
+ if (ss->ssl3.hs.rtRetries == 0) {
+ ss->ssl3.hs.rtTimer->timeout = DTLS_RETRANSMIT_INITIAL_MS;
}
- ssl_ReleaseSSL3HandshakeLock(ss);
}
- return rv;
+
+ /* Empty the ACK queue (TLS 1.3 only). */
+ ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
}
diff --git a/security/nss/lib/ssl/dtlscon.h b/security/nss/lib/ssl/dtlscon.h
new file mode 100644
index 000000000..d094380f8
--- /dev/null
+++ b/security/nss/lib/ssl/dtlscon.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __dtlscon_h_
+#define __dtlscon_h_
+
+extern void dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg);
+extern void dtls_FreeHandshakeMessages(PRCList *lst);
+SECStatus dtls_TransmitMessageFlight(sslSocket *ss);
+void dtls_InitTimers(sslSocket *ss);
+SECStatus dtls_StartTimer(sslSocket *ss, dtlsTimer *timer,
+ PRUint32 time, DTLSTimerCb cb);
+SECStatus dtls_RestartTimer(sslSocket *ss, dtlsTimer *timer);
+PRBool dtls_TimerActive(sslSocket *ss, dtlsTimer *timer);
+extern SECStatus dtls_HandleHandshake(sslSocket *ss, DTLSEpoch epoch,
+ sslSequenceNumber seqNum,
+ sslBuffer *origBuf);
+extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss,
+ PRUint8 *b, PRUint32 length);
+extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss);
+extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
+ const PRUint8 *pIn, PRInt32 nIn);
+extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
+SECStatus ssl3_DisableNonDTLSSuites(sslSocket *ss);
+extern SECStatus dtls_StartHolddownTimer(sslSocket *ss);
+extern void dtls_CheckTimer(sslSocket *ss);
+extern void dtls_CancelTimer(sslSocket *ss, dtlsTimer *timer);
+extern void dtls_SetMTU(sslSocket *ss, PRUint16 advertised);
+extern void dtls_InitRecvdRecords(DTLSRecvdRecords *records);
+extern int dtls_RecordGetRecvd(const DTLSRecvdRecords *records,
+ sslSequenceNumber seq);
+extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records,
+ sslSequenceNumber seq);
+extern void dtls_RehandshakeCleanup(sslSocket *ss);
+extern SSL3ProtocolVersion
+dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv);
+extern SSL3ProtocolVersion
+dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
+extern PRBool dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *spec,
+ const SSL3Ciphertext *cText,
+ sslSequenceNumber *seqNum);
+void dtls_ReceivedFirstMessageInFlight(sslSocket *ss);
+#endif
diff --git a/security/nss/lib/ssl/exports.gyp b/security/nss/lib/ssl/exports.gyp
index e2123af84..c3b34c6cc 100644
--- a/security/nss/lib/ssl/exports.gyp
+++ b/security/nss/lib/ssl/exports.gyp
@@ -15,6 +15,7 @@
'preenc.h',
'ssl.h',
'sslerr.h',
+ 'sslexp.h',
'sslproto.h',
'sslt.h'
],
diff --git a/security/nss/lib/ssl/manifest.mn b/security/nss/lib/ssl/manifest.mn
index fbb88baff..ca9b9ee7b 100644
--- a/security/nss/lib/ssl/manifest.mn
+++ b/security/nss/lib/ssl/manifest.mn
@@ -10,6 +10,7 @@ EXPORTS = \
ssl.h \
sslt.h \
sslerr.h \
+ sslexp.h \
sslproto.h \
preenc.h \
$(NULL)
@@ -19,13 +20,15 @@ MAPFILE = $(OBJDIR)/ssl.def
CSRCS = \
dtlscon.c \
+ dtls13con.c \
prelib.c \
ssl3con.c \
ssl3gthr.c \
sslauth.c \
+ sslbloom.c \
sslcon.c \
ssldef.c \
- ssl3encode.c \
+ sslencode.c \
sslenum.c \
sslerr.c \
sslerrstrs.c \
@@ -38,6 +41,7 @@ CSRCS = \
sslsecur.c \
sslsnce.c \
sslsock.c \
+ sslspec.c \
ssltrace.c \
sslver.c \
authcert.c \
@@ -47,7 +51,9 @@ CSRCS = \
ssl3ecc.c \
tls13con.c \
tls13exthandle.c \
+ tls13hashstate.c \
tls13hkdf.c \
+ tls13replay.c \
sslcert.c \
sslgrp.c \
$(NULL)
diff --git a/security/nss/lib/ssl/selfencrypt.c b/security/nss/lib/ssl/selfencrypt.c
index 6d6e25cfc..97217b4a6 100644
--- a/security/nss/lib/ssl/selfencrypt.c
+++ b/security/nss/lib/ssl/selfencrypt.c
@@ -11,7 +11,6 @@
#include "pk11func.h"
#include "ssl.h"
#include "sslt.h"
-#include "ssl3encode.h"
#include "sslimpl.h"
#include "selfencrypt.h"
@@ -121,12 +120,11 @@ ssl_SelfEncryptProtectInt(
PRUint8 *out, unsigned int *outLen, unsigned int maxOutLen)
{
unsigned int len;
+ unsigned int lenOffset;
unsigned char iv[AES_BLOCK_SIZE];
SECItem ivItem = { siBuffer, iv, sizeof(iv) };
- unsigned char mac[SHA256_LENGTH]; /* SHA-256 */
- unsigned int macLen;
- SECItem outItem = { siBuffer, out, maxOutLen };
- SECItem lengthBytesItem;
+ /* Write directly to out. */
+ sslBuffer buf = SSL_BUFFER_FIXED(out, maxOutLen);
SECStatus rv;
/* Generate a random IV */
@@ -137,52 +135,54 @@ ssl_SelfEncryptProtectInt(
}
/* Add header. */
- rv = ssl3_AppendToItem(&outItem, keyName, SELF_ENCRYPT_KEY_NAME_LEN);
+ rv = sslBuffer_Append(&buf, keyName, SELF_ENCRYPT_KEY_NAME_LEN);
if (rv != SECSuccess) {
return SECFailure;
}
- rv = ssl3_AppendToItem(&outItem, iv, sizeof(iv));
+ rv = sslBuffer_Append(&buf, iv, sizeof(iv));
if (rv != SECSuccess) {
return SECFailure;
}
- /* Skip forward by two so we can encode the ciphertext in place. */
- lengthBytesItem = outItem;
- rv = ssl3_AppendNumberToItem(&outItem, 0, 2);
+ /* Leave space for the length of the ciphertext. */
+ rv = sslBuffer_Skip(&buf, 2, &lenOffset);
if (rv != SECSuccess) {
return SECFailure;
}
+ /* Encode the ciphertext in place. */
rv = PK11_Encrypt(encKey, CKM_AES_CBC_PAD, &ivItem,
- outItem.data, &len, outItem.len, in, inLen);
+ SSL_BUFFER_NEXT(&buf), &len,
+ SSL_BUFFER_SPACE(&buf), in, inLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_Skip(&buf, len, NULL);
if (rv != SECSuccess) {
return SECFailure;
}
- outItem.data += len;
- outItem.len -= len;
-
- /* Now encode the ciphertext length. */
- rv = ssl3_AppendNumberToItem(&lengthBytesItem, len, 2);
+ rv = sslBuffer_InsertLength(&buf, lenOffset, 2);
if (rv != SECSuccess) {
return SECFailure;
}
- /* MAC the entire output buffer and append the MAC to the end. */
+ /* MAC the entire output buffer into the output. */
+ PORT_Assert(buf.space - buf.len >= SHA256_LENGTH);
rv = ssl_MacBuffer(macKey, CKM_SHA256_HMAC,
- out, outItem.data - out,
- mac, &macLen, sizeof(mac));
+ SSL_BUFFER_BASE(&buf), /* input */
+ SSL_BUFFER_LEN(&buf),
+ SSL_BUFFER_NEXT(&buf), &len, /* output */
+ SHA256_LENGTH);
if (rv != SECSuccess) {
return SECFailure;
}
- PORT_Assert(macLen == sizeof(mac));
-
- rv = ssl3_AppendToItem(&outItem, mac, macLen);
+ rv = sslBuffer_Skip(&buf, len, NULL);
if (rv != SECSuccess) {
return SECFailure;
}
- *outLen = outItem.data - out;
+ *outLen = SSL_BUFFER_LEN(&buf);
return SECSuccess;
}
@@ -269,6 +269,17 @@ ssl_SelfEncryptUnprotectInt(
}
#endif
+/* Predict the size of the encrypted data, including padding */
+unsigned int
+ssl_SelfEncryptGetProtectedSize(unsigned int inLen)
+{
+ return SELF_ENCRYPT_KEY_NAME_LEN +
+ AES_BLOCK_SIZE +
+ 2 +
+ ((inLen / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE + /* Padded */
+ SHA256_LENGTH;
+}
+
SECStatus
ssl_SelfEncryptProtect(
sslSocket *ss, const PRUint8 *in, unsigned int inLen,
diff --git a/security/nss/lib/ssl/selfencrypt.h b/security/nss/lib/ssl/selfencrypt.h
index 5bc8e4348..5415ac09f 100644
--- a/security/nss/lib/ssl/selfencrypt.h
+++ b/security/nss/lib/ssl/selfencrypt.h
@@ -11,6 +11,7 @@
#include "secmodt.h"
+unsigned int ssl_SelfEncryptGetProtectedSize(unsigned int inLen);
SECStatus ssl_SelfEncryptProtect(
sslSocket *ss, const PRUint8 *in, unsigned int inLen,
PRUint8 *out, unsigned int *outLen, unsigned int maxOutLen);
diff --git a/security/nss/lib/ssl/ssl.def b/security/nss/lib/ssl/ssl.def
index 94d304223..9a447dbef 100644
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -234,3 +234,9 @@ SSL_AlertSentCallback;
;+ local:
;+*;
;+};
+;+NSS_3.33 { # NSS 3.33 release
+;+ global:
+SSL_GetExperimentalAPI;
+;+ local:
+;+*;
+;+};
diff --git a/security/nss/lib/ssl/ssl.gyp b/security/nss/lib/ssl/ssl.gyp
index 03b2d6014..3694ab91a 100644
--- a/security/nss/lib/ssl/ssl.gyp
+++ b/security/nss/lib/ssl/ssl.gyp
@@ -13,18 +13,20 @@
'authcert.c',
'cmpcert.c',
'dtlscon.c',
+ 'dtls13con.c',
'prelib.c',
'selfencrypt.c',
'ssl3con.c',
'ssl3ecc.c',
- 'ssl3encode.c',
'ssl3ext.c',
'ssl3exthandle.c',
'ssl3gthr.c',
'sslauth.c',
+ 'sslbloom.c',
'sslcert.c',
'sslcon.c',
'ssldef.c',
+ 'sslencode.c',
'sslenum.c',
'sslerr.c',
'sslerrstrs.c',
@@ -37,11 +39,14 @@
'sslsecur.c',
'sslsnce.c',
'sslsock.c',
+ 'sslspec.c',
'ssltrace.c',
'sslver.c',
'tls13con.c',
'tls13exthandle.c',
+ 'tls13hashstate.c',
'tls13hkdf.c',
+ 'tls13replay.c',
],
'conditions': [
[ 'OS=="win"', {
@@ -57,14 +62,6 @@
'unix_err.c'
],
}],
- [ 'ssl_enable_zlib==1', {
- 'dependencies': [
- '<(DEPTH)/lib/zlib/zlib.gyp:nss_zlib'
- ],
- 'defines': [
- 'NSS_SSL_ENABLE_ZLIB',
- ],
- }],
[ 'fuzz_tls==1', {
'defines': [
'UNSAFE_FUZZER_MODE',
diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h
index 7e538ac1f..25aabbaa2 100644
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -107,8 +107,7 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
#define SSL_NO_LOCKS 17 /* Don't use locks for protection */
#define SSL_ENABLE_SESSION_TICKETS 18 /* Enable TLS SessionTicket */
/* extension (off by default) */
-#define SSL_ENABLE_DEFLATE 19 /* Enable TLS compression with */
- /* DEFLATE (off by default) */
+#define SSL_ENABLE_DEFLATE 19 /* (unsupported, deprecated, off) */
#define SSL_ENABLE_RENEGOTIATION 20 /* Values below (default: never) */
#define SSL_REQUIRE_SAFE_NEGOTIATION 21 /* Peer must send Signaling */
/* Cipher Suite Value (SCSV) or */
@@ -231,25 +230,46 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
* parameters.
*
* The transition between the 0-RTT and 1-RTT modes is marked by the
- * handshake callback.
+ * handshake callback. However, it is possible to force the completion
+ * of the handshake (and cause the handshake callback to be called)
+ * prior to reading all 0-RTT data using SSL_ForceHandshake(). To
+ * ensure that all early data is read before the handshake callback, any
+ * time that SSL_ForceHandshake() returns a PR_WOULD_BLOCK_ERROR, use
+ * PR_Read() to read all available data. If PR_Read() is called
+ * multiple times, this will result in the handshake completing, but the
+ * handshake callback will occur after early data has all been read.
*
* WARNING: 0-RTT data has different anti-replay and PFS properties than
- * the rest of the TLS data. See [draft-ietf-tls-tls13; Section 6.2.3]
+ * the rest of the TLS data. See [draft-ietf-tls-tls13; Section 8]
* for more details.
+ *
+ * Note: when DTLS 1.3 is in use, any 0-RTT data received after EndOfEarlyData
+ * (e.g., because of reordering) is discarded.
*/
#define SSL_ENABLE_0RTT_DATA 33
+/* Enables TLS 1.3 compatibility mode. In this mode, the client includes a fake
+ * session ID in the handshake and sends a ChangeCipherSpec. A server will
+ * always use the setting chosen by the client, so the value of this option has
+ * no effect for a server. This setting is ignored for DTLS. */
+#define SSL_ENABLE_TLS13_COMPAT_MODE 35
+
#ifdef SSL_DEPRECATED_FUNCTION
/* Old deprecated function names */
-SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on);
-SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRBool on);
+SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on);
+SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRIntn on);
#endif
-/* New function names */
-SSL_IMPORT SECStatus SSL_OptionSet(PRFileDesc *fd, PRInt32 option, PRBool on);
-SSL_IMPORT SECStatus SSL_OptionGet(PRFileDesc *fd, PRInt32 option, PRBool *on);
-SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on);
-SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRBool *on);
+/* Set (and get) options for sockets and defaults for newly created sockets.
+ *
+ * While the |val| parameter of these methods is PRIntn, options only support
+ * two values by default: PR_TRUE or PR_FALSE. The documentation of specific
+ * options will explain if other values are permitted.
+ */
+SSL_IMPORT SECStatus SSL_OptionSet(PRFileDesc *fd, PRInt32 option, PRIntn val);
+SSL_IMPORT SECStatus SSL_OptionGet(PRFileDesc *fd, PRInt32 option, PRIntn *val);
+SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRIntn val);
+SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRIntn *val);
SSL_IMPORT SECStatus SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle);
/* SSLNextProtoCallback is called during the handshake for the client, when a
@@ -1374,6 +1394,13 @@ extern const char *NSSSSL_GetVersion(void);
*/
SSL_IMPORT SECStatus SSL_AuthCertificateComplete(PRFileDesc *fd,
PRErrorCode error);
+
+/*
+ * This is used to access experimental APIs. Don't call this directly. This is
+ * used to enable the experimental APIs that are defined in "sslexp.h".
+ */
+SSL_IMPORT void *SSL_GetExperimentalAPI(const char *name);
+
SEC_END_PROTOS
#endif /* __ssl_h_ */
diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c
index 5cbe2bd09..61878ae99 100644
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -34,14 +34,13 @@
#include "blapi.h"
#include <stdio.h>
-#ifdef NSS_SSL_ENABLE_ZLIB
-#include "zlib.h"
-#endif
static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
PK11SlotInfo *serverKeySlot);
-static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
-static SECStatus ssl3_DeriveConnectionKeys(sslSocket *ss);
+static SECStatus ssl3_ComputeMasterSecret(sslSocket *ss, PK11SymKey *pms,
+ PK11SymKey **msp);
+static SECStatus ssl3_DeriveConnectionKeys(sslSocket *ss,
+ PK11SymKey *masterSecret);
static SECStatus ssl3_HandshakeFailure(sslSocket *ss);
static SECStatus ssl3_SendCertificate(sslSocket *ss);
static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
@@ -51,27 +50,28 @@ static SECStatus ssl3_SendServerHelloDone(sslSocket *ss);
static SECStatus ssl3_SendServerKeyExchange(sslSocket *ss);
static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss,
SECItem *suites,
- SECItem *comps,
- sslSessionID *sid);
+ sslSessionID *sid,
+ const PRUint8 *msg,
+ unsigned int len);
static SECStatus ssl3_HandleServerHelloPart2(sslSocket *ss,
const SECItem *sidBytes,
int *retErrCode);
static SECStatus ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss,
PRUint8 *b,
- PRUint32 length,
- SSL3Hashes *hashesPtr);
+ PRUint32 length);
static SECStatus ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
-static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
- int maxOutputLen, const unsigned char *input,
- int inputLen);
-
static CK_MECHANISM_TYPE ssl3_GetHashMechanismByHashType(SSLHashType hashType);
static CK_MECHANISM_TYPE ssl3_GetMgfMechanismByHashType(SSLHashType hash);
PRBool ssl_IsRsaPssSignatureScheme(SSLSignatureScheme scheme);
-#define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
-#define MIN_SEND_BUF_LENGTH 4000
+const PRUint8 ssl_hello_retry_random[] = {
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
+ 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
+ 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
+ 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C
+};
+PR_STATIC_ASSERT(PR_ARRAY_SIZE(ssl_hello_retry_random) == SSL3_RANDOM_LENGTH);
/* This list of SSL3 cipher suites is sorted in descending order of
* precedence (desirability). It only includes cipher suites we implement.
@@ -214,52 +214,6 @@ ssl3_CheckCipherSuiteOrderConsistency()
}
#endif
-/* This list of SSL3 compression methods is sorted in descending order of
- * precedence (desirability). It only includes compression methods we
- * implement.
- */
-static const SSLCompressionMethod ssl_compression_methods[] = {
-#ifdef NSS_SSL_ENABLE_ZLIB
- ssl_compression_deflate,
-#endif
- ssl_compression_null
-};
-
-static const unsigned int ssl_compression_method_count =
- PR_ARRAY_SIZE(ssl_compression_methods);
-
-/* compressionEnabled returns true iff the compression algorithm is enabled
- * for the given SSL socket. */
-static PRBool
-ssl_CompressionEnabled(sslSocket *ss, SSLCompressionMethod compression)
-{
- SSL3ProtocolVersion version;
-
- if (compression == ssl_compression_null) {
- return PR_TRUE; /* Always enabled */
- }
- if (ss->sec.isServer) {
- /* We can't easily check that the client didn't attempt TLS 1.3,
- * so this will have to do. */
- PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
- version = ss->version;
- } else {
- version = ss->vrange.max;
- }
- if (version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- return PR_FALSE;
- }
-#ifdef NSS_SSL_ENABLE_ZLIB
- if (compression == ssl_compression_deflate) {
- if (IS_DTLS(ss)) {
- return PR_FALSE;
- }
- return ss->opt.enableDeflate;
- }
-#endif
- return PR_FALSE;
-}
-
static const /*SSL3ClientCertificateType */ PRUint8 certificate_types[] = {
ct_RSA_sign,
ct_ECDSA_sign,
@@ -268,173 +222,125 @@ static const /*SSL3ClientCertificateType */ PRUint8 certificate_types[] = {
static SSL3Statistics ssl3stats;
-/* Record protection algorithms, indexed by SSL3BulkCipher.
- *
- * The |max_records| field (|mr| below) is set to a number that is higher than
- * recommended in some literature (esp. TLS 1.3) because we currently abort the
- * connection when this limit is reached and we want to ensure that we only
- * rarely hit this limit. See bug 1268745 for details.
- */
-#define MR_MAX RECORD_SEQ_MAX /* 2^48-1 */
-#define MR_128 (0x5aULL << 28) /* For AES and similar. */
-#define MR_LOW (1ULL << 20) /* For weak ciphers. */
-/* clang-format off */
-static const ssl3BulkCipherDef bulk_cipher_defs[] = {
- /* |--------- Lengths ---------| */
- /* cipher calg : s : */
- /* : e b n */
- /* oid short_name mr : l o */
- /* k r o t n */
- /* e e i c a c */
- /* y t type v k g e */
- {cipher_null, calg_null, 0, 0, type_stream, 0, 0, 0, 0,
- SEC_OID_NULL_CIPHER, "NULL", MR_MAX},
- {cipher_rc4, calg_rc4, 16,16, type_stream, 0, 0, 0, 0,
- SEC_OID_RC4, "RC4", MR_LOW},
- {cipher_des, calg_des, 8, 8, type_block, 8, 8, 0, 0,
- SEC_OID_DES_CBC, "DES-CBC", MR_LOW},
- {cipher_3des, calg_3des, 24,24, type_block, 8, 8, 0, 0,
- SEC_OID_DES_EDE3_CBC, "3DES-EDE-CBC", MR_LOW},
- {cipher_aes_128, calg_aes, 16,16, type_block, 16,16, 0, 0,
- SEC_OID_AES_128_CBC, "AES-128", MR_128},
- {cipher_aes_256, calg_aes, 32,32, type_block, 16,16, 0, 0,
- SEC_OID_AES_256_CBC, "AES-256", MR_128},
- {cipher_camellia_128, calg_camellia, 16,16, type_block, 16,16, 0, 0,
- SEC_OID_CAMELLIA_128_CBC, "Camellia-128", MR_128},
- {cipher_camellia_256, calg_camellia, 32,32, type_block, 16,16, 0, 0,
- SEC_OID_CAMELLIA_256_CBC, "Camellia-256", MR_128},
- {cipher_seed, calg_seed, 16,16, type_block, 16,16, 0, 0,
- SEC_OID_SEED_CBC, "SEED-CBC", MR_128},
- {cipher_aes_128_gcm, calg_aes_gcm, 16,16, type_aead, 4, 0,16, 8,
- SEC_OID_AES_128_GCM, "AES-128-GCM", MR_128},
- {cipher_aes_256_gcm, calg_aes_gcm, 32,32, type_aead, 4, 0,16, 8,
- SEC_OID_AES_256_GCM, "AES-256-GCM", MR_128},
- {cipher_chacha20, calg_chacha20, 32,32, type_aead, 12, 0,16, 0,
- SEC_OID_CHACHA20_POLY1305, "ChaCha20-Poly1305", MR_MAX},
- {cipher_missing, calg_null, 0, 0, type_stream, 0, 0, 0, 0,
- SEC_OID_UNKNOWN, "missing", 0U},
-};
-
static const ssl3KEADef kea_defs[] =
-{ /* indexed by SSL3KeyExchangeAlgorithm */
- /* kea exchKeyType signKeyType authKeyType ephemeral oid */
- {kea_null, ssl_kea_null, nullKey, ssl_auth_null, PR_FALSE, 0},
- {kea_rsa, ssl_kea_rsa, nullKey, ssl_auth_rsa_decrypt, PR_FALSE, SEC_OID_TLS_RSA},
- {kea_dh_dss, ssl_kea_dh, dsaKey, ssl_auth_dsa, PR_FALSE, SEC_OID_TLS_DH_DSS},
- {kea_dh_rsa, ssl_kea_dh, rsaKey, ssl_auth_rsa_sign, PR_FALSE, SEC_OID_TLS_DH_RSA},
- {kea_dhe_dss, ssl_kea_dh, dsaKey, ssl_auth_dsa, PR_TRUE, SEC_OID_TLS_DHE_DSS},
- {kea_dhe_rsa, ssl_kea_dh, rsaKey, ssl_auth_rsa_sign, PR_TRUE, SEC_OID_TLS_DHE_RSA},
- {kea_dh_anon, ssl_kea_dh, nullKey, ssl_auth_null, PR_TRUE, SEC_OID_TLS_DH_ANON},
- {kea_ecdh_ecdsa, ssl_kea_ecdh, nullKey, ssl_auth_ecdh_ecdsa, PR_FALSE, SEC_OID_TLS_ECDH_ECDSA},
- {kea_ecdhe_ecdsa, ssl_kea_ecdh, ecKey, ssl_auth_ecdsa, PR_TRUE, SEC_OID_TLS_ECDHE_ECDSA},
- {kea_ecdh_rsa, ssl_kea_ecdh, nullKey, ssl_auth_ecdh_rsa, PR_FALSE, SEC_OID_TLS_ECDH_RSA},
- {kea_ecdhe_rsa, ssl_kea_ecdh, rsaKey, ssl_auth_rsa_sign, PR_TRUE, SEC_OID_TLS_ECDHE_RSA},
- {kea_ecdh_anon, ssl_kea_ecdh, nullKey, ssl_auth_null, PR_TRUE, SEC_OID_TLS_ECDH_ANON},
- {kea_ecdhe_psk, ssl_kea_ecdh_psk, nullKey, ssl_auth_psk, PR_TRUE, SEC_OID_TLS_ECDHE_PSK},
- {kea_dhe_psk, ssl_kea_dh_psk, nullKey, ssl_auth_psk, PR_TRUE, SEC_OID_TLS_DHE_PSK},
- {kea_tls13_any, ssl_kea_tls13_any, nullKey, ssl_auth_tls13_any, PR_TRUE, SEC_OID_TLS13_KEA_ANY},
-};
+ {
+ /* indexed by SSL3KeyExchangeAlgorithm */
+ /* kea exchKeyType signKeyType authKeyType ephemeral oid */
+ { kea_null, ssl_kea_null, nullKey, ssl_auth_null, PR_FALSE, 0 },
+ { kea_rsa, ssl_kea_rsa, nullKey, ssl_auth_rsa_decrypt, PR_FALSE, SEC_OID_TLS_RSA },
+ { kea_dh_dss, ssl_kea_dh, dsaKey, ssl_auth_dsa, PR_FALSE, SEC_OID_TLS_DH_DSS },
+ { kea_dh_rsa, ssl_kea_dh, rsaKey, ssl_auth_rsa_sign, PR_FALSE, SEC_OID_TLS_DH_RSA },
+ { kea_dhe_dss, ssl_kea_dh, dsaKey, ssl_auth_dsa, PR_TRUE, SEC_OID_TLS_DHE_DSS },
+ { kea_dhe_rsa, ssl_kea_dh, rsaKey, ssl_auth_rsa_sign, PR_TRUE, SEC_OID_TLS_DHE_RSA },
+ { kea_dh_anon, ssl_kea_dh, nullKey, ssl_auth_null, PR_TRUE, SEC_OID_TLS_DH_ANON },
+ { kea_ecdh_ecdsa, ssl_kea_ecdh, nullKey, ssl_auth_ecdh_ecdsa, PR_FALSE, SEC_OID_TLS_ECDH_ECDSA },
+ { kea_ecdhe_ecdsa, ssl_kea_ecdh, ecKey, ssl_auth_ecdsa, PR_TRUE, SEC_OID_TLS_ECDHE_ECDSA },
+ { kea_ecdh_rsa, ssl_kea_ecdh, nullKey, ssl_auth_ecdh_rsa, PR_FALSE, SEC_OID_TLS_ECDH_RSA },
+ { kea_ecdhe_rsa, ssl_kea_ecdh, rsaKey, ssl_auth_rsa_sign, PR_TRUE, SEC_OID_TLS_ECDHE_RSA },
+ { kea_ecdh_anon, ssl_kea_ecdh, nullKey, ssl_auth_null, PR_TRUE, SEC_OID_TLS_ECDH_ANON },
+ { kea_ecdhe_psk, ssl_kea_ecdh_psk, nullKey, ssl_auth_psk, PR_TRUE, SEC_OID_TLS_ECDHE_PSK },
+ { kea_dhe_psk, ssl_kea_dh_psk, nullKey, ssl_auth_psk, PR_TRUE, SEC_OID_TLS_DHE_PSK },
+ { kea_tls13_any, ssl_kea_tls13_any, nullKey, ssl_auth_tls13_any, PR_TRUE, SEC_OID_TLS13_KEA_ANY },
+ };
/* must use ssl_LookupCipherSuiteDef to access */
static const ssl3CipherSuiteDef cipher_suite_defs[] =
-{
-/* cipher_suite bulk_cipher_alg mac_alg key_exchange_alg prf_hash */
-/* Note that the prf_hash_alg is the hash function used by the PRF, see sslimpl.h. */
-
- {TLS_NULL_WITH_NULL_NULL, cipher_null, mac_null, kea_null, ssl_hash_none},
- {TLS_RSA_WITH_NULL_MD5, cipher_null, mac_md5, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_NULL_SHA256, cipher_null, hmac_sha256, kea_rsa, ssl_hash_sha256},
- {TLS_RSA_WITH_RC4_128_MD5, cipher_rc4, mac_md5, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_DES_CBC_SHA, cipher_des, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_DHE_DSS_WITH_DES_CBC_SHA, cipher_des, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
- cipher_3des, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_DSS_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_RSA_WITH_DES_CBC_SHA, cipher_des, mac_sha, kea_dhe_rsa, ssl_hash_none},
- {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- cipher_3des, mac_sha, kea_dhe_rsa, ssl_hash_none},
-
-
-/* New TLS cipher suites */
- {TLS_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_rsa, ssl_hash_sha256},
- {TLS_DHE_DSS_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_dhe_rsa, ssl_hash_none},
- {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_dhe_rsa, ssl_hash_sha256},
- {TLS_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_RSA_WITH_AES_256_CBC_SHA256, cipher_aes_256, hmac_sha256, kea_rsa, ssl_hash_sha256},
- {TLS_DHE_DSS_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_dhe_rsa, ssl_hash_none},
- {TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, cipher_aes_256, hmac_sha256, kea_dhe_rsa, ssl_hash_sha256},
- {TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_dhe_rsa, ssl_hash_sha384},
-
- {TLS_RSA_WITH_SEED_CBC_SHA, cipher_seed, mac_sha, kea_rsa, ssl_hash_none},
-
- {TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, cipher_camellia_128, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
- cipher_camellia_128, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
- cipher_camellia_128, mac_sha, kea_dhe_rsa, ssl_hash_none},
- {TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, cipher_camellia_256, mac_sha, kea_rsa, ssl_hash_none},
- {TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
- cipher_camellia_256, mac_sha, kea_dhe_dss, ssl_hash_none},
- {TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
- cipher_camellia_256, mac_sha, kea_dhe_rsa, ssl_hash_none},
-
- {TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_dhe_rsa, ssl_hash_sha256},
- {TLS_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_rsa, ssl_hash_sha256},
-
- {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_rsa, ssl_hash_sha256},
- {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_ecdsa, ssl_hash_sha256},
- {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_ecdhe_ecdsa, ssl_hash_sha384},
- {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_ecdhe_rsa, ssl_hash_sha384},
- {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, cipher_aes_256, hmac_sha384, kea_ecdhe_ecdsa, ssl_hash_sha384},
- {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, cipher_aes_256, hmac_sha384, kea_ecdhe_rsa, ssl_hash_sha384},
- {TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_dhe_dss, ssl_hash_sha256},
- {TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_dhe_dss, ssl_hash_sha256},
- {TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, cipher_aes_256, hmac_sha256, kea_dhe_dss, ssl_hash_sha256},
- {TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_dhe_dss, ssl_hash_sha384},
- {TLS_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_rsa, ssl_hash_sha384},
-
- {TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, cipher_chacha20, mac_aead, kea_dhe_rsa, ssl_hash_sha256},
-
- {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, cipher_chacha20, mac_aead, kea_ecdhe_rsa, ssl_hash_sha256},
- {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, cipher_chacha20, mac_aead, kea_ecdhe_ecdsa, ssl_hash_sha256},
-
- {TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdh_ecdsa, ssl_hash_none},
- {TLS_ECDH_ECDSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_ecdh_ecdsa, ssl_hash_none},
- {TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_ecdh_ecdsa, ssl_hash_none},
- {TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_ecdh_ecdsa, ssl_hash_none},
- {TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_ecdh_ecdsa, ssl_hash_none},
-
- {TLS_ECDHE_ECDSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdhe_ecdsa, ssl_hash_none},
- {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_ecdhe_ecdsa, ssl_hash_none},
- {TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_ecdhe_ecdsa, ssl_hash_none},
- {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_ecdhe_ecdsa, ssl_hash_none},
- {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_ecdhe_ecdsa, ssl_hash_sha256},
- {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_ecdhe_ecdsa, ssl_hash_none},
-
- {TLS_ECDH_RSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdh_rsa, ssl_hash_none},
- {TLS_ECDH_RSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_ecdh_rsa, ssl_hash_none},
- {TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_ecdh_rsa, ssl_hash_none},
- {TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_ecdh_rsa, ssl_hash_none},
- {TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_ecdh_rsa, ssl_hash_none},
-
- {TLS_ECDHE_RSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdhe_rsa, ssl_hash_none},
- {TLS_ECDHE_RSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_ecdhe_rsa, ssl_hash_none},
- {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_ecdhe_rsa, ssl_hash_none},
- {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_ecdhe_rsa, ssl_hash_none},
- {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_ecdhe_rsa, ssl_hash_sha256},
- {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_ecdhe_rsa, ssl_hash_none},
-
- {TLS_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_tls13_any, ssl_hash_sha256},
- {TLS_CHACHA20_POLY1305_SHA256, cipher_chacha20, mac_aead, kea_tls13_any, ssl_hash_sha256},
- {TLS_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_tls13_any, ssl_hash_sha384},
-};
-/* clang-format on */
+ {
+ /* cipher_suite bulk_cipher_alg mac_alg key_exchange_alg prf_hash */
+ /* Note that the prf_hash_alg is the hash function used by the PRF, see sslimpl.h. */
+
+ { TLS_NULL_WITH_NULL_NULL, cipher_null, ssl_mac_null, kea_null, ssl_hash_none },
+ { TLS_RSA_WITH_NULL_MD5, cipher_null, ssl_mac_md5, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_NULL_SHA, cipher_null, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_NULL_SHA256, cipher_null, ssl_hmac_sha256, kea_rsa, ssl_hash_sha256 },
+ { TLS_RSA_WITH_RC4_128_MD5, cipher_rc4, ssl_mac_md5, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_RC4_128_SHA, cipher_rc4, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_DES_CBC_SHA, cipher_des, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_DHE_DSS_WITH_DES_CBC_SHA, cipher_des, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ cipher_3des, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_DSS_WITH_RC4_128_SHA, cipher_rc4, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_DES_CBC_SHA, cipher_des, ssl_mac_sha, kea_dhe_rsa, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ cipher_3des, ssl_mac_sha, kea_dhe_rsa, ssl_hash_none },
+
+ /* New TLS cipher suites */
+ { TLS_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, ssl_hmac_sha256, kea_rsa, ssl_hash_sha256 },
+ { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_dhe_rsa, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, ssl_hmac_sha256, kea_dhe_rsa, ssl_hash_sha256 },
+ { TLS_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_AES_256_CBC_SHA256, cipher_aes_256, ssl_hmac_sha256, kea_rsa, ssl_hash_sha256 },
+ { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_dhe_rsa, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, cipher_aes_256, ssl_hmac_sha256, kea_dhe_rsa, ssl_hash_sha256 },
+ { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, ssl_mac_aead, kea_dhe_rsa, ssl_hash_sha384 },
+
+ { TLS_RSA_WITH_SEED_CBC_SHA, cipher_seed, ssl_mac_sha, kea_rsa, ssl_hash_none },
+
+ { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, cipher_camellia_128, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
+ cipher_camellia_128, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
+ cipher_camellia_128, ssl_mac_sha, kea_dhe_rsa, ssl_hash_none },
+ { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, cipher_camellia_256, ssl_mac_sha, kea_rsa, ssl_hash_none },
+ { TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
+ cipher_camellia_256, ssl_mac_sha, kea_dhe_dss, ssl_hash_none },
+ { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
+ cipher_camellia_256, ssl_mac_sha, kea_dhe_rsa, ssl_hash_none },
+
+ { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, ssl_mac_aead, kea_dhe_rsa, ssl_hash_sha256 },
+ { TLS_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, ssl_mac_aead, kea_rsa, ssl_hash_sha256 },
+
+ { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, ssl_mac_aead, kea_ecdhe_rsa, ssl_hash_sha256 },
+ { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, ssl_mac_aead, kea_ecdhe_ecdsa, ssl_hash_sha256 },
+ { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, ssl_mac_aead, kea_ecdhe_ecdsa, ssl_hash_sha384 },
+ { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, ssl_mac_aead, kea_ecdhe_rsa, ssl_hash_sha384 },
+ { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, cipher_aes_256, ssl_hmac_sha384, kea_ecdhe_ecdsa, ssl_hash_sha384 },
+ { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, cipher_aes_256, ssl_hmac_sha384, kea_ecdhe_rsa, ssl_hash_sha384 },
+ { TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, ssl_mac_aead, kea_dhe_dss, ssl_hash_sha256 },
+ { TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, cipher_aes_128, ssl_hmac_sha256, kea_dhe_dss, ssl_hash_sha256 },
+ { TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, cipher_aes_256, ssl_hmac_sha256, kea_dhe_dss, ssl_hash_sha256 },
+ { TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, ssl_mac_aead, kea_dhe_dss, ssl_hash_sha384 },
+ { TLS_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, ssl_mac_aead, kea_rsa, ssl_hash_sha384 },
+
+ { TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, cipher_chacha20, ssl_mac_aead, kea_dhe_rsa, ssl_hash_sha256 },
+
+ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, cipher_chacha20, ssl_mac_aead, kea_ecdhe_rsa, ssl_hash_sha256 },
+ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, cipher_chacha20, ssl_mac_aead, kea_ecdhe_ecdsa, ssl_hash_sha256 },
+
+ { TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_null, ssl_mac_sha, kea_ecdh_ecdsa, ssl_hash_none },
+ { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, cipher_rc4, ssl_mac_sha, kea_ecdh_ecdsa, ssl_hash_none },
+ { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, ssl_mac_sha, kea_ecdh_ecdsa, ssl_hash_none },
+ { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_ecdh_ecdsa, ssl_hash_none },
+ { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_ecdh_ecdsa, ssl_hash_none },
+
+ { TLS_ECDHE_ECDSA_WITH_NULL_SHA, cipher_null, ssl_mac_sha, kea_ecdhe_ecdsa, ssl_hash_none },
+ { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, cipher_rc4, ssl_mac_sha, kea_ecdhe_ecdsa, ssl_hash_none },
+ { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, ssl_mac_sha, kea_ecdhe_ecdsa, ssl_hash_none },
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_ecdhe_ecdsa, ssl_hash_none },
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, ssl_hmac_sha256, kea_ecdhe_ecdsa, ssl_hash_sha256 },
+ { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_ecdhe_ecdsa, ssl_hash_none },
+
+ { TLS_ECDH_RSA_WITH_NULL_SHA, cipher_null, ssl_mac_sha, kea_ecdh_rsa, ssl_hash_none },
+ { TLS_ECDH_RSA_WITH_RC4_128_SHA, cipher_rc4, ssl_mac_sha, kea_ecdh_rsa, ssl_hash_none },
+ { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, ssl_mac_sha, kea_ecdh_rsa, ssl_hash_none },
+ { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_ecdh_rsa, ssl_hash_none },
+ { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_ecdh_rsa, ssl_hash_none },
+
+ { TLS_ECDHE_RSA_WITH_NULL_SHA, cipher_null, ssl_mac_sha, kea_ecdhe_rsa, ssl_hash_none },
+ { TLS_ECDHE_RSA_WITH_RC4_128_SHA, cipher_rc4, ssl_mac_sha, kea_ecdhe_rsa, ssl_hash_none },
+ { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, ssl_mac_sha, kea_ecdhe_rsa, ssl_hash_none },
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, ssl_mac_sha, kea_ecdhe_rsa, ssl_hash_none },
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, ssl_hmac_sha256, kea_ecdhe_rsa, ssl_hash_sha256 },
+ { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, cipher_aes_256, ssl_mac_sha, kea_ecdhe_rsa, ssl_hash_none },
+
+ { TLS_AES_128_GCM_SHA256, cipher_aes_128_gcm, ssl_mac_aead, kea_tls13_any, ssl_hash_sha256 },
+ { TLS_CHACHA20_POLY1305_SHA256, cipher_chacha20, ssl_mac_aead, kea_tls13_any, ssl_hash_sha256 },
+ { TLS_AES_256_GCM_SHA384, cipher_aes_256_gcm, ssl_mac_aead, kea_tls13_any, ssl_hash_sha384 },
+ };
static const CK_MECHANISM_TYPE auth_alg_defs[] = {
CKM_INVALID_MECHANISM, /* ssl_auth_null */
@@ -471,44 +377,20 @@ typedef struct SSLCipher2MechStr {
/* indexed by type SSLCipherAlgorithm */
static const SSLCipher2Mech alg2Mech[] = {
/* calg, cmech */
- { calg_null, (CK_MECHANISM_TYPE)0x80000000L },
- { calg_rc4, CKM_RC4 },
- { calg_rc2, CKM_RC2_CBC },
- { calg_des, CKM_DES_CBC },
- { calg_3des, CKM_DES3_CBC },
- { calg_idea, CKM_IDEA_CBC },
- { calg_fortezza, CKM_SKIPJACK_CBC64 },
- { calg_aes, CKM_AES_CBC },
- { calg_camellia, CKM_CAMELLIA_CBC },
- { calg_seed, CKM_SEED_CBC },
- { calg_aes_gcm, CKM_AES_GCM },
- { calg_chacha20, CKM_NSS_CHACHA20_POLY1305 },
- /* { calg_init , (CK_MECHANISM_TYPE)0x7fffffffL } */
+ { ssl_calg_null, CKM_INVALID_MECHANISM },
+ { ssl_calg_rc4, CKM_RC4 },
+ { ssl_calg_rc2, CKM_RC2_CBC },
+ { ssl_calg_des, CKM_DES_CBC },
+ { ssl_calg_3des, CKM_DES3_CBC },
+ { ssl_calg_idea, CKM_IDEA_CBC },
+ { ssl_calg_fortezza, CKM_SKIPJACK_CBC64 },
+ { ssl_calg_aes, CKM_AES_CBC },
+ { ssl_calg_camellia, CKM_CAMELLIA_CBC },
+ { ssl_calg_seed, CKM_SEED_CBC },
+ { ssl_calg_aes_gcm, CKM_AES_GCM },
+ { ssl_calg_chacha20, CKM_NSS_CHACHA20_POLY1305 },
};
-#define mmech_invalid (CK_MECHANISM_TYPE)0x80000000L
-#define mmech_md5 CKM_SSL3_MD5_MAC
-#define mmech_sha CKM_SSL3_SHA1_MAC
-#define mmech_md5_hmac CKM_MD5_HMAC
-#define mmech_sha_hmac CKM_SHA_1_HMAC
-#define mmech_sha256_hmac CKM_SHA256_HMAC
-#define mmech_sha384_hmac CKM_SHA384_HMAC
-
-/* clang-format off */
-static const ssl3MACDef mac_defs[] = { /* indexed by SSL3MACAlgorithm */
- /* pad_size is only used for SSL 3.0 MAC. See RFC 6101 Sec. 5.2.3.1. */
- /* mac mmech pad_size mac_size */
- { mac_null, mmech_invalid, 0, 0 , 0},
- { mac_md5, mmech_md5, 48, MD5_LENGTH, SEC_OID_HMAC_MD5 },
- { mac_sha, mmech_sha, 40, SHA1_LENGTH, SEC_OID_HMAC_SHA1},
- {hmac_md5, mmech_md5_hmac, 0, MD5_LENGTH, SEC_OID_HMAC_MD5},
- {hmac_sha, mmech_sha_hmac, 0, SHA1_LENGTH, SEC_OID_HMAC_SHA1},
- {hmac_sha256, mmech_sha256_hmac, 0, SHA256_LENGTH, SEC_OID_HMAC_SHA256},
- { mac_aead, mmech_invalid, 0, 0, 0 },
- {hmac_sha384, mmech_sha384_hmac, 0, SHA384_LENGTH, SEC_OID_HMAC_SHA384}
-};
-/* clang-format on */
-
const PRUint8 tls13_downgrade_random[] = { 0x44, 0x4F, 0x57, 0x4E,
0x47, 0x52, 0x44, 0x01 };
const PRUint8 tls12_downgrade_random[] = { 0x44, 0x4F, 0x57, 0x4E,
@@ -554,48 +436,57 @@ ssl3_DecodeHandshakeType(int msgType)
static char line[40];
switch (msgType) {
- case hello_request:
+ case ssl_hs_hello_request:
rv = "hello_request (0)";
break;
- case client_hello:
+ case ssl_hs_client_hello:
rv = "client_hello (1)";
break;
- case server_hello:
+ case ssl_hs_server_hello:
rv = "server_hello (2)";
break;
- case hello_verify_request:
+ case ssl_hs_hello_verify_request:
rv = "hello_verify_request (3)";
break;
- case new_session_ticket:
- rv = "session_ticket (4)";
+ case ssl_hs_new_session_ticket:
+ rv = "new_session_ticket (4)";
+ break;
+ case ssl_hs_end_of_early_data:
+ rv = "end_of_early_data (5)";
break;
- case hello_retry_request:
+ case ssl_hs_hello_retry_request:
rv = "hello_retry_request (6)";
break;
- case encrypted_extensions:
+ case ssl_hs_encrypted_extensions:
rv = "encrypted_extensions (8)";
break;
- case certificate:
+ case ssl_hs_certificate:
rv = "certificate (11)";
break;
- case server_key_exchange:
+ case ssl_hs_server_key_exchange:
rv = "server_key_exchange (12)";
break;
- case certificate_request:
+ case ssl_hs_certificate_request:
rv = "certificate_request (13)";
break;
- case server_hello_done:
+ case ssl_hs_server_hello_done:
rv = "server_hello_done (14)";
break;
- case certificate_verify:
+ case ssl_hs_certificate_verify:
rv = "certificate_verify (15)";
break;
- case client_key_exchange:
+ case ssl_hs_client_key_exchange:
rv = "client_key_exchange (16)";
break;
- case finished:
+ case ssl_hs_finished:
rv = "finished (20)";
break;
+ case ssl_hs_certificate_status:
+ rv = "certificate_status (22)";
+ break;
+ case ssl_hs_key_update:
+ rv = "key_update (24)";
+ break;
default:
sprintf(line, "*UNKNOWN* handshake type! (%d)", msgType);
rv = line;
@@ -622,6 +513,9 @@ ssl3_DecodeContentType(int msgType)
case content_application_data:
rv = "application_data (23)";
break;
+ case content_ack:
+ rv = "ack (25)";
+ break;
default:
sprintf(line, "*UNKNOWN* record type! (%d)", msgType);
rv = line;
@@ -874,20 +768,12 @@ ssl_HasCert(const sslSocket *ss, SSLAuthType authType)
return PR_FALSE;
}
-const ssl3BulkCipherDef *
-ssl_GetBulkCipherDef(const ssl3CipherSuiteDef *cipher_def)
-{
- PORT_Assert(cipher_def->bulk_cipher_alg < PR_ARRAY_SIZE(bulk_cipher_defs));
- PORT_Assert(bulk_cipher_defs[cipher_def->bulk_cipher_alg].cipher == cipher_def->bulk_cipher_alg);
- return &bulk_cipher_defs[cipher_def->bulk_cipher_alg];
-}
-
/* Initialize the suite->isPresent value for config_match
* Returns count of enabled ciphers supported by extant tokens,
* regardless of policy or user preference.
* If this returns zero, the user cannot do SSL v3.
*/
-int
+unsigned int
ssl3_config_match_init(sslSocket *ss)
{
ssl3CipherSuiteCfg *suite;
@@ -896,9 +782,9 @@ ssl3_config_match_init(sslSocket *ss)
CK_MECHANISM_TYPE cipher_mech;
SSLAuthType authType;
SSLKEAType keaType;
- int i;
- int numPresent = 0;
- int numEnabled = 0;
+ unsigned int i;
+ unsigned int numPresent = 0;
+ unsigned int numEnabled = 0;
PORT_Assert(ss);
if (!ss) {
@@ -909,6 +795,7 @@ ssl3_config_match_init(sslSocket *ss)
return 0;
}
+ ssl_FilterSupportedGroups(ss);
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
suite = &ss->cipherSuites[i];
if (suite->enabled) {
@@ -944,7 +831,7 @@ ssl3_config_match_init(sslSocket *ss)
suite->isPresent = PR_FALSE;
}
- if (cipher_alg != calg_null &&
+ if (cipher_alg != ssl_calg_null &&
!PK11_TokenExists(cipher_mech)) {
suite->isPresent = PR_FALSE;
}
@@ -955,7 +842,7 @@ ssl3_config_match_init(sslSocket *ss)
}
}
PORT_Assert(numPresent > 0 || numEnabled == 0);
- if (numPresent <= 0) {
+ if (numPresent == 0) {
PORT_SetError(SSL_ERROR_NO_CIPHERS_SUPPORTED);
}
return numPresent;
@@ -1000,10 +887,10 @@ config_match(const ssl3CipherSuiteCfg *suite, int policy,
/* Return the number of cipher suites that are usable. */
/* called from ssl3_SendClientHello */
-static int
+static unsigned int
count_cipher_suites(sslSocket *ss, int policy)
{
- int i, count = 0;
+ unsigned int i, count = 0;
if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange)) {
return 0;
@@ -1012,7 +899,7 @@ count_cipher_suites(sslSocket *ss, int policy)
if (config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss))
count++;
}
- if (count <= 0) {
+ if (count == 0) {
PORT_SetError(SSL_ERROR_SSL_DISABLED);
}
return count;
@@ -1021,7 +908,7 @@ count_cipher_suites(sslSocket *ss, int policy)
/*
* Null compression, mac and encryption functions
*/
-static SECStatus
+SECStatus
Null_Cipher(void *ctx, unsigned char *output, int *outputLen, int maxOutputLen,
const unsigned char *input, int inputLen)
{
@@ -1041,6 +928,19 @@ Null_Cipher(void *ctx, unsigned char *output, int *outputLen, int maxOutputLen,
* SSL3 Utility functions
*/
+static void
+ssl_SetSpecVersions(sslSocket *ss, ssl3CipherSpec *spec)
+{
+ spec->version = ss->version;
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ tls13_SetSpecRecordVersion(ss, spec);
+ } else if (IS_DTLS(ss)) {
+ spec->recordVersion = dtls_TLSVersionToDTLSVersion(ss->version);
+ } else {
+ spec->recordVersion = ss->version;
+ }
+}
+
/* allowLargerPeerVersion controls whether the function will select the
* highest enabled SSL version or fail when peerVersion is greater than the
* highest enabled version.
@@ -1052,6 +952,8 @@ SECStatus
ssl3_NegotiateVersion(sslSocket *ss, SSL3ProtocolVersion peerVersion,
PRBool allowLargerPeerVersion)
{
+ SSL3ProtocolVersion negotiated;
+
if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange)) {
PORT_SetError(SSL_ERROR_SSL_DISABLED);
return SECFailure;
@@ -1063,9 +965,14 @@ ssl3_NegotiateVersion(sslSocket *ss, SSL3ProtocolVersion peerVersion,
return SECFailure;
}
- ss->version = PR_MIN(peerVersion, ss->vrange.max);
- PORT_Assert(ssl3_VersionIsSupported(ss->protocolVariant, ss->version));
+ negotiated = PR_MIN(peerVersion, ss->vrange.max);
+ PORT_Assert(ssl3_VersionIsSupported(ss->protocolVariant, negotiated));
+ if (ss->firstHsDone && ss->version != negotiated) {
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
+ return SECFailure;
+ }
+ ss->version = negotiated;
return SECSuccess;
}
@@ -1104,24 +1011,16 @@ ssl_ClientReadVersion(sslSocket *ss, PRUint8 **b, unsigned int *len,
v = dtls_DTLSVersionToTLSVersion(v);
}
- PORT_Assert(!SSL_ALL_VERSIONS_DISABLED(&ss->vrange));
- if (ss->vrange.min > v || ss->vrange.max < v) {
- (void)SSL3_SendAlert(ss, alert_fatal,
- (v > SSL_LIBRARY_VERSION_3_0) ? protocol_version
- : handshake_failure);
- PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
- return SECFailure;
- }
*version = v;
return SECSuccess;
}
static SECStatus
-ssl3_GetNewRandom(SSL3Random *random)
+ssl3_GetNewRandom(SSL3Random random)
{
SECStatus rv;
- rv = PK11_GenerateRandom(random->rand, SSL3_RANDOM_LENGTH);
+ rv = PK11_GenerateRandom(random, SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
}
@@ -1135,7 +1034,7 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
{
SECStatus rv = SECFailure;
PRBool doDerEncode = PR_FALSE;
- PRBool isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
+ PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
PRBool useRsaPss = ssl_IsRsaPssSignatureScheme(ss->ssl3.hs.signatureScheme);
SECItem hashItem;
@@ -1421,124 +1320,110 @@ static SECStatus
ssl3_ComputeDHKeyHash(sslSocket *ss, SSLHashType hashAlg, SSL3Hashes *hashes,
SECItem dh_p, SECItem dh_g, SECItem dh_Ys, PRBool padY)
{
- PRUint8 *hashBuf;
- PRUint8 *pBuf;
- SECStatus rv = SECSuccess;
- unsigned int bufLen, yLen;
- PRUint8 buf[2 * SSL3_RANDOM_LENGTH + 2 + 4096 / 8 + 2 + 4096 / 8];
+ sslBuffer buf = SSL_BUFFER_EMPTY;
+ SECStatus rv;
+ unsigned int yLen;
+ unsigned int i;
PORT_Assert(dh_p.data);
PORT_Assert(dh_g.data);
PORT_Assert(dh_Ys.data);
- yLen = padY ? dh_p.len : dh_Ys.len;
- bufLen = 2 * SSL3_RANDOM_LENGTH +
- 2 + dh_p.len +
- 2 + dh_g.len +
- 2 + yLen;
- if (bufLen <= sizeof buf) {
- hashBuf = buf;
- } else {
- hashBuf = PORT_Alloc(bufLen);
- if (!hashBuf) {
- return SECFailure;
- }
+ rv = sslBuffer_Append(&buf, ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH);
+ if (rv != SECSuccess) {
+ goto loser;
}
-
- memcpy(hashBuf, &ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH);
- pBuf = hashBuf + SSL3_RANDOM_LENGTH;
- memcpy(pBuf, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH);
- pBuf += SSL3_RANDOM_LENGTH;
- pBuf = ssl_EncodeUintX(dh_p.len, 2, pBuf);
- memcpy(pBuf, dh_p.data, dh_p.len);
- pBuf += dh_p.len;
- pBuf = ssl_EncodeUintX(dh_g.len, 2, pBuf);
- memcpy(pBuf, dh_g.data, dh_g.len);
- pBuf += dh_g.len;
- pBuf = ssl_EncodeUintX(yLen, 2, pBuf);
- if (padY && dh_p.len > dh_Ys.len) {
- memset(pBuf, 0, dh_p.len - dh_Ys.len);
- pBuf += dh_p.len - dh_Ys.len;
+ rv = sslBuffer_Append(&buf, ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* p */
+ rv = sslBuffer_AppendVariable(&buf, dh_p.data, dh_p.len, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* g */
+ rv = sslBuffer_AppendVariable(&buf, dh_g.data, dh_g.len, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* y - complicated by padding */
+ yLen = padY ? dh_p.len : dh_Ys.len;
+ rv = sslBuffer_AppendNumber(&buf, yLen, 2);
+ if (rv != SECSuccess) {
+ goto loser;
}
/* If we're padding Y, dh_Ys can't be longer than dh_p. */
PORT_Assert(!padY || dh_p.len >= dh_Ys.len);
- memcpy(pBuf, dh_Ys.data, dh_Ys.len);
- pBuf += dh_Ys.len;
- PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen);
-
- rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes);
-
- PRINT_BUF(95, (NULL, "DHkey hash: ", hashBuf, bufLen));
- if (rv == SECSuccess) {
- if (hashAlg == ssl_hash_none) {
- PRINT_BUF(95, (NULL, "DHkey hash: MD5 result",
- hashes->u.s.md5, MD5_LENGTH));
- PRINT_BUF(95, (NULL, "DHkey hash: SHA1 result",
- hashes->u.s.sha, SHA1_LENGTH));
- } else {
- PRINT_BUF(95, (NULL, "DHkey hash: result",
- hashes->u.raw, hashes->len));
+ for (i = dh_Ys.len; i < yLen; ++i) {
+ rv = sslBuffer_AppendNumber(&buf, 0, 1);
+ if (rv != SECSuccess) {
+ goto loser;
}
}
-
- if (hashBuf != buf && hashBuf != NULL)
- PORT_Free(hashBuf);
- return rv;
-}
-
-/* Called twice, only from ssl3_DestroyCipherSpec (immediately below). */
-static void
-ssl3_CleanupKeyMaterial(ssl3KeyMaterial *mat)
-{
- if (mat->write_key != NULL) {
- PK11_FreeSymKey(mat->write_key);
- mat->write_key = NULL;
+ rv = sslBuffer_Append(&buf, dh_Ys.data, dh_Ys.len);
+ if (rv != SECSuccess) {
+ goto loser;
}
- if (mat->write_mac_key != NULL) {
- PK11_FreeSymKey(mat->write_mac_key);
- mat->write_mac_key = NULL;
+
+ rv = ssl3_ComputeCommonKeyHash(hashAlg, SSL_BUFFER_BASE(&buf),
+ SSL_BUFFER_LEN(&buf), hashes);
+ if (rv != SECSuccess) {
+ goto loser;
}
- if (mat->write_mac_context != NULL) {
- PK11_DestroyContext(mat->write_mac_context, PR_TRUE);
- mat->write_mac_context = NULL;
+
+ PRINT_BUF(95, (NULL, "DHkey hash: ", SSL_BUFFER_BASE(&buf),
+ SSL_BUFFER_LEN(&buf)));
+ if (hashAlg == ssl_hash_none) {
+ PRINT_BUF(95, (NULL, "DHkey hash: MD5 result",
+ hashes->u.s.md5, MD5_LENGTH));
+ PRINT_BUF(95, (NULL, "DHkey hash: SHA1 result",
+ hashes->u.s.sha, SHA1_LENGTH));
+ } else {
+ PRINT_BUF(95, (NULL, "DHkey hash: result",
+ hashes->u.raw, hashes->len));
}
+
+ sslBuffer_Clear(&buf);
+ return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&buf);
+ return SECFailure;
}
-/* Called from ssl3_SendChangeCipherSpecs() and
-** ssl3_HandleChangeCipherSpecs()
-** ssl3_DestroySSL3Info
-** Caller must hold SpecWriteLock.
-*/
-void
-ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName)
+static SECStatus
+ssl3_SetupPendingCipherSpec(sslSocket *ss, CipherSpecDirection direction,
+ const ssl3CipherSuiteDef *suiteDef,
+ ssl3CipherSpec **specp)
{
- /* PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss)); Don't have ss! */
- if (spec->encodeContext) {
- PK11_DestroyContext(spec->encodeContext, PR_TRUE);
- spec->encodeContext = NULL;
- }
- if (spec->decodeContext) {
- PK11_DestroyContext(spec->decodeContext, PR_TRUE);
- spec->decodeContext = NULL;
- }
- if (spec->destroyCompressContext && spec->compressContext) {
- spec->destroyCompressContext(spec->compressContext, 1);
- spec->compressContext = NULL;
+ ssl3CipherSpec *spec;
+ const ssl3CipherSpec *prev;
+
+ prev = (direction == CipherSpecWrite) ? ss->ssl3.cwSpec : ss->ssl3.crSpec;
+ if (prev->epoch == PR_UINT16_MAX) {
+ PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
+ return SECFailure;
}
- if (spec->destroyDecompressContext && spec->decompressContext) {
- spec->destroyDecompressContext(spec->decompressContext, 1);
- spec->decompressContext = NULL;
+
+ spec = ssl_CreateCipherSpec(ss, direction);
+ if (!spec) {
+ return SECFailure;
}
- if (spec->master_secret != NULL) {
- PK11_FreeSymKey(spec->master_secret);
- spec->master_secret = NULL;
+
+ spec->cipherDef = ssl_GetBulkCipherDef(suiteDef);
+ spec->macDef = ssl_GetMacDef(ss, suiteDef);
+
+ spec->epoch = prev->epoch + 1;
+ spec->seqNum = 0;
+ if (IS_DTLS(ss) && direction == CipherSpecRead) {
+ dtls_InitRecvdRecords(&spec->recvdRecords);
}
- spec->msItem.data = NULL;
- spec->msItem.len = 0;
- ssl3_CleanupKeyMaterial(&spec->client);
- ssl3_CleanupKeyMaterial(&spec->server);
- spec->destroyCompressContext = NULL;
- spec->destroyDecompressContext = NULL;
+ ssl_SetSpecVersions(ss, spec);
+
+ ssl_SaveCipherSpec(ss, spec);
+ *specp = spec;
+ return SECSuccess;
}
/* Fill in the pending cipher spec with info from the selected ciphersuite.
@@ -1548,272 +1433,116 @@ ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName)
** Acquires & releases SpecWriteLock.
*/
SECStatus
-ssl3_SetupPendingCipherSpec(sslSocket *ss)
+ssl3_SetupBothPendingCipherSpecs(sslSocket *ss)
{
- ssl3CipherSpec *pwSpec;
- ssl3CipherSpec *cwSpec;
ssl3CipherSuite suite = ss->ssl3.hs.cipher_suite;
- SSL3MACAlgorithm mac;
SSL3KeyExchangeAlgorithm kea;
- const ssl3CipherSuiteDef *suite_def;
- PRBool isTLS;
+ const ssl3CipherSuiteDef *suiteDef;
+ SECStatus rv;
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
ssl_GetSpecWriteLock(ss); /*******************************/
- pwSpec = ss->ssl3.pwSpec;
- PORT_Assert(pwSpec == ss->ssl3.prSpec);
-
/* This hack provides maximal interoperability with SSL 3 servers. */
- cwSpec = ss->ssl3.cwSpec;
- if (cwSpec->mac_def->mac == mac_null) {
+ if (ss->ssl3.cwSpec->macDef->mac == ssl_mac_null) {
/* SSL records are not being MACed. */
- cwSpec->version = ss->version;
+ ss->ssl3.cwSpec->version = ss->version;
}
- pwSpec->version = ss->version;
- isTLS = (PRBool)(pwSpec->version > SSL_LIBRARY_VERSION_3_0);
-
SSL_TRC(3, ("%d: SSL3[%d]: Set XXX Pending Cipher Suite to 0x%04x",
SSL_GETPID(), ss->fd, suite));
- suite_def = ssl_LookupCipherSuiteDef(suite);
- if (suite_def == NULL) {
- ssl_ReleaseSpecWriteLock(ss);
- return SECFailure; /* error code set by ssl_LookupCipherSuiteDef */
+ suiteDef = ssl_LookupCipherSuiteDef(suite);
+ if (suiteDef == NULL) {
+ goto loser;
}
if (IS_DTLS(ss)) {
/* Double-check that we did not pick an RC4 suite */
- PORT_Assert(suite_def->bulk_cipher_alg != cipher_rc4);
+ PORT_Assert(suiteDef->bulk_cipher_alg != cipher_rc4);
}
- kea = suite_def->key_exchange_alg;
- mac = suite_def->mac_alg;
- if (mac <= ssl_mac_sha && mac != ssl_mac_null && isTLS)
- mac += 2;
+ ss->ssl3.hs.suite_def = suiteDef;
- ss->ssl3.hs.suite_def = suite_def;
+ kea = suiteDef->key_exchange_alg;
ss->ssl3.hs.kea_def = &kea_defs[kea];
PORT_Assert(ss->ssl3.hs.kea_def->kea == kea);
- pwSpec->cipher_def = ssl_GetBulkCipherDef(suite_def);
-
- pwSpec->mac_def = &mac_defs[mac];
- PORT_Assert(pwSpec->mac_def->mac == mac);
-
- pwSpec->encodeContext = NULL;
- pwSpec->decodeContext = NULL;
-
- pwSpec->mac_size = pwSpec->mac_def->mac_size;
-
- pwSpec->compression_method = ss->ssl3.hs.compression;
- pwSpec->compressContext = NULL;
- pwSpec->decompressContext = NULL;
-
- ssl_ReleaseSpecWriteLock(ss); /*******************************/
- return SECSuccess;
-}
-
-#ifdef NSS_SSL_ENABLE_ZLIB
-#define SSL3_DEFLATE_CONTEXT_SIZE sizeof(z_stream)
-
-static SECStatus
-ssl3_MapZlibError(int zlib_error)
-{
- switch (zlib_error) {
- case Z_OK:
- return SECSuccess;
- default:
- return SECFailure;
- }
-}
-
-static SECStatus
-ssl3_DeflateInit(void *void_context)
-{
- z_stream *context = void_context;
- context->zalloc = NULL;
- context->zfree = NULL;
- context->opaque = NULL;
-
- return ssl3_MapZlibError(deflateInit(context, Z_DEFAULT_COMPRESSION));
-}
-
-static SECStatus
-ssl3_InflateInit(void *void_context)
-{
- z_stream *context = void_context;
- context->zalloc = NULL;
- context->zfree = NULL;
- context->opaque = NULL;
- context->next_in = NULL;
- context->avail_in = 0;
-
- return ssl3_MapZlibError(inflateInit(context));
-}
-
-static SECStatus
-ssl3_DeflateCompress(void *void_context, unsigned char *out, int *out_len,
- int maxout, const unsigned char *in, int inlen)
-{
- z_stream *context = void_context;
-
- if (!inlen) {
- *out_len = 0;
- return SECSuccess;
- }
-
- context->next_in = (unsigned char *)in;
- context->avail_in = inlen;
- context->next_out = out;
- context->avail_out = maxout;
- if (deflate(context, Z_SYNC_FLUSH) != Z_OK) {
- return SECFailure;
- }
- if (context->avail_out == 0) {
- /* We ran out of space! */
- SSL_TRC(3, ("%d: SSL3[%d] Ran out of buffer while compressing",
- SSL_GETPID()));
- return SECFailure;
- }
-
- *out_len = maxout - context->avail_out;
- return SECSuccess;
-}
-
-static SECStatus
-ssl3_DeflateDecompress(void *void_context, unsigned char *out, int *out_len,
- int maxout, const unsigned char *in, int inlen)
-{
- z_stream *context = void_context;
-
- if (!inlen) {
- *out_len = 0;
- return SECSuccess;
+ rv = ssl3_SetupPendingCipherSpec(ss, CipherSpecRead, suiteDef,
+ &ss->ssl3.prSpec);
+ if (rv != SECSuccess) {
+ goto loser;
}
-
- context->next_in = (unsigned char *)in;
- context->avail_in = inlen;
- context->next_out = out;
- context->avail_out = maxout;
- if (inflate(context, Z_SYNC_FLUSH) != Z_OK) {
- PORT_SetError(SSL_ERROR_DECOMPRESSION_FAILURE);
- return SECFailure;
+ rv = ssl3_SetupPendingCipherSpec(ss, CipherSpecWrite, suiteDef,
+ &ss->ssl3.pwSpec);
+ if (rv != SECSuccess) {
+ goto loser;
}
- *out_len = maxout - context->avail_out;
- return SECSuccess;
-}
-
-static SECStatus
-ssl3_DestroyCompressContext(void *void_context, PRBool unused)
-{
- deflateEnd(void_context);
- PORT_Free(void_context);
- return SECSuccess;
-}
-
-static SECStatus
-ssl3_DestroyDecompressContext(void *void_context, PRBool unused)
-{
- inflateEnd(void_context);
- PORT_Free(void_context);
+ ssl_ReleaseSpecWriteLock(ss); /*******************************/
return SECSuccess;
-}
-#endif /* NSS_SSL_ENABLE_ZLIB */
-
-/* Initialize the compression functions and contexts for the given
- * CipherSpec. */
-static SECStatus
-ssl3_InitCompressionContext(ssl3CipherSpec *pwSpec)
-{
- /* Setup the compression functions */
- switch (pwSpec->compression_method) {
- case ssl_compression_null:
- pwSpec->compressor = NULL;
- pwSpec->decompressor = NULL;
- pwSpec->compressContext = NULL;
- pwSpec->decompressContext = NULL;
- pwSpec->destroyCompressContext = NULL;
- pwSpec->destroyDecompressContext = NULL;
- break;
-#ifdef NSS_SSL_ENABLE_ZLIB
- case ssl_compression_deflate:
- pwSpec->compressor = ssl3_DeflateCompress;
- pwSpec->decompressor = ssl3_DeflateDecompress;
- pwSpec->compressContext = PORT_Alloc(SSL3_DEFLATE_CONTEXT_SIZE);
- pwSpec->decompressContext = PORT_Alloc(SSL3_DEFLATE_CONTEXT_SIZE);
- pwSpec->destroyCompressContext = ssl3_DestroyCompressContext;
- pwSpec->destroyDecompressContext = ssl3_DestroyDecompressContext;
- ssl3_DeflateInit(pwSpec->compressContext);
- ssl3_InflateInit(pwSpec->decompressContext);
- break;
-#endif /* NSS_SSL_ENABLE_ZLIB */
- default:
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-
- return SECSuccess;
+loser:
+ ssl_ReleaseSpecWriteLock(ss);
+ return SECFailure;
}
-/* ssl3_BuildRecordPseudoHeader writes the SSL/TLS pseudo-header (the data
- * which is included in the MAC or AEAD additional data) to |out| and returns
- * its length. See https://tools.ietf.org/html/rfc5246#section-6.2.3.3 for the
- * definition of the AEAD additional data.
+/* ssl3_BuildRecordPseudoHeader writes the SSL/TLS pseudo-header (the data which
+ * is included in the MAC or AEAD additional data) to |buf|. See
+ * https://tools.ietf.org/html/rfc5246#section-6.2.3.3 for the definition of the
+ * AEAD additional data.
*
* TLS pseudo-header includes the record's version field, SSL's doesn't. Which
- * pseudo-header defintiion to use should be decided based on the version of
+ * pseudo-header definition to use should be decided based on the version of
* the protocol that was negotiated when the cipher spec became current, NOT
* based on the version value in the record itself, and the decision is passed
* to this function as the |includesVersion| argument. But, the |version|
* argument should be the record's version value.
*/
-static unsigned int
-ssl3_BuildRecordPseudoHeader(unsigned char *out,
- sslSequenceNumber seq_num,
+static SECStatus
+ssl3_BuildRecordPseudoHeader(DTLSEpoch epoch,
+ sslSequenceNumber seqNum,
SSL3ContentType type,
PRBool includesVersion,
SSL3ProtocolVersion version,
PRBool isDTLS,
- int length)
-{
- out[0] = (unsigned char)(seq_num >> 56);
- out[1] = (unsigned char)(seq_num >> 48);
- out[2] = (unsigned char)(seq_num >> 40);
- out[3] = (unsigned char)(seq_num >> 32);
- out[4] = (unsigned char)(seq_num >> 24);
- out[5] = (unsigned char)(seq_num >> 16);
- out[6] = (unsigned char)(seq_num >> 8);
- out[7] = (unsigned char)(seq_num >> 0);
- out[8] = type;
+ int length,
+ sslBuffer *buf)
+{
+ SECStatus rv;
+ if (isDTLS) {
+ rv = sslBuffer_AppendNumber(buf, epoch, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(buf, seqNum, 6);
+ } else {
+ rv = sslBuffer_AppendNumber(buf, seqNum, 8);
+ }
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(buf, type, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
/* SSL3 MAC doesn't include the record's version field. */
- if (!includesVersion) {
- out[9] = MSB(length);
- out[10] = LSB(length);
- return 11;
+ if (includesVersion) {
+ /* TLS MAC and AEAD additional data include version. */
+ rv = sslBuffer_AppendNumber(buf, version, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
-
- /* TLS MAC and AEAD additional data include version. */
- if (isDTLS) {
- SSL3ProtocolVersion dtls_version;
-
- dtls_version = dtls_TLSVersionToDTLSVersion(version);
- out[9] = MSB(dtls_version);
- out[10] = LSB(dtls_version);
- } else {
- out[9] = MSB(version);
- out[10] = LSB(version);
+ rv = sslBuffer_AppendNumber(buf, length, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- out[11] = MSB(length);
- out[12] = LSB(length);
- return 13;
+
+ return SECSuccess;
}
static SECStatus
@@ -1833,13 +1562,12 @@ ssl3_AESGCM(ssl3KeyMaterial *keys,
unsigned int uOutLen;
CK_GCM_PARAMS gcmParams;
- const int tagSize = bulk_cipher_defs[cipher_aes_128_gcm].tag_size;
- const int explicitNonceLen =
- bulk_cipher_defs[cipher_aes_128_gcm].explicit_nonce_size;
+ const int tagSize = 16;
+ const int explicitNonceLen = 8;
/* See https://tools.ietf.org/html/rfc5288#section-3 for details of how the
* nonce is formed. */
- memcpy(nonce, keys->write_iv, 4);
+ memcpy(nonce, keys->iv, 4);
if (doDecrypt) {
memcpy(nonce + 4, in, explicitNonceLen);
in += explicitNonceLen;
@@ -1868,10 +1596,10 @@ ssl3_AESGCM(ssl3KeyMaterial *keys,
gcmParams.ulTagBits = tagSize * 8;
if (doDecrypt) {
- rv = PK11_Decrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
+ rv = PK11_Decrypt(keys->key, CKM_AES_GCM, &param, out, &uOutLen,
maxout, in, inlen);
} else {
- rv = PK11_Encrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
+ rv = PK11_Encrypt(keys->key, CKM_AES_GCM, &param, out, &uOutLen,
maxout, in, inlen);
}
*outlen += (int)uOutLen;
@@ -1893,12 +1621,12 @@ ssl3_ChaCha20Poly1305(ssl3KeyMaterial *keys, PRBool doDecrypt,
unsigned char nonce[12];
CK_NSS_AEAD_PARAMS aeadParams;
- const int tagSize = bulk_cipher_defs[cipher_chacha20].tag_size;
+ const int tagSize = 16;
/* See
* https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04#section-2
* for details of how the nonce is formed. */
- PORT_Memcpy(nonce, keys->write_iv, 12);
+ PORT_Memcpy(nonce, keys->iv, 12);
/* XOR the last 8 bytes of the IV with the sequence number. */
PORT_Assert(additionalDataLen >= 8);
@@ -1917,10 +1645,10 @@ ssl3_ChaCha20Poly1305(ssl3KeyMaterial *keys, PRBool doDecrypt,
aeadParams.ulTagLen = tagSize;
if (doDecrypt) {
- rv = PK11_Decrypt(keys->write_key, CKM_NSS_CHACHA20_POLY1305, &param,
+ rv = PK11_Decrypt(keys->key, CKM_NSS_CHACHA20_POLY1305, &param,
out, &uOutLen, maxout, in, inlen);
} else {
- rv = PK11_Encrypt(keys->write_key, CKM_NSS_CHACHA20_POLY1305, &param,
+ rv = PK11_Encrypt(keys->key, CKM_NSS_CHACHA20_POLY1305, &param,
out, &uOutLen, maxout, in, inlen);
}
*outlen = (int)uOutLen;
@@ -1933,44 +1661,31 @@ ssl3_ChaCha20Poly1305(ssl3KeyMaterial *keys, PRBool doDecrypt,
* Caller holds Spec write lock.
*/
static SECStatus
-ssl3_InitPendingContexts(sslSocket *ss)
+ssl3_InitPendingContexts(sslSocket *ss, ssl3CipherSpec *spec)
{
- ssl3CipherSpec *pwSpec;
- const ssl3BulkCipherDef *cipher_def;
- PK11Context *serverContext = NULL;
- PK11Context *clientContext = NULL;
- SECItem *param;
- CK_MECHANISM_TYPE mechanism;
- CK_MECHANISM_TYPE mac_mech;
+ CK_MECHANISM_TYPE encMechanism;
+ CK_ATTRIBUTE_TYPE encMode;
+ SECItem macParam;
CK_ULONG macLength;
SECItem iv;
- SECItem mac_param;
SSLCipherAlgorithm calg;
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
- pwSpec = ss->ssl3.pwSpec;
- cipher_def = pwSpec->cipher_def;
- macLength = pwSpec->mac_size;
- calg = cipher_def->calg;
+ macLength = spec->macDef->mac_size;
+ calg = spec->cipherDef->calg;
PORT_Assert(alg2Mech[calg].calg == calg);
- pwSpec->client.write_mac_context = NULL;
- pwSpec->server.write_mac_context = NULL;
-
- if (cipher_def->type == type_aead) {
- pwSpec->encode = NULL;
- pwSpec->decode = NULL;
- pwSpec->encodeContext = NULL;
- pwSpec->decodeContext = NULL;
+ if (spec->cipherDef->type == type_aead) {
+ spec->cipher = NULL;
+ spec->cipherContext = NULL;
switch (calg) {
- case calg_aes_gcm:
- pwSpec->aead = ssl3_AESGCM;
+ case ssl_calg_aes_gcm:
+ spec->aead = ssl3_AESGCM;
break;
- case calg_chacha20:
- pwSpec->aead = ssl3_ChaCha20Poly1305;
+ case ssl_calg_chacha20:
+ spec->aead = ssl3_ChaCha20Poly1305;
break;
default:
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -1983,128 +1698,43 @@ ssl3_InitPendingContexts(sslSocket *ss)
** Now setup the MAC contexts,
** crypto contexts are setup below.
*/
+ macParam.data = (unsigned char *)&macLength;
+ macParam.len = sizeof(macLength);
+ macParam.type = siBuffer;
- mac_mech = pwSpec->mac_def->mmech;
- mac_param.data = (unsigned char *)&macLength;
- mac_param.len = sizeof(macLength);
- mac_param.type = 0;
-
- pwSpec->client.write_mac_context = PK11_CreateContextBySymKey(
- mac_mech, CKA_SIGN, pwSpec->client.write_mac_key, &mac_param);
- if (pwSpec->client.write_mac_context == NULL) {
- ssl_MapLowLevelError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE);
- goto fail;
- }
- pwSpec->server.write_mac_context = PK11_CreateContextBySymKey(
- mac_mech, CKA_SIGN, pwSpec->server.write_mac_key, &mac_param);
- if (pwSpec->server.write_mac_context == NULL) {
+ spec->keyMaterial.macContext = PK11_CreateContextBySymKey(
+ spec->macDef->mmech, CKA_SIGN, spec->keyMaterial.macKey, &macParam);
+ if (!spec->keyMaterial.macContext) {
ssl_MapLowLevelError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE);
- goto fail;
+ return SECFailure;
}
/*
** Now setup the crypto contexts.
*/
-
- if (calg == calg_null) {
- pwSpec->encode = Null_Cipher;
- pwSpec->decode = Null_Cipher;
+ if (calg == ssl_calg_null) {
+ spec->cipher = Null_Cipher;
return SECSuccess;
}
- mechanism = ssl3_Alg2Mech(calg);
- /*
- * build the server context
- */
- iv.data = pwSpec->server.write_iv;
- iv.len = cipher_def->iv_size;
- param = PK11_ParamFromIV(mechanism, &iv);
- if (param == NULL) {
- ssl_MapLowLevelError(SSL_ERROR_IV_PARAM_FAILURE);
- goto fail;
- }
- serverContext = PK11_CreateContextBySymKey(mechanism,
- (ss->sec.isServer ? CKA_ENCRYPT
- : CKA_DECRYPT),
- pwSpec->server.write_key, param);
- iv.data = PK11_IVFromParam(mechanism, param, (int *)&iv.len);
- if (iv.data)
- PORT_Memcpy(pwSpec->server.write_iv, iv.data, iv.len);
- SECITEM_FreeItem(param, PR_TRUE);
- if (serverContext == NULL) {
- ssl_MapLowLevelError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE);
- goto fail;
- }
+ spec->cipher = (SSLCipher)PK11_CipherOp;
+ encMechanism = ssl3_Alg2Mech(calg);
+ encMode = (spec->direction == CipherSpecWrite) ? CKA_ENCRYPT : CKA_DECRYPT;
/*
- * build the client context
+ * build the context
*/
- iv.data = pwSpec->client.write_iv;
- iv.len = cipher_def->iv_size;
-
- param = PK11_ParamFromIV(mechanism, &iv);
- if (param == NULL) {
- ssl_MapLowLevelError(SSL_ERROR_IV_PARAM_FAILURE);
- goto fail;
- }
- clientContext = PK11_CreateContextBySymKey(mechanism,
- (ss->sec.isServer ? CKA_DECRYPT
- : CKA_ENCRYPT),
- pwSpec->client.write_key, param);
- iv.data = PK11_IVFromParam(mechanism, param, (int *)&iv.len);
- if (iv.data)
- PORT_Memcpy(pwSpec->client.write_iv, iv.data, iv.len);
- SECITEM_FreeItem(param, PR_TRUE);
- if (clientContext == NULL) {
+ iv.data = spec->keyMaterial.iv;
+ iv.len = spec->cipherDef->iv_size;
+ spec->cipherContext = PK11_CreateContextBySymKey(encMechanism, encMode,
+ spec->keyMaterial.key,
+ &iv);
+ if (!spec->cipherContext) {
ssl_MapLowLevelError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE);
- goto fail;
+ return SECFailure;
}
- pwSpec->encode = (SSLCipher)PK11_CipherOp;
- pwSpec->decode = (SSLCipher)PK11_CipherOp;
-
- pwSpec->encodeContext = (ss->sec.isServer) ? serverContext : clientContext;
- pwSpec->decodeContext = (ss->sec.isServer) ? clientContext : serverContext;
-
- serverContext = NULL;
- clientContext = NULL;
-
- ssl3_InitCompressionContext(pwSpec);
return SECSuccess;
-
-fail:
- if (serverContext != NULL)
- PK11_DestroyContext(serverContext, PR_TRUE);
- if (pwSpec->client.write_mac_context != NULL) {
- PK11_DestroyContext(pwSpec->client.write_mac_context, PR_TRUE);
- pwSpec->client.write_mac_context = NULL;
- }
- if (pwSpec->server.write_mac_context != NULL) {
- PK11_DestroyContext(pwSpec->server.write_mac_context, PR_TRUE);
- pwSpec->server.write_mac_context = NULL;
- }
-
- return SECFailure;
-}
-
-HASH_HashType
-ssl3_GetTls12HashType(sslSocket *ss)
-{
- if (ss->ssl3.pwSpec->version < SSL_LIBRARY_VERSION_TLS_1_2) {
- return HASH_AlgNULL;
- }
-
- switch (ss->ssl3.hs.suite_def->prf_hash) {
- case ssl_hash_sha384:
- return HASH_AlgSHA384;
- case ssl_hash_sha256:
- case ssl_hash_none:
- /* ssl_hash_none is for pre-1.2 suites, which use SHA-256. */
- return HASH_AlgSHA256;
- default:
- PORT_Assert(0);
- }
- return HASH_AlgSHA256;
}
/* Complete the initialization of all keys, ciphers, MACs and their contexts
@@ -2114,73 +1744,78 @@ ssl3_GetTls12HashType(sslSocket *ss)
* ssl3_HandleServerHello (for session restart)
* ssl3_HandleClientHello (for session restart)
* Sets error code, but caller probably should override to disambiguate.
- * NULL pms means re-use old master_secret.
*
- * If the old master secret is reused, pms is NULL and the master secret is
- * already in pwSpec->master_secret.
+ * If |secret| is a master secret from a previous connection is reused, |derive|
+ * is PR_FALSE. If the secret is a pre-master secret, then |derive| is PR_TRUE
+ * and the master secret is derived from |secret|.
*/
SECStatus
-ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms)
+ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret, PRBool derive)
{
+ PK11SymKey *masterSecret;
ssl3CipherSpec *pwSpec;
- ssl3CipherSpec *cwSpec;
+ ssl3CipherSpec *prSpec;
SECStatus rv;
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+ PORT_Assert(secret);
ssl_GetSpecWriteLock(ss); /**************************************/
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
-
+ PORT_Assert(ss->ssl3.pwSpec);
+ PORT_Assert(ss->ssl3.cwSpec->epoch == ss->ssl3.crSpec->epoch);
+ prSpec = ss->ssl3.prSpec;
pwSpec = ss->ssl3.pwSpec;
- cwSpec = ss->ssl3.cwSpec;
- if (pms || (!pwSpec->msItem.len && !pwSpec->master_secret)) {
- rv = ssl3_DeriveMasterSecret(ss, pms);
- if (rv != SECSuccess) {
- goto done; /* err code set by ssl3_DeriveMasterSecret */
- }
+ if (ss->ssl3.cwSpec->epoch == PR_UINT16_MAX) {
+ /* The problem here is that we have rehandshaked too many
+ * times (you are not allowed to wrap the epoch). The
+ * spec says you should be discarding the connection
+ * and start over, so not much we can do here. */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ goto loser;
}
- if (pwSpec->master_secret) {
- rv = ssl3_DeriveConnectionKeys(ss);
- if (rv == SECSuccess) {
- rv = ssl3_InitPendingContexts(ss);
+
+ if (derive) {
+ rv = ssl3_ComputeMasterSecret(ss, secret, &masterSecret);
+ if (rv != SECSuccess) {
+ goto loser;
}
} else {
- PORT_Assert(pwSpec->master_secret);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- rv = SECFailure;
+ masterSecret = secret;
}
+
+ PORT_Assert(masterSecret);
+ rv = ssl3_DeriveConnectionKeys(ss, masterSecret);
if (rv != SECSuccess) {
- goto done;
+ if (derive) {
+ /* masterSecret was created here. */
+ PK11_FreeSymKey(masterSecret);
+ }
+ goto loser;
}
- /* Generic behaviors -- common to all crypto methods */
- if (!IS_DTLS(ss)) {
- pwSpec->read_seq_num = pwSpec->write_seq_num = 0;
- } else {
- if (cwSpec->epoch == PR_UINT16_MAX) {
- /* The problem here is that we have rehandshaked too many
- * times (you are not allowed to wrap the epoch). The
- * spec says you should be discarding the connection
- * and start over, so not much we can do here. */
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- rv = SECFailure;
- goto done;
- }
- /* The sequence number has the high 16 bits as the epoch. */
- pwSpec->epoch = cwSpec->epoch + 1;
- pwSpec->read_seq_num = pwSpec->write_seq_num =
- (sslSequenceNumber)pwSpec->epoch << 48;
+ /* Both cipher specs maintain a reference to the master secret, since each
+ * is managed and freed independently. */
+ prSpec->masterSecret = masterSecret;
+ pwSpec->masterSecret = PK11_ReferenceSymKey(masterSecret);
+ rv = ssl3_InitPendingContexts(ss, ss->ssl3.prSpec);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
- dtls_InitRecvdRecords(&pwSpec->recvdRecords);
+ rv = ssl3_InitPendingContexts(ss, ss->ssl3.pwSpec);
+ if (rv != SECSuccess) {
+ goto loser;
}
-done:
ssl_ReleaseSpecWriteLock(ss); /******************************/
- if (rv != SECSuccess)
- ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
- return rv;
+ return SECSuccess;
+
+loser:
+ ssl_ReleaseSpecWriteLock(ss); /******************************/
+ ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
+ return SECFailure;
}
/*
@@ -2213,36 +1848,33 @@ static const unsigned char mac_pad_2[60] = {
static SECStatus
ssl3_ComputeRecordMAC(
ssl3CipherSpec *spec,
- PRBool useServerMacKey,
const unsigned char *header,
unsigned int headerLen,
const PRUint8 *input,
- int inputLength,
+ int inputLen,
unsigned char *outbuf,
- unsigned int *outLength)
+ unsigned int *outLen)
{
- const ssl3MACDef *mac_def;
+ PK11Context *context;
+ int macSize = spec->macDef->mac_size;
SECStatus rv;
PRINT_BUF(95, (NULL, "frag hash1: header", header, headerLen));
- PRINT_BUF(95, (NULL, "frag hash1: input", input, inputLength));
+ PRINT_BUF(95, (NULL, "frag hash1: input", input, inputLen));
- mac_def = spec->mac_def;
- if (mac_def->mac == mac_null) {
- *outLength = 0;
+ if (spec->macDef->mac == ssl_mac_null) {
+ *outLen = 0;
return SECSuccess;
}
- PK11Context *mac_context =
- (useServerMacKey ? spec->server.write_mac_context
- : spec->client.write_mac_context);
- rv = PK11_DigestBegin(mac_context);
- rv |= PK11_DigestOp(mac_context, header, headerLen);
- rv |= PK11_DigestOp(mac_context, input, inputLength);
- rv |= PK11_DigestFinal(mac_context, outbuf, outLength, spec->mac_size);
- PORT_Assert(rv != SECSuccess || *outLength == (unsigned)spec->mac_size);
+ context = spec->keyMaterial.macContext;
+ rv = PK11_DigestBegin(context);
+ rv |= PK11_DigestOp(context, header, headerLen);
+ rv |= PK11_DigestOp(context, input, inputLen);
+ rv |= PK11_DigestFinal(context, outbuf, outLen, macSize);
+ PORT_Assert(rv != SECSuccess || *outLen == (unsigned)macSize);
- PRINT_BUF(95, (NULL, "frag hash2: result", outbuf, *outLength));
+ PRINT_BUF(95, (NULL, "frag hash2: result", outbuf, *outLen));
if (rv != SECSuccess) {
rv = SECFailure;
@@ -2260,7 +1892,6 @@ ssl3_ComputeRecordMAC(
static SECStatus
ssl3_ComputeRecordMACConstantTime(
ssl3CipherSpec *spec,
- PRBool useServerMacKey,
const unsigned char *header,
unsigned int headerLen,
const PRUint8 *input,
@@ -2272,13 +1903,13 @@ ssl3_ComputeRecordMACConstantTime(
CK_MECHANISM_TYPE macType;
CK_NSS_MAC_CONSTANT_TIME_PARAMS params;
SECItem param, inputItem, outputItem;
+ int macSize = spec->macDef->mac_size;
SECStatus rv;
- PK11SymKey *key;
- PORT_Assert(inputLen >= spec->mac_size);
+ PORT_Assert(inputLen >= spec->macDef->mac_size);
PORT_Assert(originalLen >= inputLen);
- if (spec->mac_def->mac == mac_null) {
+ if (spec->macDef->mac == ssl_mac_null) {
*outLen = 0;
return SECSuccess;
}
@@ -2288,7 +1919,7 @@ ssl3_ComputeRecordMACConstantTime(
macType = CKM_NSS_SSL3_MAC_CONSTANT_TIME;
}
- params.macAlg = spec->mac_def->mmech;
+ params.macAlg = spec->macDef->mmech;
params.ulBodyTotalLen = originalLen;
params.pHeader = (unsigned char *)header; /* const cast */
params.ulHeaderLen = headerLen;
@@ -2305,19 +1936,14 @@ ssl3_ComputeRecordMACConstantTime(
outputItem.len = *outLen;
outputItem.type = 0;
- key = spec->server.write_mac_key;
- if (!useServerMacKey) {
- key = spec->client.write_mac_key;
- }
-
- rv = PK11_SignWithSymKey(key, macType, &param, &outputItem, &inputItem);
+ rv = PK11_SignWithSymKey(spec->keyMaterial.macKey, macType, &param,
+ &outputItem, &inputItem);
if (rv != SECSuccess) {
if (PORT_GetError() == SEC_ERROR_INVALID_ALGORITHM) {
/* ssl3_ComputeRecordMAC() expects the MAC to have been removed
* from the input length already. */
- return ssl3_ComputeRecordMAC(spec, useServerMacKey,
- header, headerLen,
- input, inputLen - spec->mac_size,
+ return ssl3_ComputeRecordMAC(spec, header, headerLen,
+ input, inputLen - macSize,
outbuf, outLen);
}
@@ -2327,7 +1953,7 @@ ssl3_ComputeRecordMACConstantTime(
return rv;
}
- PORT_Assert(outputItem.len == (unsigned)spec->mac_size);
+ PORT_Assert(outputItem.len == (unsigned)macSize);
*outLen = outputItem.len;
return rv;
@@ -2363,34 +1989,30 @@ ssl3_ClientAuthTokenPresent(sslSessionID *sid)
/* Caller must hold the spec read lock. */
SECStatus
-ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
- PRBool isServer,
- PRBool isDTLS,
- PRBool capRecordVersion,
- SSL3ContentType type,
- const PRUint8 *pIn,
- PRUint32 contentLen,
- sslBuffer *wrBuf)
-{
- const ssl3BulkCipherDef *cipher_def;
+ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec,
+ PRBool isServer,
+ PRBool isDTLS,
+ SSL3ContentType type,
+ const PRUint8 *pIn,
+ PRUint32 contentLen,
+ sslBuffer *wrBuf)
+{
SECStatus rv;
PRUint32 macLen = 0;
PRUint32 fragLen;
PRUint32 p1Len, p2Len, oddLen = 0;
unsigned int ivLen = 0;
- unsigned char pseudoHeader[13];
- unsigned int pseudoHeaderLen;
-
- cipher_def = cwSpec->cipher_def;
+ unsigned char pseudoHeaderBuf[13];
+ sslBuffer pseudoHeader = SSL_BUFFER(pseudoHeaderBuf);
- if (cipher_def->type == type_block &&
+ if (cwSpec->cipherDef->type == type_block &&
cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
/* Prepend the per-record explicit IV using technique 2b from
* RFC 4346 section 6.2.3.2: The IV is a cryptographically
* strong random number XORed with the CBC residue from the previous
* record.
*/
- ivLen = cipher_def->iv_size;
+ ivLen = cwSpec->cipherDef->iv_size;
if (ivLen > wrBuf->space) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
@@ -2400,7 +2022,7 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
return rv;
}
- rv = cwSpec->encode(cwSpec->encodeContext,
+ rv = cwSpec->cipher(cwSpec->cipherContext,
wrBuf->buf, /* output */
(int *)&wrBuf->len, /* outlen */
ivLen, /* max outlen */
@@ -2412,24 +2034,14 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
}
}
- if (cwSpec->compressor) {
- int outlen;
- rv = cwSpec->compressor(cwSpec->compressContext, wrBuf->buf + ivLen,
- &outlen, wrBuf->space - ivLen, pIn, contentLen);
- if (rv != SECSuccess)
- return rv;
- pIn = wrBuf->buf + ivLen;
- contentLen = outlen;
- }
-
- pseudoHeaderLen = ssl3_BuildRecordPseudoHeader(
- pseudoHeader, cwSpec->write_seq_num, type,
- cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_0, cwSpec->version,
- isDTLS, contentLen);
- PORT_Assert(pseudoHeaderLen <= sizeof(pseudoHeader));
- if (cipher_def->type == type_aead) {
- const int nonceLen = cipher_def->explicit_nonce_size;
- const int tagLen = cipher_def->tag_size;
+ rv = ssl3_BuildRecordPseudoHeader(
+ cwSpec->epoch, cwSpec->seqNum, type,
+ cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_0, cwSpec->recordVersion,
+ isDTLS, contentLen, &pseudoHeader);
+ PORT_Assert(rv == SECSuccess);
+ if (cwSpec->cipherDef->type == type_aead) {
+ const int nonceLen = cwSpec->cipherDef->explicit_nonce_size;
+ const int tagLen = cwSpec->cipherDef->tag_size;
if (nonceLen + contentLen + tagLen > wrBuf->space) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -2437,23 +2049,26 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
}
rv = cwSpec->aead(
- isServer ? &cwSpec->server : &cwSpec->client,
+ &cwSpec->keyMaterial,
PR_FALSE, /* do encrypt */
wrBuf->buf, /* output */
(int *)&wrBuf->len, /* out len */
wrBuf->space, /* max out */
pIn, contentLen, /* input */
- pseudoHeader, pseudoHeaderLen);
+ SSL_BUFFER_BASE(&pseudoHeader), SSL_BUFFER_LEN(&pseudoHeader));
if (rv != SECSuccess) {
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
return SECFailure;
}
} else {
+ int blockSize = cwSpec->cipherDef->block_size;
+
/*
* Add the MAC
*/
- rv = ssl3_ComputeRecordMAC(cwSpec, isServer, pseudoHeader,
- pseudoHeaderLen, pIn, contentLen,
+ rv = ssl3_ComputeRecordMAC(cwSpec, SSL_BUFFER_BASE(&pseudoHeader),
+ SSL_BUFFER_LEN(&pseudoHeader),
+ pIn, contentLen,
wrBuf->buf + ivLen + contentLen, &macLen);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
@@ -2468,16 +2083,16 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
* Pad the text (if we're doing a block cipher)
* then Encrypt it
*/
- if (cipher_def->type == type_block) {
+ if (cwSpec->cipherDef->type == type_block) {
unsigned char *pBuf;
int padding_length;
int i;
- oddLen = contentLen % cipher_def->block_size;
+ oddLen = contentLen % blockSize;
/* Assume blockSize is a power of two */
- padding_length = cipher_def->block_size - 1 - ((fragLen) & (cipher_def->block_size - 1));
+ padding_length = blockSize - 1 - ((fragLen) & (blockSize - 1));
fragLen += padding_length + 1;
- PORT_Assert((fragLen % cipher_def->block_size) == 0);
+ PORT_Assert((fragLen % blockSize) == 0);
/* Pad according to TLS rules (also acceptable to SSL3). */
pBuf = &wrBuf->buf[ivLen + fragLen - 1];
@@ -2495,13 +2110,13 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
}
if (oddLen) {
p2Len += oddLen;
- PORT_Assert((cipher_def->block_size < 2) ||
- (p2Len % cipher_def->block_size) == 0);
+ PORT_Assert((blockSize < 2) ||
+ (p2Len % blockSize) == 0);
memmove(wrBuf->buf + ivLen + p1Len, pIn + p1Len, oddLen);
}
if (p1Len > 0) {
int cipherBytesPart1 = -1;
- rv = cwSpec->encode(cwSpec->encodeContext,
+ rv = cwSpec->cipher(cwSpec->cipherContext,
wrBuf->buf + ivLen, /* output */
&cipherBytesPart1, /* actual outlen */
p1Len, /* max outlen */
@@ -2516,7 +2131,7 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
}
if (p2Len > 0) {
int cipherBytesPart2 = -1;
- rv = cwSpec->encode(cwSpec->encodeContext,
+ rv = cwSpec->cipher(cwSpec->cipherContext,
wrBuf->buf + ivLen + p1Len,
&cipherBytesPart2, /* output and actual outLen */
p2Len, /* max outlen */
@@ -2534,34 +2149,66 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *cwSpec,
return SECSuccess;
}
+/* Note: though this can report failure, it shouldn't. */
+static SECStatus
+ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec,
+ SSL3ContentType contentType, unsigned int len,
+ sslBuffer *wrBuf)
+{
+ SECStatus rv;
+
+#ifndef UNSAFE_FUZZER_MODE
+ if (cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+ cwSpec->cipherDef->calg != ssl_calg_null) {
+ contentType = content_application_data;
+ }
+#endif
+ rv = sslBuffer_AppendNumber(wrBuf, contentType, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(wrBuf, cwSpec->recordVersion, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ if (IS_DTLS(ss)) {
+ rv = sslBuffer_AppendNumber(wrBuf, cwSpec->epoch, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(wrBuf, cwSpec->seqNum, 6);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+ rv = sslBuffer_AppendNumber(wrBuf, len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
SECStatus
-ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
- PRBool capRecordVersion, SSL3ContentType type,
+ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type,
const PRUint8 *pIn, PRUint32 contentLen, sslBuffer *wrBuf)
{
- const ssl3BulkCipherDef *cipher_def = cwSpec->cipher_def;
- PRUint16 headerLen;
- sslBuffer protBuf;
- SSL3ProtocolVersion version = cwSpec->version;
+ unsigned int headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH
+ : SSL3_RECORD_HEADER_LENGTH;
+ sslBuffer protBuf = SSL_BUFFER_FIXED(SSL_BUFFER_BASE(wrBuf) + headerLen,
+ SSL_BUFFER_SPACE(wrBuf) - headerLen);
PRBool isTLS13;
- PRUint8 *ptr = wrBuf->buf;
SECStatus rv;
- if (ss->ssl3.hs.shortHeaders) {
- PORT_Assert(!IS_DTLS(ss));
- PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
- headerLen = TLS13_RECORD_HEADER_LENGTH_SHORT;
- } else {
- headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
- }
- protBuf.buf = wrBuf->buf + headerLen;
- protBuf.len = 0;
- protBuf.space = wrBuf->space - headerLen;
-
- PORT_Assert(cipher_def->max_records <= RECORD_SEQ_MAX);
- if ((cwSpec->write_seq_num & RECORD_SEQ_MAX) >= cipher_def->max_records) {
+ PORT_Assert(cwSpec->direction == CipherSpecWrite);
+ PORT_Assert(SSL_BUFFER_LEN(wrBuf) == 0);
+ PORT_Assert(cwSpec->cipherDef->max_records <= RECORD_SEQ_MAX);
+ if (cwSpec->seqNum >= cwSpec->cipherDef->max_records) {
+ /* We should have automatically updated before here in TLS 1.3. */
+ PORT_Assert(cwSpec->version < SSL_LIBRARY_VERSION_TLS_1_3);
SSL_TRC(3, ("%d: SSL[-]: write sequence number at limit 0x%0llx",
- SSL_GETPID(), cwSpec->write_seq_num));
+ SSL_GETPID(), cwSpec->seqNum));
PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
return SECFailure;
}
@@ -2569,15 +2216,22 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
isTLS13 = (PRBool)(cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_3);
#ifdef UNSAFE_FUZZER_MODE
- rv = Null_Cipher(NULL, protBuf.buf, (int *)&protBuf.len, protBuf.space,
- pIn, contentLen);
+ {
+ int len;
+ rv = Null_Cipher(NULL, SSL_BUFFER_BASE(&protBuf), &len,
+ SSL_BUFFER_SPACE(&protBuf), pIn, contentLen);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error was set */
+ }
+ rv = sslBuffer_Skip(&protBuf, len, NULL);
+ PORT_Assert(rv == SECSuccess); /* Can't fail. */
+ }
#else
if (isTLS13) {
rv = tls13_ProtectRecord(ss, cwSpec, type, pIn, contentLen, &protBuf);
} else {
- rv = ssl3_CompressMACEncryptRecord(cwSpec, ss->sec.isServer,
- IS_DTLS(ss), capRecordVersion, type,
- pIn, contentLen, &protBuf);
+ rv = ssl3_MACEncryptRecord(cwSpec, ss->sec.isServer, IS_DTLS(ss), type,
+ pIn, contentLen, &protBuf);
}
#endif
if (rv != SECSuccess) {
@@ -2585,40 +2239,58 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
}
PORT_Assert(protBuf.len <= MAX_FRAGMENT_LENGTH + (isTLS13 ? 256 : 1024));
- wrBuf->len = protBuf.len + headerLen;
- if (ss->ssl3.hs.shortHeaders) {
- PORT_Assert(!IS_DTLS(ss)); /* Decoder not yet implemented. */
- (void)ssl_EncodeUintX(0x8000 | protBuf.len, 2, ptr);
- } else {
-#ifndef UNSAFE_FUZZER_MODE
- if (isTLS13 && cipher_def->calg != ssl_calg_null) {
- *ptr++ = content_application_data;
- } else
-#endif
- {
- *ptr++ = type;
- }
+ rv = ssl_InsertRecordHeader(ss, cwSpec, type, SSL_BUFFER_LEN(&protBuf),
+ wrBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- if (IS_DTLS(ss)) {
- version = isTLS13 ? SSL_LIBRARY_VERSION_TLS_1_1 : version;
- version = dtls_TLSVersionToDTLSVersion(version);
+ PORT_Assert(SSL_BUFFER_LEN(wrBuf) == headerLen);
+ rv = sslBuffer_Skip(wrBuf, SSL_BUFFER_LEN(&protBuf), NULL);
+ if (rv != SECSuccess) {
+ PORT_Assert(0); /* Can't fail. */
+ return SECFailure;
+ }
+ ++cwSpec->seqNum;
- ptr = ssl_EncodeUintX(version, 2, ptr);
- ptr = ssl_EncodeUintX(cwSpec->write_seq_num, 8, ptr);
- } else {
- if (capRecordVersion || isTLS13) {
- version = PR_MIN(SSL_LIBRARY_VERSION_TLS_1_0, version);
- }
- ptr = ssl_EncodeUintX(version, 2, ptr);
+ return SECSuccess;
+}
+
+SECStatus
+ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSL3ContentType type,
+ const PRUint8 *pIn, unsigned int nIn,
+ unsigned int *written)
+{
+ sslBuffer *wrBuf = &ss->sec.writeBuf;
+ unsigned int contentLen;
+ unsigned int spaceNeeded;
+ SECStatus rv;
+
+ contentLen = PR_MIN(nIn, MAX_FRAGMENT_LENGTH);
+ spaceNeeded = contentLen + SSL3_BUFFER_FUDGE;
+ if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_1 &&
+ spec->cipherDef->type == type_block) {
+ spaceNeeded += spec->cipherDef->iv_size;
+ }
+ if (spaceNeeded > SSL_BUFFER_SPACE(wrBuf)) {
+ rv = sslBuffer_Grow(wrBuf, spaceNeeded);
+ if (rv != SECSuccess) {
+ SSL_DBG(("%d: SSL3[%d]: failed to expand write buffer to %d",
+ SSL_GETPID(), ss->fd, spaceNeeded));
+ return SECFailure;
}
- (void)ssl_EncodeUintX(protBuf.len, 2, ptr);
}
- ++cwSpec->write_seq_num;
+ rv = ssl_ProtectRecord(ss, spec, type, pIn, contentLen, wrBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ PRINT_BUF(50, (ss, "send (encrypted) record data:",
+ SSL_BUFFER_BASE(wrBuf), SSL_BUFFER_LEN(wrBuf)));
+ *written = contentLen;
return SECSuccess;
}
-
/* Process the plain text before sending it.
* Returns the number of bytes of plaintext that were successfully sent
* plus the number of bytes of plaintext that were copied into the
@@ -2639,16 +2311,6 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
* all ciphertext into the pending ciphertext buffer.
* ssl_SEND_FLAG_USE_EPOCH (for DTLS)
* Forces the use of the provided epoch
- * ssl_SEND_FLAG_CAP_RECORD_VERSION
- * Caps the record layer version number of TLS ClientHello to { 3, 1 }
- * (TLS 1.0). Some TLS 1.0 servers (which seem to use F5 BIG-IP) ignore
- * ClientHello.client_version and use the record layer version number
- * (TLSPlaintext.version) instead when negotiating protocol versions. In
- * addition, if the record layer version number of ClientHello is { 3, 2 }
- * (TLS 1.1) or higher, these servers reset the TCP connections. Lastly,
- * some F5 BIG-IP servers hang if a record containing a ClientHello has a
- * version greater than { 3, 1 } and a length greater than 255. Set this
- * flag to work around such servers.
*/
PRInt32
ssl3_SendRecord(sslSocket *ss,
@@ -2659,10 +2321,9 @@ ssl3_SendRecord(sslSocket *ss,
PRInt32 flags)
{
sslBuffer *wrBuf = &ss->sec.writeBuf;
+ ssl3CipherSpec *spec;
SECStatus rv;
PRInt32 totalSent = 0;
- PRBool capRecordVersion;
- ssl3CipherSpec *spec;
SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d",
SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type),
@@ -2670,6 +2331,7 @@ ssl3_SendRecord(sslSocket *ss,
PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn));
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
+ PORT_Assert(SSL_BUFFER_LEN(wrBuf) == 0);
if (ss->ssl3.fatalAlertSent) {
SSL_TRC(3, ("%d: SSL3[%d] Suppress write, fatal alert already sent",
@@ -2677,114 +2339,41 @@ ssl3_SendRecord(sslSocket *ss,
return SECFailure;
}
- capRecordVersion = ((flags & ssl_SEND_FLAG_CAP_RECORD_VERSION) != 0);
-
- if (capRecordVersion) {
- /* ssl_SEND_FLAG_CAP_RECORD_VERSION can only be used with the
- * TLS initial ClientHello. */
- PORT_Assert(!IS_DTLS(ss));
- PORT_Assert(!ss->firstHsDone);
- PORT_Assert(type == content_handshake);
- PORT_Assert(ss->ssl3.hs.ws == wait_server_hello);
- }
-
- if (ss->ssl3.initialized == PR_FALSE) {
- /* This can happen on a server if the very first incoming record
- ** looks like a defective ssl3 record (e.g. too long), and we're
- ** trying to send an alert.
- */
- PR_ASSERT(type == content_alert);
- ssl3_InitState(ss);
- }
-
/* check for Token Presence */
if (!ssl3_ClientAuthTokenPresent(ss->sec.ci.sid)) {
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
return SECFailure;
}
- while (nIn > 0) {
- PRUint32 contentLen = PR_MIN(nIn, MAX_FRAGMENT_LENGTH);
- unsigned int spaceNeeded;
- unsigned int numRecords;
-
- ssl_GetSpecReadLock(ss); /********************************/
+ if (cwSpec) {
+ /* cwSpec can only be set for retransmissions of the DTLS handshake. */
+ PORT_Assert(IS_DTLS(ss) &&
+ (type == content_handshake ||
+ type == content_change_cipher_spec));
+ spec = cwSpec;
+ } else {
+ spec = ss->ssl3.cwSpec;
+ }
- if (nIn > 1 && ss->opt.cbcRandomIV &&
- ss->ssl3.cwSpec->version < SSL_LIBRARY_VERSION_TLS_1_1 &&
- type == content_application_data &&
- ss->ssl3.cwSpec->cipher_def->type == type_block /* CBC mode */) {
- /* We will split the first byte of the record into its own record,
- * as explained in the documentation for SSL_CBC_RANDOM_IV in ssl.h
- */
- numRecords = 2;
- } else {
- numRecords = 1;
- }
+ while (nIn > 0) {
+ unsigned int written = 0;
+ PRInt32 sent;
- spaceNeeded = contentLen + (numRecords * SSL3_BUFFER_FUDGE);
- if (ss->ssl3.cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1 &&
- ss->ssl3.cwSpec->cipher_def->type == type_block) {
- spaceNeeded += ss->ssl3.cwSpec->cipher_def->iv_size;
- }
- if (spaceNeeded > wrBuf->space) {
- rv = sslBuffer_Grow(wrBuf, spaceNeeded);
- if (rv != SECSuccess) {
- SSL_DBG(("%d: SSL3[%d]: SendRecord, tried to get %d bytes",
- SSL_GETPID(), ss->fd, spaceNeeded));
- goto spec_locked_loser; /* sslBuffer_Grow set error code. */
- }
+ ssl_GetSpecReadLock(ss);
+ rv = ssl_ProtectNextRecord(ss, spec, type, pIn, nIn, &written);
+ ssl_ReleaseSpecReadLock(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (numRecords == 2) {
- sslBuffer secondRecord;
- rv = ssl_ProtectRecord(ss, ss->ssl3.cwSpec, capRecordVersion, type,
- pIn, 1, wrBuf);
- if (rv != SECSuccess)
- goto spec_locked_loser;
-
- PRINT_BUF(50, (ss, "send (encrypted) record data [1/2]:",
- wrBuf->buf, wrBuf->len));
-
- secondRecord.buf = wrBuf->buf + wrBuf->len;
- secondRecord.len = 0;
- secondRecord.space = wrBuf->space - wrBuf->len;
-
- rv = ssl_ProtectRecord(ss, ss->ssl3.cwSpec, capRecordVersion, type,
- pIn + 1, contentLen - 1, &secondRecord);
- if (rv == SECSuccess) {
- PRINT_BUF(50, (ss, "send (encrypted) record data [2/2]:",
- secondRecord.buf, secondRecord.len));
- wrBuf->len += secondRecord.len;
- }
- } else {
- if (cwSpec) {
- /* cwSpec can only be set for retransmissions of DTLS handshake
- * messages. */
- PORT_Assert(IS_DTLS(ss) &&
- (type == content_handshake ||
- type == content_change_cipher_spec));
- spec = cwSpec;
- } else {
- spec = ss->ssl3.cwSpec;
- }
-
- rv = ssl_ProtectRecord(ss, spec, !IS_DTLS(ss) && capRecordVersion,
- type, pIn, contentLen, wrBuf);
- if (rv == SECSuccess) {
- PRINT_BUF(50, (ss, "send (encrypted) record data:",
- wrBuf->buf, wrBuf->len));
- }
+ PORT_Assert(written > 0);
+ /* DTLS should not fragment non-application data here. */
+ if (IS_DTLS(ss) && type != content_application_data) {
+ PORT_Assert(written == nIn);
}
- spec_locked_loser:
- ssl_ReleaseSpecReadLock(ss); /************************************/
-
- if (rv != SECSuccess)
- return SECFailure;
-
- pIn += contentLen;
- nIn -= contentLen;
+ pIn += written;
+ nIn -= written;
PORT_Assert(nIn >= 0);
/* If there's still some previously saved ciphertext,
@@ -2794,58 +2383,64 @@ ssl3_SendRecord(sslSocket *ss,
if ((ss->pendingBuf.len > 0) ||
(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) {
- rv = ssl_SaveWriteData(ss, wrBuf->buf, wrBuf->len);
+ rv = ssl_SaveWriteData(ss, SSL_BUFFER_BASE(wrBuf),
+ SSL_BUFFER_LEN(wrBuf));
if (rv != SECSuccess) {
/* presumably a memory error, SEC_ERROR_NO_MEMORY */
- return SECFailure;
+ goto loser;
}
- wrBuf->len = 0; /* All cipher text is saved away. */
if (!(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) {
- PRInt32 sent;
ss->handshakeBegun = 1;
sent = ssl_SendSavedWriteData(ss);
if (sent < 0 && PR_GetError() != PR_WOULD_BLOCK_ERROR) {
ssl_MapLowLevelError(SSL_ERROR_SOCKET_WRITE_FAILURE);
- return SECFailure;
+ goto loser;
}
if (ss->pendingBuf.len) {
flags |= ssl_SEND_FLAG_FORCE_INTO_BUFFER;
}
}
- } else if (wrBuf->len > 0) {
- PRInt32 sent;
+ } else {
+ PORT_Assert(SSL_BUFFER_LEN(wrBuf) > 0);
ss->handshakeBegun = 1;
- sent = ssl_DefSend(ss, wrBuf->buf, wrBuf->len,
+ sent = ssl_DefSend(ss, SSL_BUFFER_BASE(wrBuf),
+ SSL_BUFFER_LEN(wrBuf),
flags & ~ssl_SEND_FLAG_MASK);
if (sent < 0) {
- if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ if (PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
ssl_MapLowLevelError(SSL_ERROR_SOCKET_WRITE_FAILURE);
- return SECFailure;
+ goto loser;
}
/* we got PR_WOULD_BLOCK_ERROR, which means none was sent. */
sent = 0;
}
- wrBuf->len -= sent;
- if (wrBuf->len) {
+ if (SSL_BUFFER_LEN(wrBuf) > (unsigned int)sent) {
if (IS_DTLS(ss)) {
/* DTLS just says no in this case. No buffering */
- PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
- return SECFailure;
+ PORT_SetError(PR_WOULD_BLOCK_ERROR);
+ goto loser;
}
/* now take all the remaining unsent new ciphertext and
* append it to the buffer of previously unsent ciphertext.
*/
- rv = ssl_SaveWriteData(ss, wrBuf->buf + sent, wrBuf->len);
+ rv = ssl_SaveWriteData(ss, SSL_BUFFER_BASE(wrBuf) + sent,
+ SSL_BUFFER_LEN(wrBuf) - sent);
if (rv != SECSuccess) {
/* presumably a memory error, SEC_ERROR_NO_MEMORY */
- return SECFailure;
+ goto loser;
}
}
}
- totalSent += contentLen;
+ wrBuf->len = 0;
+ totalSent += written;
}
return totalSent;
+
+loser:
+ /* Don't leave bits of buffer lying around. */
+ wrBuf->len = 0;
+ return -1;
}
#define SSL3_PENDING_HIGH_WATER 1024
@@ -2859,6 +2454,7 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
{
PRInt32 totalSent = 0;
PRInt32 discarded = 0;
+ PRBool splitNeeded = PR_FALSE;
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
/* These flags for internal use only */
@@ -2885,6 +2481,16 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
len--;
discarded = 1;
}
+
+ /* We will split the first byte of the record into its own record, as
+ * explained in the documentation for SSL_CBC_RANDOM_IV in ssl.h.
+ */
+ if (len > 1 && ss->opt.cbcRandomIV &&
+ ss->version < SSL_LIBRARY_VERSION_TLS_1_1 &&
+ ss->ssl3.cwSpec->cipherDef->type == type_block /* CBC */) {
+ splitNeeded = PR_TRUE;
+ }
+
while (len > totalSent) {
PRInt32 sent, toSend;
@@ -2899,7 +2505,13 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
PR_Sleep(PR_INTERVAL_NO_WAIT); /* PR_Yield(); */
ssl_GetXmitBufLock(ss);
}
- toSend = PR_MIN(len - totalSent, MAX_FRAGMENT_LENGTH);
+
+ if (splitNeeded) {
+ toSend = 1;
+ splitNeeded = PR_FALSE;
+ } else {
+ toSend = PR_MIN(len - totalSent, MAX_FRAGMENT_LENGTH);
+ }
/*
* Note that the 0 epoch is OK because flags will never require
@@ -2959,9 +2571,8 @@ ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
{
if (IS_DTLS(ss)) {
return dtls_FlushHandshakeMessages(ss, flags);
- } else {
- return ssl3_FlushHandshakeMessages(ss, flags);
}
+ return ssl3_FlushHandshakeMessages(ss, flags);
}
/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
@@ -2973,8 +2584,7 @@ ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
static SECStatus
ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
{
- static const PRInt32 allowedFlags = ssl_SEND_FLAG_FORCE_INTO_BUFFER |
- ssl_SEND_FLAG_CAP_RECORD_VERSION;
+ static const PRInt32 allowedFlags = ssl_SEND_FLAG_FORCE_INTO_BUFFER;
PRInt32 count = -1;
SECStatus rv;
@@ -3105,6 +2715,15 @@ SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc)
ss->sec.uncache(ss->sec.ci.sid);
}
}
+
+ rv = tls13_SetAlertCipherSpec(ss);
+ if (rv != SECSuccess) {
+ if (needHsLock) {
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ }
+ return rv;
+ }
+
ssl_GetXmitBufLock(ss);
rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
if (rv == SECSuccess) {
@@ -3340,9 +2959,6 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
case bad_certificate_hash_value:
error = SSL_ERROR_BAD_CERT_HASH_VALUE_ALERT;
break;
- case end_of_early_data:
- error = SSL_ERROR_END_OF_EARLY_DATA_ALERT;
- break;
default:
error = SSL_ERROR_RX_UNKNOWN_ALERT;
break;
@@ -3354,7 +2970,6 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
switch (desc) {
case close_notify:
case user_canceled:
- case end_of_early_data:
break;
default:
level = alert_fatal;
@@ -3374,9 +2989,6 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
PORT_SetError(error);
return SECFailure;
}
- if (desc == end_of_early_data) {
- return tls13_HandleEndOfEarlyData(ss);
- }
if ((desc == no_certificate) && (ss->ssl3.hs.ws == wait_client_cert)) {
/* I'm a server. I've requested a client cert. He hasn't got one. */
SECStatus rv;
@@ -3399,59 +3011,64 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
* and pending write spec pointers.
*/
-static SECStatus
-ssl3_SendChangeCipherSpecs(sslSocket *ss)
+SECStatus
+ssl3_SendChangeCipherSpecsInt(sslSocket *ss)
{
PRUint8 change = change_cipher_spec_choice;
- ssl3CipherSpec *pwSpec;
SECStatus rv;
- PRInt32 sent;
SSL_TRC(3, ("%d: SSL3[%d]: send change_cipher_spec record",
SSL_GETPID(), ss->fd));
- PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
if (rv != SECSuccess) {
- return rv; /* error code set by ssl3_FlushHandshake */
+ return SECFailure; /* error code set by ssl3_FlushHandshake */
}
+
if (!IS_DTLS(ss)) {
- sent = ssl3_SendRecord(ss, NULL, content_change_cipher_spec, &change, 1,
- ssl_SEND_FLAG_FORCE_INTO_BUFFER);
+ PRInt32 sent;
+ sent = ssl3_SendRecord(ss, NULL, content_change_cipher_spec,
+ &change, 1, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
if (sent < 0) {
- return (SECStatus)sent; /* error code set by ssl3_SendRecord */
+ return SECFailure; /* error code set by ssl3_SendRecord */
}
} else {
+ SECStatus rv;
rv = dtls_QueueMessage(ss, content_change_cipher_spec, &change, 1);
if (rv != SECSuccess) {
- return rv;
+ return SECFailure;
}
}
+ return SECSuccess;
+}
+
+static SECStatus
+ssl3_SendChangeCipherSpecs(sslSocket *ss)
+{
+ SECStatus rv;
+
+ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
+ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+ rv = ssl3_SendChangeCipherSpecsInt(ss);
+ if (rv != SECSuccess) {
+ return rv; /* Error code set. */
+ }
/* swap the pending and current write specs. */
ssl_GetSpecWriteLock(ss); /**************************************/
- pwSpec = ss->ssl3.pwSpec;
- ss->ssl3.pwSpec = ss->ssl3.cwSpec;
- ss->ssl3.cwSpec = pwSpec;
+ ssl_CipherSpecRelease(ss->ssl3.cwSpec);
+ ss->ssl3.cwSpec = ss->ssl3.pwSpec;
+ ss->ssl3.pwSpec = NULL;
SSL_TRC(3, ("%d: SSL3[%d] Set Current Write Cipher Suite to Pending",
SSL_GETPID(), ss->fd));
- /* We need to free up the contexts, keys and certs ! */
- /* If we are really through with the old cipher spec
- * (Both the read and write sides have changed) destroy it.
- */
- if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
- if (!IS_DTLS(ss)) {
- ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE /*freeSrvName*/);
- } else {
- /* With DTLS, we need to set a holddown timer in case the final
- * message got lost */
- rv = dtls_StartHolddownTimer(ss);
- }
+ /* With DTLS, we need to set a holddown timer in case the final
+ * message got lost */
+ if (IS_DTLS(ss) && ss->ssl3.crSpec->epoch == ss->ssl3.cwSpec->epoch) {
+ rv = dtls_StartHolddownTimer(ss);
}
ssl_ReleaseSpecWriteLock(ss); /**************************************/
@@ -3467,7 +3084,6 @@ ssl3_SendChangeCipherSpecs(sslSocket *ss)
static SECStatus
ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
{
- ssl3CipherSpec *prSpec;
SSL3WaitState ws = ss->ssl3.hs.ws;
SSL3ChangeCipherSpecChoice change;
@@ -3477,19 +3093,18 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
SSL_TRC(3, ("%d: SSL3[%d]: handle change_cipher_spec record",
SSL_GETPID(), ss->fd));
- if (ws != wait_change_cipher) {
- if (IS_DTLS(ss)) {
- /* Ignore this because it's out of order. */
- SSL_TRC(3, ("%d: SSL3[%d]: discard out of order "
- "DTLS change_cipher_spec",
- SSL_GETPID(), ss->fd));
- buf->len = 0;
- return SECSuccess;
- }
- (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
- PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
- return SECFailure;
+ /* For DTLS: Ignore this if we aren't expecting it. Don't kill a connection
+ * as a result of receiving trash.
+ * For TLS: Maybe ignore, but only after checking format. */
+ if (ws != wait_change_cipher && IS_DTLS(ss)) {
+ /* Ignore this because it's out of order. */
+ SSL_TRC(3, ("%d: SSL3[%d]: discard out of order "
+ "DTLS change_cipher_spec",
+ SSL_GETPID(), ss->fd));
+ buf->len = 0;
+ return SECSuccess;
}
+
/* Handshake messages should not span ChangeCipherSpec. */
if (ss->ssl3.hs.header_bytes) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
@@ -3508,26 +3123,44 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
PORT_SetError(SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER);
return SECFailure;
}
- buf->len = 0;
- /* Swap the pending and current read specs. */
- ssl_GetSpecWriteLock(ss); /*************************************/
- prSpec = ss->ssl3.prSpec;
-
- ss->ssl3.prSpec = ss->ssl3.crSpec;
- ss->ssl3.crSpec = prSpec;
- ss->ssl3.hs.ws = wait_finished;
+ buf->len = 0;
+ if (ws != wait_change_cipher) {
+ /* Ignore a CCS for TLS 1.3. This only happens if the server sends a
+ * HelloRetryRequest. In other cases, the CCS will fail decryption and
+ * will be discarded by ssl3_HandleRecord(). */
+ if (ws == wait_server_hello &&
+ ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+ ss->ssl3.hs.helloRetry) {
+ PORT_Assert(!ss->sec.isServer);
+ return SECSuccess;
+ }
+ /* Note: For a server, we can't test ss->ssl3.hs.helloRetry or
+ * ss->version because the server might be stateless (and so it won't
+ * have set either value yet). Set a flag so that at least we will
+ * guarantee that the server will treat any ClientHello properly. */
+ if (ws == wait_client_hello &&
+ ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+ !ss->ssl3.hs.receivedCcs) {
+ PORT_Assert(ss->sec.isServer);
+ ss->ssl3.hs.receivedCcs = PR_TRUE;
+ return SECSuccess;
+ }
+ (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+ return SECFailure;
+ }
SSL_TRC(3, ("%d: SSL3[%d] Set Current Read Cipher Suite to Pending",
SSL_GETPID(), ss->fd));
-
- /* If we are really through with the old cipher prSpec
- * (Both the read and write sides have changed) destroy it.
- */
- if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
- ssl3_DestroyCipherSpec(ss->ssl3.prSpec, PR_FALSE /*freeSrvName*/);
- }
+ ssl_GetSpecWriteLock(ss); /*************************************/
+ PORT_Assert(ss->ssl3.prSpec);
+ ssl_CipherSpecRelease(ss->ssl3.crSpec);
+ ss->ssl3.crSpec = ss->ssl3.prSpec;
+ ss->ssl3.prSpec = NULL;
ssl_ReleaseSpecWriteLock(ss); /*************************************/
+
+ ss->ssl3.hs.ws = wait_finished;
return SECSuccess;
}
@@ -3650,12 +3283,8 @@ static SECStatus
ssl3_ComputeMasterSecretInt(sslSocket *ss, PK11SymKey *pms,
PK11SymKey **msp)
{
- ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
- unsigned char *cr = (unsigned char *)&ss->ssl3.hs.client_random;
- unsigned char *sr = (unsigned char *)&ss->ssl3.hs.server_random;
- PRBool isTLS = (PRBool)(pwSpec->version > SSL_LIBRARY_VERSION_3_0);
- PRBool isTLS12 =
- (PRBool)(isTLS && pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
+ PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
+ PRBool isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2);
/*
* Whenever isDH is true, we need to use CKM_TLS_MASTER_KEY_DERIVE_DH
* which, unlike CKM_TLS_MASTER_KEY_DERIVE, converts arbitrary size
@@ -3701,9 +3330,9 @@ ssl3_ComputeMasterSecretInt(sslSocket *ss, PK11SymKey *pms,
}
master_params.pVersion = pms_version_ptr;
- master_params.RandomInfo.pClientRandom = cr;
+ master_params.RandomInfo.pClientRandom = ss->ssl3.hs.client_random;
master_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH;
- master_params.RandomInfo.pServerRandom = sr;
+ master_params.RandomInfo.pServerRandom = ss->ssl3.hs.server_random;
master_params.RandomInfo.ulServerRandomLen = SSL3_RANDOM_LENGTH;
if (isTLS12) {
master_params.prfHashMechanism = ssl3_GetPrfHashMechanism(ss);
@@ -3763,7 +3392,7 @@ tls_ComputeExtendedMasterSecretInt(sslSocket *ss, PK11SymKey *pms,
pms_version_ptr = &pms_version;
}
- if (pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
/* TLS 1.2+ */
extended_master_params.prfHashMechanism = ssl3_GetPrfHashMechanism(ss);
key_derive = CKM_TLS12_KEY_AND_MAC_DERIVE;
@@ -3795,7 +3424,6 @@ ssl3_ComputeMasterSecret(sslSocket *ss, PK11SymKey *pms,
{
PORT_Assert(pms != NULL);
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
if (ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn)) {
return tls_ComputeExtendedMasterSecretInt(ss, pms, msp);
@@ -3804,36 +3432,6 @@ ssl3_ComputeMasterSecret(sslSocket *ss, PK11SymKey *pms,
}
}
-/* This method uses PKCS11 to derive the MS from the PMS, where PMS
-** is a PKCS11 symkey. We call ssl3_ComputeMasterSecret to do the
-** computations and then modify the pwSpec->state as a side effect.
-**
-** This is used in all cases except the "triple bypass" with RSA key
-** exchange.
-**
-** Called from ssl3_InitPendingCipherSpec. prSpec is pwSpec.
-*/
-static SECStatus
-ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms)
-{
- SECStatus rv;
- PK11SymKey *ms = NULL;
- ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
-
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
-
- if (pms) {
- rv = ssl3_ComputeMasterSecret(ss, pms, &ms);
- pwSpec->master_secret = ms;
- if (rv != SECSuccess)
- return rv;
- }
-
- return SECSuccess;
-}
-
/*
* Derive encryption and MAC Keys (and IVs) from master secret
* Sets a useful error code when returning SECFailure.
@@ -3850,17 +3448,18 @@ ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms)
*
*/
static SECStatus
-ssl3_DeriveConnectionKeys(sslSocket *ss)
+ssl3_DeriveConnectionKeys(sslSocket *ss, PK11SymKey *masterSecret)
{
ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
- unsigned char *cr = (unsigned char *)&ss->ssl3.hs.client_random;
- unsigned char *sr = (unsigned char *)&ss->ssl3.hs.server_random;
- PRBool isTLS = (PRBool)(pwSpec->version > SSL_LIBRARY_VERSION_3_0);
+ ssl3CipherSpec *prSpec = ss->ssl3.prSpec;
+ ssl3CipherSpec *clientSpec;
+ ssl3CipherSpec *serverSpec;
+ PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
PRBool isTLS12 =
- (PRBool)(isTLS && pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
- const ssl3BulkCipherDef *cipher_def = pwSpec->cipher_def;
+ (PRBool)(isTLS && ss->version >= SSL_LIBRARY_VERSION_TLS_1_2);
+ const ssl3BulkCipherDef *cipher_def = pwSpec->cipherDef;
PK11SlotInfo *slot = NULL;
- PK11SymKey *symKey = NULL;
+ PK11SymKey *derivedKeyHandle = NULL;
void *pwArg = ss->pkcs11PinArg;
int keySize;
CK_TLS12_KEY_MAT_PARAMS key_material_params; /* may be used as a
@@ -3871,48 +3470,53 @@ ssl3_DeriveConnectionKeys(sslSocket *ss)
CK_MECHANISM_TYPE bulk_mechanism;
SSLCipherAlgorithm calg;
SECItem params;
- PRBool skipKeysAndIVs = (PRBool)(cipher_def->calg == calg_null);
+ PRBool skipKeysAndIVs = (PRBool)(cipher_def->calg == ssl_calg_null);
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
+ PORT_Assert(masterSecret);
- if (!pwSpec->master_secret) {
- PORT_SetError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
- return SECFailure;
+ /* These functions operate in terms of who is writing specs. */
+ if (ss->sec.isServer) {
+ clientSpec = prSpec;
+ serverSpec = pwSpec;
+ } else {
+ clientSpec = pwSpec;
+ serverSpec = prSpec;
}
+
/*
* generate the key material
*/
- key_material_params.ulMacSizeInBits = pwSpec->mac_size * BPB;
- key_material_params.ulKeySizeInBits = cipher_def->secret_key_size * BPB;
- key_material_params.ulIVSizeInBits = cipher_def->iv_size * BPB;
if (cipher_def->type == type_block &&
- pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+ ss->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
/* Block ciphers in >= TLS 1.1 use a per-record, explicit IV. */
key_material_params.ulIVSizeInBits = 0;
- memset(pwSpec->client.write_iv, 0, cipher_def->iv_size);
- memset(pwSpec->server.write_iv, 0, cipher_def->iv_size);
+ PORT_Memset(clientSpec->keyMaterial.iv, 0, cipher_def->iv_size);
+ PORT_Memset(serverSpec->keyMaterial.iv, 0, cipher_def->iv_size);
}
key_material_params.bIsExport = PR_FALSE;
- key_material_params.RandomInfo.pClientRandom = cr;
+ key_material_params.RandomInfo.pClientRandom = ss->ssl3.hs.client_random;
key_material_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH;
- key_material_params.RandomInfo.pServerRandom = sr;
+ key_material_params.RandomInfo.pServerRandom = ss->ssl3.hs.server_random;
key_material_params.RandomInfo.ulServerRandomLen = SSL3_RANDOM_LENGTH;
key_material_params.pReturnedKeyMaterial = &returnedKeys;
- returnedKeys.pIVClient = pwSpec->client.write_iv;
- returnedKeys.pIVServer = pwSpec->server.write_iv;
- keySize = cipher_def->key_size;
-
if (skipKeysAndIVs) {
keySize = 0;
- key_material_params.ulKeySizeInBits = 0;
- key_material_params.ulIVSizeInBits = 0;
returnedKeys.pIVClient = NULL;
returnedKeys.pIVServer = NULL;
+ key_material_params.ulKeySizeInBits = 0;
+ key_material_params.ulIVSizeInBits = 0;
+ } else {
+ keySize = cipher_def->key_size;
+ returnedKeys.pIVClient = clientSpec->keyMaterial.iv;
+ returnedKeys.pIVServer = serverSpec->keyMaterial.iv;
+ key_material_params.ulKeySizeInBits = cipher_def->secret_key_size * BPB;
+ key_material_params.ulIVSizeInBits = cipher_def->iv_size * BPB;
}
+ key_material_params.ulMacSizeInBits = pwSpec->macDef->mac_size * BPB;
calg = cipher_def->calg;
bulk_mechanism = ssl3_Alg2Mech(calg);
@@ -3934,9 +3538,9 @@ ssl3_DeriveConnectionKeys(sslSocket *ss)
/* CKM_SSL3_KEY_AND_MAC_DERIVE is defined to set ENCRYPT, DECRYPT, and
* DERIVE by DEFAULT */
- symKey = PK11_Derive(pwSpec->master_secret, key_derive, &params,
- bulk_mechanism, CKA_ENCRYPT, keySize);
- if (!symKey) {
+ derivedKeyHandle = PK11_Derive(masterSecret, key_derive, &params,
+ bulk_mechanism, CKA_ENCRYPT, keySize);
+ if (!derivedKeyHandle) {
ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
return SECFailure;
}
@@ -3944,41 +3548,44 @@ ssl3_DeriveConnectionKeys(sslSocket *ss)
* don't because these types are used to map keytype anyway and both
* mac's map to the same keytype.
*/
- slot = PK11_GetSlotFromKey(symKey);
+ slot = PK11_GetSlotFromKey(derivedKeyHandle);
PK11_FreeSlot(slot); /* slot is held until the key is freed */
- pwSpec->client.write_mac_key =
- PK11_SymKeyFromHandle(slot, symKey, PK11_OriginDerive,
- CKM_SSL3_SHA1_MAC, returnedKeys.hClientMacSecret, PR_TRUE, pwArg);
- if (pwSpec->client.write_mac_key == NULL) {
+ clientSpec->keyMaterial.macKey =
+ PK11_SymKeyFromHandle(slot, derivedKeyHandle, PK11_OriginDerive,
+ CKM_SSL3_SHA1_MAC, returnedKeys.hClientMacSecret,
+ PR_TRUE, pwArg);
+ if (clientSpec->keyMaterial.macKey == NULL) {
goto loser; /* loser sets err */
}
- pwSpec->server.write_mac_key =
- PK11_SymKeyFromHandle(slot, symKey, PK11_OriginDerive,
- CKM_SSL3_SHA1_MAC, returnedKeys.hServerMacSecret, PR_TRUE, pwArg);
- if (pwSpec->server.write_mac_key == NULL) {
+ serverSpec->keyMaterial.macKey =
+ PK11_SymKeyFromHandle(slot, derivedKeyHandle, PK11_OriginDerive,
+ CKM_SSL3_SHA1_MAC, returnedKeys.hServerMacSecret,
+ PR_TRUE, pwArg);
+ if (serverSpec->keyMaterial.macKey == NULL) {
goto loser; /* loser sets err */
}
if (!skipKeysAndIVs) {
- pwSpec->client.write_key =
- PK11_SymKeyFromHandle(slot, symKey, PK11_OriginDerive,
- bulk_mechanism, returnedKeys.hClientKey, PR_TRUE, pwArg);
- if (pwSpec->client.write_key == NULL) {
+ clientSpec->keyMaterial.key =
+ PK11_SymKeyFromHandle(slot, derivedKeyHandle, PK11_OriginDerive,
+ bulk_mechanism, returnedKeys.hClientKey,
+ PR_TRUE, pwArg);
+ if (clientSpec->keyMaterial.key == NULL) {
goto loser; /* loser sets err */
}
- pwSpec->server.write_key =
- PK11_SymKeyFromHandle(slot, symKey, PK11_OriginDerive,
- bulk_mechanism, returnedKeys.hServerKey, PR_TRUE, pwArg);
- if (pwSpec->server.write_key == NULL) {
+ serverSpec->keyMaterial.key =
+ PK11_SymKeyFromHandle(slot, derivedKeyHandle, PK11_OriginDerive,
+ bulk_mechanism, returnedKeys.hServerKey,
+ PR_TRUE, pwArg);
+ if (serverSpec->keyMaterial.key == NULL) {
goto loser; /* loser sets err */
}
}
- PK11_FreeSymKey(symKey);
+ PK11_FreeSymKey(derivedKeyHandle);
return SECSuccess;
loser:
- if (symKey)
- PK11_FreeSymKey(symKey);
+ PK11_FreeSymKey(derivedKeyHandle);
ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
return SECFailure;
}
@@ -4022,11 +3629,11 @@ ssl3_InitHandshakeHashes(sslSocket *ss)
return SECFailure;
}
ss->ssl3.hs.hashType = handshake_hash_single;
-
if (PK11_DigestBegin(ss->ssl3.hs.sha) != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return SECFailure;
}
+
} else {
/* Both ss->ssl3.hs.md5 and ss->ssl3.hs.sha should be NULL or
* created successfully. */
@@ -4117,7 +3724,7 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l
return sslBuffer_Append(&ss->ssl3.hs.messages, b, l);
}
- PRINT_BUF(90, (NULL, "handshake hash input:", b, l));
+ PRINT_BUF(90, (ss, "handshake hash input:", b, l));
if (ss->ssl3.hs.hashType == handshake_hash_single) {
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
@@ -4141,104 +3748,8 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l
return rv;
}
-/**************************************************************************
- * Append Handshake functions.
- * All these functions set appropriate error codes.
- * Most rely on ssl3_AppendHandshake to set the error code.
- **************************************************************************/
-SECStatus
-ssl3_AppendHandshake(sslSocket *ss, const void *void_src, PRInt32 bytes)
-{
- unsigned char *src = (unsigned char *)void_src;
- int room = ss->sec.ci.sendBuf.space - ss->sec.ci.sendBuf.len;
- SECStatus rv;
-
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); /* protects sendBuf. */
-
- if (!bytes)
- return SECSuccess;
- if (ss->sec.ci.sendBuf.space < MAX_SEND_BUF_LENGTH && room < bytes) {
- rv = sslBuffer_Grow(&ss->sec.ci.sendBuf, PR_MAX(MIN_SEND_BUF_LENGTH,
- PR_MIN(MAX_SEND_BUF_LENGTH, ss->sec.ci.sendBuf.len + bytes)));
- if (rv != SECSuccess)
- return rv; /* sslBuffer_Grow has set a memory error code. */
- room = ss->sec.ci.sendBuf.space - ss->sec.ci.sendBuf.len;
- }
-
- PRINT_BUF(60, (ss, "Append to Handshake", (unsigned char *)void_src, bytes));
- rv = ssl3_UpdateHandshakeHashes(ss, src, bytes);
- if (rv != SECSuccess)
- return rv; /* error code set by ssl3_UpdateHandshakeHashes */
-
- while (bytes > room) {
- if (room > 0)
- PORT_Memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, src,
- room);
- ss->sec.ci.sendBuf.len += room;
- rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
- if (rv != SECSuccess) {
- return rv; /* error code set by ssl3_FlushHandshake */
- }
- bytes -= room;
- src += room;
- room = ss->sec.ci.sendBuf.space;
- PORT_Assert(ss->sec.ci.sendBuf.len == 0);
- }
- PORT_Memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, src, bytes);
- ss->sec.ci.sendBuf.len += bytes;
- return SECSuccess;
-}
-
-SECStatus
-ssl3_AppendHandshakeNumber(sslSocket *ss, PRInt32 num, PRInt32 lenSize)
-{
- SECStatus rv;
- PRUint8 b[4];
- PRUint8 *p = b;
-
- PORT_Assert(lenSize <= 4 && lenSize > 0);
- if (lenSize < 4 && num >= (1L << (lenSize * 8))) {
- PORT_SetError(SSL_ERROR_TX_RECORD_TOO_LONG);
- return SECFailure;
- }
-
- switch (lenSize) {
- case 4:
- *p++ = (num >> 24) & 0xff;
- case 3:
- *p++ = (num >> 16) & 0xff;
- case 2:
- *p++ = (num >> 8) & 0xff;
- case 1:
- *p = num & 0xff;
- }
- SSL_TRC(60, ("%d: number:", SSL_GETPID()));
- rv = ssl3_AppendHandshake(ss, &b[0], lenSize);
- return rv; /* error code set by AppendHandshake, if applicable. */
-}
-
-SECStatus
-ssl3_AppendHandshakeVariable(
- sslSocket *ss, const PRUint8 *src, PRInt32 bytes, PRInt32 lenSize)
-{
- SECStatus rv;
-
- PORT_Assert((bytes < (1 << 8) && lenSize == 1) ||
- (bytes < (1L << 16) && lenSize == 2) ||
- (bytes < (1L << 24) && lenSize == 3));
-
- SSL_TRC(60, ("%d: append variable:", SSL_GETPID()));
- rv = ssl3_AppendHandshakeNumber(ss, bytes, lenSize);
- if (rv != SECSuccess) {
- return rv; /* error code set by AppendHandshake, if applicable. */
- }
- SSL_TRC(60, ("data:"));
- rv = ssl3_AppendHandshake(ss, src, bytes);
- return rv; /* error code set by AppendHandshake, if applicable. */
-}
-
SECStatus
-ssl3_AppendHandshakeHeader(sslSocket *ss, SSL3HandshakeType t, PRUint32 length)
+ssl3_AppendHandshakeHeader(sslSocket *ss, SSLHandshakeType t, PRUint32 length)
{
SECStatus rv;
@@ -4330,17 +3841,22 @@ ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes, PRUint8 **b,
* On error, an alert has been sent, and a generic error code has been set.
*/
SECStatus
-ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
- PRUint8 **b, PRUint32 *length)
+ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num, PRUint32 bytes,
+ PRUint8 **b, PRUint32 *length)
{
PRUint8 *buf = *b;
- int i;
+ PRUint32 i;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
*num = 0;
- if (bytes > *length || bytes > sizeof(*num)) {
+ if (bytes > sizeof(*num)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if (bytes > *length) {
return ssl3_DecodeError(ss);
}
PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
@@ -4353,6 +3869,26 @@ ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
return SECSuccess;
}
+SECStatus
+ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
+ PRUint8 **b, PRUint32 *length)
+{
+ PRUint64 num64;
+ SECStatus rv;
+
+ PORT_Assert(bytes <= sizeof(*num));
+ if (bytes > sizeof(*num)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ rv = ssl3_ConsumeHandshakeNumber64(ss, &num64, bytes, b, length);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ *num = num64 & 0xffffffff;
+ return SECSuccess;
+}
+
/* Read in two values from the incoming decrypted byte stream "b", which is
* *length bytes long. The first value is a number whose size is "bytes"
* bytes long. The second value is a byte-string whose size is the value
@@ -4762,6 +4298,8 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
unsigned int md5StateLen, shaStateLen;
unsigned char md5StackBuf[256];
unsigned char shaStackBuf[512];
+ const int md5Pad = ssl_GetMacDefByAlg(ssl_mac_md5)->pad_size;
+ const int shaPad = ssl_GetMacDefByAlg(ssl_mac_sha)->pad_size;
md5StateBuf = PK11_SaveContextAlloc(ss->ssl3.hs.md5, md5StackBuf,
sizeof md5StackBuf, &md5StateLen);
@@ -4783,7 +4321,7 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
/* compute hashes for SSL3. */
unsigned char s[4];
- if (!spec->master_secret) {
+ if (!spec->masterSecret) {
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE);
rv = SECFailure;
goto loser;
@@ -4799,11 +4337,10 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
PRINT_BUF(95, (NULL, "MD5 inner: sender", s, 4));
}
- PRINT_BUF(95, (NULL, "MD5 inner: MAC Pad 1", mac_pad_1,
- mac_defs[mac_md5].pad_size));
+ PRINT_BUF(95, (NULL, "MD5 inner: MAC Pad 1", mac_pad_1, md5Pad));
- rv |= PK11_DigestKey(md5, spec->master_secret);
- rv |= PK11_DigestOp(md5, mac_pad_1, mac_defs[mac_md5].pad_size);
+ rv |= PK11_DigestKey(md5, spec->masterSecret);
+ rv |= PK11_DigestOp(md5, mac_pad_1, md5Pad);
rv |= PK11_DigestFinal(md5, md5_inner, &outLength, MD5_LENGTH);
PORT_Assert(rv != SECSuccess || outLength == MD5_LENGTH);
if (rv != SECSuccess) {
@@ -4819,11 +4356,10 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
PRINT_BUF(95, (NULL, "SHA inner: sender", s, 4));
}
- PRINT_BUF(95, (NULL, "SHA inner: MAC Pad 1", mac_pad_1,
- mac_defs[mac_sha].pad_size));
+ PRINT_BUF(95, (NULL, "SHA inner: MAC Pad 1", mac_pad_1, shaPad));
- rv |= PK11_DigestKey(sha, spec->master_secret);
- rv |= PK11_DigestOp(sha, mac_pad_1, mac_defs[mac_sha].pad_size);
+ rv |= PK11_DigestKey(sha, spec->masterSecret);
+ rv |= PK11_DigestOp(sha, mac_pad_1, shaPad);
rv |= PK11_DigestFinal(sha, sha_inner, &outLength, SHA1_LENGTH);
PORT_Assert(rv != SECSuccess || outLength == SHA1_LENGTH);
if (rv != SECSuccess) {
@@ -4834,13 +4370,12 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
PRINT_BUF(95, (NULL, "SHA inner: result", sha_inner, outLength));
- PRINT_BUF(95, (NULL, "MD5 outer: MAC Pad 2", mac_pad_2,
- mac_defs[mac_md5].pad_size));
+ PRINT_BUF(95, (NULL, "MD5 outer: MAC Pad 2", mac_pad_2, md5Pad));
PRINT_BUF(95, (NULL, "MD5 outer: MD5 inner", md5_inner, MD5_LENGTH));
rv |= PK11_DigestBegin(md5);
- rv |= PK11_DigestKey(md5, spec->master_secret);
- rv |= PK11_DigestOp(md5, mac_pad_2, mac_defs[mac_md5].pad_size);
+ rv |= PK11_DigestKey(md5, spec->masterSecret);
+ rv |= PK11_DigestOp(md5, mac_pad_2, md5Pad);
rv |= PK11_DigestOp(md5, md5_inner, MD5_LENGTH);
}
rv |= PK11_DigestFinal(md5, hashes->u.s.md5, &outLength, MD5_LENGTH);
@@ -4854,13 +4389,12 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
PRINT_BUF(60, (NULL, "MD5 outer: result", hashes->u.s.md5, MD5_LENGTH));
if (!isTLS) {
- PRINT_BUF(95, (NULL, "SHA outer: MAC Pad 2", mac_pad_2,
- mac_defs[mac_sha].pad_size));
+ PRINT_BUF(95, (NULL, "SHA outer: MAC Pad 2", mac_pad_2, shaPad));
PRINT_BUF(95, (NULL, "SHA outer: SHA inner", sha_inner, SHA1_LENGTH));
rv |= PK11_DigestBegin(sha);
- rv |= PK11_DigestKey(sha, spec->master_secret);
- rv |= PK11_DigestOp(sha, mac_pad_2, mac_defs[mac_sha].pad_size);
+ rv |= PK11_DigestKey(sha, spec->masterSecret);
+ rv |= PK11_DigestOp(sha, mac_pad_2, shaPad);
rv |= PK11_DigestOp(sha, sha_inner, SHA1_LENGTH);
}
rv |= PK11_DigestFinal(sha, hashes->u.s.sha, &outLength, SHA1_LENGTH);
@@ -4926,6 +4460,48 @@ ssl_ClientHelloTypeName(sslClientHelloType type)
#undef CHTYPE
#endif
+PR_STATIC_ASSERT(SSL3_SESSIONID_BYTES == SSL3_RANDOM_LENGTH);
+static void
+ssl_MakeFakeSid(sslSocket *ss, PRUint8 *buf)
+{
+ PRUint8 x = 0x5a;
+ int i;
+ for (i = 0; i < SSL3_SESSIONID_BYTES; ++i) {
+ x += ss->ssl3.hs.client_random[i];
+ buf[i] = x;
+ }
+}
+
+/* Set the version fields of the cipher spec for a ClientHello. */
+static void
+ssl_SetClientHelloSpecVersion(sslSocket *ss, ssl3CipherSpec *spec)
+{
+ ssl_GetSpecWriteLock(ss);
+ PORT_Assert(spec->cipherDef->cipher == cipher_null);
+ /* This is - a best guess - but it doesn't matter here. */
+ spec->version = ss->vrange.max;
+ if (IS_DTLS(ss)) {
+ spec->recordVersion = SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
+ } else {
+ /* For new connections, cap the record layer version number of TLS
+ * ClientHello to { 3, 1 } (TLS 1.0). Some TLS 1.0 servers (which seem
+ * to use F5 BIG-IP) ignore ClientHello.client_version and use the
+ * record layer version number (TLSPlaintext.version) instead when
+ * negotiating protocol versions. In addition, if the record layer
+ * version number of ClientHello is { 3, 2 } (TLS 1.1) or higher, these
+ * servers reset the TCP connections. Lastly, some F5 BIG-IP servers
+ * hang if a record containing a ClientHello has a version greater than
+ * { 3, 1 } and a length greater than 255. Set this flag to work around
+ * such servers.
+ *
+ * The final version is set when a version is negotiated.
+ */
+ spec->recordVersion = PR_MIN(SSL_LIBRARY_VERSION_TLS_1_0,
+ ss->vrange.max);
+ }
+ ssl_ReleaseSpecWriteLock(ss);
+}
+
/* Called from ssl3_HandleHelloRequest(),
* ssl3_RedoHandshake()
* ssl_BeginClientHandshake (when resuming ssl3 session)
@@ -4942,18 +4518,18 @@ SECStatus
ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
{
sslSessionID *sid;
- ssl3CipherSpec *cwSpec;
SECStatus rv;
- int i;
- int length;
- int num_suites;
- int actual_count = 0;
+ unsigned int i;
+ unsigned int length;
+ unsigned int num_suites;
+ unsigned int actual_count = 0;
PRBool isTLS = PR_FALSE;
PRBool requestingResume = PR_FALSE, fallbackSCSV = PR_FALSE;
- PRInt32 total_exten_len = 0;
- unsigned numCompressionMethods;
- PRUint16 version;
+ PRBool unlockNeeded = PR_FALSE;
+ sslBuffer extensionBuf = SSL_BUFFER_EMPTY;
+ PRUint16 version = ss->vrange.max;
PRInt32 flags;
+ unsigned int cookieLen = ss->ssl3.hs.cookie.len;
SSL_TRC(3, ("%d: SSL3[%d]: send %s ClientHello handshake", SSL_GETPID(),
ss->fd, ssl_ClientHelloTypeName(type)));
@@ -4972,22 +4548,26 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
* to maintain the handshake hashes. */
if (ss->ssl3.hs.helloRetry) {
PORT_Assert(type == client_hello_retry);
+ /* This cookieLen applies to the cookie that appears in the DTLS
+ ClientHello, which isn't used in DTLS 1.3. */
+ cookieLen = 0;
} else {
- ssl3_InitState(ss);
ssl3_RestartHandshakeHashes(ss);
}
+ if (type == client_hello_initial) {
+ ssl_SetClientHelloSpecVersion(ss, ss->ssl3.cwSpec);
+ }
/* These must be reset every handshake. */
+ ssl3_ResetExtensionData(&ss->xtnData, ss);
ss->ssl3.hs.sendingSCSV = PR_FALSE;
ss->ssl3.hs.preliminaryInfo = 0;
PORT_Assert(IS_DTLS(ss) || type != client_hello_retransmit);
SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
- ssl3_ResetExtensionData(&ss->xtnData);
/* How many suites does our PKCS11 support (regardless of policy)? */
- num_suites = ssl3_config_match_init(ss);
- if (!num_suites) {
+ if (ssl3_config_match_init(ss) == 0) {
return SECFailure; /* ssl3_config_match_init has set error code. */
}
@@ -5035,7 +4615,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
}
/* Check that we can recover the master secret. */
- if (sidOK && sid->u.ssl3.keys.msIsWrapped) {
+ if (sidOK) {
PK11SlotInfo *slot = NULL;
if (sid->u.ssl3.masterValid) {
slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
@@ -5100,8 +4680,6 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
if (sid->version < ss->vrange.min ||
sid->version > ss->vrange.max) {
sidOK = PR_FALSE;
- } else {
- version = ss->vrange.max;
}
}
}
@@ -5135,8 +4713,6 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
*/
if (ss->firstHsDone) {
version = ss->clientHelloVersion;
- } else {
- version = ss->vrange.max;
}
sid = ssl3_NewSessionID(ss, PR_FALSE);
@@ -5149,10 +4725,9 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
isTLS = (version > SSL_LIBRARY_VERSION_3_0);
ssl_GetSpecWriteLock(ss);
- cwSpec = ss->ssl3.cwSpec;
- if (cwSpec->mac_def->mac == mac_null) {
+ if (ss->ssl3.cwSpec->macDef->mac == ssl_mac_null) {
/* SSL records are not being MACed. */
- cwSpec->version = version;
+ ss->ssl3.cwSpec->version = version;
}
ssl_ReleaseSpecWriteLock(ss);
@@ -5176,9 +4751,10 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
* NewSessionTicket that will cause the ticket in the sid to be replaced.
* Once we've copied the session ticket into our ClientHello message, it
* is OK for the ticket to change, so we just need to make sure we hold
- * the lock across the calls to ssl3_CallHelloExtensionSenders.
+ * the lock across the calls to ssl_ConstructExtensions.
*/
if (sid->u.ssl3.lock) {
+ unlockNeeded = PR_TRUE;
PR_RWLock_Rlock(sid->u.ssl3.lock);
}
@@ -5186,24 +4762,14 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
type == client_hello_initial) {
rv = tls13_SetupClientHello(ss);
if (rv != SECSuccess) {
- return SECFailure;
+ goto loser;
}
}
if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
- PRUint32 maxBytes = 65535; /* 2^16 - 1 */
- PRInt32 extLen;
-
- extLen = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes, NULL);
- if (extLen < 0) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return SECFailure;
+ rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_client_hello);
+ if (rv != SECSuccess) {
+ goto loser;
}
- total_exten_len += extLen;
-
- if (total_exten_len > 0)
- total_exten_len += 2;
}
if (IS_DTLS(ss)) {
@@ -5213,10 +4779,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy);
if (!num_suites) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return SECFailure; /* count_cipher_suites has set error code. */
+ goto loser; /* count_cipher_suites has set error code. */
}
fallbackSCSV = ss->opt.enableFallbackSCSV && (!requestingResume ||
@@ -5229,37 +4792,30 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
++num_suites;
}
- /* count compression methods */
- numCompressionMethods = 0;
- for (i = 0; i < ssl_compression_method_count; i++) {
- if (ssl_CompressionEnabled(ss, ssl_compression_methods[i]))
- numCompressionMethods++;
- }
-
length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH +
- 1 + (sid->version >= SSL_LIBRARY_VERSION_TLS_1_3
- ? 0
- : sid->u.ssl3.sessionIDLength) +
+ 1 + /* session id */
2 + num_suites * sizeof(ssl3CipherSuite) +
- 1 + numCompressionMethods + total_exten_len;
+ 1 + 1 /* compression methods */;
+ if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ length += sid->u.ssl3.sessionIDLength;
+ } else if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss)) {
+ length += SSL3_SESSIONID_BYTES;
+ }
if (IS_DTLS(ss)) {
- length += 1 + ss->ssl3.hs.cookie.len;
+ length += 1 + cookieLen;
}
- if (total_exten_len > 0) {
- ssl3_CalculatePaddingExtLen(ss, length);
- if (ss->xtnData.paddingLen) {
- total_exten_len += 4 + ss->xtnData.paddingLen;
- length += 4 + ss->xtnData.paddingLen;
+ if (extensionBuf.len) {
+ rv = ssl_InsertPaddingExtension(ss, length, &extensionBuf);
+ if (rv != SECSuccess) {
+ goto loser; /* err set by ssl_InsertPaddingExtension */
}
+ length += 2 + extensionBuf.len;
}
- rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_client_hello, length);
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
if (ss->firstHsDone) {
@@ -5277,60 +4833,49 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
}
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
/* Generate a new random if this is the first attempt. */
if (type == client_hello_initial) {
- rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
+ rv = ssl3_GetNewRandom(ss->ssl3.hs.client_random);
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by GetNewRandom. */
+ goto loser; /* err set by GetNewRandom. */
}
}
- rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
+ rv = ssl3_AppendHandshake(ss, ss->ssl3.hs.client_random,
SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
- if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3)
+ if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3) {
rv = ssl3_AppendHandshakeVariable(
ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
- else
+ } else if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss)) {
+ /* We're faking session resumption, so rather than create new
+ * randomness, just mix up the client random a little. */
+ PRUint8 buf[SSL3_SESSIONID_BYTES];
+ ssl_MakeFakeSid(ss, buf);
+ rv = ssl3_AppendHandshakeVariable(ss, buf, SSL3_SESSIONID_BYTES, 1);
+ } else {
rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
+ }
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
if (IS_DTLS(ss)) {
rv = ssl3_AppendHandshakeVariable(
- ss, ss->ssl3.hs.cookie.data, ss->ssl3.hs.cookie.len, 1);
+ ss, ss->ssl3.hs.cookie.data, cookieLen, 1);
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
}
rv = ssl3_AppendHandshakeNumber(ss, num_suites * sizeof(ssl3CipherSuite), 2);
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
if (ss->ssl3.hs.sendingSCSV) {
@@ -5338,10 +4883,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
actual_count++;
}
@@ -5349,10 +4891,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
actual_count++;
}
@@ -5361,20 +4900,14 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
if (config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
actual_count++;
if (actual_count > num_suites) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
/* set error card removal/insertion error */
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
- return SECFailure;
+ goto loser;
}
rv = ssl3_AppendHandshakeNumber(ss, suite->cipher_suite,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
}
}
@@ -5384,57 +4917,37 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
* the server.. */
if (actual_count != num_suites) {
/* Card removal/insertion error */
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
- return SECFailure;
+ goto loser;
}
- rv = ssl3_AppendHandshakeNumber(ss, numCompressionMethods, 1);
+ /* Compression methods: count is always 1, null compression. */
+ rv = ssl3_AppendHandshakeNumber(ss, 1, 1);
if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
- for (i = 0; i < ssl_compression_method_count; i++) {
- if (!ssl_CompressionEnabled(ss, ssl_compression_methods[i]))
- continue;
- rv = ssl3_AppendHandshakeNumber(ss, ssl_compression_methods[i], 1);
- if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by ssl3_AppendHandshake* */
- }
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_compression_null, 1);
+ if (rv != SECSuccess) {
+ goto loser; /* err set by ssl3_AppendHandshake* */
}
- if (total_exten_len) {
- PRUint32 maxBytes = total_exten_len - 2;
- PRInt32 extLen;
-
- rv = ssl3_AppendHandshakeNumber(ss, maxBytes, 2);
- if (rv != SECSuccess) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return rv; /* err set by AppendHandshake. */
+ if (extensionBuf.len) {
+ /* If we are sending a PSK binder, replace the dummy value. Note that
+ * we only set statelessResume on the client in TLS 1.3. */
+ if (ss->statelessResume &&
+ ss->xtnData.sentSessionTicketInClientHello) {
+ rv = tls13_WriteExtensionsWithBinder(ss, &extensionBuf);
+ } else {
+ rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensionBuf, 2);
}
-
- extLen = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, maxBytes, NULL);
- if (extLen < 0) {
- if (sid->u.ssl3.lock) {
- PR_RWLock_Unlock(sid->u.ssl3.lock);
- }
- return SECFailure;
+ if (rv != SECSuccess) {
+ goto loser; /* err set by AppendHandshake. */
}
- maxBytes -= extLen;
-
- PORT_Assert(!maxBytes);
}
- if (sid->u.ssl3.lock) {
+ sslBuffer_Clear(&extensionBuf);
+ if (unlockNeeded) {
+ /* Note: goto loser can't be used past this point. */
PR_RWLock_Unlock(sid->u.ssl3.lock);
}
@@ -5450,9 +4963,6 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
}
flags = 0;
- if (!ss->firstHsDone && !IS_DTLS(ss)) {
- flags |= ssl_SEND_FLAG_CAP_RECORD_VERSION;
- }
rv = ssl3_FlushHandshake(ss, flags);
if (rv != SECSuccess) {
return rv; /* error code set by ssl3_FlushHandshake */
@@ -5467,6 +4977,13 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
ss->ssl3.hs.ws = wait_server_hello;
return SECSuccess;
+
+loser:
+ if (unlockNeeded) {
+ PR_RWLock_Unlock(sid->u.ssl3.lock);
+ }
+ sslBuffer_Clear(&extensionBuf);
+ return SECFailure;
}
/* Called from ssl3_HandlePostHelloHandshakeMessage() when it has deciphered a
@@ -6018,7 +5535,7 @@ ssl3_SendRSAClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
/* Generate the pre-master secret ... */
ssl_GetSpecWriteLock(ss);
- isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
+ isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
pms = ssl3_GenerateRSAPMS(ss, ss->ssl3.pwSpec, NULL);
ssl_ReleaseSpecWriteLock(ss);
@@ -6041,41 +5558,20 @@ ssl3_SendRSAClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
goto loser;
}
-#ifdef NSS_ALLOW_SSLKEYLOGFILE
- if (ssl_keylog_iob) {
+#ifdef TRACE
+ if (ssl_trace >= 100) {
SECStatus extractRV = PK11_ExtractKeyValue(pms);
if (extractRV == SECSuccess) {
SECItem *keyData = PK11_GetKeyData(pms);
if (keyData && keyData->data && keyData->len) {
-#ifdef TRACE
- if (ssl_trace >= 100) {
- ssl_PrintBuf(ss, "Pre-Master Secret",
- keyData->data, keyData->len);
- }
-#endif
- if (ssl_keylog_iob && enc_pms.len >= 8 && keyData->len == 48) {
- /* https://developer.mozilla.org/en/NSS_Key_Log_Format */
-
- /* There could be multiple, concurrent writers to the
- * keylog, so we have to do everything in a single call to
- * fwrite. */
- char buf[4 + 8 * 2 + 1 + 48 * 2 + 1];
-
- strcpy(buf, "RSA ");
- hexEncode(buf + 4, enc_pms.data, 8);
- buf[20] = ' ';
- hexEncode(buf + 21, keyData->data, 48);
- buf[sizeof(buf) - 1] = '\n';
-
- fwrite(buf, sizeof(buf), 1, ssl_keylog_iob);
- fflush(ssl_keylog_iob);
- }
+ ssl_PrintBuf(ss, "Pre-Master Secret",
+ keyData->data, keyData->len);
}
}
}
#endif
- rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange,
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_client_key_exchange,
isTLS ? enc_pms.len + 2
: enc_pms.len);
if (rv != SECSuccess) {
@@ -6090,7 +5586,7 @@ ssl3_SendRSAClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
goto loser; /* err set by ssl3_AppendHandshake* */
}
- rv = ssl3_InitPendingCipherSpec(ss, pms);
+ rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE);
PK11_FreeSymKey(pms);
pms = NULL;
@@ -6114,27 +5610,27 @@ loser:
/* DH shares need to be padded to the size of their prime. Some implementations
* require this. TLS 1.3 also requires this. */
SECStatus
-ssl_AppendPaddedDHKeyShare(const sslSocket *ss, const SECKEYPublicKey *pubKey,
+ssl_AppendPaddedDHKeyShare(sslBuffer *buf, const SECKEYPublicKey *pubKey,
PRBool appendLength)
{
SECStatus rv;
unsigned int pad = pubKey->u.dh.prime.len - pubKey->u.dh.publicValue.len;
if (appendLength) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, pubKey->u.dh.prime.len, 2);
+ rv = sslBuffer_AppendNumber(buf, pubKey->u.dh.prime.len, 2);
if (rv != SECSuccess) {
return rv;
}
}
while (pad) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 1);
+ rv = sslBuffer_AppendNumber(buf, 0, 1);
if (rv != SECSuccess) {
return rv;
}
--pad;
}
- rv = ssl3_ExtAppendHandshake(ss, pubKey->u.dh.publicValue.data,
- pubKey->u.dh.publicValue.len);
+ rv = sslBuffer_Append(buf, pubKey->u.dh.publicValue.data,
+ pubKey->u.dh.publicValue.len);
if (rv != SECSuccess) {
return rv;
}
@@ -6158,11 +5654,13 @@ ssl3_SendDHClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
};
sslEphemeralKeyPair *keyPair = NULL;
SECKEYPublicKey *pubKey;
+ PRUint8 dhData[1026]; /* Enough for the 8192-bit group. */
+ sslBuffer dhBuf = SSL_BUFFER(dhData);
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
+ isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
/* Copy DH parameters from server key */
@@ -6217,22 +5715,27 @@ ssl3_SendDHClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
}
/* Note: send the DH share padded to avoid triggering bugs. */
- rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange,
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_client_key_exchange,
params->prime.len + 2);
if (rv != SECSuccess) {
goto loser; /* err set by ssl3_AppendHandshake* */
}
- rv = ssl_AppendPaddedDHKeyShare(ss, pubKey, PR_TRUE);
+ rv = ssl_AppendPaddedDHKeyShare(&dhBuf, pubKey, PR_TRUE);
if (rv != SECSuccess) {
goto loser; /* err set by ssl_AppendPaddedDHKeyShare */
}
+ rv = ssl3_AppendBufferToHandshake(ss, &dhBuf);
+ if (rv != SECSuccess) {
+ goto loser; /* err set by ssl3_AppendBufferToHandshake */
+ }
- rv = ssl3_InitPendingCipherSpec(ss, pms);
+ rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE);
goto loser;
}
+ sslBuffer_Clear(&dhBuf);
PK11_FreeSymKey(pms);
ssl_FreeEphemeralKeyPair(keyPair);
return SECSuccess;
@@ -6242,6 +5745,7 @@ loser:
PK11_FreeSymKey(pms);
if (keyPair)
ssl_FreeEphemeralKeyPair(keyPair);
+ sslBuffer_Clear(&dhBuf);
return SECFailure;
}
@@ -6422,8 +5926,8 @@ ssl3_PickServerSignatureScheme(sslSocket *ss)
/* Sets error code, if needed. */
return ssl_PickSignatureScheme(ss, keyPair->pubKey, keyPair->privKey,
- ss->xtnData.clientSigSchemes,
- ss->xtnData.numClientSigScheme,
+ ss->xtnData.sigSchemes,
+ ss->xtnData.numSigSchemes,
PR_FALSE /* requireSha1 */);
}
@@ -6540,7 +6044,7 @@ ssl3_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey)
len = buf.len + 2 + (isTLS12 ? 2 : 0);
- rv = ssl3_AppendHandshakeHeader(ss, certificate_verify, len);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_verify, len);
if (rv != SECSuccess) {
goto done; /* error code set by AppendHandshake */
}
@@ -6564,11 +6068,9 @@ done:
/* Once a cipher suite has been selected, make sure that the necessary secondary
* information is properly set. */
SECStatus
-ssl3_SetCipherSuite(sslSocket *ss, ssl3CipherSuite chosenSuite,
- PRBool initHashes)
+ssl3_SetupCipherSuite(sslSocket *ss, PRBool initHashes)
{
- ss->ssl3.hs.cipher_suite = chosenSuite;
- ss->ssl3.hs.suite_def = ssl_LookupCipherSuiteDef(chosenSuite);
+ ss->ssl3.hs.suite_def = ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
if (!ss->ssl3.hs.suite_def) {
PORT_Assert(0);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -6581,10 +6083,53 @@ ssl3_SetCipherSuite(sslSocket *ss, ssl3CipherSuite chosenSuite,
if (!initHashes) {
return SECSuccess;
}
- /* Now we've have a cipher suite, initialize the handshake hashes. */
+ /* Now we have a cipher suite, initialize the handshake hashes. */
return ssl3_InitHandshakeHashes(ss);
}
+SECStatus
+ssl_ClientSetCipherSuite(sslSocket *ss, SSL3ProtocolVersion version,
+ ssl3CipherSuite suite, PRBool initHashes)
+{
+ unsigned int i;
+ if (ssl3_config_match_init(ss) == 0) {
+ PORT_Assert(PR_FALSE);
+ return SECFailure;
+ }
+ for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
+ ssl3CipherSuiteCfg *suiteCfg = &ss->cipherSuites[i];
+ if (suite == suiteCfg->cipher_suite) {
+ SSLVersionRange vrange = { version, version };
+ if (!config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
+ /* config_match already checks whether the cipher suite is
+ * acceptable for the version, but the check is repeated here
+ * in order to give a more precise error code. */
+ if (!ssl3_CipherSuiteAllowedForVersionRange(suite, &vrange)) {
+ PORT_SetError(SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION);
+ } else {
+ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
+ }
+ return SECFailure;
+ }
+ break;
+ }
+ }
+ if (i >= ssl_V3_SUITES_IMPLEMENTED) {
+ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
+ return SECFailure;
+ }
+
+ /* Don't let the server change its mind. */
+ if (ss->ssl3.hs.helloRetry && suite != ss->ssl3.hs.cipher_suite) {
+ (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ return SECFailure;
+ }
+
+ ss->ssl3.hs.cipher_suite = (ssl3CipherSuite)suite;
+ return ssl3_SetupCipherSuite(ss, initHashes);
+}
+
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
* ssl3 ServerHello message.
* Caller must hold Handshake and RecvBuf locks.
@@ -6592,14 +6137,16 @@ ssl3_SetCipherSuite(sslSocket *ss, ssl3CipherSuite chosenSuite,
static SECStatus
ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
- PRUint32 temp;
- PRBool suite_found = PR_FALSE;
- int i;
+ PRUint32 cipher;
int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
+ PRUint32 compression;
SECStatus rv;
SECItem sidBytes = { siBuffer, NULL, 0 };
- PRBool isTLS = PR_FALSE;
+ PRBool isHelloRetry;
SSL3AlertDescription desc = illegal_parameter;
+ TLSExtension *versionExtension;
+ const PRUint8 *savedMsg = b;
+ const PRUint32 savedLength = length;
#ifndef TLS_1_3_DRAFT_VERSION
SSL3ProtocolVersion downgradeCheckVersion;
#endif
@@ -6608,7 +6155,6 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
SSL_GETPID(), ss->fd));
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->ssl3.initialized);
if (ss->ssl3.hs.ws != wait_server_hello) {
errCode = SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO;
@@ -6630,11 +6176,95 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
ss->ssl3.clientPrivateKey = NULL;
}
+ /* Note that if the server selects TLS 1.3, this will set the version to TLS
+ * 1.2. We will amend that once all other fields have been read. */
rv = ssl_ClientReadVersion(ss, &b, &length, &ss->version);
if (rv != SECSuccess) {
goto loser; /* alert has been sent */
}
+ rv = ssl3_ConsumeHandshake(
+ ss, ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* alert has been sent */
+ }
+ isHelloRetry = !PORT_Memcmp(ss->ssl3.hs.server_random,
+ ssl_hello_retry_random, SSL3_RANDOM_LENGTH);
+
+ rv = ssl3_ConsumeHandshakeVariable(ss, &sidBytes, 1, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* alert has been sent */
+ }
+ if (sidBytes.len > SSL3_SESSIONID_BYTES) {
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_0)
+ desc = decode_error;
+ goto alert_loser; /* malformed. */
+ }
+
+ /* Read the cipher suite. */
+ rv = ssl3_ConsumeHandshakeNumber(ss, &cipher, 2, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* alert has been sent */
+ }
+
+ /* Compression method. */
+ rv = ssl3_ConsumeHandshakeNumber(ss, &compression, 1, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* alert has been sent */
+ }
+ if (compression != ssl_compression_null) {
+ desc = illegal_parameter;
+ errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
+ goto alert_loser;
+ }
+
+ /* Parse extensions. */
+ if (length != 0) {
+ PRUint32 extensionLength;
+ rv = ssl3_ConsumeHandshakeNumber(ss, &extensionLength, 2, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* alert already sent */
+ }
+ if (extensionLength != length) {
+ desc = decode_error;
+ goto alert_loser;
+ }
+ rv = ssl3_ParseExtensions(ss, &b, &length);
+ if (rv != SECSuccess) {
+ goto alert_loser; /* malformed */
+ }
+ }
+
+ /* Update the version based on the extension, as necessary. */
+ versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_xtn);
+ if (versionExtension) {
+ rv = ssl_ClientReadVersion(ss, &versionExtension->data.data,
+ &versionExtension->data.len,
+ &ss->version);
+ if (rv != SECSuccess) {
+ errCode = PORT_GetError();
+ goto loser; /* An alert is sent by ssl_ClientReadVersion */
+ }
+ }
+
+ PORT_Assert(!SSL_ALL_VERSIONS_DISABLED(&ss->vrange));
+ /* Check that the version is within the configured range. */
+ if (ss->vrange.min > ss->version || ss->vrange.max < ss->version) {
+ desc = (ss->version > SSL_LIBRARY_VERSION_3_0)
+ ? protocol_version
+ : handshake_failure;
+ errCode = SSL_ERROR_UNSUPPORTED_VERSION;
+ goto alert_loser;
+ }
+
+ if (isHelloRetry && ss->ssl3.hs.helloRetry) {
+ SSL_TRC(3, ("%d: SSL3[%d]: received a second hello_retry_request",
+ SSL_GETPID(), ss->fd));
+ desc = unexpected_message;
+ errCode = SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST;
+ goto alert_loser;
+ }
+
/* The server didn't pick 1.3 although we either received a
* HelloRetryRequest, or we prepared to send early app data. */
if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
@@ -6657,18 +6287,10 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
* us to be getting this version number, but it's what we have.
* (1294697). */
if (ss->firstHsDone && (ss->version != ss->ssl3.crSpec->version)) {
- desc = illegal_parameter;
+ desc = protocol_version;
errCode = SSL_ERROR_UNSUPPORTED_VERSION;
goto alert_loser;
}
- ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_version;
- isTLS = (ss->version > SSL_LIBRARY_VERSION_3_0);
-
- rv = ssl3_ConsumeHandshake(
- ss, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH, &b, &length);
- if (rv != SECSuccess) {
- goto loser; /* alert has been sent */
- }
#ifndef TLS_1_3_DRAFT_VERSION
/* Check the ServerHello.random per
@@ -6688,8 +6310,8 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (downgradeCheckVersion >= SSL_LIBRARY_VERSION_TLS_1_2 &&
downgradeCheckVersion > ss->version) {
/* Both sections use the same sentinel region. */
- unsigned char *downgrade_sentinel =
- ss->ssl3.hs.server_random.rand +
+ PRUint8 *downgrade_sentinel =
+ ss->ssl3.hs.server_random +
SSL3_RANDOM_LENGTH - sizeof(tls13_downgrade_random);
if (!PORT_Memcmp(downgrade_sentinel,
tls13_downgrade_random,
@@ -6704,110 +6326,64 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
#endif
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = ssl3_ConsumeHandshakeVariable(ss, &sidBytes, 1, &b, &length);
- if (rv != SECSuccess) {
- goto loser; /* alert has been sent */
+ /* Finally, now all the version-related checks have passed. */
+ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_version;
+ /* Update the write cipher spec to match the version. But not after
+ * HelloRetryRequest, because cwSpec might be a 0-RTT cipher spec. */
+ if (!ss->firstHsDone && !ss->ssl3.hs.helloRetry) {
+ ssl_GetSpecWriteLock(ss);
+ ssl_SetSpecVersions(ss, ss->ssl3.cwSpec);
+ ssl_ReleaseSpecWriteLock(ss);
+ }
+
+ /* Check that the session ID is as expected. */
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ PRUint8 buf[SSL3_SESSIONID_BYTES];
+ unsigned int expectedSidLen;
+ if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss)) {
+ expectedSidLen = SSL3_SESSIONID_BYTES;
+ ssl_MakeFakeSid(ss, buf);
+ } else {
+ expectedSidLen = 0;
}
- if (sidBytes.len > SSL3_SESSIONID_BYTES) {
- if (isTLS)
- desc = decode_error;
- goto alert_loser; /* malformed. */
+ if (sidBytes.len != expectedSidLen ||
+ (expectedSidLen > 0 &&
+ PORT_Memcmp(buf, sidBytes.data, expectedSidLen) != 0)) {
+ desc = illegal_parameter;
+ errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
+ goto alert_loser;
}
}
- /* find selected cipher suite in our list. */
- rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 2, &b, &length);
+ /* Only initialize hashes if this isn't a Hello Retry. */
+ rv = ssl_ClientSetCipherSuite(ss, ss->version, cipher,
+ !isHelloRetry);
if (rv != SECSuccess) {
- goto loser; /* alert has been sent */
- }
- i = ssl3_config_match_init(ss);
- PORT_Assert(i > 0);
- if (i <= 0) {
+ desc = illegal_parameter;
errCode = PORT_GetError();
- goto loser;
+ goto alert_loser;
}
- for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
- if (temp == suite->cipher_suite) {
- SSLVersionRange vrange = { ss->version, ss->version };
- if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) {
- /* config_match already checks whether the cipher suite is
- * acceptable for the version, but the check is repeated here
- * in order to give a more precise error code. */
- if (!ssl3_CipherSuiteAllowedForVersionRange(temp, &vrange)) {
- desc = handshake_failure;
- errCode = SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION;
- goto alert_loser;
- }
- break; /* failure */
- }
+ dtls_ReceivedFirstMessageInFlight(ss);
- suite_found = PR_TRUE;
- break; /* success */
+ if (isHelloRetry) {
+ rv = tls13_HandleHelloRetryRequest(ss, savedMsg, savedLength);
+ if (rv != SECSuccess) {
+ goto loser;
}
- }
- if (!suite_found) {
- desc = handshake_failure;
- errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
- goto alert_loser;
+ return SECSuccess;
}
- rv = ssl3_SetCipherSuite(ss, (ssl3CipherSuite)temp, PR_TRUE);
+ rv = ssl3_HandleParsedExtensions(ss, ssl_hs_server_hello);
+ ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
if (rv != SECSuccess) {
- desc = internal_error;
- errCode = PORT_GetError();
goto alert_loser;
}
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- /* find selected compression method in our list. */
- rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 1, &b, &length);
- if (rv != SECSuccess) {
- goto loser; /* alert has been sent */
- }
- suite_found = PR_FALSE;
- for (i = 0; i < ssl_compression_method_count; i++) {
- if (temp == ssl_compression_methods[i]) {
- if (!ssl_CompressionEnabled(ss, ssl_compression_methods[i])) {
- break; /* failure */
- }
- suite_found = PR_TRUE;
- break; /* success */
- }
- }
- if (!suite_found) {
- desc = handshake_failure;
- errCode = SSL_ERROR_NO_COMPRESSION_OVERLAP;
- goto alert_loser;
- }
- ss->ssl3.hs.compression = (SSLCompressionMethod)temp;
- } else {
- ss->ssl3.hs.compression = ssl_compression_null;
- }
-
- /* Note that if !isTLS and the extra stuff is not extensions, we
- * do NOT goto alert_loser.
- * There are some old SSL 3.0 implementations that do send stuff
- * after the end of the server hello, and we deliberately ignore
- * such stuff in the interest of maximal interoperability (being
- * "generous in what you accept").
- * Update: Starting in NSS 3.12.6, we handle the renegotiation_info
- * extension in SSL 3.0.
- */
- if (length != 0) {
- SECItem extensions;
- rv = ssl3_ConsumeHandshakeVariable(ss, &extensions, 2, &b, &length);
- if (rv != SECSuccess || length != 0) {
- if (isTLS)
- goto alert_loser;
- } else {
- rv = ssl3_HandleExtensions(ss, &extensions.data,
- &extensions.len, server_hello);
- if (rv != SECSuccess)
- goto alert_loser;
- }
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_server_hello,
+ savedMsg, savedLength);
+ if (rv != SECSuccess) {
+ goto loser;
}
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
@@ -6835,6 +6411,51 @@ loser:
}
static SECStatus
+ssl3_UnwrapMasterSecretClient(sslSocket *ss, sslSessionID *sid, PK11SymKey **ms)
+{
+ PK11SlotInfo *slot;
+ PK11SymKey *wrapKey;
+ CK_FLAGS keyFlags = 0;
+ SECItem wrappedMS = {
+ siBuffer,
+ sid->u.ssl3.keys.wrapped_master_secret,
+ sid->u.ssl3.keys.wrapped_master_secret_len
+ };
+
+ /* unwrap master secret */
+ slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
+ sid->u.ssl3.masterSlotID);
+ if (slot == NULL) {
+ return SECFailure;
+ }
+ if (!PK11_IsPresent(slot)) {
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+ wrapKey = PK11_GetWrapKey(slot, sid->u.ssl3.masterWrapIndex,
+ sid->u.ssl3.masterWrapMech,
+ sid->u.ssl3.masterWrapSeries,
+ ss->pkcs11PinArg);
+ PK11_FreeSlot(slot);
+ if (wrapKey == NULL) {
+ return SECFailure;
+ }
+
+ if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
+ keyFlags = CKF_SIGN | CKF_VERIFY;
+ }
+
+ *ms = PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech,
+ NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE,
+ CKA_DERIVE, SSL3_MASTER_SECRET_LENGTH, keyFlags);
+ PK11_FreeSymKey(wrapKey);
+ if (!*ms) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
int *retErrCode)
{
@@ -6860,7 +6481,7 @@ ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
/* we need to call ssl3_SetupPendingCipherSpec here so we can check the
* key exchange algorithm. */
- rv = ssl3_SetupPendingCipherSpec(ss);
+ rv = ssl3_SetupBothPendingCipherSpecs(ss);
if (rv != SECSuccess) {
goto alert_loser; /* error code is set. */
}
@@ -6883,9 +6504,7 @@ ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
goto alert_loser;
}
do {
- ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
-
- SECItem wrappedMS; /* wrapped master secret. */
+ PK11SymKey *masterSecret;
/* [draft-ietf-tls-session-hash-06; Section 5.3]
*
@@ -6917,60 +6536,12 @@ ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
ss->sec.authKeyBits = sid->authKeyBits;
ss->sec.keaType = sid->keaType;
ss->sec.keaKeyBits = sid->keaKeyBits;
+ ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
+ ss->sec.signatureScheme = sid->sigScheme;
- if (sid->u.ssl3.keys.msIsWrapped) {
- PK11SlotInfo *slot;
- PK11SymKey *wrapKey; /* wrapping key */
- CK_FLAGS keyFlags = 0;
-
- /* unwrap master secret */
- slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
- sid->u.ssl3.masterSlotID);
- if (slot == NULL) {
- break; /* not considered an error. */
- }
- if (!PK11_IsPresent(slot)) {
- PK11_FreeSlot(slot);
- break; /* not considered an error. */
- }
- wrapKey = PK11_GetWrapKey(slot, sid->u.ssl3.masterWrapIndex,
- sid->u.ssl3.masterWrapMech,
- sid->u.ssl3.masterWrapSeries,
- ss->pkcs11PinArg);
- PK11_FreeSlot(slot);
- if (wrapKey == NULL) {
- break; /* not considered an error. */
- }
-
- if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
- keyFlags =
- CKF_SIGN | CKF_VERIFY;
- }
-
- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len;
- pwSpec->master_secret =
- PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech,
- NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE,
- CKA_DERIVE, sizeof(SSL3MasterSecret), keyFlags);
- errCode = PORT_GetError();
- PK11_FreeSymKey(wrapKey);
- if (pwSpec->master_secret == NULL) {
- break; /* errorCode set just after call to UnwrapSymKey. */
- }
- } else {
- /* need to import the raw master secret to session object */
- PK11SlotInfo *slot = PK11_GetInternalSlot();
- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len;
- pwSpec->master_secret =
- PK11_ImportSymKey(slot, CKM_SSL3_MASTER_KEY_DERIVE,
- PK11_OriginUnwrap, CKA_ENCRYPT,
- &wrappedMS, NULL);
- PK11_FreeSlot(slot);
- if (pwSpec->master_secret == NULL) {
- break;
- }
+ rv = ssl3_UnwrapMasterSecretClient(ss, sid, &masterSecret);
+ if (rv != SECSuccess) {
+ break; /* not considered an error */
}
/* Got a Match */
@@ -6992,8 +6563,8 @@ ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
}
- /* NULL value for PMS because we are reusing the old MS */
- rv = ssl3_InitPendingCipherSpec(ss, NULL);
+ /* We are re-using the old MS, so no need to derive again. */
+ rv = ssl3_InitPendingCipherSpecs(ss, masterSecret, PR_FALSE);
if (rv != SECSuccess) {
goto alert_loser; /* err code was set */
}
@@ -7098,11 +6669,11 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
rv = NSS_OptionGet(NSS_DH_MIN_KEY_SIZE, &minDH);
- if (rv != SECSuccess) {
+ if (rv != SECSuccess || minDH <= 0) {
minDH = SSL_DH_MIN_P_BITS;
}
dh_p_bits = SECKEY_BigIntegerBitLength(&dh_p);
- if (dh_p_bits < minDH) {
+ if (dh_p_bits < (unsigned)minDH) {
errCode = SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY;
goto alert_loser;
}
@@ -7283,7 +6854,7 @@ typedef struct dnameNode {
*/
SECStatus
ssl3_ParseCertificateRequestCAs(sslSocket *ss, PRUint8 **b, PRUint32 *length,
- PLArenaPool *arena, CERTDistNames *ca_list)
+ CERTDistNames *ca_list)
{
PRUint32 remaining;
int nnames = 0;
@@ -7298,7 +6869,7 @@ ssl3_ParseCertificateRequestCAs(sslSocket *ss, PRUint8 **b, PRUint32 *length,
if (remaining > *length)
goto alert_loser;
- ca_list->head = node = PORT_ArenaZNew(arena, dnameNode);
+ ca_list->head = node = PORT_ArenaZNew(ca_list->arena, dnameNode);
if (node == NULL)
goto no_mem;
@@ -7324,14 +6895,14 @@ ssl3_ParseCertificateRequestCAs(sslSocket *ss, PRUint8 **b, PRUint32 *length,
if (remaining <= 0)
break; /* success */
- node->next = PORT_ArenaZNew(arena, dnameNode);
+ node->next = PORT_ArenaZNew(ca_list->arena, dnameNode);
node = node->next;
if (node == NULL)
goto no_mem;
}
ca_list->nnames = nnames;
- ca_list->names = PORT_ArenaNewArray(arena, SECItem, nnames);
+ ca_list->names = PORT_ArenaNewArray(ca_list->arena, SECItem, nnames);
if (nnames > 0 && ca_list->names == NULL)
goto no_mem;
@@ -7475,7 +7046,7 @@ ssl3_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
}
- rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, arena, &ca_list);
+ rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, &ca_list);
if (rv != SECSuccess)
goto done; /* alert sent in ssl3_ParseCertificateRequestCAs */
@@ -7575,7 +7146,7 @@ ssl3_CompleteHandleCertificateRequest(sslSocket *ss,
case SECFailure:
default:
send_no_certificate:
- if (ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0) {
+ if (ss->version > SSL_LIBRARY_VERSION_3_0) {
ss->ssl3.sendEmptyCert = PR_TRUE;
} else {
(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
@@ -7606,7 +7177,7 @@ ssl3_CheckFalseStart(sslSocket *ss)
* sufficiently strong that the attack can gain no advantage.
* Therefore we always require an 80-bit cipher. */
ssl_GetSpecReadLock(ss);
- maybeFalseStart = ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10;
+ maybeFalseStart = ss->ssl3.cwSpec->cipherDef->secret_key_size >= 10;
ssl_ReleaseSpecReadLock(ss);
if (!maybeFalseStart) {
@@ -7858,7 +7429,7 @@ ssl3_SendHelloRequest(sslSocket *ss)
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- rv = ssl3_AppendHandshakeHeader(ss, hello_request, 0);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_hello_request, 0);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake */
}
@@ -7927,6 +7498,7 @@ ssl3_NewSessionID(sslSocket *ss, PRBool is_server)
sid->references = 1;
sid->cached = never_cached;
sid->version = ss->version;
+ sid->sigScheme = ssl_sig_none;
sid->u.ssl3.keys.resumable = PR_TRUE;
sid->u.ssl3.policy = SSL_ALLOWED;
@@ -8037,8 +7609,8 @@ SECStatus
ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
PRBool initHashes)
{
- int j;
- int i;
+ unsigned int j;
+ unsigned int i;
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
@@ -8049,7 +7621,8 @@ ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
for (i = 0; i + 1 < suites->len; i += 2) {
PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
if (suite_i == suite->cipher_suite) {
- return ssl3_SetCipherSuite(ss, suite_i, initHashes);
+ ss->ssl3.hs.cipher_suite = suite_i;
+ return ssl3_SetupCipherSuite(ss, initHashes);
}
}
}
@@ -8148,7 +7721,6 @@ ssl3_ServerCallSNICallback(sslSocket *ss)
* and save the name. */
SECStatus rv;
SECItem *name = &ss->xtnData.sniNameArr[ret];
- int configedCiphers;
SECItem *pwsName;
/* get rid of the old name and save the newly picked. */
@@ -8177,8 +7749,7 @@ ssl3_ServerCallSNICallback(sslSocket *ss)
ret = SSL_SNI_SEND_ALERT;
break;
}
- configedCiphers = ssl3_config_match_init(ss);
- if (configedCiphers <= 0) {
+ if (ssl3_config_match_init(ss) == 0) {
/* no ciphers are working/supported */
errCode = PORT_GetError();
desc = handshake_failure;
@@ -8189,7 +7760,7 @@ ssl3_ServerCallSNICallback(sslSocket *ss)
* the name from the offered list and reconfigured the socket.
*/
ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn,
- ssl3_SendServerNameXtn);
+ ssl_SendEmptyExtension);
} else {
/* Callback returned index outside of the boundary. */
PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
@@ -8288,13 +7859,14 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
SECItem suites = { siBuffer, NULL, 0 };
SECItem comps = { siBuffer, NULL, 0 };
PRBool isTLS13;
+ const PRUint8 *savedMsg = b;
+ const PRUint32 savedLen = length;
SSL_TRC(3, ("%d: SSL3[%d]: handle client_hello handshake",
SSL_GETPID(), ss->fd));
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->ssl3.initialized);
ss->ssl3.hs.preliminaryInfo = 0;
if (!ss->sec.isServer ||
@@ -8319,6 +7891,9 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
}
+ /* We should always be in a fresh state. */
+ SSL_ASSERT_HASHES_EMPTY(ss);
+
/* Get peer name of client */
rv = ssl_GetPeerInfo(ss);
if (rv != SECSuccess) {
@@ -8328,7 +7903,7 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
/* We might be starting session renegotiation in which case we should
* clear previous state.
*/
- ssl3_ResetExtensionData(&ss->xtnData);
+ ssl3_ResetExtensionData(&ss->xtnData, ss);
ss->statelessResume = PR_FALSE;
if (IS_DTLS(ss)) {
@@ -8349,7 +7924,7 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
/* Grab the client random data. */
rv = ssl3_ConsumeHandshake(
- ss, &ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH, &b, &length);
+ ss, ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH, &b, &length);
if (rv != SECSuccess) {
goto loser; /* malformed */
}
@@ -8366,6 +7941,9 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (rv != SECSuccess) {
goto loser; /* malformed */
}
+ if (cookieBytes.len != 0) {
+ goto loser; /* We never send cookies in DTLS 1.2. */
+ }
}
/* Grab the list of cipher suites. */
@@ -8389,14 +7967,15 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (length) {
/* Get length of hello extensions */
- PRUint32 extension_length;
- rv = ssl3_ConsumeHandshakeNumber(ss, &extension_length, 2, &b, &length);
+ PRUint32 extensionLength;
+ rv = ssl3_ConsumeHandshakeNumber(ss, &extensionLength, 2, &b, &length);
if (rv != SECSuccess) {
goto loser; /* alert already sent */
}
- if (extension_length != length) {
- ssl3_DecodeError(ss); /* send alert */
- goto loser;
+ if (extensionLength != length) {
+ errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
+ desc = decode_error;
+ goto alert_loser;
}
rv = ssl3_ParseExtensions(ss, &b, &length);
@@ -8427,17 +8006,35 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
goto alert_loser;
}
}
+
+ if (ss->firstHsDone && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ desc = unexpected_message;
+ errCode = SSL_ERROR_RENEGOTIATION_NOT_ALLOWED;
+ goto alert_loser;
+ }
+
isTLS13 = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3;
ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_version;
+ /* Update the write spec to match the selected version. */
+ if (!ss->firstHsDone) {
+ ssl_GetSpecWriteLock(ss);
+ ssl_SetSpecVersions(ss, ss->ssl3.cwSpec);
+ ssl_ReleaseSpecWriteLock(ss);
+ }
- /* You can't resume TLS 1.3 like this. */
- if (isTLS13 && sidBytes.len) {
- goto alert_loser;
+ if (isTLS13 && sidBytes.len > 0 && !IS_DTLS(ss)) {
+ SECITEM_FreeItem(&ss->ssl3.hs.fakeSid, PR_FALSE);
+ rv = SECITEM_CopyItem(NULL, &ss->ssl3.hs.fakeSid, &sidBytes);
+ if (rv != SECSuccess) {
+ desc = internal_error;
+ errCode = PORT_GetError();
+ goto alert_loser;
+ }
}
/* Generate the Server Random now so it is available
* when we process the ClientKeyShare in TLS 1.3 */
- rv = ssl3_GetNewRandom(&ss->ssl3.hs.server_random);
+ rv = ssl3_GetNewRandom(ss->ssl3.hs.server_random);
if (rv != SECSuccess) {
errCode = SSL_ERROR_GENERATE_RANDOM_FAILURE;
goto loser;
@@ -8463,8 +8060,8 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
* we ship the final version of TLS 1.3. Bug 1306672.
*/
if (ss->vrange.max > ss->version) {
- unsigned char *downgrade_sentinel =
- ss->ssl3.hs.server_random.rand +
+ PRUint8 *downgrade_sentinel =
+ ss->ssl3.hs.server_random +
SSL3_RANDOM_LENGTH - sizeof(tls13_downgrade_random);
switch (ss->vrange.max) {
@@ -8485,8 +8082,25 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
#endif
+ /* If there is a cookie, then this is a second ClientHello (TLS 1.3). */
+ if (ssl3_FindExtension(ss, ssl_tls13_cookie_xtn)) {
+ ss->ssl3.hs.helloRetry = PR_TRUE;
+ }
+
+ if (ss->ssl3.hs.receivedCcs) {
+ /* This is only valid if we sent HelloRetryRequest, so we should have
+ * negotiated TLS 1.3 and there should be a cookie extension. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ !ss->ssl3.hs.helloRetry) {
+ desc = unexpected_message;
+ errCode = SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER;
+ goto alert_loser;
+ }
+ }
+
/* Now parse the rest of the extensions. */
- rv = ssl3_HandleParsedExtensions(ss, client_hello);
+ rv = ssl3_HandleParsedExtensions(ss, ssl_hs_client_hello);
+ ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
if (rv != SECSuccess) {
goto loser; /* malformed */
}
@@ -8509,6 +8123,12 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (comps.len != 1 || comps.data[0] != ssl_compression_null) {
goto alert_loser;
}
+ } else {
+ /* Other versions need to include null somewhere. */
+ if (comps.len < 1 ||
+ !memchr(comps.data, ssl_compression_null, comps.len)) {
+ goto alert_loser;
+ }
}
if (!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
@@ -8521,34 +8141,30 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (suite_i == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
PRUint8 *b2 = (PRUint8 *)emptyRIext;
PRUint32 L2 = sizeof emptyRIext;
- (void)ssl3_HandleExtensions(ss, &b2, &L2, client_hello);
+ (void)ssl3_HandleExtensions(ss, &b2, &L2, ssl_hs_client_hello);
break;
}
}
}
- /* This is a second check for TLS 1.3 and re-handshake to stop us
- * from re-handshake up to TLS 1.3, so it happens after version
- * negotiation. */
- if (ss->firstHsDone && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- desc = unexpected_message;
- errCode = SSL_ERROR_RENEGOTIATION_NOT_ALLOWED;
- goto alert_loser;
- }
- if (ss->firstHsDone &&
- (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_REQUIRES_XTN ||
- ss->opt.enableRenegotiation == SSL_RENEGOTIATE_TRANSITIONAL) &&
- !ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
- desc = no_renegotiation;
- level = alert_warning;
- errCode = SSL_ERROR_RENEGOTIATION_NOT_ALLOWED;
- goto alert_loser;
- }
- if ((ss->opt.requireSafeNegotiation ||
- (ss->firstHsDone && ss->peerRequestedProtection)) &&
- !ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
- desc = handshake_failure;
- errCode = SSL_ERROR_UNSAFE_NEGOTIATION;
- goto alert_loser;
+
+ /* The check for renegotiation in TLS 1.3 is earlier. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ if (ss->firstHsDone &&
+ (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_REQUIRES_XTN ||
+ ss->opt.enableRenegotiation == SSL_RENEGOTIATE_TRANSITIONAL) &&
+ !ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+ desc = no_renegotiation;
+ level = alert_warning;
+ errCode = SSL_ERROR_RENEGOTIATION_NOT_ALLOWED;
+ goto alert_loser;
+ }
+ if ((ss->opt.requireSafeNegotiation ||
+ (ss->firstHsDone && ss->peerRequestedProtection)) &&
+ !ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+ desc = handshake_failure;
+ errCode = SSL_ERROR_UNSAFE_NEGOTIATION;
+ goto alert_loser;
+ }
}
/* We do stateful resumes only if we are in TLS < 1.3 and
@@ -8621,21 +8237,14 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (IS_DTLS(ss)) {
ssl3_DisableNonDTLSSuites(ss);
+ dtls_ReceivedFirstMessageInFlight(ss);
}
-#ifdef PARANOID
- /* Look for a matching cipher suite. */
- j = ssl3_config_match_init(ss);
- if (j <= 0) { /* no ciphers are working/supported by PK11 */
- errCode = PORT_GetError(); /* error code is already set. */
- goto alert_loser;
- }
-#endif
-
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = tls13_HandleClientHelloPart2(ss, &suites, sid);
+ rv = tls13_HandleClientHelloPart2(ss, &suites, sid, savedMsg, savedLen);
} else {
- rv = ssl3_HandleClientHelloPart2(ss, &suites, &comps, sid);
+ rv = ssl3_HandleClientHelloPart2(ss, &suites, sid,
+ savedMsg, savedLen);
}
if (rv != SECSuccess) {
errCode = PORT_GetError();
@@ -8652,22 +8261,60 @@ loser:
}
static SECStatus
+ssl3_UnwrapMasterSecretServer(sslSocket *ss, sslSessionID *sid, PK11SymKey **ms)
+{
+ PK11SymKey *wrapKey;
+ CK_FLAGS keyFlags = 0;
+ SECItem wrappedMS = {
+ siBuffer,
+ sid->u.ssl3.keys.wrapped_master_secret,
+ sid->u.ssl3.keys.wrapped_master_secret_len
+ };
+
+ wrapKey = ssl3_GetWrappingKey(ss, NULL, sid->u.ssl3.masterWrapMech,
+ ss->pkcs11PinArg);
+ if (!wrapKey) {
+ return SECFailure;
+ }
+
+ if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
+ keyFlags = CKF_SIGN | CKF_VERIFY;
+ }
+
+ /* unwrap the master secret. */
+ *ms = PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech,
+ NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE,
+ CKA_DERIVE, SSL3_MASTER_SECRET_LENGTH, keyFlags);
+ PK11_FreeSymKey(wrapKey);
+ if (!*ms) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
ssl3_HandleClientHelloPart2(sslSocket *ss,
SECItem *suites,
- SECItem *comps,
- sslSessionID *sid)
+ sslSessionID *sid,
+ const PRUint8 *msg,
+ unsigned int len)
{
- PRBool haveSpecWriteLock = PR_FALSE;
PRBool haveXmitBufLock = PR_FALSE;
int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
SSL3AlertDescription desc = illegal_parameter;
SECStatus rv;
unsigned int i;
- int j;
+ unsigned int j;
- /* If we already have a session for this client, be sure to pick the
- ** same cipher suite and compression method we picked before.
- ** This is not a loop, despite appearances.
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_client_hello, msg, len);
+ if (rv != SECSuccess) {
+ errCode = SEC_ERROR_LIBRARY_FAILURE;
+ desc = internal_error;
+ goto alert_loser;
+ }
+
+ /* If we already have a session for this client, be sure to pick the same
+ ** cipher suite we picked before. This is not a loop, despite appearances.
*/
if (sid)
do {
@@ -8676,18 +8323,6 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
SSLVersionRange vrange = { ss->version, ss->version };
#endif
- /* Check that the cached compression method is still enabled. */
- if (!ssl_CompressionEnabled(ss, sid->u.ssl3.compression))
- break;
-
- /* Check that the cached compression method is in the client's list */
- for (i = 0; i < comps->len; i++) {
- if (comps->data[i] == sid->u.ssl3.compression)
- break;
- }
- if (i == comps->len)
- break;
-
suite = ss->cipherSuites;
/* Find the entry for the cipher suite used in the cached session. */
for (j = ssl_V3_SUITES_IMPLEMENTED; j > 0; --j, ++suite) {
@@ -8695,7 +8330,7 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
break;
}
PORT_Assert(j > 0);
- if (j <= 0)
+ if (j == 0)
break;
#ifdef PARANOID
/* Double check that the cached cipher suite is still enabled,
@@ -8714,17 +8349,15 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
for (i = 0; i + 1 < suites->len; i += 2) {
PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
if (suite_i == suite->cipher_suite) {
- rv = ssl3_SetCipherSuite(ss, suite_i, PR_TRUE);
+ ss->ssl3.hs.cipher_suite = suite_i;
+ rv = ssl3_SetupCipherSuite(ss, PR_TRUE);
if (rv != SECSuccess) {
desc = internal_error;
errCode = PORT_GetError();
goto alert_loser;
}
- /* Use the cached compression method. */
- ss->ssl3.hs.compression =
- sid->u.ssl3.compression;
- goto compression_found;
+ goto cipher_found;
}
}
} while (0);
@@ -8732,8 +8365,7 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
#ifndef PARANOID
/* Look for a matching cipher suite. */
- j = ssl3_config_match_init(ss);
- if (j <= 0) { /* no ciphers are working/supported by PK11 */
+ if (ssl3_config_match_init(ss) == 0) {
desc = internal_error;
errCode = PORT_GetError(); /* error code is already set. */
goto alert_loser;
@@ -8747,25 +8379,8 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
goto alert_loser;
}
- /* Select a compression algorithm. */
- for (i = 0; i < comps->len; i++) {
- SSLCompressionMethod method = (SSLCompressionMethod)comps->data[i];
- if (!ssl_CompressionEnabled(ss, method))
- continue;
- for (j = 0; j < ssl_compression_method_count; j++) {
- if (method == ssl_compression_methods[j]) {
- ss->ssl3.hs.compression = ssl_compression_methods[j];
- goto compression_found;
- }
- }
- }
- errCode = SSL_ERROR_NO_COMPRESSION_OVERLAP;
- /* null compression must be supported */
- goto alert_loser;
-
-compression_found:
+cipher_found:
suites->data = NULL;
- comps->data = NULL;
/* If there are any failures while processing the old sid,
* we don't consider them to be errors. Instead, We just behave
@@ -8775,12 +8390,10 @@ compression_found:
*/
if (sid != NULL)
do {
- ssl3CipherSpec *pwSpec;
- SECItem wrappedMS; /* wrapped key */
+ PK11SymKey *masterSecret;
if (sid->version != ss->version ||
- sid->u.ssl3.cipherSuite != ss->ssl3.hs.cipher_suite ||
- sid->u.ssl3.compression != ss->ssl3.hs.compression) {
+ sid->u.ssl3.cipherSuite != ss->ssl3.hs.cipher_suite) {
break; /* not an error */
}
@@ -8829,54 +8442,13 @@ compression_found:
}
ss->sec.ci.sid = NULL;
}
- /* we need to resurrect the master secret.... */
-
- ssl_GetSpecWriteLock(ss);
- haveSpecWriteLock = PR_TRUE;
- pwSpec = ss->ssl3.pwSpec;
- if (sid->u.ssl3.keys.msIsWrapped) {
- PK11SymKey *wrapKey; /* wrapping key */
- CK_FLAGS keyFlags = 0;
-
- wrapKey = ssl3_GetWrappingKey(ss, NULL,
- sid->u.ssl3.masterWrapMech,
- ss->pkcs11PinArg);
- if (!wrapKey) {
- /* we have a SID cache entry, but no wrapping key for it??? */
- break;
- }
-
- if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
- keyFlags = CKF_SIGN | CKF_VERIFY;
- }
-
- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len;
- /* unwrap the master secret. */
- pwSpec->master_secret =
- PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech,
- NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE,
- CKA_DERIVE, sizeof(SSL3MasterSecret), keyFlags);
- PK11_FreeSymKey(wrapKey);
- if (pwSpec->master_secret == NULL) {
- break; /* not an error */
- }
- } else {
- /* need to import the raw master secret to session object */
- PK11SlotInfo *slot;
- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len;
- slot = PK11_GetInternalSlot();
- pwSpec->master_secret =
- PK11_ImportSymKey(slot, CKM_SSL3_MASTER_KEY_DERIVE,
- PK11_OriginUnwrap, CKA_ENCRYPT, &wrappedMS,
- NULL);
- PK11_FreeSlot(slot);
- if (pwSpec->master_secret == NULL) {
- break; /* not an error */
- }
+ /* we need to resurrect the master secret.... */
+ rv = ssl3_UnwrapMasterSecretServer(ss, sid, &masterSecret);
+ if (rv != SECSuccess) {
+ break; /* not an error */
}
+
ss->sec.ci.sid = sid;
if (sid->peerCert != NULL) {
ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
@@ -8884,8 +8456,6 @@ compression_found:
/*
* Old SID passed all tests, so resume this old session.
- *
- * XXX make sure compression still matches
*/
SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_hits);
if (ss->statelessResume)
@@ -8896,6 +8466,8 @@ compression_found:
ss->sec.authKeyBits = sid->authKeyBits;
ss->sec.keaType = sid->keaType;
ss->sec.keaKeyBits = sid->keaKeyBits;
+ ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
+ ss->sec.signatureScheme = sid->sigScheme;
ss->sec.localCert =
CERT_DupCertificate(ss->sec.serverCert->serverCert);
@@ -8930,13 +8502,8 @@ compression_found:
goto loser;
}
- if (haveSpecWriteLock) {
- ssl_ReleaseSpecWriteLock(ss);
- haveSpecWriteLock = PR_FALSE;
- }
-
- /* NULL value for PMS because we are re-using the old MS */
- rv = ssl3_InitPendingCipherSpec(ss, NULL);
+ /* We are re-using the old MS, so no need to derive again. */
+ rv = ssl3_InitPendingCipherSpecs(ss, masterSecret, PR_FALSE);
if (rv != SECSuccess) {
errCode = PORT_GetError();
goto loser;
@@ -8961,12 +8528,8 @@ compression_found:
return SECSuccess;
} while (0);
- if (haveSpecWriteLock) {
- ssl_ReleaseSpecWriteLock(ss);
- haveSpecWriteLock = PR_FALSE;
- }
-
if (sid) { /* we had a sid, but it's no longer valid, free it */
+ ss->statelessResume = PR_FALSE;
SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_not_ok);
ss->sec.uncache(sid);
ssl_FreeSID(sid);
@@ -8985,9 +8548,8 @@ compression_found:
*/
if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) &&
ssl3_KEASupportsTickets(ss->ssl3.hs.kea_def)) {
- ssl3_RegisterExtensionSender(ss, &ss->xtnData,
- ssl_session_ticket_xtn,
- ssl3_SendSessionTicketXtn);
+ ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_session_ticket_xtn,
+ ssl_SendEmptyExtension);
}
rv = ssl3_ServerCallSNICallback(ss);
@@ -9031,10 +8593,6 @@ compression_found:
return SECSuccess;
alert_loser:
- if (haveSpecWriteLock) {
- ssl_ReleaseSpecWriteLock(ss);
- haveSpecWriteLock = PR_FALSE;
- }
(void)SSL3_SendAlert(ss, alert_fatal, desc);
/* FALLTHRU */
loser:
@@ -9043,10 +8601,6 @@ loser:
ssl_FreeSID(sid);
}
- if (haveSpecWriteLock) {
- ssl_ReleaseSpecWriteLock(ss);
- }
-
if (haveXmitBufLock) {
ssl_ReleaseXmitBufLock(ss);
}
@@ -9060,7 +8614,7 @@ loser:
* in asking to use the V3 handshake.
*/
SECStatus
-ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
+ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, unsigned int length,
PRUint8 padding)
{
sslSessionID *sid = NULL;
@@ -9068,11 +8622,11 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
unsigned char *random;
SSL3ProtocolVersion version;
SECStatus rv;
- int i;
- int j;
- int sid_length;
- int suite_length;
- int rand_length;
+ unsigned int i;
+ unsigned int j;
+ unsigned int sid_length;
+ unsigned int suite_length;
+ unsigned int rand_length;
int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
SSL3AlertDescription desc = handshake_failure;
unsigned int total = SSL_HL_CLIENT_HELLO_HBYTES;
@@ -9083,14 +8637,11 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
ssl_GetSSL3HandshakeLock(ss);
- ssl3_ResetExtensionData(&ss->xtnData);
-
version = (buffer[1] << 8) | buffer[2];
if (version < SSL_LIBRARY_VERSION_3_0) {
goto loser;
}
- ssl3_InitState(ss);
ssl3_RestartHandshakeHashes(ss);
if (ss->ssl3.hs.ws != wait_client_hello) {
@@ -9122,6 +8673,11 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
goto alert_loser;
}
ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_version;
+ if (!ss->firstHsDone) {
+ ssl_GetSpecWriteLock(ss);
+ ssl_SetSpecVersions(ss, ss->ssl3.cwSpec);
+ ssl_ReleaseSpecWriteLock(ss);
+ }
/* if we get a non-zero SID, just ignore it. */
if (length != total) {
@@ -9144,15 +8700,14 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
PORT_Assert(SSL_MAX_CHALLENGE_BYTES == SSL3_RANDOM_LENGTH);
- PORT_Memset(&ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
- PORT_Memcpy(
- &ss->ssl3.hs.client_random.rand[SSL3_RANDOM_LENGTH - rand_length],
- random, rand_length);
+ PORT_Memset(ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
+ PORT_Memcpy(&ss->ssl3.hs.client_random[SSL3_RANDOM_LENGTH - rand_length],
+ random, rand_length);
- PRINT_BUF(60, (ss, "client random:", &ss->ssl3.hs.client_random.rand[0],
+ PRINT_BUF(60, (ss, "client random:", ss->ssl3.hs.client_random,
SSL3_RANDOM_LENGTH));
- i = ssl3_config_match_init(ss);
- if (i <= 0) {
+
+ if (ssl3_config_match_init(ss) == 0) {
errCode = PORT_GetError(); /* error code is already set. */
goto alert_loser;
}
@@ -9161,8 +8716,6 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
**
** NOTE: This suite selection algorithm should be the same as the one in
** ssl3_HandleClientHello().
- **
- ** See the comments about export cipher suites in ssl3_HandleClientHello().
*/
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
@@ -9173,7 +8726,8 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
for (i = 0; i + 2 < suite_length; i += 3) {
PRUint32 suite_i = (suites[i] << 16) | (suites[i + 1] << 8) | suites[i + 2];
if (suite_i == suite->cipher_suite) {
- rv = ssl3_SetCipherSuite(ss, suite_i, PR_TRUE);
+ ss->ssl3.hs.cipher_suite = suite_i;
+ rv = ssl3_SetupCipherSuite(ss, PR_TRUE);
if (rv != SECSuccess) {
desc = internal_error;
errCode = PORT_GetError();
@@ -9209,7 +8763,7 @@ suite_found:
if (suite_i == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
PRUint8 *b2 = (PRUint8 *)emptyRIext;
PRUint32 L2 = sizeof emptyRIext;
- (void)ssl3_HandleExtensions(ss, &b2, &L2, client_hello);
+ (void)ssl3_HandleExtensions(ss, &b2, &L2, ssl_hs_client_hello);
break;
}
}
@@ -9221,8 +8775,6 @@ suite_found:
goto alert_loser;
}
- ss->ssl3.hs.compression = ssl_compression_null;
-
rv = ssl3_SelectServerCert(ss);
if (rv != SECSuccess) {
errCode = PORT_GetError();
@@ -9266,6 +8818,64 @@ loser:
return SECFailure;
}
+SECStatus
+ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry,
+ const sslBuffer *extensionBuf, sslBuffer *messageBuf)
+{
+ SECStatus rv;
+ SSL3ProtocolVersion version;
+ sslSessionID *sid = ss->sec.ci.sid;
+
+ if (IS_DTLS(ss) && ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ version = dtls_TLSVersionToDTLSVersion(ss->version);
+ } else {
+ version = PR_MIN(ss->version, SSL_LIBRARY_VERSION_TLS_1_2);
+ }
+
+ rv = sslBuffer_AppendNumber(messageBuf, version, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* Random already generated in ssl3_HandleClientHello */
+ rv = sslBuffer_Append(messageBuf, helloRetry ? ssl_hello_retry_random : ss->ssl3.hs.server_random,
+ SSL3_RANDOM_LENGTH);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ if (sid) {
+ rv = sslBuffer_AppendVariable(messageBuf, sid->u.ssl3.sessionID,
+ sid->u.ssl3.sessionIDLength, 1);
+ } else {
+ rv = sslBuffer_AppendNumber(messageBuf, 0, 1);
+ }
+ } else {
+ rv = sslBuffer_AppendVariable(messageBuf, ss->ssl3.hs.fakeSid.data,
+ ss->ssl3.hs.fakeSid.len, 1);
+ }
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(messageBuf, ss->ssl3.hs.cipher_suite, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(messageBuf, ssl_compression_null, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ if (SSL_BUFFER_LEN(extensionBuf)) {
+ rv = sslBuffer_AppendBufferVariable(messageBuf, extensionBuf, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
/* The negotiated version number has been already placed in ss->version.
**
** Called from: ssl3_HandleClientHello (resuming session),
@@ -9275,12 +8885,9 @@ loser:
SECStatus
ssl3_SendServerHello(sslSocket *ss)
{
- sslSessionID *sid;
SECStatus rv;
- PRUint32 maxBytes = 65535;
- PRUint32 length;
- PRInt32 extensions_len = 0;
- SSL3ProtocolVersion version;
+ sslBuffer extensionBuf = SSL_BUFFER_EMPTY;
+ sslBuffer messageBuf = SSL_BUFFER_EMPTY;
SSL_TRC(3, ("%d: SSL3[%d]: send server_hello handshake", SSL_GETPID(),
ss->fd));
@@ -9294,94 +8901,43 @@ ssl3_SendServerHello(sslSocket *ss)
return SECFailure;
}
- sid = ss->sec.ci.sid;
-
- extensions_len = ssl3_CallHelloExtensionSenders(
- ss, PR_FALSE, maxBytes, &ss->xtnData.serverHelloSenders[0]);
- if (extensions_len > 0)
- extensions_len += 2; /* Add sizeof total extension length */
-
- /* TLS 1.3 doesn't use the session_id or compression_method
- * fields in the ServerHello. */
- length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH;
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- length += 1 + ((sid == NULL) ? 0 : sid->u.ssl3.sessionIDLength);
- }
- length += sizeof(ssl3CipherSuite);
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- length += 1; /* Compression */
- }
- length += extensions_len;
-
- rv = ssl3_AppendHandshakeHeader(ss, server_hello, length);
+ rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_server_hello);
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
-
- if (IS_DTLS(ss) && ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- version = dtls_TLSVersionToDTLSVersion(ss->version);
- } else {
- version = tls13_EncodeDraftVersion(ss->version);
+ goto loser;
}
- rv = ssl3_AppendHandshakeNumber(ss, version, 2);
+ rv = ssl_ConstructServerHello(ss, PR_FALSE, &extensionBuf, &messageBuf);
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
- /* Random already generated in ssl3_HandleClientHello */
- rv = ssl3_AppendHandshake(
- ss, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH);
- if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
-
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- if (sid) {
- rv = ssl3_AppendHandshakeVariable(
- ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
- } else {
- rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
- }
- if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
+ goto loser;
}
- rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.cipher_suite, 2);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_hello,
+ SSL_BUFFER_LEN(&messageBuf));
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.compression, 1);
- if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
+ goto loser; /* err set by AppendHandshake. */
}
- if (extensions_len) {
- PRInt32 sent_len;
- extensions_len -= 2;
- rv = ssl3_AppendHandshakeNumber(ss, extensions_len, 2);
- if (rv != SECSuccess)
- return rv; /* err set by ssl3_AppendHandshakeNumber */
- sent_len = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, extensions_len,
- &ss->xtnData.serverHelloSenders[0]);
- PORT_Assert(sent_len == extensions_len);
- if (sent_len != extensions_len) {
- if (sent_len >= 0)
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
+ rv = ssl3_AppendHandshake(ss, SSL_BUFFER_BASE(&messageBuf),
+ SSL_BUFFER_LEN(&messageBuf));
+ if (rv != SECSuccess) {
+ goto loser; /* err set by AppendHandshake. */
}
if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = ssl3_SetupPendingCipherSpec(ss);
+ rv = ssl3_SetupBothPendingCipherSpecs(ss);
if (rv != SECSuccess) {
- return rv; /* err set by ssl3_SetupPendingCipherSpec */
+ goto loser; /* err set */
}
}
+ sslBuffer_Clear(&extensionBuf);
+ sslBuffer_Clear(&messageBuf);
return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&extensionBuf);
+ sslBuffer_Clear(&messageBuf);
+ return SECFailure;
}
SECStatus
@@ -9438,6 +8994,8 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
SECKEYPublicKey *pubKey;
SECKEYPrivateKey *certPrivateKey;
const sslNamedGroupDef *groupDef;
+ /* Do this on the heap, this could be over 2k long. */
+ sslBuffer dhBuf = SSL_BUFFER_EMPTY;
if (kea_def->kea != kea_dhe_dss && kea_def->kea != kea_dhe_rsa) {
/* TODO: Support DH_anon. It might be sufficient to drop the signature.
@@ -9461,7 +9019,7 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
}
PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
- if (ss->ssl3.pwSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
+ if (ss->version == SSL_LIBRARY_VERSION_TLS_1_2) {
hashAlg = ssl_SignatureSchemeToHashType(ss->ssl3.hs.signatureScheme);
} else {
/* Use ssl_hash_none to represent the MD5+SHA1 combo. */
@@ -9493,11 +9051,11 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
2 + pubKey->u.dh.prime.len +
2 + signed_hash.len;
- if (ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
length += 2;
}
- rv = ssl3_AppendHandshakeHeader(ss, server_key_exchange, length);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_key_exchange, length);
if (rv != SECSuccess) {
goto loser; /* err set by AppendHandshake. */
}
@@ -9514,12 +9072,16 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
goto loser; /* err set by AppendHandshake. */
}
- rv = ssl_AppendPaddedDHKeyShare(ss, pubKey, PR_TRUE);
+ rv = ssl_AppendPaddedDHKeyShare(&dhBuf, pubKey, PR_TRUE);
+ if (rv != SECSuccess) {
+ goto loser; /* err set by AppendPaddedDHKeyShare. */
+ }
+ rv = ssl3_AppendBufferToHandshake(ss, &dhBuf);
if (rv != SECSuccess) {
goto loser; /* err set by AppendHandshake. */
}
- if (ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.signatureScheme, 2);
if (rv != SECSuccess) {
goto loser; /* err set by AppendHandshake. */
@@ -9531,12 +9093,15 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
if (rv != SECSuccess) {
goto loser; /* err set by AppendHandshake. */
}
+
+ sslBuffer_Clear(&dhBuf);
PORT_Free(signed_hash.data);
return SECSuccess;
loser:
if (signed_hash.data)
PORT_Free(signed_hash.data);
+ sslBuffer_Clear(&dhBuf);
return SECFailure;
}
@@ -9571,14 +9136,15 @@ ssl3_SendServerKeyExchange(sslSocket *ss)
}
SECStatus
-ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint8 *buf, unsigned maxLen, PRUint32 *len)
+ssl3_EncodeSigAlgs(const sslSocket *ss, sslBuffer *buf)
{
+ unsigned int lengthOffset;
unsigned int i;
- PRUint8 *p = buf;
+ PRBool found = PR_FALSE;
+ SECStatus rv;
- PORT_Assert(maxLen >= ss->ssl3.signatureSchemeCount * 2);
- if (maxLen < ss->ssl3.signatureSchemeCount * 2) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = sslBuffer_Skip(buf, 2, &lengthOffset);
+ if (rv != SECSuccess) {
return SECFailure;
}
@@ -9596,16 +9162,21 @@ ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint8 *buf, unsigned maxLen, PRUint32
if ((NSS_GetAlgorithmPolicy(hashOID, &policy) != SECSuccess) ||
(policy & NSS_USE_ALG_IN_SSL_KX)) {
- p = ssl_EncodeUintX((PRUint32)ss->ssl3.signatureSchemes[i], 2, p);
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.signatureSchemes[i], 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ found = PR_TRUE;
}
}
- if (p == buf) {
+ if (!found) {
PORT_SetError(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
return SECFailure;
}
- *len = p - buf;
- return SECSuccess;
+
+ return sslBuffer_InsertLength(buf, lengthOffset, 2);
}
static SECStatus
@@ -9614,15 +9185,15 @@ ssl3_SendCertificateRequest(sslSocket *ss)
PRBool isTLS12;
const PRUint8 *certTypes;
SECStatus rv;
- int length;
- SECItem *names;
+ PRUint32 length;
+ const SECItem *names;
unsigned int calen;
unsigned int nnames;
- SECItem *name;
- int i;
+ const SECItem *name;
+ unsigned int i;
int certTypesLength;
- PRUint8 sigAlgs[MAX_SIGNATURE_SCHEMES * 2];
- unsigned int sigAlgsLength = 0;
+ PRUint8 sigAlgs[2 + MAX_SIGNATURE_SCHEMES * 2];
+ sslBuffer sigAlgsBuf = SSL_BUFFER(sigAlgs);
SSL_TRC(3, ("%d: SSL3[%d]: send certificate_request handshake",
SSL_GETPID(), ss->fd));
@@ -9630,7 +9201,7 @@ ssl3_SendCertificateRequest(sslSocket *ss)
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
+ isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2);
rv = ssl_GetCertificateRequestCAs(ss, &calen, &names, &nnames);
if (rv != SECSuccess) {
@@ -9641,14 +9212,14 @@ ssl3_SendCertificateRequest(sslSocket *ss)
length = 1 + certTypesLength + 2 + calen;
if (isTLS12) {
- rv = ssl3_EncodeSigAlgs(ss, sigAlgs, sizeof(sigAlgs), &sigAlgsLength);
+ rv = ssl3_EncodeSigAlgs(ss, &sigAlgsBuf);
if (rv != SECSuccess) {
return rv;
}
- length += 2 + sigAlgsLength;
+ length += SSL_BUFFER_LEN(&sigAlgsBuf);
}
- rv = ssl3_AppendHandshakeHeader(ss, certificate_request, length);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_request, length);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
}
@@ -9657,7 +9228,8 @@ ssl3_SendCertificateRequest(sslSocket *ss)
return rv; /* err set by AppendHandshake. */
}
if (isTLS12) {
- rv = ssl3_AppendHandshakeVariable(ss, sigAlgs, sigAlgsLength, 2);
+ rv = ssl3_AppendHandshake(ss, SSL_BUFFER_BASE(&sigAlgsBuf),
+ SSL_BUFFER_LEN(&sigAlgsBuf));
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
}
@@ -9687,7 +9259,7 @@ ssl3_SendServerHelloDone(sslSocket *ss)
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- rv = ssl3_AppendHandshakeHeader(ss, server_hello_done, 0);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_hello_done, 0);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
}
@@ -9703,8 +9275,7 @@ ssl3_SendServerHelloDone(sslSocket *ss)
* Caller must hold Handshake and RecvBuf locks.
*/
static SECStatus
-ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
- SSL3Hashes *hashes)
+ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
SECItem signed_hash = { siBuffer, NULL, 0 };
SECStatus rv;
@@ -9712,9 +9283,9 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
SSL3AlertDescription desc = handshake_failure;
PRBool isTLS;
SSLSignatureScheme sigScheme;
- SSLHashType hashAlg;
- SSL3Hashes localHashes;
- SSL3Hashes *hashesForVerify = NULL;
+ SSL3Hashes hashes;
+ const PRUint8 *savedMsg = b;
+ const PRUint32 savedLen = length;
SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_verify handshake",
SSL_GETPID(), ss->fd));
@@ -9730,14 +9301,8 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
/* TLS 1.3 is handled by tls13_HandleCertificateVerify */
PORT_Assert(ss->ssl3.prSpec->version <= SSL_LIBRARY_VERSION_TLS_1_2);
- if (!hashes) {
- PORT_Assert(0);
- desc = internal_error;
- errCode = SEC_ERROR_LIBRARY_FAILURE;
- goto alert_loser;
- }
-
- if (ss->ssl3.hs.hashType == handshake_hash_record) {
+ if (ss->ssl3.prSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
+ PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_record);
rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
if (rv != SECSuccess) {
goto loser; /* malformed or unsupported. */
@@ -9750,25 +9315,20 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
goto alert_loser;
}
- hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
-
- /* Read from the message buffer, but we need to use only up to the end
- * of the previous handshake message. The length of the transcript up to
- * that point is saved in |hashes->u.transcriptLen|. */
rv = ssl3_ComputeHandshakeHash(ss->ssl3.hs.messages.buf,
- hashes->u.transcriptLen,
- hashAlg, &localHashes);
-
- if (rv == SECSuccess) {
- hashesForVerify = &localHashes;
- } else {
- errCode = SSL_ERROR_DIGEST_FAILURE;
- desc = decrypt_error;
- goto alert_loser;
- }
+ ss->ssl3.hs.messages.len,
+ ssl_SignatureSchemeToHashType(sigScheme),
+ &hashes);
} else {
- hashesForVerify = hashes;
+ PORT_Assert(ss->ssl3.hs.hashType != handshake_hash_record);
sigScheme = ssl_sig_none;
+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.prSpec, &hashes, 0);
+ }
+
+ if (rv != SECSuccess) {
+ errCode = SSL_ERROR_DIGEST_FAILURE;
+ desc = decrypt_error;
+ goto alert_loser;
}
rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length);
@@ -9779,7 +9339,7 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
/* XXX verify that the key & kea match */
- rv = ssl3_VerifySignedHashes(ss, sigScheme, hashesForVerify, &signed_hash);
+ rv = ssl3_VerifySignedHashes(ss, sigScheme, &hashes, &signed_hash);
if (rv != SECSuccess) {
errCode = PORT_GetError();
desc = isTLS ? decrypt_error : handshake_failure;
@@ -9792,6 +9352,14 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
desc = isTLS ? decode_error : illegal_parameter;
goto alert_loser; /* malformed */
}
+
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_certificate_verify,
+ savedMsg, savedLen);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return rv;
+ }
+
ss->ssl3.hs.ws = wait_change_cipher;
return SECSuccess;
@@ -9832,9 +9400,9 @@ ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
** slot already hold the SpecWriteLock.
*/
PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
+ PORT_Assert(ss->ssl3.prSpec->epoch == ss->ssl3.pwSpec->epoch);
- calg = spec->cipher_def->calg;
+ calg = spec->cipherDef->calg;
/* First get an appropriate slot. */
mechanism_array[0] = CKM_SSL3_PRE_MASTER_KEY_GEN;
@@ -9902,7 +9470,7 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss,
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
+ PORT_Assert(ss->ssl3.prSpec->epoch == ss->ssl3.pwSpec->epoch);
enc_pms.data = b;
enc_pms.len = length;
@@ -9999,7 +9567,7 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss,
}
/* This step will derive the MS from the PMS, among other things. */
- rv = ssl3_InitPendingCipherSpec(ss, currentPms);
+ rv = ssl3_InitPendingCipherSpecs(ss, currentPms, PR_TRUE);
PK11_FreeSymKey(currentPms);
if (rv != SECSuccess) {
@@ -10064,7 +9632,7 @@ ssl3_HandleDHClientKeyExchange(sslSocket *ss,
return SECFailure;
}
- rv = ssl3_InitPendingCipherSpec(ss, pms);
+ rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE);
PK11_FreeSymKey(pms);
ssl_FreeEphemeralKeyPairs(ss);
return rv;
@@ -10163,13 +9731,13 @@ ssl3_SendEmptyCertificate(sslSocket *ss)
const SECItem *context;
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- PORT_Assert(ss->ssl3.hs.certificateRequest);
- context = &ss->ssl3.hs.certificateRequest->context;
+ PORT_Assert(ss->ssl3.hs.clientCertRequested);
+ context = &ss->xtnData.certReqContext;
len = context->len + 1;
isTLS13 = PR_TRUE;
}
- rv = ssl3_AppendHandshakeHeader(ss, certificate, len + 3);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate, len + 3);
if (rv != SECSuccess) {
return rv;
}
@@ -10195,13 +9763,14 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
SECStatus rv;
NewSessionTicket nticket = { 0 };
- rv = ssl3_EncodeSessionTicket(ss, &nticket, &ticket);
+ rv = ssl3_EncodeSessionTicket(ss, &nticket, NULL, 0,
+ ss->ssl3.pwSpec->masterSecret, &ticket);
if (rv != SECSuccess)
goto loser;
/* Serialize the handshake message. Length =
* lifetime (4) + ticket length (2) + ticket. */
- rv = ssl3_AppendHandshakeHeader(ss, new_session_ticket,
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_new_session_ticket,
4 + 2 + ticket.len);
if (rv != SECSuccess)
goto loser;
@@ -10251,7 +9820,7 @@ ssl3_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
* until it has verified the server's Finished message." See the comment in
* ssl3_FinishHandshake for more details.
*/
- ss->ssl3.hs.newSessionTicket.received_timestamp = PR_Now();
+ ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_TimeUsec();
if (length < 4) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
@@ -10393,8 +9962,8 @@ ssl3_SendCertificate(sslSocket *ss)
if (isTLS13) {
contextLen = 1; /* Size of the context length */
if (!ss->sec.isServer) {
- PORT_Assert(ss->ssl3.hs.certificateRequest);
- context = ss->ssl3.hs.certificateRequest->context;
+ PORT_Assert(ss->ssl3.hs.clientCertRequested);
+ context = ss->xtnData.certReqContext;
contextLen += context.len;
}
}
@@ -10412,7 +9981,7 @@ ssl3_SendCertificate(sslSocket *ss)
}
}
- rv = ssl3_AppendHandshakeHeader(ss, certificate,
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate,
contextLen + certChainLen + 3);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
@@ -10487,7 +10056,7 @@ ssl3_SendCertificateStatus(sslSocket *ss)
/* Use the array's first item only (single stapling) */
len = 1 + statusToSend->items[0].len + 3;
- rv = ssl3_AppendHandshakeHeader(ss, certificate_status, len);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_status, len);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
}
@@ -10618,6 +10187,10 @@ ssl3_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
return SECFailure;
}
+ if (ss->sec.isServer) {
+ dtls_ReceivedFirstMessageInFlight(ss);
+ }
+
return ssl3_CompleteHandleCertificate(ss, b, length);
}
@@ -10837,7 +10410,8 @@ ssl3_AuthCertificate(sslSocket *ss)
}
if (pubKey) {
KeyType pubKeyType;
- PRInt32 minKey;
+ PRUint32 minKey;
+ PRInt32 optval;
/* This partly fixes Bug 124230 and may cause problems for
* callers which depend on the old (wrong) behavior. */
ss->sec.authKeyBits = SECKEY_PublicKeyStrengthInBits(pubKey);
@@ -10848,29 +10422,29 @@ ssl3_AuthCertificate(sslSocket *ss)
case rsaPssKey:
case rsaOaepKey:
rv =
- NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minKey);
- if (rv !=
- SECSuccess) {
- minKey =
- SSL_RSA_MIN_MODULUS_BITS;
+ NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &optval);
+ if (rv == SECSuccess && optval > 0) {
+ minKey = (PRUint32)optval;
+ } else {
+ minKey = SSL_RSA_MIN_MODULUS_BITS;
}
break;
case dsaKey:
rv =
- NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &minKey);
- if (rv !=
- SECSuccess) {
- minKey =
- SSL_DSA_MIN_P_BITS;
+ NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &optval);
+ if (rv == SECSuccess && optval > 0) {
+ minKey = (PRUint32)optval;
+ } else {
+ minKey = SSL_DSA_MIN_P_BITS;
}
break;
case dhKey:
rv =
- NSS_OptionGet(NSS_DH_MIN_KEY_SIZE, &minKey);
- if (rv !=
- SECSuccess) {
- minKey =
- SSL_DH_MIN_P_BITS;
+ NSS_OptionGet(NSS_DH_MIN_KEY_SIZE, &optval);
+ if (rv == SECSuccess && optval > 0) {
+ minKey = (PRUint32)optval;
+ } else {
+ minKey = SSL_DH_MIN_P_BITS;
}
break;
default:
@@ -11029,8 +10603,8 @@ ssl3_ComputeTLSFinished(sslSocket *ss, ssl3CipherSpec *spec,
PK11Context *prf_context;
unsigned int retLen;
- PORT_Assert(spec->master_secret);
- if (!spec->master_secret) {
+ PORT_Assert(spec->masterSecret);
+ if (!spec->masterSecret) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
@@ -11045,7 +10619,7 @@ ssl3_ComputeTLSFinished(sslSocket *ss, ssl3CipherSpec *spec,
param.data = (unsigned char *)&tls_mac_params;
param.len = sizeof(tls_mac_params);
prf_context = PK11_CreateContextBySymKey(CKM_TLS_MAC, CKA_SIGN,
- spec->master_secret, &param);
+ spec->masterSecret, &param);
if (!prf_context)
return SECFailure;
@@ -11070,40 +10644,39 @@ ssl3_TLSPRFWithMasterSecret(sslSocket *ss, ssl3CipherSpec *spec,
const unsigned char *val, unsigned int valLen,
unsigned char *out, unsigned int outLen)
{
- SECStatus rv = SECSuccess;
+ SECItem param = { siBuffer, NULL, 0 };
+ CK_MECHANISM_TYPE mech = CKM_TLS_PRF_GENERAL;
+ PK11Context *prf_context;
+ unsigned int retLen;
+ SECStatus rv;
- if (spec->master_secret) {
- SECItem param = { siBuffer, NULL, 0 };
- CK_MECHANISM_TYPE mech = CKM_TLS_PRF_GENERAL;
- PK11Context *prf_context;
- unsigned int retLen;
+ if (!spec->masterSecret) {
+ PORT_Assert(spec->masterSecret);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
- if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
- /* Bug 1312976 non-SHA256 exporters are broken. */
- if (ssl3_GetPrfHashMechanism(ss) != CKM_SHA256) {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- mech = CKM_NSS_TLS_PRF_GENERAL_SHA256;
- }
- prf_context = PK11_CreateContextBySymKey(mech, CKA_SIGN,
- spec->master_secret, &param);
- if (!prf_context)
+ if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
+ /* Bug 1312976 non-SHA256 exporters are broken. */
+ if (ssl3_GetPrfHashMechanism(ss) != CKM_SHA256) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
+ }
+ mech = CKM_NSS_TLS_PRF_GENERAL_SHA256;
+ }
+ prf_context = PK11_CreateContextBySymKey(mech, CKA_SIGN,
+ spec->masterSecret, &param);
+ if (!prf_context)
+ return SECFailure;
- rv = PK11_DigestBegin(prf_context);
- rv |= PK11_DigestOp(prf_context, (unsigned char *)label, labelLen);
- rv |= PK11_DigestOp(prf_context, val, valLen);
- rv |= PK11_DigestFinal(prf_context, out, &retLen, outLen);
- PORT_Assert(rv != SECSuccess || retLen == outLen);
+ rv = PK11_DigestBegin(prf_context);
+ rv |= PK11_DigestOp(prf_context, (unsigned char *)label, labelLen);
+ rv |= PK11_DigestOp(prf_context, val, valLen);
+ rv |= PK11_DigestFinal(prf_context, out, &retLen, outLen);
+ PORT_Assert(rv != SECSuccess || retLen == outLen);
- PK11_DestroyContext(prf_context, PR_TRUE);
- } else {
- PORT_Assert(spec->master_secret);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- rv = SECFailure;
- }
+ PK11_DestroyContext(prf_context, PR_TRUE);
return rv;
}
@@ -11127,7 +10700,7 @@ ssl3_SendNextProto(sslSocket *ss)
padding_len = 32 - ((ss->xtnData.nextProto.len + 2) % 32);
- rv = ssl3_AppendHandshakeHeader(ss, next_proto, ss->xtnData.nextProto.len + 2 + padding_len);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_next_proto, ss->xtnData.nextProto.len + 2 + padding_len);
if (rv != SECSuccess) {
return rv; /* error code set by AppendHandshakeHeader */
}
@@ -11143,40 +10716,44 @@ ssl3_SendNextProto(sslSocket *ss)
return rv;
}
-/* called from ssl3_SendFinished
+/* called from ssl3_SendFinished and tls13_DeriveSecret.
*
* This function is simply a debugging aid and therefore does not return a
* SECStatus. */
-static void
-ssl3_RecordKeyLog(sslSocket *ss)
+void
+ssl3_RecordKeyLog(sslSocket *ss, const char *label, PK11SymKey *secret)
{
#ifdef NSS_ALLOW_SSLKEYLOGFILE
SECStatus rv;
SECItem *keyData;
- char buf[14 /* "CLIENT_RANDOM " */ +
- SSL3_RANDOM_LENGTH * 2 /* client_random */ +
- 1 /* " " */ +
- 48 * 2 /* master secret */ +
- 1 /* new line */];
- unsigned int j;
+ /* Longest label is "CLIENT_HANDSHAKE_TRAFFIC_SECRET", master secret is 48
+ * bytes which happens to be the largest in TLS 1.3 as well (SHA384).
+ * Maximum line length: "CLIENT_HANDSHAKE_TRAFFIC_SECRET" (31) + " " (1) +
+ * client_random (32*2) + " " (1) +
+ * traffic_secret (48*2) + "\n" (1) = 194. */
+ char buf[200];
+ unsigned int offset, len;
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
if (!ssl_keylog_iob)
return;
- rv = PK11_ExtractKeyValue(ss->ssl3.cwSpec->master_secret);
+ rv = PK11_ExtractKeyValue(secret);
if (rv != SECSuccess)
return;
- ssl_GetSpecReadLock(ss);
-
/* keyData does not need to be freed. */
- keyData = PK11_GetKeyData(ss->ssl3.cwSpec->master_secret);
- if (!keyData || !keyData->data || keyData->len != 48) {
- ssl_ReleaseSpecReadLock(ss);
+ keyData = PK11_GetKeyData(secret);
+ if (!keyData || !keyData->data)
+ return;
+
+ len = strlen(label) + 1 + /* label + space */
+ SSL3_RANDOM_LENGTH * 2 + 1 + /* client random (hex) + space */
+ keyData->len * 2 + 1; /* secret (hex) + newline */
+ PORT_Assert(len <= sizeof(buf));
+ if (len > sizeof(buf))
return;
- }
/* https://developer.mozilla.org/en/NSS_Key_Log_Format */
@@ -11184,23 +10761,22 @@ ssl3_RecordKeyLog(sslSocket *ss)
* keylog, so we have to do everything in a single call to
* fwrite. */
- memcpy(buf, "CLIENT_RANDOM ", 14);
- j = 14;
- hexEncode(buf + j, ss->ssl3.hs.client_random.rand, SSL3_RANDOM_LENGTH);
- j += SSL3_RANDOM_LENGTH * 2;
- buf[j++] = ' ';
- hexEncode(buf + j, keyData->data, 48);
- j += 48 * 2;
- buf[j++] = '\n';
-
- PORT_Assert(j == sizeof(buf));
-
- ssl_ReleaseSpecReadLock(ss);
-
- if (fwrite(buf, sizeof(buf), 1, ssl_keylog_iob) != 1)
- return;
- fflush(ssl_keylog_iob);
- return;
+ strcpy(buf, label);
+ offset = strlen(label);
+ buf[offset++] += ' ';
+ hexEncode(buf + offset, ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH);
+ offset += SSL3_RANDOM_LENGTH * 2;
+ buf[offset++] = ' ';
+ hexEncode(buf + offset, keyData->data, keyData->len);
+ offset += keyData->len * 2;
+ buf[offset++] = '\n';
+
+ PORT_Assert(offset == len);
+
+ PZ_Lock(ssl_keylog_lock);
+ if (fwrite(buf, len, 1, ssl_keylog_iob) == 1)
+ fflush(ssl_keylog_iob);
+ PZ_Unlock(ssl_keylog_lock);
#endif
}
@@ -11242,7 +10818,7 @@ ssl3_SendFinished(sslSocket *ss, PRInt32 flags)
else
ss->ssl3.hs.finishedMsgs.tFinished[0] = tlsFinished;
ss->ssl3.hs.finishedBytes = sizeof tlsFinished;
- rv = ssl3_AppendHandshakeHeader(ss, finished, sizeof tlsFinished);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_finished, sizeof tlsFinished);
if (rv != SECSuccess)
goto fail; /* err set by AppendHandshake. */
rv = ssl3_AppendHandshake(ss, &tlsFinished, sizeof tlsFinished);
@@ -11255,7 +10831,7 @@ ssl3_SendFinished(sslSocket *ss, PRInt32 flags)
ss->ssl3.hs.finishedMsgs.sFinished[0] = hashes.u.s;
PORT_Assert(hashes.len == sizeof hashes.u.s);
ss->ssl3.hs.finishedBytes = sizeof hashes.u.s;
- rv = ssl3_AppendHandshakeHeader(ss, finished, sizeof hashes.u.s);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_finished, sizeof hashes.u.s);
if (rv != SECSuccess)
goto fail; /* err set by AppendHandshake. */
rv = ssl3_AppendHandshake(ss, &hashes.u.s, sizeof hashes.u.s);
@@ -11267,7 +10843,7 @@ ssl3_SendFinished(sslSocket *ss, PRInt32 flags)
goto fail; /* error code set by ssl3_FlushHandshake */
}
- ssl3_RecordKeyLog(ss);
+ ssl3_RecordKeyLog(ss, "CLIENT_RANDOM", ss->ssl3.cwSpec->masterSecret);
return SECSuccess;
@@ -11279,8 +10855,8 @@ fail:
* Caller holds the Spec read lock.
*/
SECStatus
-ssl3_CacheWrappedMasterSecret(sslSocket *ss, sslSessionID *sid,
- ssl3CipherSpec *spec)
+ssl3_CacheWrappedSecret(sslSocket *ss, sslSessionID *sid,
+ PK11SymKey *secret)
{
PK11SymKey *wrappingKey = NULL;
PK11SlotInfo *symKeySlot;
@@ -11289,7 +10865,7 @@ ssl3_CacheWrappedMasterSecret(sslSocket *ss, sslSessionID *sid,
PRBool isServer = ss->sec.isServer;
CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
- symKeySlot = PK11_GetSlotFromKey(spec->master_secret);
+ symKeySlot = PK11_GetSlotFromKey(secret);
if (!isServer) {
int wrapKeyIndex;
int incarnation;
@@ -11350,7 +10926,7 @@ ssl3_CacheWrappedMasterSecret(sslSocket *ss, sslSessionID *sid,
wmsItem.data = sid->u.ssl3.keys.wrapped_master_secret;
wmsItem.len = sizeof sid->u.ssl3.keys.wrapped_master_secret;
rv = PK11_WrapSymKey(mechanism, NULL, wrappingKey,
- spec->master_secret, &wmsItem);
+ secret, &wmsItem);
/* rv is examined below. */
sid->u.ssl3.keys.wrapped_master_secret_len = wmsItem.len;
PK11_FreeSymKey(wrappingKey);
@@ -11363,13 +10939,13 @@ ssl3_CacheWrappedMasterSecret(sslSocket *ss, sslSessionID *sid,
* Caller must hold Handshake and RecvBuf locks.
*/
static SECStatus
-ssl3_HandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
- const SSL3Hashes *hashes)
+ssl3_HandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
sslSessionID *sid = ss->sec.ci.sid;
SECStatus rv = SECSuccess;
PRBool isServer = ss->sec.isServer;
PRBool isTLS;
+ SSL3Hashes hashes;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -11383,13 +10959,23 @@ ssl3_HandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
return SECFailure;
}
- if (!hashes) {
- PORT_Assert(0);
- SSL3_SendAlert(ss, alert_fatal, internal_error);
+ if (!ss->sec.isServer || !ss->opt.requestCertificate) {
+ dtls_ReceivedFirstMessageInFlight(ss);
+ }
+
+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.crSpec, &hashes,
+ isServer ? sender_client : sender_server);
+ if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return rv;
+ }
+
isTLS = (PRBool)(ss->ssl3.crSpec->version > SSL_LIBRARY_VERSION_3_0);
if (isTLS) {
TLSFinished tlsFinished;
@@ -11402,7 +10988,7 @@ ssl3_HandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
#endif
}
rv = ssl3_ComputeTLSFinished(ss, ss->ssl3.crSpec, !isServer,
- hashes, &tlsFinished);
+ &hashes, &tlsFinished);
if (!isServer)
ss->ssl3.hs.finishedMsgs.tFinished[1] = tlsFinished;
else
@@ -11425,12 +11011,12 @@ ssl3_HandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
}
if (!isServer)
- ss->ssl3.hs.finishedMsgs.sFinished[1] = hashes->u.s;
+ ss->ssl3.hs.finishedMsgs.sFinished[1] = hashes.u.s;
else
- ss->ssl3.hs.finishedMsgs.sFinished[0] = hashes->u.s;
- PORT_Assert(hashes->len == sizeof hashes->u.s);
- ss->ssl3.hs.finishedBytes = sizeof hashes->u.s;
- if (0 != NSS_SecureMemcmp(&hashes->u.s, b, length)) {
+ ss->ssl3.hs.finishedMsgs.sFinished[0] = hashes.u.s;
+ PORT_Assert(hashes.len == sizeof hashes.u.s);
+ ss->ssl3.hs.finishedBytes = sizeof hashes.u.s;
+ if (0 != NSS_SecureMemcmp(&hashes.u.s, b, length)) {
(void)ssl3_HandshakeFailure(ss);
PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
return SECFailure;
@@ -11500,7 +11086,7 @@ xmit_loser:
}
if (sid->cached == never_cached && !ss->opt.noCache) {
- rv = ssl3_FillInCachedSID(ss, sid);
+ rv = ssl3_FillInCachedSID(ss, sid, ss->ssl3.crSpec->masterSecret);
/* If the wrap failed, we don't cache the sid.
* The connection continues normally however.
@@ -11524,21 +11110,26 @@ xmit_loser:
}
SECStatus
-ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid)
+ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid, PK11SymKey *secret)
{
- SECStatus rv;
+ PORT_Assert(secret);
/* fill in the sid */
sid->u.ssl3.cipherSuite = ss->ssl3.hs.cipher_suite;
- sid->u.ssl3.compression = ss->ssl3.hs.compression;
sid->u.ssl3.policy = ss->ssl3.policy;
sid->version = ss->version;
sid->authType = ss->sec.authType;
sid->authKeyBits = ss->sec.authKeyBits;
sid->keaType = ss->sec.keaType;
sid->keaKeyBits = ss->sec.keaKeyBits;
- sid->lastAccessTime = sid->creationTime = ssl_Time();
- sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
+ if (ss->sec.keaGroup) {
+ sid->keaGroup = ss->sec.keaGroup->name;
+ } else {
+ sid->keaGroup = ssl_grp_none;
+ }
+ sid->sigScheme = ss->sec.signatureScheme;
+ sid->lastAccessTime = sid->creationTime = ssl_TimeUsec();
+ sid->expirationTime = sid->creationTime + ssl3_sid_timeout * PR_USEC_PER_SEC;
sid->localCert = CERT_DupCertificate(ss->sec.localCert);
if (ss->sec.isServer) {
sid->namedCurve = ss->sec.serverCert->namedCurve;
@@ -11552,25 +11143,8 @@ ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid)
}
}
- ssl_GetSpecReadLock(ss); /*************************************/
-
/* Copy the master secret (wrapped or unwrapped) into the sid */
- if (ss->ssl3.crSpec->msItem.len && ss->ssl3.crSpec->msItem.data) {
- sid->u.ssl3.keys.wrapped_master_secret_len =
- ss->ssl3.crSpec->msItem.len;
- memcpy(sid->u.ssl3.keys.wrapped_master_secret,
- ss->ssl3.crSpec->msItem.data, ss->ssl3.crSpec->msItem.len);
- sid->u.ssl3.masterValid = PR_TRUE;
- sid->u.ssl3.keys.msIsWrapped = PR_FALSE;
- rv = SECSuccess;
- } else {
- rv = ssl3_CacheWrappedMasterSecret(ss, ss->sec.ci.sid,
- ss->ssl3.crSpec);
- sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
- }
- ssl_ReleaseSpecReadLock(ss); /*************************************/
-
- return rv;
+ return ssl3_CacheWrappedSecret(ss, ss->sec.ci.sid, secret);
}
/* The return type is SECStatus instead of void because this function needs
@@ -11619,8 +11193,66 @@ ssl3_FinishHandshake(sslSocket *ss)
return SECSuccess;
}
+SECStatus
+ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type,
+ PRUint32 dtlsSeq,
+ const PRUint8 *b, PRUint32 length)
+{
+ PRUint8 hdr[4];
+ PRUint8 dtlsData[8];
+ SECStatus rv;
+
+ PRINT_BUF(50, (ss, "Hash handshake message:", b, length));
+
+ hdr[0] = (PRUint8)type;
+ hdr[1] = (PRUint8)(length >> 16);
+ hdr[2] = (PRUint8)(length >> 8);
+ hdr[3] = (PRUint8)(length);
+
+ rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)hdr, 4);
+ if (rv != SECSuccess)
+ return rv; /* err code already set. */
+
+ /* Extra data to simulate a complete DTLS handshake fragment */
+ if (IS_DTLS(ss)) {
+ /* Sequence number */
+ dtlsData[0] = MSB(dtlsSeq);
+ dtlsData[1] = LSB(dtlsSeq);
+
+ /* Fragment offset */
+ dtlsData[2] = 0;
+ dtlsData[3] = 0;
+ dtlsData[4] = 0;
+
+ /* Fragment length */
+ dtlsData[5] = (PRUint8)(length >> 16);
+ dtlsData[6] = (PRUint8)(length >> 8);
+ dtlsData[7] = (PRUint8)(length);
+
+ rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)dtlsData,
+ sizeof(dtlsData));
+ if (rv != SECSuccess)
+ return rv; /* err code already set. */
+ }
+
+ /* The message body */
+ rv = ssl3_UpdateHandshakeHashes(ss, b, length);
+ if (rv != SECSuccess)
+ return rv; /* err code already set. */
+
+ return SECSuccess;
+}
+
+SECStatus
+ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type,
+ const PRUint8 *b, PRUint32 length)
+{
+ return ssl_HashHandshakeMessageInt(ss, type, ss->ssl3.hs.recvMessageSeq,
+ b, length);
+}
+
/* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3
- * hanshake message.
+ * handshake message.
* Caller must hold Handshake and RecvBuf locks.
*/
SECStatus
@@ -11628,130 +11260,43 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
PRBool endOfRecord)
{
SECStatus rv = SECSuccess;
- SSL3HandshakeType type = ss->ssl3.hs.msg_type;
- SSL3Hashes hashes; /* computed hashes are put here. */
- SSL3Hashes *hashesPtr = NULL; /* Set when hashes are computed */
- PRUint8 hdr[4];
- PRUint8 dtlsData[8];
- PRBool computeHashes = PR_FALSE;
PRUint16 epoch;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- /*
- * We have to compute the hashes before we update them with the
- * current message.
- */
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- if ((type == finished) && (ss->ssl3.hs.ws == wait_finished)) {
- computeHashes = PR_TRUE;
- } else if ((type == certificate_verify) && (ss->ssl3.hs.ws == wait_cert_verify)) {
- if (ss->ssl3.hs.hashType == handshake_hash_record) {
- /* We cannot compute the hash yet. We must wait until we have
- * decoded the certificate_verify message in
- * ssl3_HandleCertificateVerify, which will tell us which
- * hash function we must use.
- *
- * (ssl3_HandleCertificateVerify cannot simply look at the
- * buffer length itself, because at the time we reach it,
- * additional handshake messages will have been added to the
- * buffer, e.g. the certificate_verify message itself.)
- *
- * Therefore, we use SSL3Hashes.u.transcriptLen to save how much
- * data there is and read directly from ss->ssl3.hs.messages
- * when calculating the hashes.
- *
- * ssl3_HandleCertificateVerify will detect
- * hashType == handshake_hash_record
- * and use that information to calculate the hash.
- */
- hashes.u.transcriptLen = ss->ssl3.hs.messages.len;
- hashesPtr = &hashes;
- } else {
- computeHashes = PR_TRUE;
- }
- }
- } else {
- if (type == certificate_verify) {
- computeHashes = TLS13_IN_HS_STATE(ss, wait_cert_verify);
- } else if (type == finished) {
- computeHashes =
- TLS13_IN_HS_STATE(ss, wait_cert_request, wait_finished);
- }
- }
- ssl_GetSpecReadLock(ss); /************************************/
- if (computeHashes) {
- SSL3Sender sender = (SSL3Sender)0;
- ssl3CipherSpec *rSpec = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ? ss->ssl3.crSpec
- : ss->ssl3.prSpec;
-
- if (type == finished) {
- sender = ss->sec.isServer ? sender_client : sender_server;
- rSpec = ss->ssl3.crSpec;
- }
- rv = ssl3_ComputeHandshakeHashes(ss, rSpec, &hashes, sender);
- if (rv == SECSuccess) {
- hashesPtr = &hashes;
- }
- }
- ssl_ReleaseSpecReadLock(ss); /************************************/
- if (rv != SECSuccess) {
- return rv; /* error code was set by ssl3_ComputeHandshakeHashes*/
- }
SSL_TRC(30, ("%d: SSL3[%d]: handle handshake message: %s", SSL_GETPID(),
ss->fd, ssl3_DecodeHandshakeType(ss->ssl3.hs.msg_type)));
- hdr[0] = (PRUint8)ss->ssl3.hs.msg_type;
- hdr[1] = (PRUint8)(length >> 16);
- hdr[2] = (PRUint8)(length >> 8);
- hdr[3] = (PRUint8)(length);
-
- /* Start new handshake hashes when we start a new handshake. Unless this is
- * TLS 1.3 and we sent a HelloRetryRequest. */
- if (ss->ssl3.hs.msg_type == client_hello && !ss->ssl3.hs.helloRetry) {
+ /* Start new handshake hashes when we start a new handshake. */
+ if (ss->ssl3.hs.msg_type == ssl_hs_client_hello) {
ssl3_RestartHandshakeHashes(ss);
}
- /* We should not include hello_request and hello_verify_request messages
- * in the handshake hashes */
- if ((ss->ssl3.hs.msg_type != hello_request) &&
- (ss->ssl3.hs.msg_type != hello_verify_request)) {
- rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)hdr, 4);
- if (rv != SECSuccess)
- return rv; /* err code already set. */
-
- /* Extra data to simulate a complete DTLS handshake fragment */
- if (IS_DTLS(ss)) {
- /* Sequence number */
- dtlsData[0] = MSB(ss->ssl3.hs.recvMessageSeq);
- dtlsData[1] = LSB(ss->ssl3.hs.recvMessageSeq);
-
- /* Fragment offset */
- dtlsData[2] = 0;
- dtlsData[3] = 0;
- dtlsData[4] = 0;
-
- /* Fragment length */
- dtlsData[5] = (PRUint8)(length >> 16);
- dtlsData[6] = (PRUint8)(length >> 8);
- dtlsData[7] = (PRUint8)(length);
+ switch (ss->ssl3.hs.msg_type) {
+ case ssl_hs_hello_request:
+ case ssl_hs_hello_verify_request:
+ /* We don't include hello_request and hello_verify_request messages
+ * in the handshake hashes */
+ break;
- rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)dtlsData,
- sizeof(dtlsData));
- if (rv != SECSuccess)
- return rv; /* err code already set. */
- }
+ /* Defer hashing of these messages until the message handlers. */
+ case ssl_hs_client_hello:
+ case ssl_hs_server_hello:
+ case ssl_hs_certificate_verify:
+ case ssl_hs_finished:
+ break;
- /* The message body */
- rv = ssl3_UpdateHandshakeHashes(ss, b, length);
- if (rv != SECSuccess)
- return rv; /* err code already set. */
+ default:
+ rv = ssl_HashHandshakeMessage(ss, ss->ssl3.hs.msg_type, b, length);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
PORT_SetError(0); /* each message starts with no error. */
if (ss->ssl3.hs.ws == wait_certificate_status &&
- ss->ssl3.hs.msg_type != certificate_status) {
+ ss->ssl3.hs.msg_type != ssl_hs_certificate_status) {
/* If we negotiated the certificate_status extension then we deferred
* certificate validation until we get the CertificateStatus messsage.
* But the CertificateStatus message is optional. If the server did
@@ -11768,7 +11313,7 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
epoch = ss->ssl3.crSpec->epoch;
switch (ss->ssl3.hs.msg_type) {
- case client_hello:
+ case ssl_hs_client_hello:
if (!ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO);
@@ -11776,7 +11321,7 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
}
rv = ssl3_HandleClientHello(ss, b, length);
break;
- case server_hello:
+ case ssl_hs_server_hello:
if (ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO);
@@ -11786,10 +11331,9 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
break;
default:
if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = ssl3_HandlePostHelloHandshakeMessage(ss, b, length, hashesPtr);
+ rv = ssl3_HandlePostHelloHandshakeMessage(ss, b, length);
} else {
- rv = tls13_HandlePostHelloHandshakeMessage(ss, b, length,
- hashesPtr);
+ rv = tls13_HandlePostHelloHandshakeMessage(ss, b, length);
}
break;
}
@@ -11811,13 +11355,13 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
static SECStatus
ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
- PRUint32 length, SSL3Hashes *hashesPtr)
+ PRUint32 length)
{
SECStatus rv;
PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
switch (ss->ssl3.hs.msg_type) {
- case hello_request:
+ case ssl_hs_hello_request:
if (length != 0) {
(void)ssl3_DecodeError(ss);
PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_REQUEST);
@@ -11831,13 +11375,7 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
rv = ssl3_HandleHelloRequest(ss);
break;
- case hello_retry_request:
- /* This arrives here because - as a client - we haven't received a
- * final decision on the version from the server. */
- rv = tls13_HandleHelloRetryRequest(ss, b, length);
- break;
-
- case hello_verify_request:
+ case ssl_hs_hello_verify_request:
if (!IS_DTLS(ss) || ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST);
@@ -11845,13 +11383,13 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
}
rv = dtls_HandleHelloVerifyRequest(ss, b, length);
break;
- case certificate:
+ case ssl_hs_certificate:
rv = ssl3_HandleCertificate(ss, b, length);
break;
- case certificate_status:
+ case ssl_hs_certificate_status:
rv = ssl3_HandleCertificateStatus(ss, b, length);
break;
- case server_key_exchange:
+ case ssl_hs_server_key_exchange:
if (ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH);
@@ -11859,7 +11397,7 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
}
rv = ssl3_HandleServerKeyExchange(ss, b, length);
break;
- case certificate_request:
+ case ssl_hs_certificate_request:
if (ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST);
@@ -11867,7 +11405,7 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
}
rv = ssl3_HandleCertificateRequest(ss, b, length);
break;
- case server_hello_done:
+ case ssl_hs_server_hello_done:
if (length != 0) {
(void)ssl3_DecodeError(ss);
PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_DONE);
@@ -11880,15 +11418,15 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
}
rv = ssl3_HandleServerHelloDone(ss);
break;
- case certificate_verify:
+ case ssl_hs_certificate_verify:
if (!ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
return SECFailure;
}
- rv = ssl3_HandleCertificateVerify(ss, b, length, hashesPtr);
+ rv = ssl3_HandleCertificateVerify(ss, b, length);
break;
- case client_key_exchange:
+ case ssl_hs_client_key_exchange:
if (!ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CLIENT_KEY_EXCH);
@@ -11896,7 +11434,7 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
}
rv = ssl3_HandleClientKeyExchange(ss, b, length);
break;
- case new_session_ticket:
+ case ssl_hs_new_session_ticket:
if (ss->sec.isServer) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET);
@@ -11904,8 +11442,8 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
}
rv = ssl3_HandleNewSessionTicket(ss, b, length);
break;
- case finished:
- rv = ssl3_HandleFinished(ss, b, length, hashesPtr);
+ case ssl_hs_finished:
+ rv = ssl3_HandleFinished(ss, b, length);
break;
default:
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
@@ -11946,7 +11484,7 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
t = *(buf->buf++);
buf->len--;
if (ss->ssl3.hs.header_bytes++ == 0)
- ss->ssl3.hs.msg_type = (SSL3HandshakeType)t;
+ ss->ssl3.hs.msg_type = (SSLHandshakeType)t;
else
ss->ssl3.hs.msg_len = (ss->ssl3.hs.msg_len << 8) + t;
if (ss->ssl3.hs.header_bytes < 4)
@@ -12276,31 +11814,34 @@ ssl_CBCExtractMAC(sslBuffer *plaintext,
*
*/
static SECStatus
-ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
+ssl3_UnprotectRecord(sslSocket *ss,
+ ssl3CipherSpec *spec,
+ SSL3Ciphertext *cText, sslBuffer *plaintext,
SSL3AlertDescription *alert)
{
- ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
- const ssl3BulkCipherDef *cipher_def = crSpec->cipher_def;
+ const ssl3BulkCipherDef *cipher_def = spec->cipherDef;
PRBool isTLS;
unsigned int good;
unsigned int ivLen = 0;
SSL3ContentType rType;
unsigned int minLength;
unsigned int originalLen = 0;
- unsigned char header[13];
- unsigned int headerLen;
+ PRUint8 headerBuf[13];
+ sslBuffer header = SSL_BUFFER(headerBuf);
PRUint8 hash[MAX_MAC_LENGTH];
PRUint8 givenHashBuf[MAX_MAC_LENGTH];
PRUint8 *givenHash;
unsigned int hashBytes = MAX_MAC_LENGTH + 1;
SECStatus rv;
+ PORT_Assert(spec->direction == CipherSpecRead);
+
good = ~0U;
- minLength = crSpec->mac_size;
+ minLength = spec->macDef->mac_size;
if (cipher_def->type == type_block) {
/* CBC records have a padding length byte at the end. */
minLength++;
- if (crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+ if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
/* With >= TLS 1.1, CBC records have an explicit IV. */
minLength += cipher_def->iv_size;
}
@@ -12315,7 +11856,7 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
}
if (cipher_def->type == type_block &&
- crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+ spec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
/* Consume the per-record explicit IV. RFC 4346 Section 6.2.3.2 states
* "The receiver decrypts the entire GenericBlockCipher structure and
* then discards the first cipher block corresponding to the IV
@@ -12338,8 +11879,8 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
* the block it doesn't matter. The decryption of the next block
* depends only on the ciphertext of the IV block.
*/
- rv = crSpec->decode(crSpec->decodeContext, iv, &decoded,
- sizeof(iv), cText->buf->buf, ivLen);
+ rv = spec->cipher(spec->cipherContext, iv, &decoded,
+ sizeof(iv), cText->buf->buf, ivLen);
good &= SECStatusToMask(rv);
}
@@ -12347,7 +11888,7 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
PRINT_BUF(80, (ss, "ciphertext:", cText->buf->buf + ivLen,
cText->buf->len - ivLen));
- isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
+ isTLS = (PRBool)(spec->version > SSL_LIBRARY_VERSION_3_0);
if (isTLS && cText->buf->len - ivLen > (MAX_FRAGMENT_LENGTH + 2048)) {
*alert = record_overflow;
@@ -12364,19 +11905,18 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
unsigned int decryptedLen =
cText->buf->len - cipher_def->explicit_nonce_size -
cipher_def->tag_size;
- headerLen = ssl3_BuildRecordPseudoHeader(
- header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
- rType, isTLS, cText->version, IS_DTLS(ss), decryptedLen);
- PORT_Assert(headerLen <= sizeof(header));
- rv = crSpec->aead(
- ss->sec.isServer ? &crSpec->client : &crSpec->server,
- PR_TRUE, /* do decrypt */
- plaintext->buf, /* out */
- (int *)&plaintext->len, /* outlen */
- plaintext->space, /* maxout */
- cText->buf->buf, /* in */
- cText->buf->len, /* inlen */
- header, headerLen);
+ rv = ssl3_BuildRecordPseudoHeader(
+ spec->epoch, IS_DTLS(ss) ? cText->seq_num : spec->seqNum,
+ rType, isTLS, cText->version, IS_DTLS(ss), decryptedLen, &header);
+ PORT_Assert(rv == SECSuccess);
+ rv = spec->aead(&spec->keyMaterial,
+ PR_TRUE, /* do decrypt */
+ plaintext->buf, /* out */
+ (int *)&plaintext->len, /* outlen */
+ plaintext->space, /* maxout */
+ cText->buf->buf, /* in */
+ cText->buf->len, /* inlen */
+ SSL_BUFFER_BASE(&header), SSL_BUFFER_LEN(&header));
if (rv != SECSuccess) {
good = 0;
}
@@ -12387,8 +11927,8 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
}
/* decrypt from cText buf to plaintext. */
- rv = crSpec->decode(
- crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
+ rv = spec->cipher(
+ spec->cipherContext, plaintext->buf, (int *)&plaintext->len,
plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
if (rv != SECSuccess) {
goto decrypt_loser;
@@ -12401,7 +11941,7 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
/* If it's a block cipher, check and strip the padding. */
if (cipher_def->type == type_block) {
const unsigned int blockSize = cipher_def->block_size;
- const unsigned int macSize = crSpec->mac_size;
+ const unsigned int macSize = spec->macDef->mac_size;
if (!isTLS) {
good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
@@ -12413,32 +11953,32 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
}
/* compute the MAC */
- headerLen = ssl3_BuildRecordPseudoHeader(
- header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+ rv = ssl3_BuildRecordPseudoHeader(
+ spec->epoch, IS_DTLS(ss) ? cText->seq_num : spec->seqNum,
rType, isTLS, cText->version, IS_DTLS(ss),
- plaintext->len - crSpec->mac_size);
- PORT_Assert(headerLen <= sizeof(header));
+ plaintext->len - spec->macDef->mac_size, &header);
+ PORT_Assert(rv == SECSuccess);
if (cipher_def->type == type_block) {
rv = ssl3_ComputeRecordMACConstantTime(
- crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
+ spec, SSL_BUFFER_BASE(&header), SSL_BUFFER_LEN(&header),
plaintext->buf, plaintext->len, originalLen,
hash, &hashBytes);
ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
- crSpec->mac_size);
+ spec->macDef->mac_size);
givenHash = givenHashBuf;
/* plaintext->len will always have enough space to remove the MAC
* because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
* plaintext->len if the result has enough space for the MAC and we
* tested the unadjusted size against minLength, above. */
- plaintext->len -= crSpec->mac_size;
+ plaintext->len -= spec->macDef->mac_size;
} else {
/* This is safe because we checked the minLength above. */
- plaintext->len -= crSpec->mac_size;
+ plaintext->len -= spec->macDef->mac_size;
rv = ssl3_ComputeRecordMAC(
- crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
+ spec, SSL_BUFFER_BASE(&header), SSL_BUFFER_LEN(&header),
plaintext->buf, plaintext->len, hash, &hashBytes);
/* We can read the MAC directly from the record because its location
@@ -12448,8 +11988,8 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
good &= SECStatusToMask(rv);
- if (hashBytes != (unsigned)crSpec->mac_size ||
- NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
+ if (hashBytes != (unsigned)spec->macDef->mac_size ||
+ NSS_SecureMemcmp(givenHash, hash, spec->macDef->mac_size) != 0) {
/* We're allowed to leak whether or not the MAC check was correct */
good = 0;
}
@@ -12465,7 +12005,84 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
return SECSuccess;
}
-/* if cText is non-null, then decipher, check MAC, and decompress the
+static SECStatus
+ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType,
+ DTLSEpoch epoch, sslSequenceNumber seqNum,
+ sslBuffer *databuf)
+{
+ SECStatus rv;
+
+ ssl_GetSSL3HandshakeLock(ss);
+
+ /* All the functions called in this switch MUST set error code if
+ ** they return SECFailure or SECWouldBlock.
+ */
+ switch (rType) {
+ case content_change_cipher_spec:
+ rv = ssl3_HandleChangeCipherSpecs(ss, databuf);
+ break;
+ case content_alert:
+ rv = ssl3_HandleAlert(ss, databuf);
+ break;
+ case content_handshake:
+ if (!IS_DTLS(ss)) {
+ rv = ssl3_HandleHandshake(ss, databuf);
+ } else {
+ rv = dtls_HandleHandshake(ss, epoch, seqNum, databuf);
+ }
+ break;
+ case content_ack:
+ if (IS_DTLS(ss) && tls13_MaybeTls13(ss)) {
+ rv = dtls13_HandleAck(ss, databuf);
+ break;
+ }
+ /* Fall through. */
+ default:
+ SSL_DBG(("%d: SSL3[%d]: bogus content type=%d",
+ SSL_GETPID(), ss->fd, rType));
+ PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
+ ssl3_DecodeError(ss);
+ rv = SECFailure;
+ break;
+ }
+
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ return rv;
+}
+
+/* Find the cipher spec to use for a given record. For TLS, this
+ * is the current cipherspec. For DTLS, we look up by epoch.
+ * In DTLS < 1.3 this just means the current epoch or nothing,
+ * but in DTLS >= 1.3, we keep multiple reading cipherspecs.
+ * Returns NULL if no appropriate cipher spec is found.
+ */
+static ssl3CipherSpec *
+ssl3_GetCipherSpec(sslSocket *ss, sslSequenceNumber seq)
+{
+ ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
+ ssl3CipherSpec *newSpec = NULL;
+ DTLSEpoch epoch = seq >> 48;
+
+ if (!IS_DTLS(ss)) {
+ return crSpec;
+ }
+ if (crSpec->epoch == epoch) {
+ return crSpec;
+ }
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ /* Try to find the cipher spec. */
+ newSpec = ssl_FindCipherSpecByEpoch(ss, CipherSpecRead,
+ epoch);
+ if (newSpec != NULL) {
+ return newSpec;
+ }
+ }
+ SSL_TRC(10, ("%d: DTLS[%d]: Couldn't find cipherspec from epoch %d",
+ SSL_GETPID(), ss->fd, epoch));
+ return NULL;
+}
+
+/* if cText is non-null, then decipher and check the MAC of the
* SSL record from cText->buf (typically gs->inbuf)
* into databuf (typically gs->buf), and any previous contents of databuf
* is lost. Then handle databuf according to its SSL record type,
@@ -12475,8 +12092,8 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
* checked, and is already sitting in databuf. It is processed as an SSL
* Handshake message.
*
- * DOES NOT process the decrypted/decompressed application data.
- * On return, databuf contains the decrypted/decompressed record.
+ * DOES NOT process the decrypted application data.
+ * On return, databuf contains the decrypted record.
*
* Called from ssl3_GatherCompleteHandshake
* ssl3_RestartHandshakeAfterCertReq
@@ -12492,20 +12109,15 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
{
SECStatus rv;
PRBool isTLS;
- sslSequenceNumber seq_num = 0;
- ssl3CipherSpec *crSpec;
+ DTLSEpoch epoch;
+ sslSequenceNumber seqNum = 0;
+ ssl3CipherSpec *spec = NULL;
+ PRBool outOfOrderSpec = PR_FALSE;
SSL3ContentType rType;
sslBuffer *plaintext;
- sslBuffer temp_buf = { NULL, 0, 0 };
SSL3AlertDescription alert = internal_error;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
- if (!ss->ssl3.initialized) {
- ssl_GetSSL3HandshakeLock(ss);
- ssl3_InitState(ss);
- ssl_ReleaseSSL3HandshakeLock(ss);
- }
-
/* check for Token Presence */
if (!ssl3_ClientAuthTokenPresent(ss->sec.ci.sid)) {
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
@@ -12519,41 +12131,48 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
if (cText == NULL) {
SSL_DBG(("%d: SSL3[%d]: HandleRecord, resuming handshake",
SSL_GETPID(), ss->fd));
- rType = content_handshake;
- goto process_it;
+ /* Note that this doesn't pass the epoch and sequence number of the
+ * record through, which DTLS 1.3 depends on. DTLS doesn't support
+ * asynchronous certificate validation, so that should be OK. */
+ PORT_Assert(!IS_DTLS(ss));
+ return ssl3_HandleNonApplicationData(ss, content_handshake,
+ 0, 0, databuf);
}
ssl_GetSpecReadLock(ss); /******************************************/
- crSpec = ss->ssl3.crSpec;
- isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
-
+ spec = ssl3_GetCipherSpec(ss, cText->seq_num);
+ if (!spec) {
+ PORT_Assert(IS_DTLS(ss));
+ ssl_ReleaseSpecReadLock(ss); /*****************************/
+ databuf->len = 0; /* Needed to ensure data not left around */
+ return SECSuccess;
+ }
+ if (spec != ss->ssl3.crSpec) {
+ PORT_Assert(IS_DTLS(ss));
+ SSL_TRC(3, ("%d: DTLS[%d]: Handling out-of-epoch record from epoch=%d",
+ SSL_GETPID(), ss->fd, spec->epoch));
+ outOfOrderSpec = PR_TRUE;
+ }
+ isTLS = (PRBool)(spec->version > SSL_LIBRARY_VERSION_3_0);
if (IS_DTLS(ss)) {
- PRBool sameEpoch;
- if (!dtls_IsRelevant(ss, cText, &sameEpoch, &seq_num)) {
+ if (!dtls_IsRelevant(ss, spec, cText, &seqNum)) {
ssl_ReleaseSpecReadLock(ss); /*****************************/
databuf->len = 0; /* Needed to ensure data not left around */
- /* Maybe retransmit if needed. */
- return dtls_MaybeRetransmitHandshake(ss, cText, sameEpoch);
+ return SECSuccess;
}
} else {
- seq_num = crSpec->read_seq_num + 1;
+ seqNum = spec->seqNum + 1;
}
- if (seq_num >= crSpec->cipher_def->max_records) {
+ if (seqNum >= spec->cipherDef->max_records) {
ssl_ReleaseSpecReadLock(ss); /*****************************/
SSL_TRC(3, ("%d: SSL[%d]: read sequence number at limit 0x%0llx",
- SSL_GETPID(), ss->fd, seq_num));
+ SSL_GETPID(), ss->fd, seqNum));
PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
return SECFailure;
}
- /* If we will be decompressing the buffer we need to decrypt somewhere
- * other than into databuf */
- if (crSpec->decompressor) {
- plaintext = &temp_buf;
- } else {
- plaintext = databuf;
- }
+ plaintext = databuf;
plaintext->len = 0; /* filled in by Unprotect call below. */
/* We're waiting for another ClientHello, which will appear unencrypted.
@@ -12588,12 +12207,12 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
/* IMPORTANT: Unprotect functions MUST NOT send alerts
* because we still hold the spec read lock. Instead, if they
* return SECFailure, they set *alert to the alert to be sent. */
- if (crSpec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
- crSpec->cipher_def->calg == ssl_calg_null) {
+ if (spec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ spec->cipherDef->calg == ssl_calg_null) {
/* Unencrypted TLS 1.3 records use the pre-TLS 1.3 format. */
- rv = ssl3_UnprotectRecord(ss, cText, plaintext, &alert);
+ rv = ssl3_UnprotectRecord(ss, spec, cText, plaintext, &alert);
} else {
- rv = tls13_UnprotectRecord(ss, cText, plaintext, &alert);
+ rv = tls13_UnprotectRecord(ss, spec, cText, plaintext, &alert);
}
#endif
@@ -12602,14 +12221,25 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
SSL_DBG(("%d: SSL3[%d]: decryption failed", SSL_GETPID(), ss->fd));
- /* Clear the temp buffer used for decompression upon failure. */
- sslBuffer_Clear(&temp_buf);
+ /* Ensure that we don't process this data again. */
+ databuf->len = 0;
+ /* Ignore a CCS if the alternative handshake is negotiated. Note that
+ * this will fail if the server fails to negotiate the alternative
+ * handshake type in a 0-RTT session that is resumed from a session that
+ * did negotiate it. We don't care about that corner case right now. */
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+ cText->type == content_change_cipher_spec &&
+ ss->ssl3.hs.ws != idle_handshake &&
+ cText->buf->len == 1 &&
+ cText->buf->buf[0] == change_cipher_spec_choice) {
+ /* Ignore the CCS. */
+ return SECSuccess;
+ }
if (IS_DTLS(ss) ||
(ss->sec.isServer &&
ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_trial)) {
/* Silently drop the packet */
- databuf->len = 0; /* Needed to ensure data not left around */
return SECSuccess;
} else {
int errCode = PORT_GetError();
@@ -12622,10 +12252,11 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
}
/* SECSuccess */
- crSpec->read_seq_num = seq_num;
+ spec->seqNum = PR_MAX(spec->seqNum, seqNum);
if (IS_DTLS(ss)) {
- dtls_RecordSetRecvd(&crSpec->recvdRecords, seq_num);
+ dtls_RecordSetRecvd(&spec->recvdRecords, seqNum);
}
+ epoch = spec->epoch;
ssl_ReleaseSpecReadLock(ss); /*****************************************/
@@ -12635,70 +12266,16 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
rType = cText->type; /* This must go after decryption because TLS 1.3
* has encrypted content types. */
- /* possibly decompress the record. If we aren't using compression then
- * plaintext == databuf and so the uncompressed data is already in
- * databuf. */
- if (crSpec->decompressor) {
- if (databuf->space < plaintext->len + SSL3_COMPRESSION_MAX_EXPANSION) {
- rv = sslBuffer_Grow(
- databuf, plaintext->len + SSL3_COMPRESSION_MAX_EXPANSION);
- if (rv != SECSuccess) {
- SSL_DBG(("%d: SSL3[%d]: HandleRecord, tried to get %d bytes",
- SSL_GETPID(), ss->fd,
- plaintext->len +
- SSL3_COMPRESSION_MAX_EXPANSION));
- /* sslBuffer_Grow has set a memory error code. */
- /* Perhaps we should send an alert. (but we have no memory!) */
- sslBuffer_Clear(&temp_buf);
- return SECFailure;
- }
- }
-
- rv = crSpec->decompressor(crSpec->decompressContext,
- databuf->buf,
- (int *)&databuf->len,
- databuf->space,
- plaintext->buf,
- plaintext->len);
-
- if (rv != SECSuccess) {
- int err = ssl_MapLowLevelError(SSL_ERROR_DECOMPRESSION_FAILURE);
- SSL3_SendAlert(ss, alert_fatal,
- isTLS ? decompression_failure
- : bad_record_mac);
-
- /* There appears to be a bug with (at least) Apache + OpenSSL where
- * resumed SSLv3 connections don't actually use compression. See
- * comments 93-95 of
- * https://bugzilla.mozilla.org/show_bug.cgi?id=275744
- *
- * So, if we get a decompression error, and the record appears to
- * be already uncompressed, then we return a more specific error
- * code to hopefully save somebody some debugging time in the
- * future.
- */
- if (plaintext->len >= 4) {
- unsigned int len = ((unsigned int)plaintext->buf[1] << 16) |
- ((unsigned int)plaintext->buf[2] << 8) |
- (unsigned int)plaintext->buf[3];
- if (len == plaintext->len - 4) {
- /* This appears to be uncompressed already */
- err = SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD;
- }
- }
-
- sslBuffer_Clear(&temp_buf);
- PORT_SetError(err);
- return SECFailure;
- }
-
- sslBuffer_Clear(&temp_buf);
+ /* IMPORTANT: We are in DTLS 1.3 mode and we have processed something
+ * from the wrong epoch. Divert to a divert processing function to make
+ * sure we don't accidentally use the data unsafely. */
+ if (outOfOrderSpec) {
+ PORT_Assert(IS_DTLS(ss) && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ return dtls13_HandleOutOfEpochRecord(ss, spec, rType, databuf);
}
- /*
- ** Having completed the decompression, check the length again.
- */
- if (isTLS && databuf->len > (MAX_FRAGMENT_LENGTH + 1024)) {
+ /* Check the length of the plaintext. */
+ if (isTLS && databuf->len > MAX_FRAGMENT_LENGTH) {
SSL3_SendAlert(ss, alert_fatal, record_overflow);
PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
return SECFailure;
@@ -12720,45 +12297,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
return SECFailure;
}
-/* It's a record that must be handled by ssl itself, not the application.
- */
-process_it:
- /* XXX Get the xmit lock here. Odds are very high that we'll be xmiting
- * data ang getting the xmit lock here prevents deadlocks.
- */
- ssl_GetSSL3HandshakeLock(ss);
-
- /* All the functions called in this switch MUST set error code if
- ** they return SECFailure or SECWouldBlock.
- */
- switch (rType) {
- case content_change_cipher_spec:
- rv = ssl3_HandleChangeCipherSpecs(ss, databuf);
- break;
- case content_alert:
- rv = ssl3_HandleAlert(ss, databuf);
- break;
- case content_handshake:
- if (!IS_DTLS(ss)) {
- rv = ssl3_HandleHandshake(ss, databuf);
- } else {
- rv = dtls_HandleHandshake(ss, databuf);
- }
- break;
- /*
- case content_application_data is handled before this switch
- */
- default:
- SSL_DBG(("%d: SSL3[%d]: bogus content type=%d",
- SSL_GETPID(), ss->fd, cText->type));
- PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
- ssl3_DecodeError(ss);
- rv = SECFailure;
- break;
- }
-
- ssl_ReleaseSSL3HandshakeLock(ss);
- return rv;
+ return ssl3_HandleNonApplicationData(ss, rType, epoch, seqNum, databuf);
}
/*
@@ -12776,83 +12315,36 @@ ssl_InitSecState(sslSecurityInfo *sec)
sec->keaGroup = NULL;
}
-/* Called from ssl3_InitState, immediately below. */
-/* Caller must hold the SpecWriteLock. */
-void
-ssl3_InitCipherSpec(ssl3CipherSpec *spec)
-{
- spec->cipher_def = &bulk_cipher_defs[cipher_null];
- PORT_Assert(spec->cipher_def->cipher == cipher_null);
- spec->mac_def = &mac_defs[mac_null];
- PORT_Assert(spec->mac_def->mac == mac_null);
- spec->encode = Null_Cipher;
- spec->decode = Null_Cipher;
- spec->compressor = NULL;
- spec->decompressor = NULL;
- spec->destroyCompressContext = NULL;
- spec->destroyDecompressContext = NULL;
- spec->mac_size = 0;
- spec->master_secret = NULL;
-
- spec->msItem.data = NULL;
- spec->msItem.len = 0;
-
- spec->client.write_key = NULL;
- spec->client.write_mac_key = NULL;
- spec->client.write_mac_context = NULL;
-
- spec->server.write_key = NULL;
- spec->server.write_mac_key = NULL;
- spec->server.write_mac_context = NULL;
-
- spec->write_seq_num = 0;
- spec->read_seq_num = 0;
- spec->epoch = 0;
-
- spec->refCt = 128; /* Arbitrarily high number to prevent
- * non-TLS 1.3 cipherSpecs from being
- * GCed. This will be overwritten with
- * a valid refCt for TLS 1.3. */
- dtls_InitRecvdRecords(&spec->recvdRecords);
-}
-
-/* Called from: ssl3_SendRecord
-** ssl3_SendClientHello()
-** ssl3_HandleV2ClientHello()
-** ssl3_HandleRecord()
-**
-** This function should perhaps acquire and release the SpecWriteLock.
-*/
-void
+SECStatus
ssl3_InitState(sslSocket *ss)
{
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
- if (ss->ssl3.initialized)
- return; /* Function should be idempotent */
+ SECStatus rv;
ss->ssl3.policy = SSL_ALLOWED;
ssl_InitSecState(&ss->sec);
ssl_GetSpecWriteLock(ss);
- ss->ssl3.crSpec = ss->ssl3.cwSpec = &ss->ssl3.specs[0];
- ss->ssl3.prSpec = ss->ssl3.pwSpec = &ss->ssl3.specs[1];
- ssl3_InitCipherSpec(ss->ssl3.crSpec);
- ssl3_InitCipherSpec(ss->ssl3.prSpec);
- ss->ssl3.crSpec->version = ss->ssl3.prSpec->version = ss->vrange.max;
+ PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
+ rv = ssl_SetupNullCipherSpec(ss, CipherSpecRead);
+ rv |= ssl_SetupNullCipherSpec(ss, CipherSpecWrite);
+ ss->ssl3.pwSpec = ss->ssl3.prSpec = NULL;
ssl_ReleaseSpecWriteLock(ss);
+ if (rv != SECSuccess) {
+ /* Rely on ssl_CreateNullCipherSpec() to set error code. */
+ return SECFailure;
+ }
ss->ssl3.hs.sendingSCSV = PR_FALSE;
ss->ssl3.hs.preliminaryInfo = 0;
- ss->ssl3.hs.ws = (ss->sec.isServer) ? wait_client_hello : wait_server_hello;
+ ss->ssl3.hs.ws = (ss->sec.isServer) ? wait_client_hello : idle_handshake;
- ssl3_ResetExtensionData(&ss->xtnData);
+ ssl3_ResetExtensionData(&ss->xtnData, ss);
PR_INIT_CLIST(&ss->ssl3.hs.remoteExtensions);
if (IS_DTLS(ss)) {
ss->ssl3.hs.sendMessageSeq = 0;
ss->ssl3.hs.recvMessageSeq = 0;
- ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
+ ss->ssl3.hs.rtTimer->timeout = DTLS_RETRANSMIT_INITIAL_MS;
ss->ssl3.hs.rtRetries = 0;
ss->ssl3.hs.recvdHighWater = -1;
PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
@@ -12868,8 +12360,6 @@ ssl3_InitState(sslSocket *ss)
ss->ssl3.hs.serverHsTrafficSecret = NULL;
ss->ssl3.hs.clientTrafficSecret = NULL;
ss->ssl3.hs.serverTrafficSecret = NULL;
- ss->ssl3.hs.certificateRequest = NULL;
- PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space);
ss->ssl3.hs.messages.buf = NULL;
@@ -12881,9 +12371,7 @@ ssl3_InitState(sslSocket *ss)
ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
- ssl_FilterSupportedGroups(ss);
-
- ss->ssl3.initialized = PR_TRUE;
+ return SECSuccess;
}
/* record the export policy for this cipher suite */
@@ -13137,8 +12625,7 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache)
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- if (!ss->firstHsDone ||
- (ss->ssl3.initialized && (ss->ssl3.hs.ws != idle_handshake))) {
+ if (!ss->firstHsDone || (ss->ssl3.hs.ws != idle_handshake)) {
PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
return SECFailure;
}
@@ -13152,6 +12639,11 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache)
PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
return SECFailure;
}
+ if (ss->version > ss->vrange.max || ss->version < ss->vrange.min) {
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
+ return SECFailure;
+ }
+
if (sid && flushCache) {
ss->sec.uncache(sid); /* remove it from whichever cache it's in. */
ssl_FreeSID(sid); /* dec ref count and free if zero. */
@@ -13209,15 +12701,7 @@ ssl3_DestroySSL3Info(sslSocket *ss)
SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
SECITEM_FreeItem(&ss->ssl3.hs.srvVirtName, PR_FALSE);
-
- if (ss->ssl3.hs.certificateRequest) {
- PORT_FreeArena(ss->ssl3.hs.certificateRequest->arena, PR_FALSE);
- ss->ssl3.hs.certificateRequest = NULL;
- }
-
- /* free up the CipherSpecs */
- ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE /*freeSrvName*/);
- ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE /*freeSrvName*/);
+ SECITEM_FreeItem(&ss->ssl3.hs.fakeSid, PR_FALSE);
/* Destroy the DTLS data */
if (IS_DTLS(ss)) {
@@ -13229,10 +12713,10 @@ ssl3_DestroySSL3Info(sslSocket *ss)
/* Destroy remote extensions */
ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
- ssl3_ResetExtensionData(&ss->xtnData);
+ ssl3_DestroyExtensionData(&ss->xtnData);
- /* Destroy TLS 1.3 cipher specs */
- tls13_DestroyCipherSpecs(&ss->ssl3.hs.cipherSpecs);
+ /* Destroy cipher specs */
+ ssl_DestroyCipherSpecs(&ss->ssl3.hs.cipherSpecs);
/* Destroy TLS 1.3 keys */
if (ss->ssl3.hs.currentSecret)
@@ -13261,8 +12745,6 @@ ssl3_DestroySSL3Info(sslSocket *ss)
ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
/* Destroy TLS 1.3 buffered early data. */
tls13_DestroyEarlyData(&ss->ssl3.hs.bufferedEarlyData);
-
- ss->ssl3.initialized = PR_FALSE;
}
#define MAP_NULL(x) (((x) != 0) ? (x) : SEC_OID_NULL_CIPHER)
@@ -13301,7 +12783,7 @@ ssl3_ApplyNSSPolicy(void)
}
if (ssl_GetBulkCipherDef(suite)->type != type_aead) {
- policyOid = MAP_NULL(mac_defs[suite->mac_alg].oid);
+ policyOid = MAP_NULL(ssl_GetMacDefByAlg(suite->mac_alg)->oid);
rv = NSS_GetAlgorithmPolicy(policyOid, &policy);
if (rv == SECSuccess && !(policy & NSS_USE_ALG_IN_SSL)) {
ssl_CipherPrefSetDefault(suite->cipher_suite, PR_FALSE);
diff --git a/security/nss/lib/ssl/ssl3ecc.c b/security/nss/lib/ssl/ssl3ecc.c
index b440b4b02..913a14f63 100644
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -111,7 +111,7 @@ ssl_ECPubKey2NamedGroup(const SECKEYPublicKey *pubKey)
static SECStatus
ssl3_ComputeECDHKeyHash(SSLHashType hashAlg,
SECItem ec_params, SECItem server_ecpoint,
- SSL3Random *client_rand, SSL3Random *server_rand,
+ PRUint8 *client_rand, PRUint8 *server_rand,
SSL3Hashes *hashes)
{
PRUint8 *hashBuf;
@@ -175,8 +175,8 @@ ssl3_SendECDHClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
- isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
+ isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
+ isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2);
/* Generate ephemeral EC keypair */
if (svrPubKey->keyType != ecKey) {
@@ -219,7 +219,7 @@ ssl3_SendECDHClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
goto loser;
}
- rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange,
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_client_key_exchange,
pubKey->u.ec.publicValue.len + 1);
if (rv != SECSuccess) {
goto loser; /* err set by ssl3_AppendHandshake* */
@@ -232,7 +232,7 @@ ssl3_SendECDHClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey)
goto loser; /* err set by ssl3_AppendHandshake* */
}
- rv = ssl3_InitPendingCipherSpec(ss, pms);
+ rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE);
goto loser;
@@ -250,19 +250,6 @@ loser:
return SECFailure;
}
-/* This function encodes the key_exchange field in
- * the KeyShareEntry structure. */
-SECStatus
-tls13_EncodeECDHEKeyShareKEX(const sslSocket *ss, const SECKEYPublicKey *pubKey)
-{
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- PORT_Assert(pubKey->keyType == ecKey);
-
- return ssl3_ExtAppendHandshake(ss, pubKey->u.ec.publicValue.data,
- pubKey->u.ec.publicValue.len);
-}
-
/*
** Called from ssl3_HandleClientKeyExchange()
*/
@@ -326,7 +313,7 @@ ssl3_HandleECDHClientKeyExchange(sslSocket *ss, PRUint8 *b,
return SECFailure;
}
- rv = ssl3_InitPendingCipherSpec(ss, pms);
+ rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE);
PK11_FreeSymKey(pms);
if (rv != SECSuccess) {
/* error code set by ssl3_InitPendingCipherSpec */
@@ -597,8 +584,8 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length)
* check to make sure the hash is signed by right guy
*/
rv = ssl3_ComputeECDHKeyHash(hashAlg, ec_params, ec_point,
- &ss->ssl3.hs.client_random,
- &ss->ssl3.hs.server_random,
+ ss->ssl3.hs.client_random,
+ ss->ssl3.hs.server_random,
&hashes);
if (rv != SECSuccess) {
@@ -703,7 +690,7 @@ ssl3_SendECDHServerKeyExchange(sslSocket *ss)
ec_params.data[2] = keyPair->group->name & 0xff;
pubKey = keyPair->keys->pubKey;
- if (ss->ssl3.pwSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
+ if (ss->version == SSL_LIBRARY_VERSION_TLS_1_2) {
hashAlg = ssl_SignatureSchemeToHashType(ss->ssl3.hs.signatureScheme);
} else {
/* Use ssl_hash_none to represent the MD5+SHA1 combo. */
@@ -711,15 +698,15 @@ ssl3_SendECDHServerKeyExchange(sslSocket *ss)
}
rv = ssl3_ComputeECDHKeyHash(hashAlg, ec_params,
pubKey->u.ec.publicValue,
- &ss->ssl3.hs.client_random,
- &ss->ssl3.hs.server_random,
+ ss->ssl3.hs.client_random,
+ ss->ssl3.hs.server_random,
&hashes);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
goto loser;
}
- isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
+ isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2);
rv = ssl3_SignHashes(ss, &hashes,
ss->sec.serverCert->serverKeyPair->privKey, &signed_hash);
@@ -731,7 +718,7 @@ ssl3_SendECDHServerKeyExchange(sslSocket *ss)
1 + pubKey->u.ec.publicValue.len +
(isTLS12 ? 2 : 0) + 2 + signed_hash.len;
- rv = ssl3_AppendHandshakeHeader(ss, server_key_exchange, length);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_key_exchange, length);
if (rv != SECSuccess) {
goto loser; /* err set by AppendHandshake. */
}
@@ -870,20 +857,16 @@ ssl_IsDHEEnabled(const sslSocket *ss)
}
/* Send our Supported Groups extension. */
-PRInt32
-ssl_SendSupportedGroupsXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes)
+SECStatus
+ssl_SendSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
- unsigned char enabledGroups[64];
- unsigned int enabledGroupsLen = 0;
unsigned int i;
PRBool ec;
PRBool ff = PR_FALSE;
-
- if (!ss)
- return 0;
+ PRBool found = PR_FALSE;
+ SECStatus rv;
+ unsigned int lengthOffset;
/* We only send FF supported groups if we require DH named groups
* or if TLS 1.3 is a possibility. */
@@ -892,13 +875,19 @@ ssl_SendSupportedGroupsXtn(const sslSocket *ss,
if (ss->opt.requireDHENamedGroups) {
ff = ssl_IsDHEEnabled(ss);
}
- if (!ec && !ff)
- return 0;
+ if (!ec && !ff) {
+ return SECSuccess;
+ }
} else {
ec = ff = PR_TRUE;
}
- PORT_Assert(sizeof(enabledGroups) > SSL_NAMED_GROUP_COUNT * 2);
+ /* Mark the location of the length. */
+ rv = sslBuffer_Skip(buf, 2, &lengthOffset);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
const sslNamedGroupDef *group = ss->namedGroupPreferences[i];
if (!group) {
@@ -911,78 +900,53 @@ ssl_SendSupportedGroupsXtn(const sslSocket *ss,
continue;
}
- if (append) {
- (void)ssl_EncodeUintX(group->name, 2, &enabledGroups[enabledGroupsLen]);
- }
- enabledGroupsLen += 2;
- }
-
- if (enabledGroupsLen == 0) {
- return 0;
- }
-
- extension_length =
- 2 /* extension type */ +
- 2 /* extension length */ +
- 2 /* enabled groups length */ +
- enabledGroupsLen;
-
- if (maxBytes < (PRUint32)extension_length) {
- return 0;
- }
-
- if (append) {
- SECStatus rv;
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_supported_groups_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess)
- return -1;
- rv = ssl3_ExtAppendHandshakeVariable(ss, enabledGroups,
- enabledGroupsLen, 2);
- if (rv != SECSuccess)
- return -1;
- if (!ss->sec.isServer) {
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_supported_groups_xtn;
+ found = PR_TRUE;
+ rv = sslBuffer_AppendNumber(buf, group->name, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
}
- return extension_length;
+
+ if (!found) {
+ /* We added nothing, don't send the extension. */
+ return SECSuccess;
+ }
+
+ rv = sslBuffer_InsertLength(buf, lengthOffset, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
}
/* Send our "canned" (precompiled) Supported Point Formats extension,
* which says that we only support uncompressed points.
*/
-PRInt32
-ssl3_SendSupportedPointFormatsXtn(
- const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_SendSupportedPointFormatsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- static const PRUint8 ecPtFmt[6] = {
- 0, 11, /* Extension type */
- 0, 2, /* octets that follow */
- 1, /* octets that follow */
- 0 /* uncompressed type only */
- };
+ SECStatus rv;
/* No point in doing this unless we have a socket that supports ECC.
* Similarly, no point if we are going to do TLS 1.3 only or we have already
* picked TLS 1.3 (server) given that it doesn't use point formats. */
if (!ss || !ssl_IsECCEnabled(ss) ||
ss->vrange.min >= SSL_LIBRARY_VERSION_TLS_1_3 ||
- (ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3))
- return 0;
- if (append && maxBytes >= (sizeof ecPtFmt)) {
- SECStatus rv = ssl3_ExtAppendHandshake(ss, ecPtFmt, (sizeof ecPtFmt));
- if (rv != SECSuccess)
- return -1;
- if (!ss->sec.isServer) {
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_ec_point_formats_xtn;
- }
+ (ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3)) {
+ return SECSuccess;
}
- return sizeof(ecPtFmt);
+ rv = sslBuffer_AppendNumber(buf, 1, 1); /* length */
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(buf, 0, 1); /* uncompressed type only */
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
}
diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c
index 271084cf7..ade280903 100644
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -14,8 +14,20 @@
#include "sslimpl.h"
#include "sslproto.h"
#include "ssl3exthandle.h"
+#include "tls13err.h"
#include "tls13exthandle.h"
+/* Callback function that handles a received extension. */
+typedef SECStatus (*ssl3ExtensionHandlerFunc)(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+
+/* Row in a table of hello extension handlers. */
+typedef struct {
+ SSLExtensionType ex_type;
+ ssl3ExtensionHandlerFunc ex_handler;
+} ssl3ExtensionHandler;
+
/* Table of handlers for received TLS hello extensions, one per extension.
* In the second generation, this table will be dynamic, and functions
* will be registered here.
@@ -31,16 +43,15 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = {
{ ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_ServerHandleUseSRTPXtn },
{ ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn },
- { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
+ { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn },
{ ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
{ ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
{ ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
{ ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
{ ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
- { ssl_tls13_psk_key_exchange_modes_xtn,
- &tls13_ServerHandlePskKeyExchangeModesXtn },
- { ssl_tls13_short_header_xtn, &tls13_HandleShortHeaderXtn },
- { -1, NULL }
+ { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
+ { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
+ { 0, NULL }
};
/* These two tables are used by the client, to handle server hello
@@ -59,36 +70,38 @@ static const ssl3ExtensionHandler serverHelloHandlersTLS[] = {
{ ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn },
{ ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn },
{ ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn },
- { ssl_tls13_short_header_xtn, &tls13_HandleShortHeaderXtn },
- { -1, NULL }
+ { 0, NULL }
};
static const ssl3ExtensionHandler helloRetryRequestHandlers[] = {
{ ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr },
{ ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie },
- { -1, NULL }
+ { 0, NULL }
};
static const ssl3ExtensionHandler serverHelloHandlersSSL3[] = {
{ ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
- { -1, NULL }
+ { 0, NULL }
};
static const ssl3ExtensionHandler newSessionTicketHandlers[] = {
- { ssl_tls13_ticket_early_data_info_xtn,
- &tls13_ClientHandleTicketEarlyDataInfoXtn },
- { -1, NULL }
+ { ssl_tls13_early_data_xtn,
+ &tls13_ClientHandleTicketEarlyDataXtn },
+ { 0, NULL }
};
/* This table is used by the client to handle server certificates in TLS 1.3 */
static const ssl3ExtensionHandler serverCertificateHandlers[] = {
{ ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn },
{ ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
- { -1, NULL }
+ { 0, NULL }
};
static const ssl3ExtensionHandler certificateRequestHandlers[] = {
- { -1, NULL }
+ { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn },
+ { ssl_tls13_certificate_authorities_xtn,
+ &tls13_ClientHandleCertAuthoritiesXtn },
+ { 0, NULL }
};
/* Tables of functions to format TLS hello extensions, one function per
@@ -101,14 +114,14 @@ static const ssl3ExtensionHandler certificateRequestHandlers[] = {
* the client hello is empty (for example, the extended master secret
* extension, if it were listed last). See bug 1243641.
*/
-static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] =
+static const sslExtensionBuilder clientHelloSendersTLS[] =
{
- { ssl_server_name_xtn, &ssl3_SendServerNameXtn },
+ { ssl_server_name_xtn, &ssl3_ClientSendServerNameXtn },
{ ssl_extended_master_secret_xtn, &ssl3_SendExtendedMasterSecretXtn },
{ ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
{ ssl_supported_groups_xtn, &ssl_SendSupportedGroupsXtn },
{ ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
- { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
+ { ssl_session_ticket_xtn, &ssl3_ClientSendSessionTicketXtn },
{ ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
{ ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_ClientSendUseSRTPXtn },
@@ -121,22 +134,155 @@ static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS]
* client hello is empty. They are not intolerant of TLS 1.2, so list
* signature_algorithms at the end. See bug 1243641. */
{ ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn },
- { ssl_tls13_short_header_xtn, &tls13_SendShortHeaderXtn },
- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+ { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn },
{ ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn },
- { ssl_tls13_psk_key_exchange_modes_xtn,
- &tls13_ClientSendPskKeyExchangeModesXtn },
- { ssl_padding_xtn, &ssl3_ClientSendPaddingExtension },
+ { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn },
/* The pre_shared_key extension MUST be last. */
{ ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
- /* any extra entries will appear as { 0, NULL } */
+ { 0, NULL }
};
-static const ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
- { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
- /* any extra entries will appear as { 0, NULL } */
+static const sslExtensionBuilder clientHelloSendersSSL3[] = {
+ { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
+ { 0, NULL }
+};
+
+static const sslExtensionBuilder tls13_cert_req_senders[] = {
+ { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn },
+ { ssl_tls13_certificate_authorities_xtn, &tls13_SendCertAuthoritiesXtn },
+ { 0, NULL }
+};
+
+static const sslExtensionBuilder tls13_hrr_senders[] = {
+ { ssl_tls13_key_share_xtn, &tls13_ServerSendHrrKeyShareXtn },
+ { ssl_tls13_cookie_xtn, &tls13_ServerSendHrrCookieXtn },
+ { ssl_tls13_supported_versions_xtn, &tls13_ServerSendSupportedVersionsXtn },
+ { 0, NULL }
+};
+
+static const struct {
+ SSLExtensionType type;
+ SSLExtensionSupport support;
+} ssl_supported_extensions[] = {
+ { ssl_server_name_xtn, ssl_ext_native_only },
+ { ssl_cert_status_xtn, ssl_ext_native },
+ { ssl_supported_groups_xtn, ssl_ext_native_only },
+ { ssl_ec_point_formats_xtn, ssl_ext_native },
+ { ssl_signature_algorithms_xtn, ssl_ext_native_only },
+ { ssl_use_srtp_xtn, ssl_ext_native },
+ { ssl_app_layer_protocol_xtn, ssl_ext_native_only },
+ { ssl_signed_cert_timestamp_xtn, ssl_ext_native },
+ { ssl_padding_xtn, ssl_ext_native },
+ { ssl_extended_master_secret_xtn, ssl_ext_native_only },
+ { ssl_session_ticket_xtn, ssl_ext_native_only },
+ { ssl_tls13_key_share_xtn, ssl_ext_native_only },
+ { ssl_tls13_pre_shared_key_xtn, ssl_ext_native_only },
+ { ssl_tls13_early_data_xtn, ssl_ext_native_only },
+ { ssl_tls13_supported_versions_xtn, ssl_ext_native_only },
+ { ssl_tls13_cookie_xtn, ssl_ext_native_only },
+ { ssl_tls13_psk_key_exchange_modes_xtn, ssl_ext_native_only },
+ { ssl_tls13_ticket_early_data_info_xtn, ssl_ext_native_only },
+ { ssl_tls13_certificate_authorities_xtn, ssl_ext_native },
+ { ssl_next_proto_nego_xtn, ssl_ext_none },
+ { ssl_renegotiation_info_xtn, ssl_ext_native }
};
+static SSLExtensionSupport
+ssl_GetExtensionSupport(PRUint16 type)
+{
+ unsigned int i;
+ for (i = 0; i < PR_ARRAY_SIZE(ssl_supported_extensions); ++i) {
+ if (type == ssl_supported_extensions[i].type) {
+ return ssl_supported_extensions[i].support;
+ }
+ }
+ return ssl_ext_none;
+}
+
+SECStatus
+SSLExp_GetExtensionSupport(PRUint16 type, SSLExtensionSupport *support)
+{
+ *support = ssl_GetExtensionSupport(type);
+ return SECSuccess;
+}
+
+SECStatus
+SSLExp_InstallExtensionHooks(PRFileDesc *fd, PRUint16 extension,
+ SSLExtensionWriter writer, void *writerArg,
+ SSLExtensionHandler handler, void *handlerArg)
+{
+ sslSocket *ss = ssl_FindSocket(fd);
+ PRCList *cursor;
+ sslCustomExtensionHooks *hook;
+
+ if (!ss) {
+ return SECFailure; /* Code already set. */
+ }
+
+ /* Need to specify both or neither, but not just one. */
+ if ((writer && !handler) || (!writer && handler)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (ssl_GetExtensionSupport(extension) == ssl_ext_native_only) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (ss->firstHsDone || ((ss->ssl3.hs.ws != idle_handshake) &&
+ (ss->ssl3.hs.ws != wait_client_hello))) {
+ PORT_SetError(PR_INVALID_STATE_ERROR);
+ return SECFailure;
+ }
+
+ /* Remove any old handler. */
+ for (cursor = PR_NEXT_LINK(&ss->extensionHooks);
+ cursor != &ss->extensionHooks;
+ cursor = PR_NEXT_LINK(cursor)) {
+ hook = (sslCustomExtensionHooks *)cursor;
+ if (hook->type == extension) {
+ PR_REMOVE_LINK(&hook->link);
+ PORT_Free(hook);
+ break;
+ }
+ }
+
+ if (!writer && !handler) {
+ return SECSuccess;
+ }
+
+ hook = PORT_ZNew(sslCustomExtensionHooks);
+ if (!hook) {
+ return SECFailure; /* This removed the old one, oh well. */
+ }
+
+ hook->type = extension;
+ hook->writer = writer;
+ hook->writerArg = writerArg;
+ hook->handler = handler;
+ hook->handlerArg = handlerArg;
+ PR_APPEND_LINK(&hook->link, &ss->extensionHooks);
+ return SECSuccess;
+}
+
+static sslCustomExtensionHooks *
+ssl_FindCustomExtensionHooks(sslSocket *ss, PRUint16 extension)
+{
+ PRCList *cursor;
+
+ for (cursor = PR_NEXT_LINK(&ss->extensionHooks);
+ cursor != &ss->extensionHooks;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslCustomExtensionHooks *hook = (sslCustomExtensionHooks *)cursor;
+ if (hook->type == extension) {
+ return hook;
+ }
+ }
+
+ return NULL;
+}
+
static PRBool
arrayContainsExtension(const PRUint16 *array, PRUint32 len, PRUint16 ex_type)
{
@@ -156,8 +302,11 @@ ssl3_ExtensionNegotiated(const sslSocket *ss, PRUint16 ex_type)
xtnData->numNegotiated, ex_type);
}
+/* This checks for whether an extension was advertised. On the client, this
+ * covers extensions that are sent in ClientHello; on the server, extensions
+ * sent in CertificateRequest (TLS 1.3 only). */
PRBool
-ssl3_ClientExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type)
+ssl3_ExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type)
{
const TLSExtensionData *xtnData = &ss->xtnData;
return arrayContainsExtension(xtnData->advertised,
@@ -240,6 +389,44 @@ ssl3_FindExtension(sslSocket *ss, SSLExtensionType extension_type)
return NULL;
}
+static SECStatus
+ssl_CallExtensionHandler(sslSocket *ss, SSLHandshakeType handshakeMessage,
+ TLSExtension *extension,
+ const ssl3ExtensionHandler *handler)
+{
+ SECStatus rv = SECSuccess;
+ SSLAlertDescription alert = handshake_failure;
+ sslCustomExtensionHooks *customHooks;
+
+ customHooks = ssl_FindCustomExtensionHooks(ss, extension->type);
+ if (customHooks) {
+ if (customHooks->handler) {
+ rv = customHooks->handler(ss->fd, handshakeMessage,
+ extension->data.data,
+ extension->data.len,
+ &alert, customHooks->handlerArg);
+ }
+ } else {
+ /* Find extension_type in table of Hello Extension Handlers. */
+ for (; handler->ex_handler != NULL; ++handler) {
+ if (handler->ex_type == extension->type) {
+ rv = (*handler->ex_handler)(ss, &ss->xtnData, &extension->data);
+ break;
+ }
+ }
+ }
+
+ if (rv != SECSuccess) {
+ if (!ss->ssl3.fatalAlertSent) {
+ /* Send an alert if the handler didn't already. */
+ (void)SSL3_SendAlert(ss, alert_fatal, alert);
+ }
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
/* Go through the hello extensions in |ss->ssl3.hs.remoteExtensions|.
* For each one, find the extension handler in the table, and
* if present, invoke that handler.
@@ -250,42 +437,46 @@ ssl3_FindExtension(sslSocket *ss, SSLExtensionType extension_type)
* right phase.
*/
SECStatus
-ssl3_HandleParsedExtensions(sslSocket *ss,
- SSL3HandshakeType handshakeMessage)
+ssl3_HandleParsedExtensions(sslSocket *ss, SSLHandshakeType message)
{
const ssl3ExtensionHandler *handlers;
/* HelloRetryRequest doesn't set ss->version. It might be safe to
* do so, but we weren't entirely sure. TODO(ekr@rtfm.com). */
PRBool isTLS13 = (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) ||
- (handshakeMessage == hello_retry_request);
+ (message == ssl_hs_hello_retry_request);
+ /* The following messages can include extensions that were not included in
+ * the original ClientHello. */
+ PRBool allowNotOffered = (message == ssl_hs_client_hello) ||
+ (message == ssl_hs_certificate_request) ||
+ (message == ssl_hs_new_session_ticket);
PRCList *cursor;
- switch (handshakeMessage) {
- case client_hello:
+ switch (message) {
+ case ssl_hs_client_hello:
handlers = clientHelloHandlers;
break;
- case new_session_ticket:
+ case ssl_hs_new_session_ticket:
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
handlers = newSessionTicketHandlers;
break;
- case hello_retry_request:
+ case ssl_hs_hello_retry_request:
handlers = helloRetryRequestHandlers;
break;
- case encrypted_extensions:
+ case ssl_hs_encrypted_extensions:
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
/* fall through */
- case server_hello:
+ case ssl_hs_server_hello:
if (ss->version > SSL_LIBRARY_VERSION_3_0) {
handlers = serverHelloHandlersTLS;
} else {
handlers = serverHelloHandlersSSL3;
}
break;
- case certificate:
+ case ssl_hs_certificate:
PORT_Assert(!ss->sec.isServer);
handlers = serverCertificateHandlers;
break;
- case certificate_request:
+ case ssl_hs_certificate_request:
PORT_Assert(!ss->sec.isServer);
handlers = certificateRequestHandlers;
break;
@@ -299,28 +490,39 @@ ssl3_HandleParsedExtensions(sslSocket *ss,
cursor != &ss->ssl3.hs.remoteExtensions;
cursor = PR_NEXT_LINK(cursor)) {
TLSExtension *extension = (TLSExtension *)cursor;
- const ssl3ExtensionHandler *handler;
+ SECStatus rv;
/* Check whether the server sent an extension which was not advertised
- * in the ClientHello */
- if (!ss->sec.isServer &&
- !ssl3_ClientExtensionAdvertised(ss, extension->type) &&
- (handshakeMessage != new_session_ticket) &&
- (extension->type != ssl_tls13_cookie_xtn)) {
+ * in the ClientHello.
+ *
+ * Note that a TLS 1.3 server should check if CertificateRequest
+ * extensions were sent. But the extensions used for CertificateRequest
+ * do not have any response, so we rely on
+ * ssl3_ExtensionAdvertised to return false on the server. That
+ * results in the server only rejecting any extension. */
+ if (!allowNotOffered && (extension->type != ssl_tls13_cookie_xtn) &&
+ !ssl3_ExtensionAdvertised(ss, extension->type)) {
(void)SSL3_SendAlert(ss, alert_fatal, unsupported_extension);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
return SECFailure;
}
/* Check that this is a legal extension in TLS 1.3 */
- if (isTLS13 && !tls13_ExtensionAllowed(extension->type, handshakeMessage)) {
- if (handshakeMessage == client_hello) {
- /* Skip extensions not used in TLS 1.3 */
- continue;
+ if (isTLS13 &&
+ !ssl_FindCustomExtensionHooks(ss, extension->type)) {
+ switch (tls13_ExtensionStatus(extension->type, message)) {
+ case tls13_extension_allowed:
+ break;
+ case tls13_extension_unknown:
+ if (allowNotOffered) {
+ continue; /* Skip over unknown extensions. */
+ }
+ /* Fall through. */
+ case tls13_extension_disallowed:
+ tls13_FatalError(ss, SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION,
+ unsupported_extension);
+ return SECFailure;
}
- tls13_FatalError(ss, SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION,
- unsupported_extension);
- return SECFailure;
}
/* Special check for this being the last extension if it's
@@ -334,23 +536,9 @@ ssl3_HandleParsedExtensions(sslSocket *ss,
return SECFailure;
}
- /* find extension_type in table of Hello Extension Handlers */
- for (handler = handlers; handler->ex_type >= 0; handler++) {
- /* if found, call this handler */
- if (handler->ex_type == extension->type) {
- SECStatus rv;
-
- rv = (*handler->ex_handler)(ss, &ss->xtnData,
- (PRUint16)extension->type,
- &extension->data);
- if (rv != SECSuccess) {
- if (!ss->ssl3.fatalAlertSent) {
- /* send a generic alert if the handler didn't already */
- (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
- }
- return SECFailure;
- }
- }
+ rv = ssl_CallExtensionHandler(ss, message, extension, handlers);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
}
return SECSuccess;
@@ -361,7 +549,7 @@ ssl3_HandleParsedExtensions(sslSocket *ss,
SECStatus
ssl3_HandleExtensions(sslSocket *ss,
PRUint8 **b, PRUint32 *length,
- SSL3HandshakeType handshakeMessage)
+ SSLHandshakeType handshakeMessage)
{
SECStatus rv;
@@ -383,21 +571,30 @@ SECStatus
ssl3_RegisterExtensionSender(const sslSocket *ss,
TLSExtensionData *xtnData,
PRUint16 ex_type,
- ssl3HelloExtensionSenderFunc cb)
+ sslExtensionBuilderFunc cb)
{
int i;
- ssl3HelloExtensionSender *sender;
+ sslExtensionBuilder *sender;
if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
sender = &xtnData->serverHelloSenders[0];
} else {
- if (tls13_ExtensionAllowed(ex_type, server_hello)) {
- PORT_Assert(!tls13_ExtensionAllowed(ex_type, encrypted_extensions));
+ if (tls13_ExtensionStatus(ex_type, ssl_hs_server_hello) ==
+ tls13_extension_allowed) {
+ PORT_Assert(tls13_ExtensionStatus(ex_type,
+ ssl_hs_encrypted_extensions) ==
+ tls13_extension_disallowed);
sender = &xtnData->serverHelloSenders[0];
- } else if (tls13_ExtensionAllowed(ex_type, certificate)) {
+ } else if (tls13_ExtensionStatus(ex_type,
+ ssl_hs_encrypted_extensions) ==
+ tls13_extension_allowed) {
+ sender = &xtnData->encryptedExtensionsSenders[0];
+ } else if (tls13_ExtensionStatus(ex_type, ssl_hs_certificate) ==
+ tls13_extension_allowed) {
sender = &xtnData->certificateSenders[0];
} else {
- PORT_Assert(tls13_ExtensionAllowed(ex_type, encrypted_extensions));
- sender = &xtnData->encryptedExtensionsSenders[0];
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
}
for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) {
@@ -418,32 +615,289 @@ ssl3_RegisterExtensionSender(const sslSocket *ss,
return SECFailure;
}
-/* call each of the extension senders and return the accumulated length */
-PRInt32
-ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes,
- const ssl3HelloExtensionSender *sender)
+static SECStatus
+ssl_CallCustomExtensionSenders(sslSocket *ss, sslBuffer *buf,
+ SSLHandshakeType message)
{
- PRInt32 total_exten_len = 0;
- int i;
+ sslBuffer tail = SSL_BUFFER_EMPTY;
+ SECStatus rv;
+ PRCList *cursor;
- if (!sender) {
- if (ss->vrange.max > SSL_LIBRARY_VERSION_3_0) {
- sender = &clientHelloSendersTLS[0];
- } else {
- sender = &clientHelloSendersSSL3[0];
+ /* Save any extensions that want to be last. */
+ if (ss->xtnData.lastXtnOffset) {
+ rv = sslBuffer_Append(&tail, buf->buf + ss->xtnData.lastXtnOffset,
+ buf->len - ss->xtnData.lastXtnOffset);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
+ buf->len = ss->xtnData.lastXtnOffset;
}
- for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) {
- if (sender->ex_sender) {
- PRInt32 extLen = (*sender->ex_sender)(ss, &ss->xtnData, append, maxBytes);
- if (extLen < 0)
- return -1;
- maxBytes -= extLen;
- total_exten_len += extLen;
+ /* Reserve the maximum amount of space possible. */
+ rv = sslBuffer_Grow(buf, 65535);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ for (cursor = PR_NEXT_LINK(&ss->extensionHooks);
+ cursor != &ss->extensionHooks;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslCustomExtensionHooks *hook =
+ (sslCustomExtensionHooks *)cursor;
+ PRBool append = PR_FALSE;
+ unsigned int len = 0;
+
+ if (hook->writer) {
+ /* The writer writes directly into |buf|. Provide space that allows
+ * for the existing extensions, any tail, plus type and length. */
+ unsigned int space = buf->space - (buf->len + tail.len + 4);
+ append = (*hook->writer)(ss->fd, message,
+ buf->buf + buf->len + 4, &len, space,
+ hook->writerArg);
+ if (len > space) {
+ PORT_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR);
+ goto loser;
+ }
+ }
+ if (!append) {
+ continue;
+ }
+
+ rv = sslBuffer_AppendNumber(buf, hook->type, 2);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+ rv = sslBuffer_AppendNumber(buf, len, 2);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+ buf->len += len;
+
+ if (message == ssl_hs_client_hello ||
+ message == ssl_hs_certificate_request) {
+ ss->xtnData.advertised[ss->xtnData.numAdvertised++] = hook->type;
}
}
- return total_exten_len;
+
+ sslBuffer_Append(buf, tail.buf, tail.len);
+ sslBuffer_Clear(&tail);
+ return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&tail);
+ return SECFailure;
+}
+
+/* Call extension handlers for the given message. */
+SECStatus
+ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf, SSLHandshakeType message)
+{
+ const sslExtensionBuilder *sender;
+ SECStatus rv;
+
+ PORT_Assert(buf->len == 0);
+
+ switch (message) {
+ case ssl_hs_client_hello:
+ if (ss->vrange.max > SSL_LIBRARY_VERSION_3_0) {
+ sender = clientHelloSendersTLS;
+ } else {
+ sender = clientHelloSendersSSL3;
+ }
+ break;
+
+ case ssl_hs_server_hello:
+ sender = ss->xtnData.serverHelloSenders;
+ break;
+
+ case ssl_hs_certificate_request:
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ sender = tls13_cert_req_senders;
+ break;
+
+ case ssl_hs_certificate:
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ sender = ss->xtnData.certificateSenders;
+ break;
+
+ case ssl_hs_encrypted_extensions:
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ sender = ss->xtnData.encryptedExtensionsSenders;
+ break;
+
+ case ssl_hs_hello_retry_request:
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ sender = tls13_hrr_senders;
+ break;
+
+ default:
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ for (; sender->ex_sender != NULL; ++sender) {
+ PRBool append = PR_FALSE;
+ unsigned int start = buf->len;
+ unsigned int length;
+
+ if (ssl_FindCustomExtensionHooks(ss, sender->ex_type)) {
+ continue;
+ }
+
+ /* Save space for the extension type and length. Note that we don't grow
+ * the buffer now; rely on sslBuffer_Append* to do that. */
+ buf->len += 4;
+ rv = (*sender->ex_sender)(ss, &ss->xtnData, buf, &append);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Save the length and go back to the start. */
+ length = buf->len - start - 4;
+ buf->len = start;
+ if (!append) {
+ continue;
+ }
+
+ buf->len = start;
+ rv = sslBuffer_AppendNumber(buf, sender->ex_type, 2);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+ rv = sslBuffer_AppendNumber(buf, length, 2);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+ /* Skip over the extension body. */
+ buf->len += length;
+
+ if (message == ssl_hs_client_hello ||
+ message == ssl_hs_certificate_request) {
+ ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+ sender->ex_type;
+ }
+ }
+
+ if (!PR_CLIST_IS_EMPTY(&ss->extensionHooks)) {
+ rv = ssl_CallCustomExtensionSenders(ss, buf, message);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (buf->len > 0xffff) {
+ PORT_SetError(SSL_ERROR_TX_RECORD_TOO_LONG);
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ sslBuffer_Clear(buf);
+ return SECFailure;
+}
+
+/* This extension sender can be used anywhere that an always empty extension is
+ * needed. Mostly that is for ServerHello where sender registration is dynamic;
+ * ClientHello senders are usually conditional in some way. */
+SECStatus
+ssl_SendEmptyExtension(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append)
+{
+ *append = PR_TRUE;
+ return SECSuccess;
+}
+
+/* Takes the size of the ClientHello, less the record header, and determines how
+ * much padding is required. */
+static unsigned int
+ssl_CalculatePaddingExtLen(const sslSocket *ss, unsigned int clientHelloLength)
+{
+ unsigned int recordLength = 1 /* handshake message type */ +
+ 3 /* handshake message length */ +
+ clientHelloLength;
+ unsigned int extensionLen;
+
+ /* Don't pad for DTLS, for SSLv3, or for renegotiation. */
+ if (IS_DTLS(ss) ||
+ ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_0 ||
+ ss->firstHsDone) {
+ return 0;
+ }
+
+ /* A padding extension may be included to ensure that the record containing
+ * the ClientHello doesn't have a length between 256 and 511 bytes
+ * (inclusive). Initial ClientHello records with such lengths trigger bugs
+ * in F5 devices. */
+ if (recordLength < 256 || recordLength >= 512) {
+ return 0;
+ }
+
+ extensionLen = 512 - recordLength;
+ /* Extensions take at least four bytes to encode. Always include at least
+ * one byte of data if we are padding. Some servers will time out or
+ * terminate the connection if the last ClientHello extension is empty. */
+ if (extensionLen < 5) {
+ extensionLen = 5;
+ }
+
+ return extensionLen - 4;
+}
+
+/* ssl3_SendPaddingExtension possibly adds an extension which ensures that a
+ * ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures
+ * that we don't trigger bugs in F5 products.
+ *
+ * This takes an existing extension buffer, |buf|, and the length of the
+ * remainder of the ClientHello, |prefixLen|. It modifies the extension buffer
+ * to insert padding at the right place.
+ */
+SECStatus
+ssl_InsertPaddingExtension(const sslSocket *ss, unsigned int prefixLen,
+ sslBuffer *buf)
+{
+ static unsigned char padding[252] = { 0 };
+ unsigned int paddingLen;
+ unsigned int tailLen;
+ SECStatus rv;
+
+ /* Account for the size of the header, the length field of the extensions
+ * block and the size of the existing extensions. */
+ paddingLen = ssl_CalculatePaddingExtLen(ss, prefixLen + 2 + buf->len);
+ if (!paddingLen) {
+ return SECSuccess;
+ }
+
+ /* Move the tail if there is one. This only happens if we are sending the
+ * TLS 1.3 PSK extension, which needs to be at the end. */
+ if (ss->xtnData.lastXtnOffset) {
+ PORT_Assert(buf->len > ss->xtnData.lastXtnOffset);
+ tailLen = buf->len - ss->xtnData.lastXtnOffset;
+ rv = sslBuffer_Grow(buf, buf->len + 4 + paddingLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ PORT_Memmove(buf->buf + ss->xtnData.lastXtnOffset + 4 + paddingLen,
+ buf->buf + ss->xtnData.lastXtnOffset,
+ tailLen);
+ buf->len = ss->xtnData.lastXtnOffset;
+ } else {
+ tailLen = 0;
+ }
+
+ rv = sslBuffer_AppendNumber(buf, ssl_padding_xtn, 2);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Code already set. */
+ }
+ rv = sslBuffer_AppendVariable(buf, padding, paddingLen, 2);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Code already set. */
+ }
+
+ buf->len += tailLen;
+
+ return SECSuccess;
}
void
@@ -460,52 +914,59 @@ ssl3_DestroyRemoteExtensions(PRCList *list)
/* Initialize the extension data block. */
void
-ssl3_InitExtensionData(TLSExtensionData *xtnData)
+ssl3_InitExtensionData(TLSExtensionData *xtnData, const sslSocket *ss)
{
+ unsigned int advertisedMax;
+ PRCList *cursor;
+
/* Set things up to the right starting state. */
PORT_Memset(xtnData, 0, sizeof(*xtnData));
xtnData->peerSupportsFfdheGroups = PR_FALSE;
PR_INIT_CLIST(&xtnData->remoteKeyShares);
+
+ /* Allocate enough to allow for native extensions, plus any custom ones. */
+ if (ss->sec.isServer) {
+ advertisedMax = PR_MAX(PR_ARRAY_SIZE(certificateRequestHandlers),
+ PR_ARRAY_SIZE(tls13_cert_req_senders));
+ } else {
+ advertisedMax = PR_MAX(PR_ARRAY_SIZE(clientHelloHandlers),
+ PR_ARRAY_SIZE(clientHelloSendersTLS));
+ ++advertisedMax; /* For the RI SCSV, which we also track. */
+ }
+ for (cursor = PR_NEXT_LINK(&ss->extensionHooks);
+ cursor != &ss->extensionHooks;
+ cursor = PR_NEXT_LINK(cursor)) {
+ ++advertisedMax;
+ }
+ xtnData->advertised = PORT_ZNewArray(PRUint16, advertisedMax);
}
-/* Free everything that has been allocated and then reset back to
- * the starting state. */
void
-ssl3_ResetExtensionData(TLSExtensionData *xtnData)
+ssl3_DestroyExtensionData(TLSExtensionData *xtnData)
{
- /* Clean up. */
ssl3_FreeSniNameArray(xtnData);
- PORT_Free(xtnData->clientSigSchemes);
+ PORT_Free(xtnData->sigSchemes);
SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);
tls13_DestroyKeyShares(&xtnData->remoteKeyShares);
-
- /* Now reinit. */
- ssl3_InitExtensionData(xtnData);
-}
-
-/* Thunks to let extension handlers operate on const sslSocket* objects. */
-SECStatus
-ssl3_ExtAppendHandshake(const sslSocket *ss, const void *void_src,
- PRInt32 bytes)
-{
- return ssl3_AppendHandshake((sslSocket *)ss, void_src, bytes);
-}
-
-SECStatus
-ssl3_ExtAppendHandshakeNumber(const sslSocket *ss, PRInt32 num,
- PRInt32 lenSize)
-{
- return ssl3_AppendHandshakeNumber((sslSocket *)ss, num, lenSize);
+ SECITEM_FreeItem(&xtnData->certReqContext, PR_FALSE);
+ SECITEM_FreeItem(&xtnData->applicationToken, PR_FALSE);
+ if (xtnData->certReqAuthorities.arena) {
+ PORT_FreeArena(xtnData->certReqAuthorities.arena, PR_FALSE);
+ xtnData->certReqAuthorities.arena = NULL;
+ }
+ PORT_Free(xtnData->advertised);
}
-SECStatus
-ssl3_ExtAppendHandshakeVariable(const sslSocket *ss,
- const PRUint8 *src, PRInt32 bytes,
- PRInt32 lenSize)
+/* Free everything that has been allocated and then reset back to
+ * the starting state. */
+void
+ssl3_ResetExtensionData(TLSExtensionData *xtnData, const sslSocket *ss)
{
- return ssl3_AppendHandshakeVariable((sslSocket *)ss, src, bytes, lenSize);
+ ssl3_DestroyExtensionData(xtnData);
+ ssl3_InitExtensionData(xtnData, ss);
}
+/* Thunks to let extension handlers operate on const sslSocket* objects. */
void
ssl3_ExtSendAlert(const sslSocket *ss, SSL3AlertLevel level,
SSL3AlertDescription desc)
diff --git a/security/nss/lib/ssl/ssl3ext.h b/security/nss/lib/ssl/ssl3ext.h
index 90407375a..d0f75a599 100644
--- a/security/nss/lib/ssl/ssl3ext.h
+++ b/security/nss/lib/ssl/ssl3ext.h
@@ -9,54 +9,38 @@
#ifndef __ssl3ext_h_
#define __ssl3ext_h_
+#include "sslencode.h"
+
typedef enum {
sni_nametype_hostname
} SNINameType;
typedef struct TLSExtensionDataStr TLSExtensionData;
-/* registerable callback function that either appends extension to buffer
+/* Registerable callback function that either appends extension to buffer
* or returns length of data that it would have appended.
*/
-typedef PRInt32 (*ssl3HelloExtensionSenderFunc)(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-
-/* registerable callback function that handles a received extension,
- * of the given type.
- */
-typedef SECStatus (*ssl3ExtensionHandlerFunc)(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRUint16 ex_type,
- SECItem *data);
+typedef SECStatus (*sslExtensionBuilderFunc)(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
/* row in a table of hello extension senders */
typedef struct {
PRInt32 ex_type;
- ssl3HelloExtensionSenderFunc ex_sender;
-} ssl3HelloExtensionSender;
-
-/* row in a table of hello extension handlers */
-typedef struct {
- PRInt32 ex_type;
- ssl3ExtensionHandlerFunc ex_handler;
-} ssl3ExtensionHandler;
+ sslExtensionBuilderFunc ex_sender;
+} sslExtensionBuilder;
struct TLSExtensionDataStr {
/* registered callbacks that send server hello extensions */
- ssl3HelloExtensionSender serverHelloSenders[SSL_MAX_EXTENSIONS];
- ssl3HelloExtensionSender encryptedExtensionsSenders[SSL_MAX_EXTENSIONS];
- ssl3HelloExtensionSender certificateSenders[SSL_MAX_EXTENSIONS];
+ sslExtensionBuilder serverHelloSenders[SSL_MAX_EXTENSIONS];
+ sslExtensionBuilder encryptedExtensionsSenders[SSL_MAX_EXTENSIONS];
+ sslExtensionBuilder certificateSenders[SSL_MAX_EXTENSIONS];
- /* Keep track of the extensions that are negotiated. */
+ /* Keep track of the extensions that are advertised or negotiated. */
PRUint16 numAdvertised;
+ PRUint16 *advertised; /* Allocated dynamically. */
PRUint16 numNegotiated;
- PRUint16 advertised[SSL_MAX_EXTENSIONS];
PRUint16 negotiated[SSL_MAX_EXTENSIONS];
- /* Amount of padding we need to add. */
- PRUint16 paddingLen;
-
/* SessionTicket Extension related data. */
PRBool ticketTimestampVerified;
PRBool emptySessionTicket;
@@ -86,10 +70,13 @@ struct TLSExtensionDataStr {
PRBool peerSupportsFfdheGroups; /* if the peer supports named ffdhe groups */
/* clientSigAndHash contains the contents of the signature_algorithms
- * extension (if any) from the client. This is only valid for TLS 1.2
- * or later. */
- SSLSignatureScheme *clientSigSchemes;
- unsigned int numClientSigScheme;
+ * extension (if any) the other side supports. This is only valid for TLS
+ * 1.2 or later. In TLS 1.3, it is also used for CertificateRequest. */
+ SSLSignatureScheme *sigSchemes;
+ unsigned int numSigSchemes;
+
+ SECItem certReqContext;
+ CERTDistNames certReqAuthorities;
/* In a client: if the server supports Next Protocol Negotiation, then
* this is the protocol that was negotiated.
@@ -99,9 +86,18 @@ struct TLSExtensionDataStr {
PRUint16 dtlsSRTPCipherSuite; /* 0 if not selected */
- SECItem pskBinder; /* The PSK binder for the first PSK (TLS 1.3) */
- unsigned long pskBinderPrefixLen; /* The length of the binder input. */
- PRCList remoteKeyShares; /* The other side's public keys (TLS 1.3) */
+ unsigned int lastXtnOffset; /* Where to insert padding. 0 = end. */
+ PRCList remoteKeyShares; /* The other side's public keys (TLS 1.3) */
+
+ /* The following are used by a TLS 1.3 server. */
+ SECItem pskBinder; /* The binder for the first PSK. */
+ unsigned int pskBindersLen; /* The length of the binders. */
+ PRUint32 ticketAge; /* Used to accept early data. */
+ SECItem cookie; /* HRR Cookie. */
+ const sslNamedGroupDef *selectedGroup; /* For HRR. */
+ /* The application token contains a value that was passed to the client via
+ * a session ticket, or the cookie in a HelloRetryRequest. */
+ SECItem applicationToken;
};
typedef struct TLSExtensionStr {
@@ -110,40 +106,44 @@ typedef struct TLSExtensionStr {
SECItem data; /* Pointers into the handshake data. */
} TLSExtension;
+typedef struct sslCustomExtensionHooks {
+ PRCList link;
+ PRUint16 type;
+ SSLExtensionWriter writer;
+ void *writerArg;
+ SSLExtensionHandler handler;
+ void *handlerArg;
+} sslCustomExtensionHooks;
+
SECStatus ssl3_HandleExtensions(sslSocket *ss,
PRUint8 **b, PRUint32 *length,
- SSL3HandshakeType handshakeMessage);
+ SSLHandshakeType handshakeMessage);
SECStatus ssl3_ParseExtensions(sslSocket *ss,
PRUint8 **b, PRUint32 *length);
SECStatus ssl3_HandleParsedExtensions(sslSocket *ss,
- SSL3HandshakeType handshakeMessage);
+ SSLHandshakeType handshakeMessage);
TLSExtension *ssl3_FindExtension(sslSocket *ss,
SSLExtensionType extension_type);
void ssl3_DestroyRemoteExtensions(PRCList *list);
-void ssl3_InitExtensionData(TLSExtensionData *xtnData);
-void ssl3_ResetExtensionData(TLSExtensionData *xtnData);
+void ssl3_InitExtensionData(TLSExtensionData *xtnData, const sslSocket *ss);
+void ssl3_DestroyExtensionData(TLSExtensionData *xtnData);
+void ssl3_ResetExtensionData(TLSExtensionData *xtnData, const sslSocket *ss);
PRBool ssl3_ExtensionNegotiated(const sslSocket *ss, PRUint16 ex_type);
-PRBool ssl3_ClientExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type);
+PRBool ssl3_ExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type);
SECStatus ssl3_RegisterExtensionSender(const sslSocket *ss,
TLSExtensionData *xtnData,
PRUint16 ex_type,
- ssl3HelloExtensionSenderFunc cb);
-PRInt32 ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes,
- const ssl3HelloExtensionSender *sender);
-
-void ssl3_CalculatePaddingExtLen(sslSocket *ss,
- unsigned int clientHelloLength);
+ sslExtensionBuilderFunc cb);
+SECStatus ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf,
+ SSLHandshakeType message);
+SECStatus ssl_SendEmptyExtension(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus ssl_InsertPaddingExtension(const sslSocket *ss, unsigned int prefixLen,
+ sslBuffer *buf);
/* Thunks to let us operate on const sslSocket* objects. */
-SECStatus ssl3_ExtAppendHandshake(const sslSocket *ss, const void *void_src,
- PRInt32 bytes);
-SECStatus ssl3_ExtAppendHandshakeNumber(const sslSocket *ss, PRInt32 num,
- PRInt32 lenSize);
-SECStatus ssl3_ExtAppendHandshakeVariable(const sslSocket *ss,
- const PRUint8 *src, PRInt32 bytes,
- PRInt32 lenSize);
void ssl3_ExtSendAlert(const sslSocket *ss, SSL3AlertLevel level,
SSL3AlertDescription desc);
void ssl3_ExtDecodeError(const sslSocket *ss);
@@ -156,4 +156,10 @@ SECStatus ssl3_ExtConsumeHandshakeVariable(const sslSocket *ss, SECItem *i,
PRUint32 bytes, PRUint8 **b,
PRUint32 *length);
+SECStatus SSLExp_GetExtensionSupport(PRUint16 type,
+ SSLExtensionSupport *support);
+SECStatus SSLExp_InstallExtensionHooks(
+ PRFileDesc *fd, PRUint16 extension, SSLExtensionWriter writer,
+ void *writerArg, SSLExtensionHandler handler, void *handlerArg);
+
#endif
diff --git a/security/nss/lib/ssl/ssl3exthandle.c b/security/nss/lib/ssl/ssl3exthandle.c
index 370bd8b3e..c0fbda7ab 100644
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -13,7 +13,6 @@
#include "blapit.h"
#include "prinit.h"
#include "selfencrypt.h"
-#include "ssl3encode.h"
#include "ssl3ext.h"
#include "ssl3exthandle.h"
#include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */
@@ -22,70 +21,48 @@
* unless that name is a dotted decimal string.
* Used by client and server.
*/
-PRInt32
-ssl3_SendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
+ unsigned int len;
+ PRNetAddr netAddr;
SECStatus rv;
- if (!ss)
- return 0;
- if (!ss->sec.isServer) {
- PRUint32 len;
- PRNetAddr netAddr;
-
- /* must have a hostname */
- if (!ss->url || !ss->url[0])
- return 0;
- /* must not be an IPv4 or IPv6 address */
- if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) {
- /* is an IP address (v4 or v6) */
- return 0;
- }
- len = PORT_Strlen(ss->url);
- if (append && maxBytes >= len + 9) {
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_server_name_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* length of extension_data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, len + 5, 2);
- if (rv != SECSuccess)
- return -1;
- /* length of server_name_list */
- rv = ssl3_ExtAppendHandshakeNumber(ss, len + 3, 2);
- if (rv != SECSuccess)
- return -1;
- /* Name Type (sni_host_name) */
- rv = ssl3_ExtAppendHandshake(ss, "\0", 1);
- if (rv != SECSuccess)
- return -1;
- /* HostName (length and value) */
- rv = ssl3_ExtAppendHandshakeVariable(ss, (PRUint8 *)ss->url, len, 2);
- if (rv != SECSuccess)
- return -1;
- if (!ss->sec.isServer) {
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_server_name_xtn;
- }
- }
- return len + 9;
+
+ /* must have a hostname */
+ if (!ss->url || !ss->url[0]) {
+ return SECSuccess;
}
- /* Server side */
- if (append && maxBytes >= 4) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_server_name_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* length of extension_data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
+ /* must not be an IPv4 or IPv6 address */
+ if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) {
+ /* is an IP address (v4 or v6) */
+ return SECSuccess;
+ }
+ len = PORT_Strlen(ss->url);
+ /* length of server_name_list */
+ rv = sslBuffer_AppendNumber(buf, len + 3, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* Name Type (sni_host_name) */
+ rv = sslBuffer_AppendNumber(buf, 0, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* HostName (length and value) */
+ rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)ss->url, len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return 4;
+
+ *added = PR_TRUE;
+ return SECSuccess;
}
/* Handle an incoming SNI extension. */
SECStatus
-ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECItem *names = NULL;
PRUint32 listLenBytes = 0;
@@ -194,88 +171,54 @@ ssl3_FreeSniNameArray(TLSExtensionData *xtnData)
* sends an empty ticket. Servers always send empty tickets.
*/
PRInt32
-ssl3_SendSessionTicketXtn(
- const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+ssl3_ClientSendSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
NewSessionTicket *session_ticket = NULL;
sslSessionID *sid = ss->sec.ci.sid;
+ SECStatus rv;
+
+ PORT_Assert(!ss->sec.isServer);
/* Never send an extension with a ticket for TLS 1.3, but
* OK to send the empty one in case the server does 1.2. */
if (sid->cached == in_client_cache &&
sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- return 0;
+ return SECSuccess;
}
/* Ignore the SessionTicket extension if processing is disabled. */
- if (!ss->opt.enableSessionTickets)
- return 0;
-
- /* Empty extension length = extension_type (2-bytes) +
- * length(extension_data) (2-bytes)
- */
- extension_length = 4;
+ if (!ss->opt.enableSessionTickets) {
+ return SECSuccess;
+ }
- /* If we are a client then send a session ticket if one is availble.
- * Servers that support the extension and are willing to negotiate the
- * the extension always respond with an empty extension.
+ /* Send a session ticket if one is available.
+ *
+ * The caller must be holding sid->u.ssl3.lock for reading. We cannot
+ * just acquire and release the lock within this function because the
+ * caller will call this function twice, and we need the inputs to be
+ * consistent between the two calls. Note that currently the caller
+ * will only be holding the lock when we are the client and when we're
+ * attempting to resume an existing session.
*/
- if (!ss->sec.isServer) {
- /* The caller must be holding sid->u.ssl3.lock for reading. We cannot
- * just acquire and release the lock within this function because the
- * caller will call this function twice, and we need the inputs to be
- * consistent between the two calls. Note that currently the caller
- * will only be holding the lock when we are the client and when we're
- * attempting to resume an existing session.
- */
+ session_ticket = &sid->u.ssl3.locked.sessionTicket;
+ if (session_ticket->ticket.data &&
+ (xtnData->ticketTimestampVerified ||
+ ssl_TicketTimeValid(session_ticket))) {
- session_ticket = &sid->u.ssl3.locked.sessionTicket;
- if (session_ticket->ticket.data) {
- if (xtnData->ticketTimestampVerified) {
- extension_length += session_ticket->ticket.len;
- } else if (!append && ssl_TicketTimeValid(session_ticket)) {
- extension_length += session_ticket->ticket.len;
- xtnData->ticketTimestampVerified = PR_TRUE;
- }
- }
- }
+ xtnData->ticketTimestampVerified = PR_FALSE;
- if (maxBytes < (PRUint32)extension_length) {
- PORT_Assert(0);
- return 0;
- }
- if (append) {
- SECStatus rv;
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_session_ticket_xtn, 2);
- if (rv != SECSuccess)
- goto loser;
- if (session_ticket && session_ticket->ticket.data &&
- xtnData->ticketTimestampVerified) {
- rv = ssl3_ExtAppendHandshakeVariable(ss, session_ticket->ticket.data,
- session_ticket->ticket.len, 2);
- xtnData->ticketTimestampVerified = PR_FALSE;
- xtnData->sentSessionTicketInClientHello = PR_TRUE;
- } else {
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
+ rv = sslBuffer_Append(buf, session_ticket->ticket.data,
+ session_ticket->ticket.len);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (rv != SECSuccess)
- goto loser;
- if (!ss->sec.isServer) {
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_session_ticket_xtn;
- }
+ xtnData->sentSessionTicketInClientHello = PR_TRUE;
}
- return extension_length;
-loser:
- xtnData->ticketTimestampVerified = PR_FALSE;
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
PRBool
@@ -301,16 +244,18 @@ ssl_AlpnTagAllowed(const sslSocket *ss, const SECItem *tag)
/* handle an incoming Next Protocol Negotiation extension. */
SECStatus
-ssl3_ServerHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ServerHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+
if (ss->firstHsDone || data->len != 0) {
/* Clients MUST send an empty NPN extension, if any. */
PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
return SECFailure;
}
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_next_proto_nego_xtn;
/* TODO: server side NPN support would require calling
* ssl3_RegisterServerHelloExtensionSender here in order to echo the
@@ -344,7 +289,7 @@ ssl3_ValidateNextProtoNego(const unsigned char *data, unsigned int length)
/* protocol selection handler for ALPN (server side) and NPN (client side) */
static SECStatus
ssl3_SelectAppProtocol(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data)
+ PRUint16 extension, SECItem *data)
{
SECStatus rv;
unsigned char resultBuffer[255];
@@ -381,7 +326,7 @@ ssl3_SelectAppProtocol(const sslSocket *ss, TLSExtensionData *xtnData,
SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);
- if (ex_type == ssl_app_layer_protocol_xtn &&
+ if (extension == ssl_app_layer_protocol_xtn &&
xtnData->nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) {
/* The callback might say OK, but then it picks a default value - one
* that was not listed. That's OK for NPN, but not ALPN. */
@@ -390,13 +335,14 @@ ssl3_SelectAppProtocol(const sslSocket *ss, TLSExtensionData *xtnData,
return SECFailure;
}
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = extension;
return SECITEM_CopyItem(NULL, &xtnData->nextProto, &result);
}
/* handle an incoming ALPN extension at the server */
SECStatus
-ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
PRUint32 count;
SECStatus rv;
@@ -423,15 +369,16 @@ ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRU
return SECSuccess;
}
- rv = ssl3_SelectAppProtocol(ss, xtnData, ex_type, data);
+ rv = ssl3_SelectAppProtocol(ss, xtnData, ssl_app_layer_protocol_xtn, data);
if (rv != SECSuccess) {
return rv;
}
/* prepare to send back a response, if we negotiated */
if (xtnData->nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) {
- rv = ssl3_RegisterExtensionSender(
- ss, xtnData, ex_type, ssl3_ServerSendAppProtoXtn);
+ rv = ssl3_RegisterExtensionSender(ss, xtnData,
+ ssl_app_layer_protocol_xtn,
+ ssl3_ServerSendAppProtoXtn);
if (rv != SECSuccess) {
ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -442,9 +389,10 @@ ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRU
}
SECStatus
-ssl3_ClientHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ClientHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
PORT_Assert(!ss->firstHsDone);
if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) {
@@ -470,11 +418,12 @@ ssl3_ClientHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData
return SECFailure;
}
- return ssl3_SelectAppProtocol(ss, xtnData, ex_type, data);
+ return ssl3_SelectAppProtocol(ss, xtnData, ssl_next_proto_nego_xtn, data);
}
SECStatus
-ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
PRUint32 list_len;
@@ -521,265 +470,168 @@ ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRU
SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);
xtnData->nextProtoState = SSL_NEXT_PROTO_SELECTED;
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_app_layer_protocol_xtn;
return SECITEM_CopyItem(NULL, &xtnData->nextProto, &protocol_name);
}
-PRInt32
-ssl3_ClientSendNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_ClientSendNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
-
/* Renegotiations do not send this extension. */
if (!ss->opt.enableNPN || !ss->nextProtoCallback || ss->firstHsDone) {
- return 0;
- }
-
- extension_length = 4;
-
- if (maxBytes < (PRUint32)extension_length) {
- return 0;
- }
- if (append) {
- SECStatus rv;
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_next_proto_nego_xtn, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- goto loser;
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_next_proto_nego_xtn;
+ return SECSuccess;
}
- return extension_length;
-
-loser:
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
-PRInt32
-ssl3_ClientSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+SECStatus
+ssl3_ClientSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
- unsigned char *alpn_protos = NULL;
+ SECStatus rv;
+ const unsigned int len = ss->opt.nextProtoNego.len;
/* Renegotiations do not send this extension. */
if (!ss->opt.enableALPN || !ss->opt.nextProtoNego.data || ss->firstHsDone) {
- return 0;
+ return SECSuccess;
}
- extension_length = 2 /* extension type */ + 2 /* extension length */ +
- 2 /* protocol name list length */ +
- ss->opt.nextProtoNego.len;
+ /* NPN requires that the client's fallback protocol is first in the
+ * list. However, ALPN sends protocols in preference order. So move the
+ * first protocol to the end of the list. */
- if (maxBytes < (PRUint32)extension_length) {
- return 0;
- }
- if (append) {
- /* NPN requires that the client's fallback protocol is first in the
- * list. However, ALPN sends protocols in preference order. So we
- * allocate a buffer and move the first protocol to the end of the
- * list. */
- SECStatus rv;
- const unsigned int len = ss->opt.nextProtoNego.len;
+ if (len > 0) {
+ /* Each protocol string is prefixed with a single byte length. */
+ unsigned int i;
- alpn_protos = PORT_Alloc(len);
- if (alpn_protos == NULL) {
+ rv = sslBuffer_AppendNumber(buf, len, 2);
+ if (rv != SECSuccess) {
return SECFailure;
}
- if (len > 0) {
- /* Each protocol string is prefixed with a single byte length. */
- unsigned int i = ss->opt.nextProtoNego.data[0] + 1;
- if (i <= len) {
- memcpy(alpn_protos, &ss->opt.nextProtoNego.data[i], len - i);
- memcpy(alpn_protos + len - i, ss->opt.nextProtoNego.data, i);
- } else {
- /* This seems to be invalid data so we'll send as-is. */
- memcpy(alpn_protos, ss->opt.nextProtoNego.data, len);
- }
- }
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2);
- if (rv != SECSuccess) {
- goto loser;
- }
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess) {
- goto loser;
- }
- rv = ssl3_ExtAppendHandshakeVariable(ss, alpn_protos, len, 2);
- PORT_Free(alpn_protos);
- alpn_protos = NULL;
- if (rv != SECSuccess) {
- goto loser;
+ i = ss->opt.nextProtoNego.data[0] + 1;
+ if (i <= len) {
+ rv = sslBuffer_Append(buf, &ss->opt.nextProtoNego.data[i], len - i);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_Append(buf, ss->opt.nextProtoNego.data, i);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ } else {
+ /* This seems to be invalid data so we'll send as-is. */
+ rv = sslBuffer_Append(buf, ss->opt.nextProtoNego.data, len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_app_layer_protocol_xtn;
}
- return extension_length;
-
-loser:
- if (alpn_protos) {
- PORT_Free(alpn_protos);
- }
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
-PRInt32
-ssl3_ServerSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+SECStatus
+ssl3_ServerSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
+ SECStatus rv;
- /* we're in over our heads if any of these fail */
+ /* We're in over our heads if any of these fail */
PORT_Assert(ss->opt.enableALPN);
PORT_Assert(xtnData->nextProto.data);
PORT_Assert(xtnData->nextProto.len > 0);
PORT_Assert(xtnData->nextProtoState == SSL_NEXT_PROTO_NEGOTIATED);
PORT_Assert(!ss->firstHsDone);
- extension_length = 2 /* extension type */ + 2 /* extension length */ +
- 2 /* protocol name list */ + 1 /* name length */ +
- xtnData->nextProto.len;
-
- if (maxBytes < (PRUint32)extension_length) {
- return 0;
+ rv = sslBuffer_AppendNumber(buf, xtnData->nextProto.len + 1, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (append) {
- SECStatus rv;
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2);
- if (rv != SECSuccess) {
- return -1;
- }
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess) {
- return -1;
- }
- rv = ssl3_ExtAppendHandshakeNumber(ss, xtnData->nextProto.len + 1, 2);
- if (rv != SECSuccess) {
- return -1;
- }
- rv = ssl3_ExtAppendHandshakeVariable(ss, xtnData->nextProto.data,
- xtnData->nextProto.len, 1);
- if (rv != SECSuccess) {
- return -1;
- }
+ rv = sslBuffer_AppendVariable(buf, xtnData->nextProto.data,
+ xtnData->nextProto.len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-ssl3_ServerHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ServerHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
- ssl3HelloExtensionSenderFunc sender;
+ sslExtensionBuilderFunc sender;
PORT_Assert(ss->sec.isServer);
/* remember that we got this extension. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_cert_status_xtn;
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
sender = tls13_ServerSendStatusRequestXtn;
} else {
sender = ssl3_ServerSendStatusRequestXtn;
}
- return ssl3_RegisterExtensionSender(ss, xtnData, ex_type, sender);
+ return ssl3_RegisterExtensionSender(ss, xtnData, ssl_cert_status_xtn, sender);
}
-PRInt32
-ssl3_ServerSendStatusRequestXtn(
- const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
const sslServerCert *serverCert = ss->sec.serverCert;
- SECStatus rv;
if (!serverCert->certStatusArray ||
!serverCert->certStatusArray->len) {
- return 0;
- }
-
- extension_length = 2 + 2;
- if (maxBytes < (PRUint32)extension_length) {
- return 0;
- }
- if (append) {
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_cert_status_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* length of extension_data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
- /* The certificate status data is sent in ssl3_SendCertificateStatus. */
+ return SECSuccess;
}
- return extension_length;
+ *added = PR_TRUE;
+ return SECSuccess;
}
/* ssl3_ClientSendStatusRequestXtn builds the status_request extension on the
* client side. See RFC 6066 section 8. */
-PRInt32
-ssl3_ClientSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_ClientSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
-
- if (!ss->opt.enableOCSPStapling)
- return 0;
-
- /* extension_type (2-bytes) +
- * length(extension_data) (2-bytes) +
- * status_type (1) +
- * responder_id_list length (2) +
- * request_extensions length (2)
- */
- extension_length = 9;
+ SECStatus rv;
- if (maxBytes < (PRUint32)extension_length) {
- PORT_Assert(0);
- return 0;
+ if (!ss->opt.enableOCSPStapling) {
+ return SECSuccess;
}
- if (append) {
- SECStatus rv;
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_cert_status_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess)
- return -1;
- rv = ssl3_ExtAppendHandshakeNumber(ss, 1 /* status_type ocsp */, 1);
- if (rv != SECSuccess)
- return -1;
- /* A zero length responder_id_list means that the responders are
- * implicitly known to the server. */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
- /* A zero length request_extensions means that there are no extensions.
- * Specifically, we don't set the id-pkix-ocsp-nonce extension. This
- * means that the server can replay a cached OCSP response to us. */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
-
- xtnData->advertised[xtnData->numAdvertised++] = ssl_cert_status_xtn;
+ rv = sslBuffer_AppendNumber(buf, 1 /* status_type ocsp */, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
+ /* A zero length responder_id_list means that the responders are
+ * implicitly known to the server. */
+ rv = sslBuffer_AppendNumber(buf, 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* A zero length request_extensions means that there are no extensions.
+ * Specifically, we don't set the id-pkix-ocsp-nonce extension. This
+ * means that the server can replay a cached OCSP response to us. */
+ rv = sslBuffer_AppendNumber(buf, 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
/* In TLS 1.3, the extension carries the OCSP response. */
@@ -797,36 +649,32 @@ ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_cert_status_xtn;
return SECSuccess;
}
PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */
-#define TLS_EX_SESS_TICKET_VERSION (0x0105)
+#define TLS_EX_SESS_TICKET_VERSION (0x010a)
/*
* Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket
*/
SECStatus
-ssl3_EncodeSessionTicket(sslSocket *ss,
- const NewSessionTicket *ticket,
- SECItem *ticket_data)
+ssl3_EncodeSessionTicket(sslSocket *ss, const NewSessionTicket *ticket,
+ const PRUint8 *appToken, unsigned int appTokenLen,
+ PK11SymKey *secret, SECItem *ticket_data)
{
SECStatus rv;
- SECItem plaintext;
- SECItem plaintext_item = { 0, NULL, 0 };
- PRUint32 plaintext_length;
+ sslBuffer plaintext = SSL_BUFFER_EMPTY;
SECItem ticket_buf = { 0, NULL, 0 };
- PRBool ms_is_wrapped;
+ sslSessionID sid;
unsigned char wrapped_ms[SSL3_MASTER_SECRET_LENGTH];
SECItem ms_item = { 0, NULL, 0 };
- PRUint32 cert_length = 0;
- PRUint32 now;
+ PRTime now;
SECItem *srvName = NULL;
- CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value,
- * must be >= 0 */
- ssl3CipherSpec *spec;
+ CK_MECHANISM_TYPE msWrapMech;
SECItem *alpnSelection = NULL;
+ PRUint32 ticketAgeBaseline;
SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
SSL_GETPID(), ss->fd));
@@ -834,107 +682,69 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
- cert_length = 2 + ss->sec.ci.sid->peerCert->derCert.len;
- }
+ /* Extract the master secret wrapped. */
- if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- spec = ss->ssl3.cwSpec;
- } else {
- spec = ss->ssl3.pwSpec;
- }
- if (spec->msItem.len && spec->msItem.data) {
- /* The master secret is available unwrapped. */
- ms_item.data = spec->msItem.data;
- ms_item.len = spec->msItem.len;
- ms_is_wrapped = PR_FALSE;
- } else {
- /* Extract the master secret wrapped. */
- sslSessionID sid;
- PORT_Memset(&sid, 0, sizeof(sslSessionID));
+ PORT_Memset(&sid, 0, sizeof(sslSessionID));
- rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec);
- if (rv == SECSuccess) {
- if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
- goto loser;
- memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret,
- sid.u.ssl3.keys.wrapped_master_secret_len);
- ms_item.data = wrapped_ms;
- ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len;
- msWrapMech = sid.u.ssl3.masterWrapMech;
- } else {
- /* TODO: else send an empty ticket. */
+ PORT_Assert(secret);
+ rv = ssl3_CacheWrappedSecret(ss, &sid, secret);
+ if (rv == SECSuccess) {
+ if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
goto loser;
- }
- ms_is_wrapped = PR_TRUE;
+ memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret,
+ sid.u.ssl3.keys.wrapped_master_secret_len);
+ ms_item.data = wrapped_ms;
+ ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len;
+ msWrapMech = sid.u.ssl3.masterWrapMech;
+ } else {
+ /* TODO: else send an empty ticket. */
+ goto loser;
}
/* Prep to send negotiated name */
srvName = &ss->sec.ci.sid->u.ssl3.srvName;
- PORT_Assert(ss->xtnData.nextProtoState == SSL_NEXT_PROTO_SELECTED ||
- ss->xtnData.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED ||
- ss->xtnData.nextProto.len == 0);
- alpnSelection = &ss->xtnData.nextProto;
-
- plaintext_length =
- sizeof(PRUint16) /* ticket version */
- + sizeof(SSL3ProtocolVersion) /* ssl_version */
- + sizeof(ssl3CipherSuite) /* ciphersuite */
- + 1 /* compression */
- + 10 /* cipher spec parameters */
- + 1 /* certType arguments */
- + 1 /* SessionTicket.ms_is_wrapped */
- + 4 /* msWrapMech */
- + 2 /* master_secret.length */
- + ms_item.len /* master_secret */
- + 1 /* client_auth_type */
- + cert_length /* cert */
- + 2 + srvName->len /* name len + length field */
- + 1 /* extendedMasterSecretUsed */
- + sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */
- + sizeof(ticket->flags) /* ticket flags */
- + 1 + alpnSelection->len /* alpn value + length field */
- + 4; /* maxEarlyData */
-
- if (SECITEM_AllocItem(NULL, &plaintext_item, plaintext_length) == NULL)
- goto loser;
-
- plaintext = plaintext_item;
-
/* ticket version */
- rv = ssl3_AppendNumberToItem(&plaintext, TLS_EX_SESS_TICKET_VERSION,
- sizeof(PRUint16));
+ rv = sslBuffer_AppendNumber(&plaintext, TLS_EX_SESS_TICKET_VERSION,
+ sizeof(PRUint16));
if (rv != SECSuccess)
goto loser;
/* ssl_version */
- rv = ssl3_AppendNumberToItem(&plaintext, ss->version,
- sizeof(SSL3ProtocolVersion));
+ rv = sslBuffer_AppendNumber(&plaintext, ss->version,
+ sizeof(SSL3ProtocolVersion));
if (rv != SECSuccess)
goto loser;
/* ciphersuite */
- rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.cipher_suite,
- sizeof(ssl3CipherSuite));
+ rv = sslBuffer_AppendNumber(&plaintext, ss->ssl3.hs.cipher_suite,
+ sizeof(ssl3CipherSuite));
if (rv != SECSuccess)
goto loser;
- /* compression */
- rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.compression, 1);
+ /* cipher spec parameters */
+ rv = sslBuffer_AppendNumber(&plaintext, ss->sec.authType, 1);
if (rv != SECSuccess)
goto loser;
-
- /* cipher spec parameters */
- rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authType, 1);
+ rv = sslBuffer_AppendNumber(&plaintext, ss->sec.authKeyBits, 4);
if (rv != SECSuccess)
goto loser;
- rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authKeyBits, 4);
+ rv = sslBuffer_AppendNumber(&plaintext, ss->sec.keaType, 1);
if (rv != SECSuccess)
goto loser;
- rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaType, 1);
+ rv = sslBuffer_AppendNumber(&plaintext, ss->sec.keaKeyBits, 4);
if (rv != SECSuccess)
goto loser;
- rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaKeyBits, 4);
+ if (ss->sec.keaGroup) {
+ rv = sslBuffer_AppendNumber(&plaintext, ss->sec.keaGroup->name, 4);
+ if (rv != SECSuccess)
+ goto loser;
+ } else {
+ /* No kea group. Write 0 as invalid value. */
+ rv = sslBuffer_AppendNumber(&plaintext, 0, 4);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+ rv = sslBuffer_AppendNumber(&plaintext, ss->sec.signatureScheme, 4);
if (rv != SECSuccess)
goto loser;
@@ -945,102 +755,120 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
PORT_Assert(cert->namedCurve);
/* EC curves only use the second of the two bytes. */
PORT_Assert(cert->namedCurve->name < 256);
- rv = ssl3_AppendNumberToItem(&plaintext, cert->namedCurve->name, 1);
+ rv = sslBuffer_AppendNumber(&plaintext, cert->namedCurve->name, 1);
} else {
- rv = ssl3_AppendNumberToItem(&plaintext, 0, 1);
+ rv = sslBuffer_AppendNumber(&plaintext, 0, 1);
}
if (rv != SECSuccess)
goto loser;
/* master_secret */
- rv = ssl3_AppendNumberToItem(&plaintext, ms_is_wrapped, 1);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_AppendNumberToItem(&plaintext, msWrapMech, 4);
+ rv = sslBuffer_AppendNumber(&plaintext, msWrapMech, 4);
if (rv != SECSuccess)
goto loser;
- rv = ssl3_AppendNumberToItem(&plaintext, ms_item.len, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_AppendToItem(&plaintext, ms_item.data, ms_item.len);
+ rv = sslBuffer_AppendVariable(&plaintext, ms_item.data, ms_item.len, 2);
if (rv != SECSuccess)
goto loser;
/* client identity */
if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
- rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_AppendNumberToItem(&plaintext,
- ss->sec.ci.sid->peerCert->derCert.len, 2);
+ rv = sslBuffer_AppendNumber(&plaintext, CLIENT_AUTH_CERTIFICATE, 1);
if (rv != SECSuccess)
goto loser;
- rv = ssl3_AppendToItem(&plaintext,
- ss->sec.ci.sid->peerCert->derCert.data,
- ss->sec.ci.sid->peerCert->derCert.len);
+ rv = sslBuffer_AppendVariable(&plaintext,
+ ss->sec.ci.sid->peerCert->derCert.data,
+ ss->sec.ci.sid->peerCert->derCert.len, 2);
if (rv != SECSuccess)
goto loser;
} else {
- rv = ssl3_AppendNumberToItem(&plaintext, 0, 1);
+ rv = sslBuffer_AppendNumber(&plaintext, 0, 1);
if (rv != SECSuccess)
goto loser;
}
/* timestamp */
- now = ssl_Time();
- rv = ssl3_AppendNumberToItem(&plaintext, now,
- sizeof(ticket->ticket_lifetime_hint));
+ now = ssl_TimeUsec();
+ PORT_Assert(sizeof(now) == 8);
+ rv = sslBuffer_AppendNumber(&plaintext, now, 8);
if (rv != SECSuccess)
goto loser;
/* HostName (length and value) */
- rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2);
+ rv = sslBuffer_AppendVariable(&plaintext, srvName->data, srvName->len, 2);
if (rv != SECSuccess)
goto loser;
- if (srvName->len) {
- rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len);
- if (rv != SECSuccess)
- goto loser;
- }
/* extendedMasterSecretUsed */
- rv = ssl3_AppendNumberToItem(
+ rv = sslBuffer_AppendNumber(
&plaintext, ss->sec.ci.sid->u.ssl3.keys.extendedMasterSecretUsed, 1);
if (rv != SECSuccess)
goto loser;
/* Flags */
- rv = ssl3_AppendNumberToItem(&plaintext, ticket->flags,
- sizeof(ticket->flags));
+ rv = sslBuffer_AppendNumber(&plaintext, ticket->flags,
+ sizeof(ticket->flags));
if (rv != SECSuccess)
goto loser;
/* ALPN value. */
+ PORT_Assert(ss->xtnData.nextProtoState == SSL_NEXT_PROTO_SELECTED ||
+ ss->xtnData.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED ||
+ ss->xtnData.nextProto.len == 0);
+ alpnSelection = &ss->xtnData.nextProto;
PORT_Assert(alpnSelection->len < 256);
- rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection->len, 1);
+ rv = sslBuffer_AppendVariable(&plaintext, alpnSelection->data,
+ alpnSelection->len, 1);
if (rv != SECSuccess)
goto loser;
- if (alpnSelection->len) {
- rv = ssl3_AppendToItem(&plaintext, alpnSelection->data,
- alpnSelection->len);
- if (rv != SECSuccess)
- goto loser;
- }
- rv = ssl3_AppendNumberToItem(&plaintext, ssl_max_early_data_size, 4);
+ rv = sslBuffer_AppendNumber(&plaintext, ssl_max_early_data_size, 4);
if (rv != SECSuccess)
goto loser;
- /* Check that we are totally full. */
- PORT_Assert(plaintext.len == 0);
+ /*
+ * We store this in the ticket:
+ * ticket_age_baseline = 1rtt - ticket_age_add
+ *
+ * When the client resumes, it will provide:
+ * obfuscated_age = ticket_age_client + ticket_age_add
+ *
+ * We expect to receive the ticket at:
+ * ticket_create + 1rtt + ticket_age_server
+ *
+ * We calculate the client's estimate of this as:
+ * ticket_create + ticket_age_baseline + obfuscated_age
+ * = ticket_create + 1rtt + ticket_age_client
+ *
+ * This is compared to the expected time, which should differ only as a
+ * result of clock errors or errors in the RTT estimate.
+ */
+ ticketAgeBaseline = (ssl_TimeUsec() - ss->ssl3.hs.serverHelloTime) / PR_USEC_PER_MSEC;
+ ticketAgeBaseline -= ticket->ticket_age_add;
+ rv = sslBuffer_AppendNumber(&plaintext, ticketAgeBaseline, 4);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Application token */
+ rv = sslBuffer_AppendVariable(&plaintext, appToken, appTokenLen, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* This really only happens if appTokenLen is too much, and that always
+ * comes from the using application. */
+ if (SSL_BUFFER_LEN(&plaintext) > 0xffff) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
- /* 128 just gives us enough room for overhead. */
- if (SECITEM_AllocItem(NULL, &ticket_buf, plaintext_length + 128) == NULL) {
+ ticket_buf.len = ssl_SelfEncryptGetProtectedSize(SSL_BUFFER_LEN(&plaintext));
+ PORT_Assert(ticket_buf.len > 0);
+ if (SECITEM_AllocItem(NULL, &ticket_buf, ticket_buf.len) == NULL) {
goto loser;
}
/* Finally, encrypt the ticket. */
- rv = ssl_SelfEncryptProtect(ss, plaintext_item.data, plaintext_item.len,
+ rv = ssl_SelfEncryptProtect(ss, SSL_BUFFER_BASE(&plaintext),
+ SSL_BUFFER_LEN(&plaintext),
ticket_buf.data, &ticket_buf.len, ticket_buf.len);
if (rv != SECSuccess) {
goto loser;
@@ -1049,13 +877,11 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
/* Give ownership of memory to caller. */
*ticket_data = ticket_buf;
- SECITEM_FreeItem(&plaintext_item, PR_FALSE);
+ sslBuffer_Clear(&plaintext);
return SECSuccess;
loser:
- if (plaintext_item.data) {
- SECITEM_FreeItem(&plaintext_item, PR_FALSE);
- }
+ sslBuffer_Clear(&plaintext);
if (ticket_buf.data) {
SECITEM_FreeItem(&ticket_buf, PR_FALSE);
}
@@ -1067,18 +893,22 @@ loser:
* message is expected during the handshake.
*/
SECStatus
-ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+
if (data->len != 0) {
return SECSuccess; /* Ignore the extension. */
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_session_ticket_xtn;
return SECSuccess;
}
+PR_STATIC_ASSERT((TLS_EX_SESS_TICKET_VERSION >> 8) == 1);
+
static SECStatus
ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
SessionTicket *parsedTicket)
@@ -1105,6 +935,12 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
return SECFailure;
}
+ /* All ticket versions start with 0x01, so check to see if this
+ * is a ticket or some other self-encrypted thing. */
+ if ((temp >> 8) != 1) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
/* Skip the ticket if the version is wrong. This won't result in a
* handshake failure, just a failure to resume. */
if (temp != TLS_EX_SESS_TICKET_VERSION) {
@@ -1132,14 +968,6 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
}
parsedTicket->cipher_suite = (ssl3CipherSuite)temp;
- /* Read compression_method. */
- rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
- if (rv != SECSuccess) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- parsedTicket->compression_method = (SSLCompressionMethod)temp;
-
/* Read cipher spec parameters. */
rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
if (rv != SECSuccess) {
@@ -1165,6 +993,18 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
return SECFailure;
}
parsedTicket->keaKeyBits = temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->originalKeaGroup = temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->signatureScheme = (SSLSignatureScheme)temp;
/* Read the optional named curve. */
rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
@@ -1185,14 +1025,6 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
}
/* Read the master secret (and how it is wrapped). */
- rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
- if (rv != SECSuccess) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
- parsedTicket->ms_is_wrapped = (PRBool)temp;
-
rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -1240,13 +1072,21 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
- /* Read timestamp. */
+
+ /* Read timestamp. This is a 64-bit value and
+ * ssl3_ExtConsumeHandshakeNumber only reads 32-bits at a time. */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->timestamp = (PRTime)temp << 32;
rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
- parsedTicket->timestamp = temp;
+ parsedTicket->timestamp |= (PRTime)temp;
/* Read server name */
rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->srvName, 2,
@@ -1287,6 +1127,20 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
}
parsedTicket->maxEarlyData = temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->ticketAgeBaseline = temp;
+
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->applicationToken,
+ 2, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
#ifndef UNSAFE_FUZZER_MODE
/* Done parsing. Check that all bytes have been consumed. */
if (len != 0) {
@@ -1313,13 +1167,15 @@ ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket,
/* Copy over parameters. */
sid->version = parsedTicket->ssl_version;
+ sid->creationTime = parsedTicket->timestamp;
sid->u.ssl3.cipherSuite = parsedTicket->cipher_suite;
- sid->u.ssl3.compression = parsedTicket->compression_method;
sid->authType = parsedTicket->authType;
sid->authKeyBits = parsedTicket->authKeyBits;
sid->keaType = parsedTicket->keaType;
sid->keaKeyBits = parsedTicket->keaKeyBits;
+ sid->keaGroup = parsedTicket->originalKeaGroup;
sid->namedCurve = parsedTicket->namedCurve;
+ sid->sigScheme = parsedTicket->signatureScheme;
rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
rawTicket);
@@ -1338,7 +1194,6 @@ ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket,
parsedTicket->master_secret, parsedTicket->ms_length);
sid->u.ssl3.keys.wrapped_master_secret_len = parsedTicket->ms_length;
sid->u.ssl3.masterWrapMech = parsedTicket->msWrapMech;
- sid->u.ssl3.keys.msIsWrapped = parsedTicket->ms_is_wrapped;
sid->u.ssl3.masterValid = PR_TRUE;
sid->u.ssl3.keys.resumable = PR_TRUE;
sid->u.ssl3.keys.extendedMasterSecretUsed = parsedTicket->extendedMasterSecretUsed;
@@ -1381,10 +1236,12 @@ loser:
/* Generic ticket processing code, common to all TLS versions. */
SECStatus
-ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
+ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket,
+ SECItem *appToken)
{
SECItem decryptedTicket = { siBuffer, NULL, 0 };
SessionTicket parsedTicket;
+ sslSessionID *sid = NULL;
SECStatus rv;
if (ss->sec.ci.sid != NULL) {
@@ -1393,12 +1250,12 @@ ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
ss->sec.ci.sid = NULL;
}
- if (!SECITEM_AllocItem(NULL, &decryptedTicket, data->len)) {
+ if (!SECITEM_AllocItem(NULL, &decryptedTicket, ticket->len)) {
return SECFailure;
}
/* Decrypt the ticket. */
- rv = ssl_SelfEncryptUnprotect(ss, data->data, data->len,
+ rv = ssl_SelfEncryptUnprotect(ss, ticket->data, ticket->len,
decryptedTicket.data,
&decryptedTicket.len,
decryptedTicket.len);
@@ -1428,16 +1285,28 @@ ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
}
/* Use the ticket if it is valid and unexpired. */
- if (parsedTicket.valid &&
- parsedTicket.timestamp + ssl_ticket_lifetime > ssl_Time()) {
- sslSessionID *sid;
+ if (parsedTicket.timestamp + ssl_ticket_lifetime * PR_USEC_PER_SEC >
+ ssl_TimeUsec()) {
- rv = ssl_CreateSIDFromTicket(ss, data, &parsedTicket, &sid);
+ rv = ssl_CreateSIDFromTicket(ss, ticket, &parsedTicket, &sid);
if (rv != SECSuccess) {
goto loser; /* code already set */
}
+ if (appToken && parsedTicket.applicationToken.len) {
+ rv = SECITEM_CopyItem(NULL, appToken,
+ &parsedTicket.applicationToken);
+ if (rv != SECSuccess) {
+ goto loser; /* code already set */
+ }
+ }
+
ss->statelessResume = PR_TRUE;
ss->sec.ci.sid = sid;
+
+ /* We have the baseline value for the obfuscated ticket age here. Save
+ * that in xtnData temporarily. This value is updated in
+ * tls13_ServerHandlePreSharedKeyXtn with the final estimate. */
+ ss->xtnData.ticketAge = parsedTicket.ticketAgeBaseline;
}
SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
@@ -1445,15 +1314,19 @@ ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
return SECSuccess;
loser:
+ if (sid) {
+ ssl_FreeSID(sid);
+ }
SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
return SECFailure;
}
SECStatus
-ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
/* Ignore the SessionTicket extension if processing is disabled. */
if (!ss->opt.enableSessionTickets) {
@@ -1466,7 +1339,7 @@ ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_session_ticket_xtn;
/* Parse the received ticket sent in by the client. We are
* lenient about some parse errors, falling back to a fullshake
@@ -1477,7 +1350,8 @@ ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData
return SECSuccess;
}
- return ssl3_ProcessSessionTicketCommon(CONST_CAST(sslSocket, ss), data);
+ return ssl3_ProcessSessionTicketCommon(CONST_CAST(sslSocket, ss), data,
+ NULL);
}
/* Extension format:
@@ -1487,60 +1361,45 @@ ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData
* Verify Data (TLS): 12 bytes (client) or 24 bytes (server)
* Verify Data (SSL): 36 bytes (client) or 72 bytes (server)
*/
-PRInt32
-ssl3_SendRenegotiationInfoXtn(
- const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_SendRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
PRInt32 len = 0;
- PRInt32 needed;
+ SECStatus rv;
- /* In draft-ietf-tls-renegotiation-03, it is NOT RECOMMENDED to send
- * both the SCSV and the empty RI, so when we send SCSV in
- * the initial handshake, we don't also send RI.
+ /* In RFC 5746, it is NOT RECOMMENDED to send both the SCSV and the empty
+ * RI, so when we send SCSV in the initial handshake, we don't also send RI.
*/
- if (!ss || ss->ssl3.hs.sendingSCSV)
+ if (ss->ssl3.hs.sendingSCSV) {
return 0;
+ }
if (ss->firstHsDone) {
len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes * 2
: ss->ssl3.hs.finishedBytes;
}
- needed = 5 + len;
- if (maxBytes < (PRUint32)needed) {
- return 0;
- }
- if (append) {
- SECStatus rv;
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_renegotiation_info_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* length of extension_data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, len + 1, 2);
- if (rv != SECSuccess)
- return -1;
- /* verify_Data from previous Finished message(s) */
- rv = ssl3_ExtAppendHandshakeVariable(ss,
- ss->ssl3.hs.finishedMsgs.data, len, 1);
- if (rv != SECSuccess)
- return -1;
- if (!ss->sec.isServer) {
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_renegotiation_info_xtn;
- }
+
+ /* verify_Data from previous Finished message(s) */
+ rv = sslBuffer_AppendVariable(buf,
+ ss->ssl3.hs.finishedMsgs.data, len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return needed;
+
+ *added = PR_TRUE;
+ return SECSuccess;
}
/* This function runs in both the client and server. */
SECStatus
-ssl3_HandleRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_HandleRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv = SECSuccess;
PRUint32 len = 0;
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+
if (ss->firstHsDone) {
len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes
: ss->ssl3.hs.finishedBytes * 2;
@@ -1558,97 +1417,78 @@ ssl3_HandleRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
/* remember that we got this extension and it was correct. */
CONST_CAST(sslSocket, ss)
->peerRequestedProtection = 1;
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_renegotiation_info_xtn;
if (ss->sec.isServer) {
/* prepare to send back the appropriate response */
- rv = ssl3_RegisterExtensionSender(ss, xtnData, ex_type,
+ rv = ssl3_RegisterExtensionSender(ss, xtnData,
+ ssl_renegotiation_info_xtn,
ssl3_SendRenegotiationInfoXtn);
}
return rv;
}
-PRInt32
-ssl3_ClientSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+SECStatus
+ssl3_ClientSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRUint32 ext_data_len;
- PRInt16 i;
+ unsigned int i;
SECStatus rv;
- if (!ss)
- return 0;
-
- if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount)
- return 0; /* Not relevant */
-
- ext_data_len = 2 + 2 * ss->ssl3.dtlsSRTPCipherCount + 1;
+ if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) {
+ return SECSuccess; /* Not relevant */
+ }
- if (append && maxBytes >= 4 + ext_data_len) {
- /* Extension type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* Length of extension data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ext_data_len, 2);
- if (rv != SECSuccess)
- return -1;
- /* Length of the SRTP cipher list */
- rv = ssl3_ExtAppendHandshakeNumber(ss,
- 2 * ss->ssl3.dtlsSRTPCipherCount,
- 2);
- if (rv != SECSuccess)
- return -1;
- /* The SRTP ciphers */
- for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
- rv = ssl3_ExtAppendHandshakeNumber(ss,
- ss->ssl3.dtlsSRTPCiphers[i],
- 2);
- if (rv != SECSuccess)
- return -1;
+ /* Length of the SRTP cipher list */
+ rv = sslBuffer_AppendNumber(buf, 2 * ss->ssl3.dtlsSRTPCipherCount, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* The SRTP ciphers */
+ for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.dtlsSRTPCiphers[i], 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- /* Empty MKI value */
- ssl3_ExtAppendHandshakeVariable(ss, NULL, 0, 1);
-
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_use_srtp_xtn;
+ }
+ /* Empty MKI value */
+ rv = sslBuffer_AppendNumber(buf, 0, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return 4 + ext_data_len;
+ *added = PR_TRUE;
+ return SECSuccess;
}
-PRInt32
-ssl3_ServerSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+SECStatus
+ssl3_ServerSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
SECStatus rv;
- /* Server side */
- if (!append || maxBytes < 9) {
- return 9;
- }
-
- /* Extension type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* Length of extension data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 5, 2);
- if (rv != SECSuccess)
- return -1;
/* Length of the SRTP cipher list */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 2, 2);
- if (rv != SECSuccess)
- return -1;
+ rv = sslBuffer_AppendNumber(buf, 2, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
/* The selected cipher */
- rv = ssl3_ExtAppendHandshakeNumber(ss, xtnData->dtlsSRTPCipherSuite, 2);
- if (rv != SECSuccess)
- return -1;
+ rv = sslBuffer_AppendNumber(buf, xtnData->dtlsSRTPCipherSuite, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
/* Empty MKI value */
- ssl3_ExtAppendHandshakeVariable(ss, NULL, 0, 1);
+ rv = sslBuffer_AppendNumber(buf, 0, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- return 9;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-ssl3_ClientHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_ClientHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
SECItem ciphers = { siBuffer, NULL, 0 };
@@ -1718,7 +1558,8 @@ ssl3_ClientHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUi
}
SECStatus
-ssl3_ServerHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_ServerHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
SECItem ciphers = { siBuffer, NULL, 0 };
@@ -1789,11 +1630,12 @@ ssl3_ServerHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUi
ssl3_ServerSendUseSRTPXtn);
}
-/* ssl3_ServerHandleSigAlgsXtn handles the signature_algorithms extension
- * from a client.
- * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
+/* ssl3_HandleSigAlgsXtn handles the signature_algorithms extension from a
+ * client. In TLS 1.3, the client uses this to parse CertificateRequest
+ * extensions. See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
SECStatus
-ssl3_ServerHandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+ssl3_HandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
@@ -1802,15 +1644,15 @@ ssl3_ServerHandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUi
return SECSuccess;
}
- if (xtnData->clientSigSchemes) {
- PORT_Free(xtnData->clientSigSchemes);
- xtnData->clientSigSchemes = NULL;
+ if (xtnData->sigSchemes) {
+ PORT_Free(xtnData->sigSchemes);
+ xtnData->sigSchemes = NULL;
}
rv = ssl_ParseSignatureSchemes(ss, NULL,
- &xtnData->clientSigSchemes,
- &xtnData->numClientSigScheme,
+ &xtnData->sigSchemes,
+ &xtnData->numSigSchemes,
&data->data, &data->len);
- if (rv != SECSuccess || xtnData->numClientSigScheme == 0) {
+ if (rv != SECSuccess || xtnData->numSigSchemes == 0) {
ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
return SECFailure;
@@ -1823,177 +1665,52 @@ ssl3_ServerHandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUi
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_signature_algorithms_xtn;
return SECSuccess;
}
/* ssl3_ClientSendSigAlgsXtn sends the signature_algorithm extension for TLS
* 1.2 ClientHellos. */
-PRInt32
-ssl3_ClientSendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+SECStatus
+ssl3_SendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
- PRUint8 buf[MAX_SIGNATURE_SCHEMES * 2];
- PRUint32 len;
SECStatus rv;
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_2) {
- return 0;
- }
-
- rv = ssl3_EncodeSigAlgs(ss, buf, sizeof(buf), &len);
- if (rv != SECSuccess) {
- return -1;
- }
-
- extension_length =
- 2 /* extension type */ +
- 2 /* extension length */ +
- 2 /* supported_signature_algorithms length */ +
- len;
-
- if (maxBytes < extension_length) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- SECStatus rv;
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_signature_algorithms_xtn, 2);
- if (rv != SECSuccess) {
- return -1;
- }
- rv = ssl3_ExtAppendHandshakeNumber(ss, len + 2, 2);
- if (rv != SECSuccess) {
- return -1;
- }
-
- rv = ssl3_ExtAppendHandshakeVariable(ss, buf, len, 2);
- if (rv != SECSuccess) {
- return -1;
- }
-
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_signature_algorithms_xtn;
- }
-
- return extension_length;
-}
-
-/* Takes the size of the ClientHello, less the record header, and determines how
- * much padding is required. */
-void
-ssl3_CalculatePaddingExtLen(sslSocket *ss,
- unsigned int clientHelloLength)
-{
- unsigned int recordLength = 1 /* handshake message type */ +
- 3 /* handshake message length */ +
- clientHelloLength;
- unsigned int extensionLen;
-
- /* Don't pad for DTLS, for SSLv3, or for renegotiation. */
- if (IS_DTLS(ss) ||
- ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_0 ||
- ss->firstHsDone) {
- return;
- }
-
- /* A padding extension may be included to ensure that the record containing
- * the ClientHello doesn't have a length between 256 and 511 bytes
- * (inclusive). Initial ClientHello records with such lengths trigger bugs
- * in F5 devices. */
- if (recordLength < 256 || recordLength >= 512) {
- return;
- }
-
- extensionLen = 512 - recordLength;
- /* Extensions take at least four bytes to encode. Always include at least
- * one byte of data if we are padding. Some servers will time out or
- * terminate the connection if the last ClientHello extension is empty. */
- if (extensionLen < 4 + 1) {
- extensionLen = 4 + 1;
- }
-
- ss->xtnData.paddingLen = extensionLen - 4;
-}
-
-/* ssl3_SendPaddingExtension possibly adds an extension which ensures that a
- * ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures
- * that we don't trigger bugs in F5 products. */
-PRInt32
-ssl3_ClientSendPaddingExtension(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes)
-{
- static unsigned char padding[252] = { 0 };
- unsigned int extensionLen;
- SECStatus rv;
-
- /* On the length-calculation pass, report zero total length. The record
- * will be larger on the second pass if needed. */
- if (!append || !xtnData->paddingLen) {
- return 0;
- }
-
- extensionLen = xtnData->paddingLen + 4;
- if (extensionLen > maxBytes ||
- xtnData->paddingLen > sizeof(padding)) {
- PORT_Assert(0);
- return -1;
+ return SECSuccess;
}
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_padding_xtn, 2);
- if (rv != SECSuccess) {
- return -1;
- }
- rv = ssl3_ExtAppendHandshakeVariable(ss, padding, xtnData->paddingLen, 2);
+ rv = ssl3_EncodeSigAlgs(ss, buf);
if (rv != SECSuccess) {
- return -1;
+ return SECFailure;
}
- return extensionLen;
+ *added = PR_TRUE;
+ return SECSuccess;
}
-PRInt32
-ssl3_SendExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_SendExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
-
if (!ss->opt.enableExtendedMS) {
- return 0;
+ return SECSuccess;
}
/* Always send the extension in this function, since the
* client always sends it and this function is only called on
* the server if we negotiated the extension. */
- extension_length = 4; /* Type + length (0) */
- if (maxBytes < extension_length) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- SECStatus rv;
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_extended_master_secret_xtn, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- goto loser;
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_extended_master_secret_xtn;
- }
-
- return extension_length;
-
-loser:
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+
if (ss->version < SSL_LIBRARY_VERSION_TLS_1_0) {
return SECSuccess;
}
@@ -2013,54 +1730,34 @@ ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnDat
SSL_GETPID(), ss->fd));
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_extended_master_secret_xtn;
if (ss->sec.isServer) {
- return ssl3_RegisterExtensionSender(
- ss, xtnData, ex_type, ssl3_SendExtendedMasterSecretXtn);
+ return ssl3_RegisterExtensionSender(ss, xtnData,
+ ssl_extended_master_secret_xtn,
+ ssl_SendEmptyExtension);
}
return SECSuccess;
}
/* ssl3_ClientSendSignedCertTimestampXtn sends the signed_certificate_timestamp
* extension for TLS ClientHellos. */
-PRInt32
-ssl3_ClientSendSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+ssl3_ClientSendSignedCertTimestampXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length = 2 /* extension_type */ +
- 2 /* length(extension_data) */;
-
/* Only send the extension if processing is enabled. */
- if (!ss->opt.enableSignedCertTimestamps)
- return 0;
-
- if (append && maxBytes >= extension_length) {
- SECStatus rv;
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss,
- ssl_signed_cert_timestamp_xtn,
- 2);
- if (rv != SECSuccess)
- goto loser;
- /* zero length */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- goto loser;
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_signed_cert_timestamp_xtn;
- } else if (maxBytes < extension_length) {
- PORT_Assert(0);
- return 0;
+ if (!ss->opt.enableSignedCertTimestamps) {
+ return SECSuccess;
}
- return extension_length;
-loser:
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-ssl3_ClientHandleSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ssl3_ClientHandleSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
/* We do not yet know whether we'll be resuming a session or creating
@@ -2080,54 +1777,34 @@ ssl3_ClientHandleSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *x
}
*scts = *data;
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_signed_cert_timestamp_xtn;
return SECSuccess;
}
-PRInt32
+SECStatus
ssl3_ServerSendSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
const SECItem *scts = &ss->sec.serverCert->signedCertTimestamps;
+ SECStatus rv;
if (!scts->len) {
/* No timestamps to send */
- return 0;
+ return SECSuccess;
}
- extension_length = 2 /* extension_type */ +
- 2 /* length(extension_data) */ +
- scts->len;
-
- if (maxBytes < extension_length) {
- PORT_Assert(0);
- return 0;
- }
- if (append) {
- SECStatus rv;
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss,
- ssl_signed_cert_timestamp_xtn,
- 2);
- if (rv != SECSuccess) {
- return -1;
- }
- /* extension_data */
- rv = ssl3_ExtAppendHandshakeVariable(ss, scts->data, scts->len, 2);
- if (rv != SECSuccess) {
- return -1;
- }
+ rv = sslBuffer_Append(buf, scts->data, scts->len);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
ssl3_ServerHandleSignedCertTimestampXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
- PRUint16 ex_type,
SECItem *data)
{
if (data->len != 0) {
@@ -2136,22 +1813,25 @@ ssl3_ServerHandleSignedCertTimestampXtn(const sslSocket *ss,
return SECFailure;
}
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_signed_cert_timestamp_xtn;
PORT_Assert(ss->sec.isServer);
- return ssl3_RegisterExtensionSender(
- ss, xtnData, ex_type, ssl3_ServerSendSignedCertTimestampXtn);
+ return ssl3_RegisterExtensionSender(ss, xtnData,
+ ssl_signed_cert_timestamp_xtn,
+ ssl3_ServerSendSignedCertTimestampXtn);
}
/* Just make sure that the remote client supports uncompressed points,
* Since that is all we support. Disable ECC cipher suites if it doesn't.
*/
SECStatus
-ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data)
{
int i;
+ PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+
if (data->len < 2 || data->len > 255 || !data->data ||
data->len != (unsigned int)data->data[0] + 1) {
ssl3_ExtDecodeError(ss);
@@ -2160,10 +1840,9 @@ ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss, TLSExtensionData *xtnDa
for (i = data->len; --i > 0;) {
if (data->data[i] == 0) {
/* indicate that we should send a reply */
- SECStatus rv;
- rv = ssl3_RegisterExtensionSender(ss, xtnData, ex_type,
- &ssl3_SendSupportedPointFormatsXtn);
- return rv;
+ return ssl3_RegisterExtensionSender(
+ ss, xtnData, ssl_ec_point_formats_xtn,
+ &ssl3_SendSupportedPointFormatsXtn);
}
}
@@ -2248,7 +1927,7 @@ ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
*/
SECStatus
ssl_HandleSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data)
+ SECItem *data)
{
SECStatus rv;
@@ -2258,7 +1937,7 @@ ssl_HandleSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
/* TLS 1.3 permits the server to send this extension so make it so. */
if (ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = ssl3_RegisterExtensionSender(ss, xtnData, ex_type,
+ rv = ssl3_RegisterExtensionSender(ss, xtnData, ssl_supported_groups_xtn,
&ssl_SendSupportedGroupsXtn);
if (rv != SECSuccess) {
return SECFailure; /* error already set. */
@@ -2266,7 +1945,7 @@ ssl_HandleSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
}
/* Remember that we negotiated this extension. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_supported_groups_xtn;
return SECSuccess;
}
diff --git a/security/nss/lib/ssl/ssl3exthandle.h b/security/nss/lib/ssl/ssl3exthandle.h
index 5fdbe9053..b84bd074c 100644
--- a/security/nss/lib/ssl/ssl3exthandle.h
+++ b/security/nss/lib/ssl/ssl3exthandle.h
@@ -9,90 +9,114 @@
#ifndef __ssl3exthandle_h_
#define __ssl3exthandle_h_
-PRInt32 ssl3_SendRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
-SECStatus ssl3_HandleRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ClientHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ServerHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+#include "sslencode.h"
+
+SECStatus ssl3_SendRenegotiationInfoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_HandleRenegotiationInfoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ClientHandleNextProtoNegoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ClientHandleAppProtoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ServerHandleNextProtoNegoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ServerHandleAppProtoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 ssl3_ClientSendNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-PRInt32 ssl3_ClientSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-PRInt32 ssl3_ServerSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-PRInt32 ssl3_ClientSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-PRInt32 ssl3_ServerSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-SECStatus ssl3_ClientHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+SECStatus ssl3_ClientSendNextProtoNegoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ClientSendAppProtoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ServerSendAppProtoXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ClientSendUseSRTPXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ServerSendUseSRTPXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ClientHandleUseSRTPXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-SECStatus ssl3_ServerHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+SECStatus ssl3_ServerHandleUseSRTPXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 ssl3_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
-SECStatus ssl3_ServerHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus ssl3_ServerSendStatusRequestXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ServerHandleStatusRequestXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 ssl3_ClientSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-PRInt32 ssl3_ClientSendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-SECStatus ssl3_ServerHandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
- SECItem *data);
+SECStatus ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ClientSendStatusRequestXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_SendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_HandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data);
-PRInt32 ssl3_ClientSendPaddingExtension(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
+SECStatus ssl3_ClientSendPaddingExtension(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
-PRInt32 ssl3_ClientSendSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-SECStatus ssl3_ClientHandleSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus ssl3_ClientSendSignedCertTimestampXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ClientHandleSignedCertTimestampXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 ssl3_ServerSendSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-SECStatus ssl3_ServerHandleSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus ssl3_ServerSendSignedCertTimestampXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_ServerHandleSignedCertTimestampXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 ssl3_SendExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-SECStatus ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus ssl3_SendExtendedMasterSecretXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data);
-PRInt32 ssl3_SendServerNameXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-SECStatus ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl_HandleSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-SECStatus ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-PRInt32 ssl3_SendSessionTicketXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-
-PRInt32 ssl_SendSupportedGroupsXtn(const sslSocket *ss,
+SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket,
+ /* out */ SECItem *appToken);
+SECStatus ssl3_ClientSendServerNameXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_HandleServerNameXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
-PRInt32 ssl3_SendSupportedPointFormatsXtn(const sslSocket *ss,
+ SECItem *data);
+SECStatus ssl_HandleSupportedGroupsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus ssl3_ClientSendSessionTicketXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
+ sslBuffer *buf, PRBool *added);
+
+SECStatus ssl_SendSupportedGroupsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus ssl3_SendSupportedPointFormatsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
#endif
diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c
index cf6f4cb33..20404f4da 100644
--- a/security/nss/lib/ssl/ssl3gthr.c
+++ b/security/nss/lib/ssl/ssl3gthr.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Gather (Read) entire SSL3 records from socket into buffer.
*
@@ -98,7 +99,7 @@ ssl3_GatherData(sslSocket *ss, sslGather *gs, int flags, ssl2Gather *ssl2gs)
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
if (gs->state == GS_INIT) {
gs->state = GS_HEADER;
- gs->remainder = ss->ssl3.hs.shortHeaders ? 2 : 5;
+ gs->remainder = 5;
gs->offset = 0;
gs->writeOffset = 0;
gs->readOffset = 0;
@@ -156,19 +157,7 @@ ssl3_GatherData(sslSocket *ss, sslGather *gs, int flags, ssl2Gather *ssl2gs)
/* Should have a non-SSLv2 record header in gs->hdr. Extract
* the length of the following encrypted data, and then
* read in the rest of the record into gs->inbuf. */
- if (ss->ssl3.hs.shortHeaders) {
- PRUint16 len = (gs->hdr[0] << 8) | gs->hdr[1];
- if (!(len & 0x8000)) {
- SSL_DBG(("%d: SSL3[%d]: incorrectly formatted header"));
- SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
- gs->state = GS_INIT;
- PORT_SetError(SSL_ERROR_BAD_MAC_READ);
- return SECFailure;
- }
- gs->remainder = len & ~0x8000;
- } else {
- gs->remainder = (gs->hdr[3] << 8) | gs->hdr[4];
- }
+ gs->remainder = (gs->hdr[3] << 8) | gs->hdr[4];
} else {
/* Probably an SSLv2 record header. No need to handle any
* security escapes (gs->hdr[0] & 0x40) as we wouldn't get
@@ -361,6 +350,9 @@ dtls_GatherData(sslSocket *ss, sslGather *gs, int flags)
}
}
+ SSL_TRC(20, ("%d: SSL3[%d]: dtls gathered record type=%d len=%d",
+ SSL_GETPID(), ss->fd, gs->hdr[0], gs->inbuf.len));
+
memcpy(gs->inbuf.buf, gs->dtlsPacket.buf + gs->dtlsPacketOffset,
gs->remainder);
gs->inbuf.len = gs->remainder;
@@ -394,7 +386,8 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
SSL3Ciphertext cText;
PRBool keepGoing = PR_TRUE;
- SSL_TRC(30, ("ssl3_GatherCompleteHandshake"));
+ SSL_TRC(30, ("%d: SSL3[%d]: ssl3_GatherCompleteHandshake",
+ SSL_GETPID(), ss->fd));
/* ssl3_HandleRecord may end up eventually calling ssl_FinishHandshake,
* which requires the 1stHandshakeLock, which must be acquired before the
@@ -405,9 +398,12 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
do {
PRBool handleRecordNow = PR_FALSE;
+ PRBool processingEarlyData;
ssl_GetSSL3HandshakeLock(ss);
+ processingEarlyData = ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
+
/* Without this, we may end up wrongly reporting
* SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
* peer while we are waiting to be restarted.
@@ -493,18 +489,12 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
* If it's a change cipher spec, alert, or handshake message,
* ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
*/
- if (ss->ssl3.hs.shortHeaders) {
- cText.type = content_application_data;
- cText.version = SSL_LIBRARY_VERSION_TLS_1_0;
- } else {
- cText.type = (SSL3ContentType)ss->gs.hdr[0];
- cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
- }
+ cText.type = (SSL3ContentType)ss->gs.hdr[0];
+ cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
if (IS_DTLS(ss)) {
sslSequenceNumber seq_num;
- cText.version = dtls_DTLSVersionToTLSVersion(cText.version);
/* DTLS sequence number */
PORT_Memcpy(&seq_num, &ss->gs.hdr[3], sizeof(seq_num));
cText.seq_num = PR_ntohll(seq_num);
@@ -555,12 +545,22 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
} else {
ss->ssl3.hs.canFalseStart = PR_FALSE;
}
+ } else if (processingEarlyData &&
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_done &&
+ !PR_CLIST_IS_EMPTY(&ss->ssl3.hs.bufferedEarlyData)) {
+ /* If we were processing early data and we are no longer, then force
+ * the handshake to block. This ensures that early data is
+ * delivered to the application before the handshake completes. */
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ PORT_SetError(PR_WOULD_BLOCK_ERROR);
+ return SECWouldBlock;
}
ssl_ReleaseSSL3HandshakeLock(ss);
} while (keepGoing);
- /* Service the DTLS timer so that the holddown timer eventually fires. */
- if (IS_DTLS(ss)) {
+ /* Service the DTLS timer so that the post-handshake timers
+ * fire. */
+ if (IS_DTLS(ss) && (ss->ssl3.hs.ws == idle_handshake)) {
dtls_CheckTimer(ss);
}
ss->gs.readOffset = 0;
diff --git a/security/nss/lib/ssl/ssl3prot.h b/security/nss/lib/ssl/ssl3prot.h
index ac31cf263..d1f46db97 100644
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -16,13 +16,12 @@ typedef PRUint16 SSL3ProtocolVersion;
/* The TLS 1.3 draft version. Used to avoid negotiating
* between incompatible pre-standard TLS 1.3 drafts.
* TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
-#define TLS_1_3_DRAFT_VERSION 18
+#define TLS_1_3_DRAFT_VERSION 23
typedef PRUint16 ssl3CipherSuite;
/* The cipher suites are defined in sslproto.h */
#define MAX_CERT_TYPES 10
-#define MAX_COMPRESSION_METHODS 10
#define MAX_MAC_LENGTH 64
#define MAX_PADDING_LENGTH 64
#define MAX_KEY_LENGTH 64
@@ -30,7 +29,6 @@ typedef PRUint16 ssl3CipherSuite;
#define SSL3_RANDOM_LENGTH 32
#define SSL3_RECORD_HEADER_LENGTH 5
-#define TLS13_RECORD_HEADER_LENGTH_SHORT 2
/* SSL3_RECORD_HEADER_LENGTH + epoch/sequence_number */
#define DTLS_RECORD_HEADER_LENGTH 13
@@ -41,47 +39,18 @@ typedef enum {
content_change_cipher_spec = 20,
content_alert = 21,
content_handshake = 22,
- content_application_data = 23
+ content_application_data = 23,
+ content_alt_handshake = 24,
+ content_ack = 25
} SSL3ContentType;
-typedef struct {
- SSL3ContentType type;
- SSL3ProtocolVersion version;
- PRUint16 length;
- SECItem fragment;
-} SSL3Plaintext;
-
-typedef struct {
- SSL3ContentType type;
- SSL3ProtocolVersion version;
- PRUint16 length;
- SECItem fragment;
-} SSL3Compressed;
-
-typedef struct {
- SECItem content;
- PRUint8 MAC[MAX_MAC_LENGTH];
-} SSL3GenericStreamCipher;
-
-typedef struct {
- SECItem content;
- PRUint8 MAC[MAX_MAC_LENGTH];
- PRUint8 padding[MAX_PADDING_LENGTH];
- PRUint8 padding_length;
-} SSL3GenericBlockCipher;
-
typedef enum { change_cipher_spec_choice = 1 } SSL3ChangeCipherSpecChoice;
-typedef struct {
- SSL3ChangeCipherSpecChoice choice;
-} SSL3ChangeCipherSpec;
-
typedef enum { alert_warning = 1,
alert_fatal = 2 } SSL3AlertLevel;
typedef enum {
close_notify = 0,
- end_of_early_data = 1, /* TLS 1.3 */
unexpected_message = 10,
bad_record_mac = 20,
decryption_failed_RESERVED = 21, /* do not send; see RFC 5246 */
@@ -122,64 +91,13 @@ typedef enum {
no_alert = 256
} SSL3AlertDescription;
-typedef struct {
- SSL3AlertLevel level;
- SSL3AlertDescription description;
-} SSL3Alert;
-
-typedef enum {
- hello_request = 0,
- client_hello = 1,
- server_hello = 2,
- hello_verify_request = 3,
- new_session_ticket = 4,
- hello_retry_request = 6,
- encrypted_extensions = 8,
- certificate = 11,
- server_key_exchange = 12,
- certificate_request = 13,
- server_hello_done = 14,
- certificate_verify = 15,
- client_key_exchange = 16,
- finished = 20,
- certificate_status = 22,
- next_proto = 67
-} SSL3HandshakeType;
-
-typedef struct {
- PRUint8 empty;
-} SSL3HelloRequest;
-
-typedef struct {
- PRUint8 rand[SSL3_RANDOM_LENGTH];
-} SSL3Random;
+typedef PRUint8 SSL3Random[SSL3_RANDOM_LENGTH];
typedef struct {
PRUint8 id[32];
PRUint8 length;
} SSL3SessionID;
-typedef struct {
- SSL3ProtocolVersion client_version;
- SSL3Random random;
- SSL3SessionID session_id;
- SECItem cipher_suites;
- PRUint8 cm_count;
- SSLCompressionMethod compression_methods[MAX_COMPRESSION_METHODS];
-} SSL3ClientHello;
-
-typedef struct {
- SSL3ProtocolVersion server_version;
- SSL3Random random;
- SSL3SessionID session_id;
- ssl3CipherSuite cipher_suite;
- SSLCompressionMethod compression_method;
-} SSL3ServerHello;
-
-typedef struct {
- SECItem list;
-} SSL3Certificate;
-
/* SSL3SignType moved to ssl.h */
/* The SSL key exchange method used */
@@ -201,24 +119,6 @@ typedef enum {
kea_tls13_any,
} SSL3KeyExchangeAlgorithm;
-typedef struct {
- SECItem modulus;
- SECItem exponent;
-} SSL3ServerRSAParams;
-
-typedef struct {
- SECItem p;
- SECItem g;
- SECItem Ys;
-} SSL3ServerDHParams;
-
-typedef struct {
- union {
- SSL3ServerDHParams dh;
- SSL3ServerRSAParams rsa;
- } u;
-} SSL3ServerParams;
-
/* SSL3HashesIndividually contains a combination MD5/SHA1 hash, as used in TLS
* prior to 1.2. */
typedef struct {
@@ -235,17 +135,9 @@ typedef struct {
union {
PRUint8 raw[64];
SSL3HashesIndividually s;
- unsigned int transcriptLen;
} u;
} SSL3Hashes;
-typedef struct {
- union {
- PRUint8 anonymous;
- SSL3Hashes certified;
- } u;
-} SSL3ServerKeyExchange;
-
typedef enum {
ct_RSA_sign = 1,
ct_DSS_sign = 2,
@@ -256,16 +148,8 @@ typedef enum {
ct_ECDSA_sign = 64,
ct_RSA_fixed_ECDH = 65,
ct_ECDSA_fixed_ECDH = 66
-
} SSL3ClientCertificateType;
-typedef struct {
- PRUint8 client_version[2];
- PRUint8 random[46];
-} SSL3RSAPreMasterSecret;
-
-typedef PRUint8 SSL3MasterSecret[48];
-
typedef enum {
sender_client = 0x434c4e54,
sender_server = 0x53525652
diff --git a/security/nss/lib/ssl/sslbloom.c b/security/nss/lib/ssl/sslbloom.c
new file mode 100644
index 000000000..3d5f9d1f1
--- /dev/null
+++ b/security/nss/lib/ssl/sslbloom.c
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * A bloom filter.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "sslbloom.h"
+#include "prnetdb.h"
+#include "secport.h"
+
+static inline unsigned int
+sslBloom_Size(unsigned int bits)
+{
+ return (bits >= 3) ? (1 << (bits - 3)) : 1;
+}
+
+SECStatus
+sslBloom_Init(sslBloomFilter *filter, unsigned int k, unsigned int bits)
+{
+ PORT_Assert(filter);
+ PORT_Assert(bits > 0);
+ PORT_Assert(bits <= sizeof(PRUint32) * 8);
+ PORT_Assert(k > 0);
+
+ filter->filter = PORT_ZNewArray(PRUint8, sslBloom_Size(bits));
+ if (!filter->filter) {
+ return SECFailure; /* Error code already set. */
+ }
+
+ filter->k = k;
+ filter->bits = bits;
+ return SECSuccess;
+}
+
+void
+sslBloom_Zero(sslBloomFilter *filter)
+{
+ PORT_Memset(filter->filter, 0, sslBloom_Size(filter->bits));
+}
+
+void
+sslBloom_Fill(sslBloomFilter *filter)
+{
+ PORT_Memset(filter->filter, 0xff, sslBloom_Size(filter->bits));
+}
+
+static PRBool
+sslBloom_AddOrCheck(sslBloomFilter *filter, const PRUint8 *hashes, PRBool add)
+{
+ unsigned int iteration;
+ unsigned int bitIndex;
+ PRUint32 tmp = 0;
+ PRUint8 mask;
+ unsigned int bytes = (filter->bits + 7) / 8;
+ unsigned int shift = (bytes * 8) - filter->bits;
+ PRBool found = PR_TRUE;
+
+ PORT_Assert(bytes <= sizeof(unsigned int));
+
+ for (iteration = 0; iteration < filter->k; ++iteration) {
+ PORT_Memcpy(((PRUint8 *)&tmp) + (sizeof(tmp) - bytes),
+ hashes, bytes);
+ hashes += bytes;
+ bitIndex = PR_ntohl(tmp) >> shift;
+
+ mask = 1 << (bitIndex % 8);
+ found = found && filter->filter[bitIndex / 8] & mask;
+ if (add) {
+ filter->filter[bitIndex / 8] |= mask;
+ }
+ }
+ return found;
+}
+
+PRBool
+sslBloom_Add(sslBloomFilter *filter, const PRUint8 *hashes)
+{
+ return sslBloom_AddOrCheck(filter, hashes, PR_TRUE);
+}
+
+PRBool
+sslBloom_Check(sslBloomFilter *filter, const PRUint8 *hashes)
+{
+ return sslBloom_AddOrCheck(filter, hashes, PR_FALSE);
+}
+
+void
+sslBloom_Destroy(sslBloomFilter *filter)
+{
+ PORT_Free(filter->filter);
+ PORT_Memset(filter, 0, sizeof(*filter));
+}
diff --git a/security/nss/lib/ssl/sslbloom.h b/security/nss/lib/ssl/sslbloom.h
new file mode 100644
index 000000000..032c94b0f
--- /dev/null
+++ b/security/nss/lib/ssl/sslbloom.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * A bloom filter.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __sslbloom_h_
+#define __sslbloom_h_
+
+#include "prtypes.h"
+#include "seccomon.h"
+
+typedef struct sslBloomFilterStr {
+ unsigned int k; /* The number of hashes. */
+ unsigned int bits; /* The number of bits in each hash: bits = log2(m) */
+ PRUint8 *filter; /* The filter itself. */
+} sslBloomFilter;
+
+SECStatus sslBloom_Init(sslBloomFilter *filter, unsigned int k, unsigned int bits);
+void sslBloom_Zero(sslBloomFilter *filter);
+void sslBloom_Fill(sslBloomFilter *filter);
+/* Add the given hashes to the filter. It's the caller's responsibility to
+ * ensure that there is at least |ceil(k*bits/8)| bytes of data available in
+ * |hashes|. Returns PR_TRUE if the entry was already present or it was likely
+ * to be present. */
+PRBool sslBloom_Add(sslBloomFilter *filter, const PRUint8 *hashes);
+PRBool sslBloom_Check(sslBloomFilter *filter, const PRUint8 *hashes);
+void sslBloom_Destroy(sslBloomFilter *filter);
+
+#endif /* __sslbloom_h_ */
diff --git a/security/nss/lib/ssl/sslcert.c b/security/nss/lib/ssl/sslcert.c
index cc1d3c683..6cd02e402 100644
--- a/security/nss/lib/ssl/sslcert.c
+++ b/security/nss/lib/ssl/sslcert.c
@@ -46,7 +46,7 @@ ssl_SetupCAListOnce(void *arg)
}
SECStatus
-ssl_SetupCAList(sslSocket *ss)
+ssl_SetupCAList(const sslSocket *ss)
{
if (PR_SUCCESS != PR_CallOnceWithArg(&ssl_server_ca_list.setup,
&ssl_SetupCAListOnce,
@@ -58,11 +58,11 @@ ssl_SetupCAList(sslSocket *ss)
}
SECStatus
-ssl_GetCertificateRequestCAs(sslSocket *ss, unsigned int *calen,
- SECItem **names, unsigned int *nnames)
+ssl_GetCertificateRequestCAs(const sslSocket *ss, unsigned int *calen,
+ const SECItem **names, unsigned int *nnames)
{
- SECItem *name;
- CERTDistNames *ca_list;
+ const SECItem *name;
+ const CERTDistNames *ca_list;
unsigned int i;
*calen = 0;
diff --git a/security/nss/lib/ssl/sslencode.c b/security/nss/lib/ssl/sslencode.c
new file mode 100644
index 000000000..2f127fe8f
--- /dev/null
+++ b/security/nss/lib/ssl/sslencode.c
@@ -0,0 +1,296 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nss.h"
+#include "prnetdb.h"
+#include "ssl.h"
+#include "sslimpl.h"
+
+/* Helper function to encode an unsigned integer into a buffer. */
+static void
+ssl_EncodeUintX(PRUint8 *to, PRUint64 value, unsigned int bytes)
+{
+ PRUint64 encoded;
+
+ PORT_Assert(bytes > 0 && bytes <= sizeof(encoded));
+
+ encoded = PR_htonll(value);
+ PORT_Memcpy(to, ((unsigned char *)(&encoded)) + (sizeof(encoded) - bytes),
+ bytes);
+}
+
+/* Grow a buffer to hold newLen bytes of data. When used for recv/xmit buffers,
+ * the caller must hold xmitBufLock or recvBufLock, as appropriate. */
+SECStatus
+sslBuffer_Grow(sslBuffer *b, unsigned int newLen)
+{
+ if (b->fixed) {
+ PORT_Assert(newLen <= b->space);
+ if (newLen > b->space) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+
+ newLen = PR_MAX(newLen, b->len + 1024);
+ if (newLen > b->space) {
+ unsigned char *newBuf;
+ if (b->buf) {
+ newBuf = (unsigned char *)PORT_Realloc(b->buf, newLen);
+ } else {
+ newBuf = (unsigned char *)PORT_Alloc(newLen);
+ }
+ if (!newBuf) {
+ return SECFailure;
+ }
+ b->buf = newBuf;
+ b->space = newLen;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+sslBuffer_Append(sslBuffer *b, const void *data, unsigned int len)
+{
+ SECStatus rv = sslBuffer_Grow(b, b->len + len);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Code already set. */
+ }
+ PORT_Memcpy(SSL_BUFFER_NEXT(b), data, len);
+ b->len += len;
+ return SECSuccess;
+}
+
+SECStatus
+sslBuffer_AppendNumber(sslBuffer *b, PRUint64 v, unsigned int size)
+{
+ SECStatus rv = sslBuffer_Grow(b, b->len + size);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ ssl_EncodeUintX(SSL_BUFFER_NEXT(b), v, size);
+ b->len += size;
+ return SECSuccess;
+}
+
+SECStatus
+sslBuffer_AppendVariable(sslBuffer *b, const PRUint8 *data, unsigned int len,
+ unsigned int size)
+{
+ PORT_Assert(size <= 4 && size > 0);
+ if (len >= (1ULL << (8 * size))) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if (sslBuffer_Grow(b, b->len + len + size) != SECSuccess) {
+ return SECFailure;
+ }
+
+ ssl_EncodeUintX(SSL_BUFFER_NEXT(b), len, size);
+ b->len += size;
+ PORT_Memcpy(SSL_BUFFER_NEXT(b), data, len);
+ b->len += len;
+ return SECSuccess;
+}
+
+SECStatus
+sslBuffer_AppendBuffer(sslBuffer *b, const sslBuffer *append)
+{
+ return sslBuffer_Append(b, append->buf, append->len);
+}
+
+SECStatus
+sslBuffer_AppendBufferVariable(sslBuffer *b, const sslBuffer *append,
+ unsigned int size)
+{
+ return sslBuffer_AppendVariable(b, append->buf, append->len, size);
+}
+
+SECStatus
+sslBuffer_Skip(sslBuffer *b, unsigned int size, unsigned int *savedOffset)
+{
+ if (sslBuffer_Grow(b, b->len + size) != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (savedOffset) {
+ *savedOffset = b->len;
+ }
+ b->len += size;
+ return SECSuccess;
+}
+
+/* A common problem is that a buffer is used to construct a variable length
+ * structure of unknown length. The length field for that structure is then
+ * populated afterwards. This function makes this process a little easier.
+ *
+ * To use this, before encoding the variable length structure, skip the spot
+ * where the length would be using sslBuffer_Skip(). After encoding the
+ * structure, and before encoding anything else, call this function passing the
+ * value returned from sslBuffer_Skip() as |at| to have the length inserted.
+ */
+SECStatus
+sslBuffer_InsertLength(sslBuffer *b, unsigned int at, unsigned int size)
+{
+ unsigned int len;
+
+ PORT_Assert(b->len >= at + size);
+ PORT_Assert(b->space >= at + size);
+ len = b->len - (at + size);
+
+ PORT_Assert(size <= 4 && size > 0);
+ if (len >= (1ULL << (8 * size))) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ ssl_EncodeUintX(SSL_BUFFER_BASE(b) + at, len, size);
+ return SECSuccess;
+}
+
+void
+sslBuffer_Clear(sslBuffer *b)
+{
+ if (!b->fixed) {
+ if (b->buf) {
+ PORT_Free(b->buf);
+ b->buf = NULL;
+ }
+ b->space = 0;
+ }
+ b->len = 0;
+}
+
+SECStatus
+ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, unsigned int size)
+{
+ if (size > item->len) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+
+ *buf = item->data;
+ item->data += size;
+ item->len -= size;
+ return SECSuccess;
+}
+
+SECStatus
+ssl3_ConsumeNumberFromItem(SECItem *item, PRUint32 *num, unsigned int size)
+{
+ int i;
+
+ if (size > item->len || size > sizeof(*num)) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+
+ *num = 0;
+ for (i = 0; i < size; i++) {
+ *num = (*num << 8) + item->data[i];
+ }
+
+ item->data += size;
+ item->len -= size;
+
+ return SECSuccess;
+}
+
+/**************************************************************************
+ * Append Handshake functions.
+ * All these functions set appropriate error codes.
+ * Most rely on ssl3_AppendHandshake to set the error code.
+ **************************************************************************/
+#define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
+#define MIN_SEND_BUF_LENGTH 4000
+
+SECStatus
+ssl3_AppendHandshake(sslSocket *ss, const void *void_src, unsigned int bytes)
+{
+ unsigned char *src = (unsigned char *)void_src;
+ int room = ss->sec.ci.sendBuf.space - ss->sec.ci.sendBuf.len;
+ SECStatus rv;
+
+ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); /* protects sendBuf. */
+
+ if (!bytes)
+ return SECSuccess;
+ if (ss->sec.ci.sendBuf.space < MAX_SEND_BUF_LENGTH && room < bytes) {
+ rv = sslBuffer_Grow(&ss->sec.ci.sendBuf, PR_MAX(MIN_SEND_BUF_LENGTH,
+ PR_MIN(MAX_SEND_BUF_LENGTH, ss->sec.ci.sendBuf.len + bytes)));
+ if (rv != SECSuccess)
+ return SECFailure; /* sslBuffer_Grow sets a memory error code. */
+ room = ss->sec.ci.sendBuf.space - ss->sec.ci.sendBuf.len;
+ }
+
+ PRINT_BUF(60, (ss, "Append to Handshake", (unsigned char *)void_src, bytes));
+ rv = ssl3_UpdateHandshakeHashes(ss, src, bytes);
+ if (rv != SECSuccess)
+ return SECFailure; /* error code set by ssl3_UpdateHandshakeHashes */
+
+ while (bytes > room) {
+ if (room > 0)
+ PORT_Memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, src,
+ room);
+ ss->sec.ci.sendBuf.len += room;
+ rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code set by ssl3_FlushHandshake */
+ }
+ bytes -= room;
+ src += room;
+ room = ss->sec.ci.sendBuf.space;
+ PORT_Assert(ss->sec.ci.sendBuf.len == 0);
+ }
+ PORT_Memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, src, bytes);
+ ss->sec.ci.sendBuf.len += bytes;
+ return SECSuccess;
+}
+
+SECStatus
+ssl3_AppendHandshakeNumber(sslSocket *ss, PRUint64 num, unsigned int lenSize)
+{
+ PRUint8 b[sizeof(num)];
+ SSL_TRC(60, ("%d: number:", SSL_GETPID()));
+ ssl_EncodeUintX(b, num, lenSize);
+ return ssl3_AppendHandshake(ss, b, lenSize);
+}
+
+SECStatus
+ssl3_AppendHandshakeVariable(sslSocket *ss, const PRUint8 *src,
+ unsigned int bytes, unsigned int lenSize)
+{
+ SECStatus rv;
+
+ PORT_Assert((bytes < (1 << 8) && lenSize == 1) ||
+ (bytes < (1L << 16) && lenSize == 2) ||
+ (bytes < (1L << 24) && lenSize == 3));
+
+ SSL_TRC(60, ("%d: append variable:", SSL_GETPID()));
+ rv = ssl3_AppendHandshakeNumber(ss, bytes, lenSize);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code set by AppendHandshake. */
+ }
+ SSL_TRC(60, ("data:"));
+ return ssl3_AppendHandshake(ss, src, bytes);
+}
+
+SECStatus
+ssl3_AppendBufferToHandshake(sslSocket *ss, sslBuffer *buf)
+{
+ return ssl3_AppendHandshake(ss, buf->buf, buf->len);
+}
+
+SECStatus
+ssl3_AppendBufferToHandshakeVariable(sslSocket *ss, sslBuffer *buf,
+ unsigned int lenSize)
+{
+ return ssl3_AppendHandshakeVariable(ss, buf->buf, buf->len, lenSize);
+}
diff --git a/security/nss/lib/ssl/sslencode.h b/security/nss/lib/ssl/sslencode.h
new file mode 100644
index 000000000..a1b04d88f
--- /dev/null
+++ b/security/nss/lib/ssl/sslencode.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __sslencode_h_
+#define __sslencode_h_
+
+/* A buffer object, used for assembling messages. */
+typedef struct sslBufferStr {
+ PRUint8 *buf;
+ unsigned int len;
+ unsigned int space;
+ /* Set to true if the storage for the buffer is fixed, such as a stack
+ * variable or a view on another buffer. Growing a fixed buffer fails. */
+ PRBool fixed;
+} sslBuffer;
+
+#define SSL_BUFFER_EMPTY \
+ { \
+ NULL, 0, 0, PR_FALSE \
+ }
+#define SSL_BUFFER_FIXED(b, maxlen) \
+ { \
+ b, 0, maxlen, PR_TRUE \
+ }
+#define SSL_BUFFER(b) SSL_BUFFER_FIXED(b, sizeof(b))
+#define SSL_BUFFER_BASE(b) ((b)->buf)
+#define SSL_BUFFER_LEN(b) ((b)->len)
+#define SSL_BUFFER_NEXT(b) ((b)->buf + (b)->len)
+#define SSL_BUFFER_SPACE(b) ((b)->space - (b)->len)
+
+SECStatus sslBuffer_Grow(sslBuffer *b, unsigned int newLen);
+SECStatus sslBuffer_Append(sslBuffer *b, const void *data, unsigned int len);
+SECStatus sslBuffer_AppendNumber(sslBuffer *b, PRUint64 v, unsigned int size);
+SECStatus sslBuffer_AppendVariable(sslBuffer *b, const PRUint8 *data,
+ unsigned int len, unsigned int size);
+SECStatus sslBuffer_AppendBuffer(sslBuffer *b, const sslBuffer *append);
+SECStatus sslBuffer_AppendBufferVariable(sslBuffer *b, const sslBuffer *append,
+ unsigned int size);
+SECStatus sslBuffer_Skip(sslBuffer *b, unsigned int size,
+ unsigned int *savedOffset);
+SECStatus sslBuffer_InsertLength(sslBuffer *b, unsigned int at,
+ unsigned int size);
+void sslBuffer_Clear(sslBuffer *b);
+
+/* All of these functions modify the underlying SECItem, and so should
+ * be performed on a shallow copy.*/
+SECStatus ssl3_ConsumeFromItem(SECItem *item,
+ PRUint8 **buf, unsigned int size);
+SECStatus ssl3_ConsumeNumberFromItem(SECItem *item,
+ PRUint32 *num, unsigned int size);
+
+SECStatus ssl3_AppendHandshake(sslSocket *ss, const void *void_src,
+ unsigned int bytes);
+SECStatus ssl3_AppendHandshakeHeader(sslSocket *ss,
+ SSLHandshakeType t, unsigned int length);
+SECStatus ssl3_AppendHandshakeNumber(sslSocket *ss, PRUint64 num,
+ unsigned int lenSize);
+SECStatus ssl3_AppendHandshakeVariable(sslSocket *ss, const PRUint8 *src,
+ unsigned int bytes, unsigned int lenSize);
+SECStatus ssl3_AppendBufferToHandshake(sslSocket *ss, sslBuffer *buf);
+SECStatus ssl3_AppendBufferToHandshakeVariable(sslSocket *ss, sslBuffer *buf,
+ unsigned int lenSize);
+
+#endif /* __sslencode_h_ */
diff --git a/security/nss/lib/ssl/sslerr.h b/security/nss/lib/ssl/sslerr.h
index 865077cda..90815dd79 100644
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -234,6 +234,7 @@ typedef enum {
SSL_ERROR_MALFORMED_PRE_SHARED_KEY = (SSL_ERROR_BASE + 147),
SSL_ERROR_MALFORMED_EARLY_DATA = (SSL_ERROR_BASE + 148),
SSL_ERROR_END_OF_EARLY_DATA_ALERT = (SSL_ERROR_BASE + 149),
+ /* error 149 is obsolete */
SSL_ERROR_MISSING_ALPN_EXTENSION = (SSL_ERROR_BASE + 150),
SSL_ERROR_RX_UNEXPECTED_EXTENSION = (SSL_ERROR_BASE + 151),
SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION = (SSL_ERROR_BASE + 152),
@@ -246,6 +247,19 @@ typedef enum {
SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES = (SSL_ERROR_BASE + 159),
SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA = (SSL_ERROR_BASE + 160),
SSL_ERROR_TOO_MUCH_EARLY_DATA = (SSL_ERROR_BASE + 161),
+ SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA = (SSL_ERROR_BASE + 162),
+ SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA = (SSL_ERROR_BASE + 163),
+
+ SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API = (SSL_ERROR_BASE + 164),
+
+ SSL_ERROR_APPLICATION_ABORT = (SSL_ERROR_BASE + 165),
+ SSL_ERROR_APP_CALLBACK_ERROR = (SSL_ERROR_BASE + 166),
+ SSL_ERROR_NO_TIMERS_FOUND = (SSL_ERROR_BASE + 167),
+ SSL_ERROR_MISSING_COOKIE_EXTENSION = (SSL_ERROR_BASE + 168),
+
+ SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE = (SSL_ERROR_BASE + 169),
+ SSL_ERROR_RX_MALFORMED_KEY_UPDATE = (SSL_ERROR_BASE + 170),
+ SSL_ERROR_TOO_MANY_KEY_UPDATES = (SSL_ERROR_BASE + 171),
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
diff --git a/security/nss/lib/ssl/sslexp.h b/security/nss/lib/ssl/sslexp.h
new file mode 100644
index 000000000..569add861
--- /dev/null
+++ b/security/nss/lib/ssl/sslexp.h
@@ -0,0 +1,358 @@
+/*
+ * This file contains prototypes for experimental SSL functions.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __sslexp_h_
+#define __sslexp_h_
+
+#include "ssl.h"
+#include "sslerr.h"
+
+SEC_BEGIN_PROTOS
+
+/* The functions in this header file are not guaranteed to remain available in
+ * future NSS versions. Code that uses these functions needs to safeguard
+ * against the function not being available. */
+
+#define SSL_EXPERIMENTAL_API(name, arglist, args) \
+ (SSL_GetExperimentalAPI(name) \
+ ? ((SECStatus(*) arglist)SSL_GetExperimentalAPI(name))args \
+ : SECFailure)
+#define SSL_DEPRECATED_EXPERIMENTAL_API \
+ (PR_SetError(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, 0), SECFailure)
+
+/*
+ * SSL_GetExtensionSupport() returns whether NSS supports a particular TLS
+ * extension.
+ *
+ * - ssl_ext_none indicates that NSS does not support the extension and
+ * extension hooks can be installed.
+ *
+ * - ssl_ext_native indicates that NSS supports the extension natively, but
+ * allows an application to override that support and install its own
+ * extension hooks.
+ *
+ * - ssl_ext_native_only indicates that NSS supports the extension natively
+ * and does not permit custom extension hooks to be installed. These
+ * extensions are critical to the functioning of NSS.
+ */
+typedef enum {
+ ssl_ext_none,
+ ssl_ext_native,
+ ssl_ext_native_only
+} SSLExtensionSupport;
+
+#define SSL_GetExtensionSupport(extension, support) \
+ SSL_EXPERIMENTAL_API("SSL_GetExtensionSupport", \
+ (PRUint16 _extension, \
+ SSLExtensionSupport * _support), \
+ (extension, support))
+
+/*
+ * Custom extension hooks.
+ *
+ * The SSL_InstallExtensionHooks() registers two callback functions for use
+ * with the identified extension type.
+ *
+ * Installing extension hooks disables the checks in TLS 1.3 that ensure that
+ * extensions are only added to the correct messages. The application is
+ * responsible for ensuring that extensions are only sent with the right message
+ * or messages.
+ *
+ * Installing an extension handler does not disable checks for whether an
+ * extension can be used in a message that is a response to an extension in
+ * another message. Extensions in ServerHello, EncryptedExtensions and the
+ * server Certificate messages are rejected unless the client sends an extension
+ * in the ClientHello. Similarly, a client Certificate message cannot contain
+ * extensions that don't appear in a CertificateRequest (in TLS 1.3).
+ *
+ * Setting both |writer| and |handler| to NULL removes any existing hooks for
+ * that extension.
+ *
+ * == SSLExtensionWriter
+ *
+ * An SSLExtensionWriter function is responsible for constructing the contents
+ * of an extension. This function is called during the construction of all
+ * handshake messages where an extension might be included.
+ *
+ * - The |fd| argument is the socket file descriptor.
+ *
+ * - The |message| argument is the TLS handshake message type. The writer will
+ * be called for every handshake message that NSS sends. Most extensions
+ * should only be sent in a subset of messages. NSS doesn’t check that
+ * extension writers don’t violate protocol rules regarding which message an
+ * extension can be sent in.
+ *
+ * - The |data| argument is a pointer to a buffer that should be written to with
+ * any data for the extension.
+ *
+ * - The |len| argument is an outparam indicating how many bytes were written to
+ * |data|. The value referenced by |len| is initialized to zero, so an
+ * extension that is empty does not need to write to this value.
+ *
+ * - The |maxLen| indicates the maximum number of bytes that can be written to
+ * |data|.
+ *
+ * - The |arg| argument is the value of the writerArg that was passed during
+ * installation.
+ *
+ * An SSLExtensionWriter function returns PR_TRUE if an extension should be
+ * written, and PR_FALSE otherwise.
+ *
+ * If there is an error, return PR_FALSE; if the error is truly fatal, the
+ * application can mark the connection as failed. However, recursively calling
+ * functions that alter the file descriptor in the callback - such as PR_Close()
+ * - should be avoided.
+ *
+ * Note: The ClientHello message can be sent twice in TLS 1.3. An
+ * SSLExtensionWriter will be called twice with the same arguments in that case;
+ * NSS does not distinguish between a first and second ClientHello. It is up to
+ * the application to track this if it needs to act differently each time. In
+ * most cases the correct behaviour is to provide an identical extension on each
+ * invocation.
+ *
+ * == SSLExtensionHandler
+ *
+ * An SSLExtensionHandler function consumes a handshake message. This function
+ * is called when an extension is present.
+ *
+ * - The |fd| argument is the socket file descriptor.
+ *
+ * - The |message| argument is the TLS handshake message type. This can be used
+ * to validate that the extension was included in the correct handshake
+ * message.
+ *
+ * - The |data| argument points to the contents of the extension.
+ *
+ * - The |len| argument contains the length of the extension.
+ *
+ * - The |alert| argument is an outparam that allows an application to choose
+ * which alert is sent in the case of a fatal error.
+ *
+ * - The |arg| argument is the value of the handlerArg that was passed during
+ * installation.
+ *
+ * An SSLExtensionHandler function returns SECSuccess when the extension is
+ * process successfully. It can return SECFailure to cause the handshake to
+ * fail. If the value of alert is written to, NSS will generate a fatal alert
+ * using the provided alert code. The value of |alert| is otherwise not used.
+ */
+typedef PRBool(PR_CALLBACK *SSLExtensionWriter)(
+ PRFileDesc *fd, SSLHandshakeType message,
+ PRUint8 *data, unsigned int *len, unsigned int maxLen, void *arg);
+
+typedef SECStatus(PR_CALLBACK *SSLExtensionHandler)(
+ PRFileDesc *fd, SSLHandshakeType message,
+ const PRUint8 *data, unsigned int len,
+ SSLAlertDescription *alert, void *arg);
+
+#define SSL_InstallExtensionHooks(fd, extension, writer, writerArg, \
+ handler, handlerArg) \
+ SSL_EXPERIMENTAL_API("SSL_InstallExtensionHooks", \
+ (PRFileDesc * _fd, PRUint16 _extension, \
+ SSLExtensionWriter _writer, void *_writerArg, \
+ SSLExtensionHandler _handler, void *_handlerArg), \
+ (fd, extension, writer, writerArg, \
+ handler, handlerArg))
+
+/*
+ * Setup the anti-replay buffer for supporting 0-RTT in TLS 1.3 on servers.
+ *
+ * To use 0-RTT on a server, you must call this function. Failing to call this
+ * function will result in all 0-RTT being rejected. Connections will complete,
+ * but early data will be rejected.
+ *
+ * NSS uses a Bloom filter to track the ClientHello messages that it receives
+ * (specifically, it uses the PSK binder). This function initializes a pair of
+ * Bloom filters. The two filters are alternated over time, with new
+ * ClientHello messages recorded in the current filter and, if they are not
+ * already present, being checked against the previous filter. If the
+ * ClientHello is found, then early data is rejected, but the handshake is
+ * allowed to proceed.
+ *
+ * The false-positive probability of Bloom filters means that some valid
+ * handshakes will be marked as potential replays. Early data will be rejected
+ * for a false positive. To minimize this and to allow a trade-off of space
+ * against accuracy, the size of the Bloom filter can be set by this function.
+ *
+ * The first tuning parameter to consider is |window|, which determines the
+ * window over which ClientHello messages will be tracked. This also causes
+ * early data to be rejected if a ClientHello contains a ticket age parameter
+ * that is outside of this window (see Section 4.2.10.4 of
+ * draft-ietf-tls-tls13-20 for details). Set |window| to account for any
+ * potential sources of clock error. |window| is the entire width of the
+ * window, which is symmetrical. Therefore to allow 5 seconds of clock error in
+ * both directions, set the value to 10 seconds (i.e., 10 * PR_USEC_PER_SEC).
+ *
+ * After calling this function, early data will be rejected until |window|
+ * elapses. This prevents replay across crashes and restarts. Only call this
+ * function once to avoid inadvertently disabling 0-RTT (use PR_CallOnce() to
+ * avoid this problem).
+ *
+ * The primary tuning parameter is |bits| which determines the amount of memory
+ * allocated to each Bloom filter. NSS will allocate two Bloom filters, each
+ * |2^(bits - 3)| octets in size. The value of |bits| is primarily driven by
+ * the number of connections that are expected in any time window. Note that
+ * this needs to account for there being two filters both of which have
+ * (presumably) independent false positive rates. The following formulae can be
+ * used to find a value of |bits| and |k| given a chosen false positive
+ * probability |p| and the number of requests expected in a given window |n|:
+ *
+ * bits = log2(n) + log2(-ln(1 - sqrt(1 - p))) + 1.0575327458897952
+ * k = -log2(p)
+ *
+ * ... where log2 and ln are base 2 and e logarithms respectively. For a target
+ * false positive rate of 1% and 1000 handshake attempts, this produces bits=14
+ * and k=7. This results in two Bloom filters that are 2kB each in size. Note
+ * that rounding |k| and |bits| up causes the false positive probability for
+ * these values to be a much lower 0.123%.
+ *
+ * IMPORTANT: This anti-replay scheme has several weaknesses. See the TLS 1.3
+ * specification for the details of the generic problems with this technique.
+ *
+ * In addition to the generic anti-replay weaknesses, the state that the server
+ * maintains is in local memory only. Servers that operate in a cluster, even
+ * those that use shared memory for tickets, will not share anti-replay state.
+ * Early data can be replayed at least once with every server instance that will
+ * accept tickets that are encrypted with the same key.
+ */
+#define SSL_SetupAntiReplay(window, k, bits) \
+ SSL_EXPERIMENTAL_API("SSL_SetupAntiReplay", \
+ (PRTime _window, unsigned int _k, unsigned int _bits), \
+ (window, k, bits))
+
+/*
+ * This function allows a server application to generate a session ticket that
+ * will embed the provided token.
+ *
+ * This function will cause a NewSessionTicket message to be sent by a server.
+ * This happens even if SSL_ENABLE_SESSION_TICKETS is disabled. This allows a
+ * server to suppress the usually automatic generation of a session ticket at
+ * the completion of the handshake - which do not include any token - and to
+ * control when session tickets are transmitted.
+ *
+ * This function will fail unless the socket has an active TLS 1.3 session.
+ * Earlier versions of TLS do not support the spontaneous sending of the
+ * NewSessionTicket message.
+ */
+#define SSL_SendSessionTicket(fd, appToken, appTokenLen) \
+ SSL_EXPERIMENTAL_API("SSL_SendSessionTicket", \
+ (PRFileDesc * _fd, const PRUint8 *_appToken, \
+ unsigned int _appTokenLen), \
+ (fd, appToken, appTokenLen))
+
+/*
+ * A stateless retry handler gives an application some control over NSS handling
+ * of ClientHello messages.
+ *
+ * SSL_HelloRetryRequestCallback() installs a callback that allows an
+ * application to control how NSS sends HelloRetryRequest messages. This
+ * handler is only used on servers and will only be called if the server selects
+ * TLS 1.3. Support for older TLS versions could be added in other releases.
+ *
+ * The SSLHelloRetryRequestCallback is invoked during the processing of a
+ * TLS 1.3 ClientHello message. It takes the following arguments:
+ *
+ * - |firstHello| indicates if the NSS believes that this is an initial
+ * ClientHello. An initial ClientHello will never include a cookie extension,
+ * though it may contain a session ticket.
+ *
+ * - |clientToken| includes a token previously provided by the application. If
+ * |clientTokenLen| is 0, then |clientToken| may be NULL.
+ *
+ * - If |firstHello| is PR_FALSE, the value that was provided in the
+ * |retryToken| outparam of previous invocations of this callback will be
+ * present here.
+ *
+ * - If |firstHello| is PR_TRUE, and the handshake is resuming a session, then
+ * this will contain any value that was passed in the |token| parameter of
+ * SSL_SendNewSessionTicket() method (see below). If this is not resuming a
+ * session, then the token will be empty (and this value could be NULL).
+ *
+ * - |clientTokenLen| is the length of |clientToken|.
+ *
+ * - |retryToken| is an item that callback can write to. This provides NSS with
+ * a token. This token is encrypted and integrity protected and embedded in
+ * the cookie extension of a HelloRetryRequest. The value of this field is
+ * only used if the handler returns ssl_stateless_retry_check. NSS allocates
+ * space for this value.
+ *
+ * - |retryTokenLen| is an outparam for the length of the token. If this value
+ * is not set, or set to 0, an empty token will be sent.
+ *
+ * - |retryTokenMax| is the size of the space allocated for retryToken. An
+ * application cannot write more than this many bytes to retryToken.
+ *
+ * - |arg| is the same value that was passed to
+ * SSL_InstallStatelessRetryHandler().
+ *
+ * The handler can validate any the value of |clientToken|, query the socket
+ * status (using SSL_GetPreliminaryChannelInfo() for example) and decide how to
+ * proceed:
+ *
+ * - Returning ssl_hello_retry_fail causes the handshake to fail. This might be
+ * used if the token is invalid or the application wishes to abort the
+ * handshake.
+ *
+ * - Returning ssl_hello_retry_accept causes the handshake to proceed.
+ *
+ * - Returning ssl_hello_retry_request causes NSS to send a HelloRetryRequest
+ * message and request a second ClientHello. NSS generates a cookie extension
+ * and embeds the value of |retryToken|. The value of |retryToken| value may
+ * be left empty if the application does not require any additional context to
+ * validate a second ClientHello attempt. This return code cannot be used to
+ * reject a second ClientHello (i.e., when firstHello is PR_FALSE); NSS will
+ * abort the handshake if this value is returned from a second call.
+ *
+ * An application that chooses to perform a stateless retry can discard the
+ * server socket. All necessary state to continue the TLS handshake will be
+ * included in the cookie extension. This makes it possible to use a new socket
+ * to handle the remainder of the handshake. The existing socket can be safely
+ * discarded.
+ *
+ * If the same socket is retained, the information in the cookie will be checked
+ * for consistency against the existing state of the socket. Any discrepancy
+ * will result in the connection being closed.
+ *
+ * Tokens should be kept as small as possible. NSS sets a limit on the size of
+ * tokens, which it passes in |retryTokenMax|. Depending on circumstances,
+ * observing a smaller limit might be desirable or even necessary. For
+ * instance, having HelloRetryRequest and ClientHello fit in a single packet has
+ * significant performance benefits.
+ */
+typedef enum {
+ ssl_hello_retry_fail,
+ ssl_hello_retry_accept,
+ ssl_hello_retry_request
+} SSLHelloRetryRequestAction;
+
+typedef SSLHelloRetryRequestAction(PR_CALLBACK *SSLHelloRetryRequestCallback)(
+ PRBool firstHello, const PRUint8 *clientToken, unsigned int clientTokenLen,
+ PRUint8 *retryToken, unsigned int *retryTokenLen, unsigned int retryTokMax,
+ void *arg);
+
+#define SSL_HelloRetryRequestCallback(fd, cb, arg) \
+ SSL_EXPERIMENTAL_API("SSL_HelloRetryRequestCallback", \
+ (PRFileDesc * _fd, \
+ SSLHelloRetryRequestCallback _cb, void *_arg), \
+ (fd, cb, arg))
+
+/* Update traffic keys (TLS 1.3 only).
+ *
+ * The |requestUpdate| flag determines whether to request an update from the
+ * remote peer.
+ */
+#define SSL_KeyUpdate(fd, requestUpdate) \
+ SSL_EXPERIMENTAL_API("SSL_KeyUpdate", \
+ (PRFileDesc * _fd, PRBool _requestUpdate), \
+ (fd, requestUpdate))
+
+#define SSL_UseAltServerHelloType(fd, enable) \
+ SSL_DEPRECATED_EXPERIMENTAL_API
+
+SEC_END_PROTOS
+
+#endif /* __sslexp_h_ */
diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h
index 64694b0df..dee9aa20f 100644
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -19,6 +19,7 @@
#include "secport.h"
#include "secerr.h"
#include "sslerr.h"
+#include "sslexp.h"
#include "ssl3prot.h"
#include "hasht.h"
#include "nssilock.h"
@@ -34,36 +35,11 @@
#include "sslt.h" /* for some formerly private types, now public */
typedef struct sslSocketStr sslSocket;
-typedef struct ssl3CipherSpecStr ssl3CipherSpec;
+typedef struct sslNamedGroupDefStr sslNamedGroupDef;
+#include "sslencode.h"
+#include "sslexp.h"
#include "ssl3ext.h"
-
-/* to make some of these old enums public without namespace pollution,
-** it was necessary to prepend ssl_ to the names.
-** These #defines preserve compatibility with the old code here in libssl.
-*/
-typedef SSLMACAlgorithm SSL3MACAlgorithm;
-
-#define calg_null ssl_calg_null
-#define calg_rc4 ssl_calg_rc4
-#define calg_rc2 ssl_calg_rc2
-#define calg_des ssl_calg_des
-#define calg_3des ssl_calg_3des
-#define calg_idea ssl_calg_idea
-#define calg_fortezza ssl_calg_fortezza /* deprecated, must preserve */
-#define calg_aes ssl_calg_aes
-#define calg_camellia ssl_calg_camellia
-#define calg_seed ssl_calg_seed
-#define calg_aes_gcm ssl_calg_aes_gcm
-#define calg_chacha20 ssl_calg_chacha20
-
-#define mac_null ssl_mac_null
-#define mac_md5 ssl_mac_md5
-#define mac_sha ssl_mac_sha
-#define hmac_md5 ssl_hmac_md5
-#define hmac_sha ssl_hmac_sha
-#define hmac_sha256 ssl_hmac_sha256
-#define hmac_sha384 ssl_hmac_sha384
-#define mac_aead ssl_mac_aead
+#include "sslspec.h"
#if defined(DEBUG) || defined(TRACE)
#ifdef __cplusplus
@@ -160,7 +136,7 @@ typedef enum {
ticket_allow_psk_sign_auth = 16
} TLS13SessionTicketFlags;
-typedef struct {
+struct sslNamedGroupDefStr {
/* The name is the value that is encoded on the wire in TLS. */
SSLNamedGroup name;
/* The number of bits in the group. */
@@ -172,9 +148,8 @@ typedef struct {
SECOidTag oidTag;
/* Assume that the group is always supported. */
PRBool assumeSupported;
-} sslNamedGroupDef;
+};
-typedef struct sslBufferStr sslBuffer;
typedef struct sslConnectInfoStr sslConnectInfo;
typedef struct sslGatherStr sslGather;
typedef struct sslSecurityInfoStr sslSecurityInfo;
@@ -183,8 +158,6 @@ typedef struct sslSocketOpsStr sslSocketOps;
typedef struct ssl3StateStr ssl3State;
typedef struct ssl3CertNodeStr ssl3CertNode;
-typedef struct ssl3BulkCipherDefStr ssl3BulkCipherDef;
-typedef struct ssl3MACDefStr ssl3MACDef;
typedef struct sslKeyPairStr sslKeyPair;
typedef struct ssl3DHParamsStr ssl3DHParams;
@@ -201,9 +174,6 @@ typedef sslSessionID *(*sslSessionIDLookupFunc)(const PRIPv6Addr *addr,
unsigned char *sid,
unsigned int sidLen,
CERTCertDBHandle *dbHandle);
-typedef void (*sslCipherSpecChangedFunc)(void *arg,
- PRBool sending,
- ssl3CipherSpec *newSpec);
/* Socket ops */
struct sslSocketOpsStr {
@@ -229,20 +199,9 @@ struct sslSocketOpsStr {
#define ssl_SEND_FLAG_FORCE_INTO_BUFFER 0x40000000
#define ssl_SEND_FLAG_NO_BUFFER 0x20000000
#define ssl_SEND_FLAG_NO_RETRANSMIT 0x08000000 /* DTLS only */
-#define ssl_SEND_FLAG_CAP_RECORD_VERSION \
- 0x04000000 /* TLS only */
#define ssl_SEND_FLAG_MASK 0x7f000000
/*
-** A buffer object.
-*/
-struct sslBufferStr {
- unsigned char *buf;
- unsigned int len;
- unsigned int space;
-};
-
-/*
** SSL3 cipher suite policy and preference struct.
*/
typedef struct {
@@ -282,7 +241,7 @@ typedef struct sslOptionsStr {
unsigned int detectRollBack : 1;
unsigned int noLocks : 1;
unsigned int enableSessionTickets : 1;
- unsigned int enableDeflate : 1;
+ unsigned int enableDeflate : 1; /* Deprecated. */
unsigned int enableRenegotiation : 2;
unsigned int requireSafeNegotiation : 1;
unsigned int enableFalseStart : 1;
@@ -297,7 +256,7 @@ typedef struct sslOptionsStr {
unsigned int enableSignedCertTimestamps : 1;
unsigned int requireDHENamedGroups : 1;
unsigned int enable0RttData : 1;
- unsigned int enableShortHeaders : 1;
+ unsigned int enableTls13CompatMode : 1;
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -382,136 +341,13 @@ struct sslGatherStr {
#define GS_HEADER 1
#define GS_DATA 2
-/*
-** ssl3State and CipherSpec structs
-*/
-
-/* The SSL bulk cipher definition */
-typedef enum {
- cipher_null,
- cipher_rc4,
- cipher_des,
- cipher_3des,
- cipher_aes_128,
- cipher_aes_256,
- cipher_camellia_128,
- cipher_camellia_256,
- cipher_seed,
- cipher_aes_128_gcm,
- cipher_aes_256_gcm,
- cipher_chacha20,
- cipher_missing /* reserved for no such supported cipher */
- /* This enum must match ssl3_cipherName[] in ssl3con.c. */
-} SSL3BulkCipher;
-
-typedef enum { type_stream,
- type_block,
- type_aead } CipherType;
-
-#define MAX_IV_LENGTH 24
-
-typedef PRUint64 sslSequenceNumber;
-typedef PRUint16 DTLSEpoch;
-
-typedef void (*DTLSTimerCb)(sslSocket *);
-
typedef struct {
PRUint8 wrapped_master_secret[48];
PRUint16 wrapped_master_secret_len;
- PRUint8 msIsWrapped;
PRUint8 resumable;
PRUint8 extendedMasterSecretUsed;
} ssl3SidKeys; /* 52 bytes */
-typedef struct {
- PK11SymKey *write_key;
- PK11SymKey *write_mac_key;
- PK11Context *write_mac_context;
- SECItem write_key_item;
- SECItem write_iv_item;
- SECItem write_mac_key_item;
- PRUint8 write_iv[MAX_IV_LENGTH];
-} ssl3KeyMaterial;
-
-typedef SECStatus (*SSLCipher)(void *context,
- unsigned char *out,
- int *outlen,
- int maxout,
- const unsigned char *in,
- int inlen);
-typedef SECStatus (*SSLAEADCipher)(
- ssl3KeyMaterial *keys,
- PRBool doDecrypt,
- unsigned char *out,
- int *outlen,
- int maxout,
- const unsigned char *in,
- int inlen,
- const unsigned char *additionalData,
- int additionalDataLen);
-typedef SECStatus (*SSLCompressor)(void *context,
- unsigned char *out,
- int *outlen,
- int maxout,
- const unsigned char *in,
- int inlen);
-typedef SECStatus (*SSLDestroy)(void *context, PRBool freeit);
-
-/* The DTLS anti-replay window in number of packets. Defined here because we
- * need it in the cipher spec. Note that this is a ring buffer but left and
- * right represent the true window, with modular arithmetic used to map them
- * onto the buffer.
- */
-#define DTLS_RECVD_RECORDS_WINDOW 1024
-#define RECORD_SEQ_MAX ((1ULL << 48) - 1)
-PR_STATIC_ASSERT(DTLS_RECVD_RECORDS_WINDOW % 8 == 0);
-
-typedef struct DTLSRecvdRecordsStr {
- unsigned char data[DTLS_RECVD_RECORDS_WINDOW / 8];
- sslSequenceNumber left;
- sslSequenceNumber right;
-} DTLSRecvdRecords;
-
-/*
-** These are the "specs" in the "ssl3" struct.
-** Access to the pointers to these specs, and all the specs' contents
-** (direct and indirect) is protected by the reader/writer lock ss->specLock.
-*/
-struct ssl3CipherSpecStr {
- PRCList link;
- const ssl3BulkCipherDef *cipher_def;
- const ssl3MACDef *mac_def;
- SSLCompressionMethod compression_method;
- int mac_size;
- SSLCipher encode;
- SSLCipher decode;
- SSLAEADCipher aead;
- void *encodeContext;
- void *decodeContext;
- SSLCompressor compressor; /* Don't name these fields compress */
- SSLCompressor decompressor; /* and uncompress because zconf.h */
- /* may define them as macros. */
- SSLDestroy destroyCompressContext;
- void *compressContext;
- SSLDestroy destroyDecompressContext;
- void *decompressContext;
- PK11SymKey *master_secret;
- sslSequenceNumber write_seq_num;
- sslSequenceNumber read_seq_num;
- SSL3ProtocolVersion version;
- ssl3KeyMaterial client;
- ssl3KeyMaterial server;
- SECItem msItem;
- DTLSEpoch epoch;
- DTLSRecvdRecords recvdRecords;
- /* The number of 0-RTT bytes that can be sent or received in TLS 1.3. This
- * will be zero for everything but 0-RTT. */
- PRUint32 earlyDataRemaining;
-
- PRUint8 refCt;
- const char *phase;
-};
-
typedef enum { never_cached,
in_client_cache,
in_server_cache,
@@ -527,7 +363,7 @@ struct sslSessionIDStr {
sslSessionID *next; /* chain used for client sockets, only */
Cached cached;
int references;
- PRUint32 lastAccessTime; /* seconds since Jan 1, 1970 */
+ PRTime lastAccessTime;
/* The rest of the members, except for the members of u.ssl3.locked, may
* be modified only when the sid is not in any cache.
@@ -545,13 +381,15 @@ struct sslSessionIDStr {
SSL3ProtocolVersion version;
- PRUint32 creationTime; /* seconds since Jan 1, 1970 */
- PRUint32 expirationTime; /* seconds since Jan 1, 1970 */
+ PRTime creationTime;
+ PRTime expirationTime;
SSLAuthType authType;
PRUint32 authKeyBits;
SSLKEAType keaType;
PRUint32 keaKeyBits;
+ SSLNamedGroup keaGroup;
+ SSLSignatureScheme sigScheme;
union {
struct {
@@ -560,7 +398,6 @@ struct sslSessionIDStr {
PRUint8 sessionID[SSL3_SESSIONID_BYTES];
ssl3CipherSuite cipherSuite;
- SSLCompressionMethod compression;
int policy;
ssl3SidKeys keys;
/* mechanism used to wrap master secret */
@@ -627,13 +464,13 @@ struct sslSessionIDStr {
} u;
};
-typedef struct ssl3CipherSuiteDefStr {
+struct ssl3CipherSuiteDefStr {
ssl3CipherSuite cipher_suite;
SSL3BulkCipher bulk_cipher_alg;
SSL3MACAlgorithm mac_alg;
SSL3KeyExchangeAlgorithm key_exchange_alg;
SSLHashType prf_hash;
-} ssl3CipherSuiteDef;
+};
/*
** There are tables of these, all const.
@@ -656,37 +493,6 @@ typedef struct {
SECOidTag oid;
} ssl3KEADef;
-/*
-** There are tables of these, all const.
-*/
-struct ssl3BulkCipherDefStr {
- SSL3BulkCipher cipher;
- SSLCipherAlgorithm calg;
- unsigned int key_size;
- unsigned int secret_key_size;
- CipherType type;
- unsigned int iv_size;
- unsigned int block_size;
- unsigned int tag_size; /* for AEAD ciphers. */
- unsigned int explicit_nonce_size; /* for AEAD ciphers. */
- SECOidTag oid;
- const char *short_name;
- /* The maximum number of records that can be sent/received with the same
- * symmetric key before the connection will be terminated. */
- PRUint64 max_records;
-};
-
-/*
-** There are tables of these, all const.
-*/
-struct ssl3MACDefStr {
- SSL3MACAlgorithm mac;
- CK_MECHANISM_TYPE mmech;
- int pad_size;
- int mac_size;
- SECOidTag oid;
-};
-
typedef enum {
ssl_0rtt_none, /* 0-RTT not present */
ssl_0rtt_sent, /* 0-RTT sent (no decision yet) */
@@ -704,6 +510,7 @@ typedef enum {
typedef enum {
idle_handshake,
wait_client_hello,
+ wait_end_of_early_data,
wait_client_cert,
wait_client_key,
wait_cert_verify,
@@ -760,14 +567,15 @@ typedef enum {
handshake_hash_record
} SSL3HandshakeHashType;
-/* This holds state for TLS 1.3 CertificateRequest handling. */
-typedef struct TLS13CertificateRequestStr {
- PLArenaPool *arena;
- SECItem context;
- SSLSignatureScheme *signatureSchemes;
- unsigned int signatureSchemeCount;
- CERTDistNames ca_list;
-} TLS13CertificateRequest;
+// A DTLS Timer.
+typedef void (*DTLSTimerCb)(sslSocket *);
+
+typedef struct {
+ const char *label;
+ DTLSTimerCb cb;
+ PRIntervalTime started;
+ PRUint32 timeout;
+} dtlsTimer;
/*
** This is the "hs" member of the "ssl3" struct.
@@ -791,13 +599,12 @@ typedef struct SSL3HandshakeStateStr {
const ssl3KEADef *kea_def;
ssl3CipherSuite cipher_suite;
const ssl3CipherSuiteDef *suite_def;
- SSLCompressionMethod compression;
sslBuffer msg_body; /* protected by recvBufLock */
/* partial handshake message from record layer */
unsigned int header_bytes;
/* number of bytes consumed from handshake */
/* message for message type and header length */
- SSL3HandshakeType msg_type;
+ SSLHandshakeType msg_type;
unsigned long msg_len;
PRBool isResuming; /* we are resuming (not used in TLS 1.3) */
PRBool sendingSCSV; /* instead of empty RI */
@@ -834,25 +641,25 @@ typedef struct SSL3HandshakeStateStr {
PRCList remoteExtensions; /* Parsed incoming extensions */
/* This group of values is used for DTLS */
- PRUint16 sendMessageSeq; /* The sending message sequence
+ PRUint16 sendMessageSeq; /* The sending message sequence
* number */
- PRCList lastMessageFlight; /* The last message flight we
+ PRCList lastMessageFlight; /* The last message flight we
* sent */
- PRUint16 maxMessageSent; /* The largest message we sent */
- PRUint16 recvMessageSeq; /* The receiving message sequence
+ PRUint16 maxMessageSent; /* The largest message we sent */
+ PRUint16 recvMessageSeq; /* The receiving message sequence
* number */
- sslBuffer recvdFragments; /* The fragments we have received in
+ sslBuffer recvdFragments; /* The fragments we have received in
* a bitmask */
- PRInt32 recvdHighWater; /* The high water mark for fragments
+ PRInt32 recvdHighWater; /* The high water mark for fragments
* received. -1 means no reassembly
* in progress. */
- SECItem cookie; /* The Hello(Retry|Verify)Request cookie. */
- PRIntervalTime rtTimerStarted; /* When the timer was started */
- DTLSTimerCb rtTimerCb; /* The function to call on expiry */
- PRUint32 rtTimeoutMs; /* The length of the current timeout
- * used for backoff (in ms) */
- PRUint32 rtRetries; /* The retry counter */
- SECItem srvVirtName; /* for server: name that was negotiated
+ SECItem cookie; /* The Hello(Retry|Verify)Request cookie. */
+ dtlsTimer timers[3]; /* Holder for timers. */
+ dtlsTimer *rtTimer; /* Retransmit timer. */
+ dtlsTimer *ackTimer; /* Ack timer (DTLS 1.3 only). */
+ dtlsTimer *hdTimer; /* Read cipher holddown timer (DLTS 1.3 only) */
+ PRUint32 rtRetries; /* The retry counter */
+ SECItem srvVirtName; /* for server: name that was negotiated
* with a client. For client - is
* always set to NULL.*/
@@ -869,22 +676,37 @@ typedef struct SSL3HandshakeStateStr {
PK11SymKey *serverTrafficSecret; /* traffic keys */
PK11SymKey *earlyExporterSecret; /* for 0-RTT exporters */
PK11SymKey *exporterSecret; /* for exporters */
- /* The certificate request from the server. */
- TLS13CertificateRequest *certificateRequest;
- PRCList cipherSpecs; /* The cipher specs in the sequence they
- * will be applied. */
- sslZeroRttState zeroRttState; /* Are we doing a 0-RTT handshake? */
- sslZeroRttIgnore zeroRttIgnore; /* Are we ignoring 0-RTT? */
- ssl3CipherSuite zeroRttSuite; /* The cipher suite we used for 0-RTT. */
- PRCList bufferedEarlyData; /* Buffered TLS 1.3 early data
- * on server.*/
- PRBool helloRetry; /* True if HelloRetryRequest has been sent
- * or received. */
- ssl3KEADef kea_def_mutable; /* Used to hold the writable kea_def
- * we use for TLS 1.3 */
- PRBool shortHeaders; /* Assigned if we are doing short headers. */
+ PRCList cipherSpecs; /* The cipher specs in the sequence they
+ * will be applied. */
+ sslZeroRttState zeroRttState; /* Are we doing a 0-RTT handshake? */
+ sslZeroRttIgnore zeroRttIgnore; /* Are we ignoring 0-RTT? */
+ ssl3CipherSuite zeroRttSuite; /* The cipher suite we used for 0-RTT. */
+ PRCList bufferedEarlyData; /* Buffered TLS 1.3 early data
+ * on server.*/
+ PRBool helloRetry; /* True if HelloRetryRequest has been sent
+ * or received. */
+ PRBool receivedCcs; /* A server received ChangeCipherSpec
+ * before the handshake started. */
+ PRBool clientCertRequested; /* True if CertificateRequest received. */
+ ssl3KEADef kea_def_mutable; /* Used to hold the writable kea_def
+ * we use for TLS 1.3 */
+ PRTime serverHelloTime; /* Time the ServerHello flight was sent. */
+ PRUint16 ticketNonce; /* A counter we use for tickets. */
+ SECItem fakeSid; /* ... (server) the SID the client used. */
+ PRBool endOfFlight; /* Processed a full flight (DTLS 1.3). */
+
+ /* The following lists contain DTLSHandshakeRecordEntry */
+ PRCList dtlsSentHandshake; /* Used to map records to handshake fragments. */
+ PRCList dtlsRcvdHandshake; /* Handshake records we have received
+ * used to generate ACKs. */
} SSL3HandshakeState;
+#define SSL_ASSERT_HASHES_EMPTY(ss) \
+ do { \
+ PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_unknown); \
+ PORT_Assert(ss->ssl3.hs.messages.len == 0); \
+ } while (0)
+
/*
** This is the "ssl3" struct, as in "ss->ssl3".
** note:
@@ -904,6 +726,10 @@ struct ssl3StateStr {
ssl3CipherSpec *cwSpec; /* current write spec. */
ssl3CipherSpec *pwSpec; /* pending write spec. */
+ /* This is true after the peer requests a key update; false after a key
+ * update is initiated locally. */
+ PRBool peerRequestedKeyUpdate;
+
/* Internal callback for when we do a cipher suite change. Used for
* debugging in TLS 1.3. This can only be set by non-public functions. */
sslCipherSpecChangedFunc changedCipherSpecFunc;
@@ -924,9 +750,7 @@ struct ssl3StateStr {
/* chain while we are trying to validate it. */
CERTDistNames *ca_list;
/* used by server. trusted CAs for this socket. */
- PRBool initialized;
SSL3HandshakeState hs;
- ssl3CipherSpec specs[2]; /* one is current, one is pending. */
PRUint16 mtu; /* Our estimate of the MTU */
@@ -995,11 +819,12 @@ typedef struct SessionTicketStr {
PRBool valid;
SSL3ProtocolVersion ssl_version;
ssl3CipherSuite cipher_suite;
- SSLCompressionMethod compression_method;
SSLAuthType authType;
PRUint32 authKeyBits;
SSLKEAType keaType;
PRUint32 keaKeyBits;
+ SSLNamedGroup originalKeaGroup;
+ SSLSignatureScheme signatureScheme;
const sslNamedGroupDef *namedCurve; /* For certificate lookup. */
/*
@@ -1012,11 +837,13 @@ typedef struct SessionTicketStr {
PRBool extendedMasterSecretUsed;
ClientAuthenticationType client_auth_type;
SECItem peer_cert;
- PRUint32 timestamp;
+ PRTime timestamp;
PRUint32 flags;
SECItem srvName; /* negotiated server name */
SECItem alpnSelection;
PRUint32 maxEarlyData;
+ PRUint32 ticketAgeBaseline;
+ SECItem applicationToken;
} SessionTicket;
/*
@@ -1066,6 +893,7 @@ struct sslSecurityInfoStr {
SSLKEAType keaType;
PRUint32 keaKeyBits;
const sslNamedGroupDef *keaGroup;
+ const sslNamedGroupDef *originalKeaGroup;
/* The selected certificate (for servers only). */
const sslServerCert *serverCert;
@@ -1151,6 +979,9 @@ struct sslSocketStr {
void *pkcs11PinArg;
SSLNextProtoCallback nextProtoCallback;
void *nextProtoArg;
+ SSLHelloRetryRequestCallback hrrCallback;
+ void *hrrCallbackArg;
+ PRCList extensionHooks;
PRIntervalTime rTimeout; /* timeout for NSPR I/O */
PRIntervalTime wTimeout; /* timeout for NSPR I/O */
@@ -1241,6 +1072,7 @@ extern char ssl_debug;
extern char ssl_trace;
extern FILE *ssl_trace_iob;
extern FILE *ssl_keylog_iob;
+extern PZLock *ssl_keylog_lock;
extern PRUint32 ssl3_sid_timeout;
extern PRUint32 ssl_ticket_lifetime;
extern PRUint32 ssl_max_early_data_size;
@@ -1331,14 +1163,10 @@ extern SECStatus ssl_BeginClientHandshake(sslSocket *ss);
extern SECStatus ssl_BeginServerHandshake(sslSocket *ss);
extern int ssl_Do1stHandshake(sslSocket *ss);
-extern SECStatus sslBuffer_Grow(sslBuffer *b, unsigned int newLen);
-extern SECStatus sslBuffer_Append(sslBuffer *b, const void *data,
- unsigned int len);
-extern void sslBuffer_Clear(sslBuffer *b);
-
extern void ssl_ChooseSessionIDProcs(sslSecurityInfo *sec);
-extern void ssl3_InitCipherSpec(ssl3CipherSpec *spec);
+extern SECStatus ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret,
+ PRBool derive);
extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server);
extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port,
const char *peerID, const char *urlSvrName);
@@ -1363,11 +1191,20 @@ extern SECStatus ssl_CipherPrefSetDefault(PRInt32 which, PRBool enabled);
extern SECStatus ssl3_ConstrainRangeByPolicy(void);
-extern void ssl3_InitState(sslSocket *ss);
+extern SECStatus ssl3_InitState(sslSocket *ss);
+extern SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
+ int maxOutputLen, const unsigned char *input,
+ int inputLen);
extern void ssl3_RestartHandshakeHashes(sslSocket *ss);
extern SECStatus ssl3_UpdateHandshakeHashes(sslSocket *ss,
const unsigned char *b,
unsigned int l);
+SECStatus
+ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type,
+ PRUint32 dtlsSeq,
+ const PRUint8 *b, PRUint32 length);
+SECStatus ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type,
+ const PRUint8 *b, PRUint32 length);
/* Returns PR_TRUE if we are still waiting for the server to complete its
* response to our client second round. Once we've received the Finished from
@@ -1380,21 +1217,14 @@ extern PRInt32 ssl3_SendRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
const PRUint8 *pIn, PRInt32 nIn,
PRInt32 flags);
-#ifdef NSS_SSL_ENABLE_ZLIB
-/*
- * The DEFLATE algorithm can result in an expansion of 0.1% + 12 bytes. For a
- * maximum TLS record payload of 2**14 bytes, that's 29 bytes.
- */
-#define SSL3_COMPRESSION_MAX_EXPANSION 29
-#else /* !NSS_SSL_ENABLE_ZLIB */
-#define SSL3_COMPRESSION_MAX_EXPANSION 0
-#endif
+/* Clear any PRCList, optionally calling f on the value. */
+void ssl_ClearPRCList(PRCList *list, void (*f)(void *));
/*
- * make sure there is room in the write buffer for padding and
- * other compression and cryptographic expansions.
+ * Make sure there is room in the write buffer for padding and
+ * cryptographic expansions.
*/
-#define SSL3_BUFFER_FUDGE 100 + SSL3_COMPRESSION_MAX_EXPANSION
+#define SSL3_BUFFER_FUDGE 100
#define SSL_LOCK_READER(ss) \
if (ss->recvLock) \
@@ -1547,7 +1377,7 @@ extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error);
* for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
*/
extern SECStatus ssl3_HandleV2ClientHello(
- sslSocket *ss, unsigned char *buffer, int length, PRUint8 padding);
+ sslSocket *ss, unsigned char *buffer, unsigned int length, PRUint8 padding);
SECStatus ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type);
@@ -1583,7 +1413,7 @@ extern PRBool ssl_HaveEphemeralKeyPair(const sslSocket *ss,
const sslNamedGroupDef *groupDef);
extern void ssl_FreeEphemeralKeyPairs(sslSocket *ss);
-extern SECStatus ssl_AppendPaddedDHKeyShare(const sslSocket *ss,
+extern SECStatus ssl_AppendPaddedDHKeyShare(sslBuffer *buf,
const SECKEYPublicKey *pubKey,
PRBool appendLength);
extern const ssl3DHParams *ssl_GetDHEParams(const sslNamedGroupDef *groupDef);
@@ -1645,6 +1475,10 @@ extern SECStatus ssl_ClientReadVersion(sslSocket *ss, PRUint8 **b,
extern SECStatus ssl3_NegotiateVersion(sslSocket *ss,
SSL3ProtocolVersion peerVersion,
PRBool allowLargerPeerVersion);
+extern SECStatus ssl_ClientSetCipherSuite(sslSocket *ss,
+ SSL3ProtocolVersion version,
+ ssl3CipherSuite suite,
+ PRBool initHashes);
extern SECStatus ssl_GetPeerInfo(sslSocket *ss);
@@ -1660,23 +1494,11 @@ extern SECStatus ssl3_SendECDHServerKeyExchange(sslSocket *ss);
extern SECStatus ssl_ImportECDHKeyShare(
sslSocket *ss, SECKEYPublicKey *peerKey,
PRUint8 *b, PRUint32 length, const sslNamedGroupDef *curve);
-SECStatus tls13_EncodeECDHEKeyShareKEX(const sslSocket *ss,
- const SECKEYPublicKey *pubKey);
extern SECStatus ssl3_ComputeCommonKeyHash(SSLHashType hashAlg,
PRUint8 *hashBuf,
unsigned int bufLen,
SSL3Hashes *hashes);
-extern void ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName);
-extern SECStatus ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms);
-extern SECStatus ssl3_AppendHandshake(sslSocket *ss, const void *void_src,
- PRInt32 bytes);
-extern SECStatus ssl3_AppendHandshakeHeader(sslSocket *ss,
- SSL3HandshakeType t, PRUint32 length);
-extern SECStatus ssl3_AppendHandshakeNumber(sslSocket *ss, PRInt32 num,
- PRInt32 lenSize);
-extern SECStatus ssl3_AppendHandshakeVariable(sslSocket *ss,
- const PRUint8 *src, PRInt32 bytes, PRInt32 lenSize);
extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(
sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes,
@@ -1684,11 +1506,12 @@ extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes,
extern SECStatus ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num,
PRUint32 bytes, PRUint8 **b,
PRUint32 *length);
+extern SECStatus ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num,
+ PRUint32 bytes, PRUint8 **b,
+ PRUint32 *length);
extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i,
PRUint32 bytes, PRUint8 **b,
PRUint32 *length);
-extern PRUint8 *ssl_EncodeUintX(PRUint64 value, unsigned int bytes,
- PRUint8 *to);
extern PRBool ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme);
extern SECStatus ssl_CheckSignatureSchemeConsistency(
sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert);
@@ -1703,16 +1526,20 @@ extern SECStatus ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash,
SECKEYPrivateKey *key, SECItem *buf);
extern SECStatus ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme,
SSL3Hashes *hash, SECItem *buf);
-extern SECStatus ssl3_CacheWrappedMasterSecret(
- sslSocket *ss, sslSessionID *sid, ssl3CipherSpec *spec);
+extern SECStatus ssl3_CacheWrappedSecret(sslSocket *ss, sslSessionID *sid,
+ PK11SymKey *secret);
extern void ssl3_FreeSniNameArray(TLSExtensionData *xtnData);
/* Hello Extension related routines. */
extern void ssl3_SetSIDSessionTicket(sslSessionID *sid,
/*in/out*/ NewSessionTicket *session_ticket);
SECStatus ssl3_EncodeSessionTicket(sslSocket *ss,
- const NewSessionTicket *ticket_input,
- SECItem *ticket_data);
+ const NewSessionTicket *ticket,
+ const PRUint8 *appToken,
+ unsigned int appTokenLen,
+ PK11SymKey *secret, SECItem *ticket_data);
+SECStatus SSLExp_SendSessionTicket(PRFileDesc *fd, const PRUint8 *token,
+ unsigned int tokenLen);
SECStatus ssl_MaybeSetSelfEncryptKeyPair(const sslKeyPair *keyPair);
SECStatus ssl_GetSelfEncryptKeys(sslSocket *ss, unsigned char *keyName,
@@ -1728,7 +1555,7 @@ extern void ssl_FreePRSocket(PRFileDesc *fd);
/* Internal config function so SSL3 can initialize the present state of
* various ciphers */
-extern int ssl3_config_match_init(sslSocket *);
+extern unsigned int ssl3_config_match_init(sslSocket *);
/* calls for accessing wrapping keys across processes. */
extern SECStatus
@@ -1758,44 +1585,11 @@ extern SECStatus ssl_InitSessionCacheLocks(PRBool lazyInit);
extern SECStatus ssl_FreeSessionCacheLocks(void);
-/**************** DTLS-specific functions **************/
-extern void dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg);
-extern void dtls_FreeHandshakeMessages(PRCList *lst);
-
-extern SECStatus dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf);
-extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss,
- PRUint8 *b, PRUint32 length);
-extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss);
-extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
- const PRUint8 *pIn, PRInt32 nIn);
-extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
-SECStatus ssl3_DisableNonDTLSSuites(sslSocket *ss);
-extern SECStatus dtls_StartHolddownTimer(sslSocket *ss);
-extern void dtls_CheckTimer(sslSocket *ss);
-extern void dtls_CancelTimer(sslSocket *ss);
-extern void dtls_SetMTU(sslSocket *ss, PRUint16 advertised);
-extern void dtls_InitRecvdRecords(DTLSRecvdRecords *records);
-extern int dtls_RecordGetRecvd(const DTLSRecvdRecords *records,
- sslSequenceNumber seq);
-extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records,
- sslSequenceNumber seq);
-extern void dtls_RehandshakeCleanup(sslSocket *ss);
-extern SSL3ProtocolVersion
-dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv);
-extern SSL3ProtocolVersion
-dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
-extern PRBool dtls_IsRelevant(sslSocket *ss, const SSL3Ciphertext *cText,
- PRBool *sameEpoch, PRUint64 *seqNum);
-extern SECStatus dtls_MaybeRetransmitHandshake(sslSocket *ss,
- const SSL3Ciphertext *cText,
- PRBool sameEpoch);
-
CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
PRBool initHashes);
SECStatus ssl3_InitHandshakeHashes(sslSocket *ss);
SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
-SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss);
SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
SECStatus ssl3_CompleteHandleCertificate(sslSocket *ss,
PRUint8 *b, PRUint32 length);
@@ -1807,17 +1601,21 @@ SECStatus ssl3_SendCertificateStatus(sslSocket *ss);
SECStatus ssl3_AuthCertificate(sslSocket *ss);
SECStatus ssl_ReadCertificateStatus(sslSocket *ss, PRUint8 *b,
PRUint32 length);
-SECStatus ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint8 *buf,
- unsigned maxLen, PRUint32 *len);
-SECStatus ssl_GetCertificateRequestCAs(sslSocket *ss, unsigned int *calenp,
- SECItem **namesp, unsigned int *nnamesp);
+SECStatus ssl3_EncodeSigAlgs(const sslSocket *ss, sslBuffer *buf);
+SECStatus ssl_GetCertificateRequestCAs(const sslSocket *ss,
+ unsigned int *calenp,
+ const SECItem **namesp,
+ unsigned int *nnamesp);
SECStatus ssl3_ParseCertificateRequestCAs(sslSocket *ss, PRUint8 **b,
- PRUint32 *length, PLArenaPool *arena,
- CERTDistNames *ca_list);
+ PRUint32 *length, CERTDistNames *ca_list);
SECStatus ssl3_CompleteHandleCertificateRequest(
sslSocket *ss, const SSLSignatureScheme *signatureSchemes,
unsigned int signatureSchemeCount, CERTDistNames *ca_list);
+SECStatus ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry,
+ const sslBuffer *extensionBuf,
+ sslBuffer *messageBuf);
SECStatus ssl3_SendServerHello(sslSocket *ss);
+SECStatus ssl3_SendChangeCipherSpecsInt(sslSocket *ss);
SECStatus ssl3_ComputeHandshakeHashes(sslSocket *ss,
ssl3CipherSpec *spec,
SSL3Hashes *hashes,
@@ -1832,10 +1630,9 @@ PK11SymKey *ssl3_GetWrappingKey(sslSocket *ss,
PK11SlotInfo *masterSecretSlot,
CK_MECHANISM_TYPE masterWrapMech,
void *pwArg);
-SECStatus ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid);
+SECStatus ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid,
+ PK11SymKey *secret);
const ssl3CipherSuiteDef *ssl_LookupCipherSuiteDef(ssl3CipherSuite suite);
-const ssl3BulkCipherDef *
-ssl_GetBulkCipherDef(const ssl3CipherSuiteDef *cipher_def);
SECStatus ssl3_SelectServerCert(sslSocket *ss);
SECStatus ssl_PickSignatureScheme(sslSocket *ss,
SECKEYPublicKey *pubKey,
@@ -1847,11 +1644,14 @@ SECOidTag ssl3_HashTypeToOID(SSLHashType hashType);
SSLHashType ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme);
KeyType ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme);
-SECStatus ssl3_SetCipherSuite(sslSocket *ss, ssl3CipherSuite chosenSuite,
- PRBool initHashes);
+SECStatus ssl3_SetupCipherSuite(sslSocket *ss, PRBool initHashes);
+
+/* Pull in DTLS functions */
+#include "dtlscon.h"
/* Pull in TLS 1.3 functions */
#include "tls13con.h"
+#include "dtls13con.h"
/********************** misc calls *********************/
@@ -1861,22 +1661,27 @@ extern void ssl3_CheckCipherSuiteOrderConsistency();
extern int ssl_MapLowLevelError(int hiLevelError);
-extern PRUint32 ssl_Time(void);
+extern PRUint32 ssl_TimeSec(void);
+#ifdef UNSAFE_FUZZER_MODE
+#define ssl_TimeUsec() ((PRTime)12345678)
+#else
+#define ssl_TimeUsec() (PR_Now())
+#endif
extern PRBool ssl_TicketTimeValid(const NewSessionTicket *ticket);
extern void SSL_AtomicIncrementLong(long *x);
SECStatus ssl3_ApplyNSSPolicy(void);
-extern HASH_HashType
-ssl3_GetTls12HashType(sslSocket *ss);
-
extern SECStatus
ssl3_TLSPRFWithMasterSecret(sslSocket *ss, ssl3CipherSpec *spec,
const char *label, unsigned int labelLen,
const unsigned char *val, unsigned int valLen,
unsigned char *out, unsigned int outLen);
+extern void
+ssl3_RecordKeyLog(sslSocket *ss, const char *label, PK11SymKey *secret);
+
PRBool ssl_AlpnTagAllowed(const sslSocket *ss, const SECItem *tag);
#ifdef TRACE
diff --git a/security/nss/lib/ssl/sslinfo.c b/security/nss/lib/ssl/sslinfo.c
index 88162d814..4e58c5ae7 100644
--- a/security/nss/lib/ssl/sslinfo.c
+++ b/security/nss/lib/ssl/sslinfo.c
@@ -2,26 +2,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/. */
+#include "pk11pub.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "tls13hkdf.h"
-static const char *
-ssl_GetCompressionMethodName(SSLCompressionMethod compression)
-{
- switch (compression) {
- case ssl_compression_null:
- return "NULL";
-#ifdef NSS_ENABLE_ZLIB
- case ssl_compression_deflate:
- return "DEFLATE";
-#endif
- default:
- return "???";
- }
-}
-
SECStatus
SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
{
@@ -48,48 +34,58 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
inf.length = PR_MIN(sizeof inf, len);
if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
+ SSLCipherSuiteInfo cinfo;
+ SECStatus rv;
+
sid = ss->sec.ci.sid;
inf.protocolVersion = ss->version;
inf.authKeyBits = ss->sec.authKeyBits;
inf.keaKeyBits = ss->sec.keaKeyBits;
- if (ss->ssl3.initialized) {
- SSLCipherSuiteInfo cinfo;
- SECStatus rv;
-
- ssl_GetSpecReadLock(ss);
- /* XXX The cipher suite should be in the specs and this
- * function should get it from cwSpec rather than from the "hs".
- * See bug 275744 comment 69 and bug 766137.
- */
- inf.cipherSuite = ss->ssl3.hs.cipher_suite;
- inf.compressionMethod = ss->ssl3.cwSpec->compression_method;
- ssl_ReleaseSpecReadLock(ss);
- inf.compressionMethodName =
- ssl_GetCompressionMethodName(inf.compressionMethod);
-
- /* Fill in the cipher details from the cipher suite. */
- rv = SSL_GetCipherSuiteInfo(inf.cipherSuite,
- &cinfo, sizeof(cinfo));
- if (rv != SECSuccess) {
- return SECFailure; /* Error code already set. */
- }
- inf.symCipher = cinfo.symCipher;
- inf.macAlgorithm = cinfo.macAlgorithm;
- /* Get these fromm |ss->sec| because that is accurate
- * even with TLS 1.3 disaggregated cipher suites. */
- inf.keaType = ss->sec.keaType;
- inf.keaGroup = ss->sec.keaGroup ? ss->sec.keaGroup->name : ssl_grp_none;
- inf.keaKeyBits = ss->sec.keaKeyBits;
- inf.authType = ss->sec.authType;
- inf.authKeyBits = ss->sec.authKeyBits;
- inf.signatureScheme = ss->sec.signatureScheme;
+
+ ssl_GetSpecReadLock(ss);
+ /* XXX The cipher suite should be in the specs and this
+ * function should get it from cwSpec rather than from the "hs".
+ * See bug 275744 comment 69 and bug 766137.
+ */
+ inf.cipherSuite = ss->ssl3.hs.cipher_suite;
+ ssl_ReleaseSpecReadLock(ss);
+ inf.compressionMethod = ssl_compression_null;
+ inf.compressionMethodName = "NULL";
+
+ /* Fill in the cipher details from the cipher suite. */
+ rv = SSL_GetCipherSuiteInfo(inf.cipherSuite,
+ &cinfo, sizeof(cinfo));
+ if (rv != SECSuccess) {
+ return SECFailure; /* Error code already set. */
+ }
+ inf.symCipher = cinfo.symCipher;
+ inf.macAlgorithm = cinfo.macAlgorithm;
+ /* Get these fromm |ss->sec| because that is accurate
+ * even with TLS 1.3 disaggregated cipher suites. */
+ inf.keaType = ss->sec.keaType;
+ inf.originalKeaGroup = ss->sec.originalKeaGroup
+ ? ss->sec.originalKeaGroup->name
+ : ssl_grp_none;
+ inf.keaGroup = ss->sec.keaGroup
+ ? ss->sec.keaGroup->name
+ : ssl_grp_none;
+ inf.keaKeyBits = ss->sec.keaKeyBits;
+ inf.authType = ss->sec.authType;
+ inf.authKeyBits = ss->sec.authKeyBits;
+ inf.signatureScheme = ss->sec.signatureScheme;
+ /* If this is a resumed session, signatureScheme isn't set in ss->sec.
+ * Use the signature scheme from the previous handshake. */
+ if (inf.signatureScheme == ssl_sig_none && sid->sigScheme) {
+ inf.signatureScheme = sid->sigScheme;
}
+ inf.resumed = ss->statelessResume || ss->ssl3.hs.isResuming;
+
if (sid) {
unsigned int sidLen;
- inf.creationTime = sid->creationTime;
- inf.lastAccessTime = sid->lastAccessTime;
- inf.expirationTime = sid->expirationTime;
+ inf.creationTime = sid->creationTime / PR_USEC_PER_SEC;
+ inf.lastAccessTime = sid->lastAccessTime / PR_USEC_PER_SEC;
+ inf.expirationTime = sid->expirationTime / PR_USEC_PER_SEC;
inf.extendedMasterSecretUsed =
(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ||
sid->u.ssl3.keys.extendedMasterSecretUsed)
@@ -196,17 +192,17 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
#define K_ANY "TLS 1.3", ssl_kea_tls13_any
/* record protection cipher */
-#define C_SEED "SEED", calg_seed
-#define C_CAMELLIA "CAMELLIA", calg_camellia
-#define C_AES "AES", calg_aes
-#define C_RC4 "RC4", calg_rc4
-#define C_RC2 "RC2", calg_rc2
-#define C_DES "DES", calg_des
-#define C_3DES "3DES", calg_3des
-#define C_NULL "NULL", calg_null
-#define C_SJ "SKIPJACK", calg_sj
-#define C_AESGCM "AES-GCM", calg_aes_gcm
-#define C_CHACHA20 "CHACHA20POLY1305", calg_chacha20
+#define C_SEED "SEED", ssl_calg_seed
+#define C_CAMELLIA "CAMELLIA", ssl_calg_camellia
+#define C_AES "AES", ssl_calg_aes
+#define C_RC4 "RC4", ssl_calg_rc4
+#define C_RC2 "RC2", ssl_calg_rc2
+#define C_DES "DES", ssl_calg_des
+#define C_3DES "3DES", ssl_calg_3des
+#define C_NULL "NULL", ssl_calg_null
+#define C_SJ "SKIPJACK", ssl_calg_sj
+#define C_AESGCM "AES-GCM", ssl_calg_aes_gcm
+#define C_CHACHA20 "CHACHA20POLY1305", ssl_calg_chacha20
/* "block cipher" sizes */
#define B_256 256, 256, 256
@@ -367,8 +363,7 @@ SSL_GetNegotiatedHostInfo(PRFileDesc *fd)
}
if (ss->sec.isServer) {
- if (ss->version > SSL_LIBRARY_VERSION_3_0 &&
- ss->ssl3.initialized) { /* TLS */
+ if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* TLS */
SECItem *crsName;
ssl_GetSpecReadLock(ss); /*********************************/
crsName = &ss->ssl3.hs.srvVirtName;
@@ -392,22 +387,47 @@ SSL_GetNegotiatedHostInfo(PRFileDesc *fd)
return sniName;
}
+/*
+ * HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
+ * "exporter", Hash(context_value), key_length)
+ */
static SECStatus
tls13_Exporter(sslSocket *ss, PK11SymKey *secret,
const char *label, unsigned int labelLen,
const unsigned char *context, unsigned int contextLen,
unsigned char *out, unsigned int outLen)
{
+ SSL3Hashes contextHash;
+ PK11SymKey *innerSecret = NULL;
+ SECStatus rv;
+
+ static const char *kExporterInnerLabel = "exporter";
+
if (!secret) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- return tls13_HkdfExpandLabelRaw(secret,
- tls13_GetHash(ss),
- context, contextLen,
- label, labelLen,
- out, outLen);
+ /* Pre-hash the context. */
+ rv = tls13_ComputeHash(ss, &contextHash, context, contextLen);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ rv = tls13_DeriveSecretNullHash(ss, secret, label, labelLen,
+ &innerSecret);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ rv = tls13_HkdfExpandLabelRaw(innerSecret,
+ tls13_GetHash(ss),
+ contextHash.u.raw, contextHash.len,
+ kExporterInnerLabel,
+ strlen(kExporterInnerLabel),
+ out, outLen);
+ PK11_FreeSymKey(innerSecret);
+ return rv;
}
SECStatus
@@ -457,9 +477,9 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
return SECFailure;
}
i = 0;
- PORT_Memcpy(val + i, &ss->ssl3.hs.client_random.rand, SSL3_RANDOM_LENGTH);
+ PORT_Memcpy(val + i, ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH);
i += SSL3_RANDOM_LENGTH;
- PORT_Memcpy(val + i, &ss->ssl3.hs.server_random.rand, SSL3_RANDOM_LENGTH);
+ PORT_Memcpy(val + i, ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH);
i += SSL3_RANDOM_LENGTH;
if (hasContext) {
val[i++] = contextLen >> 8;
@@ -473,7 +493,7 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
* secret is available and we have sent ChangeCipherSpec.
*/
ssl_GetSpecReadLock(ss);
- if (!ss->ssl3.cwSpec->master_secret && !ss->ssl3.cwSpec->msItem.len) {
+ if (!ss->ssl3.cwSpec->masterSecret) {
PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
rv = SECFailure;
} else {
diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c
index 7ad1c6bc7..228834e3d 100644
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -256,7 +256,7 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
if (!urlSvrName)
return NULL;
- now = ssl_Time();
+ now = ssl_TimeSec();
LOCK_CACHE;
sidp = &cache;
while ((sid = *sidp) != 0) {
@@ -306,8 +306,6 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
static void
CacheSID(sslSessionID *sid)
{
- PRUint32 expirationPeriod;
-
PORT_Assert(sid->cached == never_cached);
SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
@@ -335,7 +333,6 @@ CacheSID(sslSessionID *sid)
return;
sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
}
- expirationPeriod = ssl3_sid_timeout;
PRINT_BUF(8, (0, "sessionID:",
sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
@@ -345,9 +342,9 @@ CacheSID(sslSessionID *sid)
}
PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
if (!sid->creationTime)
- sid->lastAccessTime = sid->creationTime = ssl_Time();
+ sid->lastAccessTime = sid->creationTime = ssl_TimeUsec();
if (!sid->expirationTime)
- sid->expirationTime = sid->creationTime + expirationPeriod;
+ sid->expirationTime = sid->creationTime + ssl3_sid_timeout * PR_USEC_PER_SEC;
/*
* Put sid into the cache. Bump reference count to indicate that
@@ -438,7 +435,7 @@ SSL_ClearSessionCache(void)
/* returns an unsigned int containing the number of seconds in PR_Now() */
PRUint32
-ssl_Time(void)
+ssl_TimeSec(void)
{
#ifdef UNSAFE_FUZZER_MODE
return 1234;
@@ -471,7 +468,7 @@ ssl_TicketTimeValid(const NewSessionTicket *ticket)
endTime = ticket->received_timestamp +
(PRTime)(ticket->ticket_lifetime_hint * PR_USEC_PER_SEC);
- return endTime > PR_Now();
+ return endTime > ssl_TimeUsec();
}
void
diff --git a/security/nss/lib/ssl/sslreveal.c b/security/nss/lib/ssl/sslreveal.c
index 4c124a1dc..cc16f574d 100644
--- a/security/nss/lib/ssl/sslreveal.c
+++ b/security/nss/lib/ssl/sslreveal.c
@@ -92,18 +92,16 @@ SSL_HandshakeNegotiatedExtension(PRFileDesc *socket,
/* according to public API SSL_GetChannelInfo, this doesn't need a lock */
if (sslsocket->opt.useSecurity) {
- if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */
- /* now we know this socket went through ssl3_InitState() and
- * ss->xtnData got initialized, which is the only member accessed by
- * ssl3_ExtensionNegotiated();
- * Member xtnData appears to get accessed in functions that handle
- * the handshake (hello messages and extension sending),
- * therefore the handshake lock should be sufficient.
- */
- ssl_GetSSL3HandshakeLock(sslsocket);
- *pYes = ssl3_ExtensionNegotiated(sslsocket, extId);
- ssl_ReleaseSSL3HandshakeLock(sslsocket);
- }
+ /* now we know this socket went through ssl3_InitState() and
+ * ss->xtnData got initialized, which is the only member accessed by
+ * ssl3_ExtensionNegotiated();
+ * Member xtnData appears to get accessed in functions that handle
+ * the handshake (hello messages and extension sending),
+ * therefore the handshake lock should be sufficient.
+ */
+ ssl_GetSSL3HandshakeLock(sslsocket);
+ *pYes = ssl3_ExtensionNegotiated(sslsocket, extId);
+ ssl_ReleaseSSL3HandshakeLock(sslsocket);
}
return SECSuccess;
diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c
index 8bec3d327..3f7060f22 100644
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Various SSL functions.
*
@@ -200,7 +201,7 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer)
ssl_Release1stHandshakeLock(ss);
ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
- ssl3_ResetExtensionData(&ss->xtnData);
+ ssl3_ResetExtensionData(&ss->xtnData, ss);
if (!ss->TCPconnected)
ss->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ss, &addr));
@@ -342,11 +343,6 @@ SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
return SECFailure;
}
- if (!ss->ssl3.initialized) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
-
/* Require a forward-secret key exchange. */
*canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
@@ -435,58 +431,6 @@ SSL_ForceHandshakeWithTimeout(PRFileDesc *fd,
/************************************************************************/
/*
-** Grow a buffer to hold newLen bytes of data.
-** Called for both recv buffers and xmit buffers.
-** Caller must hold xmitBufLock or recvBufLock, as appropriate.
-*/
-SECStatus
-sslBuffer_Grow(sslBuffer *b, unsigned int newLen)
-{
- newLen = PR_MAX(newLen, MAX_FRAGMENT_LENGTH + 2048);
- if (newLen > b->space) {
- unsigned char *newBuf;
- if (b->buf) {
- newBuf = (unsigned char *)PORT_Realloc(b->buf, newLen);
- } else {
- newBuf = (unsigned char *)PORT_Alloc(newLen);
- }
- if (!newBuf) {
- return SECFailure;
- }
- SSL_TRC(10, ("%d: SSL: grow buffer from %d to %d",
- SSL_GETPID(), b->space, newLen));
- b->buf = newBuf;
- b->space = newLen;
- }
- return SECSuccess;
-}
-
-SECStatus
-sslBuffer_Append(sslBuffer *b, const void *data, unsigned int len)
-{
- unsigned int newLen = b->len + len;
- SECStatus rv;
-
- rv = sslBuffer_Grow(b, newLen);
- if (rv != SECSuccess)
- return rv;
- PORT_Memcpy(b->buf + b->len, data, len);
- b->len += len;
- return SECSuccess;
-}
-
-void
-sslBuffer_Clear(sslBuffer *b)
-{
- if (b->buf) {
- PORT_Free(b->buf);
- b->buf = NULL;
- b->len = 0;
- b->space = 0;
- }
-}
-
-/*
** Save away write data that is trying to be written before the security
** handshake has been completed. When the handshake is completed, we will
** flush this data out.
@@ -774,8 +718,7 @@ ssl_SecureClose(sslSocket *ss)
if (!(ss->shutdownHow & ssl_SHUTDOWN_SEND) &&
ss->firstHsDone &&
- !ss->recvdCloseNotify &&
- ss->ssl3.initialized) {
+ !ss->recvdCloseNotify) {
/* We don't want the final alert to be Nagle delayed. */
if (!ss->delayDisabled) {
@@ -805,8 +748,7 @@ ssl_SecureShutdown(sslSocket *ss, int nsprHow)
if ((sslHow & ssl_SHUTDOWN_SEND) != 0 &&
!(ss->shutdownHow & ssl_SHUTDOWN_SEND) &&
ss->firstHsDone &&
- !ss->recvdCloseNotify &&
- ss->ssl3.initialized) {
+ !ss->recvdCloseNotify) {
(void)SSL3_SendAlert(ss, alert_warning, close_notify);
}
@@ -820,6 +762,55 @@ ssl_SecureShutdown(sslSocket *ss, int nsprHow)
/************************************************************************/
+static SECStatus
+tls13_CheckKeyUpdate(sslSocket *ss, CipherSpecDirection dir)
+{
+ PRBool keyUpdate;
+ ssl3CipherSpec *spec;
+ sslSequenceNumber seqNum;
+ sslSequenceNumber margin;
+ SECStatus rv;
+
+ /* Bug 1413368: enable for DTLS */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 || IS_DTLS(ss)) {
+ return SECSuccess;
+ }
+
+ /* If both sides update at the same number, then this will cause two updates
+ * to happen at once. The problem is that the KeyUpdate itself consumes a
+ * sequence number, and that will trigger the reading side to request an
+ * update.
+ *
+ * If we have the writing side update first, the writer will be the one that
+ * drives the update. An update by the writer doesn't need a response, so
+ * it is more efficient overall. The margins here are pretty arbitrary, but
+ * having the write margin larger reduces the number of times that a
+ * KeyUpdate is sent by a reader. */
+ ssl_GetSpecReadLock(ss);
+ if (dir == CipherSpecRead) {
+ spec = ss->ssl3.crSpec;
+ margin = spec->cipherDef->max_records / 8;
+ } else {
+ spec = ss->ssl3.cwSpec;
+ margin = spec->cipherDef->max_records / 4;
+ }
+ seqNum = spec->seqNum;
+ keyUpdate = seqNum > spec->cipherDef->max_records - margin;
+ ssl_ReleaseSpecReadLock(ss);
+ if (!keyUpdate) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(5, ("%d: SSL[%d]: automatic key update at %llx for %s cipher spec",
+ SSL_GETPID(), ss->fd, seqNum,
+ (dir == CipherSpecRead) ? "read" : "write"));
+ ssl_GetSSL3HandshakeLock(ss);
+ rv = tls13_SendKeyUpdate(ss, (dir == CipherSpecRead) ? update_requested : update_not_requested,
+ dir == CipherSpecWrite /* buffer */);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ return rv;
+}
+
int
ssl_SecureRecv(sslSocket *ss, unsigned char *buf, int len, int flags)
{
@@ -859,8 +850,17 @@ ssl_SecureRecv(sslSocket *ss, unsigned char *buf, int len, int flags)
rv = ssl_Do1stHandshake(ss);
}
ssl_Release1stHandshakeLock(ss);
+ } else {
+ if (tls13_CheckKeyUpdate(ss, CipherSpecRead) != SECSuccess) {
+ rv = PR_FAILURE;
+ }
}
if (rv < 0) {
+ if (PORT_GetError() == PR_WOULD_BLOCK_ERROR &&
+ !PR_CLIST_IS_EMPTY(&ss->ssl3.hs.bufferedEarlyData)) {
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ return tls13_Read0RttData(ss, buf, len);
+ }
return rv;
}
@@ -942,11 +942,19 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
}
ssl_Release1stHandshakeLock(ss);
}
+
if (rv < 0) {
ss->writerThread = NULL;
goto done;
}
+ if (ss->firstHsDone) {
+ if (tls13_CheckKeyUpdate(ss, CipherSpecWrite) != SECSuccess) {
+ rv = PR_FAILURE;
+ goto done;
+ }
+ }
+
if (zeroRtt) {
/* There's a limit to the number of early data octets we can send.
*
@@ -1241,14 +1249,7 @@ SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error)
}
ssl_Get1stHandshakeLock(ss);
-
- if (!ss->ssl3.initialized) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- rv = SECFailure;
- } else {
- rv = ssl3_AuthCertificateComplete(ss, error);
- }
-
+ rv = ssl3_AuthCertificateComplete(ss, error);
ssl_Release1stHandshakeLock(ss);
return rv;
diff --git a/security/nss/lib/ssl/sslsnce.c b/security/nss/lib/ssl/sslsnce.c
index 3ef11f7a7..279f3c015 100644
--- a/security/nss/lib/ssl/sslsnce.c
+++ b/security/nss/lib/ssl/sslsnce.c
@@ -85,11 +85,12 @@
/*
** Format of a cache entry in the shared memory.
*/
+PR_STATIC_ASSERT(sizeof(PRTime) == 8);
struct sidCacheEntryStr {
/* 16 */ PRIPv6Addr addr; /* client's IP address */
- /* 4 */ PRUint32 creationTime;
- /* 4 */ PRUint32 lastAccessTime;
- /* 4 */ PRUint32 expirationTime;
+ /* 8 */ PRTime creationTime;
+ /* 8 */ PRTime lastAccessTime;
+ /* 8 */ PRTime expirationTime;
/* 2 */ PRUint16 version;
/* 1 */ PRUint8 valid;
/* 1 */ PRUint8 sessionIDLength;
@@ -98,25 +99,25 @@ struct sidCacheEntryStr {
/* 2 */ PRUint16 authKeyBits;
/* 2 */ PRUint16 keaType;
/* 2 */ PRUint16 keaKeyBits;
- /* 72 - common header total */
+ /* 4 */ PRUint32 signatureScheme;
+ /* 4 */ PRUint32 keaGroup;
+ /* 92 - common header total */
union {
struct {
/* 2 */ ssl3CipherSuite cipherSuite;
- /* 2 */ PRUint16 compression; /* SSLCompressionMethod */
-
- /* 54 */ ssl3SidKeys keys; /* keys, wrapped as needed. */
+ /* 52 */ ssl3SidKeys keys; /* keys, wrapped as needed. */
/* 4 */ PRUint32 masterWrapMech;
/* 4 */ PRInt32 certIndex;
/* 4 */ PRInt32 srvNameIndex;
/* 32 */ PRUint8 srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */
/* 2 */ PRUint16 namedCurve;
-/*104 */} ssl3;
+/*100 */} ssl3;
/* force sizeof(sidCacheEntry) to be a multiple of cache line size */
struct {
- /*120 */ PRUint8 filler[120]; /* 72+120==192, a multiple of 16 */
+ /*116 */ PRUint8 filler[116]; /* 92+116==208, a multiple of 16 */
} forceSize;
} u;
};
@@ -282,7 +283,7 @@ LockSidCacheLock(sidCacheLock *lock, PRUint32 now)
if (rv != SECSuccess)
return 0;
if (!now)
- now = ssl_Time();
+ now = ssl_TimeSec();
lock->timeStamp = now;
lock->pid = myPid;
return now;
@@ -298,7 +299,7 @@ UnlockSidCacheLock(sidCacheLock *lock)
return rv;
}
-/* returns the value of ssl_Time on success, zero on failure. */
+/* returns the value of ssl_TimeSec on success, zero on failure. */
static PRUint32
LockSet(cacheDesc *cache, PRUint32 set, PRUint32 now)
{
@@ -432,9 +433,10 @@ ConvertFromSID(sidCacheEntry *to, sslSessionID *from)
to->authKeyBits = from->authKeyBits;
to->keaType = from->keaType;
to->keaKeyBits = from->keaKeyBits;
+ to->keaGroup = from->keaGroup;
+ to->signatureScheme = from->sigScheme;
to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
- to->u.ssl3.compression = (PRUint16)from->u.ssl3.compression;
to->u.ssl3.keys = from->u.ssl3.keys;
to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
to->sessionIDLength = from->u.ssl3.sessionIDLength;
@@ -452,9 +454,10 @@ ConvertFromSID(sidCacheEntry *to, sslSessionID *from)
SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%08x%08x%08x%08x "
"cipherSuite=%d",
- myPid, to->creationTime, to->addr.pr_s6_addr32[0],
- to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
- to->addr.pr_s6_addr32[3], to->u.ssl3.cipherSuite));
+ myPid, to->creationTime / PR_USEC_PER_SEC,
+ to->addr.pr_s6_addr32[0], to->addr.pr_s6_addr32[1],
+ to->addr.pr_s6_addr32[2], to->addr.pr_s6_addr32[3],
+ to->u.ssl3.cipherSuite));
}
/*
@@ -476,7 +479,6 @@ ConvertToSID(sidCacheEntry *from,
to->u.ssl3.sessionIDLength = from->sessionIDLength;
to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
- to->u.ssl3.compression = (SSLCompressionMethod)from->u.ssl3.compression;
to->u.ssl3.keys = from->u.ssl3.keys;
to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
if (from->u.ssl3.srvNameIndex != -1 && psnce) {
@@ -541,6 +543,8 @@ ConvertToSID(sidCacheEntry *from,
to->authKeyBits = from->authKeyBits;
to->keaType = from->keaType;
to->keaKeyBits = from->keaKeyBits;
+ to->keaGroup = from->keaGroup;
+ to->sigScheme = from->signatureScheme;
return to;
@@ -748,17 +752,19 @@ ServerSessionIDCache(sslSessionID *sid)
PORT_Assert(sid->creationTime != 0);
if (!sid->creationTime)
- sid->lastAccessTime = sid->creationTime = ssl_Time();
+ sid->lastAccessTime = sid->creationTime = ssl_TimeUsec();
/* override caller's expiration time, which uses client timeout
* duration, not server timeout duration.
*/
- sid->expirationTime = sid->creationTime + cache->ssl3Timeout;
+ sid->expirationTime =
+ sid->creationTime + cache->ssl3Timeout * PR_USEC_PER_SEC;
SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
"cipherSuite=%d",
myPid, sid->cached,
sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
- sid->creationTime, sid->u.ssl3.cipherSuite));
+ sid->creationTime / PR_USEC_PER_SEC,
+ sid->u.ssl3.cipherSuite));
PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
@@ -820,7 +826,8 @@ ServerSessionIDUncache(sslSessionID *sid)
myPid, sid->cached,
sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
- sid->creationTime, sid->u.ssl3.cipherSuite));
+ sid->creationTime / PR_USEC_PER_SEC,
+ sid->u.ssl3.cipherSuite));
PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
set = SIDindex(cache, &sid->addr, sessionID, sessionIDLength);
now = LockSet(cache, set, 0);
@@ -1086,7 +1093,7 @@ InitCache(cacheDesc *cache, int maxCacheEntries, int maxCertCacheEntries,
cache->srvNameCacheData = (srvNameCacheEntry *)(cache->cacheMem + (ptrdiff_t)cache->srvNameCacheData);
/* initialize the locks */
- init_time = ssl_Time();
+ init_time = ssl_TimeSec();
pLock = cache->sidCacheLocks;
for (locks_to_initialize = cache->numSIDCacheLocks + 3;
locks_initialized < locks_to_initialize;
@@ -1134,6 +1141,10 @@ SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
return SECSuccess;
}
+PR_STATIC_ASSERT(sizeof(sidCacheEntry) % 16 == 0);
+PR_STATIC_ASSERT(sizeof(certCacheEntry) == 4096);
+PR_STATIC_ASSERT(sizeof(srvNameCacheEntry) == 1072);
+
static SECStatus
ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache,
PRUint32 ssl3_timeout,
@@ -1145,10 +1156,6 @@ ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache,
{
SECStatus rv;
- PORT_Assert(sizeof(sidCacheEntry) == 192);
- PORT_Assert(sizeof(certCacheEntry) == 4096);
- PORT_Assert(sizeof(srvNameCacheEntry) == 1072);
-
rv = ssl_Init();
if (rv != SECSuccess) {
return rv;
@@ -1519,7 +1526,7 @@ LockPoller(void *arg)
if (sharedCache->stopPolling)
break;
- now = ssl_Time();
+ now = ssl_TimeSec();
then = now - expiration;
for (pLock = cache->sidCacheLocks, locks_polled = 0;
locks_to_poll > locks_polled && !sharedCache->stopPolling;
diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
index 99828c85b..4893cb9f9 100644
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -11,6 +11,7 @@
#include "cert.h"
#include "keyhi.h"
#include "ssl.h"
+#include "sslexp.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "nspr.h"
@@ -79,11 +80,7 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* enableSignedCertTimestamps */
PR_FALSE, /* requireDHENamedGroups */
PR_FALSE, /* enable0RttData */
-#ifdef NSS_ENABLE_TLS13_SHORT_HEADERS
- PR_TRUE /* enableShortHeaders */
-#else
- PR_FALSE /* enableShortHeaders */
-#endif
+ PR_FALSE /* enableTls13CompatMode */
};
/*
@@ -110,7 +107,6 @@ sslSessionIDLookupFunc ssl_sid_lookup;
sslSessionIDCacheFunc ssl_sid_cache;
sslSessionIDUncacheFunc ssl_sid_uncache;
-static PRBool ssl_inited = PR_FALSE;
static PRDescIdentity ssl_layer_id;
PRBool locksEverDisabled; /* implicitly PR_FALSE */
@@ -122,6 +118,7 @@ FILE *ssl_trace_iob;
#ifdef NSS_ALLOW_SSLKEYLOGFILE
FILE *ssl_keylog_iob;
+PZLock *ssl_keylog_lock;
#endif
char lockStatus[] = "Locks are ENABLED. ";
@@ -300,6 +297,7 @@ ssl_DupSocket(sslSocket *os)
if (ss->opt.useSecurity) {
PRCList *cursor;
+
for (cursor = PR_NEXT_LINK(&os->serverCerts);
cursor != &os->serverCerts;
cursor = PR_NEXT_LINK(cursor)) {
@@ -309,7 +307,6 @@ ssl_DupSocket(sslSocket *os)
PR_APPEND_LINK(&sc->link, &ss->serverCerts);
}
- PR_INIT_CLIST(&ss->ephemeralKeyPairs);
for (cursor = PR_NEXT_LINK(&os->ephemeralKeyPairs);
cursor != &os->ephemeralKeyPairs;
cursor = PR_NEXT_LINK(cursor)) {
@@ -320,6 +317,18 @@ ssl_DupSocket(sslSocket *os)
PR_APPEND_LINK(&skp->link, &ss->ephemeralKeyPairs);
}
+ for (cursor = PR_NEXT_LINK(&os->extensionHooks);
+ cursor != &os->extensionHooks;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslCustomExtensionHooks *oh = (sslCustomExtensionHooks *)cursor;
+ sslCustomExtensionHooks *sh = PORT_ZNew(sslCustomExtensionHooks);
+ if (!sh) {
+ goto loser;
+ }
+ *sh = *oh;
+ PR_APPEND_LINK(&sh->link, &ss->extensionHooks);
+ }
+
/*
* XXX the preceding CERT_ and SECKEY_ functions can fail and return NULL.
* XXX We should detect this, and not just march on with NULL pointers.
@@ -354,6 +363,7 @@ ssl_DupSocket(sslSocket *os)
goto loser;
}
}
+
return ss;
loser:
@@ -422,9 +432,16 @@ ssl_DestroySocketContents(sslSocket *ss)
PR_REMOVE_LINK(cursor);
ssl_FreeServerCert((sslServerCert *)cursor);
}
+
+ /* Remove extension handlers. */
+ ssl_ClearPRCList(&ss->extensionHooks, NULL);
+
ssl_FreeEphemeralKeyPairs(ss);
SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
ssl3_FreeSniNameArray(&ss->xtnData);
+
+ ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL);
+ ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
}
/*
@@ -501,7 +518,7 @@ PrepareSocket(sslSocket *ss)
}
SECStatus
-SSL_Enable(PRFileDesc *fd, int which, PRBool on)
+SSL_Enable(PRFileDesc *fd, int which, PRIntn on)
{
return SSL_OptionSet(fd, which, on);
}
@@ -513,9 +530,9 @@ static PRBool ssl_VersionIsSupportedByPolicy(
* ssl.h in the section "SSL version range setting API".
*/
static void
-ssl_EnableTLS(SSLVersionRange *vrange, PRBool on)
+ssl_EnableTLS(SSLVersionRange *vrange, PRIntn enable)
{
- if (on) {
+ if (enable) {
/* don't turn it on if tls1.0 disallowed by by policy */
if (!ssl_VersionIsSupportedByPolicy(ssl_variant_stream,
SSL_LIBRARY_VERSION_TLS_1_0)) {
@@ -523,14 +540,14 @@ ssl_EnableTLS(SSLVersionRange *vrange, PRBool on)
}
}
if (SSL_ALL_VERSIONS_DISABLED(vrange)) {
- if (on) {
+ if (enable) {
vrange->min = SSL_LIBRARY_VERSION_TLS_1_0;
vrange->max = SSL_LIBRARY_VERSION_TLS_1_0;
} /* else don't change anything */
return;
}
- if (on) {
+ if (enable) {
/* Expand the range of enabled version to include TLS 1.0 */
vrange->min = PR_MIN(vrange->min, SSL_LIBRARY_VERSION_TLS_1_0);
vrange->max = PR_MAX(vrange->max, SSL_LIBRARY_VERSION_TLS_1_0);
@@ -550,9 +567,9 @@ ssl_EnableTLS(SSLVersionRange *vrange, PRBool on)
* ssl.h in the section "SSL version range setting API".
*/
static void
-ssl_EnableSSL3(SSLVersionRange *vrange, PRBool on)
+ssl_EnableSSL3(SSLVersionRange *vrange, PRIntn enable)
{
- if (on) {
+ if (enable) {
/* don't turn it on if ssl3 disallowed by by policy */
if (!ssl_VersionIsSupportedByPolicy(ssl_variant_stream,
SSL_LIBRARY_VERSION_3_0)) {
@@ -560,14 +577,14 @@ ssl_EnableSSL3(SSLVersionRange *vrange, PRBool on)
}
}
if (SSL_ALL_VERSIONS_DISABLED(vrange)) {
- if (on) {
+ if (enable) {
vrange->min = SSL_LIBRARY_VERSION_3_0;
vrange->max = SSL_LIBRARY_VERSION_3_0;
} /* else don't change anything */
return;
}
- if (on) {
+ if (enable) {
/* Expand the range of enabled versions to include SSL 3.0. We know
* SSL 3.0 or some version of TLS is already enabled at this point, so
* we don't need to change vrange->max.
@@ -586,7 +603,7 @@ ssl_EnableSSL3(SSLVersionRange *vrange, PRBool on)
}
SECStatus
-SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
+SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val)
{
sslSocket *ss = ssl_FindSocket(fd);
SECStatus rv = SECSuccess;
@@ -605,63 +622,63 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
case SSL_SOCKS:
ss->opt.useSocks = PR_FALSE;
rv = PrepareSocket(ss);
- if (on) {
+ if (val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
break;
case SSL_SECURITY:
- ss->opt.useSecurity = on;
+ ss->opt.useSecurity = val;
rv = PrepareSocket(ss);
break;
case SSL_REQUEST_CERTIFICATE:
- ss->opt.requestCertificate = on;
+ ss->opt.requestCertificate = val;
break;
case SSL_REQUIRE_CERTIFICATE:
- ss->opt.requireCertificate = on;
+ ss->opt.requireCertificate = val;
break;
case SSL_HANDSHAKE_AS_CLIENT:
- if (ss->opt.handshakeAsServer && on) {
+ if (ss->opt.handshakeAsServer && val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
break;
}
- ss->opt.handshakeAsClient = on;
+ ss->opt.handshakeAsClient = val;
break;
case SSL_HANDSHAKE_AS_SERVER:
- if (ss->opt.handshakeAsClient && on) {
+ if (ss->opt.handshakeAsClient && val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
break;
}
- ss->opt.handshakeAsServer = on;
+ ss->opt.handshakeAsServer = val;
break;
case SSL_ENABLE_TLS:
if (IS_DTLS(ss)) {
- if (on) {
+ if (val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure; /* not allowed */
}
break;
}
- ssl_EnableTLS(&ss->vrange, on);
+ ssl_EnableTLS(&ss->vrange, val);
break;
case SSL_ENABLE_SSL3:
if (IS_DTLS(ss)) {
- if (on) {
+ if (val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure; /* not allowed */
}
break;
}
- ssl_EnableSSL3(&ss->vrange, on);
+ ssl_EnableSSL3(&ss->vrange, val);
break;
case SSL_ENABLE_SSL2:
@@ -670,26 +687,26 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
* However, if an old application requests to disable SSL v2,
* we shouldn't fail.
*/
- if (on) {
+ if (val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
break;
case SSL_NO_CACHE:
- ss->opt.noCache = on;
+ ss->opt.noCache = val;
break;
case SSL_ENABLE_FDX:
- if (on && ss->opt.noLocks) {
+ if (val && ss->opt.noLocks) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
- ss->opt.fdx = on;
+ ss->opt.fdx = val;
break;
case SSL_ROLLBACK_DETECTION:
- ss->opt.detectRollBack = on;
+ ss->opt.detectRollBack = val;
break;
case SSL_NO_STEP_DOWN:
@@ -699,14 +716,14 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
break;
case SSL_NO_LOCKS:
- if (on && ss->opt.fdx) {
+ if (val && ss->opt.fdx) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
- if (on && ssl_force_locks)
- on = PR_FALSE; /* silent override */
- ss->opt.noLocks = on;
- if (on) {
+ if (val && ssl_force_locks)
+ val = PR_FALSE; /* silent override */
+ ss->opt.noLocks = val;
+ if (val) {
locksEverDisabled = PR_TRUE;
strcpy(lockStatus + LOCKSTATUS_OFFSET, "DISABLED.");
} else if (!holdingLocks) {
@@ -718,71 +735,75 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
break;
case SSL_ENABLE_SESSION_TICKETS:
- ss->opt.enableSessionTickets = on;
+ ss->opt.enableSessionTickets = val;
break;
case SSL_ENABLE_DEFLATE:
- ss->opt.enableDeflate = on;
+ ss->opt.enableDeflate = val;
break;
case SSL_ENABLE_RENEGOTIATION:
- if (IS_DTLS(ss) && on != SSL_RENEGOTIATE_NEVER) {
+ if (IS_DTLS(ss) && val != SSL_RENEGOTIATE_NEVER) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
break;
}
- ss->opt.enableRenegotiation = on;
+ ss->opt.enableRenegotiation = val;
break;
case SSL_REQUIRE_SAFE_NEGOTIATION:
- ss->opt.requireSafeNegotiation = on;
+ ss->opt.requireSafeNegotiation = val;
break;
case SSL_ENABLE_FALSE_START:
- ss->opt.enableFalseStart = on;
+ ss->opt.enableFalseStart = val;
break;
case SSL_CBC_RANDOM_IV:
- ss->opt.cbcRandomIV = on;
+ ss->opt.cbcRandomIV = val;
break;
case SSL_ENABLE_OCSP_STAPLING:
- ss->opt.enableOCSPStapling = on;
+ ss->opt.enableOCSPStapling = val;
break;
case SSL_ENABLE_NPN:
break;
case SSL_ENABLE_ALPN:
- ss->opt.enableALPN = on;
+ ss->opt.enableALPN = val;
break;
case SSL_REUSE_SERVER_ECDHE_KEY:
- ss->opt.reuseServerECDHEKey = on;
+ ss->opt.reuseServerECDHEKey = val;
break;
case SSL_ENABLE_FALLBACK_SCSV:
- ss->opt.enableFallbackSCSV = on;
+ ss->opt.enableFallbackSCSV = val;
break;
case SSL_ENABLE_SERVER_DHE:
- ss->opt.enableServerDhe = on;
+ ss->opt.enableServerDhe = val;
break;
case SSL_ENABLE_EXTENDED_MASTER_SECRET:
- ss->opt.enableExtendedMS = on;
+ ss->opt.enableExtendedMS = val;
break;
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- ss->opt.enableSignedCertTimestamps = on;
+ ss->opt.enableSignedCertTimestamps = val;
break;
case SSL_REQUIRE_DH_NAMED_GROUPS:
- ss->opt.requireDHENamedGroups = on;
+ ss->opt.requireDHENamedGroups = val;
break;
case SSL_ENABLE_0RTT_DATA:
- ss->opt.enable0RttData = on;
+ ss->opt.enable0RttData = val;
+ break;
+
+ case SSL_ENABLE_TLS13_COMPAT_MODE:
+ ss->opt.enableTls13CompatMode = val;
break;
default:
@@ -804,19 +825,19 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
}
SECStatus
-SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
+SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRIntn *pVal)
{
sslSocket *ss = ssl_FindSocket(fd);
SECStatus rv = SECSuccess;
- PRBool on = PR_FALSE;
+ PRIntn val = PR_FALSE;
- if (!pOn) {
+ if (!pVal) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in Enable", SSL_GETPID(), fd));
- *pOn = PR_FALSE;
+ *pVal = PR_FALSE;
return SECFailure;
}
@@ -825,98 +846,101 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
switch (which) {
case SSL_SOCKS:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_SECURITY:
- on = ss->opt.useSecurity;
+ val = ss->opt.useSecurity;
break;
case SSL_REQUEST_CERTIFICATE:
- on = ss->opt.requestCertificate;
+ val = ss->opt.requestCertificate;
break;
case SSL_REQUIRE_CERTIFICATE:
- on = ss->opt.requireCertificate;
+ val = ss->opt.requireCertificate;
break;
case SSL_HANDSHAKE_AS_CLIENT:
- on = ss->opt.handshakeAsClient;
+ val = ss->opt.handshakeAsClient;
break;
case SSL_HANDSHAKE_AS_SERVER:
- on = ss->opt.handshakeAsServer;
+ val = ss->opt.handshakeAsServer;
break;
case SSL_ENABLE_TLS:
- on = ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_0;
+ val = ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_0;
break;
case SSL_ENABLE_SSL3:
- on = ss->vrange.min == SSL_LIBRARY_VERSION_3_0;
+ val = ss->vrange.min == SSL_LIBRARY_VERSION_3_0;
break;
case SSL_ENABLE_SSL2:
case SSL_V2_COMPATIBLE_HELLO:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_NO_CACHE:
- on = ss->opt.noCache;
+ val = ss->opt.noCache;
break;
case SSL_ENABLE_FDX:
- on = ss->opt.fdx;
+ val = ss->opt.fdx;
break;
case SSL_ROLLBACK_DETECTION:
- on = ss->opt.detectRollBack;
+ val = ss->opt.detectRollBack;
break;
case SSL_NO_STEP_DOWN:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_BYPASS_PKCS11:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_NO_LOCKS:
- on = ss->opt.noLocks;
+ val = ss->opt.noLocks;
break;
case SSL_ENABLE_SESSION_TICKETS:
- on = ss->opt.enableSessionTickets;
+ val = ss->opt.enableSessionTickets;
break;
case SSL_ENABLE_DEFLATE:
- on = ss->opt.enableDeflate;
+ val = ss->opt.enableDeflate;
break;
case SSL_ENABLE_RENEGOTIATION:
- on = ss->opt.enableRenegotiation;
+ val = ss->opt.enableRenegotiation;
break;
case SSL_REQUIRE_SAFE_NEGOTIATION:
- on = ss->opt.requireSafeNegotiation;
+ val = ss->opt.requireSafeNegotiation;
break;
case SSL_ENABLE_FALSE_START:
- on = ss->opt.enableFalseStart;
+ val = ss->opt.enableFalseStart;
break;
case SSL_CBC_RANDOM_IV:
- on = ss->opt.cbcRandomIV;
+ val = ss->opt.cbcRandomIV;
break;
case SSL_ENABLE_OCSP_STAPLING:
- on = ss->opt.enableOCSPStapling;
+ val = ss->opt.enableOCSPStapling;
break;
case SSL_ENABLE_NPN:
- on = ss->opt.enableNPN;
+ val = ss->opt.enableNPN;
break;
case SSL_ENABLE_ALPN:
- on = ss->opt.enableALPN;
+ val = ss->opt.enableALPN;
break;
case SSL_REUSE_SERVER_ECDHE_KEY:
- on = ss->opt.reuseServerECDHEKey;
+ val = ss->opt.reuseServerECDHEKey;
break;
case SSL_ENABLE_FALLBACK_SCSV:
- on = ss->opt.enableFallbackSCSV;
+ val = ss->opt.enableFallbackSCSV;
break;
case SSL_ENABLE_SERVER_DHE:
- on = ss->opt.enableServerDhe;
+ val = ss->opt.enableServerDhe;
break;
case SSL_ENABLE_EXTENDED_MASTER_SECRET:
- on = ss->opt.enableExtendedMS;
+ val = ss->opt.enableExtendedMS;
break;
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- on = ss->opt.enableSignedCertTimestamps;
+ val = ss->opt.enableSignedCertTimestamps;
break;
case SSL_REQUIRE_DH_NAMED_GROUPS:
- on = ss->opt.requireDHENamedGroups;
+ val = ss->opt.requireDHENamedGroups;
break;
case SSL_ENABLE_0RTT_DATA:
- on = ss->opt.enable0RttData;
+ val = ss->opt.enable0RttData;
+ break;
+ case SSL_ENABLE_TLS13_COMPAT_MODE:
+ val = ss->opt.enableTls13CompatMode;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -926,17 +950,17 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
- *pOn = on;
+ *pVal = val;
return rv;
}
SECStatus
-SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
+SSL_OptionGetDefault(PRInt32 which, PRIntn *pVal)
{
SECStatus rv = SECSuccess;
- PRBool on = PR_FALSE;
+ PRIntn val = PR_FALSE;
- if (!pOn) {
+ if (!pVal) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
@@ -945,114 +969,117 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
switch (which) {
case SSL_SOCKS:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_SECURITY:
- on = ssl_defaults.useSecurity;
+ val = ssl_defaults.useSecurity;
break;
case SSL_REQUEST_CERTIFICATE:
- on = ssl_defaults.requestCertificate;
+ val = ssl_defaults.requestCertificate;
break;
case SSL_REQUIRE_CERTIFICATE:
- on = ssl_defaults.requireCertificate;
+ val = ssl_defaults.requireCertificate;
break;
case SSL_HANDSHAKE_AS_CLIENT:
- on = ssl_defaults.handshakeAsClient;
+ val = ssl_defaults.handshakeAsClient;
break;
case SSL_HANDSHAKE_AS_SERVER:
- on = ssl_defaults.handshakeAsServer;
+ val = ssl_defaults.handshakeAsServer;
break;
case SSL_ENABLE_TLS:
- on = versions_defaults_stream.max >= SSL_LIBRARY_VERSION_TLS_1_0;
+ val = versions_defaults_stream.max >= SSL_LIBRARY_VERSION_TLS_1_0;
break;
case SSL_ENABLE_SSL3:
- on = versions_defaults_stream.min == SSL_LIBRARY_VERSION_3_0;
+ val = versions_defaults_stream.min == SSL_LIBRARY_VERSION_3_0;
break;
case SSL_ENABLE_SSL2:
case SSL_V2_COMPATIBLE_HELLO:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_NO_CACHE:
- on = ssl_defaults.noCache;
+ val = ssl_defaults.noCache;
break;
case SSL_ENABLE_FDX:
- on = ssl_defaults.fdx;
+ val = ssl_defaults.fdx;
break;
case SSL_ROLLBACK_DETECTION:
- on = ssl_defaults.detectRollBack;
+ val = ssl_defaults.detectRollBack;
break;
case SSL_NO_STEP_DOWN:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_BYPASS_PKCS11:
- on = PR_FALSE;
+ val = PR_FALSE;
break;
case SSL_NO_LOCKS:
- on = ssl_defaults.noLocks;
+ val = ssl_defaults.noLocks;
break;
case SSL_ENABLE_SESSION_TICKETS:
- on = ssl_defaults.enableSessionTickets;
+ val = ssl_defaults.enableSessionTickets;
break;
case SSL_ENABLE_DEFLATE:
- on = ssl_defaults.enableDeflate;
+ val = ssl_defaults.enableDeflate;
break;
case SSL_ENABLE_RENEGOTIATION:
- on = ssl_defaults.enableRenegotiation;
+ val = ssl_defaults.enableRenegotiation;
break;
case SSL_REQUIRE_SAFE_NEGOTIATION:
- on = ssl_defaults.requireSafeNegotiation;
+ val = ssl_defaults.requireSafeNegotiation;
break;
case SSL_ENABLE_FALSE_START:
- on = ssl_defaults.enableFalseStart;
+ val = ssl_defaults.enableFalseStart;
break;
case SSL_CBC_RANDOM_IV:
- on = ssl_defaults.cbcRandomIV;
+ val = ssl_defaults.cbcRandomIV;
break;
case SSL_ENABLE_OCSP_STAPLING:
- on = ssl_defaults.enableOCSPStapling;
+ val = ssl_defaults.enableOCSPStapling;
break;
case SSL_ENABLE_NPN:
- on = ssl_defaults.enableNPN;
+ val = ssl_defaults.enableNPN;
break;
case SSL_ENABLE_ALPN:
- on = ssl_defaults.enableALPN;
+ val = ssl_defaults.enableALPN;
break;
case SSL_REUSE_SERVER_ECDHE_KEY:
- on = ssl_defaults.reuseServerECDHEKey;
+ val = ssl_defaults.reuseServerECDHEKey;
break;
case SSL_ENABLE_FALLBACK_SCSV:
- on = ssl_defaults.enableFallbackSCSV;
+ val = ssl_defaults.enableFallbackSCSV;
break;
case SSL_ENABLE_SERVER_DHE:
- on = ssl_defaults.enableServerDhe;
+ val = ssl_defaults.enableServerDhe;
break;
case SSL_ENABLE_EXTENDED_MASTER_SECRET:
- on = ssl_defaults.enableExtendedMS;
+ val = ssl_defaults.enableExtendedMS;
break;
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- on = ssl_defaults.enableSignedCertTimestamps;
+ val = ssl_defaults.enableSignedCertTimestamps;
break;
case SSL_ENABLE_0RTT_DATA:
- on = ssl_defaults.enable0RttData;
+ val = ssl_defaults.enable0RttData;
+ break;
+ case SSL_ENABLE_TLS13_COMPAT_MODE:
+ val = ssl_defaults.enableTls13CompatMode;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
- *pOn = on;
+ *pVal = val;
return rv;
}
/* XXX Use Global Lock to protect this stuff. */
SECStatus
-SSL_EnableDefault(int which, PRBool on)
+SSL_EnableDefault(int which, PRIntn val)
{
- return SSL_OptionSetDefault(which, on);
+ return SSL_OptionSetDefault(which, val);
}
SECStatus
-SSL_OptionSetDefault(PRInt32 which, PRBool on)
+SSL_OptionSetDefault(PRInt32 which, PRIntn val)
{
SECStatus status = ssl_Init();
@@ -1065,46 +1092,46 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
switch (which) {
case SSL_SOCKS:
ssl_defaults.useSocks = PR_FALSE;
- if (on) {
+ if (val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
break;
case SSL_SECURITY:
- ssl_defaults.useSecurity = on;
+ ssl_defaults.useSecurity = val;
break;
case SSL_REQUEST_CERTIFICATE:
- ssl_defaults.requestCertificate = on;
+ ssl_defaults.requestCertificate = val;
break;
case SSL_REQUIRE_CERTIFICATE:
- ssl_defaults.requireCertificate = on;
+ ssl_defaults.requireCertificate = val;
break;
case SSL_HANDSHAKE_AS_CLIENT:
- if (ssl_defaults.handshakeAsServer && on) {
+ if (ssl_defaults.handshakeAsServer && val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- ssl_defaults.handshakeAsClient = on;
+ ssl_defaults.handshakeAsClient = val;
break;
case SSL_HANDSHAKE_AS_SERVER:
- if (ssl_defaults.handshakeAsClient && on) {
+ if (ssl_defaults.handshakeAsClient && val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- ssl_defaults.handshakeAsServer = on;
+ ssl_defaults.handshakeAsServer = val;
break;
case SSL_ENABLE_TLS:
- ssl_EnableTLS(&versions_defaults_stream, on);
+ ssl_EnableTLS(&versions_defaults_stream, val);
break;
case SSL_ENABLE_SSL3:
- ssl_EnableSSL3(&versions_defaults_stream, on);
+ ssl_EnableSSL3(&versions_defaults_stream, val);
break;
case SSL_ENABLE_SSL2:
@@ -1113,26 +1140,26 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
* However, if an old application requests to disable SSL v2,
* we shouldn't fail.
*/
- if (on) {
+ if (val) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
break;
case SSL_NO_CACHE:
- ssl_defaults.noCache = on;
+ ssl_defaults.noCache = val;
break;
case SSL_ENABLE_FDX:
- if (on && ssl_defaults.noLocks) {
+ if (val && ssl_defaults.noLocks) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- ssl_defaults.fdx = on;
+ ssl_defaults.fdx = val;
break;
case SSL_ROLLBACK_DETECTION:
- ssl_defaults.detectRollBack = on;
+ ssl_defaults.detectRollBack = val;
break;
case SSL_NO_STEP_DOWN:
@@ -1142,76 +1169,80 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
break;
case SSL_NO_LOCKS:
- if (on && ssl_defaults.fdx) {
+ if (val && ssl_defaults.fdx) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- if (on && ssl_force_locks)
- on = PR_FALSE; /* silent override */
- ssl_defaults.noLocks = on;
- if (on) {
+ if (val && ssl_force_locks)
+ val = PR_FALSE; /* silent override */
+ ssl_defaults.noLocks = val;
+ if (val) {
locksEverDisabled = PR_TRUE;
strcpy(lockStatus + LOCKSTATUS_OFFSET, "DISABLED.");
}
break;
case SSL_ENABLE_SESSION_TICKETS:
- ssl_defaults.enableSessionTickets = on;
+ ssl_defaults.enableSessionTickets = val;
break;
case SSL_ENABLE_DEFLATE:
- ssl_defaults.enableDeflate = on;
+ ssl_defaults.enableDeflate = val;
break;
case SSL_ENABLE_RENEGOTIATION:
- ssl_defaults.enableRenegotiation = on;
+ ssl_defaults.enableRenegotiation = val;
break;
case SSL_REQUIRE_SAFE_NEGOTIATION:
- ssl_defaults.requireSafeNegotiation = on;
+ ssl_defaults.requireSafeNegotiation = val;
break;
case SSL_ENABLE_FALSE_START:
- ssl_defaults.enableFalseStart = on;
+ ssl_defaults.enableFalseStart = val;
break;
case SSL_CBC_RANDOM_IV:
- ssl_defaults.cbcRandomIV = on;
+ ssl_defaults.cbcRandomIV = val;
break;
case SSL_ENABLE_OCSP_STAPLING:
- ssl_defaults.enableOCSPStapling = on;
+ ssl_defaults.enableOCSPStapling = val;
break;
case SSL_ENABLE_NPN:
break;
case SSL_ENABLE_ALPN:
- ssl_defaults.enableALPN = on;
+ ssl_defaults.enableALPN = val;
break;
case SSL_REUSE_SERVER_ECDHE_KEY:
- ssl_defaults.reuseServerECDHEKey = on;
+ ssl_defaults.reuseServerECDHEKey = val;
break;
case SSL_ENABLE_FALLBACK_SCSV:
- ssl_defaults.enableFallbackSCSV = on;
+ ssl_defaults.enableFallbackSCSV = val;
break;
case SSL_ENABLE_SERVER_DHE:
- ssl_defaults.enableServerDhe = on;
+ ssl_defaults.enableServerDhe = val;
break;
case SSL_ENABLE_EXTENDED_MASTER_SECRET:
- ssl_defaults.enableExtendedMS = on;
+ ssl_defaults.enableExtendedMS = val;
break;
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- ssl_defaults.enableSignedCertTimestamps = on;
+ ssl_defaults.enableSignedCertTimestamps = val;
break;
case SSL_ENABLE_0RTT_DATA:
- ssl_defaults.enable0RttData = on;
+ ssl_defaults.enable0RttData = val;
+ break;
+
+ case SSL_ENABLE_TLS13_COMPAT_MODE:
+ ssl_defaults.enableTls13CompatMode = val;
break;
default:
@@ -2124,6 +2155,25 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
return NULL;
PR_APPEND_LINK(&skp->link, &ss->ephemeralKeyPairs);
}
+
+ while (!PR_CLIST_IS_EMPTY(&ss->extensionHooks)) {
+ cursor = PR_LIST_TAIL(&ss->extensionHooks);
+ PR_REMOVE_LINK(cursor);
+ PORT_Free(cursor);
+ }
+ for (cursor = PR_NEXT_LINK(&sm->extensionHooks);
+ cursor != &sm->extensionHooks;
+ cursor = PR_NEXT_LINK(cursor)) {
+ SECStatus rv;
+ sslCustomExtensionHooks *hook = (sslCustomExtensionHooks *)cursor;
+ rv = SSL_InstallExtensionHooks(ss->fd, hook->type,
+ hook->writer, hook->writerArg,
+ hook->handler, hook->handlerArg);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ }
+
PORT_Memcpy((void *)ss->namedGroupPreferences,
sm->namedGroupPreferences,
sizeof(ss->namedGroupPreferences));
@@ -2214,7 +2264,7 @@ ssl3_GetEffectiveVersionPolicy(SSLProtocolVariant variant,
return SECSuccess;
}
-/*
+/*
* Assumes that rangeParam values are within the supported boundaries,
* but should contain all potentially allowed versions, even if they contain
* conflicting versions.
@@ -3124,7 +3174,7 @@ ssl_WriteV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 vectors,
}
blocking = ssl_FdIsBlocking(fd);
-#define K16 sizeof(buf)
+#define K16 ((int)sizeof(buf))
#define KILL_VECTORS \
while (vectors && !iov->iov_len) { \
++iov; \
@@ -3411,7 +3461,6 @@ ssl_InitIOLayer(void)
{
ssl_layer_id = PR_GetUniqueIdentity("SSL");
ssl_SetupIOMethods();
- ssl_inited = PR_TRUE;
return PR_SUCCESS;
}
@@ -3421,15 +3470,13 @@ ssl_PushIOLayer(sslSocket *ns, PRFileDesc *stack, PRDescIdentity id)
PRFileDesc *layer = NULL;
PRStatus status;
- if (!ssl_inited) {
- status = PR_CallOnce(&initIoLayerOnce, &ssl_InitIOLayer);
- if (status != PR_SUCCESS)
- goto loser;
+ status = PR_CallOnce(&initIoLayerOnce, &ssl_InitIOLayer);
+ if (status != PR_SUCCESS) {
+ goto loser;
}
-
- if (ns == NULL)
+ if (ns == NULL) {
goto loser;
-
+ }
layer = PR_CreateIOLayerStub(ssl_layer_id, &combined_methods);
if (layer == NULL)
goto loser;
@@ -3542,6 +3589,12 @@ ssl_SetDefaultsFromEnvironment(void)
ssl_keylog_iob);
}
SSL_TRACE(("SSL: logging SSL/TLS secrets to %s", ev));
+ ssl_keylog_lock = PR_NewLock();
+ if (!ssl_keylog_lock) {
+ SSL_TRACE(("SSL: failed to create key log lock"));
+ fclose(ssl_keylog_iob);
+ ssl_keylog_iob = NULL;
+ }
}
}
#endif
@@ -3746,7 +3799,6 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
SECStatus rv;
sslSocket *ss;
int i;
-
ssl_SetDefaultsFromEnvironment();
if (ssl_force_locks)
@@ -3777,6 +3829,7 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
PR_INIT_CLIST(&ss->serverCerts);
PR_INIT_CLIST(&ss->ephemeralKeyPairs);
+ PR_INIT_CLIST(&ss->extensionHooks);
ss->dbHandle = CERT_GetDefaultCertDB();
@@ -3804,7 +3857,11 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
PR_INIT_CLIST(&ss->ssl3.hs.bufferedEarlyData);
- ssl3_InitExtensionData(&ss->xtnData);
+ ssl3_InitExtensionData(&ss->xtnData, ss);
+ PR_INIT_CLIST(&ss->ssl3.hs.dtlsSentHandshake);
+ PR_INIT_CLIST(&ss->ssl3.hs.dtlsRcvdHandshake);
+ dtls_InitTimers(ss);
+
if (makeLocks) {
rv = ssl_MakeLocks(ss);
if (rv != SECSuccess)
@@ -3816,6 +3873,10 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
rv = ssl3_InitGather(&ss->gs);
if (rv != SECSuccess)
goto loser;
+ rv = ssl3_InitState(ss);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
return ss;
loser:
@@ -3840,3 +3901,69 @@ SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey,
*pcanbypass = PR_FALSE;
return SECSuccess;
}
+
+/* Functions that are truly experimental use EXP, functions that are no longer
+ * experimental use PUB.
+ *
+ * When initially defining a new API, add that API here using the EXP() macro
+ * and name the function with a SSLExp_ prefix. Define the experimental API as
+ * a macro in sslexp.h using the SSL_EXPERIMENTAL_API() macro defined there.
+ *
+ * Once an API is stable and proven, move the macro definition in sslexp.h to a
+ * proper function declaration in ssl.h. Keeping the function in this list
+ * ensures that code built against the release that contained the experimental
+ * API will continue to work; use PUB() to reference the public function.
+ */
+#define EXP(n) \
+ { \
+ "SSL_" #n, SSLExp_##n \
+ }
+#define PUB(n) \
+ { \
+ "SSL_" #n, SSL_##n \
+ }
+struct {
+ const char *const name;
+ void *function;
+} ssl_experimental_functions[] = {
+#ifndef SSL_DISABLE_EXPERIMENTAL_API
+ EXP(GetExtensionSupport),
+ EXP(HelloRetryRequestCallback),
+ EXP(InstallExtensionHooks),
+ EXP(KeyUpdate),
+ EXP(SendSessionTicket),
+ EXP(SetupAntiReplay),
+#endif
+ { "", NULL }
+};
+#undef EXP
+#undef PUB
+
+void *
+SSL_GetExperimentalAPI(const char *name)
+{
+ unsigned int i;
+ for (i = 0; i < PR_ARRAY_SIZE(ssl_experimental_functions); ++i) {
+ if (strcmp(name, ssl_experimental_functions[i].name) == 0) {
+ return ssl_experimental_functions[i].function;
+ }
+ }
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API);
+ return NULL;
+}
+
+void
+ssl_ClearPRCList(PRCList *list, void (*f)(void *))
+{
+ PRCList *cursor;
+
+ while (!PR_CLIST_IS_EMPTY(list)) {
+ cursor = PR_LIST_TAIL(list);
+
+ PR_REMOVE_LINK(cursor);
+ if (f) {
+ f(cursor);
+ }
+ PORT_Free(cursor);
+ }
+}
diff --git a/security/nss/lib/ssl/sslspec.c b/security/nss/lib/ssl/sslspec.c
new file mode 100644
index 000000000..26c3eb546
--- /dev/null
+++ b/security/nss/lib/ssl/sslspec.c
@@ -0,0 +1,273 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Handling of cipher specs.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "ssl.h"
+#include "sslproto.h"
+#include "pk11func.h"
+#include "secitem.h"
+
+#include "sslimpl.h"
+
+/* Record protection algorithms, indexed by SSL3BulkCipher.
+ *
+ * The |max_records| field (|mr| below) is set to a number that is higher than
+ * recommended in some literature (esp. TLS 1.3) because we currently abort the
+ * connection when this limit is reached and we want to ensure that we only
+ * rarely hit this limit. See bug 1268745 for details.
+ */
+#define MR_MAX RECORD_SEQ_MAX /* 2^48-1 */
+#define MR_128 (0x5aULL << 28) /* For AES and similar. */
+#define MR_LOW (1ULL << 20) /* For weak ciphers. */
+/* clang-format off */
+static const ssl3BulkCipherDef ssl_bulk_cipher_defs[] = {
+ /* |--------- Lengths ---------| */
+ /* cipher calg : s : */
+ /* : e b n */
+ /* oid short_name mr : c l o */
+ /* k r o t n */
+ /* e e i c a c */
+ /* y t type v k g e */
+ {cipher_null, ssl_calg_null, 0, 0, type_stream, 0, 0, 0, 0,
+ SEC_OID_NULL_CIPHER, "NULL", MR_MAX},
+ {cipher_rc4, ssl_calg_rc4, 16,16, type_stream, 0, 0, 0, 0,
+ SEC_OID_RC4, "RC4", MR_LOW},
+ {cipher_des, ssl_calg_des, 8, 8, type_block, 8, 8, 0, 0,
+ SEC_OID_DES_CBC, "DES-CBC", MR_LOW},
+ {cipher_3des, ssl_calg_3des, 24,24, type_block, 8, 8, 0, 0,
+ SEC_OID_DES_EDE3_CBC, "3DES-EDE-CBC", MR_LOW},
+ {cipher_aes_128, ssl_calg_aes, 16,16, type_block, 16,16, 0, 0,
+ SEC_OID_AES_128_CBC, "AES-128", MR_128},
+ {cipher_aes_256, ssl_calg_aes, 32,32, type_block, 16,16, 0, 0,
+ SEC_OID_AES_256_CBC, "AES-256", MR_128},
+ {cipher_camellia_128, ssl_calg_camellia, 16,16, type_block, 16,16, 0, 0,
+ SEC_OID_CAMELLIA_128_CBC, "Camellia-128", MR_128},
+ {cipher_camellia_256, ssl_calg_camellia, 32,32, type_block, 16,16, 0, 0,
+ SEC_OID_CAMELLIA_256_CBC, "Camellia-256", MR_128},
+ {cipher_seed, ssl_calg_seed, 16,16, type_block, 16,16, 0, 0,
+ SEC_OID_SEED_CBC, "SEED-CBC", MR_128},
+ {cipher_aes_128_gcm, ssl_calg_aes_gcm, 16,16, type_aead, 4, 0,16, 8,
+ SEC_OID_AES_128_GCM, "AES-128-GCM", MR_128},
+ {cipher_aes_256_gcm, ssl_calg_aes_gcm, 32,32, type_aead, 4, 0,16, 8,
+ SEC_OID_AES_256_GCM, "AES-256-GCM", MR_128},
+ {cipher_chacha20, ssl_calg_chacha20, 32,32, type_aead, 12, 0,16, 0,
+ SEC_OID_CHACHA20_POLY1305, "ChaCha20-Poly1305", MR_MAX},
+ {cipher_missing, ssl_calg_null, 0, 0, type_stream, 0, 0, 0, 0,
+ SEC_OID_UNKNOWN, "missing", 0U},
+};
+/* clang-format on */
+
+const ssl3BulkCipherDef *
+ssl_GetBulkCipherDef(const ssl3CipherSuiteDef *suiteDef)
+{
+ SSL3BulkCipher bulkCipher = suiteDef->bulk_cipher_alg;
+ PORT_Assert(bulkCipher < PR_ARRAY_SIZE(ssl_bulk_cipher_defs));
+ PORT_Assert(ssl_bulk_cipher_defs[bulkCipher].cipher == bulkCipher);
+ return &ssl_bulk_cipher_defs[bulkCipher];
+}
+
+/* indexed by SSL3MACAlgorithm */
+static const ssl3MACDef ssl_mac_defs[] = {
+ /* pad_size is only used for SSL 3.0 MAC. See RFC 6101 Sec. 5.2.3.1. */
+ /* mac mmech pad_size mac_size */
+ { ssl_mac_null, CKM_INVALID_MECHANISM, 0, 0, 0 },
+ { ssl_mac_md5, CKM_SSL3_MD5_MAC, 48, MD5_LENGTH, SEC_OID_HMAC_MD5 },
+ { ssl_mac_sha, CKM_SSL3_SHA1_MAC, 40, SHA1_LENGTH, SEC_OID_HMAC_SHA1 },
+ { ssl_hmac_md5, CKM_MD5_HMAC, 0, MD5_LENGTH, SEC_OID_HMAC_MD5 },
+ { ssl_hmac_sha, CKM_SHA_1_HMAC, 0, SHA1_LENGTH, SEC_OID_HMAC_SHA1 },
+ { ssl_hmac_sha256, CKM_SHA256_HMAC, 0, SHA256_LENGTH, SEC_OID_HMAC_SHA256 },
+ { ssl_mac_aead, CKM_INVALID_MECHANISM, 0, 0, 0 },
+ { ssl_hmac_sha384, CKM_SHA384_HMAC, 0, SHA384_LENGTH, SEC_OID_HMAC_SHA384 }
+};
+
+const ssl3MACDef *
+ssl_GetMacDefByAlg(SSL3MACAlgorithm mac)
+{
+ /* Cast here for clang: https://bugs.llvm.org/show_bug.cgi?id=16154 */
+ PORT_Assert((size_t)mac < PR_ARRAY_SIZE(ssl_mac_defs));
+ PORT_Assert(ssl_mac_defs[mac].mac == mac);
+ return &ssl_mac_defs[mac];
+}
+
+const ssl3MACDef *
+ssl_GetMacDef(const sslSocket *ss, const ssl3CipherSuiteDef *suiteDef)
+{
+ SSL3MACAlgorithm mac = suiteDef->mac_alg;
+ if (ss->version > SSL_LIBRARY_VERSION_3_0) {
+ switch (mac) {
+ case ssl_mac_md5:
+ mac = ssl_hmac_md5;
+ break;
+ case ssl_mac_sha:
+ mac = ssl_hmac_sha;
+ break;
+ default:
+ break;
+ }
+ }
+ return ssl_GetMacDefByAlg(mac);
+}
+
+ssl3CipherSpec *
+ssl_FindCipherSpecByEpoch(sslSocket *ss, CipherSpecDirection direction,
+ DTLSEpoch epoch)
+{
+ PRCList *cur_p;
+ for (cur_p = PR_LIST_HEAD(&ss->ssl3.hs.cipherSpecs);
+ cur_p != &ss->ssl3.hs.cipherSpecs;
+ cur_p = PR_NEXT_LINK(cur_p)) {
+ ssl3CipherSpec *spec = (ssl3CipherSpec *)cur_p;
+
+ if (spec->epoch != epoch) {
+ continue;
+ }
+ if (direction != spec->direction) {
+ continue;
+ }
+ return spec;
+ }
+ return NULL;
+}
+
+ssl3CipherSpec *
+ssl_CreateCipherSpec(sslSocket *ss, CipherSpecDirection direction)
+{
+ ssl3CipherSpec *spec = PORT_ZNew(ssl3CipherSpec);
+ if (!spec) {
+ return NULL;
+ }
+ spec->refCt = 1;
+ spec->version = ss->version;
+ spec->direction = direction;
+ SSL_TRC(10, ("%d: SSL[%d]: new %s spec %d ct=%d",
+ SSL_GETPID(), ss->fd, SPEC_DIR(spec), spec,
+ spec->refCt));
+ return spec;
+}
+
+void
+ssl_SaveCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
+{
+ PR_APPEND_LINK(&spec->link, &ss->ssl3.hs.cipherSpecs);
+}
+
+/* Called from ssl3_InitState. */
+/* Caller must hold the SpecWriteLock. */
+SECStatus
+ssl_SetupNullCipherSpec(sslSocket *ss, CipherSpecDirection dir)
+{
+ ssl3CipherSpec *spec;
+
+ PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
+
+ spec = ssl_CreateCipherSpec(ss, dir);
+ if (!spec) {
+ return SECFailure;
+ }
+
+ /* Set default versions. This value will be used to generate and send
+ * alerts if a version is not negotiated. These values are overridden when
+ * sending a ClientHello and when a version is negotiated. */
+ spec->version = SSL_LIBRARY_VERSION_TLS_1_0;
+ spec->recordVersion = IS_DTLS(ss)
+ ? SSL_LIBRARY_VERSION_DTLS_1_0_WIRE
+ : SSL_LIBRARY_VERSION_TLS_1_0;
+ spec->cipherDef = &ssl_bulk_cipher_defs[cipher_null];
+ PORT_Assert(spec->cipherDef->cipher == cipher_null);
+ spec->macDef = &ssl_mac_defs[ssl_mac_null];
+ PORT_Assert(spec->macDef->mac == ssl_mac_null);
+ spec->cipher = Null_Cipher;
+
+ spec->phase = "cleartext";
+ dtls_InitRecvdRecords(&spec->recvdRecords);
+
+ ssl_SaveCipherSpec(ss, spec);
+ if (dir == CipherSpecRead) {
+ ss->ssl3.crSpec = spec;
+ } else {
+ ss->ssl3.cwSpec = spec;
+ }
+ return SECSuccess;
+}
+
+void
+ssl_CipherSpecAddRef(ssl3CipherSpec *spec)
+{
+ ++spec->refCt;
+ SSL_TRC(10, ("%d: SSL[-]: Increment ref ct for %s spec %d. new ct = %d",
+ SSL_GETPID(), SPEC_DIR(spec), spec, spec->refCt));
+}
+
+static void
+ssl_DestroyKeyMaterial(ssl3KeyMaterial *keyMaterial)
+{
+ PK11_FreeSymKey(keyMaterial->key);
+ PK11_FreeSymKey(keyMaterial->macKey);
+ if (keyMaterial->macContext != NULL) {
+ PK11_DestroyContext(keyMaterial->macContext, PR_TRUE);
+ }
+}
+
+static void
+ssl_FreeCipherSpec(ssl3CipherSpec *spec)
+{
+ SSL_TRC(10, ("%d: SSL[-]: Freeing %s spec %d. epoch=%d",
+ SSL_GETPID(), SPEC_DIR(spec), spec, spec->epoch));
+
+ PR_REMOVE_LINK(&spec->link);
+
+ /* PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss)); Don't have ss! */
+ if (spec->cipherContext) {
+ PK11_DestroyContext(spec->cipherContext, PR_TRUE);
+ }
+ PK11_FreeSymKey(spec->masterSecret);
+ ssl_DestroyKeyMaterial(&spec->keyMaterial);
+
+ PORT_ZFree(spec, sizeof(*spec));
+}
+
+/* This function is never called on a spec which is on the
+ * cipherSpecs list. */
+void
+ssl_CipherSpecRelease(ssl3CipherSpec *spec)
+{
+ if (!spec) {
+ return;
+ }
+
+ PORT_Assert(spec->refCt > 0);
+ --spec->refCt;
+ SSL_TRC(10, ("%d: SSL[-]: decrement refct for %s spec %d. epoch=%d new ct = %d",
+ SSL_GETPID(), SPEC_DIR(spec), spec, spec->epoch, spec->refCt));
+ if (!spec->refCt) {
+ ssl_FreeCipherSpec(spec);
+ }
+}
+
+void
+ssl_DestroyCipherSpecs(PRCList *list)
+{
+ while (!PR_CLIST_IS_EMPTY(list)) {
+ ssl3CipherSpec *spec = (ssl3CipherSpec *)PR_LIST_TAIL(list);
+ ssl_FreeCipherSpec(spec);
+ }
+}
+
+void
+ssl_CipherSpecReleaseByEpoch(sslSocket *ss, CipherSpecDirection dir,
+ DTLSEpoch epoch)
+{
+ ssl3CipherSpec *spec;
+ SSL_TRC(10, ("%d: SSL[%d]: releasing %s cipher spec for epoch %d",
+ SSL_GETPID(), ss->fd,
+ (dir == CipherSpecRead) ? "read" : "write", epoch));
+
+ spec = ssl_FindCipherSpecByEpoch(ss, dir, epoch);
+ if (spec) {
+ ssl_CipherSpecRelease(spec);
+ }
+}
diff --git a/security/nss/lib/ssl/sslspec.h b/security/nss/lib/ssl/sslspec.h
new file mode 100644
index 000000000..729ac1006
--- /dev/null
+++ b/security/nss/lib/ssl/sslspec.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __sslspec_h_
+#define __sslspec_h_
+
+#include "sslexp.h"
+#include "prclist.h"
+
+typedef enum {
+ TrafficKeyClearText = 0,
+ TrafficKeyEarlyApplicationData = 1,
+ TrafficKeyHandshake = 2,
+ TrafficKeyApplicationData = 3
+} TrafficKeyType;
+
+typedef enum {
+ CipherSpecRead,
+ CipherSpecWrite,
+} CipherSpecDirection;
+
+#define SPEC_DIR(spec) \
+ ((spec->direction == CipherSpecRead) ? "read" : "write")
+
+typedef struct ssl3CipherSpecStr ssl3CipherSpec;
+typedef struct ssl3BulkCipherDefStr ssl3BulkCipherDef;
+typedef struct ssl3MACDefStr ssl3MACDef;
+typedef struct ssl3CipherSuiteDefStr ssl3CipherSuiteDef;
+typedef PRUint64 sslSequenceNumber;
+typedef PRUint16 DTLSEpoch;
+
+/* The SSL bulk cipher definition */
+typedef enum {
+ cipher_null,
+ cipher_rc4,
+ cipher_des,
+ cipher_3des,
+ cipher_aes_128,
+ cipher_aes_256,
+ cipher_camellia_128,
+ cipher_camellia_256,
+ cipher_seed,
+ cipher_aes_128_gcm,
+ cipher_aes_256_gcm,
+ cipher_chacha20,
+ cipher_missing /* reserved for no such supported cipher */
+ /* This enum must match ssl3_cipherName[] in ssl3con.c. */
+} SSL3BulkCipher;
+
+typedef enum {
+ type_stream,
+ type_block,
+ type_aead
+} CipherType;
+
+/*
+** There are tables of these, all const.
+*/
+struct ssl3BulkCipherDefStr {
+ SSL3BulkCipher cipher;
+ SSLCipherAlgorithm calg;
+ unsigned int key_size;
+ unsigned int secret_key_size;
+ CipherType type;
+ unsigned int iv_size;
+ unsigned int block_size;
+ unsigned int tag_size; /* for AEAD ciphers. */
+ unsigned int explicit_nonce_size; /* for AEAD ciphers. */
+ SECOidTag oid;
+ const char *short_name;
+ /* The maximum number of records that can be sent/received with the same
+ * symmetric key before the connection will be terminated. */
+ PRUint64 max_records;
+};
+
+/* to make some of these old enums public without namespace pollution,
+** it was necessary to prepend ssl_ to the names.
+** These #defines preserve compatibility with the old code here in libssl.
+*/
+typedef SSLMACAlgorithm SSL3MACAlgorithm;
+
+/*
+ * There are tables of these, all const.
+ */
+struct ssl3MACDefStr {
+ SSL3MACAlgorithm mac;
+ CK_MECHANISM_TYPE mmech;
+ int pad_size;
+ int mac_size;
+ SECOidTag oid;
+};
+
+#define MAX_IV_LENGTH 24
+
+typedef struct {
+ PK11SymKey *key;
+ PK11SymKey *macKey;
+ PK11Context *macContext;
+ PRUint8 iv[MAX_IV_LENGTH];
+} ssl3KeyMaterial;
+
+typedef SECStatus (*SSLCipher)(void *context,
+ unsigned char *out,
+ int *outlen,
+ int maxout,
+ const unsigned char *in,
+ int inlen);
+typedef SECStatus (*SSLAEADCipher)(
+ ssl3KeyMaterial *keys,
+ PRBool doDecrypt,
+ unsigned char *out,
+ int *outlen,
+ int maxout,
+ const unsigned char *in,
+ int inlen,
+ const unsigned char *additionalData,
+ int additionalDataLen);
+
+/* The DTLS anti-replay window in number of packets. Defined here because we
+ * need it in the cipher spec. Note that this is a ring buffer but left and
+ * right represent the true window, with modular arithmetic used to map them
+ * onto the buffer.
+ */
+#define DTLS_RECVD_RECORDS_WINDOW 1024
+#define RECORD_SEQ_MASK ((1ULL << 48) - 1)
+#define RECORD_SEQ_MAX RECORD_SEQ_MASK
+PR_STATIC_ASSERT(DTLS_RECVD_RECORDS_WINDOW % 8 == 0);
+
+typedef struct DTLSRecvdRecordsStr {
+ unsigned char data[DTLS_RECVD_RECORDS_WINDOW / 8];
+ sslSequenceNumber left;
+ sslSequenceNumber right;
+} DTLSRecvdRecords;
+
+/*
+ * These are the "specs" used for reading and writing records. Access to the
+ * pointers to these specs, and all the specs' contents (direct and indirect) is
+ * protected by the reader/writer lock ss->specLock.
+ */
+struct ssl3CipherSpecStr {
+ PRCList link;
+ PRUint8 refCt;
+
+ CipherSpecDirection direction;
+ SSL3ProtocolVersion version;
+ SSL3ProtocolVersion recordVersion;
+
+ const ssl3BulkCipherDef *cipherDef;
+ const ssl3MACDef *macDef;
+
+ SSLCipher cipher;
+ SSLAEADCipher aead;
+ void *cipherContext;
+
+ PK11SymKey *masterSecret;
+ ssl3KeyMaterial keyMaterial;
+
+ DTLSEpoch epoch;
+ const char *phase;
+ sslSequenceNumber seqNum;
+ DTLSRecvdRecords recvdRecords;
+
+ /* The number of 0-RTT bytes that can be sent or received in TLS 1.3. This
+ * will be zero for everything but 0-RTT. */
+ PRUint32 earlyDataRemaining;
+};
+
+typedef void (*sslCipherSpecChangedFunc)(void *arg,
+ PRBool sending,
+ ssl3CipherSpec *newSpec);
+
+const ssl3BulkCipherDef *ssl_GetBulkCipherDef(const ssl3CipherSuiteDef *cipher_def);
+const ssl3MACDef *ssl_GetMacDefByAlg(SSL3MACAlgorithm mac);
+const ssl3MACDef *ssl_GetMacDef(const sslSocket *ss, const ssl3CipherSuiteDef *suiteDef);
+
+ssl3CipherSpec *ssl_CreateCipherSpec(sslSocket *ss, CipherSpecDirection direction);
+void ssl_SaveCipherSpec(sslSocket *ss, ssl3CipherSpec *spec);
+void ssl_CipherSpecAddRef(ssl3CipherSpec *spec);
+void ssl_CipherSpecRelease(ssl3CipherSpec *spec);
+void ssl_DestroyCipherSpecs(PRCList *list);
+SECStatus ssl_SetupNullCipherSpec(sslSocket *ss, CipherSpecDirection dir);
+
+ssl3CipherSpec *ssl_FindCipherSpecByEpoch(sslSocket *ss,
+ CipherSpecDirection direction,
+ DTLSEpoch epoch);
+void ssl_CipherSpecReleaseByEpoch(sslSocket *ss, CipherSpecDirection direction,
+ DTLSEpoch epoch);
+
+#endif /* __sslspec_h_ */
diff --git a/security/nss/lib/ssl/sslt.h b/security/nss/lib/ssl/sslt.h
index bd9a2ae88..ce8f6e281 100644
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -13,6 +13,28 @@
#include "secitem.h"
#include "certt.h"
+typedef enum {
+ ssl_hs_hello_request = 0,
+ ssl_hs_client_hello = 1,
+ ssl_hs_server_hello = 2,
+ ssl_hs_hello_verify_request = 3,
+ ssl_hs_new_session_ticket = 4,
+ ssl_hs_end_of_early_data = 5,
+ ssl_hs_hello_retry_request = 6,
+ ssl_hs_encrypted_extensions = 8,
+ ssl_hs_certificate = 11,
+ ssl_hs_server_key_exchange = 12,
+ ssl_hs_certificate_request = 13,
+ ssl_hs_server_hello_done = 14,
+ ssl_hs_certificate_verify = 15,
+ ssl_hs_client_key_exchange = 16,
+ ssl_hs_finished = 20,
+ ssl_hs_certificate_status = 22,
+ ssl_hs_key_update = 24,
+ ssl_hs_next_proto = 67,
+ ssl_hs_message_hash = 254, /* Not a real message. */
+} SSLHandshakeType;
+
typedef struct SSL3StatisticsStr {
/* statistics from ssl3_SendClientHello (sch) */
long sch_sid_cache_hits;
@@ -275,6 +297,14 @@ typedef struct SSLChannelInfoStr {
SSLAuthType authType;
SSLSignatureScheme signatureScheme;
+ /* The following fields were added in NSS 3.34. */
+ /* When the session was resumed this holds the key exchange group of the
+ * original handshake. */
+ SSLNamedGroup originalKeaGroup;
+ /* This field is PR_TRUE when the session is resumed and PR_FALSE
+ * otherwise. */
+ PRBool resumed;
+
/* When adding new fields to this structure, please document the
* NSS version in which they were added. */
} SSLChannelInfo;
@@ -395,16 +425,19 @@ typedef enum {
ssl_padding_xtn = 21,
ssl_extended_master_secret_xtn = 23,
ssl_session_ticket_xtn = 35,
- ssl_tls13_key_share_xtn = 40,
+ /* 40 was used in draft versions of TLS 1.3; it is now reserved. */
ssl_tls13_pre_shared_key_xtn = 41,
ssl_tls13_early_data_xtn = 42,
ssl_tls13_supported_versions_xtn = 43,
ssl_tls13_cookie_xtn = 44,
ssl_tls13_psk_key_exchange_modes_xtn = 45,
- ssl_tls13_ticket_early_data_info_xtn = 46,
- ssl_next_proto_nego_xtn = 13172,
+ ssl_tls13_ticket_early_data_info_xtn = 46, /* Deprecated. */
+ ssl_tls13_certificate_authorities_xtn = 47,
+ ssl_signature_algorithms_cert_xtn = 50,
+ ssl_tls13_key_share_xtn = 51,
+ ssl_next_proto_nego_xtn = 13172, /* Deprecated. */
ssl_renegotiation_info_xtn = 0xff01,
- ssl_tls13_short_header_xtn = 0xff03
+ ssl_tls13_short_header_xtn = 0xff03 /* Deprecated. */
} SSLExtensionType;
/* This is the old name for the supported_groups extensions. */
diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c
index 560493848..1fecaf3f8 100644
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -17,23 +17,14 @@
#include "sslimpl.h"
#include "sslproto.h"
#include "sslerr.h"
+#include "ssl3exthandle.h"
#include "tls13hkdf.h"
#include "tls13con.h"
+#include "tls13err.h"
#include "tls13exthandle.h"
+#include "tls13hashstate.h"
-typedef enum {
- TrafficKeyClearText = 0,
- TrafficKeyEarlyApplicationData = 1,
- TrafficKeyHandshake = 2,
- TrafficKeyApplicationData = 3
-} TrafficKeyType;
-
-typedef enum {
- CipherSpecRead,
- CipherSpecWrite,
-} CipherSpecDirection;
-
-static SECStatus tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type,
+static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
CipherSpecDirection install,
PRBool deleteSecret);
static SECStatus tls13_AESGCM(
@@ -53,8 +44,9 @@ static SECStatus tls13_SendEncryptedExtensions(sslSocket *ss);
static void tls13_SetKeyExchangeType(sslSocket *ss, const sslNamedGroupDef *group);
static SECStatus tls13_HandleClientKeyShare(sslSocket *ss,
TLS13KeyShareEntry *peerShare);
-static SECStatus tls13_SendHelloRetryRequest(sslSocket *ss,
- const sslNamedGroupDef *selectedGroup);
+static SECStatus tls13_SendHelloRetryRequest(
+ sslSocket *ss, const sslNamedGroupDef *selectedGroup,
+ const PRUint8 *token, unsigned int tokenLen);
static SECStatus tls13_HandleServerKeyShare(sslSocket *ss);
static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b,
@@ -62,40 +54,46 @@ static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b,
static SECStatus tls13_SendCertificate(sslSocket *ss);
static SECStatus tls13_HandleCertificate(
sslSocket *ss, PRUint8 *b, PRUint32 length);
+static SECStatus tls13_ReinjectHandshakeTranscript(sslSocket *ss);
static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b,
PRUint32 length);
static SECStatus
tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey);
static SECStatus tls13_HandleCertificateVerify(
- sslSocket *ss, PRUint8 *b, PRUint32 length,
- SSL3Hashes *hashes);
+ sslSocket *ss, PRUint8 *b, PRUint32 length);
static SECStatus tls13_RecoverWrappedSharedSecret(sslSocket *ss,
sslSessionID *sid);
static SECStatus
+tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key,
+ const char *prefix,
+ const char *suffix,
+ const char *keylogLabel,
+ PK11SymKey **dest);
+static SECStatus
tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
- const char *prefix,
- const char *suffix,
+ const char *label,
+ unsigned int labelLen,
const SSL3Hashes *hashes,
PK11SymKey **dest);
static SECStatus tls13_SendEndOfEarlyData(sslSocket *ss);
+static SECStatus tls13_HandleEndOfEarlyData(sslSocket *ss, PRUint8 *b,
+ PRUint32 length);
static SECStatus tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey);
-static SECStatus tls13_ComputePskBinderHash(sslSocket *ss,
- unsigned long prefixLength,
+static SECStatus tls13_ComputePskBinderHash(sslSocket *ss, unsigned int prefix,
SSL3Hashes *hashes);
-static SECStatus tls13_VerifyFinished(sslSocket *ss, SSL3HandshakeType message,
+static SECStatus tls13_VerifyFinished(sslSocket *ss, SSLHandshakeType message,
PK11SymKey *secret,
PRUint8 *b, PRUint32 length,
const SSL3Hashes *hashes);
static SECStatus tls13_ClientHandleFinished(sslSocket *ss,
- PRUint8 *b, PRUint32 length,
- const SSL3Hashes *hashes);
+ PRUint8 *b, PRUint32 length);
static SECStatus tls13_ServerHandleFinished(sslSocket *ss,
- PRUint8 *b, PRUint32 length,
- const SSL3Hashes *hashes);
+ PRUint8 *b, PRUint32 length);
+static SECStatus tls13_SendNewSessionTicket(sslSocket *ss,
+ const PRUint8 *appToken,
+ unsigned int appTokenLen);
static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b,
PRUint32 length);
-static SECStatus tls13_ComputeHandshakeHashes(sslSocket *ss,
- SSL3Hashes *hashes);
static SECStatus tls13_ComputeEarlySecrets(sslSocket *ss);
static SECStatus tls13_ComputeHandshakeSecrets(sslSocket *ss);
static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss);
@@ -107,26 +105,28 @@ static SECStatus tls13_ComputeFinished(
static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
static SECStatus tls13_FinishHandshake(sslSocket *ss);
-const char kHkdfLabelClient[] = "client";
-const char kHkdfLabelServer[] = "server";
-const char kHkdfLabelPskBinderKey[] = "resumption psk binder key";
-const char kHkdfLabelEarlyTrafficSecret[] = "early traffic secret";
-const char kHkdfLabelEarlyExporterSecret[] = "early exporter master secret";
-const char kHkdfLabelHandshakeTrafficSecret[] = "handshake traffic secret";
-const char kHkdfLabelApplicationTrafficSecret[] = "application traffic secret";
+const char kHkdfLabelClient[] = "c";
+const char kHkdfLabelServer[] = "s";
+const char kHkdfLabelDerivedSecret[] = "derived";
+const char kHkdfLabelPskBinderKey[] = "res binder";
+const char kHkdfLabelEarlyTrafficSecret[] = "e traffic";
+const char kHkdfLabelEarlyExporterSecret[] = "e exp master";
+const char kHkdfLabelHandshakeTrafficSecret[] = "hs traffic";
+const char kHkdfLabelApplicationTrafficSecret[] = "ap traffic";
const char kHkdfLabelFinishedSecret[] = "finished";
-const char kHkdfLabelResumptionMasterSecret[] = "resumption master secret";
-const char kHkdfLabelExporterMasterSecret[] = "exporter master secret";
+const char kHkdfLabelResumptionMasterSecret[] = "res master";
+const char kHkdfLabelExporterMasterSecret[] = "exp master";
+const char kHkdfLabelResumption[] = "resumption";
const char kHkdfPurposeKey[] = "key";
const char kHkdfPurposeIv[] = "iv";
-#define TRAFFIC_SECRET(ss, dir, name) ((ss->sec.isServer ^ \
- (dir == CipherSpecWrite)) \
- ? ss->ssl3.hs.client##name \
- : ss->ssl3.hs.server##name)
-
-const SSL3ProtocolVersion kTlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_0;
-const SSL3ProtocolVersion kDtlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_1;
+const char keylogLabelClientEarlyTrafficSecret[] = "CLIENT_EARLY_TRAFFIC_SECRET";
+const char keylogLabelClientHsTrafficSecret[] = "CLIENT_HANDSHAKE_TRAFFIC_SECRET";
+const char keylogLabelServerHsTrafficSecret[] = "SERVER_HANDSHAKE_TRAFFIC_SECRET";
+const char keylogLabelClientTrafficSecret[] = "CLIENT_TRAFFIC_SECRET_0";
+const char keylogLabelServerTrafficSecret[] = "SERVER_TRAFFIC_SECRET_0";
+const char keylogLabelEarlyExporterSecret[] = "EARLY_EXPORTER_SECRET";
+const char keylogLabelExporterSecret[] = "EXPORTER_SECRET";
/* Belt and suspenders in case we ever add a TLS 1.4. */
PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <=
@@ -165,6 +165,7 @@ tls13_HandshakeState(SSL3WaitState st)
switch (st) {
STATE_CASE(idle_handshake);
STATE_CASE(wait_client_hello);
+ STATE_CASE(wait_end_of_early_data);
STATE_CASE(wait_client_cert);
STATE_CASE(wait_client_key);
STATE_CASE(wait_cert_verify);
@@ -336,6 +337,23 @@ tls13_GetHmacMechanism(sslSocket *ss)
}
SECStatus
+tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes,
+ const PRUint8 *buf, unsigned int len)
+{
+ SECStatus rv;
+
+ rv = PK11_HashBuf(ssl3_HashTypeToOID(tls13_GetHash(ss)),
+ hashes->u.raw, buf, len);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+ hashes->len = tls13_GetHashSize(ss);
+
+ return SECSuccess;
+}
+
+SECStatus
tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef)
{
SECStatus rv;
@@ -450,7 +468,8 @@ tls13_SetupClientHello(sslSocket *ss)
return SECFailure;
}
- rv = ssl3_SetCipherSuite(ss, ss->sec.ci.sid->u.ssl3.cipherSuite, PR_FALSE);
+ ss->ssl3.hs.cipher_suite = ss->sec.ci.sid->u.ssl3.cipherSuite;
+ rv = ssl3_SetupCipherSuite(ss, PR_FALSE);
if (rv != SECSuccess) {
FATAL_ERROR(ss, PORT_GetError(), internal_error);
return SECFailure;
@@ -558,9 +577,241 @@ loser:
return SECFailure;
}
+static PRBool
+tls13_UseServerSecret(sslSocket *ss, CipherSpecDirection direction)
+{
+ return ss->sec.isServer == (direction == CipherSpecWrite);
+}
+
+static PK11SymKey **
+tls13_TrafficSecretRef(sslSocket *ss, CipherSpecDirection direction)
+{
+ if (tls13_UseServerSecret(ss, direction)) {
+ return &ss->ssl3.hs.serverTrafficSecret;
+ }
+ return &ss->ssl3.hs.clientTrafficSecret;
+}
+
+SECStatus
+tls13_UpdateTrafficKeys(sslSocket *ss, CipherSpecDirection direction)
+{
+ PK11SymKey **secret;
+ PK11SymKey *updatedSecret;
+ PRUint16 epoch;
+ SECStatus rv;
+
+ secret = tls13_TrafficSecretRef(ss, direction);
+ rv = tls13_HkdfExpandLabel(*secret, tls13_GetHash(ss),
+ NULL, 0,
+ kHkdfLabelApplicationTrafficSecret,
+ strlen(kHkdfLabelApplicationTrafficSecret),
+ tls13_GetHmacMechanism(ss),
+ tls13_GetHashSize(ss),
+ &updatedSecret);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ PK11_FreeSymKey(*secret);
+ *secret = updatedSecret;
+
+ ssl_GetSpecReadLock(ss);
+ if (direction == CipherSpecRead) {
+ epoch = ss->ssl3.crSpec->epoch;
+ } else {
+ epoch = ss->ssl3.cwSpec->epoch;
+ }
+ ssl_ReleaseSpecReadLock(ss);
+
+ if (epoch == PR_UINT16_MAX) {
+ /* Good chance that this is an overflow from too many updates. */
+ FATAL_ERROR(ss, SSL_ERROR_TOO_MANY_KEY_UPDATES, internal_error);
+ return SECFailure;
+ }
+ ++epoch;
+
+ rv = tls13_SetCipherSpec(ss, epoch, direction, PR_FALSE);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request, PRBool buffer)
+{
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: %s send key update, response %s",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss),
+ (request == update_requested) ? "requested"
+ : "not requested"));
+
+ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+ if (!ss->firstHsDone) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_LIBRARY_FAILURE,
+ idle_handshake);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Not supported. */
+ if (IS_DTLS(ss)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ ssl_GetXmitBufLock(ss);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_key_update, 1);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ goto loser;
+ }
+ rv = ssl3_AppendHandshakeNumber(ss, request, 1);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ goto loser;
+ }
+
+ /* If we have been asked to buffer, then do so. This allows us to coalesce
+ * a KeyUpdate with a pending write. */
+ rv = ssl3_FlushHandshake(ss, buffer ? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0);
+ if (rv != SECSuccess) {
+ goto loser; /* error code set by ssl3_FlushHandshake */
+ }
+ ssl_ReleaseXmitBufLock(ss);
+
+ rv = tls13_UpdateTrafficKeys(ss, CipherSpecWrite);
+ if (rv != SECSuccess) {
+ goto loser; /* error code set by tls13_UpdateTrafficKeys */
+ }
+
+ return SECSuccess;
+
+loser:
+ ssl_ReleaseXmitBufLock(ss);
+ return SECFailure;
+}
+
+SECStatus
+SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate)
+{
+ SECStatus rv;
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ if (!ss->firstHsDone) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS,
+ idle_handshake);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ ssl_GetSSL3HandshakeLock(ss);
+ rv = tls13_SendKeyUpdate(ss, requestUpdate ? update_requested : update_not_requested,
+ PR_FALSE /* don't buffer */);
+
+ /* Remember that we are the ones that initiated this KeyUpdate. */
+ if (rv == SECSuccess) {
+ ss->ssl3.peerRequestedKeyUpdate = PR_FALSE;
+ }
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ return rv;
+}
+
+/*
+ * enum {
+ * update_not_requested(0), update_requested(1), (255)
+ * } KeyUpdateRequest;
+ *
+ * struct {
+ * KeyUpdateRequest request_update;
+ * } KeyUpdate;
+ */
+static SECStatus
+tls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length)
+{
+ SECStatus rv;
+ PRUint32 update;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: %s handle key update",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
+
+ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
+ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+ PORT_Assert(ss->firstHsDone);
+ if (!ss->firstHsDone) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, unexpected_message);
+ return SECFailure;
+ }
+
+ rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE,
+ idle_handshake);
+ if (rv != SECSuccess) {
+ /* We should never be idle_handshake prior to firstHsDone. */
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+
+ rv = ssl3_ConsumeHandshakeNumber(ss, &update, 1, &b, &length);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Error code set already. */
+ }
+ if (length != 0) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error);
+ return SECFailure;
+ }
+ if (!(update == update_requested ||
+ update == update_not_requested)) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error);
+ return SECFailure;
+ }
+
+ rv = tls13_UpdateTrafficKeys(ss, CipherSpecRead);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */
+ }
+
+ if (update == update_requested) {
+ PRBool sendUpdate;
+ if (ss->ssl3.peerRequestedKeyUpdate) {
+ /* Only send an update if we have sent with the current spec. This
+ * prevents us from being forced to crank forward pointlessly. */
+ ssl_GetSpecReadLock(ss);
+ sendUpdate = ss->ssl3.cwSpec->seqNum > 0;
+ ssl_ReleaseSpecReadLock(ss);
+ } else {
+ sendUpdate = PR_TRUE;
+ }
+ if (sendUpdate) {
+ /* Respond immediately (don't buffer). */
+ rv = tls13_SendKeyUpdate(ss, update_not_requested, PR_FALSE);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Error already set. */
+ }
+ }
+ ss->ssl3.peerRequestedKeyUpdate = PR_TRUE;
+ }
+
+ return SECSuccess;
+}
+
SECStatus
-tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
- PRUint32 length, SSL3Hashes *hashesPtr)
+tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
if (ss->sec.isServer && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
SSL_TRC(3, ("%d: TLS13[%d]: %s successfully decrypted handshake after"
@@ -571,36 +822,34 @@ tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
/* TODO(ekr@rtfm.com): Would it be better to check all the states here? */
switch (ss->ssl3.hs.msg_type) {
- case certificate:
+ case ssl_hs_certificate:
return tls13_HandleCertificate(ss, b, length);
- case certificate_request:
+ case ssl_hs_certificate_request:
return tls13_HandleCertificateRequest(ss, b, length);
- case certificate_verify:
- if (!hashesPtr) {
- FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY, unexpected_message);
- return SECFailure;
- }
- return tls13_HandleCertificateVerify(ss, b, length, hashesPtr);
+ case ssl_hs_certificate_verify:
+ return tls13_HandleCertificateVerify(ss, b, length);
- case encrypted_extensions:
+ case ssl_hs_encrypted_extensions:
return tls13_HandleEncryptedExtensions(ss, b, length);
- case new_session_ticket:
+ case ssl_hs_new_session_ticket:
return tls13_HandleNewSessionTicket(ss, b, length);
- case finished:
- if (!hashesPtr) {
- FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, unexpected_message);
- return SECFailure;
- }
+ case ssl_hs_finished:
if (ss->sec.isServer) {
- return tls13_ServerHandleFinished(ss, b, length, hashesPtr);
+ return tls13_ServerHandleFinished(ss, b, length);
} else {
- return tls13_ClientHandleFinished(ss, b, length, hashesPtr);
+ return tls13_ClientHandleFinished(ss, b, length);
}
+ case ssl_hs_end_of_early_data:
+ return tls13_HandleEndOfEarlyData(ss, b, length);
+
+ case ssl_hs_key_update:
+ return tls13_HandleKeyUpdate(ss, b, length);
+
default:
FATAL_ERROR(ss, SSL_ERROR_RX_UNKNOWN_HANDSHAKE, unexpected_message);
return SECFailure;
@@ -619,10 +868,6 @@ tls13_RecoverWrappedSharedSecret(sslSocket *ss, sslSessionID *sid)
SSL_TRC(3, ("%d: TLS13[%d]: recovering static secret (%s)",
SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
- if (!sid->u.ssl3.keys.msIsWrapped) {
- PORT_Assert(0); /* I think this can't happen. */
- return SECFailure;
- }
/* Now find the hash used as the PRF for the previous handshake. */
hashType = tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite);
@@ -673,53 +918,55 @@ tls13_RecoverWrappedSharedSecret(sslSocket *ss, sslSessionID *sid)
/* Key Derivation Functions.
*
- * Below is the key schedule from [draft-ietf-tls-tls13].
- *
- * * The relevant functions from this file are indicated by tls13_Foo().
* 0
* |
* v
- * PSK -> HKDF-Extract
+ * PSK -> HKDF-Extract = Early Secret
* |
- * v
- * Early Secret ---> Derive-Secret(., "client early traffic secret",
- * | ClientHello)
- * | = client_early_traffic_secret
- * v
- * (EC)DHE -> HKDF-Extract
+ * +-----> Derive-Secret(., "ext binder" | "res binder", "")
+ * | = binder_key
+ * |
+ * +-----> Derive-Secret(., "c e traffic",
+ * | ClientHello)
+ * | = client_early_traffic_secret
* |
+ * +-----> Derive-Secret(., "e exp master",
+ * | ClientHello)
+ * | = early_exporter_secret
* v
- * Handshake Secret
+ * Derive-Secret(., "derived", "")
* |
- * +---------> Derive-Secret(., "client handshake traffic secret",
- * | ClientHello...ServerHello)
- * | = client_handshake_traffic_secret
+ * v
+ *(EC)DHE -> HKDF-Extract = Handshake Secret
* |
- * +---------> Derive-Secret(., "server handshake traffic secret",
- * | ClientHello...ServerHello)
- * | = server_handshake_traffic_secret
+ * +-----> Derive-Secret(., "c hs traffic",
+ * | ClientHello...ServerHello)
+ * | = client_handshake_traffic_secret
* |
+ * +-----> Derive-Secret(., "s hs traffic",
+ * | ClientHello...ServerHello)
+ * | = server_handshake_traffic_secret
* v
- * 0 -> HKDF-Extract
+ * Derive-Secret(., "derived", "")
* |
* v
- * Master Secret
+ * 0 -> HKDF-Extract = Master Secret
* |
- * +---------> Derive-Secret(., "client application traffic secret",
- * | ClientHello...Server Finished)
- * | = client_traffic_secret_0
+ * +-----> Derive-Secret(., "c ap traffic",
+ * | ClientHello...Server Finished)
+ * | = client_traffic_secret_0
* |
- * +---------> Derive-Secret(., "server application traffic secret",
- * | ClientHello...Server Finished)
- * | = server_traffic_secret_0
+ * +-----> Derive-Secret(., "s ap traffic",
+ * | ClientHello...Server Finished)
+ * | = server_traffic_secret_0
* |
- * +---------> Derive-Secret(., "exporter master secret",
- * | ClientHello...Client Finished)
- * | = exporter_secret
+ * +-----> Derive-Secret(., "exp master",
+ * | ClientHello...Server Finished)
+ * | = exporter_secret
* |
- * +---------> Derive-Secret(., "resumption master secret",
- * ClientHello...Client Finished)
- * = resumption_secret
+ * +-----> Derive-Secret(., "res master",
+ * ClientHello...Client Finished)
+ * = resumption_master_secret
*
*/
@@ -742,35 +989,43 @@ tls13_ComputeEarlySecrets(sslSocket *ss)
PORT_Assert(ss->statelessResume == (ss->ssl3.hs.resumptionMasterSecret != NULL));
if (ss->statelessResume) {
- PRUint8 buf[1] = { 0 };
- SSL3Hashes hashes;
-
PK11_FreeSymKey(ss->ssl3.hs.resumptionMasterSecret);
ss->ssl3.hs.resumptionMasterSecret = NULL;
- rv = PK11_HashBuf(ssl3_HashTypeToOID(tls13_GetHash(ss)),
- hashes.u.raw, buf, 0);
+ rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelPskBinderKey,
+ strlen(kHkdfLabelPskBinderKey),
+ &ss->ssl3.hs.pskBinderKey);
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
return SECFailure;
}
- hashes.len = tls13_GetHashSize(ss);
+ }
+ PORT_Assert(!ss->ssl3.hs.resumptionMasterSecret);
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- NULL, kHkdfLabelPskBinderKey, &hashes,
- &ss->ssl3.hs.pskBinderKey);
- if (rv != SECSuccess) {
- return SECFailure;
- }
+ return SECSuccess;
+}
+
+/* This derives the early traffic and early exporter secrets. */
+static SECStatus
+tls13_DeriveEarlySecrets(sslSocket *ss)
+{
+ SECStatus rv;
+
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelClient,
+ kHkdfLabelEarlyTrafficSecret,
+ keylogLabelClientEarlyTrafficSecret,
+ &ss->ssl3.hs.clientEarlyTrafficSecret);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
NULL, kHkdfLabelEarlyExporterSecret,
- &hashes, &ss->ssl3.hs.earlyExporterSecret);
- if (rv != SECSuccess) {
- return SECFailure;
- }
- } else {
- PORT_Assert(!ss->ssl3.hs.resumptionMasterSecret);
+ keylogLabelEarlyExporterSecret,
+ &ss->ssl3.hs.earlyExporterSecret);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
return SECSuccess;
@@ -780,6 +1035,7 @@ static SECStatus
tls13_ComputeHandshakeSecrets(sslSocket *ss)
{
SECStatus rv;
+ PK11SymKey *derivedSecret = NULL;
PK11SymKey *newSecret = NULL;
SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secrets (%s)",
@@ -788,8 +1044,21 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
/* First update |currentSecret| to add |dheSecret|, if any. */
PORT_Assert(ss->ssl3.hs.currentSecret);
PORT_Assert(ss->ssl3.hs.dheSecret);
- rv = tls13_HkdfExtract(ss->ssl3.hs.currentSecret, ss->ssl3.hs.dheSecret,
+
+ /* Expand before we extract. */
+ rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelDerivedSecret,
+ strlen(kHkdfLabelDerivedSecret),
+ &derivedSecret);
+ if (rv != SECSuccess) {
+ LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+ return rv;
+ }
+
+ rv = tls13_HkdfExtract(derivedSecret, ss->ssl3.hs.dheSecret,
tls13_GetHash(ss), &newSecret);
+ PK11_FreeSymKey(derivedSecret);
+
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return rv;
@@ -800,18 +1069,20 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
ss->ssl3.hs.currentSecret = newSecret;
/* Now compute |*HsTrafficSecret| */
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- kHkdfLabelClient,
- kHkdfLabelHandshakeTrafficSecret, NULL,
- &ss->ssl3.hs.clientHsTrafficSecret);
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelClient,
+ kHkdfLabelHandshakeTrafficSecret,
+ keylogLabelClientHsTrafficSecret,
+ &ss->ssl3.hs.clientHsTrafficSecret);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return rv;
}
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- kHkdfLabelServer,
- kHkdfLabelHandshakeTrafficSecret, NULL,
- &ss->ssl3.hs.serverHsTrafficSecret);
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelServer,
+ kHkdfLabelHandshakeTrafficSecret,
+ keylogLabelServerHsTrafficSecret,
+ &ss->ssl3.hs.serverHsTrafficSecret);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return rv;
@@ -822,11 +1093,19 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
/* Crank HKDF forward to make master secret, which we
* stuff in current secret. */
- rv = tls13_HkdfExtract(ss->ssl3.hs.currentSecret,
+ rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelDerivedSecret,
+ strlen(kHkdfLabelDerivedSecret),
+ &derivedSecret);
+ if (rv != SECSuccess) {
+ LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+ return rv;
+ }
+ rv = tls13_HkdfExtract(derivedSecret,
NULL,
tls13_GetHash(ss),
&newSecret);
-
+ PK11_FreeSymKey(derivedSecret);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
@@ -842,26 +1121,27 @@ tls13_ComputeApplicationSecrets(sslSocket *ss)
{
SECStatus rv;
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- kHkdfLabelClient,
- kHkdfLabelApplicationTrafficSecret,
- NULL,
- &ss->ssl3.hs.clientTrafficSecret);
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelClient,
+ kHkdfLabelApplicationTrafficSecret,
+ keylogLabelClientTrafficSecret,
+ &ss->ssl3.hs.clientTrafficSecret);
if (rv != SECSuccess) {
return SECFailure;
}
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- kHkdfLabelServer,
- kHkdfLabelApplicationTrafficSecret,
- NULL,
- &ss->ssl3.hs.serverTrafficSecret);
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ kHkdfLabelServer,
+ kHkdfLabelApplicationTrafficSecret,
+ keylogLabelServerTrafficSecret,
+ &ss->ssl3.hs.serverTrafficSecret);
if (rv != SECSuccess) {
return SECFailure;
}
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- NULL, kHkdfLabelExporterMasterSecret,
- NULL, &ss->ssl3.hs.exporterSecret);
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ NULL, kHkdfLabelExporterMasterSecret,
+ keylogLabelExporterSecret,
+ &ss->ssl3.hs.exporterSecret);
if (rv != SECSuccess) {
return SECFailure;
}
@@ -873,30 +1153,20 @@ static SECStatus
tls13_ComputeFinalSecrets(sslSocket *ss)
{
SECStatus rv;
- PK11SymKey *resumptionMasterSecret = NULL;
- PORT_Assert(!ss->ssl3.crSpec->master_secret);
- PORT_Assert(!ss->ssl3.cwSpec->master_secret);
+ PORT_Assert(!ss->ssl3.crSpec->masterSecret);
+ PORT_Assert(!ss->ssl3.cwSpec->masterSecret);
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- NULL, kHkdfLabelResumptionMasterSecret,
- NULL, &resumptionMasterSecret);
+ rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
+ NULL, kHkdfLabelResumptionMasterSecret,
+ NULL,
+ &ss->ssl3.hs.resumptionMasterSecret);
PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
ss->ssl3.hs.currentSecret = NULL;
if (rv != SECSuccess) {
return SECFailure;
}
- /* This is pretty gross. TLS 1.3 uses a number of master secrets:
- * The master secret to generate the keys and then the resumption
- * master secret for future connections. To make this work without
- * refactoring too much of the SSLv3 code, we store the RMS in
- * |crSpec->master_secret| and |cwSpec->master_secret|.
- */
- ss->ssl3.crSpec->master_secret = resumptionMasterSecret;
- ss->ssl3.cwSpec->master_secret =
- PK11_ReferenceSymKey(ss->ssl3.crSpec->master_secret);
-
return SECSuccess;
}
@@ -909,6 +1179,8 @@ tls13_RestoreCipherInfo(sslSocket *ss, sslSessionID *sid)
*/
ss->sec.authType = sid->authType;
ss->sec.authKeyBits = sid->authKeyBits;
+ ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
+ ss->sec.signatureScheme = sid->sigScheme;
}
/* Check whether resumption-PSK is allowed. */
@@ -961,6 +1233,10 @@ tls13_CanNegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid)
&sid->u.ssl3.alpnSelection) != 0)
return PR_FALSE;
+ if (tls13_IsReplay(ss, sid)) {
+ return PR_FALSE;
+ }
+
return PR_TRUE;
}
@@ -1046,7 +1322,9 @@ tls13_FindKeyShareEntry(sslSocket *ss, const sslNamedGroupDef *group)
}
static SECStatus
-tls13_NegotiateKeyExchange(sslSocket *ss, TLS13KeyShareEntry **clientShare)
+tls13_NegotiateKeyExchange(sslSocket *ss,
+ const sslNamedGroupDef **requestedGroup,
+ TLS13KeyShareEntry **clientShare)
{
unsigned int index;
TLS13KeyShareEntry *entry = NULL;
@@ -1126,13 +1404,16 @@ tls13_NegotiateKeyExchange(sslSocket *ss, TLS13KeyShareEntry **clientShare)
SSL_TRC(3, ("%d: TLS13[%d]: group = %d", SSL_GETPID(), ss->fd,
preferredGroup->name));
- if (!entry) {
- return tls13_SendHelloRetryRequest(ss, preferredGroup);
+ /* Either provide a share, or provide a group that should be requested in a
+ * HelloRetryRequest, but not both. */
+ if (entry) {
+ PORT_Assert(preferredGroup == entry->group);
+ *clientShare = entry;
+ *requestedGroup = NULL;
+ } else {
+ *clientShare = NULL;
+ *requestedGroup = preferredGroup;
}
-
- PORT_Assert(preferredGroup == entry->group);
- *clientShare = entry;
-
return SECSuccess;
}
@@ -1190,8 +1471,8 @@ tls13_SelectServerCert(sslSocket *ss)
rv = ssl_PickSignatureScheme(ss,
cert->serverKeyPair->pubKey,
cert->serverKeyPair->privKey,
- ss->xtnData.clientSigSchemes,
- ss->xtnData.numClientSigScheme,
+ ss->xtnData.sigSchemes,
+ ss->xtnData.numSigSchemes,
PR_FALSE);
if (rv == SECSuccess) {
/* Found one. */
@@ -1208,6 +1489,62 @@ tls13_SelectServerCert(sslSocket *ss)
return SECFailure;
}
+/* Note: |requestedGroup| is non-NULL when we send a key_share extension. */
+static SECStatus
+tls13_MaybeSendHelloRetry(sslSocket *ss, const sslNamedGroupDef *requestedGroup,
+ PRBool *hrrSent)
+{
+ SSLHelloRetryRequestAction action = ssl_hello_retry_accept;
+ PRUint8 token[256] = { 0 };
+ unsigned int tokenLen = 0;
+ SECStatus rv;
+
+ if (ss->hrrCallback) {
+ action = ss->hrrCallback(!ss->ssl3.hs.helloRetry,
+ ss->xtnData.applicationToken.data,
+ ss->xtnData.applicationToken.len,
+ token, &tokenLen, sizeof(token),
+ ss->hrrCallbackArg);
+ }
+
+ /* These use SSL3_SendAlert directly to avoid an assertion in
+ * tls13_FatalError(), which is ordinarily OK. */
+ if (action == ssl_hello_retry_request && ss->ssl3.hs.helloRetry) {
+ (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
+ PORT_SetError(SSL_ERROR_APP_CALLBACK_ERROR);
+ return SECFailure;
+ }
+
+ if (action != ssl_hello_retry_request && tokenLen) {
+ (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
+ PORT_SetError(SSL_ERROR_APP_CALLBACK_ERROR);
+ return SECFailure;
+ }
+
+ if (tokenLen > sizeof(token)) {
+ (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
+ PORT_SetError(SSL_ERROR_APP_CALLBACK_ERROR);
+ return SECFailure;
+ }
+
+ if (action == ssl_hello_retry_fail) {
+ FATAL_ERROR(ss, SSL_ERROR_APPLICATION_ABORT, handshake_failure);
+ return SECFailure;
+ }
+
+ if (!requestedGroup && action != ssl_hello_retry_request) {
+ return SECSuccess;
+ }
+
+ rv = tls13_SendHelloRetryRequest(ss, requestedGroup, token, tokenLen);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Code already set. */
+ }
+
+ *hrrSent = PR_TRUE;
+ return SECSuccess;
+}
+
static SECStatus
tls13_NegotiateAuthentication(sslSocket *ss)
{
@@ -1237,13 +1574,19 @@ tls13_NegotiateAuthentication(sslSocket *ss)
SECStatus
tls13_HandleClientHelloPart2(sslSocket *ss,
const SECItem *suites,
- sslSessionID *sid)
+ sslSessionID *sid,
+ const PRUint8 *msg,
+ unsigned int len)
{
SECStatus rv;
SSL3Statistics *ssl3stats = SSL_GetStatistics();
+ const sslNamedGroupDef *requestedGroup = NULL;
TLS13KeyShareEntry *clientShare = NULL;
- int j;
- ssl3CipherSuite previousCipherSuite;
+ ssl3CipherSuite previousCipherSuite = 0;
+ const sslNamedGroupDef *previousGroup = NULL;
+ PRBool hrr = PR_FALSE;
+
+ ss->ssl3.hs.endOfFlight = PR_TRUE;
if (ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) {
ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
@@ -1251,24 +1594,59 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
#ifndef PARANOID
/* Look for a matching cipher suite. */
- j = ssl3_config_match_init(ss);
- if (j <= 0) { /* no ciphers are working/supported by PK11 */
+ if (ssl3_config_match_init(ss) == 0) { /* no ciphers are working/supported by PK11 */
FATAL_ERROR(ss, PORT_GetError(), internal_error);
goto loser;
}
#endif
- previousCipherSuite = ss->ssl3.hs.cipher_suite;
+ /* Negotiate cipher suite. */
rv = ssl3_NegotiateCipherSuite(ss, suites, PR_FALSE);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
goto loser;
}
+
/* If we are going around again, then we should make sure that the cipher
* suite selection doesn't change. That's a sign of client shennanigans. */
- if (ss->ssl3.hs.helloRetry &&
- ss->ssl3.hs.cipher_suite != previousCipherSuite) {
- FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, handshake_failure);
+ if (ss->ssl3.hs.helloRetry) {
+
+ /* Update sequence numbers before checking the cookie so that any alerts
+ * we generate are sent with the right sequence numbers. */
+ if (IS_DTLS(ss)) {
+ /* Count the first ClientHello and the HelloRetryRequest. */
+ ss->ssl3.hs.sendMessageSeq = 1;
+ ss->ssl3.hs.recvMessageSeq = 1;
+ ssl_GetSpecWriteLock(ss);
+ /* Increase the write sequence number. The read sequence number
+ * will be reset after this to early data or handshake. */
+ ss->ssl3.cwSpec->seqNum = 1;
+ ssl_ReleaseSpecWriteLock(ss);
+ }
+
+ if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_cookie_xtn) ||
+ !ss->xtnData.cookie.len) {
+ FATAL_ERROR(ss, SSL_ERROR_MISSING_COOKIE_EXTENSION,
+ missing_extension);
+ goto loser;
+ }
+ PRINT_BUF(50, (ss, "Client sent cookie",
+ ss->xtnData.cookie.data, ss->xtnData.cookie.len));
+
+ rv = tls13_RecoverHashState(ss, ss->xtnData.cookie.data,
+ ss->xtnData.cookie.len,
+ &previousCipherSuite,
+ &previousGroup);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter);
+ goto loser;
+ }
+ }
+
+ /* Now merge the ClientHello into the hash state. */
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_client_hello, msg, len);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
goto loser;
}
@@ -1296,13 +1674,50 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
}
/* Select key exchange. */
- rv = tls13_NegotiateKeyExchange(ss, &clientShare);
+ rv = tls13_NegotiateKeyExchange(ss, &requestedGroup, &clientShare);
if (rv != SECSuccess) {
goto loser;
}
+ /* We should get either one of these, but not both. */
+ PORT_Assert((requestedGroup && !clientShare) ||
+ (!requestedGroup && clientShare));
- /* If we didn't find a client key share, we have to retry. */
- if (!clientShare) {
+ /* After HelloRetryRequest, check consistency of cipher and group. */
+ if (ss->ssl3.hs.helloRetry) {
+ PORT_Assert(previousCipherSuite);
+ if (ss->ssl3.hs.cipher_suite != previousCipherSuite) {
+ FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
+ illegal_parameter);
+ goto loser;
+ }
+ if (!clientShare) {
+ FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
+ illegal_parameter);
+ goto loser;
+ }
+
+ /* If we requested a new key share, check that the client provided just
+ * one of the right type. */
+ if (previousGroup) {
+ if (PR_PREV_LINK(&ss->xtnData.remoteKeyShares) !=
+ PR_NEXT_LINK(&ss->xtnData.remoteKeyShares)) {
+ FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
+ illegal_parameter);
+ goto loser;
+ }
+ if (clientShare->group != previousGroup) {
+ FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
+ illegal_parameter);
+ goto loser;
+ }
+ }
+ }
+
+ rv = tls13_MaybeSendHelloRetry(ss, requestedGroup, &hrr);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (hrr) {
if (sid) { /* Free the sid. */
ss->sec.uncache(sid);
ssl_FreeSID(sid);
@@ -1373,14 +1788,17 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
if (ss->statelessResume) {
SSL3Hashes hashes;
- rv = tls13_ComputePskBinderHash(ss, ss->xtnData.pskBinderPrefixLen,
- &hashes);
+ PORT_Assert(ss->ssl3.hs.messages.len > ss->xtnData.pskBindersLen);
+ rv = tls13_ComputePskBinderHash(
+ ss,
+ ss->ssl3.hs.messages.len - ss->xtnData.pskBindersLen,
+ &hashes);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
goto loser;
}
- rv = tls13_VerifyFinished(ss, client_hello,
+ rv = tls13_VerifyFinished(ss, ssl_hs_client_hello,
ss->ssl3.hs.pskBinderKey,
ss->xtnData.pskBinder.data,
ss->xtnData.pskBinder.len,
@@ -1429,11 +1847,7 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
sid = NULL;
if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- kHkdfLabelClient,
- kHkdfLabelEarlyTrafficSecret,
- NULL, /* Current running hash. */
- &ss->ssl3.hs.clientEarlyTrafficSecret);
+ rv = tls13_DeriveEarlySecrets(ss);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
return SECFailure;
@@ -1458,70 +1872,143 @@ loser:
return SECFailure;
}
-static SECStatus
-tls13_SendHelloRetryRequest(sslSocket *ss, const sslNamedGroupDef *selectedGroup)
+SECStatus
+SSLExp_HelloRetryRequestCallback(PRFileDesc *fd,
+ SSLHelloRetryRequestCallback cb, void *arg)
{
- SECStatus rv;
-
- SSL_TRC(3, ("%d: TLS13[%d]: send hello retry request handshake",
- SSL_GETPID(), ss->fd));
-
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
- /* We asked already, but made no progress. */
- if (ss->ssl3.hs.helloRetry) {
- FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter);
- return SECFailure;
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure; /* Code already set. */
}
- ssl_GetXmitBufLock(ss);
- rv = ssl3_AppendHandshakeHeader(ss, hello_retry_request,
- 2 + /* version */
- 2 + /* extension length */
- 2 + /* group extension id */
- 2 + /* group extension length */
- 2 /* group */);
+ ss->hrrCallback = cb;
+ ss->hrrCallbackArg = arg;
+ return SECSuccess;
+}
+
+/*
+ * struct {
+ * ProtocolVersion server_version;
+ * CipherSuite cipher_suite;
+ * Extension extensions<2..2^16-1>;
+ * } HelloRetryRequest;
+ *
+ * Note: this function takes an empty buffer and returns
+ * a non-empty one on success, in which case the caller must
+ * eventually clean up.
+ */
+SECStatus
+tls13_ConstructHelloRetryRequest(sslSocket *ss,
+ ssl3CipherSuite cipherSuite,
+ const sslNamedGroupDef *selectedGroup,
+ PRUint8 *cookie, unsigned int cookieLen,
+ sslBuffer *buffer)
+{
+ SECStatus rv;
+ sslBuffer extensionsBuf = SSL_BUFFER_EMPTY;
+ PORT_Assert(buffer->len == 0);
+
+ /* Note: cookie is pointing to a stack variable, so is only valid
+ * now. */
+ ss->xtnData.selectedGroup = selectedGroup;
+ ss->xtnData.cookie.data = cookie;
+ ss->xtnData.cookie.len = cookieLen;
+ rv = ssl_ConstructExtensions(ss, &extensionsBuf,
+ ssl_hs_hello_retry_request);
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
goto loser;
}
+ /* These extensions can't be empty. */
+ PORT_Assert(SSL_BUFFER_LEN(&extensionsBuf) > 0);
- rv = ssl3_AppendHandshakeNumber(
- ss, tls13_EncodeDraftVersion(ss->version), 2);
+ /* Clean up cookie so we're not pointing at random memory. */
+ ss->xtnData.cookie.data = NULL;
+ ss->xtnData.cookie.len = 0;
+
+ rv = ssl_ConstructServerHello(ss, PR_TRUE, &extensionsBuf, buffer);
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
goto loser;
}
+ sslBuffer_Clear(&extensionsBuf);
+ return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&extensionsBuf);
+ sslBuffer_Clear(buffer);
+ return SECFailure;
+}
+
+static SECStatus
+tls13_SendHelloRetryRequest(sslSocket *ss,
+ const sslNamedGroupDef *requestedGroup,
+ const PRUint8 *appToken, unsigned int appTokenLen)
+{
+ SECStatus rv;
+ unsigned int cookieLen;
+ PRUint8 cookie[1024];
+ sslBuffer messageBuf = SSL_BUFFER_EMPTY;
- /* Length of extensions. */
- rv = ssl3_AppendHandshakeNumber(ss, 2 + 2 + 2, 2);
+ SSL_TRC(3, ("%d: TLS13[%d]: send hello retry request handshake",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+ /* Compute the cookie we are going to need. */
+ rv = tls13_MakeHrrCookie(ss, requestedGroup,
+ appToken, appTokenLen,
+ cookie, &cookieLen, sizeof(cookie));
if (rv != SECSuccess) {
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
- goto loser;
+ return SECFailure;
}
- /* Key share extension - currently the only reason we send this. */
- rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2);
+ /* Now build the body of the message. */
+ rv = tls13_ConstructHelloRetryRequest(ss, ss->ssl3.hs.cipher_suite,
+ requestedGroup,
+ cookie, cookieLen, &messageBuf);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
- goto loser;
+ return SECFailure;
}
- /* Key share extension length. */
- rv = ssl3_AppendHandshakeNumber(ss, 2, 2);
+
+ /* And send it. */
+ ssl_GetXmitBufLock(ss);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_hello,
+ SSL_BUFFER_LEN(&messageBuf));
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
goto loser;
}
- rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2);
+ rv = ssl3_AppendBufferToHandshake(ss, &messageBuf);
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
goto loser;
}
+ sslBuffer_Clear(&messageBuf); /* Done with messageBuf */
- rv = ssl3_FlushHandshake(ss, 0);
- if (rv != SECSuccess) {
- goto loser; /* error code set by ssl3_FlushHandshake */
+ if (ss->ssl3.hs.fakeSid.len) {
+ PRInt32 sent;
+
+ PORT_Assert(!IS_DTLS(ss));
+ rv = ssl3_SendChangeCipherSpecsInt(ss);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* ssl3_SendChangeCipherSpecsInt() only flushes to the output buffer, so we
+ * have to force a send. */
+ sent = ssl_SendSavedWriteData(ss);
+ if (sent < 0 && PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
+ PORT_SetError(SSL_ERROR_SOCKET_WRITE_FAILURE);
+ goto loser;
+ }
+ } else {
+ rv = ssl3_FlushHandshake(ss, 0);
+ if (rv != SECSuccess) {
+ goto loser; /* error code set by ssl3_FlushHandshake */
+ }
}
+
+ /* We depend on this being exactly one record and one message. */
+ PORT_Assert(!IS_DTLS(ss) || (ss->ssl3.hs.sendMessageSeq == 1 &&
+ ss->ssl3.cwSpec->seqNum == 1));
ssl_ReleaseXmitBufLock(ss);
ss->ssl3.hs.helloRetry = PR_TRUE;
@@ -1535,6 +2022,7 @@ tls13_SendHelloRetryRequest(sslSocket *ss, const sslNamedGroupDef *selectedGroup
return SECSuccess;
loser:
+ sslBuffer_Clear(&messageBuf);
ssl_ReleaseXmitBufLock(ss);
return SECFailure;
}
@@ -1606,67 +2094,96 @@ static SECStatus
tls13_SendCertificateRequest(sslSocket *ss)
{
SECStatus rv;
- unsigned int calen;
- SECItem *names;
- unsigned int nnames;
- SECItem *name;
- int i;
- PRUint8 sigSchemes[MAX_SIGNATURE_SCHEMES * 2];
- unsigned int sigSchemesLength = 0;
- int length;
+ sslBuffer extensionBuf = SSL_BUFFER_EMPTY;
SSL_TRC(3, ("%d: TLS13[%d]: begin send certificate_request",
SSL_GETPID(), ss->fd));
- rv = ssl3_EncodeSigAlgs(ss, sigSchemes, sizeof(sigSchemes),
- &sigSchemesLength);
+ rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_certificate_request);
if (rv != SECSuccess) {
- return rv;
+ return SECFailure; /* Code already set. */
}
+ /* We should always have at least one of these. */
+ PORT_Assert(SSL_BUFFER_LEN(&extensionBuf) > 0);
- rv = ssl_GetCertificateRequestCAs(ss, &calen, &names, &nnames);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_request,
+ 1 + 0 + /* empty request context */
+ 2 + /* extension length */
+ SSL_BUFFER_LEN(&extensionBuf));
if (rv != SECSuccess) {
- return rv;
+ goto loser; /* err set by AppendHandshake. */
}
- length = 1 + 0 /* length byte for empty request context */ +
- 2 + sigSchemesLength + 2 + calen + 2;
- rv = ssl3_AppendHandshakeHeader(ss, certificate_request, length);
- if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
+ /* Context. */
rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
+ goto loser; /* err set by AppendHandshake. */
}
- rv = ssl3_AppendHandshakeVariable(ss, sigSchemes, sigSchemesLength, 2);
+ /* Extensions. */
+ rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensionBuf, 2);
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
+ goto loser; /* err set by AppendHandshake. */
}
- rv = ssl3_AppendHandshakeNumber(ss, calen, 2);
+
+ sslBuffer_Clear(&extensionBuf);
+ return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&extensionBuf);
+ return SECFailure;
+}
+
+/* [draft-ietf-tls-tls13; S 4.4.1] says:
+ *
+ * Transcript-Hash(ClientHello1, HelloRetryRequest, ... MN) =
+ * Hash(message_hash || // Handshake type
+ * 00 00 Hash.length || // Handshake message length
+ * Hash(ClientHello1) || // Hash of ClientHello1
+ * HelloRetryRequest ... MN)
+ */
+static SECStatus
+tls13_ReinjectHandshakeTranscript(sslSocket *ss)
+{
+ SSL3Hashes hashes;
+ SECStatus rv;
+
+ // First compute the hash.
+ rv = tls13_ComputeHash(ss, &hashes,
+ ss->ssl3.hs.messages.buf,
+ ss->ssl3.hs.messages.len);
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
- for (i = 0, name = names; i < nnames; i++, name++) {
- rv = ssl3_AppendHandshakeVariable(ss, name->data, name->len, 2);
- if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
- }
+ return SECFailure;
}
- rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+
+ // Now re-init the handshake.
+ ssl3_RestartHandshakeHashes(ss);
+
+ // And reinject the message.
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_message_hash,
+ hashes.u.raw, hashes.len);
if (rv != SECSuccess) {
- return rv; /* err set by AppendHandshake. */
+ return SECFailure;
}
return SECSuccess;
}
+static unsigned int
+ssl_ListCount(PRCList *list)
+{
+ unsigned int c = 0;
+ PRCList *cur;
+ for (cur = PR_NEXT_LINK(list); cur != list; cur = PR_NEXT_LINK(cur)) {
+ ++c;
+ }
+ return c;
+}
+
SECStatus
-tls13_HandleHelloRetryRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
+tls13_HandleHelloRetryRequest(sslSocket *ss, const PRUint8 *savedMsg,
+ PRUint32 savedLength)
{
SECStatus rv;
- PRUint32 tmp;
- SSL3ProtocolVersion version;
SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request",
SSL_GETPID(), ss->fd));
@@ -1679,84 +2196,77 @@ tls13_HandleHelloRetryRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
unexpected_message);
return SECFailure;
}
-
- /* Client only. */
- rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST,
- wait_server_hello);
- if (rv != SECSuccess) {
- return SECFailure;
- }
-
- /* Fool me once, shame on you; fool me twice... */
- if (ss->ssl3.hs.helloRetry) {
- FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST,
- unexpected_message);
- return SECFailure;
- }
+ PORT_Assert(ss->ssl3.hs.ws == wait_server_hello);
if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) {
ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored;
/* Restore the null cipher spec for writing. */
ssl_GetSpecWriteLock(ss);
- tls13_CipherSpecRelease(ss->ssl3.cwSpec);
- ss->ssl3.cwSpec = ss->ssl3.crSpec;
- PORT_Assert(ss->ssl3.cwSpec->cipher_def->cipher == cipher_null);
+ ssl_CipherSpecRelease(ss->ssl3.cwSpec);
+ ss->ssl3.cwSpec = ssl_FindCipherSpecByEpoch(ss, CipherSpecWrite,
+ TrafficKeyClearText);
+ PORT_Assert(ss->ssl3.cwSpec);
ssl_ReleaseSpecWriteLock(ss);
} else {
PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none);
}
- /* Version. */
- rv = ssl_ClientReadVersion(ss, &b, &length, &version);
- if (rv != SECSuccess) {
- return SECFailure; /* alert already sent */
- }
- if (version > ss->vrange.max || version < SSL_LIBRARY_VERSION_TLS_1_3) {
- FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
- protocol_version);
- return SECFailure;
- }
-
- /* Extensions. */
- rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, &b, &length);
- if (rv != SECSuccess) {
- return SECFailure; /* error code already set */
- }
- /* Extensions must be non-empty and use the remainder of the message.
- * This means that a HelloRetryRequest cannot be a no-op: we must have an
- * extension, it must be one that we understand and recognize as being valid
- * for HelloRetryRequest, and all the extensions we permit cause us to
- * modify our ClientHello in some way. */
- if (!tmp || tmp != length) {
+ /* Extensions must contain more than just supported_versions. This will
+ * ensure that a HelloRetryRequest isn't a no-op: we must have at least two
+ * extensions, supported_versions plus one other. That other must be one
+ * that we understand and recognize as being valid for HelloRetryRequest,
+ * and all the extensions we permit cause us to modify our second
+ * ClientHello in some meaningful way. */
+ if (ssl_ListCount(&ss->ssl3.hs.remoteExtensions) <= 1) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
decode_error);
return SECFailure;
}
- rv = ssl3_HandleExtensions(ss, &b, &length, hello_retry_request);
+ rv = ssl3_HandleParsedExtensions(ss, ssl_hs_hello_retry_request);
+ ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
if (rv != SECSuccess) {
return SECFailure; /* Error code set below */
}
ss->ssl3.hs.helloRetry = PR_TRUE;
+ rv = tls13_ReinjectHandshakeTranscript(ss);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_server_hello,
+ savedMsg, savedLength);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
ssl_GetXmitBufLock(ss);
+ if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss) &&
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_none) {
+ rv = ssl3_SendChangeCipherSpecsInt(ss);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
rv = ssl3_SendClientHello(ss, client_hello_retry);
- ssl_ReleaseXmitBufLock(ss);
if (rv != SECSuccess) {
- return SECFailure;
+ goto loser;
}
+ ssl_ReleaseXmitBufLock(ss);
return SECSuccess;
+
+loser:
+ ssl_ReleaseXmitBufLock(ss);
+ return SECFailure;
}
static SECStatus
tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
SECStatus rv;
- TLS13CertificateRequest *certRequest = NULL;
SECItem context = { siBuffer, NULL, 0 };
- PLArenaPool *arena;
SECItem extensionsData = { siBuffer, NULL, 0 };
SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_request sequence",
@@ -1775,71 +2285,51 @@ tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
PORT_Assert(ss->ssl3.clientCertChain == NULL);
PORT_Assert(ss->ssl3.clientCertificate == NULL);
PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
- PORT_Assert(ss->ssl3.hs.certificateRequest == NULL);
+ PORT_Assert(!ss->ssl3.hs.clientCertRequested);
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if (!arena) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
+ if (rv != SECSuccess) {
return SECFailure;
}
- rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
- if (rv != SECSuccess)
- goto loser;
-
/* We don't support post-handshake client auth, the certificate request
- * context must always be null. */
+ * context must always be empty. */
if (context.len > 0) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST, illegal_parameter);
- goto loser;
- }
-
- certRequest = PORT_ArenaZNew(arena, TLS13CertificateRequest);
- if (!certRequest)
- goto loser;
- certRequest->arena = arena;
- certRequest->ca_list.arena = arena;
-
- rv = ssl_ParseSignatureSchemes(ss, arena,
- &certRequest->signatureSchemes,
- &certRequest->signatureSchemeCount,
- &b, &length);
- if (rv != SECSuccess || certRequest->signatureSchemeCount == 0) {
- FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST,
- decode_error);
- goto loser;
+ return SECFailure;
}
- rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, arena,
- &certRequest->ca_list);
- if (rv != SECSuccess)
- goto loser; /* alert already sent */
-
- /* Verify that the extensions are sane. */
rv = ssl3_ConsumeHandshakeVariable(ss, &extensionsData, 2, &b, &length);
if (rv != SECSuccess) {
- goto loser;
+ return SECFailure;
+ }
+
+ if (length) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST, decode_error);
+ return SECFailure;
}
- /* Process all the extensions (note: currently a no-op). */
+ /* Process all the extensions. */
rv = ssl3_HandleExtensions(ss, &extensionsData.data, &extensionsData.len,
- certificate_request);
+ ssl_hs_certificate_request);
if (rv != SECSuccess) {
- goto loser;
+ return SECFailure;
}
- rv = SECITEM_CopyItem(arena, &certRequest->context, &context);
- if (rv != SECSuccess)
- goto loser;
+ if (!ss->xtnData.numSigSchemes) {
+ FATAL_ERROR(ss, SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION,
+ missing_extension);
+ return SECFailure;
+ }
- TLS13_SET_HS_STATE(ss, wait_server_cert);
- ss->ssl3.hs.certificateRequest = certRequest;
+ rv = SECITEM_CopyItem(NULL, &ss->xtnData.certReqContext, &context);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ ss->ssl3.hs.clientCertRequested = PR_TRUE;
+ TLS13_SET_HS_STATE(ss, wait_server_cert);
return SECSuccess;
-
-loser:
- PORT_FreeArena(arena, PR_FALSE);
- return SECFailure;
}
static SECStatus
@@ -1859,12 +2349,10 @@ tls13_SendEncryptedServerSequence(sslSocket *ss)
return SECFailure;
}
- ss->ssl3.hs.shortHeaders = ssl3_ExtensionNegotiated(
- ss, ssl_tls13_short_header_xtn);
-
if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
- rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_tls13_early_data_xtn,
- tls13_ServerSendEarlyDataXtn);
+ rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData,
+ ssl_tls13_early_data_xtn,
+ ssl_SendEmptyExtension);
if (rv != SECSuccess) {
return SECFailure; /* Error code set already. */
}
@@ -1917,11 +2405,29 @@ tls13_SendServerHelloSequence(sslSocket *ss)
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
+ rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData,
+ ssl_tls13_supported_versions_xtn,
+ tls13_ServerSendSupportedVersionsXtn);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
rv = ssl3_SendServerHello(ss);
if (rv != SECSuccess) {
return rv; /* err code is set. */
}
+ if (ss->ssl3.hs.fakeSid.len) {
+ PORT_Assert(!IS_DTLS(ss));
+ SECITEM_FreeItem(&ss->ssl3.hs.fakeSid, PR_FALSE);
+ if (!ss->ssl3.hs.helloRetry) {
+ rv = ssl3_SendChangeCipherSpecsInt(ss);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ }
+
rv = tls13_SendEncryptedServerSequence(ss);
if (rv != SECSuccess) {
err = PORT_GetError();
@@ -1953,14 +2459,18 @@ tls13_SendServerHelloSequence(sslSocket *ss)
return SECFailure;
}
+ if (IS_DTLS(ss)) {
+ /* We need this for reading ACKs. */
+ ssl_CipherSpecAddRef(ss->ssl3.crSpec);
+ }
if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
- rv = tls13_SetCipherSpec(ss,
- TrafficKeyEarlyApplicationData,
+ rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData,
CipherSpecRead, PR_TRUE);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
+ TLS13_SET_HS_STATE(ss, wait_end_of_early_data);
} else {
PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none ||
ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored);
@@ -1972,11 +2482,12 @@ tls13_SendServerHelloSequence(sslSocket *ss)
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
+ TLS13_SET_HS_STATE(ss,
+ ss->opt.requestCertificate ? wait_client_cert
+ : wait_finished);
}
- TLS13_SET_HS_STATE(ss,
- ss->opt.requestCertificate ? wait_client_cert
- : wait_finished);
+ ss->ssl3.hs.serverHelloTime = ssl_TimeUsec();
return SECSuccess;
}
@@ -2023,7 +2534,7 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_stateless_resumes);
} else {
/* !PSK */
- if (ssl3_ClientExtensionAdvertised(ss, ssl_tls13_pre_shared_key_xtn)) {
+ if (ssl3_ExtensionAdvertised(ss, ssl_tls13_pre_shared_key_xtn)) {
SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_misses);
}
if (sid->cached == in_client_cache) {
@@ -2068,8 +2579,12 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
return SECFailure; /* error code is set. */
}
- ss->ssl3.hs.shortHeaders = ssl3_ExtensionNegotiated(
- ss, ssl_tls13_short_header_xtn);
+ if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) {
+ /* When we send 0-RTT, we saved the null spec in case we needed it to
+ * send another ClientHello in response to a HelloRetryRequest. Now
+ * that we won't be receiving a HelloRetryRequest, release the spec. */
+ ssl_CipherSpecReleaseByEpoch(ss, CipherSpecWrite, TrafficKeyClearText);
+ }
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
CipherSpecRead, PR_FALSE);
@@ -2171,8 +2686,7 @@ tls13_SendCertificate(sslSocket *ss)
int certChainLen = 0;
int i;
SECItem context = { siBuffer, NULL, 0 };
- PRInt32 extensionsLen = 0;
- PRUint32 maxBytes = 65535;
+ sslBuffer extensionBuf = SSL_BUFFER_EMPTY;
SSL_TRC(3, ("%d: TLS1.3[%d]: send certificate handshake",
SSL_GETPID(), ss->fd));
@@ -2195,26 +2709,28 @@ tls13_SendCertificate(sslSocket *ss)
ss->sec.localCert = CERT_DupCertificate(ss->ssl3.clientCertificate);
}
- /* Get the extensions length. This only applies to the leaf cert,
- * because we don't yet send extensions for non-leaf certs. */
- extensionsLen = ssl3_CallHelloExtensionSenders(
- ss, PR_FALSE, maxBytes, &ss->xtnData.certificateSenders[0]);
-
if (!ss->sec.isServer) {
- PORT_Assert(ss->ssl3.hs.certificateRequest);
- context = ss->ssl3.hs.certificateRequest->context;
+ PORT_Assert(ss->ssl3.hs.clientCertRequested);
+ context = ss->xtnData.certReqContext;
}
if (certChain) {
for (i = 0; i < certChain->len; i++) {
- certChainLen +=
- 3 + certChain->certs[i].len + /* cert length + cert */
- 2 + (!i ? extensionsLen : 0); /* extensions length + extensions */
+ /* Each cert is 3 octet length, cert, and extensions */
+ certChainLen += 3 + certChain->certs[i].len + 2;
+ }
+
+ /* Build the extensions. This only applies to the leaf cert, because we
+ * don't yet send extensions for non-leaf certs. */
+ rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_certificate);
+ if (rv != SECSuccess) {
+ return SECFailure; /* code already set */
}
+ /* extensionBuf.len is only added once, for the leaf cert. */
+ certChainLen += SSL_BUFFER_LEN(&extensionBuf);
}
- rv = ssl3_AppendHandshakeHeader(ss, certificate,
- 1 + context.len +
- 3 + certChainLen);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate,
+ 1 + context.len + 3 + certChainLen);
if (rv != SECSuccess) {
return SECFailure; /* err set by AppendHandshake. */
}
@@ -2222,50 +2738,44 @@ tls13_SendCertificate(sslSocket *ss)
rv = ssl3_AppendHandshakeVariable(ss, context.data,
context.len, 1);
if (rv != SECSuccess) {
- return SECFailure; /* err set by AppendHandshake. */
+ goto loser; /* err set by AppendHandshake. */
}
rv = ssl3_AppendHandshakeNumber(ss, certChainLen, 3);
if (rv != SECSuccess) {
- return SECFailure; /* err set by AppendHandshake. */
+ goto loser; /* err set by AppendHandshake. */
}
if (certChain) {
for (i = 0; i < certChain->len; i++) {
- PRInt32 sentLen;
-
rv = ssl3_AppendHandshakeVariable(ss, certChain->certs[i].data,
certChain->certs[i].len, 3);
if (rv != SECSuccess) {
- return SECFailure; /* err set by AppendHandshake. */
+ goto loser; /* err set by AppendHandshake. */
}
if (i) {
/* Not end-entity. */
rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
if (rv != SECSuccess) {
- return SECFailure; /* err set by AppendHandshake. */
+ goto loser; /* err set by AppendHandshake. */
}
continue;
}
/* End-entity, send extensions. */
- rv = ssl3_AppendHandshakeNumber(ss, extensionsLen, 2);
+ rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensionBuf, 2);
if (rv != SECSuccess) {
- return SECFailure; /* err set by AppendHandshake. */
- }
-
- sentLen = ssl3_CallHelloExtensionSenders(
- ss, PR_TRUE, extensionsLen,
- &ss->xtnData.certificateSenders[0]);
- PORT_Assert(sentLen == extensionsLen);
- if (sentLen != extensionsLen) {
- LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
+ goto loser; /* err set by AppendHandshake. */
}
}
}
+ sslBuffer_Clear(&extensionBuf);
return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&extensionBuf);
+ return SECFailure;
}
static SECStatus
@@ -2293,7 +2803,7 @@ tls13_HandleCertificateEntry(sslSocket *ss, SECItem *data, PRBool first,
if (first && !ss->sec.isServer) {
rv = ssl3_HandleExtensions(ss, &extensionsData.data,
&extensionsData.len,
- certificate);
+ ssl_hs_certificate);
if (rv != SECSuccess) {
return SECFailure;
}
@@ -2351,6 +2861,11 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
if (rv != SECSuccess)
return SECFailure;
+ /* We can ignore any other cleartext from the client. */
+ if (ss->sec.isServer && IS_DTLS(ss)) {
+ ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyClearText);
+ dtls_ReceivedFirstMessageInFlight(ss);
+ }
/* Process the context string */
rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
if (rv != SECSuccess)
@@ -2445,32 +2960,6 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
return ssl3_AuthCertificate(ss); /* sets ss->ssl3.hs.ws */
}
-void
-tls13_CipherSpecAddRef(ssl3CipherSpec *spec)
-{
- ++spec->refCt;
- SSL_TRC(10, ("%d: TLS13[-]: Increment ref ct for spec %d. new ct = %d",
- SSL_GETPID(), spec, spec->refCt));
-}
-
-/* This function is never called on a spec which is on the
- * cipherSpecs list. */
-void
-tls13_CipherSpecRelease(ssl3CipherSpec *spec)
-{
- PORT_Assert(spec->refCt > 0);
- --spec->refCt;
- SSL_TRC(10, ("%d: TLS13[-]: decrement refct for spec %d. phase=%s new ct = %d",
- SSL_GETPID(), spec, spec->phase, spec->refCt));
- if (!spec->refCt) {
- SSL_TRC(10, ("%d: TLS13[-]: Freeing spec %d. phase=%s",
- SSL_GETPID(), spec, spec->phase));
- PR_REMOVE_LINK(&spec->link);
- ssl3_DestroyCipherSpec(spec, PR_TRUE);
- PORT_Free(spec);
- }
-}
-
/* Add context to the hash functions as described in
[draft-ietf-tls-tls13; Section 4.9.1] */
SECStatus
@@ -2539,15 +3028,56 @@ loser:
* HKDF-Expand-Label(Secret, Label,
* Hash(Messages) + Hash(resumption_context), L))
*/
-static SECStatus
+SECStatus
tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
- const char *prefix,
- const char *suffix,
+ const char *label,
+ unsigned int labelLen,
const SSL3Hashes *hashes,
PK11SymKey **dest)
{
SECStatus rv;
- SSL3Hashes hashesTmp;
+
+ rv = tls13_HkdfExpandLabel(key, tls13_GetHash(ss),
+ hashes->u.raw, hashes->len,
+ label, labelLen,
+ tls13_GetHkdfMechanism(ss),
+ tls13_GetHashSize(ss), dest);
+ if (rv != SECSuccess) {
+ LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* Convenience wrapper for the empty hash. */
+SECStatus
+tls13_DeriveSecretNullHash(sslSocket *ss, PK11SymKey *key,
+ const char *label,
+ unsigned int labelLen,
+ PK11SymKey **dest)
+{
+ SSL3Hashes hashes;
+ SECStatus rv;
+ PRUint8 buf[] = { 0 };
+
+ rv = tls13_ComputeHash(ss, &hashes, buf, 0);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return tls13_DeriveSecret(ss, key, label, labelLen, &hashes, dest);
+}
+
+/* Convenience wrapper that lets us supply a separate prefix and suffix. */
+static SECStatus
+tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key,
+ const char *prefix,
+ const char *suffix,
+ const char *keylogLabel,
+ PK11SymKey **dest)
+{
+ SECStatus rv;
+ SSL3Hashes hashes;
char buf[100];
const char *label;
@@ -2566,25 +3096,22 @@ tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
SSL_TRC(3, ("%d: TLS13[%d]: deriving secret '%s'",
SSL_GETPID(), ss->fd, label));
- if (!hashes) {
- rv = tls13_ComputeHandshakeHashes(ss, &hashesTmp);
- if (rv != SECSuccess) {
- PORT_Assert(0); /* Should never fail */
- ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- hashes = &hashesTmp;
+ rv = tls13_ComputeHandshakeHashes(ss, &hashes);
+ if (rv != SECSuccess) {
+ PORT_Assert(0); /* Should never fail */
+ ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
- rv = tls13_HkdfExpandLabel(key, tls13_GetHash(ss),
- hashes->u.raw, hashes->len,
- label, strlen(label),
- tls13_GetHkdfMechanism(ss),
- tls13_GetHashSize(ss), dest);
+ rv = tls13_DeriveSecret(ss, key, label, strlen(label),
+ &hashes, dest);
if (rv != SECSuccess) {
- LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
+
+ if (keylogLabel) {
+ ssl3_RecordKeyLog(ss, keylogLabel, *dest);
+ }
return SECSuccess;
}
@@ -2592,49 +3119,41 @@ tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
static SECStatus
tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *spec,
TrafficKeyType type,
- CipherSpecDirection direction,
PRBool deleteSecret)
{
- size_t keySize = spec->cipher_def->key_size;
- size_t ivSize = spec->cipher_def->iv_size +
- spec->cipher_def->explicit_nonce_size; /* This isn't always going to
- * work, but it does for
- * AES-GCM */
- CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(spec->cipher_def->calg);
+ size_t keySize = spec->cipherDef->key_size;
+ size_t ivSize = spec->cipherDef->iv_size +
+ spec->cipherDef->explicit_nonce_size; /* This isn't always going to
+ * work, but it does for
+ * AES-GCM */
+ CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(spec->cipherDef->calg);
PK11SymKey **prkp = NULL;
PK11SymKey *prk = NULL;
- PRBool clientKey;
- ssl3KeyMaterial *target;
- const char *phase;
+ PRBool clientSecret;
SECStatus rv;
/* These labels are just used for debugging. */
static const char kHkdfPhaseEarlyApplicationDataKeys[] = "early application data";
static const char kHkdfPhaseHandshakeKeys[] = "handshake data";
static const char kHkdfPhaseApplicationDataKeys[] = "application data";
- if (ss->sec.isServer ^ (direction == CipherSpecWrite)) {
- clientKey = PR_TRUE;
- target = &spec->client;
- } else {
- clientKey = PR_FALSE;
- target = &spec->server;
- }
-
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+ clientSecret = !tls13_UseServerSecret(ss, spec->direction);
switch (type) {
case TrafficKeyEarlyApplicationData:
- PORT_Assert(clientKey);
- phase = kHkdfPhaseEarlyApplicationDataKeys;
+ PORT_Assert(clientSecret);
prkp = &ss->ssl3.hs.clientEarlyTrafficSecret;
+ spec->phase = kHkdfPhaseEarlyApplicationDataKeys;
break;
case TrafficKeyHandshake:
- phase = kHkdfPhaseHandshakeKeys;
- prkp = clientKey ? &ss->ssl3.hs.clientHsTrafficSecret : &ss->ssl3.hs.serverHsTrafficSecret;
+ prkp = clientSecret ? &ss->ssl3.hs.clientHsTrafficSecret
+ : &ss->ssl3.hs.serverHsTrafficSecret;
+ spec->phase = kHkdfPhaseHandshakeKeys;
break;
case TrafficKeyApplicationData:
- phase = kHkdfPhaseApplicationDataKeys;
- prkp = clientKey ? &ss->ssl3.hs.clientTrafficSecret : &ss->ssl3.hs.serverTrafficSecret;
+ prkp = clientSecret ? &ss->ssl3.hs.clientTrafficSecret
+ : &ss->ssl3.hs.serverTrafficSecret;
+ spec->phase = kHkdfPhaseApplicationDataKeys;
break;
default:
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
@@ -2644,17 +3163,15 @@ tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *spec,
PORT_Assert(prkp != NULL);
prk = *prkp;
- SSL_TRC(3, ("%d: TLS13[%d]: deriving %s traffic keys phase='%s'",
- SSL_GETPID(), ss->fd,
- (direction == CipherSpecWrite) ? "write" : "read", phase));
- PORT_Assert(phase);
- spec->phase = phase;
+ SSL_TRC(3, ("%d: TLS13[%d]: deriving %s traffic keys epoch=%d (%s)",
+ SSL_GETPID(), ss->fd, SPEC_DIR(spec),
+ spec->epoch, spec->phase));
rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss),
NULL, 0,
kHkdfPurposeKey, strlen(kHkdfPurposeKey),
bulkAlgorithm, keySize,
- &target->write_key);
+ &spec->keyMaterial.key);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
PORT_Assert(0);
@@ -2664,7 +3181,7 @@ tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *spec,
rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss),
NULL, 0,
kHkdfPurposeIv, strlen(kHkdfPurposeIv),
- target->write_iv, ivSize);
+ spec->keyMaterial.iv, ivSize);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
PORT_Assert(0);
@@ -2681,38 +3198,111 @@ loser:
return SECFailure;
}
+void
+tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec)
+{
+ /* Set the record version to pretend to be (D)TLS 1.2. */
+ if (IS_DTLS(ss)) {
+ spec->recordVersion = SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
+ } else {
+ spec->recordVersion = SSL_LIBRARY_VERSION_TLS_1_2;
+ }
+ SSL_TRC(10, ("%d: TLS13[%d]: set spec=%d record version to 0x%04x",
+ SSL_GETPID(), ss->fd, spec, spec->recordVersion));
+}
+
static SECStatus
-tls13_SetupPendingCipherSpec(sslSocket *ss)
+tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
{
- ssl3CipherSpec *pSpec;
ssl3CipherSuite suite = ss->ssl3.hs.cipher_suite;
- const ssl3BulkCipherDef *bulk = ssl_GetBulkCipherDef(
- ssl_LookupCipherSuiteDef(suite));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+ PORT_Assert(spec->epoch);
- ssl_GetSpecWriteLock(ss); /*******************************/
-
- pSpec = ss->ssl3.pwSpec;
/* Version isn't set when we send 0-RTT data. */
- pSpec->version = PR_MAX(SSL_LIBRARY_VERSION_TLS_1_3, ss->version);
+ spec->version = PR_MAX(SSL_LIBRARY_VERSION_TLS_1_3, ss->version);
+
+ ssl_SaveCipherSpec(ss, spec);
+ /* We want to keep read cipher specs around longer because
+ * there are cases where we might get either epoch N or
+ * epoch N+1. */
+ if (IS_DTLS(ss) && spec->direction == CipherSpecRead) {
+ ssl_CipherSpecAddRef(spec);
+ }
SSL_TRC(3, ("%d: TLS13[%d]: Set Pending Cipher Suite to 0x%04x",
SSL_GETPID(), ss->fd, suite));
- pSpec->cipher_def = bulk;
- ssl_ReleaseSpecWriteLock(ss); /*******************************/
+ spec->cipherDef = ssl_GetBulkCipherDef(ssl_LookupCipherSuiteDef(suite));
+ switch (spec->cipherDef->calg) {
+ case ssl_calg_aes_gcm:
+ spec->aead = tls13_AESGCM;
+ break;
+ case ssl_calg_chacha20:
+ spec->aead = tls13_ChaCha20Poly1305;
+ break;
+ default:
+ PORT_Assert(0);
+ return SECFailure;
+ }
+
+ if (spec->epoch == TrafficKeyEarlyApplicationData) {
+ spec->earlyDataRemaining =
+ ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size;
+ }
+
+ tls13_SetSpecRecordVersion(ss, spec);
+ return SECSuccess;
+}
+
+/*
+ * Called before sending alerts to set up the right key on the client.
+ * We might encounter errors during the handshake where the current
+ * key is ClearText or EarlyApplicationData. This
+ * function switches to the Handshake key if possible.
+ */
+SECStatus
+tls13_SetAlertCipherSpec(sslSocket *ss)
+{
+ SECStatus rv;
+
+ if (ss->sec.isServer) {
+ return SECSuccess;
+ }
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+ if (TLS13_IN_HS_STATE(ss, wait_server_hello)) {
+ return SECSuccess;
+ }
+ if ((ss->ssl3.cwSpec->epoch != TrafficKeyClearText) &&
+ (ss->ssl3.cwSpec->epoch != TrafficKeyEarlyApplicationData)) {
+ return SECSuccess;
+ }
+
+ rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
+ CipherSpecWrite, PR_FALSE);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
return SECSuccess;
}
-/* Install a new cipher spec for this direction. */
+/* Install a new cipher spec for this direction.
+ *
+ * During the handshake, the values for |epoch| take values from the
+ * TrafficKeyType enum. Afterwards, key update increments them.
+ */
static SECStatus
-tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type,
+tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
CipherSpecDirection direction, PRBool deleteSecret)
{
+ TrafficKeyType type;
SECStatus rv;
ssl3CipherSpec *spec = NULL;
- ssl3CipherSpec **specp = (direction == CipherSpecRead) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec;
+ ssl3CipherSpec **specp;
+
/* Flush out old handshake data. */
ssl_GetXmitBufLock(ss);
rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
@@ -2722,81 +3312,52 @@ tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type,
}
/* Create the new spec. */
- spec = PORT_ZNew(ssl3CipherSpec);
+ spec = ssl_CreateCipherSpec(ss, direction);
if (!spec) {
- PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
- spec->refCt = 1;
- PR_APPEND_LINK(&spec->link, &ss->ssl3.hs.cipherSpecs);
- ss->ssl3.pwSpec = ss->ssl3.prSpec = spec;
-
- rv = tls13_SetupPendingCipherSpec(ss);
- if (rv != SECSuccess)
- return SECFailure;
-
- switch (spec->cipher_def->calg) {
- case calg_aes_gcm:
- spec->aead = tls13_AESGCM;
- break;
- case calg_chacha20:
- spec->aead = tls13_ChaCha20Poly1305;
- break;
- default:
- PORT_Assert(0);
- return SECFailure;
- break;
+ spec->epoch = epoch;
+ spec->seqNum = 0;
+ if (IS_DTLS(ss)) {
+ dtls_InitRecvdRecords(&spec->recvdRecords);
}
- rv = tls13_DeriveTrafficKeys(ss, spec, type, direction,
- deleteSecret);
+ /* This depends on spec having a valid direction and epoch. */
+ rv = tls13_SetupPendingCipherSpec(ss, spec);
if (rv != SECSuccess) {
- return SECFailure;
- }
-
- /* We use the epoch for cipher suite identification, so increment
- * it in both TLS and DTLS. */
- if ((*specp)->epoch == PR_UINT16_MAX) {
- return SECFailure;
- }
- spec->epoch = (PRUint16)type;
-
- if (!IS_DTLS(ss)) {
- spec->read_seq_num = spec->write_seq_num = 0;
- } else {
- /* The sequence number has the high 16 bits as the epoch. */
- spec->read_seq_num = spec->write_seq_num =
- (sslSequenceNumber)spec->epoch << 48;
-
- dtls_InitRecvdRecords(&spec->recvdRecords);
+ goto loser;
}
- if (type == TrafficKeyEarlyApplicationData) {
- spec->earlyDataRemaining =
- ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size;
+ type = (TrafficKeyType)PR_MIN(TrafficKeyApplicationData, epoch);
+ rv = tls13_DeriveTrafficKeys(ss, spec, type, deleteSecret);
+ if (rv != SECSuccess) {
+ goto loser;
}
/* Now that we've set almost everything up, finally cut over. */
+ specp = (direction == CipherSpecRead) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec;
ssl_GetSpecWriteLock(ss);
- tls13_CipherSpecRelease(*specp); /* May delete old cipher. */
- *specp = spec; /* Overwrite. */
+ ssl_CipherSpecRelease(*specp); /* May delete old cipher. */
+ *specp = spec; /* Overwrite. */
ssl_ReleaseSpecWriteLock(ss);
- SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for phase='%s'.%d dir=%s",
- SSL_GETPID(), ss->fd, SSL_ROLE(ss),
- spec->phase, spec->epoch,
- direction == CipherSpecRead ? "read" : "write"));
+ SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for epoch=%d (%s) dir=%s",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss), spec->epoch,
+ spec->phase, SPEC_DIR(spec)));
if (ss->ssl3.changedCipherSpecFunc) {
ss->ssl3.changedCipherSpecFunc(ss->ssl3.changedCipherSpecArg,
direction == CipherSpecWrite, spec);
}
return SECSuccess;
+
+loser:
+ ssl_CipherSpecRelease(spec);
+ return SECFailure;
}
-static SECStatus
-tls13_ComputeHandshakeHashes(sslSocket *ss,
- SSL3Hashes *hashes)
+SECStatus
+tls13_ComputeHandshakeHashes(sslSocket *ss, SSL3Hashes *hashes)
{
SECStatus rv;
PK11Context *ctx = NULL;
@@ -2816,7 +3377,7 @@ tls13_ComputeHandshakeHashes(sslSocket *ss,
goto loser;
}
- PRINT_BUF(10, (NULL, "Handshake hash computed over saved messages",
+ PRINT_BUF(10, (ss, "Handshake hash computed over saved messages",
ss->ssl3.hs.messages.buf,
ss->ssl3.hs.messages.len));
@@ -2841,6 +3402,8 @@ tls13_ComputeHandshakeHashes(sslSocket *ss,
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
goto loser;
}
+
+ PRINT_BUF(10, (ss, "Handshake hash", hashes->u.raw, hashes->len));
PORT_Assert(hashes->len == tls13_GetHashSize(ss));
PK11_DestroyContext(ctx, PR_TRUE);
@@ -2890,19 +3453,6 @@ tls13_DestroyEarlyData(PRCList *list)
}
}
-void
-tls13_DestroyCipherSpecs(PRCList *list)
-{
- PRCList *cur_p;
-
- while (!PR_CLIST_IS_EMPTY(list)) {
- cur_p = PR_LIST_TAIL(list);
- PR_REMOVE_LINK(cur_p);
- ssl3_DestroyCipherSpec((ssl3CipherSpec *)cur_p, PR_FALSE);
- PORT_Free(cur_p);
- }
-}
-
/* draft-ietf-tls-tls13 Section 5.2.2 specifies the following
* nonce algorithm:
*
@@ -2932,7 +3482,7 @@ tls13_WriteNonce(ssl3KeyMaterial *keys,
size_t i;
PORT_Assert(nonceLen == 12);
- memcpy(nonce, keys->write_iv, 12);
+ memcpy(nonce, keys->iv, 12);
/* XOR the last 8 bytes of the IV with the sequence number. */
PORT_Assert(seqNumLen == 8);
@@ -2962,10 +3512,10 @@ tls13_AEAD(ssl3KeyMaterial *keys, PRBool doDecrypt,
};
if (doDecrypt) {
- rv = PK11_Decrypt(keys->write_key, mechanism, &param,
+ rv = PK11_Decrypt(keys->key, mechanism, &param,
out, &uOutLen, maxout, in, inlen);
} else {
- rv = PK11_Encrypt(keys->write_key, mechanism, &param,
+ rv = PK11_Encrypt(keys->key, mechanism, &param,
out, &uOutLen, maxout, in, inlen);
}
*outlen = (int)uOutLen;
@@ -3062,7 +3612,7 @@ tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b, PRUint32 length)
ss->xtnData.nextProto.data = NULL;
ss->xtnData.nextProtoState = SSL_NEXT_PROTO_NO_SUPPORT;
}
- rv = ssl3_HandleExtensions(ss, &b, &length, encrypted_extensions);
+ rv = ssl3_HandleExtensions(ss, &b, &length, ssl_hs_encrypted_extensions);
if (rv != SECSuccess) {
return SECFailure; /* Error code set below */
}
@@ -3114,10 +3664,8 @@ tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b, PRUint32 length)
static SECStatus
tls13_SendEncryptedExtensions(sslSocket *ss)
{
+ sslBuffer extensions = SSL_BUFFER_EMPTY;
SECStatus rv;
- PRInt32 extensions_len = 0;
- PRInt32 sent_len = 0;
- PRUint32 maxBytes = 65535;
SSL_TRC(3, ("%d: TLS13[%d]: send encrypted extensions handshake",
SSL_GETPID(), ss->fd));
@@ -3125,31 +3673,28 @@ tls13_SendEncryptedExtensions(sslSocket *ss)
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- extensions_len = ssl3_CallHelloExtensionSenders(
- ss, PR_FALSE, maxBytes, &ss->xtnData.encryptedExtensionsSenders[0]);
-
- rv = ssl3_AppendHandshakeHeader(ss, encrypted_extensions,
- extensions_len + 2);
+ rv = ssl_ConstructExtensions(ss, &extensions, ssl_hs_encrypted_extensions);
if (rv != SECSuccess) {
- LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
- rv = ssl3_AppendHandshakeNumber(ss, extensions_len, 2);
+
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_encrypted_extensions,
+ SSL_BUFFER_LEN(&extensions) + 2);
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
+ goto loser;
}
- sent_len = ssl3_CallHelloExtensionSenders(
- ss, PR_TRUE, extensions_len,
- &ss->xtnData.encryptedExtensionsSenders[0]);
- PORT_Assert(sent_len == extensions_len);
- if (sent_len != extensions_len) {
+ rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensions, 2);
+ if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
- PORT_Assert(sent_len == 0);
- return SECFailure;
+ goto loser;
}
-
+ sslBuffer_Clear(&extensions);
return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&extensions);
+ return SECFailure;
}
SECStatus
@@ -3210,7 +3755,7 @@ tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey)
len = buf.len + 2 + 2;
- rv = ssl3_AppendHandshakeHeader(ss, certificate_verify, len);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_verify, len);
if (rv != SECSuccess) {
goto done; /* error code set by AppendHandshake */
}
@@ -3238,14 +3783,14 @@ done:
* Caller must hold Handshake and RecvBuf locks.
*/
SECStatus
-tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
- SSL3Hashes *hashes)
+tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
SECItem signed_hash = { siBuffer, NULL, 0 };
SECStatus rv;
SSLSignatureScheme sigScheme;
SSLHashType hashAlg;
SSL3Hashes tbsHash;
+ SSL3Hashes hashes;
SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_verify handshake",
SSL_GETPID(), ss->fd));
@@ -3257,7 +3802,17 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
if (rv != SECSuccess) {
return SECFailure;
}
- PORT_Assert(hashes);
+
+ rv = tls13_ComputeHandshakeHashes(ss, &hashes);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_certificate_verify, b, length);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
if (rv != SECSuccess) {
@@ -3272,7 +3827,7 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
}
hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
- rv = tls13_AddContextToHashes(ss, hashes, hashAlg, PR_FALSE, &tbsHash);
+ rv = tls13_AddContextToHashes(ss, &hashes, hashAlg, PR_FALSE, &tbsHash);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_DIGEST_FAILURE, internal_error);
return SECFailure;
@@ -3301,13 +3856,11 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
}
/* Request a client certificate now if one was requested. */
- if (ss->ssl3.hs.certificateRequest) {
- TLS13CertificateRequest *req = ss->ssl3.hs.certificateRequest;
-
+ if (ss->ssl3.hs.clientCertRequested) {
PORT_Assert(!ss->sec.isServer);
- rv = ssl3_CompleteHandleCertificateRequest(ss, req->signatureSchemes,
- req->signatureSchemeCount,
- &req->ca_list);
+ rv = ssl3_CompleteHandleCertificateRequest(
+ ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes,
+ &ss->xtnData.certReqAuthorities);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
return rv;
@@ -3320,7 +3873,7 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length,
}
static SECStatus
-tls13_ComputePskBinderHash(sslSocket *ss, unsigned long prefixLength,
+tls13_ComputePskBinderHash(sslSocket *ss, unsigned int prefixLength,
SSL3Hashes *hashes)
{
SECStatus rv;
@@ -3332,38 +3885,75 @@ tls13_ComputePskBinderHash(sslSocket *ss, unsigned long prefixLength,
PRINT_BUF(10, (NULL, "Handshake hash computed over ClientHello prefix",
ss->ssl3.hs.messages.buf, prefixLength));
rv = PK11_HashBuf(ssl3_HashTypeToOID(tls13_GetHash(ss)),
- hashes->u.raw,
- ss->ssl3.hs.messages.buf, prefixLength);
+ hashes->u.raw, ss->ssl3.hs.messages.buf, prefixLength);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
- goto loser;
+ return SECFailure;
}
- hashes->len = tls13_GetHashSize(ss);
- PRINT_BUF(10, (NULL, "PSK Binder hash",
- hashes->u.raw, hashes->len));
+ hashes->len = tls13_GetHashSize(ss);
+ PRINT_BUF(10, (NULL, "PSK Binder hash", hashes->u.raw, hashes->len));
return SECSuccess;
-
-loser:
- return SECFailure;
}
-/* Compute the PSK Binder This is kind of sneaky.*/
+
+/* Compute and inject the PSK Binder for sending.
+ *
+ * When sending a ClientHello, we construct all the extensions with a dummy
+ * value for the binder. To construct the binder, we commit the entire message
+ * up to the point where the binders start. Then we calculate the hash using
+ * the saved message (in ss->ssl3.hs.messages). This is written over the dummy
+ * binder, after which we write the remainder of the binder extension. */
SECStatus
-tls13_ComputePskBinder(sslSocket *ss, PRBool sending,
- unsigned int prefixLength,
- PRUint8 *output, unsigned int *outputLen,
- unsigned int maxOutputLen)
+tls13_WriteExtensionsWithBinder(sslSocket *ss, sslBuffer *extensions)
{
SSL3Hashes hashes;
SECStatus rv;
+ unsigned int size = tls13_GetHashSize(ss);
+ unsigned int prefixLen = extensions->len - size - 3;
+ unsigned int finishedLen;
- rv = tls13_ComputePskBinderHash(ss, prefixLength, &hashes);
- if (rv != SECSuccess)
+ PORT_Assert(extensions->len >= size + 3);
+
+ rv = ssl3_AppendHandshakeNumber(ss, extensions->len, 2);
+ if (rv != SECSuccess) {
return SECFailure;
+ }
- return tls13_ComputeFinished(ss, ss->ssl3.hs.pskBinderKey, &hashes,
- sending, output, outputLen, maxOutputLen);
+ /* Only write the extension up to the point before the binders. Assume that
+ * the pre_shared_key extension is at the end of the buffer. Don't write
+ * the binder, or the lengths that precede it (a 2 octet length for the list
+ * of all binders, plus a 1 octet length for the binder length). */
+ rv = ssl3_AppendHandshake(ss, extensions->buf, prefixLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Calculate the binder based on what has been written out. */
+ rv = tls13_ComputePskBinderHash(ss, ss->ssl3.hs.messages.len, &hashes);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Write the binder into the extensions buffer, over the zeros we reserved
+ * previously. This avoids an allocation and means that we don't need a
+ * separate write for the extra bits that precede the binder. */
+ rv = tls13_ComputeFinished(ss, ss->ssl3.hs.pskBinderKey, &hashes, PR_TRUE,
+ extensions->buf + extensions->len - size,
+ &finishedLen, size);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ PORT_Assert(finishedLen == size);
+
+ /* Write out the remainder of the extension. */
+ rv = ssl3_AppendHandshake(ss, extensions->buf + prefixLen,
+ extensions->len - prefixLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
}
static SECStatus
@@ -3462,7 +4052,7 @@ tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey)
return SECFailure;
}
- rv = ssl3_AppendHandshakeHeader(ss, finished, finishedLen);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_finished, finishedLen);
if (rv != SECSuccess) {
return SECFailure; /* Error code already set. */
}
@@ -3477,7 +4067,7 @@ tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey)
}
static SECStatus
-tls13_VerifyFinished(sslSocket *ss, SSL3HandshakeType message,
+tls13_VerifyFinished(sslSocket *ss, SSLHandshakeType message,
PK11SymKey *secret,
PRUint8 *b, PRUint32 length,
const SSL3Hashes *hashes)
@@ -3500,7 +4090,7 @@ tls13_VerifyFinished(sslSocket *ss, SSL3HandshakeType message,
if (length != finishedLen) {
#ifndef UNSAFE_FUZZER_MODE
- FATAL_ERROR(ss, message == finished ? SSL_ERROR_RX_MALFORMED_FINISHED : SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ FATAL_ERROR(ss, message == ssl_hs_finished ? SSL_ERROR_RX_MALFORMED_FINISHED : SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
#endif
}
@@ -3517,8 +4107,37 @@ tls13_VerifyFinished(sslSocket *ss, SSL3HandshakeType message,
}
static SECStatus
-tls13_ClientHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
- const SSL3Hashes *hashes)
+tls13_CommonHandleFinished(sslSocket *ss, PK11SymKey *key,
+ PRUint8 *b, PRUint32 length)
+{
+ SECStatus rv;
+ SSL3Hashes hashes;
+
+ rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED,
+ wait_finished);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ ss->ssl3.hs.endOfFlight = PR_TRUE;
+
+ rv = tls13_ComputeHandshakeHashes(ss, &hashes);
+ if (rv != SECSuccess) {
+ LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ return tls13_VerifyFinished(ss, ssl_hs_finished,
+ key, b, length, &hashes);
+}
+
+static SECStatus
+tls13_ClientHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
SECStatus rv;
@@ -3528,27 +4147,19 @@ tls13_ClientHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
SSL_TRC(3, ("%d: TLS13[%d]: client handle finished handshake",
SSL_GETPID(), ss->fd));
- rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED,
- wait_finished);
+ rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.serverHsTrafficSecret,
+ b, length);
if (rv != SECSuccess) {
return SECFailure;
}
- rv = tls13_VerifyFinished(ss, finished,
- ss->ssl3.hs.serverHsTrafficSecret,
- b, length, hashes);
- if (rv != SECSuccess)
- return SECFailure;
-
return tls13_SendClientSecondRound(ss);
}
static SECStatus
-tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
- const SSL3Hashes *hashes)
+tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
SECStatus rv;
- PK11SymKey *secret;
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -3556,61 +4167,68 @@ tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length,
SSL_TRC(3, ("%d: TLS13[%d]: server handle finished handshake",
SSL_GETPID(), ss->fd));
- rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, wait_finished);
+ rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.clientHsTrafficSecret,
+ b, length);
if (rv != SECSuccess) {
return SECFailure;
}
- if (TLS13_IN_HS_STATE(ss, wait_finished)) {
- secret = ss->ssl3.hs.clientHsTrafficSecret;
- } else {
- secret = ss->ssl3.hs.clientEarlyTrafficSecret;
+ if (!ss->opt.requestCertificate &&
+ (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) {
+ dtls_ReceivedFirstMessageInFlight(ss);
}
- rv = tls13_VerifyFinished(ss, finished, secret, b, length, hashes);
- if (rv != SECSuccess)
- return SECFailure;
-
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
- CipherSpecRead, PR_TRUE);
+ CipherSpecRead, PR_FALSE);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
return SECFailure;
}
- rv = tls13_FinishHandshake(ss);
+ if (IS_DTLS(ss)) {
+ ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyClearText);
+ /* We need to keep the handshake cipher spec so we can
+ * read re-transmitted client Finished. */
+ rv = dtls_StartTimer(ss, ss->ssl3.hs.hdTimer,
+ DTLS_RETRANSMIT_FINISHED_MS,
+ dtls13_HolddownTimerCb);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ rv = tls13_ComputeFinalSecrets(ss);
if (rv != SECSuccess) {
- return SECFailure; /* Error code and alerts handled below */
+ return SECFailure;
}
+
ssl_GetXmitBufLock(ss);
if (ss->opt.enableSessionTickets) {
- rv = tls13_SendNewSessionTicket(ss);
+ rv = tls13_SendNewSessionTicket(ss, NULL, 0);
if (rv != SECSuccess) {
- ssl_ReleaseXmitBufLock(ss);
- return SECFailure; /* Error code and alerts handled below */
+ goto loser;
}
rv = ssl3_FlushHandshake(ss, 0);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
}
ssl_ReleaseXmitBufLock(ss);
- if (rv != SECSuccess)
- return SECFailure;
- return SECSuccess;
+ return tls13_FinishHandshake(ss);
+
+loser:
+ ssl_ReleaseXmitBufLock(ss);
+ return SECFailure;
}
static SECStatus
tls13_FinishHandshake(sslSocket *ss)
{
- SECStatus rv;
-
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->ssl3.hs.restartTarget == NULL);
- rv = tls13_ComputeFinalSecrets(ss);
- if (rv != SECSuccess)
- return SECFailure;
-
/* The first handshake is now completed. */
ss->handshake = NULL;
@@ -3652,9 +4270,15 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert,
return SECFailure; /* error code is set. */
}
}
- if (ss->ssl3.hs.certificateRequest) {
- PORT_FreeArena(ss->ssl3.hs.certificateRequest->arena, PR_FALSE);
- ss->ssl3.hs.certificateRequest = NULL;
+ if (ss->ssl3.hs.clientCertRequested) {
+ SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE);
+ if (ss->xtnData.certReqAuthorities.arena) {
+ PORT_FreeArena(ss->xtnData.certReqAuthorities.arena, PR_FALSE);
+ ss->xtnData.certReqAuthorities.arena = NULL;
+ }
+ PORT_Memset(&ss->xtnData.certReqAuthorities, 0,
+ sizeof(ss->xtnData.certReqAuthorities));
+ ss->ssl3.hs.clientCertRequested = PR_FALSE;
}
if (sendClientCert) {
@@ -3670,7 +4294,7 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert,
if (rv != SECSuccess) {
return SECFailure; /* err code was set. */
}
- rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0);
+ rv = ssl3_FlushHandshake(ss, 0);
if (rv != SECSuccess) {
/* No point in sending an alert here because we're not going to
* be able to send it if we couldn't flush the handshake. */
@@ -3678,11 +4302,6 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert,
return SECFailure;
}
- rv = dtls_StartHolddownTimer(ss);
- if (rv != SECSuccess) {
- return SECFailure; /* err code was set. */
- }
-
return SECSuccess;
}
@@ -3717,11 +4336,28 @@ tls13_SendClientSecondRound(sslSocket *ss)
return SECWouldBlock;
}
+ rv = tls13_ComputeApplicationSecrets(ss);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+
if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
+ ssl_GetXmitBufLock(ss); /*******************************/
rv = tls13_SendEndOfEarlyData(ss);
+ ssl_ReleaseXmitBufLock(ss); /*******************************/
if (rv != SECSuccess) {
return SECFailure; /* Error code already set. */
}
+ } else if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss) &&
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_none &&
+ !ss->ssl3.hs.helloRetry) {
+ ssl_GetXmitBufLock(ss); /*******************************/
+ rv = ssl3_SendChangeCipherSpecsInt(ss);
+ ssl_ReleaseXmitBufLock(ss); /*******************************/
+ if (rv != SECSuccess) {
+ return rv;
+ }
}
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
@@ -3731,12 +4367,6 @@ tls13_SendClientSecondRound(sslSocket *ss)
return SECFailure;
}
- rv = tls13_ComputeApplicationSecrets(ss);
- if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
- return SECFailure;
- }
-
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
CipherSpecRead, PR_FALSE);
if (rv != SECSuccess) {
@@ -3756,12 +4386,17 @@ tls13_SendClientSecondRound(sslSocket *ss)
return SECFailure;
}
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
- CipherSpecWrite, PR_TRUE);
+ CipherSpecWrite, PR_FALSE);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
+ rv = tls13_ComputeFinalSecrets(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
/* The handshake is now finished */
return tls13_FinishHandshake(ss);
}
@@ -3777,6 +4412,7 @@ tls13_SendClientSecondRound(sslSocket *ss)
* struct {
* uint32 ticket_lifetime;
* uint32 ticket_age_add;
+ * opaque ticket_nonce<1..255>;
* opaque ticket<1..2^16-1>;
* TicketExtension extensions<0..2^16-2>;
* } NewSessionTicket;
@@ -3784,14 +4420,22 @@ tls13_SendClientSecondRound(sslSocket *ss)
PRUint32 ssl_max_early_data_size = (2 << 16); /* Arbitrary limit. */
-SECStatus
-tls13_SendNewSessionTicket(sslSocket *ss)
+static SECStatus
+tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken,
+ unsigned int appTokenLen)
{
PRUint16 message_length;
+ PK11SymKey *secret;
SECItem ticket_data = { 0, NULL, 0 };
SECStatus rv;
NewSessionTicket ticket = { 0 };
PRUint32 max_early_data_size_len = 0;
+ PRUint8 ticketNonce[sizeof(ss->ssl3.hs.ticketNonce)];
+ sslBuffer ticketNonceBuf = SSL_BUFFER(ticketNonce);
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send new session ticket message %d",
+ SSL_GETPID(), ss->fd, ss->ssl3.hs.ticketNonce));
+
ticket.flags = 0;
if (ss->opt.enable0RttData) {
ticket.flags |= ticket_allow_early_data;
@@ -3799,18 +4443,44 @@ tls13_SendNewSessionTicket(sslSocket *ss)
}
ticket.ticket_lifetime_hint = ssl_ticket_lifetime;
- rv = ssl3_EncodeSessionTicket(ss, &ticket, &ticket_data);
+ /* The ticket age obfuscator. */
+ rv = PK11_GenerateRandom((PRUint8 *)&ticket.ticket_age_add,
+ sizeof(ticket.ticket_age_add));
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = sslBuffer_AppendNumber(&ticketNonceBuf, ss->ssl3.hs.ticketNonce,
+ sizeof(ticketNonce));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ ++ss->ssl3.hs.ticketNonce;
+ rv = tls13_HkdfExpandLabel(ss->ssl3.hs.resumptionMasterSecret,
+ tls13_GetHash(ss),
+ ticketNonce, sizeof(ticketNonce),
+ kHkdfLabelResumption,
+ strlen(kHkdfLabelResumption),
+ tls13_GetHkdfMechanism(ss),
+ tls13_GetHashSize(ss), &secret);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = ssl3_EncodeSessionTicket(ss, &ticket, appToken, appTokenLen,
+ secret, &ticket_data);
+ PK11_FreeSymKey(secret);
if (rv != SECSuccess)
goto loser;
message_length =
4 + /* lifetime */
4 + /* ticket_age_add */
+ 1 + sizeof(ticketNonce) + /* ticket_nonce */
2 + max_early_data_size_len + /* max_early_data_size_len */
2 + /* ticket length */
ticket_data.len;
- rv = ssl3_AppendHandshakeHeader(ss, new_session_ticket,
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_new_session_ticket,
message_length);
if (rv != SECSuccess)
goto loser;
@@ -3820,13 +4490,12 @@ tls13_SendNewSessionTicket(sslSocket *ss)
if (rv != SECSuccess)
goto loser;
- /* The ticket age obfuscator. */
- rv = PK11_GenerateRandom((PRUint8 *)&ticket.ticket_age_add,
- sizeof(ticket.ticket_age_add));
+ rv = ssl3_AppendHandshakeNumber(ss, ticket.ticket_age_add, 4);
if (rv != SECSuccess)
goto loser;
- rv = ssl3_AppendHandshakeNumber(ss, ticket.ticket_age_add, 4);
+ /* The ticket nonce. */
+ rv = ssl3_AppendHandshakeVariable(ss, ticketNonce, sizeof(ticketNonce), 1);
if (rv != SECSuccess)
goto loser;
@@ -3843,7 +4512,7 @@ tls13_SendNewSessionTicket(sslSocket *ss)
if (max_early_data_size_len) {
rv = ssl3_AppendHandshakeNumber(
- ss, ssl_tls13_ticket_early_data_info_xtn, 2);
+ ss, ssl_tls13_early_data_xtn, 2);
if (rv != SECSuccess)
goto loser;
@@ -3867,6 +4536,42 @@ loser:
return SECFailure;
}
+SECStatus
+SSLExp_SendSessionTicket(PRFileDesc *fd, const PRUint8 *token,
+ unsigned int tokenLen)
+{
+ sslSocket *ss;
+ SECStatus rv;
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ if (IS_DTLS(ss)) {
+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ if (!ss->sec.isServer || !ss->firstHsDone ||
+ ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ tokenLen > 0xffff) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ ssl_GetSSL3HandshakeLock(ss);
+ ssl_GetXmitBufLock(ss);
+ rv = tls13_SendNewSessionTicket(ss, token, tokenLen);
+ if (rv == SECSuccess) {
+ rv = ssl3_FlushHandshake(ss, 0);
+ }
+ ssl_ReleaseXmitBufLock(ss);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+
+ return rv;
+}
+
static SECStatus
tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
@@ -3874,6 +4579,7 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
PRUint32 utmp;
NewSessionTicket ticket = { 0 };
SECItem data;
+ SECItem ticket_nonce;
SECItem ticket_data;
SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message",
@@ -3890,7 +4596,7 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
return SECFailure;
}
- ticket.received_timestamp = PR_Now();
+ ticket.received_timestamp = ssl_TimeUsec();
rv = ssl3_ConsumeHandshakeNumber(ss, &ticket.ticket_lifetime_hint, 4, &b,
&length);
if (rv != SECSuccess) {
@@ -3908,6 +4614,14 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
ticket.ticket_age_add = PR_ntohl(utmp);
+ /* The nonce. */
+ rv = ssl3_ConsumeHandshakeVariable(ss, &ticket_nonce, 1, &b, &length);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
+ decode_error);
+ return SECFailure;
+ }
+
/* Get the ticket value. */
rv = ssl3_ConsumeHandshakeVariable(ss, &ticket_data, 2, &b, &length);
if (rv != SECSuccess || !ticket_data.len) {
@@ -3918,14 +4632,14 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
/* Parse extensions. */
rv = ssl3_ConsumeHandshakeVariable(ss, &data, 2, &b, &length);
- if (rv != SECSuccess) {
+ if (rv != SECSuccess || length) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
decode_error);
return SECFailure;
}
rv = ssl3_HandleExtensions(ss, &data.data,
- &data.len, new_session_ticket);
+ &data.len, ssl_hs_new_session_ticket);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
decode_error);
@@ -3936,13 +4650,9 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
ticket.max_early_data_size = ss->xtnData.max_early_data_size;
}
- if (length != 0) {
- FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
- decode_error);
- return SECFailure;
- }
-
if (!ss->opt.noCache) {
+ PK11SymKey *secret;
+
PORT_Assert(ss->sec.ci.sid);
rv = SECITEM_CopyItem(NULL, &ticket.ticket, &ticket_data);
if (rv != SECSuccess) {
@@ -3979,9 +4689,22 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ticket);
PORT_Assert(!ticket.ticket.data);
- rv = ssl3_FillInCachedSID(ss, ss->sec.ci.sid);
- if (rv != SECSuccess)
+ rv = tls13_HkdfExpandLabel(ss->ssl3.hs.resumptionMasterSecret,
+ tls13_GetHash(ss),
+ ticket_nonce.data, ticket_nonce.len,
+ kHkdfLabelResumption,
+ strlen(kHkdfLabelResumption),
+ tls13_GetHkdfMechanism(ss),
+ tls13_GetHashSize(ss), &secret);
+ if (rv != SECSuccess) {
return SECFailure;
+ }
+
+ rv = ssl3_FillInCachedSID(ss, ss->sec.ci.sid, secret);
+ PK11_FreeSymKey(secret);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
/* Cache the session. */
ss->sec.cache(ss->sec.ci.sid);
@@ -3990,111 +4713,103 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
return SECSuccess;
}
-typedef enum {
- ExtensionNotUsed,
- ExtensionClientOnly,
- ExtensionSendClear,
- ExtensionSendClearOrHrr,
- ExtensionSendHrr,
- ExtensionSendEncrypted,
- ExtensionSendCertificate,
- ExtensionNewSessionTicket
-} Tls13ExtensionStatus;
+#define _M(a) (1 << PR_MIN(a, 31))
+#define _M1(a) (_M(ssl_hs_##a))
+#define _M2(a, b) (_M1(a) | _M1(b))
+#define _M3(a, b, c) (_M1(a) | _M2(b, c))
static const struct {
PRUint16 ex_value;
- Tls13ExtensionStatus status;
+ PRUint32 messages;
} KnownExtensions[] = {
- { ssl_server_name_xtn, ExtensionSendEncrypted },
- { ssl_supported_groups_xtn, ExtensionSendEncrypted },
- { ssl_ec_point_formats_xtn, ExtensionNotUsed },
- { ssl_signature_algorithms_xtn, ExtensionClientOnly },
- { ssl_use_srtp_xtn, ExtensionSendEncrypted },
- { ssl_app_layer_protocol_xtn, ExtensionSendEncrypted },
- { ssl_padding_xtn, ExtensionNotUsed },
- { ssl_extended_master_secret_xtn, ExtensionNotUsed },
- { ssl_session_ticket_xtn, ExtensionClientOnly },
- { ssl_tls13_key_share_xtn, ExtensionSendClearOrHrr },
- { ssl_tls13_pre_shared_key_xtn, ExtensionSendClear },
- { ssl_tls13_early_data_xtn, ExtensionSendEncrypted },
- { ssl_next_proto_nego_xtn, ExtensionNotUsed },
- { ssl_renegotiation_info_xtn, ExtensionNotUsed },
- { ssl_signed_cert_timestamp_xtn, ExtensionSendCertificate },
- { ssl_cert_status_xtn, ExtensionSendCertificate },
- { ssl_tls13_ticket_early_data_info_xtn, ExtensionNewSessionTicket },
- { ssl_tls13_cookie_xtn, ExtensionSendHrr },
- { ssl_tls13_short_header_xtn, ExtensionSendClear }
+ { ssl_server_name_xtn, _M2(client_hello, encrypted_extensions) },
+ { ssl_supported_groups_xtn, _M2(client_hello, encrypted_extensions) },
+ { ssl_signature_algorithms_xtn, _M2(client_hello, certificate_request) },
+ { ssl_signature_algorithms_cert_xtn, _M2(client_hello,
+ certificate_request) },
+ { ssl_use_srtp_xtn, _M2(client_hello, encrypted_extensions) },
+ { ssl_app_layer_protocol_xtn, _M2(client_hello, encrypted_extensions) },
+ { ssl_padding_xtn, _M1(client_hello) },
+ { ssl_tls13_key_share_xtn, _M3(client_hello, server_hello,
+ hello_retry_request) },
+ { ssl_tls13_pre_shared_key_xtn, _M2(client_hello, server_hello) },
+ { ssl_tls13_psk_key_exchange_modes_xtn, _M1(client_hello) },
+ { ssl_tls13_early_data_xtn, _M3(client_hello, encrypted_extensions,
+ new_session_ticket) },
+ { ssl_signed_cert_timestamp_xtn, _M3(client_hello, certificate_request,
+ certificate) },
+ { ssl_cert_status_xtn, _M3(client_hello, certificate_request,
+ certificate) },
+ { ssl_tls13_cookie_xtn, _M2(client_hello, hello_retry_request) },
+ { ssl_tls13_certificate_authorities_xtn, _M1(certificate_request) },
+ { ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello,
+ hello_retry_request) }
};
-PRBool
-tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message)
+tls13ExtensionStatus
+tls13_ExtensionStatus(PRUint16 extension, SSLHandshakeType message)
{
unsigned int i;
- PORT_Assert((message == client_hello) ||
- (message == server_hello) ||
- (message == hello_retry_request) ||
- (message == encrypted_extensions) ||
- (message == new_session_ticket) ||
- (message == certificate) ||
- (message == certificate_request));
+ PORT_Assert((message == ssl_hs_client_hello) ||
+ (message == ssl_hs_server_hello) ||
+ (message == ssl_hs_hello_retry_request) ||
+ (message == ssl_hs_encrypted_extensions) ||
+ (message == ssl_hs_new_session_ticket) ||
+ (message == ssl_hs_certificate) ||
+ (message == ssl_hs_certificate_request));
for (i = 0; i < PR_ARRAY_SIZE(KnownExtensions); i++) {
- if (KnownExtensions[i].ex_value == extension)
+ /* Hacky check for message numbers > 30. */
+ PORT_Assert(!(KnownExtensions[i].messages & (1U << 31)));
+ if (KnownExtensions[i].ex_value == extension) {
break;
+ }
}
- if (i == PR_ARRAY_SIZE(KnownExtensions)) {
- /* We have never heard of this extension which is OK
- * in client_hello and new_session_ticket. */
- return (message == client_hello) ||
- (message == new_session_ticket);
- }
-
- switch (KnownExtensions[i].status) {
- case ExtensionNotUsed:
- return PR_FALSE;
- case ExtensionClientOnly:
- return message == client_hello;
- case ExtensionSendClear:
- return message == client_hello ||
- message == server_hello;
- case ExtensionSendClearOrHrr:
- return message == client_hello ||
- message == server_hello ||
- message == hello_retry_request;
- case ExtensionSendHrr:
- return message == client_hello ||
- message == hello_retry_request;
- case ExtensionSendEncrypted:
- return message == client_hello ||
- message == encrypted_extensions;
- case ExtensionNewSessionTicket:
- return message == new_session_ticket;
- case ExtensionSendCertificate:
- return message == client_hello ||
- message == certificate;
+ if (i >= PR_ARRAY_SIZE(KnownExtensions)) {
+ return tls13_extension_unknown;
}
- PORT_Assert(0);
+ /* Return "disallowed" if the message mask bit isn't set. */
+ if (!(_M(message) & KnownExtensions[i].messages)) {
+ SSL_TRC(3, ("%d: TLS13: unexpected extension %d in message %d",
+ SSL_GETPID(), extension, message));
- /* Not reached */
- return PR_TRUE;
+ return tls13_extension_disallowed;
+ }
+
+ return tls13_extension_allowed;
}
+#undef _M
+#undef _M1
+#undef _M2
+#undef _M3
+
/* TLS 1.3 doesn't actually have additional data but the aead function
* signature overloads additional data to carry the record sequence
* number and that's what we put here. The TLS 1.3 AEAD functions
* just use this input as the sequence number and not as additional
* data. */
-static void
-tls13_FormatAdditionalData(PRUint8 *aad, unsigned int length,
- sslSequenceNumber seqNum)
+static SECStatus
+tls13_FormatAdditionalData(sslSocket *ss, PRUint8 *aad, unsigned int length,
+ DTLSEpoch epoch, sslSequenceNumber seqNum)
{
- PRUint8 *ptr = aad;
+ SECStatus rv;
+ sslBuffer buf = SSL_BUFFER_FIXED(aad, length);
PORT_Assert(length == 8);
- ptr = ssl_EncodeUintX(seqNum, 8, ptr);
- PORT_Assert((ptr - aad) == length);
+ if (IS_DTLS(ss)) {
+ rv = sslBuffer_AppendNumber(&buf, epoch, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+ rv = sslBuffer_AppendNumber(&buf, seqNum, IS_DTLS(ss) ? 6 : 8);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ return SECSuccess;
}
PRInt32
@@ -4127,13 +4842,14 @@ tls13_ProtectRecord(sslSocket *ss,
PRUint32 contentLen,
sslBuffer *wrBuf)
{
- const ssl3BulkCipherDef *cipher_def = cwSpec->cipher_def;
+ const ssl3BulkCipherDef *cipher_def = cwSpec->cipherDef;
const int tagLen = cipher_def->tag_size;
SECStatus rv;
- SSL_TRC(3, ("%d: TLS13[%d]: spec=%d (%s) protect record 0x%0llx len=%u",
- SSL_GETPID(), ss->fd, cwSpec, cwSpec->phase,
- cwSpec->write_seq_num, contentLen));
+ PORT_Assert(cwSpec->direction == CipherSpecWrite);
+ SSL_TRC(3, ("%d: TLS13[%d]: spec=%d epoch=%d (%s) protect 0x%0llx len=%u",
+ SSL_GETPID(), ss->fd, cwSpec, cwSpec->epoch, cwSpec->phase,
+ cwSpec->seqNum, contentLen));
if (contentLen + 1 + tagLen > wrBuf->space) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -4154,15 +4870,18 @@ tls13_ProtectRecord(sslSocket *ss,
/* Add the content type at the end. */
wrBuf->buf[contentLen] = type;
- tls13_FormatAdditionalData(aad, sizeof(aad), cwSpec->write_seq_num);
- rv = cwSpec->aead(
- ss->sec.isServer ? &cwSpec->server : &cwSpec->client,
- PR_FALSE, /* do encrypt */
- wrBuf->buf, /* output */
- (int *)&wrBuf->len, /* out len */
- wrBuf->space, /* max out */
- wrBuf->buf, contentLen + 1, /* input */
- aad, sizeof(aad));
+ rv = tls13_FormatAdditionalData(ss, aad, sizeof(aad), cwSpec->epoch,
+ cwSpec->seqNum);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = cwSpec->aead(&cwSpec->keyMaterial,
+ PR_FALSE, /* do encrypt */
+ wrBuf->buf, /* output */
+ (int *)&wrBuf->len, /* out len */
+ wrBuf->space, /* max out */
+ wrBuf->buf, contentLen + 1, /* input */
+ aad, sizeof(aad));
if (rv != SECSuccess) {
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
return SECFailure;
@@ -4182,19 +4901,27 @@ tls13_ProtectRecord(sslSocket *ss,
* 2. Call PORT_SetError() witn an appropriate code.
*/
SECStatus
-tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
+tls13_UnprotectRecord(sslSocket *ss,
+ ssl3CipherSpec *spec,
+ SSL3Ciphertext *cText, sslBuffer *plaintext,
SSL3AlertDescription *alert)
{
- ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
- const ssl3BulkCipherDef *cipher_def = crSpec->cipher_def;
+ const ssl3BulkCipherDef *cipher_def = spec->cipherDef;
+ sslSequenceNumber seqNum;
PRUint8 aad[8];
SECStatus rv;
*alert = bad_record_mac; /* Default alert for most issues. */
- SSL_TRC(3, ("%d: TLS13[%d]: spec=%d (%s) unprotect record 0x%0llx len=%u",
- SSL_GETPID(), ss->fd, crSpec, crSpec->phase,
- crSpec->read_seq_num, cText->buf->len));
+ PORT_Assert(spec->direction == CipherSpecRead);
+ if (IS_DTLS(ss)) {
+ seqNum = cText->seq_num & RECORD_SEQ_MASK;
+ } else {
+ seqNum = spec->seqNum;
+ }
+ SSL_TRC(3, ("%d: TLS13[%d]: spec=%d epoch=%d (%s) unprotect 0x%0llx len=%u",
+ SSL_GETPID(), ss->fd, spec, spec->epoch, spec->phase, seqNum,
+ cText->buf->len));
/* We can perform this test in variable time because the record's total
* length and the ciphersuite are both public knowledge. */
@@ -4216,9 +4943,8 @@ tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext
return SECFailure;
}
- /* Check the version number in the record */
- if ((IS_DTLS(ss) && cText->version != kDtlsRecordVersion) ||
- (!IS_DTLS(ss) && cText->version != kTlsRecordVersion)) {
+ /* Check the version number in the record. */
+ if (cText->version != spec->recordVersion) {
/* Do we need a better error here? */
SSL_TRC(3,
("%d: TLS13[%d]: record has bogus version",
@@ -4228,18 +4954,18 @@ tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext
/* Decrypt */
PORT_Assert(cipher_def->type == type_aead);
- tls13_FormatAdditionalData(aad, sizeof(aad),
- IS_DTLS(ss) ? cText->seq_num
- : crSpec->read_seq_num);
- rv = crSpec->aead(
- ss->sec.isServer ? &crSpec->client : &crSpec->server,
- PR_TRUE, /* do decrypt */
- plaintext->buf, /* out */
- (int *)&plaintext->len, /* outlen */
- plaintext->space, /* maxout */
- cText->buf->buf, /* in */
- cText->buf->len, /* inlen */
- aad, sizeof(aad));
+ rv = tls13_FormatAdditionalData(ss, aad, sizeof(aad), spec->epoch, seqNum);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = spec->aead(&spec->keyMaterial,
+ PR_TRUE, /* do decrypt */
+ plaintext->buf, /* out */
+ (int *)&plaintext->len, /* outlen */
+ plaintext->space, /* maxout */
+ cText->buf->buf, /* in */
+ cText->buf->len, /* inlen */
+ aad, sizeof(aad));
if (rv != SECSuccess) {
SSL_TRC(3,
("%d: TLS13[%d]: record has bogus MAC",
@@ -4271,14 +4997,14 @@ tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext
--plaintext->len;
/* Check that we haven't received too much 0-RTT data. */
- if (crSpec->epoch == TrafficKeyEarlyApplicationData &&
+ if (spec->epoch == TrafficKeyEarlyApplicationData &&
cText->type == content_application_data) {
- if (plaintext->len > crSpec->earlyDataRemaining) {
+ if (plaintext->len > spec->earlyDataRemaining) {
*alert = unexpected_message;
PORT_SetError(SSL_ERROR_TOO_MUCH_EARLY_DATA);
return SECFailure;
}
- crSpec->earlyDataRemaining -= plaintext->len;
+ spec->earlyDataRemaining -= plaintext->len;
}
SSL_TRC(10,
@@ -4326,7 +5052,7 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
/* Don't do anything if there is no early_data xtn, which means we're
* not doing early data. */
- if (!ssl3_ClientExtensionAdvertised(ss, ssl_tls13_early_data_xtn)) {
+ if (!ssl3_ExtensionAdvertised(ss, ssl_tls13_early_data_xtn)) {
return SECSuccess;
}
@@ -4341,25 +5067,41 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
ss->xtnData.nextProtoState = SSL_NEXT_PROTO_EARLY_VALUE;
rv = SECITEM_CopyItem(NULL, &ss->xtnData.nextProto,
&ss->sec.ci.sid->u.ssl3.alpnSelection);
- if (rv != SECSuccess)
- return rv;
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss)) {
+ /* Pretend that this is a proper ChangeCipherSpec even though it is sent
+ * before receiving the ServerHello. */
+ ssl_GetSpecWriteLock(ss);
+ tls13_SetSpecRecordVersion(ss, ss->ssl3.cwSpec);
+ ssl_ReleaseSpecWriteLock(ss);
+ ssl_GetXmitBufLock(ss);
+ rv = ssl3_SendChangeCipherSpecsInt(ss);
+ ssl_ReleaseXmitBufLock(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
/* Cipher suite already set in tls13_SetupClientHello. */
ss->ssl3.hs.preliminaryInfo = 0;
- rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
- kHkdfLabelClient,
- kHkdfLabelEarlyTrafficSecret,
- NULL,
- &ss->ssl3.hs.clientEarlyTrafficSecret);
- if (rv != SECSuccess)
+ rv = tls13_DeriveEarlySecrets(ss);
+ if (rv != SECSuccess) {
return SECFailure;
+ }
+
+ /* Save cwSpec in case we get a HelloRetryRequest and have to send another
+ * ClientHello. */
+ ssl_CipherSpecAddRef(ss->ssl3.cwSpec);
rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData,
CipherSpecWrite, PR_TRUE);
if (rv != SECSuccess) {
- return rv;
+ return SECFailure;
}
return SECSuccess;
@@ -4392,32 +5134,45 @@ tls13_SendEndOfEarlyData(sslSocket *ss)
{
SECStatus rv;
- SSL_TRC(3, ("%d: TLS13[%d]: send end_of_early_data extension",
- SSL_GETPID(), ss->fd));
+ SSL_TRC(3, ("%d: TLS13[%d]: send EndOfEarlyData", SSL_GETPID(), ss->fd));
+ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- rv = SSL3_SendAlert(ss, alert_warning, end_of_early_data);
+ rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_end_of_early_data, 0);
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
- return SECFailure;
+ return rv; /* err set by AppendHandshake. */
}
ss->ssl3.hs.zeroRttState = ssl_0rtt_done;
return SECSuccess;
}
-SECStatus
-tls13_HandleEndOfEarlyData(sslSocket *ss)
+static SECStatus
+tls13_HandleEndOfEarlyData(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
SECStatus rv;
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
- ss->ssl3.hs.zeroRttState != ssl_0rtt_accepted) {
- (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
- PORT_SetError(SSL_ERROR_END_OF_EARLY_DATA_ALERT);
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA,
+ wait_end_of_early_data);
+ if (rv != SECSuccess) {
return SECFailure;
}
- PORT_Assert(TLS13_IN_HS_STATE(ss, ss->opt.requestCertificate ? wait_client_cert : wait_finished));
+ /* We shouldn't be getting any more early data, and if we do,
+ * it is because of reordering and we drop it. */
+ if (IS_DTLS(ss)) {
+ ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead,
+ TrafficKeyEarlyApplicationData);
+ dtls_ReceivedFirstMessageInFlight(ss);
+ }
+
+ PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
+
+ if (length) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA, decode_error);
+ return SECFailure;
+ }
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
CipherSpecRead, PR_FALSE);
@@ -4427,6 +5182,9 @@ tls13_HandleEndOfEarlyData(sslSocket *ss)
}
ss->ssl3.hs.zeroRttState = ssl_0rtt_done;
+ TLS13_SET_HS_STATE(ss,
+ ss->opt.requestCertificate ? wait_client_cert
+ : wait_finished);
return SECSuccess;
}
@@ -4477,11 +5235,11 @@ tls13_EncodeDraftVersion(SSL3ProtocolVersion version)
/* Pick the highest version we support that is also advertised. */
SECStatus
-tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supported_versions)
+tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supportedVersions)
{
PRUint16 version;
- /* Make a copy so we're nondestructive*/
- SECItem data = supported_versions->data;
+ /* Make a copy so we're nondestructive. */
+ SECItem data = supportedVersions->data;
SECItem versions;
SECStatus rv;
@@ -4511,3 +5269,22 @@ tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supported_versions)
FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_VERSION, protocol_version);
return SECFailure;
}
+
+/* This is TLS 1.3 or might negotiate to it. */
+PRBool
+tls13_MaybeTls13(sslSocket *ss)
+{
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return PR_TRUE;
+ }
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return PR_FALSE;
+ }
+
+ if (!(ss->ssl3.hs.preliminaryInfo & ssl_preinfo_version)) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
diff --git a/security/nss/lib/ssl/tls13con.h b/security/nss/lib/ssl/tls13con.h
index 92eb545b0..1aaffb651 100644
--- a/security/nss/lib/ssl/tls13con.h
+++ b/security/nss/lib/ssl/tls13con.h
@@ -9,15 +9,25 @@
#ifndef __tls13con_h_
#define __tls13con_h_
+#include "sslexp.h"
+#include "sslspec.h"
+
+typedef enum {
+ tls13_extension_allowed,
+ tls13_extension_disallowed,
+ tls13_extension_unknown
+} tls13ExtensionStatus;
+
typedef enum {
- StaticSharedSecret,
- EphemeralSharedSecret
-} SharedSecretType;
+ update_not_requested = 0,
+ update_requested = 1
+} tls13KeyUpdateRequest;
#define TLS13_MAX_FINISHED_SIZE 64
SECStatus tls13_UnprotectRecord(
- sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
+ sslSocket *ss, ssl3CipherSpec *spec,
+ SSL3Ciphertext *cText, sslBuffer *plaintext,
SSL3AlertDescription *alert);
#if defined(WIN32)
@@ -41,6 +51,14 @@ SSLHashType tls13_GetHash(const sslSocket *ss);
unsigned int tls13_GetHashSizeForHash(SSLHashType hash);
unsigned int tls13_GetHashSize(const sslSocket *ss);
CK_MECHANISM_TYPE tls13_GetHkdfMechanism(sslSocket *ss);
+SECStatus tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes,
+ const PRUint8 *buf, unsigned int len);
+SECStatus tls13_ComputeHandshakeHashes(sslSocket *ss,
+ SSL3Hashes *hashes);
+SECStatus tls13_DeriveSecretNullHash(sslSocket *ss, PK11SymKey *key,
+ const char *label,
+ unsigned int labelLen,
+ PK11SymKey **dest);
void tls13_FatalError(sslSocket *ss, PRErrorCode prError,
SSL3AlertDescription desc);
SECStatus tls13_SetupClientHello(sslSocket *ss);
@@ -49,27 +67,30 @@ PRInt32 tls13_LimitEarlyData(sslSocket *ss, SSL3ContentType type, PRInt32 toSend
PRBool tls13_AllowPskCipher(const sslSocket *ss,
const ssl3CipherSuiteDef *cipher_def);
PRBool tls13_PskSuiteEnabled(sslSocket *ss);
-SECStatus tls13_ComputePskBinder(sslSocket *ss, PRBool sending,
- unsigned int prefixLength,
- PRUint8 *output, unsigned int *outputLen,
- unsigned int maxOutputLen);
+SECStatus tls13_WriteExtensionsWithBinder(sslSocket *ss, sslBuffer *extensions);
SECStatus tls13_HandleClientHelloPart2(sslSocket *ss,
const SECItem *suites,
- sslSessionID *sid);
+ sslSessionID *sid,
+ const PRUint8 *msg,
+ unsigned int len);
SECStatus tls13_HandleServerHelloPart2(sslSocket *ss);
SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
- PRUint32 length,
- SSL3Hashes *hashesPtr);
-SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, PRUint8 *b,
+ PRUint32 length);
+SECStatus tls13_ConstructHelloRetryRequest(sslSocket *ss,
+ ssl3CipherSuite cipherSuite,
+ const sslNamedGroupDef *selectedGroup,
+ PRUint8 *cookie,
+ unsigned int cookieLen,
+ sslBuffer *buffer);
+SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, const PRUint8 *b,
PRUint32 length);
void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry);
void tls13_DestroyKeyShares(PRCList *list);
SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef);
void tls13_DestroyEarlyData(PRCList *list);
-void tls13_CipherSpecAddRef(ssl3CipherSpec *spec);
-void tls13_CipherSpecRelease(ssl3CipherSpec *spec);
-void tls13_DestroyCipherSpecs(PRCList *list);
-PRBool tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message);
+SECStatus tls13_SetAlertCipherSpec(sslSocket *ss);
+tls13ExtensionStatus tls13_ExtensionStatus(PRUint16 extension,
+ SSLHandshakeType message);
SECStatus tls13_ProtectRecord(sslSocket *ss,
ssl3CipherSpec *cwSpec,
SSL3ContentType type,
@@ -77,13 +98,25 @@ SECStatus tls13_ProtectRecord(sslSocket *ss,
PRUint32 contentLen,
sslBuffer *wrBuf);
PRInt32 tls13_Read0RttData(sslSocket *ss, void *buf, PRInt32 len);
-SECStatus tls13_HandleEndOfEarlyData(sslSocket *ss);
SECStatus tls13_HandleEarlyApplicationData(sslSocket *ss, sslBuffer *origBuf);
PRBool tls13_ClientAllow0Rtt(const sslSocket *ss, const sslSessionID *sid);
PRUint16 tls13_EncodeDraftVersion(SSL3ProtocolVersion version);
-PRUint16 tls13_DecodeDraftVersion(PRUint16 version);
SECStatus tls13_NegotiateVersion(sslSocket *ss,
const TLSExtension *supported_versions);
-SECStatus tls13_SendNewSessionTicket(sslSocket *ss);
+
+PRBool tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid);
+void tls13_AntiReplayRollover(PRTime now);
+
+SECStatus SSLExp_SetupAntiReplay(PRTime window, unsigned int k,
+ unsigned int bits);
+
+SECStatus SSLExp_HelloRetryRequestCallback(PRFileDesc *fd,
+ SSLHelloRetryRequestCallback cb,
+ void *arg);
+SECStatus tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request,
+ PRBool buffer);
+SECStatus SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate);
+PRBool tls13_MaybeTls13(sslSocket *ss);
+void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec);
#endif /* __tls13con_h_ */
diff --git a/security/nss/lib/ssl/tls13err.h b/security/nss/lib/ssl/tls13err.h
new file mode 100644
index 000000000..8cdeb12eb
--- /dev/null
+++ b/security/nss/lib/ssl/tls13err.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __tls13err_h_
+#define __tls13err_h_
+
+/* Use this instead of FATAL_ERROR when an alert isn't possible. */
+#define LOG_ERROR(ss, prError) \
+ do { \
+ SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)", \
+ SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \
+ PORT_SetError(prError); \
+ } while (0)
+
+/* Log an error and generate an alert because something is irreparably wrong. */
+#define FATAL_ERROR(ss, prError, desc) \
+ do { \
+ LOG_ERROR(ss, prError); \
+ tls13_FatalError(ss, prError, desc); \
+ } while (0)
+
+void tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc);
+#endif
diff --git a/security/nss/lib/ssl/tls13exthandle.c b/security/nss/lib/ssl/tls13exthandle.c
index c2ce390ff..899f23827 100644
--- a/security/nss/lib/ssl/tls13exthandle.c
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -14,50 +14,35 @@
#include "ssl3exthandle.h"
#include "tls13exthandle.h"
-PRInt32
-tls13_ServerSendStatusRequestXtn(
- const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+SECStatus
+tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
const sslServerCert *serverCert = ss->sec.serverCert;
const SECItem *item;
SECStatus rv;
if (!serverCert->certStatusArray ||
!serverCert->certStatusArray->len) {
- return 0;
+ return SECSuccess;
}
item = &serverCert->certStatusArray->items[0];
/* Only send the first entry. */
- extension_length = 2 + 2 + 1 /* status_type */ + 3 + item->len;
- if (maxBytes < (PRUint32)extension_length) {
- return 0;
+ /* status_type == ocsp */
+ rv = sslBuffer_AppendNumber(buf, 1 /*ocsp*/, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (append) {
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_cert_status_xtn, 2);
- if (rv != SECSuccess)
- return -1;
- /* length of extension_data */
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess)
- return -1;
- /* status_type == ocsp */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 1 /*ocsp*/, 1);
- if (rv != SECSuccess)
- return rv; /* err set by AppendHandshake. */
- /* opaque OCSPResponse<1..2^24-1> */
- rv = ssl3_ExtAppendHandshakeVariable(ss, item->data, item->len, 3);
- if (rv != SECSuccess)
- return rv; /* err set by AppendHandshake. */
+ /* opaque OCSPResponse<1..2^24-1> */
+ rv = sslBuffer_AppendVariable(buf, item->data, item->len, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
+ *added = PR_TRUE;
+ return SECSuccess;
}
/*
@@ -101,41 +86,27 @@ tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey)
return 0;
}
-static PRUint32
-tls13_SizeOfClientKeyShareExtension(const sslSocket *ss)
-{
- PRCList *cursor;
- /* Size is: extension(2) + extension_len(2) + client_shares(2) */
- PRUint32 size = 2 + 2 + 2;
- for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
- cursor != &ss->ephemeralKeyPairs;
- cursor = PR_NEXT_LINK(cursor)) {
- sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
- size += tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey);
- }
- return size;
-}
-
static SECStatus
-tls13_EncodeKeyShareEntry(const sslSocket *ss, const sslEphemeralKeyPair *keyPair)
+tls13_EncodeKeyShareEntry(sslBuffer *buf, const sslEphemeralKeyPair *keyPair)
{
SECStatus rv;
SECKEYPublicKey *pubKey = keyPair->keys->pubKey;
unsigned int size = tls13_SizeOfKeyShareEntry(pubKey);
- rv = ssl3_ExtAppendHandshakeNumber(ss, keyPair->group->name, 2);
+ rv = sslBuffer_AppendNumber(buf, keyPair->group->name, 2);
if (rv != SECSuccess)
return rv;
- rv = ssl3_ExtAppendHandshakeNumber(ss, size - 4, 2);
+ rv = sslBuffer_AppendNumber(buf, size - 4, 2);
if (rv != SECSuccess)
return rv;
switch (pubKey->keyType) {
case ecKey:
- rv = tls13_EncodeECDHEKeyShareKEX(ss, pubKey);
+ rv = sslBuffer_Append(buf, pubKey->u.ec.publicValue.data,
+ pubKey->u.ec.publicValue.len);
break;
case dhKey:
- rv = ssl_AppendPaddedDHKeyShare(ss, pubKey, PR_FALSE);
+ rv = ssl_AppendPaddedDHKeyShare(buf, pubKey, PR_FALSE);
break;
default:
PORT_Assert(0);
@@ -146,14 +117,16 @@ tls13_EncodeKeyShareEntry(const sslSocket *ss, const sslEphemeralKeyPair *keyPai
return rv;
}
-PRInt32
-tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRUint32 extension_length;
+ SECStatus rv;
+ PRCList *cursor;
+ unsigned int lengthOffset;
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
- return 0;
+ return SECSuccess;
}
/* Optimistically try to send an ECDHE key using the
@@ -161,47 +134,28 @@ tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBo
SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn",
SSL_GETPID(), ss->fd));
- extension_length = tls13_SizeOfClientKeyShareExtension(ss);
- if (maxBytes < extension_length) {
- PORT_Assert(0);
- return 0;
+ /* Save the offset to the length. */
+ rv = sslBuffer_Skip(buf, 2, &lengthOffset);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (append) {
- SECStatus rv;
- PRCList *cursor;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2);
- if (rv != SECSuccess)
- goto loser;
-
- /* The extension length */
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess)
- goto loser;
-
- /* The length of KeyShares */
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 6, 2);
- if (rv != SECSuccess)
- goto loser;
-
- for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
- cursor != &ss->ephemeralKeyPairs;
- cursor = PR_NEXT_LINK(cursor)) {
- sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
- rv = tls13_EncodeKeyShareEntry(ss, keyPair);
- if (rv != SECSuccess)
- goto loser;
+ for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
+ cursor != &ss->ephemeralKeyPairs;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
+ rv = tls13_EncodeKeyShareEntry(buf, keyPair);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
-
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_tls13_key_share_xtn;
+ }
+ rv = sslBuffer_InsertLength(buf, lengthOffset, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
-
-loser:
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
static SECStatus
@@ -250,7 +204,8 @@ loser:
* |xtnData->remoteKeyShares| for future use. The key
* share is processed in tls13_HandleServerKeyShare(). */
SECStatus
-tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
@@ -281,7 +236,8 @@ tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PR
}
SECStatus
-tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
PRUint32 tmp;
@@ -331,7 +287,8 @@ tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData,
* |xtnData->remoteKeyShares| for future use. The key
* share is processed in tls13_HandleClientKeyShare(). */
SECStatus
-tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
PRUint32 length;
@@ -364,16 +321,6 @@ tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PR
goto loser;
}
- /* Check that the client only offered one share if this is
- * after HRR. */
- if (ss->ssl3.hs.helloRetry) {
- if (PR_PREV_LINK(&xtnData->remoteKeyShares) !=
- PR_NEXT_LINK(&xtnData->remoteKeyShares)) {
- PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
- goto loser;
- }
- }
-
return SECSuccess;
loser:
@@ -381,12 +328,10 @@ loser:
return SECFailure;
}
-PRInt32
-tls13_ServerSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+tls13_ServerSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRUint32 extension_length;
- PRUint32 entry_length;
SECStatus rv;
sslEphemeralKeyPair *keyPair;
@@ -397,31 +342,13 @@ tls13_ServerSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBo
keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs);
- entry_length = tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey);
- extension_length = 2 + 2 + entry_length; /* Type + length + entry_length */
- if (maxBytes < extension_length) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2);
- if (rv != SECSuccess)
- goto loser;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, entry_length, 2);
- if (rv != SECSuccess)
- goto loser;
-
- rv = tls13_EncodeKeyShareEntry(ss, keyPair);
- if (rv != SECSuccess)
- goto loser;
+ rv = tls13_EncodeKeyShareEntry(buf, keyPair);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
-
-loser:
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
}
/* Called by clients.
@@ -448,113 +375,83 @@ loser:
* Presently the only way to get a PSK is by resumption, so this is
* really a ticket label and there will be at most one.
*/
-PRInt32
+SECStatus
tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length;
- PRInt32 identities_length;
- PRInt32 binders_length;
NewSessionTicket *session_ticket;
+ PRTime age;
+ const static PRUint8 binder[TLS13_MAX_FINISHED_SIZE] = { 0 };
+ unsigned int binderLen;
+ SECStatus rv;
/* We only set statelessResume on the client in TLS 1.3 code. */
- if (!ss->statelessResume)
- return 0;
+ if (!ss->statelessResume) {
+ return SECSuccess;
+ }
+
+ /* Save where this extension starts so that if we have to add padding, it
+ * can be inserted before this extension. */
+ PORT_Assert(buf->len >= 4);
+ xtnData->lastXtnOffset = buf->len - 4;
PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
- /* The length computations are simplified by the fact that there
- * is just one ticket at most. */
+ /* Send a single ticket identity. */
session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket;
- identities_length =
- 2 + /* vector length */
- 2 + session_ticket->ticket.len + /* identity length + ticket len */
- 4; /* obfuscated_ticket_age */
- binders_length =
- 2 + /* vector length */
- 1 + tls13_GetHashSizeForHash(
- tls13_GetHashForCipherSuite(ss->sec.ci.sid->u.ssl3.cipherSuite));
- extension_length =
- 2 + 2 + /* Type + length */
- identities_length + binders_length;
-
- if (maxBytes < (PRUint32)extension_length) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- SECStatus rv;
- PRTime age;
- unsigned int prefixLength;
- PRUint8 binder[TLS13_MAX_FINISHED_SIZE];
- unsigned int binderLen;
-
- /* extension_type */
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_ExtAppendHandshakeNumber(ss, identities_length - 2, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_ExtAppendHandshakeVariable(ss, session_ticket->ticket.data,
- session_ticket->ticket.len, 2);
- if (rv != SECSuccess)
- goto loser;
+ rv = sslBuffer_AppendNumber(buf, 2 + /* identity length */
+ session_ticket->ticket.len + /* ticket */
+ 4 /* obfuscated_ticket_age */,
+ 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = sslBuffer_AppendVariable(buf, session_ticket->ticket.data,
+ session_ticket->ticket.len, 2);
+ if (rv != SECSuccess)
+ goto loser;
- /* Obfuscated age. */
- age = PR_Now() - session_ticket->received_timestamp;
- age /= PR_USEC_PER_MSEC;
- age += session_ticket->ticket_age_add;
- rv = ssl3_ExtAppendHandshakeNumber(ss, age, 4);
- if (rv != SECSuccess)
- goto loser;
+ /* Obfuscated age. */
+ age = ssl_TimeUsec() - session_ticket->received_timestamp;
+ age /= PR_USEC_PER_MSEC;
+ age += session_ticket->ticket_age_add;
+ rv = sslBuffer_AppendNumber(buf, age, 4);
+ if (rv != SECSuccess)
+ goto loser;
- /* Now the binders. */
- prefixLength = ss->ssl3.hs.messages.len;
- rv = tls13_ComputePskBinder(CONST_CAST(sslSocket, ss), PR_TRUE,
- prefixLength, binder, &binderLen,
- sizeof(binder));
- if (rv != SECSuccess)
- goto loser;
- PORT_Assert(binderLen == tls13_GetHashSize(ss));
- rv = ssl3_ExtAppendHandshakeNumber(ss, binders_length - 2, 2);
- if (rv != SECSuccess)
- goto loser;
- rv = ssl3_ExtAppendHandshakeVariable(ss,
- binder, binderLen, 1);
- if (rv != SECSuccess)
- goto loser;
+ /* Write out the binder list length. */
+ binderLen = tls13_GetHashSize(ss);
+ rv = sslBuffer_AppendNumber(buf, binderLen + 1, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ /* Write zeroes for the binder for the moment. */
+ rv = sslBuffer_AppendVariable(buf, binder, binderLen, 1);
+ if (rv != SECSuccess)
+ goto loser;
- PRINT_BUF(50, (ss, "Sending PreSharedKey value",
- session_ticket->ticket.data,
- session_ticket->ticket.len));
+ PRINT_BUF(50, (ss, "Sending PreSharedKey value",
+ session_ticket->ticket.data,
+ session_ticket->ticket.len));
- xtnData->sentSessionTicketInClientHello = PR_TRUE;
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_tls13_pre_shared_key_xtn;
- }
- return extension_length;
+ xtnData->sentSessionTicketInClientHello = PR_TRUE;
+ *added = PR_TRUE;
+ return SECSuccess;
loser:
xtnData->ticketTimestampVerified = PR_FALSE;
- return -1;
+ return SECFailure;
}
/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
* that contain session tickets. */
SECStatus
-tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
SECItem inner;
SECStatus rv;
unsigned int numIdentities = 0;
unsigned int numBinders = 0;
+ SECItem *appToken;
SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
SSL_GETPID(), ss->fd));
@@ -564,16 +461,26 @@ tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData
return SECSuccess;
}
+ /* The application token is set via the cookie extension if this is the
+ * second ClientHello. Don't set it twice. The cookie extension handler
+ * sets |helloRetry| and that will have been called already because this
+ * extension always comes last. */
+ if (!ss->ssl3.hs.helloRetry) {
+ appToken = &xtnData->applicationToken;
+ } else {
+ appToken = NULL;
+ }
+
/* Parse the identities list. */
- rv = ssl3_ExtConsumeHandshakeVariable(ss,
- &inner, 2, &data->data, &data->len);
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &inner, 2,
+ &data->data, &data->len);
if (rv != SECSuccess) {
return SECFailure;
}
while (inner.len) {
SECItem label;
- PRUint32 utmp;
+ PRUint32 obfuscatedAge;
rv = ssl3_ExtConsumeHandshakeVariable(ss, &label, 2,
&inner.data, &inner.len);
@@ -583,9 +490,8 @@ tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData
goto alert_loser;
}
- /* Read and discard session ticket age. Bug 1295163 */
- rv = ssl3_ExtConsumeHandshake(ss, &utmp, 4,
- &inner.data, &inner.len);
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &obfuscatedAge, 4,
+ &inner.data, &inner.len);
if (rv != SECSuccess)
return rv;
@@ -593,17 +499,29 @@ tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData
PRINT_BUF(50, (ss, "Handling PreSharedKey value",
label.data, label.len));
rv = ssl3_ProcessSessionTicketCommon(
- CONST_CAST(sslSocket, ss), &label);
+ CONST_CAST(sslSocket, ss), &label, appToken);
/* This only happens if we have an internal error, not
* a malformed ticket. Bogus tickets just don't resume
* and return SECSuccess. */
if (rv != SECSuccess)
return SECFailure;
+
+ if (ss->sec.ci.sid) {
+ /* xtnData->ticketAge contains the baseline we use for
+ * calculating the ticket age (i.e., our RTT estimate less the
+ * value of ticket_age_add).
+ *
+ * Add that to the obfuscated ticket age to recover the client's
+ * view of the ticket age plus the estimated RTT.
+ *
+ * See ssl3_EncodeSessionTicket() for details. */
+ xtnData->ticketAge += obfuscatedAge;
+ }
}
++numIdentities;
}
- xtnData->pskBinderPrefixLen = ss->ssl3.hs.messages.len - data->len;
+ xtnData->pskBindersLen = data->len;
/* Parse the binders list. */
rv = ssl3_ExtConsumeHandshakeVariable(ss,
@@ -635,7 +553,7 @@ tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData
/* Keep track of negotiated extensions. Note that this does not
* mean we are resuming. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_pre_shared_key_xtn;
return SECSuccess;
@@ -645,43 +563,27 @@ alert_loser:
return SECFailure;
}
-PRInt32
+SECStatus
tls13_ServerSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_length =
- 2 + 2 + 2; /* type + len + index */
SECStatus rv;
- if (maxBytes < (PRUint32)extension_length) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
- if (rv != SECSuccess)
- return -1;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, 2, 2);
- if (rv != SECSuccess)
- return -1;
-
- /* We only process the first session ticket the client sends,
- * so the index is always 0. */
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
+ /* We only process the first session ticket the client sends,
+ * so the index is always 0. */
+ rv = sslBuffer_AppendNumber(buf, 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extension_length;
+ *added = PR_TRUE;
+ return SECSuccess;
}
/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
* that contain session tickets. */
SECStatus
-tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
PRUint32 index;
@@ -713,7 +615,7 @@ tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_pre_shared_key_xtn;
return SECSuccess;
}
@@ -721,43 +623,20 @@ tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData
/*
* struct { } EarlyDataIndication;
*/
-PRInt32
+SECStatus
tls13_ClientSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
+ sslBuffer *buf, PRBool *added)
{
- SECStatus rv;
- PRInt32 extension_length;
-
- if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid))
- return 0;
-
- /* type + length */
- extension_length = 2 + 2;
-
- if (maxBytes < (PRUint32)extension_length) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2);
- if (rv != SECSuccess)
- return -1;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
-
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_tls13_early_data_xtn;
+ if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid)) {
+ return SECSuccess;
}
- return extension_length;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
@@ -779,44 +658,14 @@ tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, P
return SECFailure;
}
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_early_data_xtn;
return SECSuccess;
}
-/* This is only registered if we are sending it. */
-PRInt32
-tls13_ServerSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes)
-{
- SSL_TRC(3, ("%d: TLS13[%d]: send early_data extension",
- SSL_GETPID(), ss->fd));
-
- PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
- if (maxBytes < 4) {
- PORT_Assert(0);
- return 0;
- }
-
- if (append) {
- SECStatus rv;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2);
- if (rv != SECSuccess)
- return -1;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
- }
-
- return 4;
-}
-
/* This will only be called if we also offered the extension. */
SECStatus
-tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
{
SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
@@ -834,19 +683,19 @@ tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, P
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_early_data_xtn;
return SECSuccess;
}
SECStatus
-tls13_ClientHandleTicketEarlyDataInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
- SECItem *data)
+tls13_ClientHandleTicketEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
PRUint32 utmp;
SECStatus rv;
- SSL_TRC(3, ("%d: TLS13[%d]: handle early_data_info extension",
+ SSL_TRC(3, ("%d: TLS13[%d]: handle ticket early_data extension",
SSL_GETPID(), ss->fd));
/* The server must not send this extension when negotiating < TLS 1.3. */
@@ -873,59 +722,71 @@ tls13_ClientHandleTicketEarlyDataInfoXtn(const sslSocket *ss, TLSExtensionData *
/*
* struct {
+ * select (Handshake.msg_type) {
+ * case client_hello:
* ProtocolVersion versions<2..254>;
+ * case server_hello:
+ * ProtocolVersion version;
+ * };
* } SupportedVersions;
*/
-PRInt32
-tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes)
+SECStatus
+tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extensions_len;
PRUint16 version;
+ unsigned int lengthOffset;
SECStatus rv;
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
- return 0;
+ return SECSuccess;
}
- SSL_TRC(3, ("%d: TLS13[%d]: send supported_versions extension",
+ SSL_TRC(3, ("%d: TLS13[%d]: client send supported_versions extension",
SSL_GETPID(), ss->fd));
- /* Extension type, extension len fiels, vector len field,
- * length of the values. */
- extensions_len = 2 + 2 + 1 +
- 2 * (ss->vrange.max - ss->vrange.min + 1);
+ rv = sslBuffer_Skip(buf, 1, &lengthOffset);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- if (maxBytes < (PRUint32)extensions_len) {
- PORT_Assert(0);
- return 0;
+ for (version = ss->vrange.max; version >= ss->vrange.min; --version) {
+ rv = sslBuffer_AppendNumber(buf, tls13_EncodeDraftVersion(version), 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
- if (append) {
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_supported_versions_xtn, 2);
- if (rv != SECSuccess)
- return -1;
+ rv = sslBuffer_InsertLength(buf, lengthOffset, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- rv = ssl3_ExtAppendHandshakeNumber(ss, extensions_len - 4, 2);
- if (rv != SECSuccess)
- return -1;
+ *added = PR_TRUE;
+ return SECSuccess;
+}
- rv = ssl3_ExtAppendHandshakeNumber(ss, extensions_len - 5, 1);
- if (rv != SECSuccess)
- return -1;
+SECStatus
+tls13_ServerSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
- for (version = ss->vrange.max; version >= ss->vrange.min; --version) {
- rv = ssl3_ExtAppendHandshakeNumber(
- ss, tls13_EncodeDraftVersion(version), 2);
- if (rv != SECSuccess)
- return -1;
- }
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: server send supported_versions extension",
+ SSL_GETPID(), ss->fd));
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_tls13_supported_versions_xtn;
+ rv = sslBuffer_AppendNumber(
+ buf, tls13_EncodeDraftVersion(SSL_LIBRARY_VERSION_TLS_1_3), 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- return extensions_len;
+ *added = PR_TRUE;
+ return SECSuccess;
}
/*
@@ -934,7 +795,8 @@ tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnD
* } Cookie;
*/
SECStatus
-tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
@@ -960,41 +822,57 @@ tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData, PRUi
return SECSuccess;
}
-PRInt32
-tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+SECStatus
+tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRInt32 extension_len;
+ SECStatus rv;
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
!ss->ssl3.hs.cookie.len) {
- return 0;
+ return SECSuccess;
}
SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd));
+ rv = sslBuffer_AppendVariable(buf, ss->ssl3.hs.cookie.data,
+ ss->ssl3.hs.cookie.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- /* Extension type, length, cookie length, cookie value. */
- extension_len = 2 + 2 + 2 + ss->ssl3.hs.cookie.len;
+ *added = PR_TRUE;
+ return SECSuccess;
+}
- if (maxBytes < (PRUint32)extension_len) {
- PORT_Assert(0);
- return 0;
- }
+SECStatus
+tls13_ServerHandleCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
- if (append) {
- SECStatus rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_cookie_xtn, 2);
- if (rv != SECSuccess)
- return -1;
+ SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
+ SSL_GETPID(), ss->fd));
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_len - 4, 2);
- if (rv != SECSuccess)
- return -1;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &xtnData->cookie, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
- rv = ssl3_ExtAppendHandshakeVariable(ss, ss->ssl3.hs.cookie.data,
- ss->ssl3.hs.cookie.len, 2);
- if (rv != SECSuccess)
- return -1;
+ if (xtnData->cookie.len == 0) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ return SECFailure;
}
- return extension_len;
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_cookie_xtn;
+
+ return SECSuccess;
}
/*
@@ -1004,54 +882,33 @@ tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRB
* PskKeyExchangeMode ke_modes<1..255>;
* } PskKeyExchangeModes;
*/
-PRInt32
-tls13_ClientSendPskKeyExchangeModesXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes)
+SECStatus
+tls13_ClientSendPskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
static const PRUint8 ke_modes[] = { tls13_psk_dh_ke };
- static const unsigned long ke_modes_len = sizeof(ke_modes);
- PRInt32 extension_len;
+ SECStatus rv;
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
ss->opt.noCache) {
- return 0;
+ return SECSuccess;
}
- extension_len =
- 2 + 2 + /* Type + length */
- 1 + ke_modes_len; /* key exchange modes vector */
-
SSL_TRC(3, ("%d: TLS13[%d]: send psk key exchange modes extension",
SSL_GETPID(), ss->fd));
- if (maxBytes < (PRUint32)extension_len) {
- PORT_Assert(0);
- return 0;
+ rv = sslBuffer_AppendVariable(buf, ke_modes, sizeof(ke_modes), 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (append) {
- SECStatus rv = ssl3_ExtAppendHandshakeNumber(
- ss, ssl_tls13_psk_key_exchange_modes_xtn, 2);
- if (rv != SECSuccess)
- return -1;
-
- rv = ssl3_ExtAppendHandshakeNumber(ss, extension_len - 4, 2);
- if (rv != SECSuccess)
- return -1;
-
- rv = ssl3_ExtAppendHandshakeVariable(
- ss, ke_modes, ke_modes_len, 1);
- if (rv != SECSuccess)
- return -1;
- }
- return extension_len;
+ *added = PR_TRUE;
+ return SECSuccess;
}
SECStatus
-tls13_ServerHandlePskKeyExchangeModesXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data)
+tls13_ServerHandlePskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
{
SECStatus rv;
@@ -1076,112 +933,126 @@ tls13_ServerHandlePskKeyExchangeModesXtn(const sslSocket *ss,
}
/* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+ xtnData->negotiated[xtnData->numNegotiated++] =
+ ssl_tls13_psk_key_exchange_modes_xtn;
return SECSuccess;
}
-PRInt32
-tls13_SendShortHeaderXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes)
+SECStatus
+tls13_SendCertAuthoritiesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- PRUint32 extension_len = 2 + 2; /* Type + length (0). */
+ unsigned int calen;
+ const SECItem *name;
+ unsigned int nnames;
+ SECStatus rv;
- if (!ss->opt.enableShortHeaders) {
- return 0;
- }
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
- /* Presently this is incompatible with 0-RTT. We will fix if
- * it becomes more than an experiment. */
- if (ss->opt.enable0RttData) {
- return 0;
+ rv = ssl_GetCertificateRequestCAs(ss, &calen, &name, &nnames);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- if (IS_DTLS(ss)) {
- return 0;
+ if (!calen) {
+ return SECSuccess;
}
- /* Don't send this if TLS 1.3 isn't at least possible. */
- if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
- /* This should only happen on the client. */
- PORT_Assert(!ss->sec.isServer);
- return 0;
+ rv = sslBuffer_AppendNumber(buf, calen, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- SSL_TRC(3, ("%d: TLS13[%d]: send short_header extension",
- SSL_GETPID(), ss->fd));
-
- if (maxBytes < extension_len) {
- PORT_Assert(0);
- return 0;
+ while (nnames) {
+ rv = sslBuffer_AppendVariable(buf, name->data, name->len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ ++name;
+ --nnames;
}
- if (append) {
- SECStatus rv;
+ *added = PR_TRUE;
+ return SECSuccess;
+}
- rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_short_header_xtn, 2);
- if (rv != SECSuccess)
- return -1;
+SECStatus
+tls13_ClientHandleCertAuthoritiesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ PLArenaPool *arena;
- rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess)
- return -1;
+ if (!data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST);
+ return SECFailure;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
- xtnData->advertised[xtnData->numAdvertised++] =
- ssl_tls13_short_header_xtn;
+ xtnData->certReqAuthorities.arena = arena;
+ rv = ssl3_ParseCertificateRequestCAs((sslSocket *)ss,
+ &data->data, &data->len,
+ &xtnData->certReqAuthorities);
+ if (rv != SECSuccess) {
+ goto loser;
}
+ if (data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST);
+ goto loser;
+ }
+ return SECSuccess;
- return extension_len;
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ xtnData->certReqAuthorities.arena = NULL;
+ return SECFailure;
}
SECStatus
-tls13_HandleShortHeaderXtn(
- const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
- SECItem *data)
+tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
{
- SSL_TRC(3, ("%d: TLS13[%d]: handle short_header extension",
- SSL_GETPID(), ss->fd));
+ SECStatus rv;
- /* The client might have asked for this, but we didn't negotiate TLS 1.3. */
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- return SECSuccess;
- }
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
- /* Presently this is incompatible with 0-RTT. We will fix if
- * it becomes more than an experiment. */
- if (ss->opt.enable0RttData) {
+ if (!xtnData->selectedGroup) {
return SECSuccess;
}
- if (IS_DTLS(ss)) {
- PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
- return SECFailure;
- }
-
- if (data->len) {
- PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
+ rv = sslBuffer_AppendNumber(buf, xtnData->selectedGroup->name, 2);
+ if (rv != SECSuccess) {
return SECFailure;
}
- if (!ss->opt.enableShortHeaders) {
- /* Ignore. */
- return SECSuccess;
- }
+ *added = PR_TRUE;
+ return SECSuccess;
+}
- /* Keep track of negotiated extensions. */
- xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+SECStatus
+tls13_ServerSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
- if (ss->sec.isServer) {
- SECStatus rv;
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ PORT_Assert(xtnData->cookie.len > 0);
- rv = ssl3_RegisterExtensionSender(ss, xtnData,
- ssl_tls13_short_header_xtn,
- tls13_SendShortHeaderXtn);
- if (rv != SECSuccess) {
- return SECFailure;
- }
+ rv = sslBuffer_AppendVariable(buf,
+ xtnData->cookie.data, xtnData->cookie.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
+ *added = PR_TRUE;
return SECSuccess;
}
diff --git a/security/nss/lib/ssl/tls13exthandle.h b/security/nss/lib/ssl/tls13exthandle.h
index b798c6b55..edce94d83 100644
--- a/security/nss/lib/ssl/tls13exthandle.h
+++ b/security/nss/lib/ssl/tls13exthandle.h
@@ -9,66 +9,80 @@
#ifndef __tls13exthandle_h_
#define __tls13exthandle_h_
-PRInt32 tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
-PRInt32 tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-SECStatus tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus tls13_ServerSendStatusRequestXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ClientSendKeyShareXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ClientHandleKeyShareXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-SECStatus tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-SECStatus tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus tls13_ServerHandleKeyShareXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 tls13_ServerSendKeyShareXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-PRInt32 tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
- PRUint32 maxBytes);
-SECStatus tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus tls13_ServerSendKeyShareXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-SECStatus tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRUint16 ex_type,
+SECStatus tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 tls13_ServerSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-PRInt32 tls13_ClientSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-SECStatus tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+SECStatus tls13_ServerSendPreSharedKeyXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ClientSendEarlyDataXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ServerHandleEarlyDataXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-SECStatus tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+SECStatus tls13_ClientHandleEarlyDataXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 tls13_ServerSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-SECStatus tls13_ClientHandleTicketEarlyDataInfoXtn(
- const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
- SECItem *data);
-PRInt32 tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-SECStatus tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+SECStatus tls13_ClientHandleTicketEarlyDataXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ServerSendSupportedVersionsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus tls13_ClientHandleHrrCookie(const sslSocket *ss,
+ TLSExtensionData *xtnData,
SECItem *data);
-PRInt32 tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
- PRBool append,
- PRUint32 maxBytes);
-PRInt32 tls13_ClientSendPskKeyExchangeModesXtn(const sslSocket *ss,
+SECStatus tls13_ClientSendHrrCookieXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ClientSendPskModesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ServerHandlePskModesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus tls13_SendCertAuthoritiesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *append);
+SECStatus tls13_ClientHandleCertAuthoritiesXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
-SECStatus tls13_ServerHandlePskKeyExchangeModesXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRUint16 ex_type, SECItem *data);
-PRInt32 tls13_SendShortHeaderXtn(const sslSocket *ss,
- TLSExtensionData *xtnData,
- PRBool append, PRUint32 maxBytes);
-SECStatus tls13_HandleShortHeaderXtn(
- const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
- SECItem *data);
+ SECItem *data);
+SECStatus tls13_ServerHandleCookieXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
+SECStatus tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus tls13_ServerSendHrrCookieXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
#endif
diff --git a/security/nss/lib/ssl/tls13hashstate.c b/security/nss/lib/ssl/tls13hashstate.c
new file mode 100644
index 000000000..e3232f524
--- /dev/null
+++ b/security/nss/lib/ssl/tls13hashstate.c
@@ -0,0 +1,181 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "pk11func.h"
+#include "ssl.h"
+#include "sslt.h"
+#include "sslimpl.h"
+#include "selfencrypt.h"
+#include "tls13con.h"
+#include "tls13err.h"
+#include "tls13hashstate.h"
+
+/*
+ * The cookie is structured as a self-encrypted structure with the
+ * inner value being.
+ *
+ * struct {
+ * uint8 indicator = 0xff; // To disambiguate from tickets.
+ * uint16 cipherSuite; // Selected cipher suite.
+ * uint16 keyShare; // Requested key share group (0=none)
+ * opaque applicationToken<0..65535>; // Application token
+ * opaque ch_hash[rest_of_buffer]; // H(ClientHello)
+ * } CookieInner;
+ */
+SECStatus
+tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup,
+ const PRUint8 *appToken, unsigned int appTokenLen,
+ PRUint8 *buf, unsigned int *len, unsigned int maxlen)
+{
+ SECStatus rv;
+ SSL3Hashes hashes;
+ PRUint8 cookie[1024];
+ sslBuffer cookieBuf = SSL_BUFFER(cookie);
+ static const PRUint8 indicator = 0xff;
+
+ /* Encode header. */
+ rv = sslBuffer_Append(&cookieBuf, &indicator, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(&cookieBuf, ss->ssl3.hs.cipher_suite, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(&cookieBuf,
+ selectedGroup ? selectedGroup->name : 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Application token. */
+ rv = sslBuffer_AppendVariable(&cookieBuf, appToken, appTokenLen, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Compute and encode hashes. */
+ rv = tls13_ComputeHandshakeHashes(ss, &hashes);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_Append(&cookieBuf, hashes.u.raw, hashes.len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Encrypt right into the buffer. */
+ rv = ssl_SelfEncryptProtect(ss, cookieBuf.buf, cookieBuf.len,
+ buf, len, maxlen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Recover the hash state from the cookie. */
+SECStatus
+tls13_RecoverHashState(sslSocket *ss,
+ unsigned char *cookie, unsigned int cookieLen,
+ ssl3CipherSuite *previousCipherSuite,
+ const sslNamedGroupDef **previousGroup)
+{
+ SECStatus rv;
+ unsigned char plaintext[1024];
+ SECItem ptItem = { siBuffer, plaintext, 0 };
+ sslBuffer messageBuf = SSL_BUFFER_EMPTY;
+ PRUint32 sentinel;
+ PRUint32 cipherSuite;
+ PRUint32 group;
+ const sslNamedGroupDef *selectedGroup;
+ PRUint32 appTokenLen;
+ PRUint8 *appToken;
+
+ rv = ssl_SelfEncryptUnprotect(ss, cookie, cookieLen,
+ ptItem.data, &ptItem.len, sizeof(plaintext));
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Should start with 0xff. */
+ rv = ssl3_ConsumeNumberFromItem(&ptItem, &sentinel, 1);
+ if ((rv != SECSuccess) || (sentinel != 0xff)) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ /* The cipher suite should be the same or there are some shenanigans. */
+ rv = ssl3_ConsumeNumberFromItem(&ptItem, &cipherSuite, 2);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* The named group, if any. */
+ rv = ssl3_ConsumeNumberFromItem(&ptItem, &group, 2);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ selectedGroup = ssl_LookupNamedGroup(group);
+
+ /* Application token. */
+ PORT_Assert(ss->xtnData.applicationToken.len == 0);
+ rv = ssl3_ConsumeNumberFromItem(&ptItem, &appTokenLen, 2);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ if (SECITEM_AllocItem(NULL, &ss->xtnData.applicationToken,
+ appTokenLen) == NULL) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
+ ss->xtnData.applicationToken.len = appTokenLen;
+ rv = ssl3_ConsumeFromItem(&ptItem, &appToken, appTokenLen);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ PORT_Memcpy(ss->xtnData.applicationToken.data, appToken, appTokenLen);
+
+ /* The remainder is the hash. */
+ if (ptItem.len != tls13_GetHashSize(ss)) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* Now reinject the message. */
+ SSL_ASSERT_HASHES_EMPTY(ss);
+ rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0,
+ ptItem.data, ptItem.len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* And finally reinject the HRR. */
+ rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite,
+ selectedGroup,
+ cookie, cookieLen,
+ &messageBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0,
+ SSL_BUFFER_BASE(&messageBuf),
+ SSL_BUFFER_LEN(&messageBuf));
+ sslBuffer_Clear(&messageBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *previousCipherSuite = cipherSuite;
+ *previousGroup = selectedGroup;
+ return SECSuccess;
+}
diff --git a/security/nss/lib/ssl/tls13hashstate.h b/security/nss/lib/ssl/tls13hashstate.h
new file mode 100644
index 000000000..e9a4aa84f
--- /dev/null
+++ b/security/nss/lib/ssl/tls13hashstate.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __tls13hashstate_h_
+#define __tls13hashstate_h_
+
+#include "ssl.h"
+#include "sslt.h"
+#include "sslimpl.h"
+
+SECStatus tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup,
+ const PRUint8 *appToken, unsigned int appTokenLen,
+ PRUint8 *buf, unsigned int *len, unsigned int maxlen);
+SECStatus tls13_GetHrrCookieLength(sslSocket *ss, unsigned int *length);
+SECStatus tls13_RecoverHashState(sslSocket *ss,
+ unsigned char *cookie,
+ unsigned int cookieLen,
+ ssl3CipherSuite *previousCipherSuite,
+ const sslNamedGroupDef **previousGroup);
+#endif
diff --git a/security/nss/lib/ssl/tls13hkdf.c b/security/nss/lib/ssl/tls13hkdf.c
index 7e69bb882..8fa3375c6 100644
--- a/security/nss/lib/ssl/tls13hkdf.c
+++ b/security/nss/lib/ssl/tls13hkdf.c
@@ -134,10 +134,10 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash,
* Label, plus HandshakeHash. If it's ever to small, the code will abort.
*/
PRUint8 info[256];
- PRUint8 *ptr = info;
- unsigned int infoLen;
+ sslBuffer infoBuf = SSL_BUFFER(info);
PK11SymKey *derived;
- const char *kLabelPrefix = "TLS 1.3, ";
+ SECStatus rv;
+ const char *kLabelPrefix = "tls13 ";
const unsigned int kLabelPrefixLen = strlen(kLabelPrefix);
if (handshakeHash) {
@@ -170,29 +170,31 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash,
* - HkdfLabel.label is "TLS 1.3, " + Label
*
*/
- infoLen = 2 + 1 + kLabelPrefixLen + labelLen + 1 + handshakeHashLen;
- if (infoLen > sizeof(info)) {
- PORT_Assert(0);
- goto abort;
+ rv = sslBuffer_AppendNumber(&infoBuf, keySize, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
-
- ptr = ssl_EncodeUintX(keySize, 2, ptr);
- ptr = ssl_EncodeUintX(labelLen + kLabelPrefixLen, 1, ptr);
- PORT_Memcpy(ptr, kLabelPrefix, kLabelPrefixLen);
- ptr += kLabelPrefixLen;
- PORT_Memcpy(ptr, label, labelLen);
- ptr += labelLen;
- ptr = ssl_EncodeUintX(handshakeHashLen, 1, ptr);
- if (handshakeHash) {
- PORT_Memcpy(ptr, handshakeHash, handshakeHashLen);
- ptr += handshakeHashLen;
+ rv = sslBuffer_AppendNumber(&infoBuf, labelLen + kLabelPrefixLen, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_Append(&infoBuf, kLabelPrefix, kLabelPrefixLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_Append(&infoBuf, label, labelLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(&infoBuf, handshakeHash, handshakeHashLen, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
}
- PORT_Assert((ptr - info) == infoLen);
params.bExtract = CK_FALSE;
params.bExpand = CK_TRUE;
- params.pInfo = info;
- params.ulInfoLen = infoLen;
+ params.pInfo = SSL_BUFFER_BASE(&infoBuf);
+ params.ulInfoLen = SSL_BUFFER_LEN(&infoBuf);
paramsi.data = (unsigned char *)&params;
paramsi.len = sizeof(params);
@@ -211,20 +213,17 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash,
char labelStr[100];
PORT_Memcpy(labelStr, label, labelLen);
labelStr[labelLen] = 0;
- SSL_TRC(50, ("HKDF Expand: label=[TLS 1.3, ] + '%s',requested length=%d",
+ SSL_TRC(50, ("HKDF Expand: label='tls13 %s',requested length=%d",
labelStr, keySize));
}
PRINT_KEY(50, (NULL, "PRK", prk));
PRINT_BUF(50, (NULL, "Hash", handshakeHash, handshakeHashLen));
- PRINT_BUF(50, (NULL, "Info", info, infoLen));
+ PRINT_BUF(50, (NULL, "Info", SSL_BUFFER_BASE(&infoBuf),
+ SSL_BUFFER_LEN(&infoBuf)));
PRINT_KEY(50, (NULL, "Derived key", derived));
#endif
return SECSuccess;
-
-abort:
- PORT_SetError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE);
- return SECFailure;
}
SECStatus
diff --git a/security/nss/lib/ssl/tls13replay.c b/security/nss/lib/ssl/tls13replay.c
new file mode 100644
index 000000000..b090f9bca
--- /dev/null
+++ b/security/nss/lib/ssl/tls13replay.c
@@ -0,0 +1,276 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Anti-replay measures for TLS 1.3.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nss.h" /* for NSS_RegisterShutdown */
+#include "nssilock.h" /* for PZMonitor */
+#include "pk11pub.h"
+#include "prinit.h" /* for PR_CallOnce */
+#include "prmon.h"
+#include "prtime.h"
+#include "secerr.h"
+#include "ssl.h"
+#include "sslbloom.h"
+#include "sslimpl.h"
+#include "tls13hkdf.h"
+
+static struct {
+ /* Used to ensure that we only initialize the cleanup function once. */
+ PRCallOnceType init;
+ /* Used to serialize access to the filters. */
+ PZMonitor *lock;
+ /* The filters, use of which alternates. */
+ sslBloomFilter filters[2];
+ /* Which of the two filters is active (0 or 1). */
+ PRUint8 current;
+ /* The time that we will next update. */
+ PRTime nextUpdate;
+ /* The width of the window; i.e., the period of updates. */
+ PRTime window;
+ /* This key ensures that the bloom filter index is unpredictable. */
+ PK11SymKey *key;
+} ssl_anti_replay;
+
+/* Clear the current state and free any resources we allocated. The signature
+ * here is odd to allow this to be called during shutdown. */
+static SECStatus
+tls13_AntiReplayReset(void *appData, void *nssData)
+{
+ if (ssl_anti_replay.key) {
+ PK11_FreeSymKey(ssl_anti_replay.key);
+ ssl_anti_replay.key = NULL;
+ }
+ if (ssl_anti_replay.lock) {
+ PZ_DestroyMonitor(ssl_anti_replay.lock);
+ ssl_anti_replay.lock = NULL;
+ }
+ sslBloom_Destroy(&ssl_anti_replay.filters[0]);
+ sslBloom_Destroy(&ssl_anti_replay.filters[1]);
+ return SECSuccess;
+}
+
+static PRStatus
+tls13_AntiReplayInit(void)
+{
+ SECStatus rv = NSS_RegisterShutdown(tls13_AntiReplayReset, NULL);
+ if (rv != SECSuccess) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static SECStatus
+tls13_AntiReplayKeyGen()
+{
+ PRUint8 buf[32];
+ SECItem keyItem = { siBuffer, buf, sizeof(buf) };
+ PK11SlotInfo *slot;
+ SECStatus rv;
+
+ slot = PK11_GetInternalSlot();
+ if (!slot) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ rv = PK11_GenerateRandomOnSlot(slot, buf, sizeof(buf));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ ssl_anti_replay.key = PK11_ImportSymKey(slot, CKM_NSS_HKDF_SHA256,
+ PK11_OriginUnwrap, CKA_DERIVE,
+ &keyItem, NULL);
+ if (!ssl_anti_replay.key) {
+ goto loser;
+ }
+
+ PK11_FreeSlot(slot);
+ return SECSuccess;
+
+loser:
+ PK11_FreeSlot(slot);
+ return SECFailure;
+}
+
+/* Set a limit on the combination of number of hashes and bits in each hash. */
+#define SSL_MAX_BLOOM_FILTER_SIZE 64
+
+/*
+ * The structures created by this function can be called concurrently on
+ * multiple threads if the server is multi-threaded. A monitor is used to
+ * ensure that only one thread can access the structures that change over time,
+ * but no such guarantee is provided for configuration data.
+ *
+ * Functions that read from static configuration data depend on there being a
+ * memory barrier between the setup and use of this function.
+ */
+SECStatus
+SSLExp_SetupAntiReplay(PRTime window, unsigned int k, unsigned int bits)
+{
+ SECStatus rv;
+
+ if (k == 0 || bits == 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if ((k * (bits + 7) / 8) > SSL_MAX_BLOOM_FILTER_SIZE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (PR_SUCCESS != PR_CallOnce(&ssl_anti_replay.init,
+ tls13_AntiReplayInit)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ (void)tls13_AntiReplayReset(NULL, NULL);
+
+ ssl_anti_replay.lock = PZ_NewMonitor(nssILockSSL);
+ if (!ssl_anti_replay.lock) {
+ goto loser; /* Code already set. */
+ }
+
+ rv = tls13_AntiReplayKeyGen();
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+
+ rv = sslBloom_Init(&ssl_anti_replay.filters[0], k, bits);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+ rv = sslBloom_Init(&ssl_anti_replay.filters[1], k, bits);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+ /* When starting out, ensure that 0-RTT is not accepted until the window is
+ * updated. A ClientHello might have been accepted prior to a restart. */
+ sslBloom_Fill(&ssl_anti_replay.filters[1]);
+
+ ssl_anti_replay.current = 0;
+ ssl_anti_replay.nextUpdate = ssl_TimeUsec() + window;
+ ssl_anti_replay.window = window;
+ return SECSuccess;
+
+loser:
+ (void)tls13_AntiReplayReset(NULL, NULL);
+ return SECFailure;
+}
+
+/* This is exposed to tests. Though it could, this doesn't take the lock on the
+ * basis that those tests use thread confinement. */
+void
+tls13_AntiReplayRollover(PRTime now)
+{
+ ssl_anti_replay.current ^= 1;
+ ssl_anti_replay.nextUpdate = now + ssl_anti_replay.window;
+ sslBloom_Zero(ssl_anti_replay.filters + ssl_anti_replay.current);
+}
+
+static void
+tls13_AntiReplayUpdate()
+{
+ PRTime now;
+
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ssl_anti_replay.lock);
+
+ now = ssl_TimeUsec();
+ if (now < ssl_anti_replay.nextUpdate) {
+ return;
+ }
+
+ tls13_AntiReplayRollover(now);
+}
+
+PRBool
+tls13_InWindow(const sslSocket *ss, const sslSessionID *sid)
+{
+ PRInt32 timeDelta;
+
+ /* Calculate the difference between the client's view of the age of the
+ * ticket (in |ss->xtnData.ticketAge|) and the server's view, which we now
+ * calculate. The result should be close to zero. timeDelta is signed to
+ * make the comparisons below easier. */
+ timeDelta = ss->xtnData.ticketAge -
+ ((ssl_TimeUsec() - sid->creationTime) / PR_USEC_PER_MSEC);
+
+ /* Only allow the time delta to be at most half of our window. This is
+ * symmetrical, though it doesn't need to be; this assumes that clock errors
+ * on server and client will tend to cancel each other out.
+ *
+ * There are two anti-replay filters that roll over each window. In the
+ * worst case, immediately after a rollover of the filters, we only have a
+ * single window worth of recorded 0-RTT attempts. Thus, the period in
+ * which we can accept 0-RTT is at most one window wide. This uses PR_ABS()
+ * and half the window so that the first attempt can be up to half a window
+ * early and then replays will be caught until the attempts are half a
+ * window late.
+ *
+ * For example, a 0-RTT attempt arrives early, but near the end of window 1.
+ * The attempt is then recorded in window 1. Rollover to window 2 could
+ * occur immediately afterwards. Window 1 is still checked for new 0-RTT
+ * attempts for the remainder of window 2. Therefore, attempts to replay
+ * are detected because the value is recorded in window 1. When rollover
+ * occurs again, window 1 is erased and window 3 instated. If we allowed an
+ * attempt to be late by more than half a window, then this check would not
+ * prevent the same 0-RTT attempt from being accepted during window 1 and
+ * later window 3.
+ */
+ return PR_ABS(timeDelta) < (ssl_anti_replay.window / 2);
+}
+
+/* Checks for a duplicate in the two filters we have. Performs maintenance on
+ * the filters as a side-effect. This only detects a probable replay, it's
+ * possible that this will return true when the 0-RTT attempt is not genuinely a
+ * replay. In that case, we reject 0-RTT unnecessarily, but that's OK because
+ * no client expects 0-RTT to work every time. */
+PRBool
+tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid)
+{
+ PRBool replay;
+ unsigned int size;
+ PRUint8 index;
+ SECStatus rv;
+ static const char *label = "tls13 anti-replay";
+ PRUint8 buf[SSL_MAX_BLOOM_FILTER_SIZE];
+
+ /* If SSL_SetupAntiReplay hasn't been called, then treat all attempts at
+ * 0-RTT as a replay. */
+ if (!ssl_anti_replay.init.initialized) {
+ return PR_TRUE;
+ }
+
+ if (!tls13_InWindow(ss, sid)) {
+ return PR_TRUE;
+ }
+
+ size = ssl_anti_replay.filters[0].k *
+ (ssl_anti_replay.filters[0].bits + 7) / 8;
+ PORT_Assert(size <= SSL_MAX_BLOOM_FILTER_SIZE);
+ rv = tls13_HkdfExpandLabelRaw(ssl_anti_replay.key, ssl_hash_sha256,
+ ss->xtnData.pskBinder.data,
+ ss->xtnData.pskBinder.len,
+ label, strlen(label),
+ buf, size);
+ if (rv != SECSuccess) {
+ return PR_TRUE;
+ }
+
+ PZ_EnterMonitor(ssl_anti_replay.lock);
+ tls13_AntiReplayUpdate();
+
+ index = ssl_anti_replay.current;
+ replay = sslBloom_Add(&ssl_anti_replay.filters[index], buf);
+ if (!replay) {
+ replay = sslBloom_Check(&ssl_anti_replay.filters[index ^ 1],
+ buf);
+ }
+
+ PZ_ExitMonitor(ssl_anti_replay.lock);
+ return replay;
+}
diff --git a/security/nss/lib/util/nssb64d.c b/security/nss/lib/util/nssb64d.c
index 886ce21c0..e4bb20a3e 100644
--- a/security/nss/lib/util/nssb64d.c
+++ b/security/nss/lib/util/nssb64d.c
@@ -249,7 +249,7 @@ pl_base64_decode_buffer(PLBase64Decoder *data, const unsigned char *in,
}
i = 0;
- PR_ASSERT((out - data->output_buffer + 3) <= data->output_buflen);
+ PR_ASSERT((PRUint32)(out - data->output_buffer + 3) <= data->output_buflen);
/*
* Assume we are not at the end; the following function only works
diff --git a/security/nss/lib/util/nssrwlk.c b/security/nss/lib/util/nssrwlk.c
index dbaeca24b..5af021762 100644
--- a/security/nss/lib/util/nssrwlk.c
+++ b/security/nss/lib/util/nssrwlk.c
@@ -120,6 +120,8 @@ NSSRWLock_Destroy(NSSRWLock *rwlock)
{
PR_ASSERT(rwlock != NULL);
PR_ASSERT(rwlock->rw_waiting_readers == 0);
+ PR_ASSERT(rwlock->rw_writer_locks == 0);
+ PR_ASSERT(rwlock->rw_reader_locks == 0);
/* XXX Shouldn't we lock the PZLock before destroying this?? */
diff --git a/security/nss/lib/util/nssutil.def b/security/nss/lib/util/nssutil.def
index f4b9ef7ba..936455f6e 100644
--- a/security/nss/lib/util/nssutil.def
+++ b/security/nss/lib/util/nssutil.def
@@ -307,3 +307,19 @@ PK11URI_GetQueryAttribute;
;+ local:
;+ *;
;+};
+;+NSSUTIL_3.33 { # NSS Utilities 3.33 release
+;+ global:
+PORT_ZAllocAligned_Util;
+PORT_ZAllocAlignedOffset_Util;
+NSS_SecureMemcmpZero;
+;+ local:
+;+ *;
+;+};
+;-NSSUTIL_3.35 { # NSS Utilities 3.35 release
+;- global:
+;-# private exports for softoken
+_NSSUTIL_UTF8ToWide;-
+_NSSUTIL_Access;-
+;- local:
+;- *;
+;-};
diff --git a/security/nss/lib/util/nssutil.h b/security/nss/lib/util/nssutil.h
index e8cb52aed..f86dfa91e 100644
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -19,10 +19,10 @@
* The format of the version string should be
* "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
*/
-#define NSSUTIL_VERSION "3.32.1"
+#define NSSUTIL_VERSION "3.35"
#define NSSUTIL_VMAJOR 3
-#define NSSUTIL_VMINOR 32
-#define NSSUTIL_VPATCH 1
+#define NSSUTIL_VMINOR 35
+#define NSSUTIL_VPATCH 0
#define NSSUTIL_VBUILD 0
#define NSSUTIL_BETA PR_FALSE
diff --git a/security/nss/lib/util/pkcs11uri.c b/security/nss/lib/util/pkcs11uri.c
index 453440293..94b00171e 100644
--- a/security/nss/lib/util/pkcs11uri.c
+++ b/security/nss/lib/util/pkcs11uri.c
@@ -242,7 +242,7 @@ static int
pk11uri_CompareByPosition(const char *a, const char *b,
const char **attr_names, size_t num_attr_names)
{
- int i, j;
+ size_t i, j;
for (i = 0; i < num_attr_names; i++) {
if (strcmp(a, attr_names[i]) == 0) {
diff --git a/security/nss/lib/util/quickder.c b/security/nss/lib/util/quickder.c
index 1b474822e..7a6ac1c53 100644
--- a/security/nss/lib/util/quickder.c
+++ b/security/nss/lib/util/quickder.c
@@ -520,8 +520,7 @@ DecodeGroup(void* dest,
if (SECSuccess == rv) {
/* allocate room for pointer array and entries */
/* we want to allocate the array even if there is 0 entry */
- entries = (void**)PORT_ArenaZAlloc(arena, sizeof(void*) *
- (totalEntries + 1) + /* the extra one is for NULL termination */
+ entries = (void**)PORT_ArenaZAlloc(arena, sizeof(void*) * (totalEntries + 1) + /* the extra one is for NULL termination */
subTemplate->size * totalEntries);
if (entries) {
diff --git a/security/nss/lib/util/secasn1d.c b/security/nss/lib/util/secasn1d.c
index e6abb5fd5..4c5f0ce4b 100644
--- a/security/nss/lib/util/secasn1d.c
+++ b/security/nss/lib/util/secasn1d.c
@@ -2721,9 +2721,7 @@ dump_states(SEC_ASN1DecoderContext *cx)
(state == cx->current) ? "STATE" : "State",
state->theTemplate,
kindBuf);
- printf(" %s", (state->place >= 0 && state->place <= notInUse)
- ? place_names[state->place]
- : "(undefined)");
+ printf(" %s", (state->place >= 0 && state->place <= notInUse) ? place_names[state->place] : "(undefined)");
if (!i)
printf(", expect 0x%02x",
state->expect_tag_number | state->expect_tag_modifiers);
diff --git a/security/nss/lib/util/secoid.c b/security/nss/lib/util/secoid.c
index da03b7c06..a05621c59 100644
--- a/security/nss/lib/util/secoid.c
+++ b/security/nss/lib/util/secoid.c
@@ -1841,13 +1841,11 @@ secoid_FindDynamic(const SECItem *key)
{
SECOidData *ret = NULL;
+ NSSRWLock_LockRead(dynOidLock);
if (dynOidHash) {
- NSSRWLock_LockRead(dynOidLock);
- if (dynOidHash) { /* must check it again with lock held. */
- ret = (SECOidData *)PL_HashTableLookup(dynOidHash, key);
- }
- NSSRWLock_UnlockRead(dynOidLock);
+ ret = (SECOidData *)PL_HashTableLookup(dynOidHash, key);
}
+ NSSRWLock_UnlockRead(dynOidLock);
if (ret == NULL) {
PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID);
}
@@ -1866,14 +1864,12 @@ secoid_FindDynamicByTag(SECOidTag tagnum)
}
tagNumDiff = tagnum - SEC_OID_TOTAL;
- if (dynOidTable) {
- NSSRWLock_LockRead(dynOidLock);
- if (dynOidTable != NULL && /* must check it again with lock held. */
- tagNumDiff < dynOidEntriesUsed) {
- dxo = dynOidTable[tagNumDiff];
- }
- NSSRWLock_UnlockRead(dynOidLock);
+ NSSRWLock_LockRead(dynOidLock);
+ if (dynOidTable != NULL &&
+ tagNumDiff < dynOidEntriesUsed) {
+ dxo = dynOidTable[tagNumDiff];
}
+ NSSRWLock_UnlockRead(dynOidLock);
if (dxo == NULL) {
PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID);
}
diff --git a/security/nss/lib/util/secport.c b/security/nss/lib/util/secport.c
index 01a7d0834..e5bd4c1bb 100644
--- a/security/nss/lib/util/secport.c
+++ b/security/nss/lib/util/secport.c
@@ -21,6 +21,8 @@
#include "prenv.h"
#include "prinit.h"
+#include <stdint.h>
+
#ifdef DEBUG
#define THREADMARK
#endif /* DEBUG */
@@ -119,6 +121,51 @@ PORT_ZAlloc(size_t bytes)
return rv;
}
+/* aligned_alloc is C11. This is an alternative to get aligned memory. */
+void *
+PORT_ZAllocAligned(size_t bytes, size_t alignment, void **mem)
+{
+ size_t x = alignment - 1;
+
+ /* This only works if alignment is a power of 2. */
+ if ((alignment == 0) || (alignment & (alignment - 1))) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (!mem) {
+ return NULL;
+ }
+
+ /* Always allocate a non-zero amount of bytes */
+ *mem = PORT_ZAlloc((bytes ? bytes : 1) + x);
+ if (!*mem) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ return (void *)(((uintptr_t)*mem + x) & ~(uintptr_t)x);
+}
+
+void *
+PORT_ZAllocAlignedOffset(size_t size, size_t alignment, size_t offset)
+{
+ PORT_Assert(offset < size);
+ if (offset > size) {
+ return NULL;
+ }
+
+ void *mem = NULL;
+ void *v = PORT_ZAllocAligned(size, alignment, &mem);
+ if (!v) {
+ return NULL;
+ }
+
+ PORT_Assert(mem);
+ *((void **)((uintptr_t)v + offset)) = mem;
+ return v;
+}
+
void
PORT_Free(void *ptr)
{
@@ -733,3 +780,18 @@ NSS_SecureMemcmp(const void *ia, const void *ib, size_t n)
return r;
}
+
+/*
+ * Perform a constant-time check if a memory region is all 0. The return value
+ * is 0 if the memory region is all zero.
+ */
+unsigned int
+NSS_SecureMemcmpZero(const void *mem, size_t n)
+{
+ PRUint8 zero = 0;
+ size_t i;
+ for (i = 0; i < n; ++i) {
+ zero |= *(PRUint8 *)((uintptr_t)mem + i);
+ }
+ return zero;
+}
diff --git a/security/nss/lib/util/secport.h b/security/nss/lib/util/secport.h
index fb9ff4ebb..f1665a2f5 100644
--- a/security/nss/lib/util/secport.h
+++ b/security/nss/lib/util/secport.h
@@ -45,6 +45,7 @@
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
+#include <stdint.h>
#include "prtypes.h"
#include "prlog.h" /* for PR_ASSERT */
#include "plarena.h"
@@ -88,6 +89,9 @@ SEC_BEGIN_PROTOS
extern void *PORT_Alloc(size_t len);
extern void *PORT_Realloc(void *old, size_t len);
extern void *PORT_ZAlloc(size_t len);
+extern void *PORT_ZAllocAligned(size_t bytes, size_t alignment, void **mem);
+extern void *PORT_ZAllocAlignedOffset(size_t bytes, size_t alignment,
+ size_t offset);
extern void PORT_Free(void *ptr);
extern void PORT_ZFree(void *ptr, size_t len);
extern char *PORT_Strdup(const char *s);
@@ -131,6 +135,8 @@ SEC_END_PROTOS
#define PORT_CheckSuccess(f) (f)
#endif
#define PORT_ZNew(type) (type *)PORT_ZAlloc(sizeof(type))
+#define PORT_ZNewAligned(type, alignment, mem) \
+ (type *)PORT_ZAllocAlignedOffset(sizeof(type), alignment, offsetof(type, mem))
#define PORT_New(type) (type *)PORT_Alloc(sizeof(type))
#define PORT_ArenaNew(poolp, type) \
(type *)PORT_ArenaAlloc(poolp, sizeof(type))
@@ -246,6 +252,7 @@ sec_port_iso88591_utf8_conversion_function(
extern int NSS_PutEnv(const char *envVarName, const char *envValue);
extern int NSS_SecureMemcmp(const void *a, const void *b, size_t n);
+extern unsigned int NSS_SecureMemcmpZero(const void *mem, size_t n);
/*
* Load a shared library called "newShLibName" in the same directory as
diff --git a/security/nss/lib/util/utilmod.c b/security/nss/lib/util/utilmod.c
index 971b6c1dc..7d3fcda81 100644
--- a/security/nss/lib/util/utilmod.c
+++ b/security/nss/lib/util/utilmod.c
@@ -24,6 +24,7 @@
#if defined(_WIN32)
#include <io.h>
+#include <windows.h>
#endif
#ifdef XP_UNIX
#include <unistd.h>
@@ -34,15 +35,184 @@
#include <fcntl.h>
#if defined(_WIN32)
-#define os_open _open
#define os_fdopen _fdopen
-#define os_stat _stat
#define os_truncate_open_flags _O_CREAT | _O_RDWR | _O_TRUNC
#define os_append_open_flags _O_CREAT | _O_RDWR | _O_APPEND
#define os_open_permissions_type int
#define os_open_permissions_default _S_IREAD | _S_IWRITE
#define os_stat_type struct _stat
+
+/*
+ * Convert a UTF8 string to Unicode wide character
+ */
+LPWSTR
+_NSSUTIL_UTF8ToWide(const char *buf)
+{
+ DWORD size;
+ LPWSTR wide;
+
+ if (!buf) {
+ return NULL;
+ }
+
+ size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
+ if (size == 0) {
+ return NULL;
+ }
+ wide = PORT_Alloc(sizeof(WCHAR) * size);
+ if (!wide) {
+ return NULL;
+ }
+ size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wide, size);
+ if (size == 0) {
+ PORT_Free(wide);
+ return NULL;
+ }
+ return wide;
+}
+
+static int
+os_open(const char *filename, int oflag, int pmode)
+{
+ int fd;
+
+ if (!filename) {
+ return -1;
+ }
+
+ wchar_t *filenameWide = _NSSUTIL_UTF8ToWide(filename);
+ if (!filenameWide) {
+ return -1;
+ }
+ fd = _wopen(filenameWide, oflag, pmode);
+ PORT_Free(filenameWide);
+
+ return fd;
+}
+
+static int
+os_stat(const char *path, os_stat_type *buffer)
+{
+ int result;
+
+ if (!path) {
+ return -1;
+ }
+
+ wchar_t *pathWide = _NSSUTIL_UTF8ToWide(path);
+ if (!pathWide) {
+ return -1;
+ }
+ result = _wstat(pathWide, buffer);
+ PORT_Free(pathWide);
+
+ return result;
+}
+
+static FILE *
+os_fopen(const char *filename, const char *mode)
+{
+ FILE *fp;
+
+ if (!filename || !mode) {
+ return NULL;
+ }
+
+ wchar_t *filenameWide = _NSSUTIL_UTF8ToWide(filename);
+ if (!filenameWide) {
+ return NULL;
+ }
+ wchar_t *modeWide = _NSSUTIL_UTF8ToWide(mode);
+ if (!modeWide) {
+ PORT_Free(filenameWide);
+ return NULL;
+ }
+ fp = _wfopen(filenameWide, modeWide);
+ PORT_Free(filenameWide);
+ PORT_Free(modeWide);
+
+ return fp;
+}
+
+PRStatus
+_NSSUTIL_Access(const char *path, PRAccessHow how)
+{
+ int result;
+
+ if (!path) {
+ return PR_FAILURE;
+ }
+
+ int mode;
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ mode = 2;
+ break;
+ case PR_ACCESS_READ_OK:
+ mode = 4;
+ break;
+ case PR_ACCESS_EXISTS:
+ mode = 0;
+ break;
+ default:
+ return PR_FAILURE;
+ }
+
+ wchar_t *pathWide = _NSSUTIL_UTF8ToWide(path);
+ if (!pathWide) {
+ return PR_FAILURE;
+ }
+ result = _waccess(pathWide, mode);
+ PORT_Free(pathWide);
+
+ return result < 0 ? PR_FAILURE : PR_SUCCESS;
+}
+
+static PRStatus
+nssutil_Delete(const char *name)
+{
+ BOOL result;
+
+ if (!name) {
+ return PR_FAILURE;
+ }
+
+ wchar_t *nameWide = _NSSUTIL_UTF8ToWide(name);
+ if (!nameWide) {
+ return PR_FAILURE;
+ }
+ result = DeleteFileW(nameWide);
+ PORT_Free(nameWide);
+
+ return result ? PR_SUCCESS : PR_FAILURE;
+}
+
+static PRStatus
+nssutil_Rename(const char *from, const char *to)
+{
+ BOOL result;
+
+ if (!from || !to) {
+ return PR_FAILURE;
+ }
+
+ wchar_t *fromWide = _NSSUTIL_UTF8ToWide(from);
+ if (!fromWide) {
+ return PR_FAILURE;
+ }
+ wchar_t *toWide = _NSSUTIL_UTF8ToWide(to);
+ if (!toWide) {
+ PORT_Free(fromWide);
+ return PR_FAILURE;
+ }
+ result = MoveFileW(fromWide, toWide);
+ PORT_Free(fromWide);
+ PORT_Free(toWide);
+
+ return result ? PR_SUCCESS : PR_FAILURE;
+}
#else
+#define os_fopen fopen
#define os_open open
#define os_fdopen fdopen
#define os_stat stat
@@ -51,6 +221,8 @@
#define os_open_permissions_type mode_t
#define os_open_permissions_default 0600
#define os_stat_type struct stat
+#define nssutil_Delete PR_Delete
+#define nssutil_Rename PR_Rename
#endif
/****************************************************************
@@ -219,7 +391,7 @@ nssutil_ReadSecmodDB(const char *appName,
}
/* do we really want to use streams here */
- fd = fopen(dbname, "r");
+ fd = os_fopen(dbname, "r");
if (fd == NULL)
goto done;
@@ -403,7 +575,7 @@ done:
}
/* old one exists */
- status = PR_Access(olddbname, PR_ACCESS_EXISTS);
+ status = _NSSUTIL_Access(olddbname, PR_ACCESS_EXISTS);
if (status == PR_SUCCESS) {
PR_smprintf_free(olddbname);
PORT_ZFree(moduleList, useCount * sizeof(char *));
@@ -532,7 +704,7 @@ nssutil_DeleteSecmodDBEntry(const char *appName,
}
/* do we really want to use streams here */
- fd = fopen(dbname, "r");
+ fd = os_fopen(dbname, "r");
if (fd == NULL)
goto loser;
@@ -602,10 +774,10 @@ nssutil_DeleteSecmodDBEntry(const char *appName,
fclose(fd2);
if (found) {
/* rename dbname2 to dbname */
- PR_Delete(dbname);
- PR_Rename(dbname2, dbname);
+ nssutil_Delete(dbname);
+ nssutil_Rename(dbname2, dbname);
} else {
- PR_Delete(dbname2);
+ nssutil_Delete(dbname2);
}
PORT_Free(dbname2);
PORT_Free(lib);
@@ -621,7 +793,7 @@ loser:
fclose(fd2);
}
if (dbname2) {
- PR_Delete(dbname2);
+ nssutil_Delete(dbname2);
PORT_Free(dbname2);
}
PORT_Free(lib);
diff --git a/security/nss/lib/util/utilpars.c b/security/nss/lib/util/utilpars.c
index 7116d26f3..e7435bfcc 100644
--- a/security/nss/lib/util/utilpars.c
+++ b/security/nss/lib/util/utilpars.c
@@ -589,6 +589,7 @@ struct nssutilArgSlotFlagTable {
}
static struct nssutilArgSlotFlagTable nssutil_argSlotFlagTable[] = {
NSSUTIL_ARG_ENTRY(RSA, SECMOD_RSA_FLAG),
+ NSSUTIL_ARG_ENTRY(ECC, SECMOD_ECC_FLAG),
NSSUTIL_ARG_ENTRY(DSA, SECMOD_RSA_FLAG),
NSSUTIL_ARG_ENTRY(RC2, SECMOD_RC4_FLAG),
NSSUTIL_ARG_ENTRY(RC4, SECMOD_RC2_FLAG),
@@ -1110,12 +1111,8 @@ _NSSUTIL_EvaluateConfigDir(const char *configdir,
NSSDBType dbType;
PRBool checkEnvDefaultDB = PR_FALSE;
*appName = NULL;
-/* force the default */
-#ifdef NSS_DISABLE_DBM
+ /* force the default */
dbType = NSS_DB_TYPE_SQL;
-#else
- dbType = NSS_DB_TYPE_LEGACY;
-#endif
if (configdir == NULL) {
checkEnvDefaultDB = PR_TRUE;
} else if (PORT_Strncmp(configdir, MULTIACCESS, sizeof(MULTIACCESS) - 1) == 0) {
diff --git a/security/nss/lib/util/utilpars.h b/security/nss/lib/util/utilpars.h
index 70767263a..1b0b1ff1c 100644
--- a/security/nss/lib/util/utilpars.h
+++ b/security/nss/lib/util/utilpars.h
@@ -59,5 +59,11 @@ char *NSSUTIL_MkNSSString(char **slotStrings, int slotCount, PRBool internal,
char *_NSSUTIL_GetSecmodName(const char *param, NSSDBType *dbType,
char **appName, char **filename, PRBool *rw);
const char *_NSSUTIL_EvaluateConfigDir(const char *configdir, NSSDBType *dbType, char **app);
+#if defined(_WIN32)
+wchar_t *_NSSUTIL_UTF8ToWide(const char *buf);
+PRStatus _NSSUTIL_Access(const char *path, PRAccessHow how);
+#else
+#define _NSSUTIL_Access(path, how) PR_Access((path), (how))
+#endif
#endif /* _UTILPARS_H_ */
diff --git a/security/nss/lib/util/utilparst.h b/security/nss/lib/util/utilparst.h
index f2148e6e3..5dda09028 100644
--- a/security/nss/lib/util/utilparst.h
+++ b/security/nss/lib/util/utilparst.h
@@ -43,7 +43,7 @@
#define NSSUTIL_DEFAULT_INTERNAL_INIT3 \
" askpw=any timeout=30})\""
#define NSSUTIL_DEFAULT_SFTKN_FLAGS \
- "slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512]"
+ "slotFlags=[ECC,RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512]"
#define NSSUTIL_DEFAULT_CIPHER_ORDER 0
#define NSSUTIL_DEFAULT_TRUST_ORDER 50
diff --git a/security/nss/lib/util/utilrename.h b/security/nss/lib/util/utilrename.h
index 1aea3d284..19ddba666 100644
--- a/security/nss/lib/util/utilrename.h
+++ b/security/nss/lib/util/utilrename.h
@@ -70,6 +70,8 @@
#define PORT_UCS2_ASCIIConversion PORT_UCS2_ASCIIConversion_Util
#define PORT_UCS2_UTF8Conversion PORT_UCS2_UTF8Conversion_Util
#define PORT_ZAlloc PORT_ZAlloc_Util
+#define PORT_ZAllocAligned PORT_ZAllocAligned_Util
+#define PORT_ZAllocAlignedOffset PORT_ZAllocAlignedOffset_Util
#define PORT_ZFree PORT_ZFree_Util
#define SEC_ASN1Decode SEC_ASN1Decode_Util
#define SEC_ASN1DecodeInteger SEC_ASN1DecodeInteger_Util
diff --git a/security/nss/mach b/security/nss/mach
new file mode 100644
index 000000000..715f1a9e3
--- /dev/null
+++ b/security/nss/mach
@@ -0,0 +1,219 @@
+#!/usr/bin/env 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/.
+##########################################################################
+#
+# This is a collection of helper tools to get stuff done in NSS.
+#
+
+import sys
+import argparse
+import subprocess
+import os
+import platform
+from hashlib import sha256
+
+cwd = os.path.dirname(os.path.abspath(__file__))
+
+
+class cfAction(argparse.Action):
+ docker_command = ["docker"]
+ restorecon = None
+
+ def __call__(self, parser, args, values, option_string=None):
+ if not args.noroot:
+ self.setDockerCommand()
+
+ if values:
+ files = [os.path.relpath(os.path.abspath(x), start=cwd) for x in values]
+ else:
+ files = self.modifiedFiles()
+ files = [os.path.join('/home/worker/nss', x) for x in files]
+
+ # First check if we can run docker.
+ try:
+ with open(os.devnull, "w") as f:
+ subprocess.check_call(
+ self.docker_command + ["images"], stdout=f)
+ except:
+ print("Please install docker and start the docker daemon.")
+ sys.exit(1)
+
+ docker_image = 'clang-format-service:latest'
+ cf_docker_folder = cwd + "/automation/clang-format"
+
+ # Build the image if necessary.
+ if self.filesChanged(cf_docker_folder):
+ self.buildImage(docker_image, cf_docker_folder)
+
+ # Check if we have the docker image.
+ try:
+ command = self.docker_command + [
+ "image", "inspect", "clang-format-service:latest"
+ ]
+ with open(os.devnull, "w") as f:
+ subprocess.check_call(command, stdout=f)
+ except:
+ print("I have to build the docker image first.")
+ self.buildImage(docker_image, cf_docker_folder)
+
+ command = self.docker_command + [
+ 'run', '-v', cwd + ':/home/worker/nss:Z', '--rm', '-ti', docker_image
+ ]
+ # The clang format script returns 1 if something's to do. We don't
+ # care.
+ subprocess.call(command + files)
+ if self.restorecon is not None:
+ subprocess.call([self.restorecon, '-R', cwd])
+
+ def filesChanged(self, path):
+ hash = sha256()
+ for dirname, dirnames, files in os.walk(path):
+ for file in files:
+ with open(os.path.join(dirname, file), "rb") as f:
+ hash.update(f.read())
+ chk_file = cwd + "/.chk"
+ old_chk = ""
+ new_chk = hash.hexdigest()
+ if os.path.exists(chk_file):
+ with open(chk_file) as f:
+ old_chk = f.readline()
+ if old_chk != new_chk:
+ with open(chk_file, "w+") as f:
+ f.write(new_chk)
+ return True
+ return False
+
+ def buildImage(self, docker_image, cf_docker_folder):
+ command = self.docker_command + [
+ "build", "-t", docker_image, cf_docker_folder
+ ]
+ subprocess.check_call(command)
+ return
+
+ def setDockerCommand(self):
+ if platform.system() == "Linux":
+ from distutils.spawn import find_executable
+ self.restorecon = find_executable('restorecon')
+ self.docker_command = ["sudo"] + self.docker_command
+
+ def modifiedFiles(self):
+ files = []
+ if os.path.exists(os.path.join(cwd, '.hg')):
+ st = subprocess.Popen(['hg', 'status', '-m', '-a'],
+ cwd=cwd, stdout=subprocess.PIPE)
+ for line in iter(st.stdout.readline, ''):
+ files += [line[2:].rstrip()]
+ elif os.path.exists(os.path.join(cwd, '.git')):
+ st = subprocess.Popen(['git', 'status', '--porcelain'],
+ cwd=cwd, stdout=subprocess.PIPE)
+ for line in iter(st.stdout.readline, ''):
+ if line[1] == 'M' or line[1] != 'D' and \
+ (line[0] == 'M' or line[0] == 'A' or
+ line[0] == 'C' or line[0] == 'U'):
+ files += [line[3:].rstrip()]
+ elif line[0] == 'R':
+ files += [line[line.index(' -> ', beg=4) + 4:]]
+ else:
+ print('Warning: neither mercurial nor git detected!')
+
+ def isFormatted(x):
+ return x[-2:] == '.c' or x[-3:] == '.cc' or x[-2:] == '.h'
+ return [x for x in files if isFormatted(x)]
+
+
+class buildAction(argparse.Action):
+
+ def __call__(self, parser, args, values, option_string=None):
+ cwd = os.path.dirname(os.path.abspath(__file__))
+ subprocess.check_call([cwd + "/build.sh"] + values)
+
+
+class testAction(argparse.Action):
+
+ def runTest(self, test, cycles="standard"):
+ cwd = os.path.dirname(os.path.abspath(__file__))
+ domsuf = os.getenv('DOMSUF', "localdomain")
+ host = os.getenv('HOST', "localhost")
+ env = {
+ "NSS_TESTS": test,
+ "NSS_CYCLES": cycles,
+ "DOMSUF": domsuf,
+ "HOST": host
+ }
+ os_env = os.environ
+ os_env.update(env)
+ command = cwd + "/tests/all.sh"
+ subprocess.check_call(command, env=os_env)
+
+ def __call__(self, parser, args, values, option_string=None):
+ self.runTest(values)
+
+
+class commandsAction(argparse.Action):
+ commands = []
+
+ def __call__(self, parser, args, values, option_string=None):
+ for c in commandsAction.commands:
+ print(c)
+
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(
+ description='NSS helper script. ' +
+ 'Make sure to separate sub-command arguments with --.')
+ subparsers = parser.add_subparsers()
+
+ parser_build = subparsers.add_parser(
+ 'build', help='All arguments are passed to build.sh')
+ parser_build.add_argument(
+ 'build_args', nargs='*', help="build arguments", action=buildAction)
+
+ parser_cf = subparsers.add_parser(
+ 'clang-format',
+ help="""
+ Run clang-format.
+
+ By default this runs against any files that you have modified. If
+ there are no modified files, it checks everything.
+ """)
+ parser_cf.add_argument(
+ '--noroot',
+ help='On linux, suppress the use of \'sudo\' for running docker.',
+ action='store_true')
+ parser_cf.add_argument(
+ '<file/dir>',
+ nargs='*',
+ help="Specify files or directories to run clang-format on",
+ action=cfAction)
+
+ parser_test = subparsers.add_parser(
+ 'tests', help='Run tests through tests/all.sh.')
+ tests = [
+ "cipher", "lowhash", "chains", "cert", "dbtests", "tools", "fips",
+ "sdr", "crmf", "smime", "ssl", "ocsp", "merge", "pkits", "ec",
+ "gtests", "ssl_gtests"
+ ]
+ parser_test.add_argument(
+ 'test', choices=tests, help="Available tests", action=testAction)
+
+ parser_commands = subparsers.add_parser(
+ 'mach-commands',
+ help="list commands")
+ parser_commands.add_argument(
+ 'mach-commands',
+ nargs='*',
+ action=commandsAction)
+
+ commandsAction.commands = [c for c in subparsers.choices]
+ return parser.parse_args()
+
+
+def main():
+ parse_arguments()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/security/nss/nss-tool/.clang-format b/security/nss/nss-tool/.clang-format
new file mode 100644
index 000000000..06e3c5115
--- /dev/null
+++ b/security/nss/nss-tool/.clang-format
@@ -0,0 +1,4 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+...
diff --git a/security/nss/nss-tool/common/argparse.cc b/security/nss/nss-tool/common/argparse.cc
new file mode 100644
index 000000000..3b7c73891
--- /dev/null
+++ b/security/nss/nss-tool/common/argparse.cc
@@ -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/. */
+
+#include "argparse.h"
+
+ArgParser::ArgParser(const std::vector<std::string>& arguments) {
+ for (size_t i = 0; i < arguments.size(); i++) {
+ std::string arg = arguments.at(i);
+ if (arg.find("--") == 0) {
+ // look for an option argument
+ if (i + 1 < arguments.size() && arguments.at(i + 1).find("--") != 0) {
+ programArgs_[arg] = arguments.at(i + 1);
+ i++;
+ } else {
+ programArgs_[arg] = "";
+ }
+ } else {
+ // positional argument (e.g. required argument)
+ positionalArgs_.push_back(arg);
+ }
+ }
+}
diff --git a/security/nss/nss-tool/common/argparse.h b/security/nss/nss-tool/common/argparse.h
new file mode 100644
index 000000000..8645d5aaa
--- /dev/null
+++ b/security/nss/nss-tool/common/argparse.h
@@ -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/. */
+
+#ifndef argparse_h__
+#define argparse_h__
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class ArgParser {
+ public:
+ ArgParser(const std::vector<std::string>& arguments);
+
+ bool Has(std::string arg) const { return programArgs_.count(arg) > 0; }
+
+ std::string Get(std::string arg) const { return programArgs_.at(arg); }
+
+ size_t GetPositionalArgumentCount() const { return positionalArgs_.size(); }
+ std::string GetPositionalArgument(size_t pos) const {
+ return positionalArgs_.at(pos);
+ }
+
+ private:
+ std::unordered_map<std::string, std::string> programArgs_;
+ std::vector<std::string> positionalArgs_;
+};
+
+#endif // argparse_h__
diff --git a/security/nss/nss-tool/common/tool.h b/security/nss/nss-tool/common/tool.h
new file mode 100644
index 000000000..17ebcac29
--- /dev/null
+++ b/security/nss/nss-tool/common/tool.h
@@ -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/. */
+
+#ifndef tool_h__
+#define tool_h__
+
+#include <string>
+#include <vector>
+
+class Tool {
+ public:
+ virtual bool Run(const std::vector<std::string>& arguments) = 0;
+ virtual ~Tool() {}
+
+ private:
+ virtual void Usage() = 0;
+};
+
+#endif // tool_h__
diff --git a/security/nss/nss-tool/common/util.cc b/security/nss/nss-tool/common/util.cc
new file mode 100644
index 000000000..77459155a
--- /dev/null
+++ b/security/nss/nss-tool/common/util.cc
@@ -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 "util.h"
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <prerror.h>
+
+#if defined(__unix__) || defined(__APPLE__)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(WIN32) || defined(_WIN64)
+#include <Windows.h>
+#endif
+
+static std::string GetPassword(const std::string &prompt) {
+ std::cout << prompt << std::endl;
+
+#if defined(__unix__) || defined(__APPLE__)
+ termios oldt;
+ tcgetattr(STDIN_FILENO, &oldt);
+ termios newt = oldt;
+ newt.c_lflag &= ~ECHO;
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+#elif defined(WIN32) || defined(_WIN64)
+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD mode = 0;
+ GetConsoleMode(hStdin, &mode);
+ SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
+#endif
+
+ std::string pw;
+ std::getline(std::cin, pw);
+
+#if defined(__unix__) || defined(__APPLE__)
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+#elif defined(WIN32) || defined(_WIN64)
+ SetConsoleMode(hStdin, mode);
+#endif
+
+ return pw;
+}
+
+static char *GetModulePassword(PK11SlotInfo *slot, int retry, void *arg) {
+ if (arg == nullptr) {
+ return nullptr;
+ }
+
+ PwData *pwData = reinterpret_cast<PwData *>(arg);
+
+ if (retry > 0) {
+ std::cerr << "Incorrect password/PIN entered." << std::endl;
+ return nullptr;
+ }
+
+ switch (pwData->source) {
+ case PW_NONE:
+ case PW_FROMFILE:
+ std::cerr << "Password input method not supported." << std::endl;
+ return nullptr;
+ case PW_PLAINTEXT:
+ return PL_strdup(pwData->data);
+ default:
+ break;
+ }
+
+ std::cerr << "Password check failed: No password found." << std::endl;
+ return nullptr;
+}
+
+static std::vector<uint8_t> ReadFromIstream(std::istream &is) {
+ std::vector<uint8_t> data;
+ while (is) {
+ char buf[1024];
+ is.read(buf, sizeof(buf));
+ data.insert(data.end(), buf, buf + is.gcount());
+ }
+
+ return data;
+}
+
+static std::string GetNewPasswordFromUser(void) {
+ std::string pw;
+
+ while (true) {
+ pw = GetPassword("Enter new password: ");
+ if (pw == GetPassword("Re-enter password: ")) {
+ break;
+ }
+
+ std::cerr << "Passwords do not match. Try again." << std::endl;
+ }
+
+ return pw;
+}
+
+bool InitSlotPassword(void) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ std::cout << "Enter a password which will be used to encrypt your keys."
+ << std::endl
+ << std::endl;
+ std::string pw = GetNewPasswordFromUser();
+
+ SECStatus rv = PK11_InitPin(slot.get(), nullptr, pw.c_str());
+ if (rv != SECSuccess) {
+ std::cerr << "Init db password failed." << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool ChangeSlotPassword(void) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ // get old password and authenticate to db
+ PK11_SetPasswordFunc(&GetModulePassword);
+ std::string oldPw = GetPassword("Enter your current password: ");
+ PwData pwData = {PW_PLAINTEXT, const_cast<char *>(oldPw.c_str())};
+ SECStatus rv = PK11_Authenticate(slot.get(), false /*loadCerts*/, &pwData);
+ if (rv != SECSuccess) {
+ std::cerr << "Password incorrect." << std::endl;
+ return false;
+ }
+
+ // get new password
+ std::string newPw = GetNewPasswordFromUser();
+
+ if (PK11_ChangePW(slot.get(), oldPw.c_str(), newPw.c_str()) != SECSuccess) {
+ std::cerr << "Failed to change password." << std::endl;
+ return false;
+ }
+
+ std::cout << "Password changed successfully." << std::endl;
+ return true;
+}
+
+bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot) {
+ if (!PK11_NeedLogin(slot.get())) {
+ return true;
+ }
+
+ PK11_SetPasswordFunc(&GetModulePassword);
+ std::string pw = GetPassword("Enter your password: ");
+ PwData pwData = {PW_PLAINTEXT, const_cast<char *>(pw.c_str())};
+ SECStatus rv = PK11_Authenticate(slot.get(), true /*loadCerts*/, &pwData);
+ if (rv != SECSuccess) {
+ std::cerr << "Could not authenticate to token "
+ << PK11_GetTokenName(slot.get()) << ". Failed with error "
+ << PR_ErrorToName(PR_GetError()) << std::endl;
+ return false;
+ }
+ std::cout << std::endl;
+
+ return true;
+}
+
+std::string StringToHex(const ScopedSECItem &input) {
+ std::stringstream ss;
+ ss << "0x";
+ for (size_t i = 0; i < input->len; i++) {
+ ss << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>(input->data[i]);
+ }
+
+ return ss.str();
+}
+
+std::vector<uint8_t> ReadInputData(std::string dataPath) {
+ std::vector<uint8_t> data;
+ if (dataPath.empty()) {
+ std::cout << "No input file path given, using stdin." << std::endl;
+ data = ReadFromIstream(std::cin);
+ } else {
+ std::ifstream is(dataPath, std::ifstream::binary);
+ if (is.good()) {
+ data = ReadFromIstream(is);
+ } else {
+ std::cerr << "IO Error when opening " << dataPath << std::endl;
+ std::cerr << "Input file does not exist or you don't have permissions."
+ << std::endl;
+ }
+ }
+
+ return data;
+}
+
+std::istream &GetStreamFromFileOrStdin(std::string &path, std::ifstream &ifs) {
+ if (path.empty()) {
+ return std::cin;
+ }
+
+ ifs.open(path, std::ifstream::binary);
+ if (!ifs.good()) {
+ std::cerr << "IO Error when opening " << path << std::endl;
+ std::cerr << "Input file does not exist or you don't have permissions."
+ << std::endl;
+ }
+
+ return ifs;
+}
diff --git a/security/nss/nss-tool/common/util.h b/security/nss/nss-tool/common/util.h
new file mode 100644
index 000000000..58fb05839
--- /dev/null
+++ b/security/nss/nss-tool/common/util.h
@@ -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/. */
+
+#ifndef util_h__
+#define util_h__
+
+#include "nspr.h"
+#include "scoped_ptrs.h"
+
+#include <secmodt.h>
+#include <string>
+#include <vector>
+
+#ifndef PORT_Malloc
+#define PORT_Malloc PR_Malloc
+#endif
+
+enum PwDataType { PW_NONE = 0, PW_FROMFILE = 1, PW_PLAINTEXT = 2 };
+typedef struct {
+ PwDataType source;
+ char *data;
+} PwData;
+
+bool InitSlotPassword(void);
+bool ChangeSlotPassword(void);
+bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot);
+std::string StringToHex(const ScopedSECItem &input);
+std::vector<uint8_t> ReadInputData(std::string dataPath);
+std::istream &GetStreamFromFileOrStdin(std::string &path, std::ifstream &ifs);
+
+#endif // util_h__
diff --git a/security/nss/nss-tool/db/dbtool.cc b/security/nss/nss-tool/db/dbtool.cc
new file mode 100644
index 000000000..8c369cf05
--- /dev/null
+++ b/security/nss/nss-tool/db/dbtool.cc
@@ -0,0 +1,497 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "dbtool.h"
+#include "argparse.h"
+#include "scoped_ptrs.h"
+#include "util.h"
+
+#include <iomanip>
+#include <iostream>
+#include <regex>
+#include <sstream>
+
+#include <cert.h>
+#include <certdb.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <prio.h>
+
+const std::vector<std::string> kCommandArgs(
+ {"--create", "--list-certs", "--import-cert", "--list-keys", "--import-key",
+ "--delete-cert", "--delete-key", "--change-password"});
+
+static bool HasSingleCommandArgument(const ArgParser &parser) {
+ auto pred = [&](const std::string &cmd) { return parser.Has(cmd); };
+ return std::count_if(kCommandArgs.begin(), kCommandArgs.end(), pred) == 1;
+}
+
+static bool HasArgumentRequiringWriteAccess(const ArgParser &parser) {
+ return parser.Has("--create") || parser.Has("--import-cert") ||
+ parser.Has("--import-key") || parser.Has("--delete-cert") ||
+ parser.Has("--delete-key") || parser.Has("--change-password");
+}
+
+static std::string PrintFlags(unsigned int flags) {
+ std::stringstream ss;
+ if ((flags & CERTDB_VALID_CA) && !(flags & CERTDB_TRUSTED_CA) &&
+ !(flags & CERTDB_TRUSTED_CLIENT_CA)) {
+ ss << "c";
+ }
+ if ((flags & CERTDB_TERMINAL_RECORD) && !(flags & CERTDB_TRUSTED)) {
+ ss << "p";
+ }
+ if (flags & CERTDB_TRUSTED_CA) {
+ ss << "C";
+ }
+ if (flags & CERTDB_TRUSTED_CLIENT_CA) {
+ ss << "T";
+ }
+ if (flags & CERTDB_TRUSTED) {
+ ss << "P";
+ }
+ if (flags & CERTDB_USER) {
+ ss << "u";
+ }
+ if (flags & CERTDB_SEND_WARN) {
+ ss << "w";
+ }
+ if (flags & CERTDB_INVISIBLE_CA) {
+ ss << "I";
+ }
+ if (flags & CERTDB_GOVT_APPROVED_CA) {
+ ss << "G";
+ }
+ return ss.str();
+}
+
+static const char *const keyTypeName[] = {"null", "rsa", "dsa", "fortezza",
+ "dh", "kea", "ec"};
+
+void DBTool::Usage() {
+ std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
+ std::cerr << " --create" << std::endl;
+ std::cerr << " --change-password" << std::endl;
+ std::cerr << " --list-certs" << std::endl;
+ std::cerr << " --import-cert [<path>] --name <name> [--trusts <trusts>]"
+ << std::endl;
+ std::cerr << " --list-keys" << std::endl;
+ std::cerr << " --import-key [<path> [-- name <name>]]" << std::endl;
+ std::cerr << " --delete-cert <name>" << std::endl;
+ std::cerr << " --delete-key <name>" << std::endl;
+}
+
+bool DBTool::Run(const std::vector<std::string> &arguments) {
+ ArgParser parser(arguments);
+
+ if (!HasSingleCommandArgument(parser)) {
+ Usage();
+ return false;
+ }
+
+ PRAccessHow how = PR_ACCESS_READ_OK;
+ bool readOnly = true;
+ if (HasArgumentRequiringWriteAccess(parser)) {
+ how = PR_ACCESS_WRITE_OK;
+ readOnly = false;
+ }
+
+ std::string initDir(".");
+ if (parser.Has("--path")) {
+ initDir = parser.Get("--path");
+ }
+ if (PR_Access(initDir.c_str(), how) != PR_SUCCESS) {
+ std::cerr << "Directory '" << initDir
+ << "' does not exist or you don't have permissions!" << std::endl;
+ return false;
+ }
+
+ std::cout << "Using database directory: " << initDir << std::endl
+ << std::endl;
+
+ bool dbFilesExist = PathHasDBFiles(initDir);
+ if (parser.Has("--create") && dbFilesExist) {
+ std::cerr << "Trying to create database files in a directory where they "
+ "already exists. Delete the db files before creating new ones."
+ << std::endl;
+ return false;
+ }
+ if (!parser.Has("--create") && !dbFilesExist) {
+ std::cerr << "No db files found." << std::endl;
+ std::cerr << "Create them using 'nss db --create [--path /foo/bar]' before "
+ "continuing."
+ << std::endl;
+ return false;
+ }
+
+ // init NSS
+ const char *certPrefix = ""; // certutil -P option --- can leave this empty
+ SECStatus rv = NSS_Initialize(initDir.c_str(), certPrefix, certPrefix,
+ "secmod.db", readOnly ? NSS_INIT_READONLY : 0);
+ if (rv != SECSuccess) {
+ std::cerr << "NSS init failed!" << std::endl;
+ return false;
+ }
+
+ bool ret = true;
+ if (parser.Has("--list-certs")) {
+ ListCertificates();
+ } else if (parser.Has("--import-cert")) {
+ ret = ImportCertificate(parser);
+ } else if (parser.Has("--create")) {
+ ret = InitSlotPassword();
+ if (ret) {
+ std::cout << "DB files created successfully." << std::endl;
+ }
+ } else if (parser.Has("--list-keys")) {
+ ret = ListKeys();
+ } else if (parser.Has("--import-key")) {
+ ret = ImportKey(parser);
+ } else if (parser.Has("--delete-cert")) {
+ ret = DeleteCert(parser);
+ } else if (parser.Has("--delete-key")) {
+ ret = DeleteKey(parser);
+ } else if (parser.Has("--change-password")) {
+ ret = ChangeSlotPassword();
+ }
+
+ // shutdown nss
+ if (NSS_Shutdown() != SECSuccess) {
+ std::cerr << "NSS Shutdown failed!" << std::endl;
+ return false;
+ }
+
+ return ret;
+}
+
+bool DBTool::PathHasDBFiles(std::string path) {
+ std::regex certDBPattern("cert.*\\.db");
+ std::regex keyDBPattern("key.*\\.db");
+
+ PRDir *dir = PR_OpenDir(path.c_str());
+ if (!dir) {
+ std::cerr << "Directory " << path << " could not be accessed!" << std::endl;
+ return false;
+ }
+
+ PRDirEntry *ent;
+ bool dbFileExists = false;
+ while ((ent = PR_ReadDir(dir, PR_SKIP_BOTH))) {
+ if (std::regex_match(ent->name, certDBPattern) ||
+ std::regex_match(ent->name, keyDBPattern) ||
+ "secmod.db" == std::string(ent->name)) {
+ dbFileExists = true;
+ break;
+ }
+ }
+
+ (void)PR_CloseDir(dir);
+ return dbFileExists;
+}
+
+void DBTool::ListCertificates() {
+ ScopedCERTCertList list(PK11_ListCerts(PK11CertListAll, nullptr));
+ CERTCertListNode *node;
+
+ std::cout << std::setw(60) << std::left << "Certificate Nickname"
+ << " "
+ << "Trust Attributes" << std::endl;
+ std::cout << std::setw(60) << std::left << ""
+ << " "
+ << "SSL,S/MIME,JAR/XPI" << std::endl
+ << std::endl;
+
+ for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificate *cert = node->cert;
+
+ std::string name("(unknown)");
+ char *appData = static_cast<char *>(node->appData);
+ if (appData && strlen(appData) > 0) {
+ name = appData;
+ } else if (cert->nickname && strlen(cert->nickname) > 0) {
+ name = cert->nickname;
+ } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
+ name = cert->emailAddr;
+ }
+
+ CERTCertTrust trust;
+ std::string trusts;
+ if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
+ std::stringstream ss;
+ ss << PrintFlags(trust.sslFlags);
+ ss << ",";
+ ss << PrintFlags(trust.emailFlags);
+ ss << ",";
+ ss << PrintFlags(trust.objectSigningFlags);
+ trusts = ss.str();
+ } else {
+ trusts = ",,";
+ }
+ std::cout << std::setw(60) << std::left << name << " " << trusts
+ << std::endl;
+ }
+}
+
+bool DBTool::ImportCertificate(const ArgParser &parser) {
+ if (!parser.Has("--name")) {
+ std::cerr << "A name (--name) is required to import a certificate."
+ << std::endl;
+ Usage();
+ return false;
+ }
+
+ std::string derFilePath = parser.Get("--import-cert");
+ std::string certName = parser.Get("--name");
+ std::string trustString("TCu,Cu,Tu");
+ if (parser.Has("--trusts")) {
+ trustString = parser.Get("--trusts");
+ }
+
+ CERTCertTrust trust;
+ SECStatus rv = CERT_DecodeTrustString(&trust, trustString.c_str());
+ if (rv != SECSuccess) {
+ std::cerr << "Cannot decode trust string!" << std::endl;
+ return false;
+ }
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ std::vector<uint8_t> certData = ReadInputData(derFilePath);
+
+ ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(
+ reinterpret_cast<char *>(certData.data()), certData.size()));
+ if (cert.get() == nullptr) {
+ std::cerr << "Error: Could not decode certificate!" << std::endl;
+ return false;
+ }
+
+ rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
+ certName.c_str(), PR_FALSE);
+ if (rv != SECSuccess) {
+ // TODO handle authentication -> PK11_Authenticate (see certutil.c line
+ // 134)
+ std::cerr << "Error: Could not add certificate to database!" << std::endl;
+ return false;
+ }
+
+ rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(), &trust);
+ if (rv != SECSuccess) {
+ std::cerr << "Cannot change cert's trust" << std::endl;
+ return false;
+ }
+
+ std::cout << "Certificate import was successful!" << std::endl;
+ // TODO show information about imported certificate
+ return true;
+}
+
+bool DBTool::ListKeys() {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ if (!DBLoginIfNeeded(slot)) {
+ return false;
+ }
+
+ ScopedSECKEYPrivateKeyList list(PK11_ListPrivateKeysInSlot(slot.get()));
+ if (list.get() == nullptr) {
+ std::cerr << "Listing private keys failed with error "
+ << PR_ErrorToName(PR_GetError()) << std::endl;
+ return false;
+ }
+
+ SECKEYPrivateKeyListNode *node;
+ int count = 0;
+ for (node = PRIVKEY_LIST_HEAD(list.get());
+ !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
+ char *keyNameRaw = PK11_GetPrivateKeyNickname(node->key);
+ std::string keyName(keyNameRaw ? keyNameRaw : "");
+
+ if (keyName.empty()) {
+ ScopedCERTCertificate cert(PK11_GetCertFromPrivateKey(node->key));
+ if (cert.get()) {
+ if (cert->nickname && strlen(cert->nickname) > 0) {
+ keyName = cert->nickname;
+ } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
+ keyName = cert->emailAddr;
+ }
+ }
+ if (keyName.empty()) {
+ keyName = "(none)"; // default value
+ }
+ }
+
+ SECKEYPrivateKey *key = node->key;
+ ScopedSECItem keyIDItem(PK11_GetLowLevelKeyIDForPrivateKey(key));
+ if (keyIDItem.get() == nullptr) {
+ std::cerr << "Error: PK11_GetLowLevelKeyIDForPrivateKey failed!"
+ << std::endl;
+ continue;
+ }
+
+ std::string keyID = StringToHex(keyIDItem);
+
+ if (count++ == 0) {
+ // print header
+ std::cout << std::left << std::setw(20) << "<key#, key name>"
+ << std::setw(20) << "key type"
+ << "key id" << std::endl;
+ }
+
+ std::stringstream leftElem;
+ leftElem << "<" << count << ", " << keyName << ">";
+ std::cout << std::left << std::setw(20) << leftElem.str() << std::setw(20)
+ << keyTypeName[key->keyType] << keyID << std::endl;
+ }
+
+ if (count == 0) {
+ std::cout << "No keys found." << std::endl;
+ }
+
+ return true;
+}
+
+bool DBTool::ImportKey(const ArgParser &parser) {
+ std::string privKeyFilePath = parser.Get("--import-key");
+ std::string name;
+ if (parser.Has("--name")) {
+ name = parser.Get("--name");
+ }
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ if (!DBLoginIfNeeded(slot)) {
+ return false;
+ }
+
+ std::vector<uint8_t> privKeyData = ReadInputData(privKeyFilePath);
+ if (privKeyData.empty()) {
+ return false;
+ }
+ SECItem pkcs8PrivKeyItem = {
+ siBuffer, reinterpret_cast<unsigned char *>(privKeyData.data()),
+ static_cast<unsigned int>(privKeyData.size())};
+
+ SECItem nickname = {siBuffer, nullptr, 0};
+ if (!name.empty()) {
+ nickname.data = const_cast<unsigned char *>(
+ reinterpret_cast<const unsigned char *>(name.c_str()));
+ nickname.len = static_cast<unsigned int>(name.size());
+ }
+
+ SECStatus rv = PK11_ImportDERPrivateKeyInfo(
+ slot.get(), &pkcs8PrivKeyItem,
+ nickname.data == nullptr ? nullptr : &nickname, nullptr /*publicValue*/,
+ true /*isPerm*/, false /*isPrivate*/, KU_ALL, nullptr);
+ if (rv != SECSuccess) {
+ std::cerr << "Importing a private key in DER format failed with error "
+ << PR_ErrorToName(PR_GetError()) << std::endl;
+ return false;
+ }
+
+ std::cout << "Key import succeeded." << std::endl;
+ return true;
+}
+
+bool DBTool::DeleteCert(const ArgParser &parser) {
+ std::string certName = parser.Get("--delete-cert");
+ if (certName.empty()) {
+ std::cerr << "A name is required to delete a certificate." << std::endl;
+ Usage();
+ return false;
+ }
+
+ ScopedCERTCertificate cert(CERT_FindCertByNicknameOrEmailAddr(
+ CERT_GetDefaultCertDB(), certName.c_str()));
+ if (!cert) {
+ std::cerr << "Could not find certificate with name " << certName << "."
+ << std::endl;
+ return false;
+ }
+
+ SECStatus rv = SEC_DeletePermCertificate(cert.get());
+ if (rv != SECSuccess) {
+ std::cerr << "Unable to delete certificate with name " << certName << "."
+ << std::endl;
+ return false;
+ }
+
+ std::cout << "Certificate with name " << certName << " deleted successfully."
+ << std::endl;
+ return true;
+}
+
+bool DBTool::DeleteKey(const ArgParser &parser) {
+ std::string keyName = parser.Get("--delete-key");
+ if (keyName.empty()) {
+ std::cerr << "A name is required to delete a key." << std::endl;
+ Usage();
+ return false;
+ }
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ if (!DBLoginIfNeeded(slot)) {
+ return false;
+ }
+
+ ScopedSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
+ slot.get(), const_cast<char *>(keyName.c_str()), nullptr));
+ if (list.get() == nullptr) {
+ std::cerr << "Fetching private keys with nickname " << keyName
+ << " failed with error " << PR_ErrorToName(PR_GetError())
+ << std::endl;
+ return false;
+ }
+
+ unsigned int foundKeys = 0, deletedKeys = 0;
+ SECKEYPrivateKeyListNode *node;
+ for (node = PRIVKEY_LIST_HEAD(list.get());
+ !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
+ SECKEYPrivateKey *privKey = node->key;
+ foundKeys++;
+ // see PK11_DeleteTokenPrivateKey for example usage
+ // calling PK11_DeleteTokenPrivateKey directly does not work because it also
+ // destroys the SECKEYPrivateKey (by calling SECKEY_DestroyPrivateKey) -
+ // then SECKEY_DestroyPrivateKeyList does not
+ // work because it also calls SECKEY_DestroyPrivateKey
+ SECStatus rv =
+ PK11_DestroyTokenObject(privKey->pkcs11Slot, privKey->pkcs11ID);
+ if (rv == SECSuccess) {
+ deletedKeys++;
+ }
+ }
+
+ if (foundKeys > deletedKeys) {
+ std::cerr << "Some keys could not be deleted." << std::endl;
+ }
+
+ if (deletedKeys > 0) {
+ std::cout << "Found " << foundKeys << " keys." << std::endl;
+ std::cout << "Successfully deleted " << deletedKeys
+ << " key(s) with nickname " << keyName << "." << std::endl;
+ } else {
+ std::cout << "No key with nickname " << keyName << " found to delete."
+ << std::endl;
+ }
+
+ return true;
+}
diff --git a/security/nss/nss-tool/db/dbtool.h b/security/nss/nss-tool/db/dbtool.h
new file mode 100644
index 000000000..dd0ef0ace
--- /dev/null
+++ b/security/nss/nss-tool/db/dbtool.h
@@ -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/. */
+
+#ifndef dbtool_h__
+#define dbtool_h__
+
+#include <string>
+#include <vector>
+#include "argparse.h"
+#include "tool.h"
+
+class DBTool : public Tool {
+ public:
+ bool Run(const std::vector<std::string>& arguments) override;
+
+ private:
+ void Usage() override;
+ bool PathHasDBFiles(std::string path);
+ void ListCertificates();
+ bool ImportCertificate(const ArgParser& parser);
+ bool ListKeys();
+ bool ImportKey(const ArgParser& parser);
+ bool DeleteCert(const ArgParser& parser);
+ bool DeleteKey(const ArgParser& parser);
+};
+
+#endif // dbtool_h__
diff --git a/security/nss/nss-tool/digest/digesttool.cc b/security/nss/nss-tool/digest/digesttool.cc
new file mode 100644
index 000000000..08c3e3ba7
--- /dev/null
+++ b/security/nss/nss-tool/digest/digesttool.cc
@@ -0,0 +1,161 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "digesttool.h"
+#include "argparse.h"
+#include "scoped_ptrs.h"
+#include "util.h"
+
+#include <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+#include <hasht.h> // contains supported digest types
+#include <nss.h>
+#include <pk11pub.h>
+#include <prio.h>
+
+static SECOidData* HashTypeToOID(HASH_HashType hashtype) {
+ SECOidTag hashtag;
+
+ if (hashtype <= HASH_AlgNULL || hashtype >= HASH_AlgTOTAL) {
+ return nullptr;
+ }
+
+ switch (hashtype) {
+ case HASH_AlgMD5:
+ hashtag = SEC_OID_MD5;
+ break;
+ case HASH_AlgSHA1:
+ hashtag = SEC_OID_SHA1;
+ break;
+ case HASH_AlgSHA224:
+ hashtag = SEC_OID_SHA224;
+ break;
+ case HASH_AlgSHA256:
+ hashtag = SEC_OID_SHA256;
+ break;
+ case HASH_AlgSHA384:
+ hashtag = SEC_OID_SHA384;
+ break;
+ case HASH_AlgSHA512:
+ hashtag = SEC_OID_SHA512;
+ break;
+ default:
+ return nullptr;
+ }
+
+ return SECOID_FindOIDByTag(hashtag);
+}
+
+static SECOidData* HashNameToOID(const std::string& hashName) {
+ for (size_t htype = HASH_AlgNULL + 1; htype < HASH_AlgTOTAL; htype++) {
+ SECOidData* hashOID = HashTypeToOID(static_cast<HASH_HashType>(htype));
+ if (hashOID && std::string(hashOID->desc) == hashName) {
+ return hashOID;
+ }
+ }
+
+ return nullptr;
+}
+
+static bool Digest(const ArgParser& parser, SECOidData* hashOID);
+static bool ComputeDigest(std::istream& is, ScopedPK11Context& hashCtx);
+
+bool DigestTool::Run(const std::vector<std::string>& arguments) {
+ ArgParser parser(arguments);
+
+ if (parser.GetPositionalArgumentCount() != 1) {
+ Usage();
+ return false;
+ }
+
+ // no need for a db for the digest tool
+ SECStatus rv = NSS_NoDB_Init(".");
+ if (rv != SECSuccess) {
+ std::cerr << "NSS init failed!" << std::endl;
+ return false;
+ }
+
+ std::string hashName = parser.GetPositionalArgument(0);
+ std::transform(hashName.begin(), hashName.end(), hashName.begin(), ::toupper);
+ SECOidData* hashOID = HashNameToOID(hashName);
+ if (hashOID == nullptr) {
+ std::cerr << "Error: Unknown digest type "
+ << parser.GetPositionalArgument(0) << "." << std::endl;
+ return false;
+ }
+
+ bool ret = Digest(parser, hashOID);
+
+ // shutdown nss
+ if (NSS_Shutdown() != SECSuccess) {
+ std::cerr << "NSS Shutdown failed!" << std::endl;
+ return false;
+ }
+
+ return ret;
+}
+
+void DigestTool::Usage() {
+ std::cerr << "Usage: nss digest md5|sha-1|sha-224|sha-256|sha-384|sha-512 "
+ "[--infile <path>]"
+ << std::endl;
+}
+
+static bool Digest(const ArgParser& parser, SECOidData* hashOID) {
+ std::string inputFile;
+ if (parser.Has("--infile")) {
+ inputFile = parser.Get("--infile");
+ }
+
+ ScopedPK11Context hashCtx(PK11_CreateDigestContext(hashOID->offset));
+ if (hashCtx == nullptr) {
+ std::cerr << "Creating digest context failed." << std::endl;
+ return false;
+ }
+ PK11_DigestBegin(hashCtx.get());
+
+ std::ifstream fis;
+ std::istream& is = GetStreamFromFileOrStdin(inputFile, fis);
+ if (!is.good() || !ComputeDigest(is, hashCtx)) {
+ return false;
+ }
+
+ unsigned char digest[HASH_LENGTH_MAX];
+ unsigned int len;
+ SECStatus rv = PK11_DigestFinal(hashCtx.get(), digest, &len, HASH_LENGTH_MAX);
+ if (rv != SECSuccess || len == 0) {
+ std::cerr << "Calculating final hash value failed." << std::endl;
+ return false;
+ }
+
+ // human readable output
+ for (size_t i = 0; i < len; i++) {
+ std::cout << std::setw(2) << std::setfill('0') << std::hex
+ << static_cast<int>(digest[i]);
+ }
+ std::cout << std::endl;
+
+ return true;
+}
+
+static bool ComputeDigest(std::istream& is, ScopedPK11Context& hashCtx) {
+ while (is) {
+ unsigned char buf[4096];
+ is.read(reinterpret_cast<char*>(buf), sizeof(buf));
+ if (is.fail() && !is.eof()) {
+ std::cerr << "Error reading from input stream." << std::endl;
+ return false;
+ }
+ SECStatus rv = PK11_DigestOp(hashCtx.get(), buf, is.gcount());
+ if (rv != SECSuccess) {
+ std::cerr << "PK11_DigestOp failed." << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/security/nss/nss-tool/digest/digesttool.h b/security/nss/nss-tool/digest/digesttool.h
new file mode 100644
index 000000000..0e18346f5
--- /dev/null
+++ b/security/nss/nss-tool/digest/digesttool.h
@@ -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/. */
+
+#ifndef digest_tool_h__
+#define digest_tool_h__
+
+#include <string>
+#include <vector>
+#include "tool.h"
+
+class DigestTool : public Tool {
+ public:
+ bool Run(const std::vector<std::string>& arguments) override;
+
+ private:
+ void Usage() override;
+};
+
+#endif // digest_tool_h__
diff --git a/security/nss/nss-tool/enc/enctool.cc b/security/nss/nss-tool/enc/enctool.cc
new file mode 100644
index 000000000..b3c0d1dbe
--- /dev/null
+++ b/security/nss/nss-tool/enc/enctool.cc
@@ -0,0 +1,464 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "enctool.h"
+#include "argparse.h"
+#include "util.h"
+
+#include "nss.h"
+
+#include <assert.h>
+#include <chrono>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+void EncTool::PrintError(const std::string& m, size_t line_number) {
+ std::cerr << m << " - enctool.cc:" << line_number << std::endl;
+}
+
+void EncTool::PrintError(const std::string& m, PRErrorCode err,
+ size_t line_number) {
+ std::cerr << m << " (error " << err << ")"
+ << " - enctool.cc:" << line_number << std::endl;
+}
+
+void EncTool::PrintBytes(const std::vector<uint8_t>& bytes,
+ const std::string& txt) {
+ if (debug_) {
+ std::cerr << txt << ": ";
+ for (uint8_t b : bytes) {
+ std::cerr << std::setfill('0') << std::setw(2) << std::hex
+ << static_cast<int>(b);
+ }
+ std::cerr << std::endl << std::dec;
+ }
+}
+
+std::vector<uint8_t> EncTool::GenerateRandomness(size_t num_bytes) {
+ std::vector<uint8_t> bytes(num_bytes);
+ if (PK11_GenerateRandom(bytes.data(), num_bytes) != SECSuccess) {
+ PrintError("No randomness available. Abort!", __LINE__);
+ exit(1);
+ }
+ return bytes;
+}
+
+bool EncTool::WriteBytes(const std::vector<uint8_t>& bytes,
+ std::string out_file) {
+ std::fstream output(out_file, std::ios::out | std::ios::binary);
+ if (!output.good()) {
+ return false;
+ }
+ output.write(reinterpret_cast<const char*>(
+ const_cast<const unsigned char*>(bytes.data())),
+ bytes.size());
+ output.flush();
+ output.close();
+ return true;
+}
+
+bool EncTool::GetKey(const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& key_item) {
+ if (key_bytes.empty()) {
+ return false;
+ }
+
+ // Build key.
+ key_item =
+ ScopedSECItem(SECITEM_AllocItem(nullptr, nullptr, key_bytes.size()));
+ if (!key_item) {
+ return false;
+ }
+ key_item->type = siBuffer;
+ memcpy(key_item->data, key_bytes.data(), key_bytes.size());
+ key_item->len = key_bytes.size();
+
+ return true;
+}
+
+bool EncTool::GetAesGcmKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& aes_key, ScopedSECItem& params) {
+ if (iv_bytes.empty()) {
+ return false;
+ }
+
+ // GCM params.
+ CK_GCM_PARAMS* gcm_params =
+ static_cast<CK_GCM_PARAMS*>(PORT_Malloc(sizeof(struct CK_GCM_PARAMS)));
+ if (!gcm_params) {
+ return false;
+ }
+
+ uint8_t* iv = static_cast<uint8_t*>(PORT_Malloc(iv_bytes.size()));
+ if (!iv) {
+ return false;
+ }
+ memcpy(iv, iv_bytes.data(), iv_bytes.size());
+ gcm_params->pIv = iv;
+ gcm_params->ulIvLen = iv_bytes.size();
+ gcm_params->ulTagBits = 128;
+ if (aad.empty()) {
+ gcm_params->pAAD = nullptr;
+ gcm_params->ulAADLen = 0;
+ } else {
+ uint8_t* ad = static_cast<uint8_t*>(PORT_Malloc(aad.size()));
+ if (!ad) {
+ return false;
+ }
+ memcpy(ad, aad.data(), aad.size());
+ gcm_params->pAAD = ad;
+ gcm_params->ulAADLen = aad.size();
+ }
+
+ params =
+ ScopedSECItem(SECITEM_AllocItem(nullptr, nullptr, sizeof(*gcm_params)));
+ if (!params) {
+ return false;
+ }
+ params->len = sizeof(*gcm_params);
+ params->type = siBuffer;
+ params->data = reinterpret_cast<unsigned char*>(gcm_params);
+
+ return GetKey(key_bytes, aes_key);
+}
+
+bool EncTool::GenerateAesGcmKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& aes_key, ScopedSECItem& params) {
+ size_t key_size = 16, iv_size = 12;
+ std::vector<uint8_t> iv_bytes = GenerateRandomness(iv_size);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = GenerateRandomness(key_size);
+ PrintBytes(key_bytes, "key");
+ // Maybe write out the key and parameters.
+ if (write_key_ && !WriteBytes(key_bytes, key_file_)) {
+ return false;
+ }
+ if (write_iv_ && !WriteBytes(iv_bytes, iv_file_)) {
+ return false;
+ }
+ return GetAesGcmKey(aad, iv_bytes, key_bytes, aes_key, params);
+}
+
+bool EncTool::ReadAesGcmKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& aes_key, ScopedSECItem& params) {
+ std::vector<uint8_t> iv_bytes = ReadInputData(iv_file_);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = ReadInputData(key_file_);
+ PrintBytes(key_bytes, "key");
+ return GetAesGcmKey(aad, iv_bytes, key_bytes, aes_key, params);
+}
+
+bool EncTool::GetChachaKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& chacha_key, ScopedSECItem& params) {
+ if (iv_bytes.empty()) {
+ return false;
+ }
+
+ // AEAD params.
+ CK_NSS_AEAD_PARAMS* aead_params = static_cast<CK_NSS_AEAD_PARAMS*>(
+ PORT_Malloc(sizeof(struct CK_NSS_AEAD_PARAMS)));
+ if (!aead_params) {
+ return false;
+ }
+
+ uint8_t* iv = static_cast<uint8_t*>(PORT_Malloc(iv_bytes.size()));
+ if (!iv) {
+ return false;
+ }
+ memcpy(iv, iv_bytes.data(), iv_bytes.size());
+ aead_params->pNonce = iv;
+ aead_params->ulNonceLen = iv_bytes.size();
+ aead_params->ulTagLen = 16;
+ if (aad.empty()) {
+ aead_params->pAAD = nullptr;
+ aead_params->ulAADLen = 0;
+ } else {
+ uint8_t* ad = static_cast<uint8_t*>(PORT_Malloc(aad.size()));
+ if (!ad) {
+ return false;
+ }
+ memcpy(ad, aad.data(), aad.size());
+ aead_params->pAAD = ad;
+ aead_params->ulAADLen = aad.size();
+ }
+
+ params =
+ ScopedSECItem(SECITEM_AllocItem(nullptr, nullptr, sizeof(*aead_params)));
+ if (!params) {
+ return false;
+ }
+ params->len = sizeof(*aead_params);
+ params->type = siBuffer;
+ params->data = reinterpret_cast<unsigned char*>(aead_params);
+
+ return GetKey(key_bytes, chacha_key);
+}
+
+bool EncTool::GenerateChachaKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key,
+ ScopedSECItem& params) {
+ size_t key_size = 32, iv_size = 12;
+ std::vector<uint8_t> iv_bytes = GenerateRandomness(iv_size);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = GenerateRandomness(key_size);
+ PrintBytes(key_bytes, "key");
+ // Maybe write out the key and parameters.
+ if (write_key_ && !WriteBytes(key_bytes, key_file_)) {
+ return false;
+ }
+ if (write_iv_ && !WriteBytes(iv_bytes, iv_file_)) {
+ return false;
+ }
+ return GetChachaKey(aad, iv_bytes, key_bytes, chacha_key, params);
+}
+
+bool EncTool::ReadChachaKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key, ScopedSECItem& params) {
+ std::vector<uint8_t> iv_bytes = ReadInputData(iv_file_);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = ReadInputData(key_file_);
+ PrintBytes(key_bytes, "key");
+ return GetChachaKey(aad, iv_bytes, key_bytes, chacha_key, params);
+}
+
+bool EncTool::DoCipher(std::string file_name, std::string out_file,
+ bool encrypt, key_func_t get_params) {
+ SECStatus rv;
+ unsigned int outLen = 0, chunkSize = 1024;
+ char buffer[1040];
+ const unsigned char* bufferStart =
+ reinterpret_cast<const unsigned char*>(buffer);
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ PrintError("Unable to find security device", PR_GetError(), __LINE__);
+ return false;
+ }
+
+ ScopedSECItem key, params;
+ if (!(this->*get_params)(std::vector<uint8_t>(), key, params)) {
+ PrintError("Geting keys and params failed.", __LINE__);
+ return false;
+ }
+
+ ScopedPK11SymKey symKey(
+ PK11_ImportSymKey(slot.get(), cipher_mech_, PK11_OriginUnwrap,
+ CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
+ if (!symKey) {
+ PrintError("Failure to import key into NSS", PR_GetError(), __LINE__);
+ return false;
+ }
+
+ std::streambuf* buf;
+ std::ofstream output_file(out_file, std::ios::out | std::ios::binary);
+ if (!out_file.empty()) {
+ if (!output_file.good()) {
+ return false;
+ }
+ buf = output_file.rdbuf();
+ } else {
+ buf = std::cout.rdbuf();
+ }
+ std::ostream output(buf);
+
+ // Read from stdin.
+ if (file_name.empty()) {
+ std::vector<uint8_t> data = ReadInputData("");
+ std::vector<uint8_t> out(data.size() + 16);
+ SECStatus rv;
+ if (encrypt) {
+ rv = PK11_Encrypt(symKey.get(), cipher_mech_, params.get(), out.data(),
+ &outLen, data.size() + 16, data.data(), data.size());
+ } else {
+ rv = PK11_Decrypt(symKey.get(), cipher_mech_, params.get(), out.data(),
+ &outLen, data.size() + 16, data.data(), data.size());
+ }
+ if (rv != SECSuccess) {
+ PrintError(encrypt ? "Error encrypting" : "Error decrypting",
+ PR_GetError(), __LINE__);
+ return false;
+ };
+ output.write(reinterpret_cast<char*>(out.data()), outLen);
+ output.flush();
+ if (output_file.good()) {
+ output_file.close();
+ } else {
+ output << std::endl;
+ }
+
+ std::cerr << "Done " << (encrypt ? "encrypting" : "decrypting")
+ << std::endl;
+ return true;
+ }
+
+ // Read file from file_name.
+ std::ifstream input(file_name, std::ios::binary);
+ if (!input.good()) {
+ return false;
+ }
+ uint8_t out[1040];
+ while (input) {
+ if (encrypt) {
+ input.read(buffer, chunkSize);
+ rv = PK11_Encrypt(symKey.get(), cipher_mech_, params.get(), out, &outLen,
+ chunkSize + 16, bufferStart, input.gcount());
+ } else {
+ // We have to read the tag when decrypting.
+ input.read(buffer, chunkSize + 16);
+ rv = PK11_Decrypt(symKey.get(), cipher_mech_, params.get(), out, &outLen,
+ chunkSize + 16, bufferStart, input.gcount());
+ }
+ if (rv != SECSuccess) {
+ PrintError(encrypt ? "Error encrypting" : "Error decrypting",
+ PR_GetError(), __LINE__);
+ return false;
+ };
+ output.write(reinterpret_cast<const char*>(out), outLen);
+ output.flush();
+ }
+ if (output_file.good()) {
+ output_file.close();
+ } else {
+ output << std::endl;
+ }
+ std::cerr << "Done " << (encrypt ? "encrypting" : "decrypting") << std::endl;
+
+ return true;
+}
+
+size_t EncTool::PrintFileSize(std::string file_name) {
+ std::ifstream input(file_name, std::ifstream::ate | std::ifstream::binary);
+ auto size = input.tellg();
+ std::cerr << "Size of file to encrypt: " << size / 1024 / 1024 << " MB"
+ << std::endl;
+ return size;
+}
+
+bool EncTool::IsValidCommand(ArgParser arguments) {
+ // Either encrypt or decrypt is fine.
+ bool valid = arguments.Has("--encrypt") != arguments.Has("--decrypt");
+ // An input file is required for decryption only.
+ valid &= arguments.Has("--in") || arguments.Has("--encrypt");
+ // An output file is required for encryption only.
+ valid &= arguments.Has("--out") || arguments.Has("--decrypt");
+ // Files holding the IV and key are required for decryption.
+ valid &= arguments.Has("--iv") || arguments.Has("--encrypt");
+ valid &= arguments.Has("--key") || arguments.Has("--encrypt");
+ // Cipher is always required.
+ valid &= arguments.Has("--cipher");
+ return valid;
+}
+
+bool EncTool::Run(const std::vector<std::string>& arguments) {
+ ArgParser parser(arguments);
+
+ if (!IsValidCommand(parser)) {
+ Usage();
+ return false;
+ }
+
+ if (NSS_NoDB_Init(nullptr) != SECSuccess) {
+ PrintError("NSS initialization failed", PR_GetError(), __LINE__);
+ return false;
+ }
+
+ if (parser.Has("--debug")) {
+ debug_ = 1;
+ }
+ if (parser.Has("--iv")) {
+ iv_file_ = parser.Get("--iv");
+ } else {
+ write_iv_ = false;
+ }
+ if (parser.Has("--key")) {
+ key_file_ = parser.Get("--key");
+ } else {
+ write_key_ = false;
+ }
+
+ key_func_t get_params;
+ bool encrypt = parser.Has("--encrypt");
+ if (parser.Get("--cipher") == kAESCommand) {
+ cipher_mech_ = CKM_AES_GCM;
+ if (encrypt) {
+ get_params = &EncTool::GenerateAesGcmKey;
+ } else {
+ get_params = &EncTool::ReadAesGcmKey;
+ }
+ } else if (parser.Get("--cipher") == kChaChaCommand) {
+ cipher_mech_ = CKM_NSS_CHACHA20_POLY1305;
+ if (encrypt) {
+ get_params = &EncTool::GenerateChachaKey;
+ } else {
+ get_params = &EncTool::ReadChachaKey;
+ }
+ } else {
+ Usage();
+ return false;
+ }
+ // Don't write out key and iv when decrypting.
+ if (!encrypt) {
+ write_key_ = false;
+ write_iv_ = false;
+ }
+
+ std::string input_file = parser.Has("--in") ? parser.Get("--in") : "";
+ std::string output_file = parser.Has("--out") ? parser.Get("--out") : "";
+ size_t file_size = 0;
+ if (!input_file.empty()) {
+ file_size = PrintFileSize(input_file);
+ }
+ auto begin = std::chrono::high_resolution_clock::now();
+ if (!DoCipher(input_file, output_file, encrypt, get_params)) {
+ (void)NSS_Shutdown();
+ return false;
+ }
+ auto end = std::chrono::high_resolution_clock::now();
+ auto ns =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
+ auto seconds = ns / 1000000000;
+ std::cerr << ns << " ns (~" << seconds << " s) and " << std::endl;
+ std::cerr << "That's approximately " << (double)file_size / ns << " b/ns"
+ << std::endl;
+
+ if (NSS_Shutdown() != SECSuccess) {
+ return false;
+ }
+
+ return true;
+}
+
+void EncTool::Usage() {
+ std::string const txt = R"~(
+Usage: nss encrypt|decrypt --cipher aes|chacha [--in <file>] [--out <file>]
+ [--key <file>] [--iv <file>]
+
+ --cipher Set the cipher to use.
+ --cipher aes: Use AES-GCM to encrypt/decrypt.
+ --cipher chacha: Use ChaCha20/Poly1305 to encrypt/decrypt.
+ --in The file to encrypt/decrypt. If no file is given, we read
+ from stdin (only when encrypting).
+ --out The file to write the ciphertext/plaintext to. If no file
+ is given we write the plaintext to stdout (only when
+ decrypting).
+ --key The file to write the used key to/to read the key
+ from. Optional parameter. When not given, don't write out
+ the key.
+ --iv The file to write the used IV to/to read the IV
+ from. Optional parameter. When not given, don't write out
+ the IV.
+
+ Examples:
+ nss encrypt --cipher aes --iv iv --key key --out ciphertext
+ nss decrypt --cipher chacha --iv iv --key key --in ciphertex
+
+ Note: This tool overrides files without asking.
+)~";
+ std::cerr << txt << std::endl;
+}
diff --git a/security/nss/nss-tool/enc/enctool.h b/security/nss/nss-tool/enc/enctool.h
new file mode 100644
index 000000000..5a6a5a164
--- /dev/null
+++ b/security/nss/nss-tool/enc/enctool.h
@@ -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/. */
+
+#ifndef enctool_h__
+#define enctool_h__
+
+#include <string>
+#include <vector>
+#include "argparse.h"
+#include "prerror.h"
+#include "scoped_ptrs.h"
+#include "tool.h"
+
+class EncTool : public Tool {
+ public:
+ bool Run(const std::vector<std::string>& arguments) override;
+ void Usage() override;
+
+ private:
+ typedef bool (EncTool::*key_func_t)(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key,
+ ScopedSECItem& params);
+ void PrintBytes(const std::vector<uint8_t>& bytes, const std::string& txt);
+ bool WriteBytes(const std::vector<uint8_t>& bytes, std::string out_file);
+ void PrintError(const std::string& m, PRErrorCode err, size_t line_number);
+ void PrintError(const std::string& m, size_t line_number);
+ bool GetKey(const std::vector<uint8_t>& key_bytes, ScopedSECItem& key_item);
+ bool GetAesGcmKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& aes_key, ScopedSECItem& params);
+ bool GetChachaKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& chacha_key, ScopedSECItem& params);
+ bool GenerateAesGcmKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& aes_key, ScopedSECItem& params);
+ bool ReadAesGcmKey(const std::vector<uint8_t>& aad, ScopedSECItem& aes_key,
+ ScopedSECItem& params);
+ std::vector<uint8_t> GenerateRandomness(size_t num_bytes);
+ bool GenerateChachaKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key, ScopedSECItem& params);
+ bool ReadChachaKey(const std::vector<uint8_t>& aad, ScopedSECItem& chacha_key,
+ ScopedSECItem& params);
+ bool DoCipher(std::string fileName, std::string outFile, bool encrypt,
+ key_func_t get_params);
+ size_t PrintFileSize(std::string fileName);
+ bool IsValidCommand(ArgParser arguments);
+
+ bool debug_ = false;
+ bool write_key_ = true;
+ bool write_iv_ = true;
+ std::string key_file_ = "/tmp/key";
+ std::string iv_file_ = "/tmp/iv";
+ CK_MECHANISM_TYPE cipher_mech_;
+
+ const std::string kAESCommand = "aes";
+ const std::string kChaChaCommand = "chacha";
+};
+
+#endif // enctool_h__
diff --git a/security/nss/nss-tool/nss_tool.cc b/security/nss/nss-tool/nss_tool.cc
new file mode 100644
index 000000000..8864f140d
--- /dev/null
+++ b/security/nss/nss-tool/nss_tool.cc
@@ -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/. */
+
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <prinit.h>
+
+#include "argparse.h"
+#include "db/dbtool.h"
+#include "digest/digesttool.h"
+#include "enc/enctool.h"
+#include "tool.h"
+
+static void Usage() {
+ std::cerr << "Usage: nss <command> <subcommand> [options]" << std::endl;
+ std::cerr << " nss db [--path <directory>] <commands>" << std::endl;
+ std::cerr << " nss encrypt <options>" << std::endl;
+ std::cerr << " nss decrypt <options>" << std::endl;
+ std::cerr << " nss digest <options>" << std::endl;
+}
+
+static const std::string kDbCommand = "db";
+static const std::string kEncryptCommand = "encrypt";
+static const std::string kDecryptCommand = "decrypt";
+static const std::string kDigestCommand = "digest";
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ Usage();
+ return 1;
+ }
+ std::vector<std::string> arguments(argv + 2, argv + argc);
+
+ std::unique_ptr<Tool> tool = nullptr;
+ if (argv[1] == kDbCommand) {
+ tool = std::unique_ptr<Tool>(new DBTool());
+ }
+ if (argv[1] == kEncryptCommand) {
+ tool = std::unique_ptr<Tool>(new EncTool());
+ arguments.push_back("--encrypt");
+ }
+ if (argv[1] == kDecryptCommand) {
+ tool = std::unique_ptr<Tool>(new EncTool());
+ arguments.push_back("--decrypt");
+ }
+ if (argv[1] == kDigestCommand) {
+ tool = std::unique_ptr<Tool>(new DigestTool());
+ }
+ if (!tool) {
+ Usage();
+ return 1;
+ }
+
+ int exit_code = 0;
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+ if (!tool->Run(arguments)) {
+ exit_code = 1;
+ }
+
+ PR_Cleanup();
+
+ return exit_code;
+}
diff --git a/security/nss/nss-tool/nss_tool.gyp b/security/nss/nss-tool/nss_tool.gyp
new file mode 100644
index 000000000..a5d03fcf9
--- /dev/null
+++ b/security/nss/nss-tool/nss_tool.gyp
@@ -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/.
+{
+ 'includes' : [
+ '../coreconf/config.gypi',
+ '../cmd/platlibs.gypi',
+ ],
+ 'targets' : [
+ {
+ 'target_name' : 'nss',
+ 'type' : 'executable',
+ 'sources' : [
+ 'nss_tool.cc',
+ 'common/argparse.cc',
+ 'common/util.cc',
+ 'db/dbtool.cc',
+ 'enc/enctool.cc',
+ 'digest/digesttool.cc'
+ ],
+ 'include_dirs': [
+ 'common',
+ ],
+ 'dependencies' : [
+ '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
+ '<(DEPTH)/exports.gyp:dbm_exports',
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ],
+ }
+ ],
+}
diff --git a/security/nss/nss.gyp b/security/nss/nss.gyp
index e62d28449..9e9b49f7f 100644
--- a/security/nss/nss.gyp
+++ b/security/nss/nss.gyp
@@ -131,7 +131,6 @@
'cmd/digest/digest.gyp:digest',
'cmd/ecperf/ecperf.gyp:ecperf',
'cmd/fbectest/fbectest.gyp:fbectest',
- 'cmd/fipstest/fipstest.gyp:fipstest',
'cmd/httpserv/httpserv.gyp:httpserv',
'cmd/listsuites/listsuites.gyp:listsuites',
'cmd/makepqg/makepqg.gyp:makepqg',
@@ -149,6 +148,7 @@
'cmd/pk1sign/pk1sign.gyp:pk1sign',
'cmd/pp/pp.gyp:pp',
'cmd/rsaperf/rsaperf.gyp:rsaperf',
+ 'cmd/rsapoptst/rsapoptst.gyp:rsapoptst',
'cmd/sdrtest/sdrtest.gyp:sdrtest',
'cmd/selfserv/selfserv.gyp:selfserv',
'cmd/shlibsign/mangle/mangle.gyp:mangle',
@@ -164,10 +164,13 @@
'cmd/vfychain/vfychain.gyp:vfychain',
'cmd/vfyserv/vfyserv.gyp:vfyserv',
'gtests/certhigh_gtest/certhigh_gtest.gyp:certhigh_gtest',
+ 'gtests/cryptohi_gtest/cryptohi_gtest.gyp:cryptohi_gtest',
'gtests/der_gtest/der_gtest.gyp:der_gtest',
'gtests/certdb_gtest/certdb_gtest.gyp:certdb_gtest',
'gtests/freebl_gtest/freebl_gtest.gyp:prng_gtest',
+ 'gtests/freebl_gtest/freebl_gtest.gyp:blake2b_gtest',
'gtests/pk11_gtest/pk11_gtest.gyp:pk11_gtest',
+ 'gtests/softoken_gtest/softoken_gtest.gyp:softoken_gtest',
'gtests/ssl_gtest/ssl_gtest.gyp:ssl_gtest',
'gtests/util_gtest/util_gtest.gyp:util_gtest',
'gtests/nss_bogo_shim/nss_bogo_shim.gyp:nss_bogo_shim',
@@ -189,6 +192,11 @@
'gtests/freebl_gtest/freebl_gtest.gyp:freebl_gtest',
],
}],
+ [ 'disable_fips==0', {
+ 'dependencies': [
+ 'cmd/fipstest/fipstest.gyp:fipstest',
+ ],
+ }],
],
},
],
diff --git a/security/nss/readme.md b/security/nss/readme.md
index b75bfe7dd..17b99e805 100644
--- a/security/nss/readme.md
+++ b/security/nss/readme.md
@@ -41,49 +41,8 @@ directory `lib`, and tools in directory `bin`. In order to run the tools, set
your system environment to use the libraries of your build from the "lib"
directory, e.g., using the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH`.
- Usage: build.sh [-hcv] [-j <n>] [--nspr] [--gyp|-g] [--opt|-o] [-m32]
- [--test] [--pprof] [--scan-build[=output]] [--ct-verif]
- [--asan] [--ubsan] [--msan] [--sancov[=edge|bb|func|...]]
- [--disable-tests] [--fuzz[=tls|oss]] [--system-sqlite]
- [--no-zdefs] [--with-nspr] [--system-nspr] [--enable-libpkix]
-
- This script builds NSS with gyp and ninja.
-
- This build system is still under development. It does not yet support all
- the features or platforms that NSS supports.
-
- NSS build tool options:
-
- -h display this help and exit
- -c clean before build
- -v verbose build
- -j <n> run at most <n> concurrent jobs
- --nspr force a rebuild of NSPR
- --gyp|-g force a rerun of gyp
- --opt|-o do an opt build
- -m32 do a 32-bit build on a 64-bit system
- --test ignore map files and export everything we have
- --fuzz build fuzzing targets (this always enables test builds)
- --fuzz=tls to enable TLS fuzzing mode
- --fuzz=oss to build for OSS-Fuzz
- --pprof build with gperftool support
- --ct-verif build with valgrind for ct-verif
- --scan-build run the build with scan-build (scan-build has to be in the path)
- --scan-build=/out/path sets the output path for scan-build
- --asan do an asan build
- --ubsan do an ubsan build
- --ubsan=bool,shift,... sets specific UB sanitizers
- --msan do an msan build
- --sancov do sanitize coverage builds
- --sancov=func sets coverage to function level for example
- --disable-tests don't build tests and corresponding cmdline utils
- --system-sqlite use system sqlite
- --no-zdefs don't set -Wl,-z,defs
- --with-nspr don't build NSPR but use the one at the given location, e.g.
- --with-nspr=/path/to/nspr/include:/path/to/nspr/lib
- --system-nspr use system nspr. This requires an installation of NSPR and
- might not work on all systems.
- --enable-libpkix make libpkix part of the build.
+See [help.txt](https://hg.mozilla.org/projects/nss/raw-file/tip/help.txt) for
+more information on using build.sh.
## Building NSS (legacy build system)
@@ -122,10 +81,6 @@ set or export:
Note that you might have to add `nss.local` to `/etc/hosts` if it's not
there. The entry should look something like `127.0.0.1 nss.local nss`.
-If you get name resolution errors, try to ensure that you are using an IPv4
-address; IPv6 is the default on many systems for the loopback device which
-doesn't work.
-
### Running tests
**Runnning all tests will take a while!**
@@ -182,3 +137,50 @@ The nss directory contains the following important subdirectories:
A more comprehensible overview of the NSS folder structure and API guidelines
can be found
[here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines).
+
+## Build mechanisms related to FIPS compliance
+
+NSS supports build configurations for FIPS-140 compliance, and alternative build
+configurations that disable functionality specific to FIPS-140 compliance.
+
+This section documents the environment variables and build parameters that
+control these configurations.
+
+### Build FIPS startup tests
+
+The C macro NSS_NO_INIT_SUPPORT controls the FIPS startup self tests.
+If NSS_NO_INIT_SUPPORT is defined, the startup tests are disabled.
+
+The legacy build system (make) by default disables these tests.
+To enable these tests, set environment variable NSS_FORCE_FIPS=1 at build time.
+
+The gyp build system by default disables these tests.
+To enable these tests, pass parameter --enable-fips to build.sh.
+
+### Building either FIPS compliant or alternative compliant code
+
+The C macro NSS_FIPS_DISABLED can be used to disable some FIPS compliant code
+and enable alternative implementations.
+
+The legacy build system (make) never defines NSS_FIPS_DISABLED and always uses
+the FIPS compliant code.
+
+The gyp build system by default defines NSS_FIPS_DISABLED.
+To use the FIPS compliant code, pass parameter --enable-fips to build.sh.
+
+### Test execution
+
+The NSS test suite may contain tests that are included, excluded, or are
+different based on the FIPS build configuration. To execute the correct tests,
+it's necessary to determine which build configuration was used.
+
+The legacy build system (make) uses environment variables to control all
+aspects of the build configuration, including FIPS build configuration.
+
+Because the gyp build system doesn't use environment variables to control the
+build configuration, the NSS tests cannot rely on environment variables to
+determine the build configuration.
+
+A helper binary named nss-build-flags is produced as part of the NSS build,
+which prints the C macro symbols that were defined at build time, and which are
+relevant to test execution.
diff --git a/security/nss/tests/all.sh b/security/nss/tests/all.sh
index 833817f4a..8d5bd2dbb 100755
--- a/security/nss/tests/all.sh
+++ b/security/nss/tests/all.sh
@@ -63,10 +63,6 @@
# BUILT_OPT - use optimized/debug build
# USE_64 - use 64bit/32bit build
#
-# Optional environment variables to enable specific NSS features:
-# ---------------------------------------------------------------
-# NSS_DISABLE_ECC - disable ECC
-#
# Optional environment variables to select which cycles/suites to test:
# ---------------------------------------------------------------------
# NSS_CYCLES - list of cycles to run (separated by space
@@ -107,12 +103,16 @@
#
########################################################################
+RUN_FIPS=""
+
############################## run_tests ###############################
# run test suites defined in TESTS variable, skip scripts defined in
# TESTS_SKIP variable
########################################################################
run_tests()
{
+ echo "Running test cycle: ${TEST_MODE} ----------------------"
+ echo "List of tests that will be executed: ${TESTS}"
for TEST in ${TESTS}
do
# NOTE: the spaces are important. If you don't include
@@ -132,14 +132,20 @@ run_tests()
}
########################## run_cycle_standard ##########################
-# run test suites with defaults settings (no PKIX, no sharedb)
+# run test suites with dbm database (no PKIX, no sharedb)
########################################################################
run_cycle_standard()
{
TEST_MODE=STANDARD
TESTS="${ALL_TESTS}"
- TESTS_SKIP=
+ TESTS_SKIP="cipher libpkix sdr ocsp pkits"
+
+ NSS_DEFAULT_DB_TYPE="dbm"
+ export NSS_DEFAULT_DB_TYPE
+
+ NSS_SSL_TESTS=`echo "${NSS_SSL_TESTS}" | sed -e "s/normal//g" -e "s/fips//g" -e "s/_//g"`
+ NSS_SSL_RUN=`echo "${NSS_SSL_RUN}" | sed -e "s/cov//g" -e "s/auth//g"`
run_tests
}
@@ -164,7 +170,13 @@ run_cycle_pkix()
TESTS="${ALL_TESTS}"
TESTS_SKIP="cipher dbtests sdr crmf smime merge multinit"
+
NSS_SSL_TESTS=`echo "${NSS_SSL_TESTS}" | sed -e "s/normal//g" -e "s/fips//g" -e "s/_//g"`
+ export -n NSS_SSL_RUN
+
+ # use the default format. (unset for the shell, export -n for binaries)
+ export -n NSS_DEFAULT_DB_TYPE
+ unset NSS_DEFAULT_DB_TYPE
run_tests
}
@@ -187,7 +199,7 @@ run_cycle_upgrade_db()
init_directories
if [ -r "${OLDHOSTDIR}/cert.log" ]; then
- DIRS="alicedir bobdir CA cert_extensions client clientCA dave eccurves eve ext_client ext_server fips SDR server serverCA stapling tools/copydir cert.log cert.done tests.*"
+ DIRS="alicedir bobdir CA cert_extensions client clientCA dave eccurves eve ext_client ext_server $RUN_FIPS SDR server serverCA stapling tools/copydir cert.log cert.done tests.*"
for i in $DIRS
do
cp -r ${OLDHOSTDIR}/${i} ${HOSTDIR} #2> /dev/null
@@ -233,10 +245,10 @@ run_cycle_shared_db()
# run the tests for native sharedb support
TESTS="${ALL_TESTS}"
- TESTS_SKIP="cipher libpkix dbupgrade sdr ocsp pkits"
+ TESTS_SKIP="dbupgrade"
- NSS_SSL_TESTS=`echo "${NSS_SSL_TESTS}" | sed -e "s/normal//g" -e "s/fips//g" -e "s/_//g"`
- NSS_SSL_RUN=`echo "${NSS_SSL_RUN}" | sed -e "s/cov//g" -e "s/auth//g"`
+ export -n NSS_SSL_TESTS
+ export -n NSS_SSL_RUN
run_tests
}
@@ -270,10 +282,25 @@ run_cycles()
############################## main code ###############################
+SCRIPTNAME=all.sh
+CLEANUP="${SCRIPTNAME}"
+cd `dirname $0`
+
+# all.sh should be the first one to try to source the init
+if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
+ cd common
+ . ./init.sh
+fi
+
cycles="standard pkix upgradedb sharedb"
CYCLES=${NSS_CYCLES:-$cycles}
-tests="cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits ec gtests ssl_gtests"
+NO_INIT_SUPPORT=`certutil --build-flags |grep -cw NSS_NO_INIT_SUPPORT`
+if [ $NO_INIT_SUPPORT -eq 0 ]; then
+ RUN_FIPS="fips"
+fi
+
+tests="cipher lowhash libpkix cert dbtests tools $RUN_FIPS sdr crmf smime ssl ocsp merge pkits ec gtests ssl_gtests"
# Don't run chains tests when we have a gyp build.
if [ "$OBJDIR" != "Debug" -a "$OBJDIR" != "Release" ]; then
tests="$tests chains"
@@ -282,22 +309,15 @@ TESTS=${NSS_TESTS:-$tests}
ALL_TESTS=${TESTS}
-nss_ssl_tests="crl fips_normal normal_fips iopr policy"
+nss_ssl_tests="crl iopr policy"
+if [ $NO_INIT_SUPPORT -eq 0 ]; then
+ nss_ssl_tests="$nss_ssl_tests fips_normal normal_fips"
+fi
NSS_SSL_TESTS="${NSS_SSL_TESTS:-$nss_ssl_tests}"
nss_ssl_run="cov auth stapling stress"
NSS_SSL_RUN="${NSS_SSL_RUN:-$nss_ssl_run}"
-SCRIPTNAME=all.sh
-CLEANUP="${SCRIPTNAME}"
-cd `dirname $0`
-
-# all.sh should be the first one to try to source the init
-if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
- cd common
- . ./init.sh
-fi
-
# NOTE:
# Lists of enabled tests and other settings are stored to ${ENV_BACKUP}
# file and are are restored after every test cycle.
diff --git a/security/nss/tests/cert/TestCA-bogus-rsa-pss1.crt b/security/nss/tests/cert/TestCA-bogus-rsa-pss1.crt
new file mode 100644
index 000000000..e3c8fcdcf
--- /dev/null
+++ b/security/nss/tests/cert/TestCA-bogus-rsa-pss1.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEbDCCAxqgAwIBAgIBATBHBgkqhkiG9w0BAQowOqAPMA0GCWCGSAFlAwQCAQUA
+oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASCjBAICEmcwgYMxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFp
+biBWaWV3MRIwEAYDVQQKEwlCT0dVUyBOU1MxMzAxBgNVBAMTKk5TUyBUZXN0IENB
+IChSU0EtUFNTIGludmFsaWQgdHJhaWxlckZpZWxkKTAgFw0xNzEyMDcxMjU3NDBa
+GA8yMDY3MTIwNzEyNTc0MFowgYMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxp
+Zm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRIwEAYDVQQKEwlCT0dVUyBO
+U1MxMzAxBgNVBAMTKk5TUyBUZXN0IENBIChSU0EtUFNTIGludmFsaWQgdHJhaWxl
+ckZpZWxkKTCCAVwwRwYJKoZIhvcNAQEKMDqgDzANBglghkgBZQMEAgEFAKEcMBoG
+CSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgowQCAhJnA4IBDwAwggEKAoIB
+AQDgkKJk+PoFpESak7kMQ0w147/xilUZCG7hDGG2uuGTbX8jqy9N9pxzB9sJjgJX
+yYND0XEmrUQ2Memmy8jufhXML5DekW1tr3Gi2L3VivbIReJZfXk1xDMvNbB/Gjjo
+SoPyu8C4hnevjgMlmqG3KdMkB+eN6PnBG64YFyki3vnLO5iTNHEBTgFYo0gTX4uK
+xl0hLtiDL+4K5l7BwVgxZwQF6uHoHjrjjlhkzR0FwjjqR8U0pH20Pb6IlRsFMv07
+/1GHf+jm34pKb/1ZNzAbiKxYv7YAQUWEZ7e/GSXgA6gbTpV9ueiLkVucUeXN/mXK
+Tqb4zivi5FaSGVl8SJnqsJXJAgMBAAGjOTA3MBQGCWCGSAGG+EIBAQEB/wQEAwIC
+BDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwICBDBHBgkqhkiG9w0BAQow
+OqAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUA
+ogMCASCjBAICEmcDggEBAJht9t9p/dlhJtx7ShDvUXyq8N4tCoGKdREM83K/jlW8
+HxdHOz5PuvZx+UMlaUtqZVIriSCnRtEWkoSo0hWmcv1rp80it2G1zLfLPYdyrPba
+nQmE1iFb69Wr9dwrX7o/CII+WHQgoIGeFGntZ8YRZTe5+JeiGAlAyZCqUKbl9lhh
+pCpf1YYxb3VI8mAGVi0jwabWBEbInGBZYH9HP0nK7/Tflk6UY3f4h4Fbkk5D4WZA
+hFfkebx6Wh90QGiKQhp4/N+dYira8bKvWqqn0VqwzBoJBU/RmMaJVpwqFFvcaUJh
+uEKUPeQbqkYvj1WJYmy4ettVwi4OZU50+kCaRQhMsFA=
+-----END CERTIFICATE-----
diff --git a/security/nss/tests/cert/TestCA-bogus-rsa-pss2.crt b/security/nss/tests/cert/TestCA-bogus-rsa-pss2.crt
new file mode 100644
index 000000000..d46442dc4
--- /dev/null
+++ b/security/nss/tests/cert/TestCA-bogus-rsa-pss2.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEFzCCAs2gAwIBAgIBATA/BgkqhkiG9w0BAQowMqAOMAwGCCqGSIb3DQIFBQCh
+GzAZBgkqhkiG9w0BAQgwDAYIKoZIhvcNAgUFAKIDAgEgMH4xCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRIw
+EAYDVQQKEwlCT0dVUyBOU1MxLjAsBgNVBAMTJU5TUyBUZXN0IENBIChSU0EtUFNT
+IGludmFsaWQgaGFzaEFsZykwIBcNMTcxMjA3MTQwNjQ0WhgPMjA2ODAxMDcxNDA2
+NDRaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
+Ew1Nb3VudGFpbiBWaWV3MRIwEAYDVQQKEwlCT0dVUyBOU1MxLjAsBgNVBAMTJU5T
+UyBUZXN0IENBIChSU0EtUFNTIGludmFsaWQgaGFzaEFsZykwggEgMAsGCSqGSIb3
+DQEBCgOCAQ8AMIIBCgKCAQEAtDXA73yTOgs8zVYNMCtuQ9a07UgbfeQbjHp3pkF6
+7rsC/Q28mrLh+zLkht5e7qU/Qf/8a2ZkcYhPOBAjCzjgIXOdE2lsWvdVujOJLR0x
+Fesd3hDLRmL6f6momc+j1/Tw3bKyZinaeJ9BFRv9c94SayB3QUe+6+TNJKASwlhj
+sx6mUsND+h3DkuL77gi7hIUpUXfFSwa+zM69VLhIu+/WRZfG8gfKkCAIGUC3WYJa
+eU1HgQKfVSXW0ok4ototXWEe9ohU+Z1tO9LJStcY8mMpig7EU9zbpObhG46Sykfu
+aKsubB9J+gFgwP5Tb85tRYT6SbHeHR6U/N8GBrKdRcomWwIDAQABozwwOjAUBglg
+hkgBhvhCAQEBAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
+BAMCAgQwPwYJKoZIhvcNAQEKMDKgDjAMBggqhkiG9w0CBQUAoRswGQYJKoZIhvcN
+AQEIMAwGCCqGSIb3DQIFBQCiAwIBIAOCAQEAjeemeTxh2xrMUJ6Z5Yn2nH2FbcPY
+fTHJcdfXjfNBkrMl5pe2/lk0JyNuACTuTYFCxdWNRL1coN//h9DSUbF3dpF1ex6D
+difo+6PwxkO2aPVGPYw4DSivt4SFbn5dKGgVqBQfnmNK7p/iT91AcErg/grRrNL+
+4jeT0UiRjQYeX9xKJArv+ocIidNpQL3QYxXuBLZxVC92Af69ol7WG8QBRLnFi1p2
+g6q8hOHqOfB29qnsSo3PkI1yuShOl50tRLbNgyotEfZdk1N3oXvapoBsm/jlcdCT
+0aKelCSQYYAfyl5PKCpa1lgBm7zfcHSDStMhEEFu/fbnJhqO9g9znj3STQ==
+-----END CERTIFICATE-----
diff --git a/security/nss/tests/cert/cert.sh b/security/nss/tests/cert/cert.sh
index 9b3455747..d1a9148a9 100755
--- a/security/nss/tests/cert/cert.sh
+++ b/security/nss/tests/cert/cert.sh
@@ -46,11 +46,7 @@ cert_init()
fi
SCRIPTNAME="cert.sh"
CRL_GRP_DATE=`date -u "+%Y%m%d%H%M%SZ"`
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- html_head "Certutil and Crlutil Tests with ECC"
- else
- html_head "Certutil and Crlutil Tests"
- fi
+ html_head "Certutil and Crlutil Tests"
LIBDIR="${DIST}/${OBJDIR}/lib"
@@ -300,14 +296,12 @@ cert_create_cert()
fi
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Import EC Root CA for $CERTNAME"
certu -A -n "TestCA-ec" -t "TC,TC,TC" -f "${R_PWFILE}" \
-d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ec.ca.cert" 2>&1
if [ "$RET" -ne 0 ]; then
return $RET
fi
- fi
cert_add_cert "$5"
return $?
@@ -402,7 +396,6 @@ cert_add_cert()
#
# Generate and add EC cert
#
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CURVE="secp384r1"
CU_ACTION="Generate EC Cert Request for $CERTNAME"
CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}-ec@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
@@ -454,7 +447,6 @@ cert_add_cert()
return $RET
fi
cert_log "SUCCESS: $CERTNAME's mixed EC Cert Created"
- fi
return 0
}
@@ -467,6 +459,7 @@ cert_add_cert()
cert_all_CA()
{
echo nss > ${PWFILE}
+ echo > ${EMPTY_FILE}
ALL_CU_SUBJECT="CN=NSS Test CA, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
cert_CA $CADIR TestCA -x "CTu,CTu,CTu" ${D_CA} "1"
@@ -517,10 +510,16 @@ cert_all_CA()
# dsaroot.cert in $CLIENT_CADIR and in $SERVER_CADIR is one of the last
# in the chain
+#
+# Create RSA-PSS version of TestCA
+ ALL_CU_SUBJECT="CN=NSS Test CA (RSA-PSS), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ cert_rsa_pss_CA $CADIR TestCA-rsa-pss -x "CTu,CTu,CTu" ${D_CA} "1" SHA256
+ rm $CADIR/rsapssroot.cert
+ ALL_CU_SUBJECT="CN=NSS Test CA (RSA-PSS-SHA1), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ cert_rsa_pss_CA $CADIR TestCA-rsa-pss-sha1 -x "CTu,CTu,CTu" ${D_CA} "1" SHA1
+ rm $CADIR/rsapssroot.cert
-
- if [ -z "$NSS_DISABLE_ECC" ] ; then
#
# Create EC version of TestCA
CA_CURVE="secp521r1"
@@ -545,8 +544,6 @@ cert_all_CA()
rm $CLIENT_CADIR/ecroot.cert $SERVER_CADIR/ecroot.cert
# ecroot.cert in $CLIENT_CADIR and in $SERVER_CADIR is one of the last
# in the chain
-
- fi
}
################################# cert_CA ################################
@@ -637,7 +634,7 @@ CERTSCRIPT
################################ cert_dsa_CA #############################
# local shell function to build the Temp. Certificate Authority (CA)
# used for testing purposes, creating a CA Certificate and a root cert
-# This is the ECC version of cert_CA.
+# This is the DSA version of cert_CA.
##########################################################################
cert_dsa_CA()
{
@@ -648,7 +645,7 @@ cert_dsa_CA()
DOMAIN=$5
CERTSERIAL=$6
- echo "$SCRIPTNAME: Creating an DSA CA Certificate $NICKNAME =========================="
+ echo "$SCRIPTNAME: Creating a DSA CA Certificate $NICKNAME =========================="
if [ ! -d "${CUR_CADIR}" ]; then
mkdir -p "${CUR_CADIR}"
@@ -661,7 +658,7 @@ cert_dsa_CA()
LPROFILE="multiaccess:${DOMAIN}"
fi
- ################# Creating an DSA CA Cert ###############################
+ ################# Creating a DSA CA Cert ###############################
#
CU_ACTION="Creating DSA CA Cert $NICKNAME "
CU_SUBJECT=$ALL_CU_SUBJECT
@@ -700,6 +697,79 @@ CERTSCRIPT
+
+################################ cert_rsa_pss_CA #############################
+# local shell function to build the Temp. Certificate Authority (CA)
+# used for testing purposes, creating a CA Certificate and a root cert
+# This is the RSA-PSS version of cert_CA.
+##########################################################################
+cert_rsa_pss_CA()
+{
+ CUR_CADIR=$1
+ NICKNAME=$2
+ SIGNER=$3
+ TRUSTARG=$4
+ DOMAIN=$5
+ CERTSERIAL=$6
+ HASHALG=$7
+
+ echo "$SCRIPTNAME: Creating an RSA-PSS CA Certificate $NICKNAME =========================="
+
+ if [ ! -d "${CUR_CADIR}" ]; then
+ mkdir -p "${CUR_CADIR}"
+ fi
+ cd ${CUR_CADIR}
+ pwd
+
+ LPROFILE=.
+ if [ -n "${MULTIACCESS_DBM}" ]; then
+ LPROFILE="multiaccess:${DOMAIN}"
+ fi
+
+ HASHOPT=
+ if [ -n "$HASHALG" ]; then
+ HASHOPT="-Z $HASHALG"
+ fi
+
+ ################# Creating an RSA-PSS CA Cert ###############################
+ #
+ CU_ACTION="Creating RSA-PSS CA Cert $NICKNAME "
+ CU_SUBJECT=$ALL_CU_SUBJECT
+ certu -S -n $NICKNAME -k rsa --pss $HASHOPT -t $TRUSTARG -v 600 $SIGNER \
+ -d ${LPROFILE} -1 -2 -5 -f ${R_PWFILE} -z ${R_NOISE_FILE} \
+ -m $CERTSERIAL 2>&1 <<CERTSCRIPT
+5
+6
+9
+n
+y
+-1
+n
+5
+6
+7
+9
+n
+CERTSCRIPT
+
+ if [ "$RET" -ne 0 ]; then
+ echo "return value is $RET"
+ Exit 6 "Fatal - failed to create RSA-PSS CA cert"
+ fi
+
+ ################# Exporting RSA-PSS Root Cert ###############################
+ #
+ CU_ACTION="Exporting RSA-PSS Root Cert"
+ certu -L -n $NICKNAME -r -d ${LPROFILE} -o rsapssroot.cert
+ if [ "$RET" -ne 0 ]; then
+ Exit 7 "Fatal - failed to export RSA-PSS root cert"
+ fi
+ cp rsapssroot.cert ${NICKNAME}.ca.cert
+}
+
+
+
+
################################ cert_ec_CA ##############################
# local shell function to build the Temp. Certificate Authority (CA)
# used for testing purposes, creating a CA Certificate and a root cert
@@ -831,7 +901,6 @@ cert_smime_client()
certu -E -t ",," -d ${P_R_BOBDIR} -f ${R_PWFILE} \
-i ${R_EVEDIR}/Eve.cert 2>&1
- if [ -z "$NSS_DISABLE_ECC" ] ; then
echo "$SCRIPTNAME: Importing EC Certificates =============================="
CU_ACTION="Import Bob's EC cert into Alice's db"
certu -E -t ",," -d ${P_R_ALICEDIR} -f ${R_PWFILE} \
@@ -855,7 +924,6 @@ cert_smime_client()
# CU_ACTION="Import Eve's EC cert into Bob's DB"
# certu -E -t ",," -d ${P_R_BOBDIR} -f ${R_PWFILE} \
# -i ${R_EVEDIR}/Eve-ec.cert 2>&1
- fi
if [ "$CERTFAILED" != 0 ] ; then
cert_log "ERROR: SMIME failed $RET"
@@ -946,7 +1014,6 @@ cert_extended_ssl()
# -d "${PROFILEDIR}" -i "${CLIENT_CADIR}/clientCA-dsamixed.ca.cert" \
# 2>&1
- if [ -z "$NSS_DISABLE_ECC" ] ; then
#
# Repeat the above for EC certs
#
@@ -992,7 +1059,6 @@ cert_extended_ssl()
# certu -A -n "clientCA-ecmixed" -t "T,," -f "${R_PWFILE}" \
# -d "${PROFILEDIR}" -i "${CLIENT_CADIR}/clientCA-ecmixed.ca.cert" \
# 2>&1
- fi
echo "Importing all the server's own CA chain into the servers DB"
for CA in `find ${SERVER_CADIR} -name "?*.ca.cert"` ;
@@ -1081,7 +1147,6 @@ cert_extended_ssl()
# done with mixed DSA certs
#
- if [ -z "$NSS_DISABLE_ECC" ] ; then
#
# Repeat the above for EC certs
#
@@ -1128,7 +1193,6 @@ cert_extended_ssl()
#
# done with mixed EC certs
#
- fi
echo "Importing all the client's own CA chain into the servers DB"
for CA in `find ${CLIENT_CADIR} -name "?*.ca.cert"` ;
@@ -1175,10 +1239,8 @@ cert_ssl()
CU_ACTION="Modify trust attributes of DSA Root CA -t TC,TC,TC"
certu -M -n "TestCA-dsa" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- CU_ACTION="Modify trust attributes of EC Root CA -t TC,TC,TC"
- certu -M -n "TestCA-ec" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
- fi
+ CU_ACTION="Modify trust attributes of EC Root CA -t TC,TC,TC"
+ certu -M -n "TestCA-ec" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
# cert_init_cert ${SERVERDIR} "${HOSTADDR}" 1 ${D_SERVER}
# echo "************* Copying CA files to ${SERVERDIR}"
# cp ${CADIR}/*.db .
@@ -1199,6 +1261,12 @@ cert_ssl()
cp -r ${R_SERVERDIR} ${R_STAPLINGDIR}
pk12u -o ${R_STAPLINGDIR}/ca.p12 -n TestCA -k ${R_PWFILE} -w ${R_PWFILE} -d ${R_CADIR}
pk12u -i ${R_STAPLINGDIR}/ca.p12 -k ${R_PWFILE} -w ${R_PWFILE} -d ${R_STAPLINGDIR}
+
+ echo "$SCRIPTNAME: Creating database for strsclnt no login tests ==============="
+ echo "cp -r ${CLIENTDIR} ${NOLOGINDIR}"
+ cp -r ${R_CLIENTDIR} ${R_NOLOGINDIR}
+ # change the password to empty
+ certu -W -d "${R_NOLOGINDIR}" -f "${R_PWFILE}" -@ "${R_EMPTY_FILE}" 2>&1
}
############################## cert_stresscerts ################################
@@ -1269,12 +1337,35 @@ MODSCRIPT
html_passed "${CU_ACTION}"
fi
+ CU_ACTION="Setting invalid database password in FIPS mode"
+ RETEXPECTED=255
+ certu -W -d "${PROFILEDIR}" -f "${R_FIPSPWFILE}" -@ "${R_FIPSBADPWFILE}" 2>&1
+ CU_ACTION="Attempt to generate a key with exponent of 3 (too small)"
+ certu -G -k rsa -g 2048 -y 3 -d "${PROFILEDIR}" -z ${R_NOISE_FILE} -f "${R_FIPSPWFILE}"
+ CU_ACTION="Attempt to generate a key with exponent of 17 (too small)"
+ certu -G -k rsa -g 2048 -y 17 -d "${PROFILEDIR}" -z ${R_NOISE_FILE} -f "${R_FIPSPWFILE}"
+ RETEXPECTED=0
+
CU_ACTION="Generate Certificate for ${CERTNAME}"
CU_SUBJECT="CN=${CERTNAME}, E=fips@bogus.com, O=BOGUS NSS, OU=FIPS PUB 140, L=Mountain View, ST=California, C=US"
certu -S -n ${FIPSCERTNICK} -x -t "Cu,Cu,Cu" -d "${PROFILEDIR}" -f "${R_FIPSPWFILE}" -k dsa -v 600 -m 500 -z "${R_NOISE_FILE}" 2>&1
if [ "$RET" -eq 0 ]; then
cert_log "SUCCESS: FIPS passed"
fi
+
+}
+
+########################## cert_rsa_exponent #################################
+# local shell function to verify small rsa exponent can be used (only
+# run if FIPS has not been turned on in the build).
+##############################################################################
+cert_rsa_exponent_nonfips()
+{
+ echo "$SCRIPTNAME: Verify that small RSA exponents still work =============="
+ CU_ACTION="Attempt to generate a key with exponent of 3"
+ certu -G -k rsa -g 2048 -y 3 -d "${CLIENTDIR}" -z ${R_NOISE_FILE} -f "${R_PWFILE}"
+ CU_ACTION="Attempt to generate a key with exponent of 17"
+ certu -G -k rsa -g 2048 -y 17 -d "${CLIENTDIR}" -z ${R_NOISE_FILE} -f "${R_PWFILE}"
}
############################## cert_eccurves ###########################
@@ -1284,7 +1375,6 @@ cert_eccurves()
{
################# Creating Certs for EC curves test ########################
#
- if [ -z "$NSS_DISABLE_ECC" ] ; then
echo "$SCRIPTNAME: Creating Server CA Issued Certificate for "
echo " EC Curves Test Certificates ------------------------------------"
@@ -1325,8 +1415,6 @@ cert_eccurves()
-f "${R_PWFILE}" -i "${CERTNAME}-ec.cert" 2>&1
fi
done
-
- fi # $NSS_DISABLE_ECC
}
########################### cert_extensions_test #############################
@@ -1678,7 +1766,6 @@ EOF_CRLINI
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Generating CRL (ECC) for range ${CRL_GRP_1_BEGIN}-${CRL_GRP_END} TestCA-ec authority"
# Until Bug 292285 is resolved, do not encode x400 Addresses. After
@@ -1693,7 +1780,6 @@ addext issuerAltNames 0 "rfc822Name:ca-ecemail@ca.com|dnsName:ca-ec.com|director
EOF_CRLINI
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
chmod 600 ${CRL_FILE_GRP_1}_or-ec
- fi
echo test > file
############################# Modification ##################################
@@ -1724,7 +1810,6 @@ EOF_CRLINI
TEMPFILES="$TEMPFILES ${CRL_FILE_GRP_1}_or-dsa"
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Modify CRL (ECC) by adding one more cert"
crlu -d $CADIR -M -n "TestCA-ec" -f ${R_PWFILE} \
-o ${CRL_FILE_GRP_1}_or1-ec -i ${CRL_FILE_GRP_1}_or-ec <<EOF_CRLINI
@@ -1734,7 +1819,6 @@ EOF_CRLINI
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
chmod 600 ${CRL_FILE_GRP_1}_or1-ec
TEMPFILES="$TEMPFILES ${CRL_FILE_GRP_1}_or-ec"
- fi
########### Removing one cert ${UNREVOKED_CERT_GRP_1} #######################
echo "$SCRIPTNAME: Modifying CA CRL by removing one cert ==============="
@@ -1763,7 +1847,6 @@ EOF_CRLINI
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Modify CRL (ECC) by removing one cert"
crlu -d $CADIR -M -n "TestCA-ec" -f ${R_PWFILE} -o ${CRL_FILE_GRP_1}-ec \
-i ${CRL_FILE_GRP_1}_or1-ec <<EOF_CRLINI
@@ -1772,7 +1855,6 @@ rmcert ${UNREVOKED_CERT_GRP_1}
EOF_CRLINI
chmod 600 ${CRL_FILE_GRP_1}-ec
TEMPFILES="$TEMPFILES ${CRL_FILE_GRP_1}_or1-ec"
- fi
########### Creating second CRL which includes groups 1 and 2 ##############
CRL_GRP_END=`expr ${CRL_GRP_2_BEGIN} + ${CRL_GRP_2_RANGE} - 1`
@@ -1792,7 +1874,6 @@ rmcert ${UNREVOKED_CERT_GRP_2}
EOF_CRLINI
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
chmod 600 ${CRL_FILE_GRP_2}
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Creating CRL (ECC) for groups 1 and 2"
crlu -d $CADIR -M -n "TestCA-ec" -f ${R_PWFILE} -o ${CRL_FILE_GRP_2}-ec \
-i ${CRL_FILE_GRP_1}-ec <<EOF_CRLINI
@@ -1803,7 +1884,6 @@ rmcert ${UNREVOKED_CERT_GRP_2}
EOF_CRLINI
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
chmod 600 ${CRL_FILE_GRP_2}-ec
- fi
########### Creating second CRL which includes groups 1, 2 and 3 ##############
CRL_GRP_END=`expr ${CRL_GRP_3_BEGIN} + ${CRL_GRP_3_RANGE} - 1`
@@ -1825,7 +1905,6 @@ addext crlNumber 0 2
EOF_CRLINI
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
chmod 600 ${CRL_FILE_GRP_3}
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Creating CRL (ECC) for groups 1, 2 and 3"
crlu -d $CADIR -M -n "TestCA-ec" -f ${R_PWFILE} -o ${CRL_FILE_GRP_3}-ec \
-i ${CRL_FILE_GRP_2}-ec <<EOF_CRLINI
@@ -1836,7 +1915,6 @@ addext crlNumber 0 2
EOF_CRLINI
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
chmod 600 ${CRL_FILE_GRP_3}-ec
- fi
############ Importing Server CA Issued CRL for certs of first group #######
@@ -1845,13 +1923,11 @@ EOF_CRLINI
crlu -D -n TestCA -f "${R_PWFILE}" -d "${R_SERVERDIR}"
crlu -I -i ${CRL_FILE} -n "TestCA" -f "${R_PWFILE}" -d "${R_SERVERDIR}"
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
- if [ -z "$NSS_DISABLE_ECC" ] ; then
CU_ACTION="Importing CRL (ECC) for groups 1"
crlu -D -n TestCA-ec -f "${R_PWFILE}" -d "${R_SERVERDIR}"
crlu -I -i ${CRL_FILE}-ec -n "TestCA-ec" -f "${R_PWFILE}" \
-d "${R_SERVERDIR}"
CRL_GEN_RES=`expr $? + $CRL_GEN_RES`
- fi
if [ "$CERTFAILED" != 0 -o "$CRL_GEN_RES" != 0 ] ; then
cert_log "ERROR: SSL CRL prep failed $CERTFAILED : $CRL_GEN_RES"
@@ -1968,6 +2044,387 @@ cert_test_ocspresp()
ocspr ${SERVER_CADIR} "serverCA" "chain-1-serverCA" -f "${R_PWFILE}" 2>&1
}
+cert_test_implicit_db_init()
+{
+ echo "$SCRIPTNAME: test implicit database init"
+
+ CU_ACTION="Add cert with trust flags to db with implicit init"
+ mkdir ${IMPLICIT_INIT_DIR}
+ certu -A -n ca -t 'C,C,C' -d ${P_R_IMPLICIT_INIT_DIR} -i "${SERVER_CADIR}/serverCA.ca.cert"
+}
+
+check_sign_algo()
+{
+ certu -L -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" | \
+ sed -n '/^ *Data:/,/^$/{
+/^ Signature Algorithm/,/^ *Salt length/s/^ //p
+}' > ${TMP}/signalgo.txt
+
+ diff ${TMP}/signalgo.exp ${TMP}/signalgo.txt
+ RET=$?
+ if [ "$RET" -ne 0 ]; then
+ CERTFAILED=$RET
+ html_failed "${CU_ACTION} ($RET) "
+ cert_log "ERROR: ${CU_ACTION} failed $RET"
+ else
+ html_passed "${CU_ACTION}"
+ fi
+}
+
+cert_test_rsapss()
+{
+ TEMPFILES="$TEMPFILES ${TMP}/signalgo.exp ${TMP}/signalgo.txt"
+
+ cert_init_cert "${RSAPSSDIR}" "RSA-PSS Test Cert" 1000 "${D_RSAPSS}"
+
+ CU_ACTION="Initialize Cert DB"
+ certu -N -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1
+
+ CU_ACTION="Import RSA CA Cert"
+ certu -A -n "TestCA" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${R_CADIR}/TestCA.ca.cert" 2>&1
+
+ CU_ACTION="Import RSA-PSS CA Cert"
+ certu -A -n "TestCA-rsa-pss" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${R_CADIR}/TestCA-rsa-pss.ca.cert" 2>&1
+
+ CU_ACTION="Verify RSA-PSS CA Cert"
+ certu -V -u L -e -n "TestCA-rsa-pss" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+
+ CU_ACTION="Import RSA-PSS CA Cert (SHA1)"
+ certu -A -n "TestCA-rsa-pss-sha1" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${R_CADIR}/TestCA-rsa-pss-sha1.ca.cert" 2>&1
+
+ CU_ACTION="Import Bogus RSA-PSS CA Cert (invalid trailerField)"
+ certu -A -n "TestCA-bogus-rsa-pss1" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${QADIR}/cert/TestCA-bogus-rsa-pss1.crt" 2>&1
+ RETEXPECTED=255
+ certu -V -b 1712101010Z -n TestCA-bogus-rsa-pss1 -u L -e -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1
+ RETEXPECTED=0
+
+ CU_ACTION="Import Bogus RSA-PSS CA Cert (invalid hashAlg)"
+ certu -A -n "TestCA-bogus-rsa-pss2" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${QADIR}/cert/TestCA-bogus-rsa-pss2.crt" 2>&1
+ RETEXPECTED=255
+ certu -V -b 1712101010Z -n TestCA-bogus-rsa-pss2 -u L -e -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1
+ RETEXPECTED=0
+
+ CERTSERIAL=200
+
+ # Subject certificate: RSA
+ # Issuer certificate: RSA
+ # Signature: RSA-PSS (explicit, with --pss-sign)
+ CERTNAME="TestUser-rsa-pss1"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA" --pss-sign -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA
+ # Issuer certificate: RSA
+ # Signature: RSA-PSS (explict, with --pss-sign -Z SHA512)
+ CERTNAME="TestUser-rsa-pss2"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA" --pss-sign -Z SHA512 -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-512
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-512
+ Salt length: 64 (0x40)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS
+ CERTNAME="TestUser-rsa-pss3"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA-rsa-pss" -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA
+ # Signature: RSA-PSS (explicit, with --pss-sign)
+ CERTNAME="TestUser-rsa-pss4"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA" --pss-sign -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (explicit, with --pss-sign)
+ CERTNAME="TestUser-rsa-pss5"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA-rsa-pss" --pss-sign -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (implicit, without --pss-sign)
+ CERTNAME="TestUser-rsa-pss6"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ # Sign without --pss-sign nor -Z option
+ certu -C -c "TestCA-rsa-pss" -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (with conflicting hash algorithm)
+ CERTNAME="TestUser-rsa-pss7"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ RETEXPECTED=255
+ certu -C -c "TestCA-rsa-pss" --pss-sign -Z SHA512 -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+ RETEXPECTED=0
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (with compatible hash algorithm)
+ CERTNAME="TestUser-rsa-pss8"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA-rsa-pss" --pss-sign -Z SHA256 -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA
+ # Issuer certificate: RSA
+ # Signature: RSA-PSS (explict, with --pss-sign -Z SHA1)
+ CERTNAME="TestUser-rsa-pss9"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ certu -C -c "TestCA" --pss-sign -Z SHA1 -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: default, SHA-1
+ Mask algorithm: default, MGF1
+ Mask hash algorithm: default, SHA-1
+ Salt length: default, 20 (0x14)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (implicit, without --pss-sign, default parameters)
+ CERTNAME="TestUser-rsa-pss10"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ # Sign without --pss-sign nor -Z option
+ certu -C -c "TestCA-rsa-pss-sha1" -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+ CU_ACTION="Import $CERTNAME's Cert"
+ certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -i "${CERTNAME}.cert" 2>&1
+
+ CU_ACTION="Verify $CERTNAME's Cert"
+ certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+ cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: default, SHA-1
+ Mask algorithm: default, MGF1
+ Mask hash algorithm: default, SHA-1
+ Salt length: default, 20 (0x14)
+EOF
+ check_sign_algo
+
+ CERTSERIAL=`expr $CERTSERIAL + 1`
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (with conflicting hash algorithm, default parameters)
+ CERTNAME="TestUser-rsa-pss11"
+
+ CU_ACTION="Generate Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1
+
+ CU_ACTION="Sign ${CERTNAME}'s Request"
+ RETEXPECTED=255
+ certu -C -c "TestCA-rsa-pss-sha1" --pss-sign -Z SHA256 -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+ RETEXPECTED=0
+}
+
############################## cert_cleanup ############################
# local shell function to finish this script (no exit since it might be
# sourced)
@@ -1975,20 +2432,24 @@ cert_test_ocspresp()
cert_cleanup()
{
cert_log "$SCRIPTNAME: finished $SCRIPTNAME"
- html "</TABLE><BR>"
+ html "</TABLE><BR>"
cd ${QADIR}
. common/cleanup.sh
}
################## main #################################################
-cert_init
+cert_init
cert_all_CA
-cert_extended_ssl
-cert_ssl
-cert_smime_client
-if [ -z "$NSS_TEST_DISABLE_FIPS" ]; then
- cert_fips
+cert_test_implicit_db_init
+cert_extended_ssl
+cert_ssl
+cert_smime_client
+IS_FIPS_DISABLED=`certutil --build-flags |grep -cw NSS_FIPS_DISABLED`
+if [ $IS_FIPS_DISABLED -ne 0 ]; then
+ cert_rsa_exponent_nonfips
+else
+ cert_fips
fi
cert_eccurves
cert_extensions
@@ -1996,6 +2457,7 @@ cert_san_and_generic_extensions
cert_test_password
cert_test_distrust
cert_test_ocspresp
+cert_test_rsapss
if [ -z "$NSS_TEST_DISABLE_CRL" ] ; then
cert_crl_ssl
@@ -2004,7 +2466,7 @@ else
fi
if [ -n "$DO_DIST_ST" -a "$DO_DIST_ST" = "TRUE" ] ; then
- cert_stresscerts
+ cert_stresscerts
fi
cert_iopr_setup
diff --git a/security/nss/tests/cipher/cipher.sh b/security/nss/tests/cipher/cipher.sh
index 1d2561d9c..11a621815 100755
--- a/security/nss/tests/cipher/cipher.sh
+++ b/security/nss/tests/cipher/cipher.sh
@@ -107,6 +107,21 @@ cipher_gcm()
done < ${GCM_TXT}
}
+###################### cipher_rsa_populate ############################
+# Test the ability to reconstruct rsa private key reconstruction
+# also test the PK11GenericObject interface
+###################################################################
+cipher_rsa_populate()
+{
+ TESTNAME="RSA Reconstruct Private Keys Test"
+ echo "$SCRIPTNAME: $TESTNAME --------------------------------"
+ echo "rsapoptst -t all -r 10"
+# skip e_d_q. It isn't reliable, and can return incorrect data. e_d_q should
+# be turned off.
+ ${PROFTOOL} ${BINDIR}/rsapoptst -t e_n_p,d_n_q,d_p_q,e_d_n -r 10
+ html_msg $? 0 "$TESTNAME"
+}
+
############################## cipher_cleanup ############################
# local shell function to finish this script (no exit since it might be
# sourced)
@@ -136,5 +151,6 @@ fi
# Skip cipher_gcm if this is a softoken only build.
if [ "${NSS_BUILD_SOFTOKEN_ONLY}" != "1" ]; then
cipher_gcm
+ cipher_rsa_populate
fi
cipher_cleanup
diff --git a/security/nss/tests/common/init.sh b/security/nss/tests/common/init.sh
index caf3013e6..933551e83 100644
--- a/security/nss/tests/common/init.sh
+++ b/security/nss/tests/common/init.sh
@@ -63,11 +63,13 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
DBPASSDIR=${HOSTDIR}/dbpass
ECCURVES_DIR=${HOSTDIR}/eccurves
DISTRUSTDIR=${HOSTDIR}/distrust
+ RSAPSSDIR=${HOSTDIR}/rsapss
SERVER_CADIR=${HOSTDIR}/serverCA
CLIENT_CADIR=${HOSTDIR}/clientCA
EXT_SERVERDIR=${HOSTDIR}/ext_server
EXT_CLIENTDIR=${HOSTDIR}/ext_client
+ IMPLICIT_INIT_DIR=${HOSTDIR}/implicit_init
IOPR_CADIR=${HOSTDIR}/CA_iopr
IOPR_SSL_SERVERDIR=${HOSTDIR}/server_ssl_iopr
@@ -76,10 +78,12 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
CERT_EXTENSIONS_DIR=${HOSTDIR}/cert_extensions
STAPLINGDIR=${HOSTDIR}/stapling
+ NOLOGINDIR=${HOSTDIR}/nologin
SSLGTESTDIR=${HOSTDIR}/ssl_gtests
GTESTDIR=${HOSTDIR}/gtests
PWFILE=${HOSTDIR}/tests.pw
+ EMPTY_FILE=${HOSTDIR}/tests_empty
NOISE_FILE=${HOSTDIR}/tests_noise
CORELIST_FILE=${HOSTDIR}/clist
@@ -528,13 +532,16 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
D_CLIENT_CA="ClientCA.$version"
D_SERVER="Server.$version"
D_CLIENT="Client.$version"
+ D_NOLOGIN="NoLogin.$version"
D_FIPS="FIPS.$version"
D_DBPASS="DBPASS.$version"
D_ECCURVES="ECCURVES.$version"
D_EXT_SERVER="ExtendedServer.$version"
D_EXT_CLIENT="ExtendedClient.$version"
+ D_IMPLICIT_INIT="ImplicitInit.$version"
D_CERT_EXTENSTIONS="CertExtensions.$version"
D_DISTRUST="Distrust.$version"
+ D_RSAPSS="RSAPSS.$version"
# we need relative pathnames of these files abd directories, since our
# tools can't handle the unix style absolut pathnames on cygnus
@@ -552,8 +559,10 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
R_EVEDIR=../eve
R_EXT_SERVERDIR=../ext_server
R_EXT_CLIENTDIR=../ext_client
+ R_IMPLICIT_INIT_DIR=../implicit_init
R_CERT_EXT=../cert_extensions
R_STAPLINGDIR=../stapling
+ R_NOLOGINDIR=../nologin
R_SSLGTESTDIR=../ssl_gtests
R_GTESTDIR=../gtests
@@ -568,8 +577,10 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
P_R_EVEDIR=${R_EVEDIR}
P_R_SERVERDIR=${R_SERVERDIR}
P_R_CLIENTDIR=${R_CLIENTDIR}
+ P_R_NOLOGINDIR=${R_NOLOGINDIR}
P_R_EXT_SERVERDIR=${R_EXT_SERVERDIR}
P_R_EXT_CLIENTDIR=${R_EXT_CLIENTDIR}
+ P_R_IMPLICIT_INIT_DIR=${R_IMPLICIT_INIT_DIR}
if [ -n "${MULTIACCESS_DBM}" ]; then
P_R_CADIR="multiaccess:${D_CA}"
P_R_ALICEDIR="multiaccess:${D_ALICE}"
@@ -578,11 +589,14 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
P_R_EVEDIR="multiaccess:${D_EVE}"
P_R_SERVERDIR="multiaccess:${D_SERVER}"
P_R_CLIENTDIR="multiaccess:${D_CLIENT}"
+ P_R_NOLOGINDIR="multiaccess:${D_NOLOGIN}"
P_R_EXT_SERVERDIR="multiaccess:${D_EXT_SERVER}"
P_R_EXT_CLIENTDIR="multiaccess:${D_EXT_CLIENT}"
+ P_R_IMPLICIT_INIT_DIR="multiaccess:${D_IMPLICIT_INIT}"
fi
R_PWFILE=../tests.pw
+ R_EMPTY_FILE=../tests_empty
R_NOISE_FILE=../tests_noise
R_FIPSPWFILE=../tests.fipspw
diff --git a/security/nss/tests/gtests/gtests.sh b/security/nss/tests/gtests/gtests.sh
index c785241c4..6606b59e7 100755
--- a/security/nss/tests/gtests/gtests.sh
+++ b/security/nss/tests/gtests/gtests.sh
@@ -83,7 +83,7 @@ gtest_cleanup()
}
################## main #################################################
-GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest"
+GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest blake2b_gtest"
SOURCE_DIR="$PWD"/../..
gtest_init $0
gtest_start
diff --git a/security/nss/tests/merge/merge.sh b/security/nss/tests/merge/merge.sh
index 1929b12c8..d17a8c4ef 100755
--- a/security/nss/tests/merge/merge.sh
+++ b/security/nss/tests/merge/merge.sh
@@ -98,7 +98,7 @@ merge_init()
# are dbm databases.
if [ "${TEST_MODE}" = "UPGRADE_DB" ]; then
save=${NSS_DEFAULT_DB_TYPE}
- NSS_DEFAULT_DB_TYPE= ; export NSS_DEFAULT_DB_TYPE
+ NSS_DEFAULT_DB_TYPE=dbm ; export NSS_DEFAULT_DB_TYPE
fi
certutil -N -d ${CONFLICT1DIR} -f ${R_PWFILE}
diff --git a/security/nss/tests/pkits/pkits.sh b/security/nss/tests/pkits/pkits.sh
index ecf007736..e79fdd382 100755
--- a/security/nss/tests/pkits/pkits.sh
+++ b/security/nss/tests/pkits/pkits.sh
@@ -93,7 +93,7 @@ pkits_init()
${BINDIR}/certutil -N -d ${PKITSdb} -f ${PKITSdb}/pw
${BINDIR}/certutil -A -n TrustAnchorRootCertificate -t "C,C,C" -i \
- $certs/TrustAnchorRootCertificate.crt -d $PKITSdb
+ $certs/TrustAnchorRootCertificate.crt -d $PKITSdb -f ${PKITSdb}/pw
if [ -z "$NSS_NO_PKITS_CRLS" ]; then
${BINDIR}/crlutil -I -i $crls/TrustAnchorRootCRL.crl -d ${PKITSdb} -f ${PKITSdb}/pw
else
diff --git a/security/nss/tests/remote/Makefile b/security/nss/tests/remote/Makefile
index 6c6e5bd55..4635bccc5 100644
--- a/security/nss/tests/remote/Makefile
+++ b/security/nss/tests/remote/Makefile
@@ -80,7 +80,6 @@ package_for_testing:
echo 'export USE_64=$(USE_64)' >> $(RTSH)
echo 'export BUILD_OPT=$(BUILD_OPT)' >> $(RTSH)
echo 'export PKITS_DATA=$(PKITS_DATA)' >> $(RTSH)
- echo 'export NSS_DISABLE_ECC=$(NSS_DISABLE_ECC)' >> $(RTSH)
echo 'export NSPR_LOG_MODULES=$(NSPR_LOG_MODULES)' >> $(RTSH)
ifeq ($(OS_TARGET),Android)
# Android doesn't support FIPS tests, because
diff --git a/security/nss/tests/smime/smime.sh b/security/nss/tests/smime/smime.sh
index 2360100de..9cdc0875b 100755
--- a/security/nss/tests/smime/smime.sh
+++ b/security/nss/tests/smime/smime.sh
@@ -40,11 +40,7 @@ smime_init()
fi
SCRIPTNAME=smime.sh
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- html_head "S/MIME Tests with ECC"
- else
- html_head "S/MIME Tests"
- fi
+ html_head "S/MIME Tests"
grep "SUCCESS: SMIME passed" $CERT_LOG_FILE >/dev/null || {
Exit 11 "Fatal - S/MIME of cert.sh needs to pass first"
@@ -85,29 +81,27 @@ smime_sign()
html_msg $? 0 "Compare Attached Signed Data and Original (${HASH})" "."
# Test ECDSA signing for all hash algorithms.
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- echo "$SCRIPTNAME: Signing Detached Message ECDSA w/ {$HASH} ------------------"
- echo "cmsutil -S -T -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.d${SIG}"
- ${PROFTOOL} ${BINDIR}/cmsutil -S -T -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.d${SIG}
- html_msg $? 0 "Create Detached Signature Alice (ECDSA w/ ${HASH})" "."
-
- echo "cmsutil -D -i alice-ec.d${SIG} -c alice.txt -d ${P_R_BOBDIR} "
- ${PROFTOOL} ${BINDIR}/cmsutil -D -i alice-ec.d${SIG} -c alice.txt -d ${P_R_BOBDIR}
- html_msg $? 0 "Verifying Alice's Detached Signature (ECDSA w/ ${HASH})" "."
-
- echo "$SCRIPTNAME: Signing Attached Message (ECDSA w/ ${HASH}) ------------------"
- echo "cmsutil -S -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.${SIG}"
- ${PROFTOOL} ${BINDIR}/cmsutil -S -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.${SIG}
- html_msg $? 0 "Create Attached Signature Alice (ECDSA w/ ${HASH})" "."
-
- echo "cmsutil -D -i alice-ec.${SIG} -d ${P_R_BOBDIR} -o alice-ec.data.${HASH}"
- ${PROFTOOL} ${BINDIR}/cmsutil -D -i alice-ec.${SIG} -d ${P_R_BOBDIR} -o alice-ec.data.${HASH}
- html_msg $? 0 "Decode Alice's Attached Signature (ECDSA w/ ${HASH})" "."
-
- echo "diff alice.txt alice-ec.data.${HASH}"
- diff alice.txt alice-ec.data.${HASH}
- html_msg $? 0 "Compare Attached Signed Data and Original (ECDSA w/ ${HASH})" "."
- fi
+ echo "$SCRIPTNAME: Signing Detached Message ECDSA w/ {$HASH} ------------------"
+ echo "cmsutil -S -T -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.d${SIG}"
+ ${PROFTOOL} ${BINDIR}/cmsutil -S -T -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.d${SIG}
+ html_msg $? 0 "Create Detached Signature Alice (ECDSA w/ ${HASH})" "."
+
+ echo "cmsutil -D -i alice-ec.d${SIG} -c alice.txt -d ${P_R_BOBDIR} "
+ ${PROFTOOL} ${BINDIR}/cmsutil -D -i alice-ec.d${SIG} -c alice.txt -d ${P_R_BOBDIR}
+ html_msg $? 0 "Verifying Alice's Detached Signature (ECDSA w/ ${HASH})" "."
+
+ echo "$SCRIPTNAME: Signing Attached Message (ECDSA w/ ${HASH}) ------------------"
+ echo "cmsutil -S -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.${SIG}"
+ ${PROFTOOL} ${BINDIR}/cmsutil -S -N Alice-ec ${HASH_CMD} -i alice.txt -d ${P_R_ALICEDIR} -p nss -o alice-ec.${SIG}
+ html_msg $? 0 "Create Attached Signature Alice (ECDSA w/ ${HASH})" "."
+
+ echo "cmsutil -D -i alice-ec.${SIG} -d ${P_R_BOBDIR} -o alice-ec.data.${HASH}"
+ ${PROFTOOL} ${BINDIR}/cmsutil -D -i alice-ec.${SIG} -d ${P_R_BOBDIR} -o alice-ec.data.${HASH}
+ html_msg $? 0 "Decode Alice's Attached Signature (ECDSA w/ ${HASH})" "."
+
+ echo "diff alice.txt alice-ec.data.${HASH}"
+ diff alice.txt alice-ec.data.${HASH}
+ html_msg $? 0 "Compare Attached Signed Data and Original (ECDSA w/ ${HASH})" "."
}
diff --git a/security/nss/tests/ssl/ssl.sh b/security/nss/tests/ssl/ssl.sh
index 944849ad3..de867a4bd 100755
--- a/security/nss/tests/ssl/ssl.sh
+++ b/security/nss/tests/ssl/ssl.sh
@@ -57,10 +57,16 @@ ssl_init()
fi
PORT=${PORT-8443}
+ # Avoid port conflicts when multiple tests are running on the same machine.
+ if [ -n "$NSS_TASKCLUSTER_MAC" ]; then
+ cwd=$(cd $(dirname $0); pwd -P)
+ padd=$(echo $cwd | cut -d "/" -f4 | sed 's/[^0-9]//g')
+ PORT=$(($PORT + $padd))
+ fi
NSS_SSL_TESTS=${NSS_SSL_TESTS:-normal_normal}
- nss_ssl_run="stapling signed_cert_timestamps cov auth stress"
+ nss_ssl_run="stapling signed_cert_timestamps cov auth stress dtls"
NSS_SSL_RUN=${NSS_SSL_RUN:-$nss_ssl_run}
-
+
# Test case files
SSLCOV=${QADIR}/ssl/sslcov.txt
SSLAUTH=${QADIR}/ssl/sslauth.txt
@@ -90,15 +96,8 @@ ssl_init()
NON_EC_SUITES=":0016:0032:0033:0038:0039:003B:003C:003D:0040:0041:0067:006A:006B"
NON_EC_SUITES="${NON_EC_SUITES}:0084:009C:009D:009E:009F:00A2:00A3:CCAAcdeinvyz"
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- ECC_STRING=" - with ECC"
- # List of cipher suites to test, including ECC cipher suites.
- CIPHER_SUITES="-c ${EC_SUITES}${NON_EC_SUITES}"
- else
- ECC_STRING=""
- # List of cipher suites to test, excluding ECC cipher suites.
- CIPHER_SUITES="-c ${NON_EC_SUITES}"
- fi
+ # List of cipher suites to test, including ECC cipher suites.
+ CIPHER_SUITES="-c ${EC_SUITES}${NON_EC_SUITES}"
if [ "${OS_ARCH}" != "WINNT" ]; then
ulimit -n 1000 # make sure we have enough file descriptors
@@ -141,16 +140,16 @@ wait_for_selfserv()
{
#verbose="-v"
echo "trying to connect to selfserv at `date`"
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \\"
echo " -d ${P_R_CLIENTDIR} $verbose < ${REQUEST_FILE}"
- ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \
+ ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \
-d ${P_R_CLIENTDIR} $verbose < ${REQUEST_FILE}
if [ $? -ne 0 ]; then
sleep 5
echo "retrying to connect to selfserv at `date`"
echo "tstclnt -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \\"
echo " -d ${P_R_CLIENTDIR} $verbose < ${REQUEST_FILE}"
- ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \
+ ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} ${CLIENT_OPTIONS} -q \
-d ${P_R_CLIENTDIR} $verbose < ${REQUEST_FILE}
if [ $? -ne 0 ]; then
html_failed "Waiting for Server"
@@ -212,8 +211,7 @@ start_selfserv()
echo "$SCRIPTNAME: $testname ----"
fi
sparam=`echo $sparam | sed -e 's;_; ;g'`
- if [ -z "$NSS_DISABLE_ECC" ] && \
- [ -z "$NO_ECC_CERTS" -o "$NO_ECC_CERTS" != "1" ] ; then
+ if [ -z "$NO_ECC_CERTS" -o "$NO_ECC_CERTS" != "1" ] ; then
ECC_OPTIONS="-e ${HOSTADDR}-ecmixed -e ${HOSTADDR}-ec"
else
ECC_OPTIONS=""
@@ -258,13 +256,18 @@ start_selfserv()
echo "selfserv with PID ${PID} started at `date`"
}
+ignore_blank_lines()
+{
+ LC_ALL=C grep -v '^[[:space:]]*\(#\|$\)' "$1"
+}
+
############################## ssl_cov #################################
# local shell function to perform SSL Cipher Coverage tests
########################################################################
ssl_cov()
{
#verbose="-v"
- html_head "SSL Cipher Coverage $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE $ECC_STRING"
+ html_head "SSL Cipher Coverage $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
testname=""
sparam="$CIPHER_SUITES"
@@ -274,15 +277,15 @@ ssl_cov()
VMIN="ssl3"
VMAX="tls1.1"
- exec < ${SSLCOV}
+ ignore_blank_lines ${SSLCOV} | \
while read ectype testmax param testname
do
echo "${testname}" | grep "EXPORT" > /dev/null
EXP=$?
- if [ "$ectype" = "ECC" -a -n "$NSS_DISABLE_ECC" ] ; then
+ if [ "$ectype" = "ECC" ] ; then
echo "$SCRIPTNAME: skipping $testname (ECC only)"
- elif [ "`echo $ectype | cut -b 1`" != "#" ] ; then
+ else
echo "$SCRIPTNAME: running $testname ----------------------------"
VMAX="ssl3"
if [ "$testmax" = "TLS10" ]; then
@@ -295,11 +298,11 @@ ssl_cov()
VMAX="tls1.2"
fi
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\"
echo " -f -d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \
-d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -320,12 +323,11 @@ ssl_cov()
ssl_auth()
{
#verbose="-v"
- html_head "SSL Client Authentication $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE $ECC_STRING"
+ html_head "SSL Client Authentication $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
- exec < ${SSLAUTH}
+ ignore_blank_lines ${SSLAUTH} | \
while read ectype value sparam cparam testname
do
- [ -z "$ectype" ] && continue
echo "${testname}" | grep "don't require client auth" > /dev/null
CAUTH=$?
@@ -333,9 +335,9 @@ ssl_auth()
echo "$SCRIPTNAME: skipping $testname (non-FIPS only)"
elif [ "$ectype" = "SNI" -a "$NORM_EXT" = "Extended Test" ] ; then
echo "$SCRIPTNAME: skipping $testname for $NORM_EXT"
- elif [ "$ectype" = "ECC" -a -n "$NSS_DISABLE_ECC" ] ; then
+ elif [ "$ectype" = "ECC" ] ; then
echo "$SCRIPTNAME: skipping $testname (ECC only)"
- elif [ "`echo $ectype | cut -b 1`" != "#" ]; then
+ else
cparam=`echo $cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" `
if [ "$ectype" = "SNI" ]; then
cparam=`echo $cparam | sed -e "s/Host/$HOST/g" -e "s/Dom/$DOMSUF/g" `
@@ -343,10 +345,10 @@ ssl_auth()
fi
start_selfserv
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
echo " ${cparam} < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -f ${cparam} $verbose ${CLIENT_OPTIONS} \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${cparam} $verbose ${CLIENT_OPTIONS} \
-d ${P_R_CLIENTDIR} < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -395,10 +397,10 @@ ssl_stapling_sub()
start_selfserv
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
echo " -c v -T -O -F -M 1 -V ssl3:tls1.2 < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \
-d ${P_R_CLIENTDIR} $verbose -c v -T -O -F -M 1 -V ssl3:tls1.2 < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -465,7 +467,7 @@ ssl_stapling_stress()
########################################################################
ssl_stapling()
{
- html_head "SSL Cert Status (OCSP Stapling) $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE $ECC_STRING"
+ html_head "SSL Cert Status (OCSP Stapling) $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
# tstclnt Exit code:
# 0: have fresh and valid revocation data, status good
@@ -498,7 +500,7 @@ ssl_stapling()
ssl_signed_cert_timestamps()
{
#verbose="-v"
- html_head "SSL Signed Certificate Timestamps $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE $ECC_STRING"
+ html_head "SSL Signed Certificate Timestamps $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
testname="ssl_signed_cert_timestamps"
value=0
@@ -514,10 +516,10 @@ ssl_signed_cert_timestamps()
# Since we don't have server-side support, this test only covers advertising the
# extension in the client hello.
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
echo " -U -V tls1.0:tls1.2 < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \
-d ${P_R_CLIENTDIR} $verbose -U -V tls1.0:tls1.2 < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -536,26 +538,26 @@ ssl_signed_cert_timestamps()
########################################################################
ssl_stress()
{
- html_head "SSL Stress Test $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE $ECC_STRING"
+ html_head "SSL Stress Test $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
- exec < ${SSLSTRESS}
+ ignore_blank_lines ${SSLSTRESS} | \
while read ectype value sparam cparam testname
do
- if [ -z "$ectype" ]; then
- # silently ignore blank lines
- continue
- fi
-
echo "${testname}" | grep "client auth" > /dev/null
CAUTH=$?
+ echo "${testname}" | grep "no login" > /dev/null
+ NOLOGIN=$?
if [ "$ectype" = "SNI" -a "$NORM_EXT" = "Extended Test" ] ; then
echo "$SCRIPTNAME: skipping $testname for $NORM_EXT"
- elif [ "$ectype" = "ECC" -a -n "$NSS_DISABLE_ECC" ] ; then
+ elif [ "$ectype" = "ECC" ] ; then
echo "$SCRIPTNAME: skipping $testname (ECC only)"
elif [ "${CLIENT_MODE}" = "fips" -a "${CAUTH}" -ne 0 ] ; then
echo "$SCRIPTNAME: skipping $testname (non-FIPS only)"
- elif [ "`echo $ectype | cut -b 1`" != "#" ]; then
+ elif [ "${NOLOGIN}" -eq 0 ] && \
+ [ "${CLIENT_MODE}" = "fips" -o "$NORM_EXT" = "Extended Test" ] ; then
+ echo "$SCRIPTNAME: skipping $testname for $NORM_EXT"
+ else
cparam=`echo $cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" `
if [ "$ectype" = "SNI" ]; then
cparam=`echo $cparam | sed -e "s/Host/$HOST/g" -e "s/Dom/$DOMSUF/g" `
@@ -569,10 +571,16 @@ ssl_stress()
ps -ef | grep selfserv
fi
- echo "strsclnt -q -p ${PORT} -d ${P_R_CLIENTDIR} ${CLIENT_OPTIONS} -w nss $cparam \\"
+ if [ "${NOLOGIN}" -eq 0 ] ; then
+ dbdir=${P_R_NOLOGINDIR}
+ else
+ dbdir=${P_R_CLIENTDIR}
+ fi
+
+ echo "strsclnt -q -p ${PORT} -d ${dbdir} ${CLIENT_OPTIONS} -w nss $cparam \\"
echo " -V ssl3:tls1.2 $verbose ${HOSTADDR}"
echo "strsclnt started at `date`"
- ${PROFTOOL} ${BINDIR}/strsclnt -q -p ${PORT} -d ${P_R_CLIENTDIR} ${CLIENT_OPTIONS} -w nss $cparam \
+ ${PROFTOOL} ${BINDIR}/strsclnt -q -p ${PORT} -d ${dbdir} ${CLIENT_OPTIONS} -w nss $cparam \
-V ssl3:tls1.2 $verbose ${HOSTADDR}
ret=$?
echo "strsclnt completed at `date`"
@@ -596,7 +604,7 @@ ssl_stress()
ssl_crl_ssl()
{
#verbose="-v"
- html_head "CRL SSL Client Tests $NORM_EXT $ECC_STRING"
+ html_head "CRL SSL Client Tests $NORM_EXT"
# Using First CRL Group for this test. There are $CRL_GRP_1_RANGE certs in it.
# Cert number $UNREVOKED_CERT_GRP_1 was not revoked
@@ -604,15 +612,14 @@ ssl_crl_ssl()
CRL_GROUP_RANGE=$CRL_GRP_1_RANGE
UNREVOKED_CERT=$UNREVOKED_CERT_GRP_1
- exec < ${SSLAUTH}
+ ignore_blank_lines ${SSLAUTH} | \
while read ectype value sparam cparam testname
do
- [ "$ectype" = "" ] && continue
- if [ "$ectype" = "ECC" -a -n "$NSS_DISABLE_ECC" ] ; then
+ if [ "$ectype" = "ECC" ] ; then
echo "$SCRIPTNAME: skipping $testname (ECC only)"
elif [ "$ectype" = "SNI" ]; then
continue
- elif [ "`echo $ectype | cut -b 1`" != "#" ]; then
+ else
servarg=`echo $sparam | awk '{r=split($0,a,"-r") - 1;print r;}'`
pwd=`echo $cparam | grep nss`
user=`echo $cparam | grep TestUser`
@@ -642,10 +649,10 @@ ssl_crl_ssl()
cparam=`echo $_cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" `
start_selfserv
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\"
echo " ${cparam} < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -f ${cparam} \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${cparam} \
-d ${R_CLIENTDIR} $verbose < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -669,19 +676,47 @@ ssl_crl_ssl()
html "</TABLE><BR>"
}
-############################## ssl_cov #################################
+############################# setup_policy #############################
+# local shell function to create policy configuration
+########################################################################
+setup_policy()
+{
+ policy="$1"
+ outdir="$2"
+ OUTFILE="${outdir}/pkcs11.txt"
+ cat > "$OUTFILE" << ++EOF++
+library=
+name=NSS Internal PKCS #11 Module
+parameters=configdir='./client' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
+NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
+++EOF++
+ echo "config=${policy}" >> "$OUTFILE"
+ echo "" >> "$OUTFILE"
+ echo "library=${DIST}/${OBJDIR}/lib/libnssckbi.so" >> "$OUTFILE"
+ cat >> "$OUTFILE" << ++EOF++
+name=RootCerts
+NSS=trustOrder=100
+++EOF++
+
+ echo "******************************Testing with: "
+ cat "$OUTFILE"
+ echo "******************************"
+}
+
+############################## ssl_policy ##############################
# local shell function to perform SSL Policy tests
########################################################################
ssl_policy()
{
#verbose="-v"
- html_head "SSL POLICY $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE $ECC_STRING"
+ html_head "SSL POLICY $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
testname=""
sparam="$CIPHER_SUITES"
if [ ! -f "${P_R_CLIENTDIR}/pkcs11.txt" ] ; then
- return;
+ html_failed "${SCRIPTNAME}: ${P_R_CLIENTDIR} is not initialized"
+ return 1;
fi
echo "Saving pkcs11.txt"
@@ -689,17 +724,14 @@ ssl_policy()
start_selfserv # Launch the server
- VMIN="ssl3"
- VMAX="tls1.2"
-
- exec < ${SSLPOLICY}
+ ignore_blank_lines ${SSLPOLICY} | \
while read value ectype testmax param policy testname
do
VMIN="ssl3"
- if [ "$ectype" = "ECC" -a -n "$NSS_DISABLE_ECC" ] ; then
+ if [ "$ectype" = "ECC" ] ; then
echo "$SCRIPTNAME: skipping $testname (ECC only)"
- elif [ "`echo $value | cut -b 1`" != "#" ] ; then
+ else
echo "$SCRIPTNAME: running $testname ----------------------------"
VMAX="ssl3"
if [ "$testmax" = "TLS10" ]; then
@@ -714,30 +746,13 @@ ssl_policy()
# load the policy
policy=`echo ${policy} | sed -e 's;_; ;g'`
+ setup_policy "$policy" ${P_R_CLIENTDIR}
- cat > ${P_R_CLIENTDIR}/pkcs11.txt << ++EOF++
-library=
-name=NSS Internal PKCS #11 Module
-parameters=configdir='./client' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
-NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
-++EOF++
- echo "config=${policy}" >> ${P_R_CLIENTDIR}/pkcs11.txt
- echo "" >> ${P_R_CLIENTDIR}/pkcs11.txt
- echo "library=${DIST}/${OBJDIR}/lib/libnssckbi.so" >> ${P_R_CLIENTDIR}/pkcs11.txt >> ${P_R_CLIENTDIR}/pkcs11.txt
- cat >> ${P_R_CLIENTDIR}/pkcs11.txt << ++EOF++
-name=RootCerts
-NSS=trustOrder=100
-++EOF++
-
- echo "******************************Testing with: "
- cat ${P_R_CLIENTDIR}/pkcs11.txt
- echo "******************************"
-
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\"
echo " -f -d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \
-d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -757,6 +772,103 @@ NSS=trustOrder=100
kill_selfserv
html "</TABLE><BR>"
}
+
+list_enabled_suites()
+{
+ echo "SSL_DIR=${P_R_CLIENTDIR} ${BINDIR}/listsuites"
+ SSL_DIR="${P_R_CLIENTDIR}" ${BINDIR}/listsuites | tail -n+3 | \
+ sed -n -e '/^TLS_/h' -e '/^ .*Enabled.*/{g;p}' | sed 's/:$//'
+}
+
+############################## ssl_policy_listsuites ###################
+# local shell function to perform SSL Policy tests, using listsuites
+########################################################################
+ssl_policy_listsuites()
+{
+ #verbose="-v"
+ html_head "SSL POLICY LISTSUITES $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
+
+ testname=""
+ sparam="$CIPHER_SUITES"
+
+ if [ ! -f "${P_R_CLIENTDIR}/pkcs11.txt" ] ; then
+ html_failed "${SCRIPTNAME}: ${P_R_CLIENTDIR} is not initialized"
+ return 1;
+ fi
+
+ echo "Saving pkcs11.txt"
+ cp ${P_R_CLIENTDIR}/pkcs11.txt ${P_R_CLIENTDIR}/pkcs11.txt.sav
+
+ # Disallow all explicitly
+ setup_policy "disallow=all" ${P_R_CLIENTDIR}
+ RET_EXP=1
+ list_enabled_suites | grep '^TLS_'
+ RET=$?
+ html_msg $RET $RET_EXP "${testname}" \
+ "produced a returncode of $RET, expected is $RET_EXP"
+
+ # Disallow RSA in key exchange explicitly
+ setup_policy "disallow=rsa/ssl-key-exchange" ${P_R_CLIENTDIR}
+ RET_EXP=1
+ list_enabled_suites | grep '^TLS_RSA_'
+ RET=$?
+ html_msg $RET $RET_EXP "${testname}" \
+ "produced a returncode of $RET, expected is $RET_EXP"
+
+ cp ${P_R_CLIENTDIR}/pkcs11.txt.sav ${P_R_CLIENTDIR}/pkcs11.txt
+
+ html "</TABLE><BR>"
+}
+
+############################## ssl_policy_selfserv #####################
+# local shell function to perform SSL Policy tests, using selfserv
+########################################################################
+ssl_policy_selfserv()
+{
+ #verbose="-v"
+ html_head "SSL POLICY SELFSERV $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
+
+ testname=""
+ sparam="$CIPHER_SUITES"
+
+ if [ ! -f "${P_R_SERVERDIR}/pkcs11.txt" ] ; then
+ html_failed "${SCRIPTNAME}: ${P_R_SERVERDIR} is not initialized"
+ return 1;
+ fi
+
+ echo "Saving pkcs11.txt"
+ cp ${P_R_SERVERDIR}/pkcs11.txt ${P_R_SERVERDIR}/pkcs11.txt.sav
+
+ # Disallow RSA in key exchange explicitly
+ setup_policy "disallow=rsa/ssl-key-exchange" ${P_R_SERVERDIR}
+
+ start_selfserv # Launch the server
+
+ VMIN="ssl3"
+ VMAX="tls1.2"
+
+ # Try to connect to the server with a ciphersuite using RSA in key exchange
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c d -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\"
+ echo " -f -d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE}"
+
+ rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
+ RET_EXP=254
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c d -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \
+ -d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE} \
+ >${TMP}/$HOST.tmp.$$ 2>&1
+ RET=$?
+ cat ${TMP}/$HOST.tmp.$$
+ rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
+
+ html_msg $RET $RET_EXP "${testname}" \
+ "produced a returncode of $RET, expected is $RET_EXP"
+
+ cp ${P_R_SERVERDIR}/pkcs11.txt.sav ${P_R_SERVERDIR}/pkcs11.txt
+
+ kill_selfserv
+ html "</TABLE><BR>"
+}
+
############################# is_revoked ###############################
# local shell function to check if certificate is revoked
########################################################################
@@ -826,7 +938,7 @@ load_group_crl() {
fi
echo "================= Reloading ${eccomment}CRL for group $grpBegin - $grpEnd ============="
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\"
echo " -V ssl3:tls1.2 -w nss -n TestUser${UNREVOKED_CERT_GRP_1}${ecsuffix}"
echo "Request:"
echo "GET crl://${SERVERDIR}/root.crl_${grpBegin}-${grpEnd}${ecsuffix}"
@@ -839,7 +951,7 @@ GET crl://${SERVERDIR}/root.crl_${grpBegin}-${grpEnd}${ecsuffix}
_EOF_REQUEST_
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -f \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f \
-d ${R_CLIENTDIR} $verbose -V ssl3:tls1.2 -w nss -n TestUser${UNREVOKED_CERT_GRP_1}${ecsuffix} \
>${OUTFILE_TMP} 2>&1 < ${REQF}
@@ -876,7 +988,7 @@ _EOF_REQUEST_
ssl_crl_cache()
{
#verbose="-v"
- html_head "Cache CRL SSL Client Tests $NORM_EXT $ECC_STRING"
+ html_head "Cache CRL SSL Client Tests $NORM_EXT"
SSLAUTH_TMP=${TMP}/authin.tl.tmp
SERV_ARG=-r_-r
rm -f ${SSLAUTH_TMP}
@@ -892,7 +1004,7 @@ ssl_crl_cache()
while read ectype value sparam cparam testname
do
[ "$ectype" = "" ] && continue
- if [ "$ectype" = "ECC" -a -n "$NSS_DISABLE_ECC" ] ; then
+ if [ "$ectype" = "ECC" ] ; then
echo "$SCRIPTNAME: skipping $testname (ECC only)"
elif [ "$ectype" = "SNI" ]; then
continue
@@ -930,10 +1042,10 @@ ssl_crl_cache()
cparam=`echo $_cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" `
echo "Server Args: $SERV_ARG"
- echo "tstclnt -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\"
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\"
echo " ${cparam} < ${REQUEST_FILE}"
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
- ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -f ${cparam} \
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${cparam} \
-d ${R_CLIENTDIR} $verbose < ${REQUEST_FILE} \
>${TMP}/$HOST.tmp.$$ 2>&1
ret=$?
@@ -985,6 +1097,47 @@ ssl_crl_cache()
html "</TABLE><BR>"
}
+############################ ssl_dtls ###################################
+# local shell function to test tstclnt acting as client and server for DTLS
+#########################################################################
+ssl_dtls()
+{
+ #verbose="-v"
+ html_head "SSL DTLS $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
+
+ testname="ssl_dtls"
+ value=0
+
+ if [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] ; then
+ echo "$SCRIPTNAME: skipping $testname (non-FIPS only)"
+ return 0
+ fi
+
+ echo "${testname}"
+
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_SERVERDIR} $verbose ${SERVER_OPTIONS} \\"
+ echo " -U -V tls1.1:tls1.2 -P server -Q < ${REQUEST_FILE} &"
+
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${SERVER_OPTIONS} \
+ -d ${P_R_SERVERDIR} $verbose -U -V tls1.1:tls1.2 -P server -n ${HOSTADDR} -w nss < ${REQUEST_FILE} 2>&1 &
+
+ PID=$!
+
+ sleep 1
+
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
+ echo " -U -V tls1.1:tls1.2 -P client -Q < ${REQUEST_FILE}"
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \
+ -d ${P_R_CLIENTDIR} $verbose -U -V tls1.1:tls1.2 -P client -Q < ${REQUEST_FILE} 2>&1
+ ret=$?
+ html_msg $ret $value "${testname}" \
+ "produced a returncode of $ret, expected is $value"
+
+ kill ${PID}
+
+ html "</TABLE><BR>"
+}
+
############################## ssl_cleanup #############################
# local shell function to finish this script (no exit since it might be
@@ -1022,6 +1175,9 @@ ssl_run()
"stress")
ssl_stress
;;
+ "dtls")
+ ssl_dtls
+ ;;
esac
done
}
@@ -1143,7 +1299,9 @@ ssl_run_tests()
case "${SSL_TEST}" in
"policy")
if [ "${TEST_MODE}" = "SHARED_DB" ] ; then
- ssl_policy
+ ssl_policy_listsuites
+ ssl_policy_selfserv
+ ssl_policy
fi
;;
"crl")
@@ -1166,8 +1324,8 @@ ssl_run_tests()
ssl_set_fips server on
;;
*)
- echo "${SCRIPTNAME}: Error: Unknown server mode ${SERVER_MODE}"
- continue
+ html_failed "${SCRIPTNAME}: Error: Unknown server mode ${SERVER_MODE}"
+ return 1
;;
esac
@@ -1180,8 +1338,8 @@ ssl_run_tests()
ssl_set_fips client on
;;
*)
- echo "${SCRIPTNAME}: Error: Unknown client mode ${CLIENT_MODE}"
- continue
+ html_failed "${SCRIPTNAME}: Error: Unknown client mode ${CLIENT_MODE}"
+ return 1
;;
esac
diff --git a/security/nss/tests/ssl/sslstress.txt b/security/nss/tests/ssl/sslstress.txt
index e9defc502..a87eedad7 100644
--- a/security/nss/tests/ssl/sslstress.txt
+++ b/security/nss/tests/ssl/sslstress.txt
@@ -21,6 +21,7 @@
# add client auth versions here...
#
noECC 0 -r_-r -c_100_-C_c_-V_ssl3:ssl3_-N_-n_TestUser Stress SSL3 RC4 128 with MD5 (no reuse, client auth)
+ noECC 0 -r_-r -c_100_-C_c_-V_ssl3:ssl3_-N_-n_TestUser Stress SSL3 RC4 128 with MD5 (no reuse, client auth, no login)
noECC 0 -r_-r -c_100_-C_c_-N_-n_TestUser Stress TLS RC4 128 with MD5 (no reuse, client auth)
noECC 0 -r_-r_-u -V_ssl3:tls1.2_-c_100_-C_c_-n_TestUser_-u Stress TLS RC4 128 with MD5 (session ticket, client auth)
noECC 0 -r_-r_-z -V_ssl3:tls1.2_-c_100_-C_c_-n_TestUser_-z Stress TLS RC4 128 with MD5 (compression, client auth)
diff --git a/security/nss/tests/ssl_gtests/ssl_gtests.sh b/security/nss/tests/ssl_gtests/ssl_gtests.sh
index ac39f212c..fd678bf59 100755
--- a/security/nss/tests/ssl_gtests/ssl_gtests.sh
+++ b/security/nss/tests/ssl_gtests/ssl_gtests.sh
@@ -21,16 +21,17 @@
# Generate input to certutil
certscript() {
+ ca=n
while [ $# -gt 0 ]; do
case $1 in
sign) echo 0 ;;
kex) echo 2 ;;
- ca) echo 5;echo 6 ;;
+ ca) echo 5;echo 6;ca=y ;;
esac; shift
done;
echo 9
echo n
- echo ${ca:-n}
+ echo $ca
echo
echo n
}
@@ -41,6 +42,7 @@ certscript() {
make_cert() {
name=$1
type=$2
+ unset type_args trust sign
case $type in
dsa) type_args='-g 1024' ;;
rsa) type_args='-g 1024' ;;
@@ -49,8 +51,11 @@ make_cert() {
p256) type_args='-q nistp256';type=ec ;;
p384) type_args='-q secp384r1';type=ec ;;
p521) type_args='-q secp521r1';type=ec ;;
- rsa_ca) type_args='-g 1024';trust='CT,CT,CT';ca=y;type=rsa ;;
+ rsa_ca) type_args='-g 1024';trust='CT,CT,CT';type=rsa ;;
rsa_chain) type_args='-g 1024';sign='-c rsa_ca';type=rsa;;
+ rsapss_ca) type_args='-g 1024 --pss';trust='CT,CT,CT';type=rsa ;;
+ rsapss_chain) type_args='-g 1024';sign='-c rsa_pss_ca';type=rsa;;
+ rsa_ca_rsapss_chain) type_args='-g 1024 --pss-sign';sign='-c rsa_ca';type=rsa;;
ecdh_rsa) type_args='-q nistp256';sign='-c rsa_ca';type=ec ;;
esac
shift 2
@@ -87,6 +92,9 @@ ssl_gtest_certs() {
make_cert ecdh_ecdsa p256 kex
make_cert rsa_ca rsa_ca ca
make_cert rsa_chain rsa_chain sign
+ make_cert rsa_pss_ca rsapss_ca ca
+ make_cert rsa_pss_chain rsapss_chain sign
+ make_cert rsa_ca_rsa_pss_chain rsa_ca_rsapss_chain sign
make_cert ecdh_rsa ecdh_rsa kex
make_cert dsa dsa sign
}
diff --git a/security/nss/tests/tools/TestOldAES128CA.p12 b/security/nss/tests/tools/TestOldAES128CA.p12
new file mode 100644
index 000000000..a05be8bde
--- /dev/null
+++ b/security/nss/tests/tools/TestOldAES128CA.p12
Binary files differ
diff --git a/security/nss/tests/tools/TestOldCA.p12 b/security/nss/tests/tools/TestOldCA.p12
new file mode 100644
index 000000000..40d5671b9
--- /dev/null
+++ b/security/nss/tests/tools/TestOldCA.p12
Binary files differ
diff --git a/security/nss/tests/tools/tools.sh b/security/nss/tests/tools/tools.sh
index 769bafa00..11be23e05 100644
--- a/security/nss/tests/tools/tools.sh
+++ b/security/nss/tests/tools/tools.sh
@@ -76,11 +76,7 @@ tools_init()
fi
SCRIPTNAME=tools.sh
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- html_head "Tools Tests with ECC"
- else
- html_head "Tools Tests"
- fi
+ html_head "Tools Tests"
grep "SUCCESS: SMIME passed" $CERT_LOG_FILE >/dev/null || {
Exit 15 "Fatal - S/MIME of cert.sh needs to pass first"
@@ -106,6 +102,9 @@ tools_init()
cp ${ALICEDIR}/* ${SIGNDIR}/
mkdir -p ${TOOLSDIR}/html
cp ${QADIR}/tools/sign*.html ${TOOLSDIR}/html
+ mkdir -p ${TOOLSDIR}/data
+ cp ${QADIR}/tools/TestOldCA.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/TestOldAES128CA.p12 ${TOOLSDIR}/data
cd ${TOOLSDIR}
}
@@ -397,30 +396,44 @@ tools_p12_export_list_import_with_default_ciphers()
export_list_import "DEFAULT" "DEFAULT"
- if [ -z "$NSS_DISABLE_ECC" ] ; then
- echo "$SCRIPTNAME: Exporting Alice's email EC cert & key---------------"
- echo "pk12util -o Alice-ec.p12 -n \"Alice-ec\" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \\"
- echo " -w ${R_PWFILE}"
- ${BINDIR}/pk12util -o Alice-ec.p12 -n "Alice-ec" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \
- -w ${R_PWFILE} 2>&1
- ret=$?
- html_msg $ret 0 "Exporting Alice's email EC cert & key (pk12util -o)"
- check_tmpfile
-
- echo "$SCRIPTNAME: Importing Alice's email EC cert & key --------------"
- echo "pk12util -i Alice-ec.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
- ${BINDIR}/pk12util -i Alice-ec.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
- ret=$?
- html_msg $ret 0 "Importing Alice's email EC cert & key (pk12util -i)"
- check_tmpfile
-
- echo "$SCRIPTNAME: Listing Alice's pk12 EC file -----------------"
- echo "pk12util -l Alice-ec.p12 -w ${R_PWFILE}"
- ${BINDIR}/pk12util -l Alice-ec.p12 -w ${R_PWFILE} 2>&1
- ret=$?
- html_msg $ret 0 "Listing Alice's pk12 EC file (pk12util -l)"
- check_tmpfile
- fi
+ echo "$SCRIPTNAME: Exporting Alice's email EC cert & key---------------"
+ echo "pk12util -o Alice-ec.p12 -n \"Alice-ec\" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \\"
+ echo " -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -o Alice-ec.p12 -n "Alice-ec" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \
+ -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Exporting Alice's email EC cert & key (pk12util -o)"
+ check_tmpfile
+
+ echo "$SCRIPTNAME: Importing Alice's email EC cert & key --------------"
+ echo "pk12util -i Alice-ec.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -i Alice-ec.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Importing Alice's email EC cert & key (pk12util -i)"
+ check_tmpfile
+
+ echo "$SCRIPTNAME: Listing Alice's pk12 EC file -----------------"
+ echo "pk12util -l Alice-ec.p12 -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -l Alice-ec.p12 -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Listing Alice's pk12 EC file (pk12util -l)"
+ check_tmpfile
+}
+
+tools_p12_import_old_files()
+{
+ echo "$SCRIPTNAME: Importing PKCS#12 files created with older NSS --------------"
+ echo "pk12util -i TestOldCA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -i ${TOOLSDIR}/data/TestOldCA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Importing PKCS#12 file created with NSS 3.21 (PBES2 with BMPString password)"
+ check_tmpfile
+
+ echo "pk12util -i TestOldAES128CA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -i ${TOOLSDIR}/data/TestOldAES128CA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Importing PKCS#12 file created with NSS 3.29.5 (PBES2 with incorrect AES-128-CBC algorithm ID)"
+ check_tmpfile
}
############################## tools_p12 ###############################
@@ -434,6 +447,7 @@ tools_p12()
tools_p12_export_list_import_all_pkcs12v2pbe_ciphers
tools_p12_export_with_none_ciphers
tools_p12_export_with_invalid_ciphers
+ tools_p12_import_old_files
}
############################## tools_sign ##############################
@@ -503,6 +517,21 @@ SIGNSCRIPT
}
+tools_modutil()
+{
+ echo "$SCRIPTNAME: Test if DB created by modutil -create is initialized"
+ mkdir -p ${R_TOOLSDIR}/moddir
+ # copied from modu function in cert.sh
+ # echo is used to press Enter expected by modutil
+ echo | ${BINDIR}/modutil -create -dbdir "${R_TOOLSDIR}/moddir" 2>&1
+ ret=$?
+ ${BINDIR}/certutil -S -s 'CN=TestUser' -d "${TOOLSDIR}/moddir" -n TestUser \
+ -x -t ',,' -z "${R_NOISE_FILE}"
+ ret=$?
+ html_msg $ret 0 "Test if DB created by modutil -create is initialized"
+ check_tmpfile
+}
+
############################## tools_cleanup ###########################
# local shell function to finish this script (no exit since it might be
# sourced)
@@ -519,6 +548,7 @@ tools_cleanup()
tools_init
tools_p12
tools_sign
+tools_modutil
tools_cleanup
diff --git a/services/crypto/modules/utils.js b/services/crypto/modules/utils.js
index c17f5dfa1..e3a77ad0a 100644
--- a/services/crypto/modules/utils.js
+++ b/services/crypto/modules/utils.js
@@ -77,43 +77,6 @@ this.CryptoUtils = {
},
/**
- * UTF-8 encode a message and perform a SHA-1 over it.
- *
- * @param message
- * (string) Buffer to perform operation on. Should be a JS string.
- * It is possible to pass in a string representing an array
- * of bytes. But, you probably don't want to UTF-8 encode
- * such data and thus should not be using this function.
- *
- * @return string
- * Raw bytes constituting SHA-1 hash. Value is a JS string. Each
- * character is the byte value for that offset. Returned string
- * always has .length == 20.
- */
- UTF8AndSHA1: function UTF8AndSHA1(message) {
- let hasher = Cc["@mozilla.org/security/hash;1"]
- .createInstance(Ci.nsICryptoHash);
- hasher.init(hasher.SHA1);
-
- return CryptoUtils.digestUTF8(message, hasher);
- },
-
- sha1: function sha1(message) {
- return CommonUtils.bytesAsHex(CryptoUtils.UTF8AndSHA1(message));
- },
-
- sha1Base32: function sha1Base32(message) {
- return CommonUtils.encodeBase32(CryptoUtils.UTF8AndSHA1(message));
- },
-
- sha256(message) {
- let hasher = Cc["@mozilla.org/security/hash;1"]
- .createInstance(Ci.nsICryptoHash);
- hasher.init(hasher.SHA256);
- return CommonUtils.bytesAsHex(CryptoUtils.digestUTF8(message, hasher));
- },
-
- /**
* Produce an HMAC key object from a key string.
*/
makeHMACKey: function makeHMACKey(str) {
@@ -187,9 +150,8 @@ this.CryptoUtils = {
hmacAlg=Ci.nsICryptoHMAC.SHA1, hmacLen=20) {
// We don't have a default in the algo itself, as NSS does.
- // Use the constant.
if (!dkLen) {
- dkLen = SYNC_KEY_DECODED_LENGTH;
+ throw new Error("dkLen should be defined");
}
function F(S, c, i, h) {
@@ -551,6 +513,48 @@ this.CryptoUtils = {
};
+/**
+ * Hashing Algorithms SHA-X.
+ * These values map directly onto the values defined
+ * in netwerk/base/nsICryptoHash.idl.
+ */
+let shaX = ["1", "256", "384", "512", "224"];
+
+for (let shaIdx = 0, shaIdxLen = shaX.length; shaIdx < shaIdxLen; shaIdx++) {
+ let shaXIdx = shaX[shaIdx];
+
+ /**
+ * UTF-8 encode a message and perform a SHA-X over it.
+ *
+ * @param message
+ * (string) Buffer to perform operation on. Should be a JS string.
+ * It is possible to pass in a string representing an array
+ * of bytes. But, you probably don't want to UTF-8 encode
+ * such data and thus should not be using this function.
+ *
+ * @return string
+ * Raw bytes constituting SHA-X hash. Value is a JS string.
+ * Each character is the byte value for that offset.
+ */
+ CryptoUtils["UTF8AndSHA" + shaXIdx] = function (message) {
+ let hasher = Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash);
+ hasher.init(hasher["SHA" + shaXIdx]);
+
+ return CryptoUtils.digestUTF8(message, hasher);
+ };
+
+ CryptoUtils["sha" + shaXIdx] = function (message) {
+ return CommonUtils.bytesAsHex(
+ CryptoUtils["UTF8AndSHA" + shaXIdx](message));
+ };
+
+ CryptoUtils["sha" + shaXIdx + "Base32"] = function (message) {
+ return CommonUtils.encodeBase32(
+ CryptoUtils["UTF8AndSHA" + shaXIdx](message));
+ };
+}
+
XPCOMUtils.defineLazyGetter(CryptoUtils, "_utf8Converter", function() {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
diff --git a/toolkit/components/alerts/nsAlertsService.cpp b/toolkit/components/alerts/nsAlertsService.cpp
index 35418dd17..dd67ad983 100644
--- a/toolkit/components/alerts/nsAlertsService.cpp
+++ b/toolkit/components/alerts/nsAlertsService.cpp
@@ -22,6 +22,10 @@
#include "nsIFaviconService.h"
#endif // MOZ_PLACES
+#ifdef XP_WIN
+#include <shellapi.h>
+#endif
+
using namespace mozilla;
using mozilla::dom::ContentChild;
@@ -154,23 +158,12 @@ bool nsAlertsService::ShouldShowAlert()
bool result = true;
#ifdef XP_WIN
- HMODULE shellDLL = ::LoadLibraryW(L"shell32.dll");
- if (!shellDLL)
- return result;
-
- SHQueryUserNotificationStatePtr pSHQueryUserNotificationState =
- (SHQueryUserNotificationStatePtr) ::GetProcAddress(shellDLL, "SHQueryUserNotificationState");
-
- if (pSHQueryUserNotificationState) {
- MOZ_QUERY_USER_NOTIFICATION_STATE qstate;
- if (SUCCEEDED(pSHQueryUserNotificationState(&qstate))) {
- if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) {
- result = false;
- }
+ QUERY_USER_NOTIFICATION_STATE qstate;
+ if (SUCCEEDED(SHQueryUserNotificationState(&qstate))) {
+ if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) {
+ result = false;
}
}
-
- ::FreeLibrary(shellDLL);
#endif
return result;
diff --git a/toolkit/components/alerts/nsAlertsService.h b/toolkit/components/alerts/nsAlertsService.h
index 3f23eaabf..d2b2e1e6c 100644
--- a/toolkit/components/alerts/nsAlertsService.h
+++ b/toolkit/components/alerts/nsAlertsService.h
@@ -10,23 +10,6 @@
#include "nsCOMPtr.h"
#include "nsXULAlerts.h"
-#ifdef XP_WIN
-typedef enum tagMOZ_QUERY_USER_NOTIFICATION_STATE {
- QUNS_NOT_PRESENT = 1,
- QUNS_BUSY = 2,
- QUNS_RUNNING_D3D_FULL_SCREEN = 3,
- QUNS_PRESENTATION_MODE = 4,
- QUNS_ACCEPTS_NOTIFICATIONS = 5,
- QUNS_QUIET_TIME = 6,
- QUNS_IMMERSIVE = 7
-} MOZ_QUERY_USER_NOTIFICATION_STATE;
-
-extern "C" {
-// This function is Windows Vista or later
-typedef HRESULT (__stdcall *SHQueryUserNotificationStatePtr)(MOZ_QUERY_USER_NOTIFICATION_STATE *pquns);
-}
-#endif // defined(XP_WIN)
-
class nsAlertsService : public nsIAlertsService,
public nsIAlertsDoNotDisturb
{
diff --git a/toolkit/components/alerts/resources/content/alert.js b/toolkit/components/alerts/resources/content/alert.js
index 523ec378e..ead4d503f 100644
--- a/toolkit/components/alerts/resources/content/alert.js
+++ b/toolkit/components/alerts/resources/content/alert.js
@@ -7,7 +7,21 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Services.jsm");
-// Copied from nsILookAndFeel.h, see comments on eMetric_AlertNotificationOrigin
+/*
+ * This indicates from which corner of the screen alerts slide in,
+ * and from which direction (horizontal/vertical).
+ * 0, the default, represents bottom right, sliding vertically.
+ * Use any bitwise combination of the following constants:
+ * NS_ALERT_HORIZONTAL (1), NS_ALERT_LEFT (2), NS_ALERT_TOP (4).
+ *
+ * 6 4
+ * +-----------+
+ * 7| |5
+ * | |
+ * 3| |1
+ * +-----------+
+ * 2 0
+ */
const NS_ALERT_HORIZONTAL = 1;
const NS_ALERT_LEFT = 2;
const NS_ALERT_TOP = 4;
@@ -41,6 +55,8 @@ function prefillAlertInfo() {
// arguments[11] -> the nsIURI.hostPort of the origin, optional
// arguments[12] -> the alert icon URL, optional
+ document.getElementById('alertTime').setAttribute('value', (new Date).getTime());
+
switch (window.arguments.length) {
default:
case 13: {
@@ -235,7 +251,15 @@ function moveWindowToEnd() {
let windows = Services.wm.getEnumerator("alert:alert");
while (windows.hasMoreElements()) {
let alertWindow = windows.getNext();
- if (alertWindow != window) {
+ let alertWindowTime = Number(
+ alertWindow.document.getElementById('alertTime').getAttribute('value'));
+ let windowTime = Number(
+ window.document.getElementById('alertTime').getAttribute('value'));
+ // The time of window creation.
+ // Otherwise calling the notification twice (and more) in a row
+ // does not work.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1263155
+ if ((alertWindow != window) && (alertWindowTime <= windowTime)) {
if (gOrigin & NS_ALERT_TOP) {
y = Math.max(y, alertWindow.screenY + alertWindow.outerHeight - WINDOW_SHADOW_SPREAD);
} else {
diff --git a/toolkit/components/alerts/resources/content/alert.xul b/toolkit/components/alerts/resources/content/alert.xul
index 8597d9954..1549f4530 100644
--- a/toolkit/components/alerts/resources/content/alert.xul
+++ b/toolkit/components/alerts/resources/content/alert.xul
@@ -24,6 +24,7 @@
<script type="application/javascript" src="chrome://global/content/alerts/alert.js"/>
<vbox id="alertBox" class="alertBox">
+ <label id="alertTime" value="" hidden="true"/>
<box id="alertTitleBox">
<image id="alertIcon"/>
<label id="alertTitleLabel" class="alertTitle plain" crop="end"/>
diff --git a/toolkit/components/build/nsToolkitCompsModule.cpp b/toolkit/components/build/nsToolkitCompsModule.cpp
index 675c8c92b..22bb434a0 100644
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -35,7 +35,9 @@
#include "nsBrowserStatusFilter.h"
#include "mozilla/FinalizationWitnessService.h"
#include "mozilla/NativeOSFileInternals.h"
+#ifdef MOZ_WEBEXTENSIONS
#include "mozilla/AddonContentPolicy.h"
+#endif
#include "mozilla/AddonPathService.h"
#if defined(XP_WIN)
@@ -122,7 +124,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(FinalizationWitnessService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
+#ifdef MOZ_WEBEXTENSIONS
NS_GENERIC_FACTORY_CONSTRUCTOR(AddonContentPolicy)
+#endif
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
@@ -155,7 +159,9 @@ NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID);
#endif
NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID);
NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID);
+#ifdef MOZ_WEBEXTENSIONS
NS_DEFINE_NAMED_CID(NS_ADDONCONTENTPOLICY_CID);
+#endif
NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID);
NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
@@ -189,7 +195,9 @@ static const Module::CIDEntry kToolkitCIDs[] = {
#endif
{ &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor },
{ &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor },
+#ifdef MOZ_WEBEXTENSIONS
{ &kNS_ADDONCONTENTPOLICY_CID, false, nullptr, AddonContentPolicyConstructor },
+#endif
{ &kNS_ADDON_PATH_SERVICE_CID, false, nullptr, AddonPathServiceConstructor },
{ &kNATIVE_FILEWATCHER_SERVICE_CID, false, nullptr, NativeFileWatcherServiceConstructor },
{ nullptr }
@@ -225,14 +233,18 @@ static const Module::ContractIDEntry kToolkitContracts[] = {
#endif
{ FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID },
{ NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID },
+#ifdef MOZ_WEBEXTENSIONS
{ NS_ADDONCONTENTPOLICY_CONTRACTID, &kNS_ADDONCONTENTPOLICY_CID },
+#endif
{ NS_ADDONPATHSERVICE_CONTRACTID, &kNS_ADDON_PATH_SERVICE_CID },
{ NATIVE_FILEWATCHER_SERVICE_CONTRACTID, &kNATIVE_FILEWATCHER_SERVICE_CID },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kToolkitCategories[] = {
+#ifdef MOZ_WEBEXTENSIONS
{ "content-policy", NS_ADDONCONTENTPOLICY_CONTRACTID, NS_ADDONCONTENTPOLICY_CONTRACTID },
+#endif
{ nullptr }
};
diff --git a/toolkit/components/maintenanceservice/bootstrapinstaller/maintenanceservice_installer.nsi b/toolkit/components/maintenanceservice/bootstrapinstaller/maintenanceservice_installer.nsi
index 9e831dc9c..d4d21e377 100644
--- a/toolkit/components/maintenanceservice/bootstrapinstaller/maintenanceservice_installer.nsi
+++ b/toolkit/components/maintenanceservice/bootstrapinstaller/maintenanceservice_installer.nsi
@@ -117,10 +117,7 @@ Function .onInit
System::Call 'kernel32::SetDllDirectoryW(w "")'
SetSilent silent
- ; On Windows 2000 we do not install the maintenance service.
- ; We won't run this installer from the parent installer, but just in case
- ; someone tries to execute it on Windows 2000...
- ${Unless} ${AtLeastWinXP}
+ ${Unless} ${AtLeastWin7}
Abort
${EndUnless}
FunctionEnd
diff --git a/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest b/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
index cb317c47d..e6bfba8ca 100644
--- a/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
+++ b/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
@@ -20,7 +20,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/toolkit/components/places/BookmarkHTMLUtils.jsm b/toolkit/components/places/BookmarkHTMLUtils.jsm
index 2285aae6e..f8fc0ba51 100644
--- a/toolkit/components/places/BookmarkHTMLUtils.jsm
+++ b/toolkit/components/places/BookmarkHTMLUtils.jsm
@@ -746,6 +746,7 @@ BookmarkImporter.prototype = {
this._curFrame.inDescription = true;
break;
case "hr":
+ this._closeContainer(aElt);
this._handleSeparator(aElt);
break;
}
diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js
index 95cadfbe7..5daf6d189 100644
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -968,31 +968,32 @@ function safeModeRestart() {
* Set up event listeners for buttons.
*/
function setupEventListeners() {
- $("show-update-history-button").addEventListener("click", function (event) {
+#ifdef MOZ_UPDATER
+ $("show-update-history-button").addEventListener("click", function(event) {
var prompter = Cc["@mozilla.org/updates/update-prompt;1"].createInstance(Ci.nsIUpdatePrompt);
prompter.showUpdateHistory(window);
});
- $("reset-box-button").addEventListener("click", function (event) {
+#endif
+ $("reset-box-button").addEventListener("click", function(event) {
ResetProfile.openConfirmationDialog(window);
});
- $("copy-raw-data-to-clipboard").addEventListener("click", function (event) {
+ $("copy-raw-data-to-clipboard").addEventListener("click", function(event) {
copyRawDataToClipboard(this);
});
- $("copy-to-clipboard").addEventListener("click", function (event) {
+ $("copy-to-clipboard").addEventListener("click", function(event) {
copyContentsToClipboard();
});
- $("profile-dir-button").addEventListener("click", function (event) {
+ $("profile-dir-button").addEventListener("click", function(event) {
openProfileDirectory();
});
- $("restart-in-safe-mode-button").addEventListener("click", function (event) {
+ $("restart-in-safe-mode-button").addEventListener("click", function(event) {
if (Services.obs.enumerateObservers("restart-in-safe-mode").hasMoreElements()) {
Services.obs.notifyObservers(null, "restart-in-safe-mode", "");
- }
- else {
+ } else {
safeModeRestart();
}
});
- $("verify-place-integrity-button").addEventListener("click", function (event) {
+ $("verify-place-integrity-button").addEventListener("click", function(event) {
PlacesDBUtils.checkAndFixDatabase(function(aLog) {
let msg = aLog.join("\n");
$("verify-place-result").style.display = "block";
diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml
index f5939a6eb..e2885c8b8 100644
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -99,6 +99,7 @@
</tr>
#ifndef ANDROID
+#ifdef MOZ_UPDATER
<tr class="no-copy">
<th class="column">
&aboutSupport.appBasicsUpdateHistory;
@@ -111,6 +112,7 @@
</td>
</tr>
#endif
+#endif
#ifdef MOZ_UPDATER
<tr>
diff --git a/toolkit/content/mozilla.xhtml b/toolkit/content/mozilla.xhtml
index 1ffde19e4..2acfc9f5d 100644
--- a/toolkit/content/mozilla.xhtml
+++ b/toolkit/content/mozilla.xhtml
@@ -13,7 +13,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset='utf-8' />
- <title>&mozilla.title.15.1;</title>
+ <title>&chronicles.title.55.2;</title>
<style>
html {
@@ -54,11 +54,11 @@ a {
<section>
<p id="moztext">
- &mozilla.quote.15.1;
+ &chronicles.quote.55.2;
</p>
<p id="from">
- &mozilla.from.15.1;
+ &chronicles.from.55.2;
</p>
</section>
diff --git a/toolkit/crashreporter/client/crashreporter.exe.manifest b/toolkit/crashreporter/client/crashreporter.exe.manifest
index f8587bfad..e6b2ceefb 100644
--- a/toolkit/crashreporter/client/crashreporter.exe.manifest
+++ b/toolkit/crashreporter/client/crashreporter.exe.manifest
@@ -32,7 +32,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/toolkit/library/libxul.mk b/toolkit/library/libxul.mk
index d918d9c84..9e7e8beee 100644
--- a/toolkit/library/libxul.mk
+++ b/toolkit/library/libxul.mk
@@ -16,16 +16,6 @@ EXTRA_DEPS += symverscript
endif
endif
-ifdef MOZ_WEBRTC
-ifeq (WINNT,$(OS_TARGET))
-ifndef MOZ_HAS_WINSDK_WITH_D3D
-OS_LDFLAGS += \
- -LIBPATH:'$(MOZ_DIRECTX_SDK_PATH)/lib/$(MOZ_D3D_CPU_SUFFIX)' \
- $(NULL)
-endif
-endif
-endif
-
# Generate GDB pretty printer-autoload files only on Linux. OSX's GDB is
# too old to support Python pretty-printers; if this changes, we could make
# this 'ifdef GNU_CC'.
diff --git a/toolkit/locales/en-US/chrome/global/config.dtd b/toolkit/locales/en-US/chrome/global/config.dtd
index 083363aea..a9bf8ab48 100644
--- a/toolkit/locales/en-US/chrome/global/config.dtd
+++ b/toolkit/locales/en-US/chrome/global/config.dtd
@@ -6,10 +6,10 @@
<!-- about:config warning page -->
<!-- LOCALIZATION NOTE: aboutWarningTitle.label should be attention grabbing and playful -->
-<!ENTITY aboutWarningTitle.label "This might void your warranty!">
-<!ENTITY aboutWarningText.label "Changing these advanced settings can be harmful to the stability, security, and performance of this application. You should only continue if you are sure of what you are doing.">
-<!ENTITY aboutWarningButton2.label "I accept the risk!">
-<!ENTITY aboutWarningCheckbox.label "Show this warning next time">
+<!ENTITY aboutWarningTitle.label "There be dragons here!">
+<!ENTITY aboutWarningText.label "Changing these advanced settings can be (severely) harmful to the stability, security, operation and performance of this application. You should only continue if you are sure of what you are doing; we will not be responsible for any cases of users being mauled, set on fire, or eaten.">
+<!ENTITY aboutWarningButton2.label "I promise to be careful!">
+<!ENTITY aboutWarningCheckbox.label "Keep reminding me that this is dangerous">
<!ENTITY searchPrefs.label "Search:">
<!ENTITY searchPrefs.accesskey "r">
diff --git a/toolkit/locales/en-US/chrome/global/mozilla.dtd b/toolkit/locales/en-US/chrome/global/mozilla.dtd
index 74ae40d44..038d8eb75 100644
--- a/toolkit/locales/en-US/chrome/global/mozilla.dtd
+++ b/toolkit/locales/en-US/chrome/global/mozilla.dtd
@@ -2,13 +2,13 @@
- License, v. 2.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 mozilla.title.15.1
-'The Book of Mozilla, 15:1'>
+<!ENTITY chronicles.title.55.2
+'The Chronicles of the Pale Moon, 55:2'>
-<!ENTITY mozilla.quote.15.1
-'The <em>twins</em> of Mammon quarrelled. Their warring plunged the world into a <em>new darkness</em>, and the beast
-abhorred the darkness. So it began to move <em>swiftly</em>, and grew more powerful, and went forth and multiplied.
-And the beasts brought <em>fire</em> and light to the darkness.'>
+<!ENTITY chronicles.quote.55.2
+'And so, our focus was drawn through time and space to the <em>emerging dragon</em> who would not abandon hope.<br/>
+Its resilience, stubbornness and spirit unbroken, and searching for long hours to find those willing to <em>join</em> its cause.<br/>
+The old nest abandoned, the death throes of the Beast ignored, and more determined than ever to find glory in the future.'>
-<!ENTITY mozilla.from.15.1
-'from <strong>The Book of Mozilla,</strong> 15:1'>
+<!ENTITY chronicles.from.55.2
+'from <strong>The Chronicles of the Pale Moon,</strong> 55:2'>
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/about.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/about.dtd
new file mode 100644
index 000000000..4f9098966
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/about.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 creator.label "Created By:">
+<!ENTITY developers.label "Developers:">
+<!ENTITY translators.label "Translators:">
+<!ENTITY contributors.label "Contributors:">
+<!ENTITY homepage.label "Visit Home Page">
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/blocklist.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/blocklist.dtd
new file mode 100644
index 000000000..f393cc906
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/blocklist.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 blocklist.title "Add-ons may be causing problems">
+<!ENTITY blocklist.style "width: 45em; height: 30em">
+<!ENTITY blocklist.summary "&brandShortName; has determined that the following add-ons are known to cause stability or security problems:">
+<!ENTITY blocklist.softblocked "For your protection, it is highly recommended that you restart with these add-ons disabled.">
+<!ENTITY blocklist.hardblocked "These add-ons have a high risk of causing stability or security problems and have been blocked, but a restart is required to disable them completely.">
+<!ENTITY blocklist.softandhard "The add-ons that have a high risk of causing stability or security problems have been blocked. The others are lower risk, but it is highly recommended that you restart with them disabled.">
+<!ENTITY blocklist.moreinfo "More information">
+
+<!ENTITY blocklist.accept.label "Restart &brandShortName;">
+<!ENTITY blocklist.accept.accesskey "R">
+
+<!ENTITY blocklist.blocked.label "Blocked">
+<!ENTITY blocklist.checkbox.label "Disable">
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
new file mode 100644
index 000000000..c74fdeb2f
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
@@ -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/. -->
+<!ENTITY addons.windowTitle "Add-ons Manager">
+
+<!ENTITY search.placeholder "Search all add-ons">
+<!ENTITY search.buttonlabel "Search">
+<!-- LOCALIZATION NOTE (search.commandKey):
+ The search command key should match findOnCmd.commandkey from browser.dtd -->
+<!ENTITY search.commandkey "f">
+
+<!ENTITY loading.label "Loading…">
+<!ENTITY listEmpty.installed.label "You don't have any add-ons of this type installed">
+<!ENTITY listEmpty.availableUpdates.label "No updates found">
+<!ENTITY listEmpty.recentUpdates.label "You haven't recently updated any add-ons">
+<!ENTITY listEmpty.findUpdates.label "Check For Updates">
+<!ENTITY listEmpty.search.label "Could not find any matching add-ons">
+<!ENTITY listEmpty.button.label "Learn more about add-ons">
+<!ENTITY installAddonFromFile.label "Install Add-on From File…">
+<!ENTITY installAddonFromFile.accesskey "I">
+<!ENTITY toolsMenu.tooltip "Tools for all add-ons">
+
+<!ENTITY cmd.back.tooltip "Go back one page">
+<!ENTITY cmd.forward.tooltip "Go forward one page">
+
+<!-- global warnings -->
+<!ENTITY warning.safemode.label "All add-ons have been disabled by safe mode.">
+<!ENTITY warning.checkcompatibility.label "Add-on compatibility checking is disabled. You may have incompatible add-ons.">
+<!ENTITY warning.checkcompatibility.enable.label "Enable">
+<!ENTITY warning.checkcompatibility.enable.tooltip "Enable add-on compatibility checking">
+<!ENTITY warning.updatesecurity.label "Add-on update security checking is disabled. You may be compromised by updates.">
+<!ENTITY warning.updatesecurity.enable.label "Enable">
+<!ENTITY warning.updatesecurity.enable.tooltip "Enable add-on update security checking">
+
+<!-- global informations -->
+<!ENTITY info.plugincheck.label "Check to see if your plugins are up to date">
+<!ENTITY info.plugincheck.tooltip "Check to see if your plugins are up to date">
+
+<!-- categories / views -->
+<!ENTITY view.search.label "Search">
+<!ENTITY view.discover.label "Get Add-ons">
+<!ENTITY view.recentUpdates.label "Recent Updates">
+<!ENTITY view.availableUpdates.label "Available Updates">
+
+<!-- addon updates -->
+<!ENTITY updates.checkForUpdates.label "Check for Updates">
+<!ENTITY updates.checkForUpdates.accesskey "C">
+<!ENTITY updates.viewUpdates.label "View Recent Updates">
+<!ENTITY updates.viewUpdates.accesskey "V">
+<!-- LOCALIZATION NOTE (updates.updateAddonsAutomatically.label): This menu item
+ is a checkbox that toggles the default global behavior for add-on update
+ checking. -->
+<!ENTITY updates.updateAddonsAutomatically.label "Update Add-ons Automatically">
+<!ENTITY updates.updateAddonsAutomatically.accesskey "A">
+<!-- LOCALIZATION NOTE (updates.resetUpdatesToAutomatic.label, updates.resetUpdatesToManual.label):
+ Specific addons can have custom update checking behaviors ("Manually",
+ "Automatically", "Use default global behavior"). These menu items reset the
+ update checking behavior for all add-ons to the default global behavior
+ (which itself is either "Automatically" or "Manually", controlled by the
+ updates.updateAddonsAutomatically.label menu item). -->
+<!ENTITY updates.resetUpdatesToAutomatic.label "Reset All Add-ons to Update Automatically">
+<!ENTITY updates.resetUpdatesToAutomatic.accesskey "R">
+<!ENTITY updates.resetUpdatesToManual.label "Reset All Add-ons to Update Manually">
+<!ENTITY updates.resetUpdatesToManual.accesskey "R">
+<!ENTITY updates.updating.label "Updating add-ons">
+<!ENTITY updates.installed.label "Your add-ons have been updated.">
+<!ENTITY updates.downloaded.label "Your add-on updates have been downloaded.">
+<!ENTITY updates.restart.label "Restart now to complete installation">
+<!ENTITY updates.noneFound.label "No updates found">
+<!ENTITY updates.manualUpdatesFound.label "View Available Updates">
+<!ENTITY updates.updateSelected.label "Install Updates">
+<!ENTITY updates.updateSelected.tooltip "Install available updates in this list">
+
+<!-- addon actions -->
+<!ENTITY cmd.showDetails.label "Show More Information">
+<!ENTITY cmd.showDetails.accesskey "S">
+<!ENTITY cmd.findUpdates.label "Find Updates">
+<!ENTITY cmd.findUpdates.accesskey "F">
+<!ENTITY cmd.preferencesWin.label "Options">
+<!ENTITY cmd.preferencesWin.accesskey "O">
+<!ENTITY cmd.preferencesUnix.label "Preferences">
+<!ENTITY cmd.preferencesUnix.accesskey "P">
+<!ENTITY cmd.about.label "About">
+<!ENTITY cmd.about.accesskey "A">
+
+<!ENTITY cmd.enableAddon.label "Enable">
+<!ENTITY cmd.enableAddon.accesskey "E">
+<!ENTITY cmd.disableAddon.label "Disable">
+<!ENTITY cmd.disableAddon.accesskey "D">
+<!ENTITY cmd.enableTheme.label "Wear Theme">
+<!ENTITY cmd.enableTheme.accesskey "W">
+<!ENTITY cmd.disableTheme.label "Stop Wearing Theme">
+<!ENTITY cmd.disableTheme.accesskey "W">
+<!ENTITY cmd.askToActivate.label "Ask to Activate">
+<!ENTITY cmd.askToActivate.tooltip "Ask to use this add-on each time">
+<!ENTITY cmd.alwaysActivate.label "Always Activate">
+<!ENTITY cmd.alwaysActivate.tooltip "Always use this add-on">
+<!ENTITY cmd.neverActivate.label "Never Activate">
+<!ENTITY cmd.neverActivate.tooltip "Never use this add-on">
+<!ENTITY cmd.stateMenu.tooltip "Change when this add-on runs">
+<!ENTITY cmd.installAddon.label "Install">
+<!ENTITY cmd.installAddon.accesskey "I">
+<!ENTITY cmd.uninstallAddon.label "Remove">
+<!ENTITY cmd.uninstallAddon.accesskey "R">
+<!ENTITY cmd.debugAddon.label "Debug">
+<!ENTITY cmd.showPreferencesWin.label "Options">
+<!ENTITY cmd.showPreferencesWin.tooltip "Change this add-on's options">
+<!ENTITY cmd.showPreferencesUnix.label "Preferences">
+<!ENTITY cmd.showPreferencesUnix.tooltip "Change this add-on's preferences">
+<!ENTITY cmd.contribute.label "Contribute">
+<!ENTITY cmd.contribute.accesskey "C">
+<!ENTITY cmd.contribute.tooltip "Contribute to the development of this add-on">
+
+<!ENTITY cmd.showReleaseNotes.label "Show Release Notes">
+<!ENTITY cmd.showReleaseNotes.tooltip "Show the release notes for this update">
+<!ENTITY cmd.hideReleaseNotes.label "Hide Release Notes">
+<!ENTITY cmd.hideReleaseNotes.tooltip "Hide the release notes for this update">
+
+<!-- discovery view -->
+<!-- LOCALIZATION NOTE (discover.title,discover.description,discover.footer):
+ Displayed in the center of the Get Add-ons view, see bug 601143 for mockups. -->
+<!ENTITY discover.title "What are Add-ons?">
+<!ENTITY discover.description2 "Add-ons are applications that let you personalize &brandShortName; with
+ extra functionality or style. Try a time-saving sidebar, a weather notifier, or a themed look to make &brandShortName;
+ your own.">
+<!ENTITY discover.footer "When you're connected to the internet, this pane will feature
+ some of the best and most popular add-ons for you to try out.">
+
+<!-- detail view -->
+<!ENTITY detail.version.label "Version">
+<!ENTITY detail.lastupdated.label "Last Updated">
+<!ENTITY detail.creator.label "Developer">
+<!ENTITY detail.homepage.label "Homepage">
+<!ENTITY detail.numberOfDownloads.label "Downloads">
+
+<!ENTITY detail.contributions.description "The developer of this add-on asks that you help support its continued development by making a small contribution.">
+
+<!ENTITY detail.updateType "Automatic Updates">
+<!ENTITY detail.updateDefault.label "Default">
+<!ENTITY detail.updateDefault.tooltip "Automatically install updates only if that's the default">
+<!ENTITY detail.updateAutomatic.label "On">
+<!ENTITY detail.updateAutomatic.tooltip "Automatically install updates">
+<!ENTITY detail.updateManual.label "Off">
+<!ENTITY detail.updateManual.tooltip "Don't automatically install updates">
+<!ENTITY detail.home "Homepage">
+<!ENTITY detail.repository "Add-on Profile">
+<!ENTITY detail.size "Size">
+
+<!ENTITY detail.checkForUpdates.label "Check for Updates">
+<!ENTITY detail.checkForUpdates.accesskey "F">
+<!ENTITY detail.checkForUpdates.tooltip "Check for updates for this add-on">
+<!ENTITY detail.showPreferencesWin.label "Options">
+<!ENTITY detail.showPreferencesWin.accesskey "O">
+<!ENTITY detail.showPreferencesWin.tooltip "Change this add-on's options">
+<!ENTITY detail.showPreferencesUnix.label "Preferences">
+<!ENTITY detail.showPreferencesUnix.accesskey "P">
+<!ENTITY detail.showPreferencesUnix.tooltip "Change this add-on's preferences">
+
+
+<!-- ratings -->
+<!ENTITY rating2.label "Rating">
+
+<!-- download/install progress -->
+<!ENTITY progress.pause.tooltip "Pause">
+<!ENTITY progress.cancel.tooltip "Cancel">
+
+
+<!-- list sorting -->
+<!ENTITY sort.name.label "Name">
+<!ENTITY sort.name.tooltip "Sort by name">
+<!ENTITY sort.dateUpdated.label "Last Updated">
+<!ENTITY sort.dateUpdated.tooltip "Sort by date updated">
+<!ENTITY sort.relevance.label "Best match">
+<!ENTITY sort.relevance.tooltip "Sort by relevance">
+<!ENTITY sort.price.label "Price">
+<!ENTITY sort.price.tooltip "Sort by price">
+
+<!ENTITY search.filter2.label "Search:">
+<!ENTITY search.filter2.installed.label "My Add-ons">
+<!ENTITY search.filter2.installed.tooltip "Show installed add-ons">
+<!ENTITY search.filter2.available.label "Available Add-ons">
+<!ENTITY search.filter2.available.tooltip "Show add-ons available to install">
+
+<!ENTITY addon.homepage "Homepage">
+<!ENTITY addon.details.label "More">
+<!ENTITY addon.details.tooltip "Show more details about this add-on">
+<!ENTITY addon.unknownDate "Unknown">
+<!-- LOCALIZATION NOTE (addon.disabled.postfix): This is used in a normal list
+ to signify that an add-on is disabled, in the form
+ "<Addon name> <1.0> (disabled)" -->
+<!ENTITY addon.disabled.postfix "(disabled)">
+<!-- LOCALIZATION NOTE (addon.update.postfix): This is used in the available
+ updates list to signify that an item is an update, in the form
+ "<Addon name> <1.1> Update". It is fine to use constructs like brackets if
+ necessary -->
+<!ENTITY addon.update.postfix "Update">
+<!ENTITY addon.undoAction.label "Undo">
+<!ENTITY addon.undoAction.tooltip "Undo this action">
+<!ENTITY addon.undoRemove.label "Undo">
+<!ENTITY addon.undoRemove.tooltip "Keep this add-on installed">
+<!ENTITY addon.restartNow.label "Restart now">
+<!ENTITY addon.install.label "Install">
+<!ENTITY addon.install.tooltip "Install this add-on">
+<!ENTITY addon.updateNow.label "Update Now">
+<!ENTITY addon.updateNow.tooltip "Install the update for this add-on">
+<!ENTITY addon.includeUpdate.label "Include in Update">
+<!ENTITY addon.updateAvailable.label "An update is available">
+<!ENTITY addon.checkingForUpdates.label "Checking for updates…">
+<!ENTITY addon.releaseNotes.label "Release Notes:">
+<!ENTITY addon.loadingReleaseNotes.label "Loading…">
+<!ENTITY addon.errorLoadingReleaseNotes.label "Sorry, but there was an error loading the release notes.">
+
+<!ENTITY addon.nativeAddon "This add-on directly targets Pale Moon">
+<!ENTITY addon.compatAddon "This add-on targets Mozilla Firefox and runs in compatibility mode">
+
+<!ENTITY addon.createdBy.label "By ">
+
+<!ENTITY eula.title "End-User License Agreement">
+<!ENTITY eula.width "560px">
+<!ENTITY eula.height "400px">
+<!ENTITY eula.accept "Accept and Install…">
+
+<!ENTITY settings.path.button.label "Browse…">
+
+<!-- LOCALIZATION NOTE (experiment.info.label): The strings related to
+ experiments are present on the "Experiments" tab of the add-ons manager.
+ This tab won't be displayed unless an Experiment add-on is installed.
+ Install https://people.mozilla.org/~gszorc/dummy-experiment-addon.xpi
+ to cause this tab to appear. -->
+<!ENTITY experiment.info.label "What's this? Telemetry may install and run experiments from time to time.">
+<!ENTITY experiment.info.learnmore "Learn More">
+<!ENTITY experiment.info.learnmore.accesskey "L">
+<!ENTITY experiment.info.changetelemetry "Telemetry Settings">
+<!ENTITY experiment.info.changetelemetry.accesskey "T">
+
+<!ENTITY setting.learnmore "Learn More…">
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
new file mode 100644
index 000000000..c4e2660ee
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
@@ -0,0 +1,177 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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 (aboutWindowTitle) %S is the addon name
+aboutWindowTitle=About %S
+aboutWindowCloseButton=Close
+#LOCALIZATION NOTE (aboutWindowVersionString) %S is the addon version
+aboutWindowVersionString=version %S
+#LOCALIZATION NOTE (aboutAddon) %S is the addon name
+aboutAddon=About %S
+
+#LOCALIZATION NOTE (uninstallNotice) %S is the add-on name
+uninstallNotice=%S has been removed.
+
+#LOCALIZATION NOTE (numReviews): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of reviews
+numReviews=#1 review;#1 reviews
+
+#LOCALIZATION NOTE (dateUpdated) %S is the date the addon was last updated
+dateUpdated=Updated %S
+
+#LOCALIZATION NOTE (notification.incompatible) %1$S is the add-on name, %2$S is brand name, %3$S is application version
+notification.incompatible=%1$S is incompatible with %2$S %3$S.
+notification.jetsdk=This is a Jetpack/SDK extension which are not supported in %1$S %2$S.
+#LOCALIZATION NOTE (notification.blocked) %1$S is the add-on name
+notification.blocked=%1$S has been disabled due to security or stability issues.
+notification.blocked.link=More Information
+#LOCALIZATION NOTE (notification.softblocked) %1$S is the add-on name
+notification.softblocked=%1$S is known to cause security or stability issues.
+notification.softblocked.link=More Information
+#LOCALIZATION NOTE (notification.outdated) %1$S is the add-on name
+notification.outdated=An important update is available for %1$S.
+notification.outdated.link=Update Now
+#LOCALIZATION NOTE (notification.vulnerableUpdatable) %1$S is the add-on name
+notification.vulnerableUpdatable=%1$S is known to be vulnerable and should be updated.
+notification.vulnerableUpdatable.link=Update Now
+#LOCALIZATION NOTE (notification.vulnerableNoUpdate) %1$S is the add-on name
+notification.vulnerableNoUpdate=%1$S is known to be vulnerable. Use with caution.
+notification.vulnerableNoUpdate.link=More Information
+#LOCALIZATION NOTE (notification.enable) %1$S is the add-on name, %2$S is brand name
+notification.enable=%1$S will be enabled after you restart %2$S.
+#LOCALIZATION NOTE (notification.disable) %1$S is the add-on name, %2$S is brand name
+notification.disable=%1$S will be disabled after you restart %2$S.
+#LOCALIZATION NOTE (notification.install) %1$S is the add-on name, %2$S is brand name
+notification.install=%1$S will be installed after you restart %2$S.
+#LOCALIZATION NOTE (notification.uninstall) %1$S is the add-on name, %2$S is brand name
+notification.uninstall=%1$S will be uninstalled after you restart %2$S.
+#LOCALIZATION NOTE (notification.upgrade) %1$S is the add-on name, %2$S is brand name
+notification.upgrade=%1$S will be updated after you restart %2$S.
+#LOCALIZATION NOTE (notification.downloadError) %1$S is the add-on name.
+notification.downloadError=There was an error downloading %1$S.
+notification.downloadError.retry=Try again
+notification.downloadError.retry.tooltip=Try downloading this add-on again
+#LOCALIZATION NOTE (notification.installError) %1$S is the add-on name.
+notification.installError=There was an error installing %1$S.
+notification.installError.retry=Try again
+notification.installError.retry.tooltip=Try downloading and installing this add-on again
+#LOCALIZATION NOTE (notification.gmpPending) %1$S is the add-on name.
+notification.gmpPending=%1$S will be installed shortly.
+
+#LOCALIZATION NOTE (contributionAmount2) %S is the currency amount recommended for contributions
+contributionAmount2=Suggested Contribution: %S
+
+installDownloading=Downloading
+installDownloaded=Downloaded
+installDownloadFailed=Error downloading
+installVerifying=Verifying
+installInstalling=Installing
+installEnablePending=Restart to enable
+installDisablePending=Restart to disable
+installFailed=Error installing
+installCancelled=Install cancelled
+
+#LOCALIZATION NOTE (details.notification.incompatible) %1$S is the add-on name, %2$S is brand name, %3$S is application version
+details.notification.incompatible=%1$S is incompatible with %2$S %3$S.
+#LOCALIZATION NOTE (details.notification.blocked) %1$S is the add-on name
+details.notification.blocked=%1$S has been disabled due to security or stability issues.
+details.notification.blocked.link=More Information
+#LOCALIZATION NOTE (details.notification.softblocked) %1$S is the add-on name
+details.notification.softblocked=%1$S is known to cause security or stability issues.
+details.notification.softblocked.link=More Information
+#LOCALIZATION NOTE (details.notification.outdated) %1$S is the add-on name
+details.notification.outdated=An important update is available for %1$S.
+details.notification.outdated.link=Update Now
+#LOCALIZATION NOTE (details.notification.vulnerableUpdatable) %1$S is the add-on name
+details.notification.vulnerableUpdatable=%1$S is known to be vulnerable and should be updated.
+details.notification.vulnerableUpdatable.link=Update Now
+#LOCALIZATION NOTE (details.notification.vulnerableNoUpdate) %1$S is the add-on name
+details.notification.vulnerableNoUpdate=%1$S is known to be vulnerable. Use with caution.
+details.notification.vulnerableNoUpdate.link=More Information
+#LOCALIZATION NOTE (details.notification.enable) %1$S is the add-on name, %2$S is brand name
+details.notification.enable=%1$S will be enabled after you restart %2$S.
+#LOCALIZATION NOTE (details.notification.disable) %1$S is the add-on name, %2$S is brand name
+details.notification.disable=%1$S will be disabled after you restart %2$S.
+#LOCALIZATION NOTE (details.notification.install) %1$S is the add-on name, %2$S is brand name
+details.notification.install=%1$S will be installed after you restart %2$S.
+#LOCALIZATION NOTE (details.notification.uninstall) %1$S is the add-on name, %2$S is brand name
+details.notification.uninstall=%1$S will be uninstalled after you restart %2$S.
+#LOCALIZATION NOTE (details.notification.upgrade) %1$S is the add-on name, %2$S is brand name
+details.notification.upgrade=%1$S will be updated after you restart %2$S.
+#LOCALIZATION NOTE (details.notification.gmpPending) %1$S is the add-on name
+details.notification.gmpPending=%1$S will be installed shortly.
+
+# LOCALIZATION NOTE (details.experiment.time.daysRemaining):
+# Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of days from now that the experiment will remain active (detail view).
+details.experiment.time.daysRemaining=#1 day remaining;#1 days remaining
+#LOCALIZATION NOTE (details.experiment.time.endsToday) The experiment will end in less than a day (detail view).
+details.experiment.time.endsToday=Less than a day remaining
+# LOCALIZATION NOTE (details.experiment.time.daysPassed):
+# Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of days since the experiment ran (detail view).
+details.experiment.time.daysPassed=#1 day ago;#1 days ago
+#LOCALIZATION NOTE (details.experiment.time.endedToday) The experiment ended less than a day ago (detail view).
+details.experiment.time.endedToday=Less than a day ago
+#LOCALIZATION NOTE (details.experiment.state.active) This experiment is active (detail view).
+details.experiment.state.active=Active
+#LOCALIZATION NOTE (details.experiment.state.complete) This experiment is complete (it was previously active) (detail view).
+details.experiment.state.complete=Complete
+
+# LOCALIZATION NOTE (experiment.time.daysRemaining):
+# Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of days from now that the experiment will remain active (list view item).
+experiment.time.daysRemaining=#1 day remaining;#1 days remaining
+#LOCALIZATION NOTE (experiment.time.endsToday) The experiment will end in less than a day (list view item).
+experiment.time.endsToday=Less than a day remaining
+# LOCALIZATION NOTE (experiment.time.daysPassed):
+# Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of days since the experiment ran (list view item).
+experiment.time.daysPassed=#1 day ago;#1 days ago
+#LOCALIZATION NOTE (experiment.time.endedToday) The experiment ended less than a day ago (list view item).
+experiment.time.endedToday=Less than a day ago
+#LOCALIZATION NOTE (experiment.state.active) This experiment is active (list view item).
+experiment.state.active=Active
+#LOCALIZATION NOTE (experiment.state.complete) This experiment is complete (it was previously active) (list view item).
+experiment.state.complete=Complete
+
+installFromFile.dialogTitle=Select add-on to install
+installFromFile.filterName=Add-ons
+
+uninstallAddonTooltip=Uninstall this add-on
+uninstallAddonRestartRequiredTooltip=Uninstall this add-on (restart required)
+enableAddonTooltip=Enable this add-on
+enableAddonRestartRequiredTooltip=Enable this add-on (restart required)
+disableAddonTooltip=Disable this add-on
+disableAddonRestartRequiredTooltip=Disable this add-on (restart required)
+
+#LOCALIZATION NOTE (showAllSearchResults): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the total number of search results
+showAllSearchResults=See one result;See all #1 results
+
+#LOCALIZATION NOTE (addon.purchase.label) displayed on a button in the list
+# view, %S is the price of the add-on including currency symbol
+addon.purchase.label=Purchase for %S…
+addon.purchase.tooltip=Visit the add-ons gallery to purchase this add-on
+#LOCALIZATION NOTE (cmd.purchaseAddon.label) displayed on a button in the detail
+# view, %S is the price of the add-on including currency symbol
+cmd.purchaseAddon.label=Purchase for %S…
+cmd.purchaseAddon.accesskey=u
+
+#LOCALIZATION NOTE (eulaHeader) %S is name of the add-on asking the user to agree to the EULA
+eulaHeader=%S requires that you accept the following End User License Agreement before installation can proceed:
+
+type.extension.name=Extensions
+type.theme.name=Appearance
+type.locale.name=Languages
+type.plugin.name=Plugins
+type.dictionary.name=Dictionaries
+type.service.name=Services
+type.experiment.name=Experiments
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd
new file mode 100644
index 000000000..1307cebb9
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.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 title "Install Add-on">
+<!ENTITY intro "Another program on your computer would like to modify
+ &brandShortName; with the following add-on:">
+<!ENTITY warning "Install add-ons only from authors whom you trust.">
+<!ENTITY allow "Allow this installation">
+<!ENTITY later "You can always change your mind at any time by going
+ to the Add-ons Manager.">
+<!ENTITY continue "Continue">
+<!ENTITY restartMessage "You must restart &brandShortName; to finish installing this add-on.">
+<!ENTITY restartButton "Restart &brandShortName;">
+<!ENTITY cancelButton "Cancel">
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties
new file mode 100644
index 000000000..bd5997a26
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.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/.
+
+#LOCALIZATION NOTE (name) %1$S is the add-on name, %2$S is the add-on version
+name=%1$S %2$S
+#LOCALIZATION NOTE (author) %S is the author of the add-on
+author=By %S
+#LOCALIZATION NOTE (location) %S is the path the add-on is installed in
+location=Location: %S
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.dtd
new file mode 100644
index 000000000..2f6f1cd57
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.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/. -->
+
+<!ENTITY upgrade.style "width: 93ch; height: 448px;">
+
+<!ENTITY checking.heading "Checking Your Add-ons">
+<!ENTITY checking.progress.label "Checking your add-ons for compatibility with this version of &brandShortName;.">
+
+<!ENTITY select.heading "Select Your Add-ons">
+<!-- LOCALIZATION NOTE (select.description): The term used for "third parties"
+ here should match the string source.other in selectAddons.properties. -->
+<!ENTITY select.description "Make &brandShortName; even faster by disabling add-ons you no longer use. Add-ons already installed by third parties will be disabled automatically unless you select them below.">
+<!ENTITY select.keep "Keep">
+<!-- LOCALIZATION NOTE (select.keep.style): Should be a width wide enough for
+ the string in select.keep above. -->
+<!ENTITY select.keep.style "width: 6ch;">
+<!ENTITY select.action "Action">
+<!-- LOCALIZATION NOTE (select.action.style): Should be a width wide enough for
+ the action strings in selectAddons.properties or brandShortName. -->
+<!ENTITY select.action.style "width: 35ch;">
+<!ENTITY select.source "Installed By">
+<!ENTITY select.name "Name">
+<!-- LOCALIZATION NOTE (select.name.style): Should be a width small enough so
+ the source column still has enough room for the source strings in
+ selectAddons.properties. -->
+<!ENTITY select.name.style "width: 33ch;">
+
+<!ENTITY confirm.heading "Select Your Add-ons">
+<!-- LOCALIZATION NOTE (confirm.description): The term used for "third parties"
+ here should match the string source.other in selectAddons.properties. -->
+<!ENTITY confirm.description "Make &brandShortName; even faster by disabling add-ons you no longer use. Add-ons already installed by third parties will be disabled automatically unless you select them below.">
+
+<!ENTITY action.disable.heading "The following add-ons will be disabled:">
+<!ENTITY action.incompatible.heading "The following add-ons are disabled, but will be enabled as soon as they are compatible:">
+<!ENTITY action.update.heading "The following add-ons will be updated:">
+<!ENTITY action.enable.heading "The following add-ons will be enabled:">
+
+<!ENTITY update.heading "Updating Your Add-ons">
+<!ENTITY update.progress.label "Downloading and installing updates for your selected add-ons.">
+
+<!ENTITY errors.heading "&brandShortName; could not update some of your add-ons.">
+<!ENTITY errors.description "Installing updates for some of your add-ons failed. &brandShortName; will automatically try to update them again later.">
+
+<!ENTITY footer.label "You can always change your add-ons by going to the Add-ons Manager.">
+<!ENTITY cancel.label "Cancel">
+<!ENTITY back.label "Back">
+<!ENTITY next.label "Next">
+<!ENTITY done.label "Done">
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.properties b/toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.properties
new file mode 100644
index 000000000..2824758d6
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/selectAddons.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 (source.profile) add-ons installed by the user, this may be
+# translated as "You" or "User" depending on the locale
+source.profile=You
+#LOCALIZATION NOTE (source.bundled) add-ons shipped with the application, and thus
+# treated as installed by the user. This may be
+# translated as "You" or "User" depending on the locale
+source.bundled=You (Bundled)
+#LOCALIZATION NOTE (source.other) add-ons installed by other applications
+# installed on the computer
+source.other=Third Party
+
+action.enabled=Will be enabled
+action.disabled=Will be disabled
+action.autoupdate=Will be updated to be compatible
+action.incompatible=Will be enabled when compatible
+action.neededupdate=Update to make compatible
+action.unneededupdate=Optional update
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/update.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/update.dtd
new file mode 100644
index 000000000..6c820e088
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/update.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/. -->
+
+<!ENTITY updateWizard.title "&brandShortName; Update">
+
+<!ENTITY offline.title "&brandShortName; is working offline">
+<!ENTITY offline.description "&brandShortName; needs to go online in order to see if updates
+ are available for your add-ons to make them compatible with this
+ version.">
+<!ENTITY offline.toggleOffline.label "Go online now.">
+<!ENTITY offline.toggleOffline.accesskey "G">
+
+<!ENTITY mismatch.win.title "Incompatible Add-ons">
+<!ENTITY mismatch.top.label "The following add-ons are not compatible with this version of
+ &brandShortName; and have been disabled:">
+<!ENTITY mismatch.bottom.label "&brandShortName; can check if there are compatible versions
+ of these add-ons available.">
+
+<!ENTITY checking.wizard.title "Checking for Compatible Add-ons">
+<!ENTITY checking.top.label "Checking your incompatible add-ons for updates…">
+<!ENTITY checking.status "This may take a few minutes…">
+
+<!ENTITY found.wizard.title "Found Compatible Add-ons">
+<!ENTITY found.top.label "Select the add-ons you would like to install:">
+<!ENTITY found.disabledXPinstall.label "These updates can't be installed because software installation is currently
+ disabled. You can change this setting below.">
+<!ENTITY found.enableXPInstall.label "Allow websites to install software">
+<!ENTITY found.enableXPInstall.accesskey "A">
+
+<!ENTITY installing.wizard.title "Installing Compatible Add-ons">
+<!ENTITY installing.top.label "Downloading and installing updates to your add-ons…">
+
+<!ENTITY noupdates.wizard.title "No Compatible Add-ons Found">
+<!ENTITY noupdates.intro.desc "&brandShortName; was unable to find updates to your
+ incompatible add-ons.">
+<!ENTITY noupdates.error.desc "Some problems were encountered when trying to find updates.">
+<!ENTITY noupdates.checkEnabled.desc "&brandShortName; will check periodically and inform you
+ when compatible updates for these add-ons are found.">
+
+<!ENTITY finished.wizard.title "Compatible Add-ons Installed">
+<!ENTITY finished.top.label "&brandShortName; has installed the updates to your add-ons.">
+<!ENTITY finished.checkDisabled.desc "&brandShortName; can check periodically and inform you
+ when updates for add-ons are found.">
+<!ENTITY finished.checkEnabled.desc "&brandShortName; will check periodically and inform you
+ when updates for add-ons are found.">
+
+<!ENTITY adminDisabled.wizard.title "Unable to Check for Updates">
+<!ENTITY adminDisabled.warning.label "It is not possible to check for updates to incompatible add-ons
+ because software installation for &brandShortName; has been disabled.
+ Please contact your System Administrator for assistance.">
+
+<!ENTITY versioninfo.wizard.title "Checking Compatibility of Add-ons">
+<!ENTITY versioninfo.top.label "Checking your add-ons for compatibility with this
+ version of &brandShortName;.">
+<!ENTITY versioninfo.waiting "This may take a few minutes…">
+
+<!ENTITY installerrors.wizard.title "Problems Installing Updates">
+<!ENTITY installerrors.intro.label "&brandShortName; encountered problems when updating
+ some of your add-ons.">
+
+<!-- general strings used by several of the finish pages -->
+<!ENTITY clickFinish.label "Click Finish to continue starting &brandShortName;.">
+<!ENTITY clickFinish.labelMac "Click Done to continue starting &brandShortName;.">
+<!ENTITY enableChecking.label "Allow &brandShortName; to check for updates.">
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/update.properties b/toolkit/locales/en-US/chrome/mozapps/extensions/update.properties
new file mode 100644
index 000000000..7cf79f9c1
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/update.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/.
+
+mismatchCheckNow=Check Now
+mismatchCheckNowAccesskey=C
+mismatchDontCheck=Don't Check
+mismatchDontCheckAccesskey=D
+installButtonText=Install Now
+installButtonTextAccesskey=I
+nextButtonText=Next >
+nextButtonTextAccesskey=N
+cancelButtonText=Cancel
+cancelButtonTextAccesskey=C
+statusPrefix=Finished checking %S
+downloadingPrefix=Downloading: %S
+installingPrefix=Installing: %S
+closeButton=Close
+installErrors=%S was unable to install updates for the following add-ons:
+checkingErrors=%S was unable to check for updates for the following add-ons:
+installErrorItemFormat=%S (%S)
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.dtd
new file mode 100644
index 000000000..6a7d17a16
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.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/. -->
+
+<!-- extracted from institems.xul -->
+
+<!ENTITY dialog.title "Software Installation">
+<!ENTITY dialog.style "width: 45em">
+<!ENTITY warningPrimary.label "Install add-ons only from authors whom you trust.">
+<!ENTITY warningSecondary.label "Malicious software can damage your computer or violate your privacy.">
+
+<!ENTITY from.label "from:">
+
diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.properties b/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.properties
new file mode 100644
index 000000000..b538e9e90
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.properties
@@ -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/.
+
+unverified=(Author not verified)
+signed=(%S)
+
+itemWarnIntroMultiple=You have asked to install the following %S items:
+itemWarnIntroSingle=You have asked to install the following item:
+installButtonDisabledLabel=Install (%S)
+installButtonLabel=Install Now
+
+installComplete=Software Installation is complete. You will have to restart %S for changes to take effect.
+installCompleteTitle=Installation Complete
+
+error-203=Error Installing Item
diff --git a/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd b/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd
index e8dba6714..c2d49d683 100644
--- a/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd
+++ b/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd
@@ -2,10 +2,21 @@
- License, v. 2.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 (unsupportedPlatform.pre): Mobile only. Flash (the only plugin available on mobile)
+ is not supported on some devices. Include a trailing space as needed. -->
+<!ENTITY unsupportedPlatform.pre "We're very sorry, but &brandShortName; can't play Flash on this device. ">
+<!-- LOCALIZATION NOTE (unsupportedPlatform.learnMore): Mobile only. This text is used to link to a SUMO page explaining why Flash is not
+ supported on this device. Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale. -->
+<!ENTITY unsupportedPlatform.learnMore "Learn More…">
+<!-- LOCALIZATION NOTE (unsupportedPlatform.post): Mobile only. Include text here if needed for your locale. -->
+<!ENTITY unsupportedPlatform.post "">
+
+<!ENTITY missingPlugin "A plugin is needed to display this content.">
<!-- LOCALIZATION NOTE (tapToPlayPlugin): Mobile (used for touch interfaces) only has one type of plugin possible. -->
<!ENTITY tapToPlayPlugin "Tap here to activate plugin.">
<!ENTITY clickToActivatePlugin "Activate plugin.">
<!ENTITY checkForUpdates "Check for updates…">
+<!ENTITY disabledPlugin "This plugin is disabled.">
<!ENTITY blockedPlugin.label "This plugin has been blocked for your protection.">
<!ENTITY hidePluginBtn.label "Hide plugin">
<!ENTITY managePlugins "Manage plugins…">
diff --git a/toolkit/locales/jar.mn b/toolkit/locales/jar.mn
index e42f0de00..e49e978f5 100644
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -116,6 +116,17 @@
locale/@AB_CD@/mozapps/extensions/newaddon.dtd (%chrome/mozapps/webextensions/newaddon.dtd)
locale/@AB_CD@/mozapps/extensions/newaddon.properties (%chrome/mozapps/webextensions/newaddon.properties)
#endif
+#else
+ locale/@AB_CD@/mozapps/extensions/extensions.dtd (%chrome/mozapps/extensions/extensions.dtd)
+ locale/@AB_CD@/mozapps/extensions/extensions.properties (%chrome/mozapps/extensions/extensions.properties)
+ locale/@AB_CD@/mozapps/extensions/blocklist.dtd (%chrome/mozapps/extensions/blocklist.dtd)
+ locale/@AB_CD@/mozapps/extensions/about.dtd (%chrome/mozapps/extensions/about.dtd)
+ locale/@AB_CD@/mozapps/extensions/selectAddons.dtd (%chrome/mozapps/extensions/selectAddons.dtd)
+ locale/@AB_CD@/mozapps/extensions/selectAddons.properties (%chrome/mozapps/extensions/selectAddons.properties)
+ locale/@AB_CD@/mozapps/extensions/update.dtd (%chrome/mozapps/extensions/update.dtd)
+ locale/@AB_CD@/mozapps/extensions/update.properties (%chrome/mozapps/extensions/update.properties)
+ locale/@AB_CD@/mozapps/extensions/newaddon.dtd (%chrome/mozapps/extensions/newaddon.dtd)
+ locale/@AB_CD@/mozapps/extensions/newaddon.properties (%chrome/mozapps/extensions/newaddon.properties)
#endif
locale/@AB_CD@/mozapps/handling/handling.dtd (%chrome/mozapps/handling/handling.dtd)
locale/@AB_CD@/mozapps/handling/handling.properties (%chrome/mozapps/handling/handling.properties)
@@ -133,6 +144,9 @@
#ifdef MOZ_WEBEXTENSIONS
locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.dtd (%chrome/mozapps/webextensions/xpinstallConfirm.dtd)
locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.properties (%chrome/mozapps/webextensions/xpinstallConfirm.properties)
+#else
+ locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.dtd (%chrome/mozapps/extensions/xpinstallConfirm.dtd)
+ locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.properties (%chrome/mozapps/extensions/xpinstallConfirm.properties)
#endif
% locale pluginproblem @AB_CD@ %locale/@AB_CD@/pluginproblem/
locale/@AB_CD@/pluginproblem/pluginproblem.dtd (%chrome/pluginproblem/pluginproblem.dtd)
diff --git a/toolkit/locales/l10n.mk b/toolkit/locales/l10n.mk
index c3d47d163..34d78d33c 100644
--- a/toolkit/locales/l10n.mk
+++ b/toolkit/locales/l10n.mk
@@ -112,10 +112,6 @@ repackage-zip: UNPACKAGE='$(ZIP_IN)'
repackage-zip: libs-$(AB_CD)
# call a hook for apps to put their uninstall helper.exe into the package
$(UNINSTALLER_PACKAGE_HOOK)
-# call a hook for apps to build the stub installer
-ifdef MOZ_STUB_INSTALLER
- $(STUB_HOOK)
-endif
$(PYTHON) $(MOZILLA_DIR)/toolkit/mozapps/installer/l10n-repack.py $(STAGEDIST) $(DIST)/xpi-stage/locale-$(AB_CD) \
$(MOZ_PKG_EXTRAL10N) \
$(if $(filter omni,$(MOZ_PACKAGER_FORMAT)),$(if $(NON_OMNIJAR_FILES),--non-resource $(NON_OMNIJAR_FILES)))
diff --git a/toolkit/modules/UpdateChannel.jsm b/toolkit/modules/UpdateChannel.jsm
new file mode 100644
index 000000000..c2bdce8ad
--- /dev/null
+++ b/toolkit/modules/UpdateChannel.jsm
@@ -0,0 +1,47 @@
+#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/. */
+
+this.EXPORTED_SYMBOLS = ["UpdateChannel"];
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+this.UpdateChannel = {
+ /**
+ * Read the update channel from defaults only. We do this to ensure that
+ * the channel is tightly coupled with the application and does not apply
+ * to other instances of the application that may use the same profile.
+ *
+ * @param [optional] aIncludePartners
+ * Whether or not to include the partner bits. Default: true.
+ */
+ get: function UpdateChannel_get(aIncludePartners = true) {
+ let channel = "@MOZ_UPDATE_CHANNEL@";
+ let defaults = Services.prefs.getDefaultBranch(null);
+ try {
+ channel = defaults.getCharPref("app.update.channel");
+ } catch (e) {
+ // use default value when pref not found
+ }
+
+ if (aIncludePartners) {
+ try {
+ let partners = Services.prefs.getChildList("app.partner.").sort();
+ if (partners.length) {
+ channel += "-cck";
+ partners.forEach(function (prefName) {
+ channel += "-" + Services.prefs.getCharPref(prefName);
+ });
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+
+ return channel;
+ }
+};
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
index 271c9b8f7..257741274 100644
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -104,6 +104,9 @@ EXTRA_JS_MODULES += [
EXTRA_JS_MODULES.third_party.jsesc += ['third_party/jsesc/jsesc.js']
EXTRA_JS_MODULES.sessionstore += ['sessionstore/Utils.jsm']
+if not CONFIG['MOZ_WEBEXTENSIONS']:
+ EXTRA_PP_JS_MODULES += ['UpdateChannel.jsm']
+
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
diff --git a/toolkit/moz.build b/toolkit/moz.build
index 9501938ae..9444a5179 100644
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -9,7 +9,6 @@ DIRS += [
'content',
'forgetaboutsite',
'identity',
- 'jetpack',
'locales',
'modules',
'mozapps/downloads',
@@ -21,8 +20,13 @@ DIRS += [
'themes',
]
+if CONFIG['MOZ_JETPACK']:
+ DIRS += ['jetpack']
+
if CONFIG['MOZ_WEBEXTENSIONS']:
DIRS += ['mozapps/webextensions']
+else:
+ DIRS += ['mozapps/extensions']
if CONFIG['MOZ_UPDATER'] and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
DIRS += ['mozapps/update']
diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm
new file mode 100644
index 000000000..51030cb56
--- /dev/null
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -0,0 +1,3026 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+// Cannot use Services.appinfo here, or else xpcshell-tests will blow up, as
+// most tests later register different nsIAppInfo implementations, which
+// wouldn't be reflected in Services.appinfo anymore, as the lazy getter
+// underlying it would have been initialized if we used it here.
+if ("@mozilla.org/xre/app-info;1" in Cc) {
+ let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+ if (runtime.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+ // Refuse to run in child processes.
+ throw new Error("You cannot use the AddonManager in child processes!");
+ }
+}
+
+
+const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
+const PREF_DEFAULT_PROVIDERS_ENABLED = "extensions.defaultProviders.enabled";
+const PREF_EM_UPDATE_ENABLED = "extensions.update.enabled";
+const PREF_EM_LAST_APP_VERSION = "extensions.lastAppVersion";
+const PREF_EM_LAST_PLATFORM_VERSION = "extensions.lastPlatformVersion";
+const PREF_EM_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault";
+const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
+const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
+const PREF_EM_UPDATE_BACKGROUND_URL = "extensions.update.background.url";
+const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
+const PREF_APP_UPDATE_AUTO = "app.update.auto";
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const UNKNOWN_XPCOM_ABI = "unknownABI";
+
+const UPDATE_REQUEST_VERSION = 2;
+const CATEGORY_UPDATE_PARAMS = "extension-update-params";
+
+const XMLURI_BLOCKLIST = "http://www.mozilla.org/2006/addons-blocklist";
+
+const KEY_PROFILEDIR = "ProfD";
+const KEY_APPDIR = "XCurProcD";
+const FILE_BLOCKLIST = "blocklist.xml";
+
+const BRANCH_REGEXP = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
+const PREF_EM_CHECK_COMPATIBILITY_BASE = "extensions.checkCompatibility";
+var PREF_EM_CHECK_COMPATIBILITY;
+
+const TOOLKIT_ID = "toolkit@mozilla.org";
+
+const VALID_TYPES_REGEXP = /^[\w\-]+$/;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/AsyncShutdown.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
+ "resource://gre/modules/addons/AddonRepository.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
+ let certUtils = {};
+ Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
+ return certUtils;
+});
+
+
+this.EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
+
+const CATEGORY_PROVIDER_MODULE = "addon-provider-module";
+
+// A list of providers to load by default
+const DEFAULT_PROVIDERS = [
+ "resource://gre/modules/addons/XPIProvider.jsm",
+ "resource://gre/modules/LightweightThemeManager.jsm"
+];
+
+Cu.import("resource://gre/modules/Log.jsm");
+// Configure a logger at the parent 'addons' level to format
+// messages for all the modules under addons.*
+const PARENT_LOGGER_ID = "addons";
+let parentLogger = Log.repository.getLogger(PARENT_LOGGER_ID);
+parentLogger.level = Log.Level.Warn;
+let formatter = new Log.BasicFormatter();
+// Set parent logger (and its children) to append to
+// the Javascript section of the Browser Console
+parentLogger.addAppender(new Log.ConsoleAppender(formatter));
+// Set parent logger (and its children) to
+// also append to standard out
+parentLogger.addAppender(new Log.DumpAppender(formatter));
+
+// Create a new logger (child of 'addons' logger)
+// for use by the Addons Manager
+const LOGGER_ID = "addons.manager";
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+// Provide the ability to enable/disable logging
+// messages at runtime.
+// If the "extensions.logging.enabled" preference is
+// missing or 'false', messages at the WARNING and higher
+// severity should be logged to the JS console and standard error.
+// If "extensions.logging.enabled" is set to 'true', messages
+// at DEBUG and higher should go to JS console and standard error.
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+const UNNAMED_PROVIDER = "<unnamed-provider>";
+function providerName(aProvider) {
+ return aProvider.name || UNNAMED_PROVIDER;
+}
+
+/**
+ * Preference listener which listens for a change in the
+ * "extensions.logging.enabled" preference and changes the logging level of the
+ * parent 'addons' level logger accordingly.
+ */
+var PrefObserver = {
+ init: function PrefObserver_init() {
+ Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
+ Services.obs.addObserver(this, "xpcom-shutdown", false);
+ this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
+ },
+
+ observe: function PrefObserver_observe(aSubject, aTopic, aData) {
+ if (aTopic == "xpcom-shutdown") {
+ Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
+ Services.obs.removeObserver(this, "xpcom-shutdown");
+ }
+ else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
+ let debugLogEnabled = false;
+ try {
+ debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
+ }
+ catch (e) {
+ }
+ if (debugLogEnabled) {
+ parentLogger.level = Log.Level.Debug;
+ }
+ else {
+ parentLogger.level = Log.Level.Warn;
+ }
+ }
+ }
+};
+
+PrefObserver.init();
+
+/**
+ * Calls a callback method consuming any thrown exception. Any parameters after
+ * the callback parameter will be passed to the callback.
+ *
+ * @param aCallback
+ * The callback method to call
+ */
+function safeCall(aCallback, ...aArgs) {
+ try {
+ aCallback.apply(null, aArgs);
+ }
+ catch (e) {
+ logger.warn("Exception calling callback", e);
+ }
+}
+
+/**
+ * Report an exception thrown by a provider API method.
+ */
+function reportProviderError(aProvider, aMethod, aError) {
+ let method = `provider ${providerName(aProvider)}.${aMethod}`;
+ AddonManagerPrivate.recordException("AMI", method, aError);
+ logger.error("Exception calling " + method, aError);
+}
+
+/**
+ * Calls a method on a provider if it exists and consumes any thrown exception.
+ * Any parameters after the aDefault parameter are passed to the provider's method.
+ *
+ * @param aProvider
+ * The provider to call
+ * @param aMethod
+ * The method name to call
+ * @param aDefault
+ * A default return value if the provider does not implement the named
+ * method or throws an error.
+ * @return the return value from the provider, or aDefault if the provider does not
+ * implement method or throws an error
+ */
+function callProvider(aProvider, aMethod, aDefault, ...aArgs) {
+ if (!(aMethod in aProvider))
+ return aDefault;
+
+ try {
+ return aProvider[aMethod].apply(aProvider, aArgs);
+ }
+ catch (e) {
+ reportProviderError(aProvider, aMethod, e);
+ return aDefault;
+ }
+}
+
+/**
+ * Calls a method on a provider if it exists and consumes any thrown exception.
+ * Parameters after aMethod are passed to aProvider.aMethod().
+ * The last parameter must be a callback function.
+ * If the provider does not implement the method, or the method throws, calls
+ * the callback with 'undefined'.
+ *
+ * @param aProvider
+ * The provider to call
+ * @param aMethod
+ * The method name to call
+ */
+function callProviderAsync(aProvider, aMethod, ...aArgs) {
+ let callback = aArgs[aArgs.length - 1];
+ if (!(aMethod in aProvider)) {
+ callback(undefined);
+ return;
+ }
+ try {
+ return aProvider[aMethod].apply(aProvider, aArgs);
+ }
+ catch (e) {
+ reportProviderError(aProvider, aMethod, e);
+ callback(undefined);
+ return;
+ }
+}
+
+/**
+ * Gets the currently selected locale for display.
+ * @return the selected locale or "en-US" if none is selected
+ */
+function getLocale() {
+ try {
+ if (Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE))
+ return Services.locale.getLocaleComponentForUserAgent();
+ }
+ catch (e) { }
+
+ try {
+ let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
+ Ci.nsIPrefLocalizedString);
+ if (locale)
+ return locale;
+ }
+ catch (e) { }
+
+ try {
+ return Services.prefs.getCharPref(PREF_SELECTED_LOCALE);
+ }
+ catch (e) { }
+
+ return "en-US";
+}
+
+/**
+ * Previously the APIs for installing add-ons from webpages accepted nsIURI
+ * arguments for the installing page. They now take an nsIPrincipal but for now
+ * maintain backwards compatibility by converting an nsIURI to an nsIPrincipal.
+ *
+ * @param aPrincipalOrURI
+ * The argument passed to the API function. Can be null, an nsIURI or
+ * an nsIPrincipal.
+ * @return an nsIPrincipal.
+ */
+function ensurePrincipal(principalOrURI) {
+ if (principalOrURI instanceof Ci.nsIPrincipal)
+ return principalOrURI;
+
+ logger.warn("Deprecated API call, please pass a non-null nsIPrincipal instead of an nsIURI");
+
+ // Previously a null installing URI meant allowing the install regardless.
+ if (!principalOrURI) {
+ return Services.scriptSecurityManager.getSystemPrincipal();
+ }
+
+ if (principalOrURI instanceof Ci.nsIURI) {
+ return Services.scriptSecurityManager.createCodebasePrincipal(principalOrURI, {
+ inBrowser: true
+ });
+ }
+
+ // Just return whatever we have, the API method will log an error about it.
+ return principalOrURI;
+}
+
+/**
+ * A helper class to repeatedly call a listener with each object in an array
+ * optionally checking whether the object has a method in it.
+ *
+ * @param aObjects
+ * The array of objects to iterate through
+ * @param aMethod
+ * An optional method name, if not null any objects without this method
+ * will not be passed to the listener
+ * @param aListener
+ * A listener implementing nextObject and noMoreObjects methods. The
+ * former will be called with the AsyncObjectCaller as the first
+ * parameter and the object as the second. noMoreObjects will be passed
+ * just the AsyncObjectCaller
+ */
+function AsyncObjectCaller(aObjects, aMethod, aListener) {
+ this.objects = [...aObjects];
+ this.method = aMethod;
+ this.listener = aListener;
+
+ this.callNext();
+}
+
+AsyncObjectCaller.prototype = {
+ objects: null,
+ method: null,
+ listener: null,
+
+ /**
+ * Passes the next object to the listener or calls noMoreObjects if there
+ * are none left.
+ */
+ callNext: function AOC_callNext() {
+ if (this.objects.length == 0) {
+ this.listener.noMoreObjects(this);
+ return;
+ }
+
+ let object = this.objects.shift();
+ if (!this.method || this.method in object)
+ this.listener.nextObject(this, object);
+ else
+ this.callNext();
+ }
+};
+
+/**
+ * Listens for a browser changing origin and cancels the installs that were
+ * started by it.
+ */
+function BrowserListener(aBrowser, aInstallingPrincipal, aInstalls) {
+ this.browser = aBrowser;
+ this.principal = aInstallingPrincipal;
+ this.installs = aInstalls;
+ this.installCount = aInstalls.length;
+
+ aBrowser.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
+ Services.obs.addObserver(this, "message-manager-disconnect", true);
+
+ for (let install of this.installs)
+ install.addListener(this);
+
+ this.registered = true;
+}
+
+BrowserListener.prototype = {
+ browser: null,
+ installs: null,
+ installCount: null,
+ registered: false,
+
+ unregister: function() {
+ if (!this.registered)
+ return;
+ this.registered = false;
+
+ Services.obs.removeObserver(this, "message-manager-disconnect");
+ // The browser may have already been detached
+ if (this.browser.removeProgressListener)
+ this.browser.removeProgressListener(this);
+
+ for (let install of this.installs)
+ install.removeListener(this);
+ this.installs = null;
+ },
+
+ cancelInstalls: function() {
+ for (let install of this.installs) {
+ try {
+ install.cancel();
+ }
+ catch (e) {
+ // Some installs may have already failed or been cancelled, ignore these
+ }
+ }
+ },
+
+ observe: function(subject, topic, data) {
+ if (subject != this.browser.messageManager)
+ return;
+
+ // The browser's message manager has closed and so the browser is
+ // going away, cancel all installs
+ this.cancelInstalls();
+ },
+
+ onLocationChange: function(webProgress, request, location) {
+ if (this.browser.contentPrincipal && this.principal.subsumes(this.browser.contentPrincipal))
+ return;
+
+ // The browser has navigated to a new origin so cancel all installs
+ this.cancelInstalls();
+ },
+
+ onDownloadCancelled: function(install) {
+ // Don't need to hear more events from this install
+ install.removeListener(this);
+
+ // Once all installs have ended unregister everything
+ if (--this.installCount == 0)
+ this.unregister();
+ },
+
+ onDownloadFailed: function(install) {
+ this.onDownloadCancelled(install);
+ },
+
+ onInstallFailed: function(install) {
+ this.onDownloadCancelled(install);
+ },
+
+ onInstallEnded: function(install) {
+ this.onDownloadCancelled(install);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
+ Ci.nsIWebProgressListener,
+ Ci.nsIObserver])
+};
+
+/**
+ * This represents an author of an add-on (e.g. creator or developer)
+ *
+ * @param aName
+ * The name of the author
+ * @param aURL
+ * The URL of the author's profile page
+ */
+function AddonAuthor(aName, aURL) {
+ this.name = aName;
+ this.url = aURL;
+}
+
+AddonAuthor.prototype = {
+ name: null,
+ url: null,
+
+ // Returns the author's name, defaulting to the empty string
+ toString: function AddonAuthor_toString() {
+ return this.name || "";
+ }
+}
+
+/**
+ * This represents an screenshot for an add-on
+ *
+ * @param aURL
+ * The URL to the full version of the screenshot
+ * @param aWidth
+ * The width in pixels of the screenshot
+ * @param aHeight
+ * The height in pixels of the screenshot
+ * @param aThumbnailURL
+ * The URL to the thumbnail version of the screenshot
+ * @param aThumbnailWidth
+ * The width in pixels of the thumbnail version of the screenshot
+ * @param aThumbnailHeight
+ * The height in pixels of the thumbnail version of the screenshot
+ * @param aCaption
+ * The caption of the screenshot
+ */
+function AddonScreenshot(aURL, aWidth, aHeight, aThumbnailURL,
+ aThumbnailWidth, aThumbnailHeight, aCaption) {
+ this.url = aURL;
+ if (aWidth) this.width = aWidth;
+ if (aHeight) this.height = aHeight;
+ if (aThumbnailURL) this.thumbnailURL = aThumbnailURL;
+ if (aThumbnailWidth) this.thumbnailWidth = aThumbnailWidth;
+ if (aThumbnailHeight) this.thumbnailHeight = aThumbnailHeight;
+ if (aCaption) this.caption = aCaption;
+}
+
+AddonScreenshot.prototype = {
+ url: null,
+ width: null,
+ height: null,
+ thumbnailURL: null,
+ thumbnailWidth: null,
+ thumbnailHeight: null,
+ caption: null,
+
+ // Returns the screenshot URL, defaulting to the empty string
+ toString: function AddonScreenshot_toString() {
+ return this.url || "";
+ }
+}
+
+
+/**
+ * This represents a compatibility override for an addon.
+ *
+ * @param aType
+ * Overrride type - "compatible" or "incompatible"
+ * @param aMinVersion
+ * Minimum version of the addon to match
+ * @param aMaxVersion
+ * Maximum version of the addon to match
+ * @param aAppID
+ * Application ID used to match appMinVersion and appMaxVersion
+ * @param aAppMinVersion
+ * Minimum version of the application to match
+ * @param aAppMaxVersion
+ * Maximum version of the application to match
+ */
+function AddonCompatibilityOverride(aType, aMinVersion, aMaxVersion, aAppID,
+ aAppMinVersion, aAppMaxVersion) {
+ this.type = aType;
+ this.minVersion = aMinVersion;
+ this.maxVersion = aMaxVersion;
+ this.appID = aAppID;
+ this.appMinVersion = aAppMinVersion;
+ this.appMaxVersion = aAppMaxVersion;
+}
+
+AddonCompatibilityOverride.prototype = {
+ /**
+ * Type of override - "incompatible" or "compatible".
+ * Only "incompatible" is supported for now.
+ */
+ type: null,
+
+ /**
+ * Min version of the addon to match.
+ */
+ minVersion: null,
+
+ /**
+ * Max version of the addon to match.
+ */
+ maxVersion: null,
+
+ /**
+ * Application ID to match.
+ */
+ appID: null,
+
+ /**
+ * Min version of the application to match.
+ */
+ appMinVersion: null,
+
+ /**
+ * Max version of the application to match.
+ */
+ appMaxVersion: null
+};
+
+
+/**
+ * A type of add-on, used by the UI to determine how to display different types
+ * of add-ons.
+ *
+ * @param aID
+ * The add-on type ID
+ * @param aLocaleURI
+ * The URI of a localized properties file to get the displayable name
+ * for the type from
+ * @param aLocaleKey
+ * The key for the string in the properties file or the actual display
+ * name if aLocaleURI is null. Include %ID% to include the type ID in
+ * the key
+ * @param aViewType
+ * The optional type of view to use in the UI
+ * @param aUIPriority
+ * The priority is used by the UI to list the types in order. Lower
+ * values push the type higher in the list.
+ * @param aFlags
+ * An option set of flags that customize the display of the add-on in
+ * the UI.
+ */
+function AddonType(aID, aLocaleURI, aLocaleKey, aViewType, aUIPriority, aFlags) {
+ if (!aID)
+ throw Components.Exception("An AddonType must have an ID", Cr.NS_ERROR_INVALID_ARG);
+
+ if (aViewType && aUIPriority === undefined)
+ throw Components.Exception("An AddonType with a defined view must have a set UI priority",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aLocaleKey)
+ throw Components.Exception("An AddonType must have a displayable name",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ this.id = aID;
+ this.uiPriority = aUIPriority;
+ this.viewType = aViewType;
+ this.flags = aFlags;
+
+ if (aLocaleURI) {
+ this.__defineGetter__("name", function nameGetter() {
+ delete this.name;
+ let bundle = Services.strings.createBundle(aLocaleURI);
+ this.name = bundle.GetStringFromName(aLocaleKey.replace("%ID%", aID));
+ return this.name;
+ });
+ }
+ else {
+ this.name = aLocaleKey;
+ }
+}
+
+var gStarted = false;
+var gStartupComplete = false;
+var gCheckCompatibility = true;
+var gStrictCompatibility = true;
+var gCheckUpdateSecurityDefault = true;
+var gCheckUpdateSecurity = gCheckUpdateSecurityDefault;
+var gUpdateEnabled = true;
+var gAutoUpdateDefault = true;
+var gShutdownBarrier = null;
+var gRepoShutdownState = "";
+var gShutdownInProgress = false;
+
+/**
+ * This is the real manager, kept here rather than in AddonManager to keep its
+ * contents hidden from API users.
+ */
+var AddonManagerInternal = {
+ managerListeners: [],
+ installListeners: [],
+ addonListeners: [],
+ typeListeners: [],
+ pendingProviders: new Set(),
+ providers: new Set(),
+ providerShutdowns: new Map(),
+ types: {},
+ startupChanges: {},
+ // Store telemetry details per addon provider
+ telemetryDetails: {},
+
+ recordTimestamp: function AMI_recordTimestamp(name, value) {
+ this.TelemetryTimestamps.add(name, value);
+ },
+
+ validateBlocklist: function AMI_validateBlocklist() {
+ let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+
+ // If there is no application shipped blocklist then there is nothing to do
+ if (!appBlocklist.exists())
+ return;
+
+ let profileBlocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+
+ // If there is no blocklist in the profile then copy the application shipped
+ // one there
+ if (!profileBlocklist.exists()) {
+ try {
+ appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
+ }
+ catch (e) {
+ logger.warn("Failed to copy the application shipped blocklist to the profile", e);
+ }
+ return;
+ }
+
+ let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ try {
+ let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Ci.nsIConverterInputStream);
+ fileStream.init(appBlocklist, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+ cstream.init(fileStream, "UTF-8", 0, 0);
+
+ let data = "";
+ let str = {};
+ let read = 0;
+ do {
+ read = cstream.readString(0xffffffff, str);
+ data += str.value;
+ } while (read != 0);
+
+ let parser = Cc["@mozilla.org/xmlextras/domparser;1"].
+ createInstance(Ci.nsIDOMParser);
+ var doc = parser.parseFromString(data, "text/xml");
+ }
+ catch (e) {
+ logger.warn("Application shipped blocklist could not be loaded", e);
+ return;
+ }
+ finally {
+ try {
+ fileStream.close();
+ }
+ catch (e) {
+ logger.warn("Unable to close blocklist file stream", e);
+ }
+ }
+
+ // If the namespace is incorrect then ignore the application shipped
+ // blocklist
+ if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
+ logger.warn("Application shipped blocklist has an unexpected namespace (" +
+ doc.documentElement.namespaceURI + ")");
+ return;
+ }
+
+ // If there is no lastupdate information then ignore the application shipped
+ // blocklist
+ if (!doc.documentElement.hasAttribute("lastupdate"))
+ return;
+
+ // If the application shipped blocklist is older than the profile blocklist
+ // then do nothing
+ if (doc.documentElement.getAttribute("lastupdate") <=
+ profileBlocklist.lastModifiedTime)
+ return;
+
+ // Otherwise copy the application shipped blocklist to the profile
+ try {
+ appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
+ }
+ catch (e) {
+ logger.warn("Failed to copy the application shipped blocklist to the profile", e);
+ }
+ },
+
+ /**
+ * Start up a provider, and register its shutdown hook if it has one
+ */
+ _startProvider(aProvider, aAppChanged, aOldAppVersion, aOldPlatformVersion) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ logger.debug(`Starting provider: ${providerName(aProvider)}`);
+ callProvider(aProvider, "startup", null, aAppChanged, aOldAppVersion, aOldPlatformVersion);
+ if ('shutdown' in aProvider) {
+ let name = providerName(aProvider);
+ let AMProviderShutdown = () => {
+ // If the provider has been unregistered, it will have been removed from
+ // this.providers. If it hasn't been unregistered, then this is a normal
+ // shutdown - and we move it to this.pendingProviders incase we're
+ // running in a test that will start AddonManager again.
+ if (this.providers.has(aProvider)) {
+ this.providers.delete(aProvider);
+ this.pendingProviders.add(aProvider);
+ }
+
+ return new Promise((resolve, reject) => {
+ logger.debug("Calling shutdown blocker for " + name);
+ resolve(aProvider.shutdown());
+ })
+ .catch(err => {
+ logger.warn("Failure during shutdown of " + name, err);
+ AddonManagerPrivate.recordException("AMI", "Async shutdown of " + name, err);
+ });
+ };
+ logger.debug("Registering shutdown blocker for " + name);
+ this.providerShutdowns.set(aProvider, AMProviderShutdown);
+ AddonManager.shutdown.addBlocker(name, AMProviderShutdown);
+ }
+
+ this.pendingProviders.delete(aProvider);
+ this.providers.add(aProvider);
+ logger.debug(`Provider finished startup: ${providerName(aProvider)}`);
+ },
+
+ /**
+ * Initializes the AddonManager, loading any known providers and initializing
+ * them.
+ */
+ startup: function AMI_startup() {
+ try {
+ if (gStarted)
+ return;
+
+ this.recordTimestamp("AMI_startup_begin");
+
+ // clear this for xpcshell test restarts
+ for (let provider in this.telemetryDetails)
+ delete this.telemetryDetails[provider];
+
+ let appChanged = undefined;
+
+ let oldAppVersion = null;
+ try {
+ oldAppVersion = Services.prefs.getCharPref(PREF_EM_LAST_APP_VERSION);
+ appChanged = Services.appinfo.version != oldAppVersion;
+ }
+ catch (e) { }
+
+ let oldPlatformVersion = null;
+ try {
+ oldPlatformVersion = Services.prefs.getCharPref(PREF_EM_LAST_PLATFORM_VERSION);
+ }
+ catch (e) { }
+
+ if (appChanged !== false) {
+ logger.debug("Application has been upgraded");
+ Services.prefs.setCharPref(PREF_EM_LAST_APP_VERSION,
+ Services.appinfo.version);
+ Services.prefs.setCharPref(PREF_EM_LAST_PLATFORM_VERSION,
+ Services.appinfo.platformVersion);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION,
+ (appChanged === undefined ? 0 : -1));
+ this.validateBlocklist();
+ }
+
+ PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
+ Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
+
+ try {
+ gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
+ } catch (e) {}
+ Services.prefs.addObserver(PREF_EM_CHECK_COMPATIBILITY, this, false);
+
+ try {
+ gStrictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
+ } catch (e) {}
+ Services.prefs.addObserver(PREF_EM_STRICT_COMPATIBILITY, this, false);
+
+ try {
+ let defaultBranch = Services.prefs.getDefaultBranch("");
+ gCheckUpdateSecurityDefault = defaultBranch.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
+ } catch(e) {}
+
+ try {
+ gCheckUpdateSecurity = Services.prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
+ } catch (e) {}
+ Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false);
+
+ try {
+ gUpdateEnabled = Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED);
+ } catch (e) {}
+ Services.prefs.addObserver(PREF_EM_UPDATE_ENABLED, this, false);
+
+ try {
+ gAutoUpdateDefault = Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
+ } catch (e) {}
+ Services.prefs.addObserver(PREF_EM_AUTOUPDATE_DEFAULT, this, false);
+
+ let defaultProvidersEnabled = true;
+ try {
+ defaultProvidersEnabled = Services.prefs.getBoolPref(PREF_DEFAULT_PROVIDERS_ENABLED);
+ } catch (e) {}
+ AddonManagerPrivate.recordSimpleMeasure("default_providers", defaultProvidersEnabled);
+
+ // Ensure all default providers have had a chance to register themselves
+ if (defaultProvidersEnabled) {
+ for (let url of DEFAULT_PROVIDERS) {
+ try {
+ let scope = {};
+ Components.utils.import(url, scope);
+ // Sanity check - make sure the provider exports a symbol that
+ // has a 'startup' method
+ let syms = Object.keys(scope);
+ if ((syms.length < 1) ||
+ (typeof scope[syms[0]].startup != "function")) {
+ logger.warn("Provider " + url + " has no startup()");
+ AddonManagerPrivate.recordException("AMI", "provider " + url, "no startup()");
+ }
+ logger.debug("Loaded provider scope for " + url + ": " + Object.keys(scope).toSource());
+ }
+ catch (e) {
+ AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
+ logger.error("Exception loading default provider \"" + url + "\"", e);
+ }
+ };
+ }
+
+ // Load any providers registered in the category manager
+ let catman = Cc["@mozilla.org/categorymanager;1"].
+ getService(Ci.nsICategoryManager);
+ let entries = catman.enumerateCategory(CATEGORY_PROVIDER_MODULE);
+ while (entries.hasMoreElements()) {
+ let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
+ let url = catman.getCategoryEntry(CATEGORY_PROVIDER_MODULE, entry);
+
+ try {
+ Components.utils.import(url, {});
+ logger.debug(`Loaded provider scope for ${url}`);
+ }
+ catch (e) {
+ AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
+ logger.error("Exception loading provider " + entry + " from category \"" +
+ url + "\"", e);
+ }
+ }
+
+ // Register our shutdown handler with the AsyncShutdown manager
+ gShutdownBarrier = new AsyncShutdown.Barrier("AddonManager: Waiting for providers to shut down.");
+ AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down.",
+ this.shutdownManager.bind(this),
+ {fetchState: this.shutdownState.bind(this)});
+
+ // Once we start calling providers we must allow all normal methods to work.
+ gStarted = true;
+
+ for (let provider of this.pendingProviders) {
+ this._startProvider(provider, appChanged, oldAppVersion, oldPlatformVersion);
+ }
+
+ // If this is a new profile just pretend that there were no changes
+ if (appChanged === undefined) {
+ for (let type in this.startupChanges)
+ delete this.startupChanges[type];
+ }
+
+ gStartupComplete = true;
+ this.recordTimestamp("AMI_startup_end");
+ }
+ catch (e) {
+ logger.error("startup failed", e);
+ AddonManagerPrivate.recordException("AMI", "startup failed", e);
+ }
+
+ logger.debug("Completed startup sequence");
+ this.callManagerListeners("onStartup");
+ },
+
+ /**
+ * Registers a new AddonProvider.
+ *
+ * @param aProvider
+ * The provider to register
+ * @param aTypes
+ * An optional array of add-on types
+ */
+ registerProvider: function AMI_registerProvider(aProvider, aTypes) {
+ if (!aProvider || typeof aProvider != "object")
+ throw Components.Exception("aProvider must be specified",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aTypes && !Array.isArray(aTypes))
+ throw Components.Exception("aTypes must be an array or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ this.pendingProviders.add(aProvider);
+
+ if (aTypes) {
+ aTypes.forEach(function(aType) {
+ if (!(aType.id in this.types)) {
+ if (!VALID_TYPES_REGEXP.test(aType.id)) {
+ logger.warn("Ignoring invalid type " + aType.id);
+ return;
+ }
+
+ this.types[aType.id] = {
+ type: aType,
+ providers: [aProvider]
+ };
+
+ let typeListeners = this.typeListeners.slice(0);
+ for (let listener of typeListeners) {
+ safeCall(function listenerSafeCall() {
+ listener.onTypeAdded(aType);
+ });
+ }
+ }
+ else {
+ this.types[aType.id].providers.push(aProvider);
+ }
+ }, this);
+ }
+
+ // If we're registering after startup call this provider's startup.
+ if (gStarted) {
+ this._startProvider(aProvider);
+ }
+ },
+
+ /**
+ * Unregisters an AddonProvider.
+ *
+ * @param aProvider
+ * The provider to unregister
+ * @return Whatever the provider's 'shutdown' method returns (if anything).
+ * For providers that have async shutdown methods returning Promises,
+ * the caller should wait for that Promise to resolve.
+ */
+ unregisterProvider: function AMI_unregisterProvider(aProvider) {
+ if (!aProvider || typeof aProvider != "object")
+ throw Components.Exception("aProvider must be specified",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ this.providers.delete(aProvider);
+ // The test harness will unregister XPIProvider *after* shutdown, which is
+ // after the provider will have been moved from providers to
+ // pendingProviders.
+ this.pendingProviders.delete(aProvider);
+
+ for (let type in this.types) {
+ this.types[type].providers = this.types[type].providers.filter(function filterProvider(p) p != aProvider);
+ if (this.types[type].providers.length == 0) {
+ let oldType = this.types[type].type;
+ delete this.types[type];
+
+ let typeListeners = this.typeListeners.slice(0);
+ for (let listener of typeListeners) {
+ safeCall(function listenerSafeCall() {
+ listener.onTypeRemoved(oldType);
+ });
+ }
+ }
+ }
+
+ // If we're unregistering after startup but before shutting down,
+ // remove the blocker for this provider's shutdown and call it.
+ // If we're already shutting down, just let gShutdownBarrier call it to avoid races.
+ if (gStarted && !gShutdownInProgress) {
+ logger.debug("Unregistering shutdown blocker for " + providerName(aProvider));
+ let shutter = this.providerShutdowns.get(aProvider);
+ if (shutter) {
+ this.providerShutdowns.delete(aProvider);
+ gShutdownBarrier.client.removeBlocker(shutter);
+ return shutter();
+ }
+ }
+ return undefined;
+ },
+
+ /**
+ * Mark a provider as safe to access via AddonManager APIs, before its
+ * startup has completed.
+ *
+ * Normally a provider isn't marked as safe until after its (synchronous)
+ * startup() method has returned. Until a provider has been marked safe,
+ * it won't be used by any of the AddonManager APIs. markProviderSafe()
+ * allows a provider to mark itself as safe during its startup; this can be
+ * useful if the provider wants to perform tasks that block startup, which
+ * happen after its required initialization tasks and therefore when the
+ * provider is in a safe state.
+ *
+ * @param aProvider Provider object to mark safe
+ */
+ markProviderSafe: function AMI_markProviderSafe(aProvider) {
+ if (!gStarted) {
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+ }
+
+ if (!aProvider || typeof aProvider != "object") {
+ throw Components.Exception("aProvider must be specified",
+ Cr.NS_ERROR_INVALID_ARG);
+ }
+
+ if (!this.pendingProviders.has(aProvider)) {
+ return;
+ }
+
+ this.pendingProviders.delete(aProvider);
+ this.providers.add(aProvider);
+ },
+
+ /**
+ * Calls a method on all registered providers if it exists and consumes any
+ * thrown exception. Return values are ignored. Any parameters after the
+ * method parameter are passed to the provider's method.
+ * WARNING: Do not use for asynchronous calls; callProviders() does not
+ * invoke callbacks if provider methods throw synchronous exceptions.
+ *
+ * @param aMethod
+ * The method name to call
+ * @see callProvider
+ */
+ callProviders: function AMI_callProviders(aMethod, ...aArgs) {
+ if (!aMethod || typeof aMethod != "string")
+ throw Components.Exception("aMethod must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let providers = [...this.providers];
+ for (let provider of providers) {
+ try {
+ if (aMethod in provider)
+ provider[aMethod].apply(provider, aArgs);
+ }
+ catch (e) {
+ reportProviderError(aProvider, aMethod, e);
+ }
+ }
+ },
+
+ /**
+ * Report the current state of asynchronous shutdown
+ */
+ shutdownState() {
+ let state = [];
+ if (gShutdownBarrier) {
+ state.push({
+ name: gShutdownBarrier.client.name,
+ state: gShutdownBarrier.state
+ });
+ }
+ state.push({
+ name: "AddonRepository: async shutdown",
+ state: gRepoShutdownState
+ });
+ return state;
+ },
+
+ /**
+ * Shuts down the addon manager and all registered providers, this must clean
+ * up everything in order for automated tests to fake restarts.
+ * @return Promise{null} that resolves when all providers and dependent modules
+ * have finished shutting down
+ */
+ shutdownManager: Task.async(function* () {
+ logger.debug("shutdown");
+ this.callManagerListeners("onShutdown");
+
+ gRepoShutdownState = "pending";
+ gShutdownInProgress = true;
+ // Clean up listeners
+ Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
+ Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
+ Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
+ Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
+ Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
+
+ let savedError = null;
+ // Only shut down providers if they've been started.
+ if (gStarted) {
+ try {
+ yield gShutdownBarrier.wait();
+ }
+ catch(err) {
+ savedError = err;
+ logger.error("Failure during wait for shutdown barrier", err);
+ AddonManagerPrivate.recordException("AMI", "Async shutdown of AddonManager providers", err);
+ }
+ }
+
+ // Shut down AddonRepository after providers (if any).
+ try {
+ gRepoShutdownState = "in progress";
+ yield AddonRepository.shutdown();
+ gRepoShutdownState = "done";
+ }
+ catch(err) {
+ savedError = err;
+ logger.error("Failure during AddonRepository shutdown", err);
+ AddonManagerPrivate.recordException("AMI", "Async shutdown of AddonRepository", err);
+ }
+
+ logger.debug("Async provider shutdown done");
+ this.managerListeners.splice(0, this.managerListeners.length);
+ this.installListeners.splice(0, this.installListeners.length);
+ this.addonListeners.splice(0, this.addonListeners.length);
+ this.typeListeners.splice(0, this.typeListeners.length);
+ this.providerShutdowns.clear();
+ for (let type in this.startupChanges)
+ delete this.startupChanges[type];
+ gStarted = false;
+ gStartupComplete = false;
+ gShutdownBarrier = null;
+ gShutdownInProgress = false;
+ if (savedError) {
+ throw savedError;
+ }
+ }),
+
+ /**
+ * Notified when a preference we're interested in has changed.
+ *
+ * @see nsIObserver
+ */
+ observe: function AMI_observe(aSubject, aTopic, aData) {
+ switch (aData) {
+ case PREF_EM_CHECK_COMPATIBILITY: {
+ let oldValue = gCheckCompatibility;
+ try {
+ gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
+ } catch(e) {
+ gCheckCompatibility = true;
+ }
+
+ this.callManagerListeners("onCompatibilityModeChanged");
+
+ if (gCheckCompatibility != oldValue)
+ this.updateAddonAppDisabledStates();
+
+ break;
+ }
+ case PREF_EM_STRICT_COMPATIBILITY: {
+ let oldValue = gStrictCompatibility;
+ try {
+ gStrictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
+ } catch(e) {
+ gStrictCompatibility = true;
+ }
+
+ this.callManagerListeners("onCompatibilityModeChanged");
+
+ if (gStrictCompatibility != oldValue)
+ this.updateAddonAppDisabledStates();
+
+ break;
+ }
+ case PREF_EM_CHECK_UPDATE_SECURITY: {
+ let oldValue = gCheckUpdateSecurity;
+ try {
+ gCheckUpdateSecurity = Services.prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
+ } catch(e) {
+ gCheckUpdateSecurity = true;
+ }
+
+ this.callManagerListeners("onCheckUpdateSecurityChanged");
+
+ if (gCheckUpdateSecurity != oldValue)
+ this.updateAddonAppDisabledStates();
+
+ break;
+ }
+ case PREF_EM_UPDATE_ENABLED: {
+ let oldValue = gUpdateEnabled;
+ try {
+ gUpdateEnabled = Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED);
+ } catch(e) {
+ gUpdateEnabled = true;
+ }
+
+ this.callManagerListeners("onUpdateModeChanged");
+ break;
+ }
+ case PREF_EM_AUTOUPDATE_DEFAULT: {
+ let oldValue = gAutoUpdateDefault;
+ try {
+ gAutoUpdateDefault = Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
+ } catch(e) {
+ gAutoUpdateDefault = true;
+ }
+
+ this.callManagerListeners("onUpdateModeChanged");
+ break;
+ }
+ }
+ },
+
+ /**
+ * Replaces %...% strings in an addon url (update and updateInfo) with
+ * appropriate values.
+ *
+ * @param aAddon
+ * The Addon representing the add-on
+ * @param aUri
+ * The string representation of the URI to escape
+ * @param aAppVersion
+ * The optional application version to use for %APP_VERSION%
+ * @return The appropriately escaped URI.
+ */
+ escapeAddonURI: function AMI_escapeAddonURI(aAddon, aUri, aAppVersion)
+ {
+ if (!aAddon || typeof aAddon != "object")
+ throw Components.Exception("aAddon must be an Addon object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aUri || typeof aUri != "string")
+ throw Components.Exception("aUri must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aAppVersion && typeof aAppVersion != "string")
+ throw Components.Exception("aAppVersion must be a string or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ var addonStatus = aAddon.userDisabled || aAddon.softDisabled ? "userDisabled"
+ : "userEnabled";
+
+ if (!aAddon.isCompatible)
+ addonStatus += ",incompatible";
+ if (aAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
+ addonStatus += ",blocklisted";
+ if (aAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
+ addonStatus += ",softblocked";
+
+ try {
+ var xpcomABI = Services.appinfo.XPCOMABI;
+ } catch (ex) {
+ xpcomABI = UNKNOWN_XPCOM_ABI;
+ }
+
+ let uri = aUri.replace(/%ITEM_ID%/g, aAddon.id);
+ uri = uri.replace(/%ITEM_VERSION%/g, aAddon.version);
+ uri = uri.replace(/%ITEM_STATUS%/g, addonStatus);
+ uri = uri.replace(/%APP_ID%/g, Services.appinfo.ID);
+ uri = uri.replace(/%APP_VERSION%/g, aAppVersion ? aAppVersion :
+ Services.appinfo.version);
+ uri = uri.replace(/%REQ_VERSION%/g, UPDATE_REQUEST_VERSION);
+ uri = uri.replace(/%APP_OS%/g, Services.appinfo.OS);
+ uri = uri.replace(/%APP_ABI%/g, xpcomABI);
+ uri = uri.replace(/%APP_LOCALE%/g, getLocale());
+ uri = uri.replace(/%CURRENT_APP_VERSION%/g, Services.appinfo.version);
+
+ // Replace custom parameters (names of custom parameters must have at
+ // least 3 characters to prevent lookups for something like %D0%C8)
+ var catMan = null;
+ uri = uri.replace(/%(\w{3,})%/g, function parameterReplace(aMatch, aParam) {
+ if (!catMan) {
+ catMan = Cc["@mozilla.org/categorymanager;1"].
+ getService(Ci.nsICategoryManager);
+ }
+
+ try {
+ var contractID = catMan.getCategoryEntry(CATEGORY_UPDATE_PARAMS, aParam);
+ var paramHandler = Cc[contractID].getService(Ci.nsIPropertyBag2);
+ return paramHandler.getPropertyAsAString(aParam);
+ }
+ catch(e) {
+ return aMatch;
+ }
+ });
+
+ // escape() does not properly encode + symbols in any embedded FVF strings.
+ return uri.replace(/\+/g, "%2B");
+ },
+
+ /**
+ * Performs a background update check by starting an update for all add-ons
+ * that can be updated.
+ * @return Promise{null} Resolves when the background update check is complete
+ * (the resulting addon installations may still be in progress).
+ */
+ backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ let buPromise = Task.spawn(function* backgroundUpdateTask() {
+ logger.debug("Background update check beginning");
+
+ Services.obs.notifyObservers(null, "addons-background-update-start", null);
+
+ if (this.updateEnabled) {
+ let scope = {};
+ Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
+ scope.LightweightThemeManager.updateCurrentTheme();
+
+ let allAddons = yield new Promise((resolve, reject) => this.getAllAddons(resolve));
+
+ // Repopulate repository cache first, to ensure compatibility overrides
+ // are up to date before checking for addon updates.
+ yield AddonRepository.backgroundUpdateCheck();
+
+ // Keep track of all the async add-on updates happening in parallel
+ let updates = [];
+
+ for (let addon of allAddons) {
+ // Check all add-ons for updates so that any compatibility updates will
+ // be applied
+ updates.push(new Promise((resolve, reject) => {
+ addon.findUpdates({
+ onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
+ // Start installing updates when the add-on can be updated and
+ // background updates should be applied.
+ logger.debug("Found update for add-on ${id}", aAddon);
+ if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
+ AddonManager.shouldAutoUpdate(aAddon)) {
+ // XXX we really should resolve when this install is done,
+ // not when update-available check completes, no?
+ logger.debug("Starting install of ${id}", aAddon);
+ aInstall.install();
+ }
+ },
+
+ onUpdateFinished: aAddon => { logger.debug("onUpdateFinished for ${id}", aAddon); resolve(); }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }));
+ }
+ yield Promise.all(updates);
+ }
+
+ logger.debug("Background update check complete");
+ Services.obs.notifyObservers(null,
+ "addons-background-update-complete",
+ null);
+ }.bind(this));
+ // Fork the promise chain so we can log the error and let our caller see it too.
+ buPromise.then(null, e => logger.warn("Error in background update", e));
+ return buPromise;
+ },
+
+ /**
+ * Adds a add-on to the list of detected changes for this startup. If
+ * addStartupChange is called multiple times for the same add-on in the same
+ * startup then only the most recent change will be remembered.
+ *
+ * @param aType
+ * The type of change as a string. Providers can define their own
+ * types of changes or use the existing defined STARTUP_CHANGE_*
+ * constants
+ * @param aID
+ * The ID of the add-on
+ */
+ addStartupChange: function AMI_addStartupChange(aType, aID) {
+ if (!aType || typeof aType != "string")
+ throw Components.Exception("aType must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aID || typeof aID != "string")
+ throw Components.Exception("aID must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (gStartupComplete)
+ return;
+
+ // Ensure that an ID is only listed in one type of change
+ for (let type in this.startupChanges)
+ this.removeStartupChange(type, aID);
+
+ if (!(aType in this.startupChanges))
+ this.startupChanges[aType] = [];
+ this.startupChanges[aType].push(aID);
+ },
+
+ /**
+ * Removes a startup change for an add-on.
+ *
+ * @param aType
+ * The type of change
+ * @param aID
+ * The ID of the add-on
+ */
+ removeStartupChange: function AMI_removeStartupChange(aType, aID) {
+ if (!aType || typeof aType != "string")
+ throw Components.Exception("aType must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aID || typeof aID != "string")
+ throw Components.Exception("aID must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (gStartupComplete)
+ return;
+
+ if (!(aType in this.startupChanges))
+ return;
+
+ this.startupChanges[aType] = this.startupChanges[aType].filter(
+ function filterItem(aItem) aItem != aID);
+ },
+
+ /**
+ * Calls all registered AddonManagerListeners with an event. Any parameters
+ * after the method parameter are passed to the listener.
+ *
+ * @param aMethod
+ * The method on the listeners to call
+ */
+ callManagerListeners: function AMI_callManagerListeners(aMethod, ...aArgs) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aMethod || typeof aMethod != "string")
+ throw Components.Exception("aMethod must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let managerListeners = this.managerListeners.slice(0);
+ for (let listener of managerListeners) {
+ try {
+ if (aMethod in listener)
+ listener[aMethod].apply(listener, aArgs);
+ }
+ catch (e) {
+ logger.warn("AddonManagerListener threw exception when calling " + aMethod, e);
+ }
+ }
+ },
+
+ /**
+ * Calls all registered InstallListeners with an event. Any parameters after
+ * the extraListeners parameter are passed to the listener.
+ *
+ * @param aMethod
+ * The method on the listeners to call
+ * @param aExtraListeners
+ * An optional array of extra InstallListeners to also call
+ * @return false if any of the listeners returned false, true otherwise
+ */
+ callInstallListeners: function AMI_callInstallListeners(aMethod,
+ aExtraListeners, ...aArgs) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aMethod || typeof aMethod != "string")
+ throw Components.Exception("aMethod must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aExtraListeners && !Array.isArray(aExtraListeners))
+ throw Components.Exception("aExtraListeners must be an array or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let result = true;
+ let listeners;
+ if (aExtraListeners)
+ listeners = aExtraListeners.concat(this.installListeners);
+ else
+ listeners = this.installListeners.slice(0);
+
+ for (let listener of listeners) {
+ try {
+ if (aMethod in listener) {
+ if (listener[aMethod].apply(listener, aArgs) === false)
+ result = false;
+ }
+ }
+ catch (e) {
+ logger.warn("InstallListener threw exception when calling " + aMethod, e);
+ }
+ }
+ return result;
+ },
+
+ /**
+ * Calls all registered AddonListeners with an event. Any parameters after
+ * the method parameter are passed to the listener.
+ *
+ * @param aMethod
+ * The method on the listeners to call
+ */
+ callAddonListeners: function AMI_callAddonListeners(aMethod, ...aArgs) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aMethod || typeof aMethod != "string")
+ throw Components.Exception("aMethod must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let addonListeners = this.addonListeners.slice(0);
+ for (let listener of addonListeners) {
+ try {
+ if (aMethod in listener)
+ listener[aMethod].apply(listener, aArgs);
+ }
+ catch (e) {
+ logger.warn("AddonListener threw exception when calling " + aMethod, e);
+ }
+ }
+ },
+
+ /**
+ * Notifies all providers that an add-on has been enabled when that type of
+ * add-on only supports a single add-on being enabled at a time. This allows
+ * the providers to disable theirs if necessary.
+ *
+ * @param aID
+ * The ID of the enabled add-on
+ * @param aType
+ * The type of the enabled add-on
+ * @param aPendingRestart
+ * A boolean indicating if the change will only take place the next
+ * time the application is restarted
+ */
+ notifyAddonChanged: function AMI_notifyAddonChanged(aID, aType, aPendingRestart) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (aID && typeof aID != "string")
+ throw Components.Exception("aID must be a string or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aType || typeof aType != "string")
+ throw Components.Exception("aType must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ // Temporary hack until bug 520124 lands.
+ // We can get here during synchronous startup, at which point it's
+ // considered unsafe (and therefore disallowed by AddonManager.jsm) to
+ // access providers that haven't been initialized yet. Since this is when
+ // XPIProvider is starting up, XPIProvider can't access itself via APIs
+ // going through AddonManager.jsm. Furthermore, LightweightThemeManager may
+ // not be initialized until after XPIProvider is, and therefore would also
+ // be unaccessible during XPIProvider startup. Thankfully, these are the
+ // only two uses of this API, and we know it's safe to use this API with
+ // both providers; so we have this hack to allow bypassing the normal
+ // safetey guard.
+ // The notifyAddonChanged/addonChanged API will be unneeded and therefore
+ // removed by bug 520124, so this is a temporary quick'n'dirty hack.
+ let providers = [...this.providers, ...this.pendingProviders];
+ for (let provider of providers) {
+ callProvider(provider, "addonChanged", null, aID, aType, aPendingRestart);
+ }
+ },
+
+ /**
+ * Notifies all providers they need to update the appDisabled property for
+ * their add-ons in response to an application change such as a blocklist
+ * update.
+ */
+ updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ this.callProviders("updateAddonAppDisabledStates");
+ },
+
+ /**
+ * Notifies all providers that the repository has updated its data for
+ * installed add-ons.
+ *
+ * @param aCallback
+ * Function to call when operation is complete.
+ */
+ updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ new AsyncObjectCaller(this.providers, "updateAddonRepositoryData", {
+ nextObject: function updateAddonRepositoryData_nextObject(aCaller, aProvider) {
+ callProviderAsync(aProvider, "updateAddonRepositoryData",
+ aCaller.callNext.bind(aCaller));
+ },
+ noMoreObjects: function updateAddonRepositoryData_noMoreObjects(aCaller) {
+ safeCall(aCallback);
+ // only tests should care about this
+ Services.obs.notifyObservers(null, "TEST:addon-repository-data-updated", null);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets an AddonInstall for a URL.
+ *
+ * @param aUrl
+ * The string represenation of the URL the add-on is located at
+ * @param aCallback
+ * A callback to pass the AddonInstall to
+ * @param aMimetype
+ * The mimetype of the add-on
+ * @param aHash
+ * An optional hash of the add-on
+ * @param aName
+ * An optional placeholder name while the add-on is being downloaded
+ * @param aIcons
+ * Optional placeholder icons while the add-on is being downloaded
+ * @param aVersion
+ * An optional placeholder version while the add-on is being downloaded
+ * @param aLoadGroup
+ * An optional nsILoadGroup to associate any network requests with
+ * @throws if the aUrl, aCallback or aMimetype arguments are not specified
+ */
+ getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype,
+ aHash, aName, aIcons,
+ aVersion, aBrowser) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aUrl || typeof aUrl != "string")
+ throw Components.Exception("aURL must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aMimetype || typeof aMimetype != "string")
+ throw Components.Exception("aMimetype must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aHash && typeof aHash != "string")
+ throw Components.Exception("aHash must be a string or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aName && typeof aName != "string")
+ throw Components.Exception("aName must be a string or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aIcons) {
+ if (typeof aIcons == "string")
+ aIcons = { "32": aIcons };
+ else if (typeof aIcons != "object")
+ throw Components.Exception("aIcons must be a string, an object or null",
+ Cr.NS_ERROR_INVALID_ARG);
+ } else {
+ aIcons = {};
+ }
+
+ if (aVersion && typeof aVersion != "string")
+ throw Components.Exception("aVersion must be a string or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aBrowser && (!(aBrowser instanceof Ci.nsIDOMElement)))
+ throw Components.Exception("aBrowser must be a nsIDOMElement or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let providers = [...this.providers];
+ for (let provider of providers) {
+ if (callProvider(provider, "supportsMimetype", false, aMimetype)) {
+ callProviderAsync(provider, "getInstallForURL",
+ aUrl, aHash, aName, aIcons, aVersion, aBrowser,
+ function getInstallForURL_safeCall(aInstall) {
+ safeCall(aCallback, aInstall);
+ });
+ return;
+ }
+ }
+ safeCall(aCallback, null);
+ },
+
+ /**
+ * Asynchronously gets an AddonInstall for an nsIFile.
+ *
+ * @param aFile
+ * The nsIFile where the add-on is located
+ * @param aCallback
+ * A callback to pass the AddonInstall to
+ * @param aMimetype
+ * An optional mimetype hint for the add-on
+ * @throws if the aFile or aCallback arguments are not specified
+ */
+ getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!(aFile instanceof Ci.nsIFile))
+ throw Components.Exception("aFile must be a nsIFile",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aMimetype && typeof aMimetype != "string")
+ throw Components.Exception("aMimetype must be a string or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ new AsyncObjectCaller(this.providers, "getInstallForFile", {
+ nextObject: function getInstallForFile_nextObject(aCaller, aProvider) {
+ callProviderAsync(aProvider, "getInstallForFile", aFile,
+ function getInstallForFile_safeCall(aInstall) {
+ if (aInstall)
+ safeCall(aCallback, aInstall);
+ else
+ aCaller.callNext();
+ });
+ },
+
+ noMoreObjects: function getInstallForFile_noMoreObjects(aCaller) {
+ safeCall(aCallback, null);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets all current AddonInstalls optionally limiting to a list
+ * of types.
+ *
+ * @param aTypes
+ * An optional array of types to retrieve. Each type is a string name
+ * @param aCallback
+ * A callback which will be passed an array of AddonInstalls
+ * @throws If the aCallback argument is not specified
+ */
+ getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (aTypes && !Array.isArray(aTypes))
+ throw Components.Exception("aTypes must be an array or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let installs = [];
+
+ new AsyncObjectCaller(this.providers, "getInstallsByTypes", {
+ nextObject: function getInstallsByTypes_nextObject(aCaller, aProvider) {
+ callProviderAsync(aProvider, "getInstallsByTypes", aTypes,
+ function getInstallsByTypes_safeCall(aProviderInstalls) {
+ if (aProviderInstalls) {
+ installs = installs.concat(aProviderInstalls);
+ }
+ aCaller.callNext();
+ });
+ },
+
+ noMoreObjects: function getInstallsByTypes_noMoreObjects(aCaller) {
+ safeCall(aCallback, installs);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets all current AddonInstalls.
+ *
+ * @param aCallback
+ * A callback which will be passed an array of AddonInstalls
+ */
+ getAllInstalls: function AMI_getAllInstalls(aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ this.getInstallsByTypes(null, aCallback);
+ },
+
+ /**
+ * Synchronously map a URI to the corresponding Addon ID.
+ *
+ * Mappable URIs are limited to in-application resources belonging to the
+ * add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
+ * but do not include URIs from meta data, such as the add-on homepage.
+ *
+ * @param aURI
+ * nsIURI to map to an addon id
+ * @return string containing the Addon ID or null
+ * @see amIAddonManager.mapURIToAddonID
+ */
+ mapURIToAddonID: function AMI_mapURIToAddonID(aURI) {
+ if (!(aURI instanceof Ci.nsIURI)) {
+ throw Components.Exception("aURI is not a nsIURI",
+ Cr.NS_ERROR_INVALID_ARG);
+ }
+
+ // Try all providers
+ let providers = [...this.providers];
+ for (let provider of providers) {
+ var id = callProvider(provider, "mapURIToAddonID", null, aURI);
+ if (id !== null) {
+ return id;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Checks whether installation is enabled for a particular mimetype.
+ *
+ * @param aMimetype
+ * The mimetype to check
+ * @return true if installation is enabled for the mimetype
+ */
+ isInstallEnabled: function AMI_isInstallEnabled(aMimetype) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aMimetype || typeof aMimetype != "string")
+ throw Components.Exception("aMimetype must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let providers = [...this.providers];
+ for (let provider of providers) {
+ if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
+ callProvider(provider, "isInstallEnabled"))
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Checks whether a particular source is allowed to install add-ons of a
+ * given mimetype.
+ *
+ * @param aMimetype
+ * The mimetype of the add-on
+ * @param aInstallingPrincipal
+ * The nsIPrincipal that initiated the install
+ * @return true if the source is allowed to install this mimetype
+ */
+ isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aInstallingPrincipal) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aMimetype || typeof aMimetype != "string")
+ throw Components.Exception("aMimetype must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aInstallingPrincipal || !(aInstallingPrincipal instanceof Ci.nsIPrincipal))
+ throw Components.Exception("aInstallingPrincipal must be a nsIPrincipal",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let providers = [...this.providers];
+ for (let provider of providers) {
+ if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
+ callProvider(provider, "isInstallAllowed", null, aInstallingPrincipal))
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Starts installation of an array of AddonInstalls notifying the registered
+ * web install listener of blocked or started installs.
+ *
+ * @param aMimetype
+ * The mimetype of add-ons being installed
+ * @param aBrowser
+ * The optional browser element that started the installs
+ * @param aInstallingPrincipal
+ * The nsIPrincipal that initiated the install
+ * @param aInstalls
+ * The array of AddonInstalls to be installed
+ */
+ installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype,
+ aBrowser,
+ aInstallingPrincipal,
+ aInstalls) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aMimetype || typeof aMimetype != "string")
+ throw Components.Exception("aMimetype must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (aBrowser && !(aBrowser instanceof Ci.nsIDOMElement))
+ throw Components.Exception("aSource must be a nsIDOMElement, or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!aInstallingPrincipal || !(aInstallingPrincipal instanceof Ci.nsIPrincipal))
+ throw Components.Exception("aInstallingPrincipal must be a nsIPrincipal",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!Array.isArray(aInstalls))
+ throw Components.Exception("aInstalls must be an array",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
+ logger.warn("No web installer available, cancelling all installs");
+ aInstalls.forEach(function(aInstall) {
+ aInstall.cancel();
+ });
+ return;
+ }
+
+ // When a chrome in-content UI has loaded a <browser> inside to host a
+ // website we want to do our security checks on the inner-browser but
+ // notify front-end that install events came from the outer-browser (the
+ // main tab's browser). Check this by seeing if the browser we've been
+ // passed is in a content type docshell and if so get the outer-browser.
+ let topBrowser = aBrowser;
+ let docShell = aBrowser.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIDocShellTreeItem);
+ if (docShell.itemType == Ci.nsIDocShellTreeItem.typeContent)
+ topBrowser = docShell.chromeEventHandler;
+
+ try {
+ let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
+ getService(Ci.amIWebInstallListener);
+
+ if (!this.isInstallEnabled(aMimetype)) {
+ for (let install of aInstalls)
+ install.cancel();
+
+ weblistener.onWebInstallDisabled(topBrowser, aInstallingPrincipal.URI,
+ aInstalls, aInstalls.length);
+ return;
+ }
+ else if (!aBrowser.contentPrincipal || !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)) {
+ for (let install of aInstalls)
+ install.cancel();
+
+ if (weblistener instanceof Ci.amIWebInstallListener2) {
+ weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
+ aInstalls, aInstalls.length);
+ }
+ return;
+ }
+
+ // The installs may start now depending on the web install listener,
+ // listen for the browser navigating to a new origin and cancel the
+ // installs in that case.
+ new BrowserListener(aBrowser, aInstallingPrincipal, aInstalls);
+
+ if (!this.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
+ if (weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
+ aInstalls, aInstalls.length)) {
+ aInstalls.forEach(function(aInstall) {
+ aInstall.install();
+ });
+ }
+ }
+ else if (weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
+ aInstalls, aInstalls.length)) {
+ aInstalls.forEach(function(aInstall) {
+ aInstall.install();
+ });
+ }
+ }
+ catch (e) {
+ // In the event that the weblistener throws during instantiation or when
+ // calling onWebInstallBlocked or onWebInstallRequested all of the
+ // installs should get cancelled.
+ logger.warn("Failure calling web installer", e);
+ aInstalls.forEach(function(aInstall) {
+ aInstall.cancel();
+ });
+ }
+ },
+
+ /**
+ * Adds a new InstallListener if the listener is not already registered.
+ *
+ * @param aListener
+ * The InstallListener to add
+ */
+ addInstallListener: function AMI_addInstallListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be a InstallListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!this.installListeners.some(function addInstallListener_matchListener(i) {
+ return i == aListener; }))
+ this.installListeners.push(aListener);
+ },
+
+ /**
+ * Removes an InstallListener if the listener is registered.
+ *
+ * @param aListener
+ * The InstallListener to remove
+ */
+ removeInstallListener: function AMI_removeInstallListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be a InstallListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let pos = 0;
+ while (pos < this.installListeners.length) {
+ if (this.installListeners[pos] == aListener)
+ this.installListeners.splice(pos, 1);
+ else
+ pos++;
+ }
+ },
+
+ /**
+ * Asynchronously gets an add-on with a specific ID.
+ *
+ * @param aID
+ * The ID of the add-on to retrieve
+ * @param aCallback
+ * The callback to pass the retrieved add-on to
+ * @throws if the aID or aCallback arguments are not specified
+ */
+ getAddonByID: function AMI_getAddonByID(aID, aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aID || typeof aID != "string")
+ throw Components.Exception("aID must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ new AsyncObjectCaller(this.providers, "getAddonByID", {
+ nextObject: function getAddonByID_nextObject(aCaller, aProvider) {
+ callProviderAsync(aProvider, "getAddonByID", aID,
+ function getAddonByID_safeCall(aAddon) {
+ if (aAddon)
+ safeCall(aCallback, aAddon);
+ else
+ aCaller.callNext();
+ });
+ },
+
+ noMoreObjects: function getAddonByID_noMoreObjects(aCaller) {
+ safeCall(aCallback, null);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously get an add-on with a specific Sync GUID.
+ *
+ * @param aGUID
+ * String GUID of add-on to retrieve
+ * @param aCallback
+ * The callback to pass the retrieved add-on to.
+ * @throws if the aGUID or aCallback arguments are not specified
+ */
+ getAddonBySyncGUID: function AMI_getAddonBySyncGUID(aGUID, aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!aGUID || typeof aGUID != "string")
+ throw Components.Exception("aGUID must be a non-empty string",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ new AsyncObjectCaller(this.providers, "getAddonBySyncGUID", {
+ nextObject: function getAddonBySyncGUID_nextObject(aCaller, aProvider) {
+ callProviderAsync(aProvider, "getAddonBySyncGUID", aGUID,
+ function getAddonBySyncGUID_safeCall(aAddon) {
+ if (aAddon) {
+ safeCall(aCallback, aAddon);
+ } else {
+ aCaller.callNext();
+ }
+ });
+ },
+
+ noMoreObjects: function getAddonBySyncGUID_noMoreObjects(aCaller) {
+ safeCall(aCallback, null);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets an array of add-ons.
+ *
+ * @param aIDs
+ * The array of IDs to retrieve
+ * @param aCallback
+ * The callback to pass an array of Addons to
+ * @throws if the aID or aCallback arguments are not specified
+ */
+ getAddonsByIDs: function AMI_getAddonsByIDs(aIDs, aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (!Array.isArray(aIDs))
+ throw Components.Exception("aIDs must be an array",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let addons = [];
+
+ new AsyncObjectCaller(aIDs, null, {
+ nextObject: function getAddonsByIDs_nextObject(aCaller, aID) {
+ AddonManagerInternal.getAddonByID(aID,
+ function getAddonsByIDs_getAddonByID(aAddon) {
+ addons.push(aAddon);
+ aCaller.callNext();
+ });
+ },
+
+ noMoreObjects: function getAddonsByIDs_noMoreObjects(aCaller) {
+ safeCall(aCallback, addons);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets add-ons of specific types.
+ *
+ * @param aTypes
+ * An optional array of types to retrieve. Each type is a string name
+ * @param aCallback
+ * The callback to pass an array of Addons to.
+ * @throws if the aCallback argument is not specified
+ */
+ getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (aTypes && !Array.isArray(aTypes))
+ throw Components.Exception("aTypes must be an array or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let addons = [];
+
+ new AsyncObjectCaller(this.providers, "getAddonsByTypes", {
+ nextObject: function getAddonsByTypes_nextObject(aCaller, aProvider) {
+ callProviderAsync(aProvider, "getAddonsByTypes", aTypes,
+ function getAddonsByTypes_concatAddons(aProviderAddons) {
+ if (aProviderAddons) {
+ addons = addons.concat(aProviderAddons);
+ }
+ aCaller.callNext();
+ });
+ },
+
+ noMoreObjects: function getAddonsByTypes_noMoreObjects(aCaller) {
+ safeCall(aCallback, addons);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets all installed add-ons.
+ *
+ * @param aCallback
+ * A callback which will be passed an array of Addons
+ */
+ getAllAddons: function AMI_getAllAddons(aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ this.getAddonsByTypes(null, aCallback);
+ },
+
+ /**
+ * Asynchronously gets add-ons that have operations waiting for an application
+ * restart to complete.
+ *
+ * @param aTypes
+ * An optional array of types to retrieve. Each type is a string name
+ * @param aCallback
+ * The callback to pass the array of Addons to
+ * @throws if the aCallback argument is not specified
+ */
+ getAddonsWithOperationsByTypes:
+ function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
+ if (!gStarted)
+ throw Components.Exception("AddonManager is not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED);
+
+ if (aTypes && !Array.isArray(aTypes))
+ throw Components.Exception("aTypes must be an array or null",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (typeof aCallback != "function")
+ throw Components.Exception("aCallback must be a function",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let addons = [];
+
+ new AsyncObjectCaller(this.providers, "getAddonsWithOperationsByTypes", {
+ nextObject: function getAddonsWithOperationsByTypes_nextObject
+ (aCaller, aProvider) {
+ callProviderAsync(aProvider, "getAddonsWithOperationsByTypes", aTypes,
+ function getAddonsWithOperationsByTypes_concatAddons
+ (aProviderAddons) {
+ if (aProviderAddons) {
+ addons = addons.concat(aProviderAddons);
+ }
+ aCaller.callNext();
+ });
+ },
+
+ noMoreObjects: function getAddonsWithOperationsByTypes_noMoreObjects(caller) {
+ safeCall(aCallback, addons);
+ }
+ });
+ },
+
+ /**
+ * Adds a new AddonManagerListener if the listener is not already registered.
+ *
+ * @param aListener
+ * The listener to add
+ */
+ addManagerListener: function AMI_addManagerListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be an AddonManagerListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!this.managerListeners.some(function addManagerListener_matchListener(i) {
+ return i == aListener; }))
+ this.managerListeners.push(aListener);
+ },
+
+ /**
+ * Removes an AddonManagerListener if the listener is registered.
+ *
+ * @param aListener
+ * The listener to remove
+ */
+ removeManagerListener: function AMI_removeManagerListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be an AddonManagerListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let pos = 0;
+ while (pos < this.managerListeners.length) {
+ if (this.managerListeners[pos] == aListener)
+ this.managerListeners.splice(pos, 1);
+ else
+ pos++;
+ }
+ },
+
+ /**
+ * Adds a new AddonListener if the listener is not already registered.
+ *
+ * @param aListener
+ * The AddonListener to add
+ */
+ addAddonListener: function AMI_addAddonListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be an AddonListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!this.addonListeners.some(function addAddonListener_matchListener(i) {
+ return i == aListener; }))
+ this.addonListeners.push(aListener);
+ },
+
+ /**
+ * Removes an AddonListener if the listener is registered.
+ *
+ * @param aListener
+ * The AddonListener to remove
+ */
+ removeAddonListener: function AMI_removeAddonListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be an AddonListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let pos = 0;
+ while (pos < this.addonListeners.length) {
+ if (this.addonListeners[pos] == aListener)
+ this.addonListeners.splice(pos, 1);
+ else
+ pos++;
+ }
+ },
+
+ /**
+ * Adds a new TypeListener if the listener is not already registered.
+ *
+ * @param aListener
+ * The TypeListener to add
+ */
+ addTypeListener: function AMI_addTypeListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be a TypeListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!this.typeListeners.some(function addTypeListener_matchListener(i) {
+ return i == aListener; }))
+ this.typeListeners.push(aListener);
+ },
+
+ /**
+ * Removes an TypeListener if the listener is registered.
+ *
+ * @param aListener
+ * The TypeListener to remove
+ */
+ removeTypeListener: function AMI_removeTypeListener(aListener) {
+ if (!aListener || typeof aListener != "object")
+ throw Components.Exception("aListener must be a TypeListener object",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ let pos = 0;
+ while (pos < this.typeListeners.length) {
+ if (this.typeListeners[pos] == aListener)
+ this.typeListeners.splice(pos, 1);
+ else
+ pos++;
+ }
+ },
+
+ get addonTypes() {
+ // A read-only wrapper around the types dictionary
+ return new Proxy(this.types, {
+ defineProperty(target, property, descriptor) {
+ // Not allowed to define properties
+ return false;
+ },
+
+ deleteProperty(target, property) {
+ // Not allowed to delete properties
+ return false;
+ },
+
+ get(target, property, receiver) {
+ if (!target.hasOwnProperty(property))
+ return undefined;
+
+ return target[property].type;
+ },
+
+ getOwnPropertyDescriptor(target, property) {
+ if (!target.hasOwnProperty(property))
+ return undefined;
+
+ return {
+ value: target[property].type,
+ writable: false,
+ // Claim configurability to maintain the proxy invariants.
+ configurable: true,
+ enumerable: true
+ }
+ },
+
+ preventExtensions(target) {
+ // Not allowed to prevent adding new properties
+ return false;
+ },
+
+ set(target, property, value, receiver) {
+ // Not allowed to set properties
+ return false;
+ },
+
+ setPrototypeOf(target, prototype) {
+ // Not allowed to change prototype
+ return false;
+ }
+ });
+ },
+
+ get autoUpdateDefault() {
+ return gAutoUpdateDefault;
+ },
+
+ set autoUpdateDefault(aValue) {
+ aValue = !!aValue;
+ if (aValue != gAutoUpdateDefault)
+ Services.prefs.setBoolPref(PREF_EM_AUTOUPDATE_DEFAULT, aValue);
+ return aValue;
+ },
+
+ get checkCompatibility() {
+ return gCheckCompatibility;
+ },
+
+ set checkCompatibility(aValue) {
+ aValue = !!aValue;
+ if (aValue != gCheckCompatibility) {
+ if (!aValue)
+ Services.prefs.setBoolPref(PREF_EM_CHECK_COMPATIBILITY, false);
+ else
+ Services.prefs.clearUserPref(PREF_EM_CHECK_COMPATIBILITY);
+ }
+ return aValue;
+ },
+
+ get strictCompatibility() {
+ return gStrictCompatibility;
+ },
+
+ set strictCompatibility(aValue) {
+ aValue = !!aValue;
+ if (aValue != gStrictCompatibility)
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, aValue);
+ return aValue;
+ },
+
+ get checkUpdateSecurityDefault() {
+ return gCheckUpdateSecurityDefault;
+ },
+
+ get checkUpdateSecurity() {
+ return gCheckUpdateSecurity;
+ },
+
+ set checkUpdateSecurity(aValue) {
+ aValue = !!aValue;
+ if (aValue != gCheckUpdateSecurity) {
+ if (aValue != gCheckUpdateSecurityDefault)
+ Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, aValue);
+ else
+ Services.prefs.clearUserPref(PREF_EM_CHECK_UPDATE_SECURITY);
+ }
+ return aValue;
+ },
+
+ get updateEnabled() {
+ return gUpdateEnabled;
+ },
+
+ set updateEnabled(aValue) {
+ aValue = !!aValue;
+ if (aValue != gUpdateEnabled)
+ Services.prefs.setBoolPref(PREF_EM_UPDATE_ENABLED, aValue);
+ return aValue;
+ },
+};
+
+/**
+ * Should not be used outside of core Mozilla code. This is a private API for
+ * the startup and platform integration code to use. Refer to the methods on
+ * AddonManagerInternal for documentation however note that these methods are
+ * subject to change at any time.
+ */
+this.AddonManagerPrivate = {
+ startup: function AMP_startup() {
+ AddonManagerInternal.startup();
+ },
+
+ registerProvider: function AMP_registerProvider(aProvider, aTypes) {
+ AddonManagerInternal.registerProvider(aProvider, aTypes);
+ },
+
+ unregisterProvider: function AMP_unregisterProvider(aProvider) {
+ AddonManagerInternal.unregisterProvider(aProvider);
+ },
+
+ markProviderSafe: function AMP_markProviderSafe(aProvider) {
+ AddonManagerInternal.markProviderSafe(aProvider);
+ },
+
+ backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
+ return AddonManagerInternal.backgroundUpdateCheck();
+ },
+
+ backgroundUpdateTimerHandler() {
+ // Don't call through to the real update check if no checks are enabled.
+ if (!AddonManagerInternal.updateEnabled) {
+ logger.info("Skipping background update check");
+ return;
+ }
+ // Don't return the promise here, since the caller doesn't care.
+ AddonManagerInternal.backgroundUpdateCheck();
+ },
+
+ addStartupChange: function AMP_addStartupChange(aType, aID) {
+ AddonManagerInternal.addStartupChange(aType, aID);
+ },
+
+ removeStartupChange: function AMP_removeStartupChange(aType, aID) {
+ AddonManagerInternal.removeStartupChange(aType, aID);
+ },
+
+ notifyAddonChanged: function AMP_notifyAddonChanged(aID, aType, aPendingRestart) {
+ AddonManagerInternal.notifyAddonChanged(aID, aType, aPendingRestart);
+ },
+
+ updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() {
+ AddonManagerInternal.updateAddonAppDisabledStates();
+ },
+
+ updateAddonRepositoryData: function AMP_updateAddonRepositoryData(aCallback) {
+ AddonManagerInternal.updateAddonRepositoryData(aCallback);
+ },
+
+ callInstallListeners: function AMP_callInstallListeners(...aArgs) {
+ return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal,
+ aArgs);
+ },
+
+ callAddonListeners: function AMP_callAddonListeners(...aArgs) {
+ AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs);
+ },
+
+ AddonAuthor: AddonAuthor,
+
+ AddonScreenshot: AddonScreenshot,
+
+ AddonCompatibilityOverride: AddonCompatibilityOverride,
+
+ AddonType: AddonType,
+
+ recordTimestamp: function AMP_recordTimestamp(name, value) {
+ AddonManagerInternal.recordTimestamp(name, value);
+ },
+
+ _simpleMeasures: {},
+ recordSimpleMeasure: function AMP_recordSimpleMeasure(name, value) {
+ this._simpleMeasures[name] = value;
+ },
+
+ recordException: function AMP_recordException(aModule, aContext, aException) {
+ let report = {
+ module: aModule,
+ context: aContext
+ };
+
+ if (typeof aException == "number") {
+ report.message = Components.Exception("", aException).name;
+ }
+ else {
+ report.message = aException.toString();
+ if (aException.fileName) {
+ report.file = aException.fileName;
+ report.line = aException.lineNumber;
+ }
+ }
+
+ this._simpleMeasures.exception = report;
+ },
+
+ getSimpleMeasures: function AMP_getSimpleMeasures() {
+ return this._simpleMeasures;
+ },
+
+ getTelemetryDetails: function AMP_getTelemetryDetails() {
+ return AddonManagerInternal.telemetryDetails;
+ },
+
+ setTelemetryDetails: function AMP_setTelemetryDetails(aProvider, aDetails) {
+ AddonManagerInternal.telemetryDetails[aProvider] = aDetails;
+ },
+
+ // Start a timer, record a simple measure of the time interval when
+ // timer.done() is called
+ simpleTimer: function(aName) {
+ let startTime = Cu.now();
+ return {
+ done: () => this.recordSimpleMeasure(aName, Math.round(Cu.now() - startTime))
+ };
+ },
+
+ /**
+ * Helper to call update listeners when no update is available.
+ *
+ * This can be used as an implementation for Addon.findUpdates() when
+ * no update mechanism is available.
+ */
+ callNoUpdateListeners: function (addon, listener, reason, appVersion, platformVersion) {
+ if ("onNoCompatibilityUpdateAvailable" in listener) {
+ safeCall(listener.onNoCompatibilityUpdateAvailable.bind(listener), addon);
+ }
+ if ("onNoUpdateAvailable" in listener) {
+ safeCall(listener.onNoUpdateAvailable.bind(listener), addon);
+ }
+ if ("onUpdateFinished" in listener) {
+ safeCall(listener.onUpdateFinished.bind(listener), addon);
+ }
+ },
+};
+
+/**
+ * This is the public API that UI and developers should be calling. All methods
+ * just forward to AddonManagerInternal.
+ */
+this.AddonManager = {
+ // Constants for the AddonInstall.state property
+ // The install is available for download.
+ STATE_AVAILABLE: 0,
+ // The install is being downloaded.
+ STATE_DOWNLOADING: 1,
+ // The install is checking for compatibility information.
+ STATE_CHECKING: 2,
+ // The install is downloaded and ready to install.
+ STATE_DOWNLOADED: 3,
+ // The download failed.
+ STATE_DOWNLOAD_FAILED: 4,
+ // The add-on is being installed.
+ STATE_INSTALLING: 5,
+ // The add-on has been installed.
+ STATE_INSTALLED: 6,
+ // The install failed.
+ STATE_INSTALL_FAILED: 7,
+ // The install has been cancelled.
+ STATE_CANCELLED: 8,
+
+ // Constants representing different types of errors while downloading an
+ // add-on.
+ // The download failed due to network problems.
+ ERROR_NETWORK_FAILURE: -1,
+ // The downloaded file did not match the provided hash.
+ ERROR_INCORRECT_HASH: -2,
+ // The downloaded file seems to be corrupted in some way.
+ ERROR_CORRUPT_FILE: -3,
+ // An error occured trying to write to the filesystem.
+ ERROR_FILE_ACCESS: -4,
+ // The downloaded file seems to be WebExtension.
+ ERROR_WEBEXT_FILE: -5,
+
+ // These must be kept in sync with AddonUpdateChecker.
+ // No error was encountered.
+ UPDATE_STATUS_NO_ERROR: 0,
+ // The update check timed out
+ UPDATE_STATUS_TIMEOUT: -1,
+ // There was an error while downloading the update information.
+ UPDATE_STATUS_DOWNLOAD_ERROR: -2,
+ // The update information was malformed in some way.
+ UPDATE_STATUS_PARSE_ERROR: -3,
+ // The update information was not in any known format.
+ UPDATE_STATUS_UNKNOWN_FORMAT: -4,
+ // The update information was not correctly signed or there was an SSL error.
+ UPDATE_STATUS_SECURITY_ERROR: -5,
+ // The update was cancelled.
+ UPDATE_STATUS_CANCELLED: -6,
+
+ // Constants to indicate why an update check is being performed
+ // Update check has been requested by the user.
+ UPDATE_WHEN_USER_REQUESTED: 1,
+ // Update check is necessary to see if the Addon is compatibile with a new
+ // version of the application.
+ UPDATE_WHEN_NEW_APP_DETECTED: 2,
+ // Update check is necessary because a new application has been installed.
+ UPDATE_WHEN_NEW_APP_INSTALLED: 3,
+ // Update check is a regular background update check.
+ UPDATE_WHEN_PERIODIC_UPDATE: 16,
+ // Update check is needed to check an Addon that is being installed.
+ UPDATE_WHEN_ADDON_INSTALLED: 17,
+
+ // Constants for operations in Addon.pendingOperations
+ // Indicates that the Addon has no pending operations.
+ PENDING_NONE: 0,
+ // Indicates that the Addon will be enabled after the application restarts.
+ PENDING_ENABLE: 1,
+ // Indicates that the Addon will be disabled after the application restarts.
+ PENDING_DISABLE: 2,
+ // Indicates that the Addon will be uninstalled after the application restarts.
+ PENDING_UNINSTALL: 4,
+ // Indicates that the Addon will be installed after the application restarts.
+ PENDING_INSTALL: 8,
+ PENDING_UPGRADE: 16,
+
+ // Constants for operations in Addon.operationsRequiringRestart
+ // Indicates that restart isn't required for any operation.
+ OP_NEEDS_RESTART_NONE: 0,
+ // Indicates that restart is required for enabling the addon.
+ OP_NEEDS_RESTART_ENABLE: 1,
+ // Indicates that restart is required for disabling the addon.
+ OP_NEEDS_RESTART_DISABLE: 2,
+ // Indicates that restart is required for uninstalling the addon.
+ OP_NEEDS_RESTART_UNINSTALL: 4,
+ // Indicates that restart is required for installing the addon.
+ OP_NEEDS_RESTART_INSTALL: 8,
+
+ // Constants for permissions in Addon.permissions.
+ // Indicates that the Addon can be uninstalled.
+ PERM_CAN_UNINSTALL: 1,
+ // Indicates that the Addon can be enabled by the user.
+ PERM_CAN_ENABLE: 2,
+ // Indicates that the Addon can be disabled by the user.
+ PERM_CAN_DISABLE: 4,
+ // Indicates that the Addon can be upgraded.
+ PERM_CAN_UPGRADE: 8,
+ // Indicates that the Addon can be set to be optionally enabled
+ // on a case-by-case basis.
+ PERM_CAN_ASK_TO_ACTIVATE: 16,
+
+ // General descriptions of where items are installed.
+ // Installed in this profile.
+ SCOPE_PROFILE: 1,
+ // Installed for all of this user's profiles.
+ SCOPE_USER: 2,
+ // Installed and owned by the application.
+ SCOPE_APPLICATION: 4,
+ // Installed for all users of the computer.
+ SCOPE_SYSTEM: 8,
+ // The combination of all scopes.
+ SCOPE_ALL: 15,
+
+ // Add-on type is expected to be displayed in the UI in a list.
+ VIEW_TYPE_LIST: "list",
+
+ // Constants describing how add-on types behave.
+
+ // If no add-ons of a type are installed, then the category for that add-on
+ // type should be hidden in the UI.
+ TYPE_UI_HIDE_EMPTY: 16,
+ // Indicates that this add-on type supports the ask-to-activate state.
+ // That is, add-ons of this type can be set to be optionally enabled
+ // on a case-by-case basis.
+ TYPE_SUPPORTS_ASK_TO_ACTIVATE: 32,
+ // The add-on type natively supports undo for restartless uninstalls.
+ // If this flag is not specified, the UI is expected to handle this via
+ // disabling the add-on, and performing the actual uninstall at a later time.
+ TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL: 64,
+
+ // Constants for Addon.applyBackgroundUpdates.
+ // Indicates that the Addon should not update automatically.
+ AUTOUPDATE_DISABLE: 0,
+ // Indicates that the Addon should update automatically only if
+ // that's the global default.
+ AUTOUPDATE_DEFAULT: 1,
+ // Indicates that the Addon should update automatically.
+ AUTOUPDATE_ENABLE: 2,
+
+ // Constants for how Addon options should be shown.
+ // Options will be opened in a new window
+ OPTIONS_TYPE_DIALOG: 1,
+ // Options will be displayed within the AM detail view
+ OPTIONS_TYPE_INLINE: 2,
+ // Options will be displayed in a new tab, if possible
+ OPTIONS_TYPE_TAB: 3,
+ // Same as OPTIONS_TYPE_INLINE, but no Preferences button will be shown.
+ // Used to indicate that only non-interactive information will be shown.
+ OPTIONS_TYPE_INLINE_INFO: 4,
+
+ // Constants for displayed or hidden options notifications
+ // Options notification will be displayed
+ OPTIONS_NOTIFICATION_DISPLAYED: "addon-options-displayed",
+ // Options notification will be hidden
+ OPTIONS_NOTIFICATION_HIDDEN: "addon-options-hidden",
+
+ // Constants for getStartupChanges, addStartupChange and removeStartupChange
+ // Add-ons that were detected as installed during startup. Doesn't include
+ // add-ons that were pending installation the last time the application ran.
+ STARTUP_CHANGE_INSTALLED: "installed",
+ // Add-ons that were detected as changed during startup. This includes an
+ // add-on moving to a different location, changing version or just having
+ // been detected as possibly changed.
+ STARTUP_CHANGE_CHANGED: "changed",
+ // Add-ons that were detected as uninstalled during startup. Doesn't include
+ // add-ons that were pending uninstallation the last time the application ran.
+ STARTUP_CHANGE_UNINSTALLED: "uninstalled",
+ // Add-ons that were detected as disabled during startup, normally because of
+ // an application change making an add-on incompatible. Doesn't include
+ // add-ons that were pending being disabled the last time the application ran.
+ STARTUP_CHANGE_DISABLED: "disabled",
+ // Add-ons that were detected as enabled during startup, normally because of
+ // an application change making an add-on compatible. Doesn't include
+ // add-ons that were pending being enabled the last time the application ran.
+ STARTUP_CHANGE_ENABLED: "enabled",
+
+ // Constants for the Addon.userDisabled property
+ // Indicates that the userDisabled state of this add-on is currently
+ // ask-to-activate. That is, it can be conditionally enabled on a
+ // case-by-case basis.
+ STATE_ASK_TO_ACTIVATE: "askToActivate",
+
+#ifdef MOZ_EM_DEBUG
+ get __AddonManagerInternal__() {
+ return AddonManagerInternal;
+ },
+#endif
+
+ get isReady() {
+ return gStartupComplete && !gShutdownInProgress;
+ },
+
+ getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
+ aHash, aName, aIcons,
+ aVersion, aBrowser) {
+ AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
+ aName, aIcons, aVersion, aBrowser);
+ },
+
+ getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
+ AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype);
+ },
+
+ /**
+ * Gets an array of add-on IDs that changed during the most recent startup.
+ *
+ * @param aType
+ * The type of startup change to get
+ * @return An array of add-on IDs
+ */
+ getStartupChanges: function AM_getStartupChanges(aType) {
+ if (!(aType in AddonManagerInternal.startupChanges))
+ return [];
+ return AddonManagerInternal.startupChanges[aType].slice(0);
+ },
+
+ getAddonByID: function AM_getAddonByID(aID, aCallback) {
+ AddonManagerInternal.getAddonByID(aID, aCallback);
+ },
+
+ getAddonBySyncGUID: function AM_getAddonBySyncGUID(aGUID, aCallback) {
+ AddonManagerInternal.getAddonBySyncGUID(aGUID, aCallback);
+ },
+
+ getAddonsByIDs: function AM_getAddonsByIDs(aIDs, aCallback) {
+ AddonManagerInternal.getAddonsByIDs(aIDs, aCallback);
+ },
+
+ getAddonsWithOperationsByTypes:
+ function AM_getAddonsWithOperationsByTypes(aTypes, aCallback) {
+ AddonManagerInternal.getAddonsWithOperationsByTypes(aTypes, aCallback);
+ },
+
+ getAddonsByTypes: function AM_getAddonsByTypes(aTypes, aCallback) {
+ AddonManagerInternal.getAddonsByTypes(aTypes, aCallback);
+ },
+
+ getAllAddons: function AM_getAllAddons(aCallback) {
+ AddonManagerInternal.getAllAddons(aCallback);
+ },
+
+ getInstallsByTypes: function AM_getInstallsByTypes(aTypes, aCallback) {
+ AddonManagerInternal.getInstallsByTypes(aTypes, aCallback);
+ },
+
+ getAllInstalls: function AM_getAllInstalls(aCallback) {
+ AddonManagerInternal.getAllInstalls(aCallback);
+ },
+
+ mapURIToAddonID: function AM_mapURIToAddonID(aURI) {
+ return AddonManagerInternal.mapURIToAddonID(aURI);
+ },
+
+ isInstallEnabled: function AM_isInstallEnabled(aType) {
+ return AddonManagerInternal.isInstallEnabled(aType);
+ },
+
+ isInstallAllowed: function AM_isInstallAllowed(aType, aInstallingPrincipal) {
+ return AddonManagerInternal.isInstallAllowed(aType, ensurePrincipal(aInstallingPrincipal));
+ },
+
+ installAddonsFromWebpage: function AM_installAddonsFromWebpage(aType, aBrowser,
+ aInstallingPrincipal,
+ aInstalls) {
+ AddonManagerInternal.installAddonsFromWebpage(aType, aBrowser,
+ ensurePrincipal(aInstallingPrincipal),
+ aInstalls);
+ },
+
+ addManagerListener: function AM_addManagerListener(aListener) {
+ AddonManagerInternal.addManagerListener(aListener);
+ },
+
+ removeManagerListener: function AM_removeManagerListener(aListener) {
+ AddonManagerInternal.removeManagerListener(aListener);
+ },
+
+ addInstallListener: function AM_addInstallListener(aListener) {
+ AddonManagerInternal.addInstallListener(aListener);
+ },
+
+ removeInstallListener: function AM_removeInstallListener(aListener) {
+ AddonManagerInternal.removeInstallListener(aListener);
+ },
+
+ addAddonListener: function AM_addAddonListener(aListener) {
+ AddonManagerInternal.addAddonListener(aListener);
+ },
+
+ removeAddonListener: function AM_removeAddonListener(aListener) {
+ AddonManagerInternal.removeAddonListener(aListener);
+ },
+
+ addTypeListener: function AM_addTypeListener(aListener) {
+ AddonManagerInternal.addTypeListener(aListener);
+ },
+
+ removeTypeListener: function AM_removeTypeListener(aListener) {
+ AddonManagerInternal.removeTypeListener(aListener);
+ },
+
+ get addonTypes() {
+ return AddonManagerInternal.addonTypes;
+ },
+
+ /**
+ * Determines whether an Addon should auto-update or not.
+ *
+ * @param aAddon
+ * The Addon representing the add-on
+ * @return true if the addon should auto-update, false otherwise.
+ */
+ shouldAutoUpdate: function AM_shouldAutoUpdate(aAddon) {
+ if (!aAddon || typeof aAddon != "object")
+ throw Components.Exception("aAddon must be specified",
+ Cr.NS_ERROR_INVALID_ARG);
+
+ if (!("applyBackgroundUpdates" in aAddon))
+ return false;
+ if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE)
+ return true;
+ if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE)
+ return false;
+ return this.autoUpdateDefault;
+ },
+
+ get checkCompatibility() {
+ return AddonManagerInternal.checkCompatibility;
+ },
+
+ set checkCompatibility(aValue) {
+ AddonManagerInternal.checkCompatibility = aValue;
+ },
+
+ get strictCompatibility() {
+ return AddonManagerInternal.strictCompatibility;
+ },
+
+ set strictCompatibility(aValue) {
+ AddonManagerInternal.strictCompatibility = aValue;
+ },
+
+ get checkUpdateSecurityDefault() {
+ return AddonManagerInternal.checkUpdateSecurityDefault;
+ },
+
+ get checkUpdateSecurity() {
+ return AddonManagerInternal.checkUpdateSecurity;
+ },
+
+ set checkUpdateSecurity(aValue) {
+ AddonManagerInternal.checkUpdateSecurity = aValue;
+ },
+
+ get updateEnabled() {
+ return AddonManagerInternal.updateEnabled;
+ },
+
+ set updateEnabled(aValue) {
+ AddonManagerInternal.updateEnabled = aValue;
+ },
+
+ get autoUpdateDefault() {
+ return AddonManagerInternal.autoUpdateDefault;
+ },
+
+ set autoUpdateDefault(aValue) {
+ AddonManagerInternal.autoUpdateDefault = aValue;
+ },
+
+ escapeAddonURI: function AM_escapeAddonURI(aAddon, aUri, aAppVersion) {
+ return AddonManagerInternal.escapeAddonURI(aAddon, aUri, aAppVersion);
+ },
+
+ get shutdown() {
+ return gShutdownBarrier.client;
+ },
+};
+
+// load the timestamps module into AddonManagerInternal
+Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", AddonManagerInternal);
+Object.freeze(AddonManagerInternal);
+Object.freeze(AddonManagerPrivate);
+Object.freeze(AddonManager);
diff --git a/toolkit/mozapps/extensions/AddonPathService.cpp b/toolkit/mozapps/extensions/AddonPathService.cpp
new file mode 100644
index 000000000..006149100
--- /dev/null
+++ b/toolkit/mozapps/extensions/AddonPathService.cpp
@@ -0,0 +1,233 @@
+/* -*- 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 "AddonPathService.h"
+
+#include "amIAddonManager.h"
+#include "nsIURI.h"
+#include "nsXULAppAPI.h"
+#include "jsapi.h"
+#include "nsServiceManagerUtils.h"
+#include "nsLiteralString.h"
+#include "nsThreadUtils.h"
+#include "nsIIOService.h"
+#include "nsNetUtil.h"
+#include "nsIFileURL.h"
+#include "nsIResProtocolHandler.h"
+#include "nsIChromeRegistry.h"
+#include "nsIJARURI.h"
+#include "nsJSUtils.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/AddonPathService.h"
+#include "mozilla/Omnijar.h"
+
+#include <algorithm>
+
+namespace mozilla {
+
+struct PathEntryComparator
+{
+ typedef AddonPathService::PathEntry PathEntry;
+
+ bool Equals(const PathEntry& entry1, const PathEntry& entry2) const
+ {
+ return entry1.mPath == entry2.mPath;
+ }
+
+ bool LessThan(const PathEntry& entry1, const PathEntry& entry2) const
+ {
+ return entry1.mPath < entry2.mPath;
+ }
+};
+
+AddonPathService::AddonPathService()
+{
+}
+
+AddonPathService::~AddonPathService()
+{
+ sInstance = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(AddonPathService, amIAddonPathService)
+
+AddonPathService *AddonPathService::sInstance;
+
+/* static */ AddonPathService*
+AddonPathService::GetInstance()
+{
+ if (!sInstance) {
+ sInstance = new AddonPathService();
+ }
+ NS_ADDREF(sInstance);
+ return sInstance;
+}
+
+static JSAddonId*
+ConvertAddonId(const nsAString& addonIdString)
+{
+ AutoSafeJSContext cx;
+ JS::RootedValue strv(cx);
+ if (!mozilla::dom::ToJSValue(cx, addonIdString, &strv)) {
+ return nullptr;
+ }
+ JS::RootedString str(cx, strv.toString());
+ return JS::NewAddonId(cx, str);
+}
+
+JSAddonId*
+AddonPathService::Find(const nsAString& path)
+{
+ // Use binary search to find the nearest entry that is <= |path|.
+ PathEntryComparator comparator;
+ unsigned index = mPaths.IndexOfFirstElementGt(PathEntry(path, nullptr), comparator);
+ if (index == 0) {
+ return nullptr;
+ }
+ const PathEntry& entry = mPaths[index - 1];
+
+ // Return the entry's addon if its path is a prefix of |path|.
+ if (StringBeginsWith(path, entry.mPath)) {
+ return entry.mAddonId;
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+AddonPathService::FindAddonId(const nsAString& path, nsAString& addonIdString)
+{
+ if (JSAddonId* id = Find(path)) {
+ JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id));
+ AssignJSFlatString(addonIdString, flat);
+ }
+ return NS_OK;
+}
+
+/* static */ JSAddonId*
+AddonPathService::FindAddonId(const nsAString& path)
+{
+ // If no service has been created, then we're not going to find anything.
+ if (!sInstance) {
+ return nullptr;
+ }
+
+ return sInstance->Find(path);
+}
+
+NS_IMETHODIMP
+AddonPathService::InsertPath(const nsAString& path, const nsAString& addonIdString)
+{
+ JSAddonId* addonId = ConvertAddonId(addonIdString);
+
+ // Add the new path in sorted order.
+ PathEntryComparator comparator;
+ mPaths.InsertElementSorted(PathEntry(path, addonId), comparator);
+ return NS_OK;
+}
+
+static nsresult
+ResolveURI(nsIURI* aURI, nsAString& out)
+{
+ bool equals;
+ nsresult rv;
+ nsCOMPtr<nsIURI> uri;
+ nsAutoCString spec;
+
+ // Resolve resource:// URIs. At the end of this if/else block, we
+ // have both spec and uri variables identifying the same URI.
+ if (NS_SUCCEEDED(aURI->SchemeIs("resource", &equals)) && equals) {
+ nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ nsCOMPtr<nsIProtocolHandler> ph;
+ rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph));
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv));
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ rv = irph->ResolveURI(aURI, spec);
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+ } else if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &equals)) && equals) {
+ nsCOMPtr<nsIChromeRegistry> chromeReg =
+ mozilla::services::GetChromeRegistryService();
+ if (NS_WARN_IF(!chromeReg))
+ return NS_ERROR_UNEXPECTED;
+
+ rv = chromeReg->ConvertChromeURL(aURI, getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+ } else {
+ uri = aURI;
+ }
+
+ if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) {
+ nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ nsCOMPtr<nsIURI> jarFileURI;
+ rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI));
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ return ResolveURI(jarFileURI, out);
+ }
+
+ if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) {
+ nsCOMPtr<nsIFileURL> baseFileURL = do_QueryInterface(uri, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ nsCOMPtr<nsIFile> file;
+ rv = baseFileURL->GetFile(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ return file->GetPath(out);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+JSAddonId*
+MapURIToAddonID(nsIURI* aURI)
+{
+ if (!NS_IsMainThread() || !XRE_IsParentProcess()) {
+ return nullptr;
+ }
+
+ nsAutoString filePath;
+ nsresult rv = ResolveURI(aURI, filePath);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsCOMPtr<nsIFile> greJar = Omnijar::GetPath(Omnijar::GRE);
+ nsCOMPtr<nsIFile> appJar = Omnijar::GetPath(Omnijar::APP);
+ if (greJar && appJar) {
+ nsAutoString greJarString, appJarString;
+ if (NS_FAILED(greJar->GetPath(greJarString)) || NS_FAILED(appJar->GetPath(appJarString)))
+ return nullptr;
+
+ // If |aURI| is part of either Omnijar, then it can't be part of an
+ // add-on. This catches pretty much all URLs for Firefox content.
+ if (filePath.Equals(greJarString) || filePath.Equals(appJarString))
+ return nullptr;
+ }
+
+ // If it's not part of Firefox, we resort to binary searching through the
+ // add-on paths.
+ return AddonPathService::FindAddonId(filePath);
+}
+
+}
diff --git a/toolkit/mozapps/extensions/AddonPathService.h b/toolkit/mozapps/extensions/AddonPathService.h
new file mode 100644
index 000000000..f739b018f
--- /dev/null
+++ b/toolkit/mozapps/extensions/AddonPathService.h
@@ -0,0 +1,55 @@
+//* -*- 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/. */
+
+#ifndef AddonPathService_h
+#define AddonPathService_h
+
+#include "amIAddonPathService.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+class nsIURI;
+class JSAddonId;
+
+namespace mozilla {
+
+JSAddonId*
+MapURIToAddonID(nsIURI* aURI);
+
+class AddonPathService final : public amIAddonPathService
+{
+public:
+ AddonPathService();
+
+ static AddonPathService* GetInstance();
+
+ JSAddonId* Find(const nsAString& path);
+ static JSAddonId* FindAddonId(const nsAString& path);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_AMIADDONPATHSERVICE
+
+ struct PathEntry
+ {
+ nsString mPath;
+ JSAddonId* mAddonId;
+
+ PathEntry(const nsAString& aPath, JSAddonId* aAddonId)
+ : mPath(aPath), mAddonId(aAddonId)
+ {}
+ };
+
+private:
+ virtual ~AddonPathService();
+
+ // Paths are stored sorted in order of their mPath.
+ nsTArray<PathEntry> mPaths;
+
+ static AddonPathService* sInstance;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/mozapps/extensions/ChromeManifestParser.jsm b/toolkit/mozapps/extensions/ChromeManifestParser.jsm
new file mode 100644
index 000000000..9a77c5429
--- /dev/null
+++ b/toolkit/mozapps/extensions/ChromeManifestParser.jsm
@@ -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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["ChromeManifestParser"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const MSG_JAR_FLUSH = "AddonJarFlush";
+
+
+/**
+ * Sends local and remote notifications to flush a JAR file cache entry
+ *
+ * @param aJarFile
+ * The ZIP/XPI/JAR file as a nsIFile
+ */
+function flushJarCache(aJarFile) {
+ Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null);
+ Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster)
+ .broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
+}
+
+
+/**
+ * Parses chrome manifest files.
+ */
+this.ChromeManifestParser = {
+
+ /**
+ * Reads and parses a chrome manifest file located at a specified URI, and all
+ * secondary manifests it references.
+ *
+ * @param aURI
+ * A nsIURI pointing to a chrome manifest.
+ * Typically a file: or jar: URI.
+ * @return Array of objects describing each manifest instruction, in the form:
+ * { type: instruction-type, baseURI: string-uri, args: [arguments] }
+ **/
+ parseSync: function CMP_parseSync(aURI) {
+ function parseLine(aLine) {
+ let line = aLine.trim();
+ if (line.length == 0 || line.charAt(0) == '#')
+ return;
+ let tokens = line.split(/\s+/);
+ let type = tokens.shift();
+ if (type == "manifest") {
+ let uri = NetUtil.newURI(tokens.shift(), null, aURI);
+ data = data.concat(this.parseSync(uri));
+ } else {
+ data.push({type: type, baseURI: baseURI, args: tokens});
+ }
+ }
+
+ let contents = "";
+ try {
+ if (aURI.scheme == "jar")
+ contents = this._readFromJar(aURI);
+ else
+ contents = this._readFromFile(aURI);
+ } catch (e) {
+ // Silently fail.
+ }
+
+ if (!contents)
+ return [];
+
+ let baseURI = NetUtil.newURI(".", null, aURI).spec;
+
+ let data = [];
+ let lines = contents.split("\n");
+ lines.forEach(parseLine.bind(this));
+ return data;
+ },
+
+ _readFromJar: function CMP_readFromJar(aURI) {
+ let data = "";
+ let entries = [];
+ let readers = [];
+
+ try {
+ // Deconstrict URI, which can be nested jar: URIs.
+ let uri = aURI.clone();
+ while (uri instanceof Ci.nsIJARURI) {
+ entries.push(uri.JAREntry);
+ uri = uri.JARFile;
+ }
+
+ // Open the base jar.
+ let reader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ reader.open(uri.QueryInterface(Ci.nsIFileURL).file);
+ readers.push(reader);
+
+ // Open the nested jars.
+ for (let i = entries.length - 1; i > 0; i--) {
+ let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ innerReader.openInner(reader, entries[i]);
+ readers.push(innerReader);
+ reader = innerReader;
+ }
+
+ // First entry is the actual file we want to read.
+ let zis = reader.getInputStream(entries[0]);
+ data = NetUtil.readInputStreamToString(zis, zis.available());
+ }
+ finally {
+ // Close readers in reverse order.
+ for (let i = readers.length - 1; i >= 0; i--) {
+ readers[i].close();
+ flushJarCache(readers[i].file);
+ }
+ }
+
+ return data;
+ },
+
+ _readFromFile: function CMP_readFromFile(aURI) {
+ let file = aURI.QueryInterface(Ci.nsIFileURL).file;
+ if (!file.exists() || !file.isFile())
+ return "";
+
+ let data = "";
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ try {
+ fis.init(file, -1, -1, false);
+ data = NetUtil.readInputStreamToString(fis, fis.available());
+ } finally {
+ fis.close();
+ }
+ return data;
+ },
+
+ /**
+ * Detects if there were any instructions of a specified type in a given
+ * chrome manifest.
+ *
+ * @param aManifest
+ * Manifest data, as returned by ChromeManifestParser.parseSync().
+ * @param aType
+ * Instruction type to filter by.
+ * @return True if any matching instructions were found in the manifest.
+ */
+ hasType: function CMP_hasType(aManifest, aType) {
+ return aManifest.some(function hasType_matchEntryType(aEntry) {
+ return aEntry.type == aType;
+ });
+ }
+};
diff --git a/toolkit/mozapps/extensions/DeferredSave.jsm b/toolkit/mozapps/extensions/DeferredSave.jsm
new file mode 100644
index 000000000..d7f5b8864
--- /dev/null
+++ b/toolkit/mozapps/extensions/DeferredSave.jsm
@@ -0,0 +1,274 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+
+// Make it possible to mock out timers for testing
+let MakeTimer = () => Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+this.EXPORTED_SYMBOLS = ["DeferredSave"];
+
+// If delay parameter is not provided, default is 50 milliseconds.
+const DEFAULT_SAVE_DELAY_MS = 50;
+
+Cu.import("resource://gre/modules/Log.jsm");
+//Configure a logger at the parent 'DeferredSave' level to format
+//messages for all the modules under DeferredSave.*
+const DEFERREDSAVE_PARENT_LOGGER_ID = "DeferredSave";
+let parentLogger = Log.repository.getLogger(DEFERREDSAVE_PARENT_LOGGER_ID);
+parentLogger.level = Log.Level.Warn;
+let formatter = new Log.BasicFormatter();
+//Set parent logger (and its children) to append to
+//the Javascript section of the Browser Console
+parentLogger.addAppender(new Log.ConsoleAppender(formatter));
+//Set parent logger (and its children) to
+//also append to standard out
+parentLogger.addAppender(new Log.DumpAppender(formatter));
+
+//Provide the ability to enable/disable logging
+//messages at runtime.
+//If the "extensions.logging.enabled" preference is
+//missing or 'false', messages at the WARNING and higher
+//severity should be logged to the JS console and standard error.
+//If "extensions.logging.enabled" is set to 'true', messages
+//at DEBUG and higher should go to JS console and standard error.
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+/**
+* Preference listener which listens for a change in the
+* "extensions.logging.enabled" preference and changes the logging level of the
+* parent 'addons' level logger accordingly.
+*/
+var PrefObserver = {
+ init: function PrefObserver_init() {
+ Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
+ Services.obs.addObserver(this, "xpcom-shutdown", false);
+ this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
+ },
+
+ observe: function PrefObserver_observe(aSubject, aTopic, aData) {
+ if (aTopic == "xpcom-shutdown") {
+ Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
+ Services.obs.removeObserver(this, "xpcom-shutdown");
+ }
+ else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
+ let debugLogEnabled = false;
+ try {
+ debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
+ }
+ catch (e) {
+ }
+ if (debugLogEnabled) {
+ parentLogger.level = Log.Level.Debug;
+ }
+ else {
+ parentLogger.level = Log.Level.Warn;
+ }
+ }
+ }
+};
+
+PrefObserver.init();
+
+/**
+ * A module to manage deferred, asynchronous writing of data files
+ * to disk. Writing is deferred by waiting for a specified delay after
+ * a request to save the data, before beginning to write. If more than
+ * one save request is received during the delay, all requests are
+ * fulfilled by a single write.
+ *
+ * @constructor
+ * @param aPath
+ * String representing the full path of the file where the data
+ * is to be written.
+ * @param aDataProvider
+ * Callback function that takes no argument and returns the data to
+ * be written. If aDataProvider returns an ArrayBufferView, the
+ * bytes it contains are written to the file as is.
+ * If aDataProvider returns a String the data are UTF-8 encoded
+ * and then written to the file.
+ * @param [optional] aDelay
+ * The delay in milliseconds between the first saveChanges() call
+ * that marks the data as needing to be saved, and when the DeferredSave
+ * begins writing the data to disk. Default 50 milliseconds.
+ */
+this.DeferredSave = function (aPath, aDataProvider, aDelay) {
+ // Create a new logger (child of 'DeferredSave' logger)
+ // for use by this particular instance of DeferredSave object
+ let leafName = OS.Path.basename(aPath);
+ let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName;
+ this.logger = Log.repository.getLogger(logger_id);
+
+ // @type {Deferred|null}, null when no data needs to be written
+ // @resolves with the result of OS.File.writeAtomic when all writes complete
+ // @rejects with the error from OS.File.writeAtomic if the write fails,
+ // or with the error from aDataProvider() if that throws.
+ this._pending = null;
+
+ // @type {Promise}, completes when the in-progress write (if any) completes,
+ // kept as a resolved promise at other times to simplify logic.
+ // Because _deferredSave() always uses _writing.then() to execute
+ // its next action, we don't need a special case for whether a write
+ // is in progress - if the previous write is complete (and the _writing
+ // promise is already resolved/rejected), _writing.then() starts
+ // the next action immediately.
+ //
+ // @resolves with the result of OS.File.writeAtomic
+ // @rejects with the error from OS.File.writeAtomic
+ this._writing = Promise.resolve(0);
+
+ // Are we currently waiting for a write to complete
+ this.writeInProgress = false;
+
+ this._path = aPath;
+ this._dataProvider = aDataProvider;
+
+ this._timer = null;
+
+ // Some counters for telemetry
+ // The total number of times the file was written
+ this.totalSaves = 0;
+
+ // The number of times the data became dirty while
+ // another save was in progress
+ this.overlappedSaves = 0;
+
+ // Error returned by the most recent write (if any)
+ this._lastError = null;
+
+ if (aDelay && (aDelay > 0))
+ this._delay = aDelay;
+ else
+ this._delay = DEFAULT_SAVE_DELAY_MS;
+}
+
+this.DeferredSave.prototype = {
+ get dirty() {
+ return this._pending || this.writeInProgress;
+ },
+
+ get lastError() {
+ return this._lastError;
+ },
+
+ // Start the pending timer if data is dirty
+ _startTimer: function() {
+ if (!this._pending) {
+ return;
+ }
+
+ this.logger.debug("Starting timer");
+ if (!this._timer)
+ this._timer = MakeTimer();
+ this._timer.initWithCallback(() => this._deferredSave(),
+ this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+
+ /**
+ * Mark the current stored data dirty, and schedule a flush to disk
+ * @return A Promise<integer> that will be resolved after the data is written to disk;
+ * the promise is resolved with the number of bytes written.
+ */
+ saveChanges: function() {
+ this.logger.debug("Save changes");
+ if (!this._pending) {
+ if (this.writeInProgress) {
+ this.logger.debug("Data changed while write in progress");
+ this.overlappedSaves++;
+ }
+ this._pending = Promise.defer();
+ // Wait until the most recent write completes or fails (if it hasn't already)
+ // and then restart our timer
+ this._writing.then(count => this._startTimer(), error => this._startTimer());
+ }
+ return this._pending.promise;
+ },
+
+ _deferredSave: function() {
+ let pending = this._pending;
+ this._pending = null;
+ let writing = this._writing;
+ this._writing = pending.promise;
+
+ // In either the success or the exception handling case, we don't need to handle
+ // the error from _writing here; it's already being handled in another then()
+ let toSave = null;
+ try {
+ toSave = this._dataProvider();
+ }
+ catch(e) {
+ this.logger.error("Deferred save dataProvider failed", e);
+ writing.then(null, error => {})
+ .then(count => {
+ pending.reject(e);
+ });
+ return;
+ }
+
+ writing.then(null, error => {return 0;})
+ .then(count => {
+ this.logger.debug("Starting write");
+ this.totalSaves++;
+ this.writeInProgress = true;
+
+ OS.File.writeAtomic(this._path, toSave, {tmpPath: this._path + ".tmp"})
+ .then(
+ result => {
+ this._lastError = null;
+ this.writeInProgress = false;
+ this.logger.debug("Write succeeded");
+ pending.resolve(result);
+ },
+ error => {
+ this._lastError = error;
+ this.writeInProgress = false;
+ this.logger.warn("Write failed", error);
+ pending.reject(error);
+ });
+ });
+ },
+
+ /**
+ * Immediately save the dirty data to disk, skipping
+ * the delay of normal operation. Note that the write
+ * still happens asynchronously in the worker
+ * thread from OS.File.
+ *
+ * There are four possible situations:
+ * 1) Nothing to flush
+ * 2) Data is not currently being written, in-memory copy is dirty
+ * 3) Data is currently being written, in-memory copy is clean
+ * 4) Data is being written and in-memory copy is dirty
+ *
+ * @return Promise<integer> that will resolve when all in-memory data
+ * has finished being flushed, returning the number of bytes
+ * written. If all in-memory data is clean, completes with the
+ * result of the most recent write.
+ */
+ flush: function() {
+ // If we have pending changes, cancel our timer and set up the write
+ // immediately (_deferredSave queues the write for after the most
+ // recent write completes, if it hasn't already)
+ if (this._pending) {
+ this.logger.debug("Flush called while data is dirty");
+ if (this._timer) {
+ this._timer.cancel();
+ this._timer = null;
+ }
+ this._deferredSave();
+ }
+
+ return this._writing;
+ }
+};
diff --git a/toolkit/mozapps/extensions/LightweightThemeManager.jsm b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
new file mode 100644
index 000000000..5856bfa91
--- /dev/null
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -0,0 +1,810 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+this.EXPORTED_SYMBOLS = ["LightweightThemeManager"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const ID_SUFFIX = "@personas.mozilla.org";
+const PREF_LWTHEME_TO_SELECT = "extensions.lwThemeToSelect";
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+const PREF_EM_DSS_ENABLED = "extensions.dss.enabled";
+const ADDON_TYPE = "theme";
+
+const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
+
+const STRING_TYPE_NAME = "type.%ID%.name";
+
+const DEFAULT_MAX_USED_THEMES_COUNT = 30;
+
+const MAX_PREVIEW_SECONDS = 30;
+
+const MANDATORY = ["id", "name", "headerURL"];
+const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL",
+ "previewURL", "author", "description", "homepageURL",
+ "updateURL", "version"];
+
+const PERSIST_ENABLED = true;
+const PERSIST_BYPASS_CACHE = false;
+const PERSIST_FILES = {
+ headerURL: "lightweighttheme-header",
+ footerURL: "lightweighttheme-footer"
+};
+
+XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeImageOptimizer",
+ "resource://gre/modules/addons/LightweightThemeImageOptimizer.jsm");
+
+this.__defineGetter__("_prefs", function prefsGetter() {
+ delete this._prefs;
+ return this._prefs = Services.prefs.getBranch("lightweightThemes.");
+});
+
+this.__defineGetter__("_maxUsedThemes", function maxUsedThemesGetter() {
+ delete this._maxUsedThemes;
+ try {
+ this._maxUsedThemes = _prefs.getIntPref("maxUsedThemes");
+ }
+ catch (e) {
+ this._maxUsedThemes = DEFAULT_MAX_USED_THEMES_COUNT;
+ }
+ return this._maxUsedThemes;
+});
+
+this.__defineSetter__("_maxUsedThemes", function maxUsedThemesSetter(aVal) {
+ delete this._maxUsedThemes;
+ return this._maxUsedThemes = aVal;
+});
+
+// Holds the ID of the theme being enabled or disabled while sending out the
+// events so cached AddonWrapper instances can return correct values for
+// permissions and pendingOperations
+var _themeIDBeingEnabled = null;
+var _themeIDBeingDisabled = null;
+
+this.LightweightThemeManager = {
+ get name() "LightweightThemeManager",
+
+ get usedThemes () {
+ try {
+ return JSON.parse(_prefs.getComplexValue("usedThemes",
+ Ci.nsISupportsString).data);
+ } catch (e) {
+ return [];
+ }
+ },
+
+ get currentTheme () {
+ try {
+ if (_prefs.getBoolPref("isThemeSelected"))
+ var data = this.usedThemes[0];
+ } catch (e) {}
+
+ return data || null;
+ },
+
+ get currentThemeForDisplay () {
+ var data = this.currentTheme;
+
+ if (data && PERSIST_ENABLED) {
+ for (let key in PERSIST_FILES) {
+ try {
+ if (data[key] && _prefs.getBoolPref("persisted." + key))
+ data[key] = _getLocalImageURI(PERSIST_FILES[key]).spec
+ + "?" + data.id + ";" + _version(data);
+ } catch (e) {}
+ }
+ }
+
+ return data;
+ },
+
+ set currentTheme (aData) {
+ return _setCurrentTheme(aData, false);
+ },
+
+ setLocalTheme: function LightweightThemeManager_setLocalTheme(aData) {
+ _setCurrentTheme(aData, true);
+ },
+
+ getUsedTheme: function LightweightThemeManager_getUsedTheme(aId) {
+ var usedThemes = this.usedThemes;
+ for (let usedTheme of usedThemes) {
+ if (usedTheme.id == aId)
+ return usedTheme;
+ }
+ return null;
+ },
+
+ forgetUsedTheme: function LightweightThemeManager_forgetUsedTheme(aId) {
+ let theme = this.getUsedTheme(aId);
+ if (!theme)
+ return;
+
+ let wrapper = new AddonWrapper(theme);
+ AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
+
+ var currentTheme = this.currentTheme;
+ if (currentTheme && currentTheme.id == aId) {
+ this.themeChanged(null);
+ AddonManagerPrivate.notifyAddonChanged(null, ADDON_TYPE, false);
+ }
+
+ _updateUsedThemes(_usedThemesExceptId(aId));
+ AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
+ },
+
+ previewTheme: function LightweightThemeManager_previewTheme(aData) {
+ let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+ cancel.data = false;
+ Services.obs.notifyObservers(cancel, "lightweight-theme-preview-requested",
+ JSON.stringify(aData));
+ if (cancel.data)
+ return;
+
+ if (_previewTimer)
+ _previewTimer.cancel();
+ else
+ _previewTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ _previewTimer.initWithCallback(_previewTimerCallback,
+ MAX_PREVIEW_SECONDS * 1000,
+ _previewTimer.TYPE_ONE_SHOT);
+
+ _notifyWindows(aData);
+ },
+
+ resetPreview: function LightweightThemeManager_resetPreview() {
+ if (_previewTimer) {
+ _previewTimer.cancel();
+ _previewTimer = null;
+ _notifyWindows(this.currentThemeForDisplay);
+ }
+ },
+
+ parseTheme: function LightweightThemeManager_parseTheme(aString, aBaseURI) {
+ try {
+ return _sanitizeTheme(JSON.parse(aString), aBaseURI, false);
+ } catch (e) {
+ return null;
+ }
+ },
+
+ updateCurrentTheme: function LightweightThemeManager_updateCurrentTheme() {
+ try {
+ if (!_prefs.getBoolPref("update.enabled"))
+ return;
+ } catch (e) {
+ return;
+ }
+
+ var theme = this.currentTheme;
+ if (!theme || !theme.updateURL)
+ return;
+
+ var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest);
+
+ req.mozBackgroundRequest = true;
+ req.overrideMimeType("text/plain");
+ req.open("GET", theme.updateURL, true);
+ // Prevent the request from reading from the cache.
+ req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ // Prevent the request from writing to the cache.
+ req.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+
+ var self = this;
+ req.addEventListener("load", function loadEventListener() {
+ if (req.status != 200)
+ return;
+
+ let newData = self.parseTheme(req.responseText, theme.updateURL);
+ if (!newData ||
+ newData.id != theme.id ||
+ _version(newData) == _version(theme))
+ return;
+
+ var currentTheme = self.currentTheme;
+ if (currentTheme && currentTheme.id == theme.id)
+ self.currentTheme = newData;
+ }, false);
+
+ req.send(null);
+ },
+
+ /**
+ * Switches to a new lightweight theme.
+ *
+ * @param aData
+ * The lightweight theme to switch to
+ */
+ themeChanged: function LightweightThemeManager_themeChanged(aData) {
+ if (_previewTimer) {
+ _previewTimer.cancel();
+ _previewTimer = null;
+ }
+
+ if (aData) {
+ let usedThemes = _usedThemesExceptId(aData.id);
+ usedThemes.unshift(aData);
+ _updateUsedThemes(usedThemes);
+ if (PERSIST_ENABLED) {
+ LightweightThemeImageOptimizer.purge();
+ _persistImages(aData, function themeChanged_persistImages() {
+ _notifyWindows(this.currentThemeForDisplay);
+ }.bind(this));
+ }
+ }
+
+ _prefs.setBoolPref("isThemeSelected", aData != null);
+ _notifyWindows(aData);
+ Services.obs.notifyObservers(null, "lightweight-theme-changed", null);
+ },
+
+ /**
+ * Starts the Addons provider and enables the new lightweight theme if
+ * necessary.
+ */
+ startup: function LightweightThemeManager_startup() {
+ if (Services.prefs.prefHasUserValue(PREF_LWTHEME_TO_SELECT)) {
+ let id = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
+ if (id)
+ this.themeChanged(this.getUsedTheme(id));
+ else
+ this.themeChanged(null);
+ Services.prefs.clearUserPref(PREF_LWTHEME_TO_SELECT);
+ }
+
+ _prefs.addObserver("", _prefObserver, false);
+ },
+
+ /**
+ * Shuts down the provider.
+ */
+ shutdown: function LightweightThemeManager_shutdown() {
+ _prefs.removeObserver("", _prefObserver);
+ },
+
+ /**
+ * Called when a new add-on has been enabled when only one add-on of that type
+ * can be enabled.
+ *
+ * @param aId
+ * The ID of the newly enabled add-on
+ * @param aType
+ * The type of the newly enabled add-on
+ * @param aPendingRestart
+ * true if the newly enabled add-on will only become enabled after a
+ * restart
+ */
+ addonChanged: function LightweightThemeManager_addonChanged(aId, aType, aPendingRestart) {
+ if (aType != ADDON_TYPE)
+ return;
+
+ let id = _getInternalID(aId);
+ let current = this.currentTheme;
+
+ try {
+ let next = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
+ if (id == next && aPendingRestart)
+ return;
+
+ Services.prefs.clearUserPref(PREF_LWTHEME_TO_SELECT);
+ if (next) {
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled",
+ new AddonWrapper(this.getUsedTheme(next)));
+ }
+ else {
+ if (id == current.id) {
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled",
+ new AddonWrapper(current));
+ return;
+ }
+ }
+ }
+ catch (e) {
+ }
+
+ if (current) {
+ if (current.id == id)
+ return;
+ _themeIDBeingDisabled = current.id;
+ let wrapper = new AddonWrapper(current);
+ if (aPendingRestart) {
+ Services.prefs.setCharPref(PREF_LWTHEME_TO_SELECT, "");
+ AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, true);
+ }
+ else {
+ AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, false);
+ this.themeChanged(null);
+ AddonManagerPrivate.callAddonListeners("onDisabled", wrapper);
+ }
+ _themeIDBeingDisabled = null;
+ }
+
+ if (id) {
+ let theme = this.getUsedTheme(id);
+ _themeIDBeingEnabled = id;
+ let wrapper = new AddonWrapper(theme);
+ if (aPendingRestart) {
+ AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, true);
+ Services.prefs.setCharPref(PREF_LWTHEME_TO_SELECT, id);
+
+ // Flush the preferences to disk so they survive any crash
+ Services.prefs.savePrefFile(null);
+ }
+ else {
+ AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, false);
+ this.themeChanged(theme);
+ AddonManagerPrivate.callAddonListeners("onEnabled", wrapper);
+ }
+ _themeIDBeingEnabled = null;
+ }
+ },
+
+ /**
+ * Called to get an Addon with a particular ID.
+ *
+ * @param aId
+ * The ID of the add-on to retrieve
+ * @param aCallback
+ * A callback to pass the Addon to
+ */
+ getAddonByID: function LightweightThemeManager_getAddonByID(aId, aCallback) {
+ let id = _getInternalID(aId);
+ if (!id) {
+ aCallback(null);
+ return;
+ }
+
+ let theme = this.getUsedTheme(id);
+ if (!theme) {
+ aCallback(null);
+ return;
+ }
+
+ aCallback(new AddonWrapper(theme));
+ },
+
+ /**
+ * Called to get Addons of a particular type.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types.
+ * @param aCallback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsByTypes: function LightweightThemeManager_getAddonsByTypes(aTypes, aCallback) {
+ if (aTypes && aTypes.indexOf(ADDON_TYPE) == -1) {
+ aCallback([]);
+ return;
+ }
+
+ // Tycho: aCallback([new AddonWrapper(a) for each (a in this.usedThemes)]);
+ let result = [];
+ for each(let a in this.usedThemes) {
+ result.push(new AddonWrapper(a));
+ }
+
+ aCallback(result);
+ },
+};
+
+/**
+ * The AddonWrapper wraps lightweight theme to provide the data visible to
+ * consumers of the AddonManager API.
+ */
+function AddonWrapper(aTheme) {
+ this.__defineGetter__("id", function AddonWrapper_idGetter() aTheme.id + ID_SUFFIX);
+ this.__defineGetter__("type", function AddonWrapper_typeGetter() ADDON_TYPE);
+ this.__defineGetter__("isActive", function AddonWrapper_isActiveGetter() {
+ let current = LightweightThemeManager.currentTheme;
+ if (current)
+ return aTheme.id == current.id;
+ return false;
+ });
+
+ this.__defineGetter__("name", function AddonWrapper_nameGetter() aTheme.name);
+ this.__defineGetter__("version", function AddonWrapper_versionGetter() {
+ return "version" in aTheme ? aTheme.version : "";
+ });
+
+ ["description", "homepageURL", "iconURL"].forEach(function(prop) {
+ this.__defineGetter__(prop, function AddonWrapper_optionalPropGetter() {
+ return prop in aTheme ? aTheme[prop] : null;
+ });
+ }, this);
+
+ ["installDate", "updateDate"].forEach(function(prop) {
+ this.__defineGetter__(prop, function AddonWrapper_datePropGetter() {
+ return prop in aTheme ? new Date(aTheme[prop]) : null;
+ });
+ }, this);
+
+ this.__defineGetter__("creator", function AddonWrapper_creatorGetter() {
+ return new AddonManagerPrivate.AddonAuthor(aTheme.author);
+ });
+
+ this.__defineGetter__("screenshots", function AddonWrapper_screenshotsGetter() {
+ let url = aTheme.previewURL;
+ return [new AddonManagerPrivate.AddonScreenshot(url)];
+ });
+
+ this.__defineGetter__("pendingOperations",
+ function AddonWrapper_pendingOperationsGetter() {
+ let pending = AddonManager.PENDING_NONE;
+ if (this.isActive == this.userDisabled)
+ pending |= this.isActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE;
+ return pending;
+ });
+
+ this.__defineGetter__("operationsRequiringRestart",
+ function AddonWrapper_operationsRequiringRestartGetter() {
+ // If a non-default theme is in use then a restart will be required to
+ // enable lightweight themes unless dynamic theme switching is enabled
+ if (Services.prefs.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN)) {
+ try {
+ if (Services.prefs.getBoolPref(PREF_EM_DSS_ENABLED))
+ return AddonManager.OP_NEEDS_RESTART_NONE;
+ }
+ catch (e) {
+ }
+ return AddonManager.OP_NEEDS_RESTART_ENABLE;
+ }
+
+ return AddonManager.OP_NEEDS_RESTART_NONE;
+ });
+
+ this.__defineGetter__("size", function AddonWrapper_sizeGetter() {
+ // The size changes depending on whether the theme is in use or not, this is
+ // probably not worth exposing.
+ return null;
+ });
+
+ this.__defineGetter__("permissions", function AddonWrapper_permissionsGetter() {
+ let permissions = AddonManager.PERM_CAN_UNINSTALL;
+ if (this.userDisabled)
+ permissions |= AddonManager.PERM_CAN_ENABLE;
+ else
+ permissions |= AddonManager.PERM_CAN_DISABLE;
+ return permissions;
+ });
+
+ this.__defineGetter__("userDisabled", function AddonWrapper_userDisabledGetter() {
+ if (_themeIDBeingEnabled == aTheme.id)
+ return false;
+ if (_themeIDBeingDisabled == aTheme.id)
+ return true;
+
+ try {
+ let toSelect = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
+ return aTheme.id != toSelect;
+ }
+ catch (e) {
+ let current = LightweightThemeManager.currentTheme;
+ return !current || current.id != aTheme.id;
+ }
+ });
+
+ this.__defineSetter__("userDisabled", function AddonWrapper_userDisabledSetter(val) {
+ if (val == this.userDisabled)
+ return val;
+
+ if (val)
+ LightweightThemeManager.currentTheme = null;
+ else
+ LightweightThemeManager.currentTheme = aTheme;
+
+ return val;
+ });
+
+ this.uninstall = function AddonWrapper_uninstall() {
+ LightweightThemeManager.forgetUsedTheme(aTheme.id);
+ };
+
+ this.cancelUninstall = function AddonWrapper_cancelUninstall() {
+ throw new Error("Theme is not marked to be uninstalled");
+ };
+
+ this.findUpdates = function AddonWrapper_findUpdates(listener, reason, appVersion, platformVersion) {
+ AddonManagerPrivate.callNoUpdateListeners(this, listener, reason, appVersion, platformVersion);
+ };
+}
+
+AddonWrapper.prototype = {
+ // Lightweight themes are never disabled by the application
+ get appDisabled() {
+ return false;
+ },
+
+ // Lightweight themes are always compatible
+ get isCompatible() {
+ return true;
+ },
+
+ get isPlatformCompatible() {
+ return true;
+ },
+
+ get scope() {
+ return AddonManager.SCOPE_PROFILE;
+ },
+
+ get foreignInstall() {
+ return false;
+ },
+
+ // Lightweight themes are always compatible
+ isCompatibleWith: function AddonWrapper_isCompatibleWith(appVersion, platformVersion) {
+ return true;
+ },
+
+ // Lightweight themes are always securely updated
+ get providesUpdatesSecurely() {
+ return true;
+ },
+
+ // Lightweight themes are never blocklisted
+ get blocklistState() {
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ }
+};
+
+/**
+ * Converts the ID used by the public AddonManager API to an lightweight theme
+ * ID.
+ *
+ * @param id
+ * The ID to be converted
+ *
+ * @return the lightweight theme ID or null if the ID was not for a lightweight
+ * theme.
+ */
+function _getInternalID(id) {
+ if (!id)
+ return null;
+ let len = id.length - ID_SUFFIX.length;
+ if (len > 0 && id.substring(len) == ID_SUFFIX)
+ return id.substring(0, len);
+ return null;
+}
+
+function _setCurrentTheme(aData, aLocal) {
+ aData = _sanitizeTheme(aData, null, aLocal);
+
+ let needsRestart = (ADDON_TYPE == "theme") &&
+ Services.prefs.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN);
+
+ let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+ cancel.data = false;
+ Services.obs.notifyObservers(cancel, "lightweight-theme-change-requested",
+ JSON.stringify(aData));
+
+ if (aData) {
+ let theme = LightweightThemeManager.getUsedTheme(aData.id);
+ let isInstall = !theme || theme.version != aData.version;
+ if (isInstall) {
+ aData.updateDate = Date.now();
+ if (theme && "installDate" in theme)
+ aData.installDate = theme.installDate;
+ else
+ aData.installDate = aData.updateDate;
+
+ var oldWrapper = theme ? new AddonWrapper(theme) : null;
+ var wrapper = new AddonWrapper(aData);
+ AddonManagerPrivate.callInstallListeners("onExternalInstall", null,
+ wrapper, oldWrapper, false);
+ AddonManagerPrivate.callAddonListeners("onInstalling", wrapper, false);
+ }
+
+ let current = LightweightThemeManager.currentTheme;
+ let usedThemes = _usedThemesExceptId(aData.id);
+ if (current && current.id != aData.id)
+ usedThemes.splice(1, 0, aData);
+ else
+ usedThemes.unshift(aData);
+ _updateUsedThemes(usedThemes);
+
+ if (isInstall)
+ AddonManagerPrivate.callAddonListeners("onInstalled", wrapper);
+ }
+
+ if (cancel.data)
+ return null;
+
+ AddonManagerPrivate.notifyAddonChanged(aData ? aData.id + ID_SUFFIX : null,
+ ADDON_TYPE, needsRestart);
+
+ return LightweightThemeManager.currentTheme;
+}
+
+function _sanitizeTheme(aData, aBaseURI, aLocal) {
+ if (!aData || typeof aData != "object")
+ return null;
+
+ var resourceProtocols = ["http", "https", "resource"];
+ if (aLocal)
+ resourceProtocols.push("file");
+ var resourceProtocolExp = new RegExp("^(" + resourceProtocols.join("|") + "):");
+
+ function sanitizeProperty(prop) {
+ if (!(prop in aData))
+ return null;
+ if (typeof aData[prop] != "string")
+ return null;
+ let val = aData[prop].trim();
+ if (!val)
+ return null;
+
+ if (!/URL$/.test(prop))
+ return val;
+
+ try {
+ val = _makeURI(val, aBaseURI ? _makeURI(aBaseURI) : null).spec;
+ if ((prop == "updateURL" ? /^https:/ : resourceProtocolExp).test(val))
+ return val;
+ return null;
+ }
+ catch (e) {
+ return null;
+ }
+ }
+
+ let result = {};
+ for (let mandatoryProperty of MANDATORY) {
+ let val = sanitizeProperty(mandatoryProperty);
+ if (!val)
+ throw Components.results.NS_ERROR_INVALID_ARG;
+ result[mandatoryProperty] = val;
+ }
+
+ for (let optionalProperty of OPTIONAL) {
+ let val = sanitizeProperty(optionalProperty);
+ if (!val)
+ continue;
+ result[optionalProperty] = val;
+ }
+
+ return result;
+}
+
+function _usedThemesExceptId(aId)
+ LightweightThemeManager.usedThemes.filter(
+ function usedThemesExceptId_filterID(t) "id" in t && t.id != aId);
+
+function _version(aThemeData)
+ aThemeData.version || "";
+
+function _makeURI(aURL, aBaseURI)
+ Services.io.newURI(aURL, null, aBaseURI);
+
+function _updateUsedThemes(aList) {
+ // Send uninstall events for all themes that need to be removed.
+ while (aList.length > _maxUsedThemes) {
+ let wrapper = new AddonWrapper(aList[aList.length - 1]);
+ AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
+ aList.pop();
+ AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
+ }
+
+ var str = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ str.data = JSON.stringify(aList);
+ _prefs.setComplexValue("usedThemes", Ci.nsISupportsString, str);
+
+ Services.obs.notifyObservers(null, "lightweight-theme-list-changed", null);
+}
+
+function _notifyWindows(aThemeData) {
+ Services.obs.notifyObservers(null, "lightweight-theme-styling-update",
+ JSON.stringify(aThemeData));
+}
+
+var _previewTimer;
+var _previewTimerCallback = {
+ notify: function _previewTimerCallback_notify() {
+ LightweightThemeManager.resetPreview();
+ }
+};
+
+/**
+ * Called when any of the lightweightThemes preferences are changed.
+ */
+function _prefObserver(aSubject, aTopic, aData) {
+ switch (aData) {
+ case "maxUsedThemes":
+ try {
+ _maxUsedThemes = _prefs.getIntPref(aData);
+ }
+ catch (e) {
+ _maxUsedThemes = DEFAULT_MAX_USED_THEMES_COUNT;
+ }
+ // Update the theme list to remove any themes over the number we keep
+ _updateUsedThemes(LightweightThemeManager.usedThemes);
+ break;
+ }
+}
+
+function _persistImages(aData, aCallback) {
+ function onSuccess(key) function () {
+ let current = LightweightThemeManager.currentTheme;
+ if (current && current.id == aData.id) {
+ _prefs.setBoolPref("persisted." + key, true);
+ }
+ if (--numFilesToPersist == 0 && aCallback) {
+ aCallback();
+ }
+ };
+
+ let numFilesToPersist = 0;
+ for (let key in PERSIST_FILES) {
+ _prefs.setBoolPref("persisted." + key, false);
+ if (aData[key]) {
+ numFilesToPersist++;
+ _persistImage(aData[key], PERSIST_FILES[key], onSuccess(key));
+ }
+ }
+}
+
+function _getLocalImageURI(localFileName) {
+ var localFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ localFile.append(localFileName);
+ return Services.io.newFileURI(localFile);
+}
+
+function _persistImage(sourceURL, localFileName, successCallback) {
+ if (/^(file|resource):/.test(sourceURL))
+ return;
+
+ var targetURI = _getLocalImageURI(localFileName);
+ var sourceURI = _makeURI(sourceURL);
+
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist);
+
+ persist.persistFlags =
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
+ (PERSIST_BYPASS_CACHE ?
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE :
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE);
+
+ persist.progressListener = new _persistProgressListener(successCallback);
+
+ persist.saveURI(sourceURI, null,
+ null, Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
+ null, null, targetURI, null);
+}
+
+function _persistProgressListener(successCallback) {
+ this.onLocationChange = function persistProgressListener_onLocationChange() {};
+ this.onProgressChange = function persistProgressListener_onProgressChange() {};
+ this.onStatusChange = function persistProgressListener_onStatusChange() {};
+ this.onSecurityChange = function persistProgressListener_onSecurityChange() {};
+ this.onStateChange = function persistProgressListener_onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aRequest &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ try {
+ if (aRequest.QueryInterface(Ci.nsIHttpChannel).requestSucceeded) {
+ // success
+ successCallback();
+ return;
+ }
+ } catch (e) { }
+ // failure
+ }
+ };
+}
+
+AddonManagerPrivate.registerProvider(LightweightThemeManager, [
+ new AddonManagerPrivate.AddonType("theme", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 5000)
+]);
diff --git a/toolkit/mozapps/extensions/addonManager.js b/toolkit/mozapps/extensions/addonManager.js
new file mode 100644
index 000000000..862b1ea69
--- /dev/null
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -0,0 +1,204 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 serves as integration between the platform and AddonManager.
+ * It is responsible for initializing and shutting down the AddonManager as well
+ * as passing new installs from webpages to the AddonManager.
+ */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
+
+// The old XPInstall error codes
+const EXECUTION_ERROR = -203;
+const CANT_READ_ARCHIVE = -207;
+const USER_CANCELLED = -210;
+const DOWNLOAD_ERROR = -228;
+const UNSUPPORTED_TYPE = -244;
+const SUCCESS = 0;
+
+const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
+const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
+
+const CHILD_SCRIPT = "resource://gre/modules/addons/Content.js";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+let gSingleton = null;
+
+let gParentMM = null;
+
+
+function amManager() {
+ Cu.import("resource://gre/modules/AddonManager.jsm");
+
+ let globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager);
+ globalMM.loadFrameScript(CHILD_SCRIPT, true);
+ globalMM.addMessageListener(MSG_INSTALL_ADDONS, this);
+
+ gParentMM = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager);
+ gParentMM.addMessageListener(MSG_INSTALL_ENABLED, this);
+
+ // Needed so receiveMessage can be called directly by JS callers
+ this.wrappedJSObject = this;
+}
+
+amManager.prototype = {
+ observe: function AMC_observe(aSubject, aTopic, aData) {
+ if (aTopic == "addons-startup")
+ AddonManagerPrivate.startup();
+ },
+
+ /**
+ * @see amIAddonManager.idl
+ */
+ mapURIToAddonID: function AMC_mapURIToAddonID(uri, id) {
+ id.value = AddonManager.mapURIToAddonID(uri);
+ return !!id.value;
+ },
+
+ /**
+ * @see amIWebInstaller.idl
+ */
+ isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) {
+ return AddonManager.isInstallEnabled(aMimetype);
+ },
+
+ /**
+ * @see amIWebInstaller.idl
+ */
+ installAddonsFromWebpage: function AMC_installAddonsFromWebpage(aMimetype,
+ aBrowser,
+ aInstallingPrincipal,
+ aUris, aHashes,
+ aNames, aIcons,
+ aCallback) {
+ if (aUris.length == 0)
+ return false;
+
+ let retval = true;
+ if (!AddonManager.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
+ aCallback = null;
+ retval = false;
+ }
+
+ let installs = [];
+ function buildNextInstall() {
+ if (aUris.length == 0) {
+ AddonManager.installAddonsFromWebpage(aMimetype, aBrowser, aInstallingPrincipal, installs);
+ return;
+ }
+ let uri = aUris.shift();
+ AddonManager.getInstallForURL(uri, function buildNextInstall_getInstallForURL(aInstall) {
+ function callCallback(aUri, aStatus) {
+ try {
+ aCallback.onInstallEnded(aUri, aStatus);
+ }
+ catch (e) {
+ Components.utils.reportError(e);
+ }
+ }
+
+ if (aInstall) {
+ installs.push(aInstall);
+ if (aCallback) {
+ aInstall.addListener({
+ onDownloadCancelled: function buildNextInstall_onDownloadCancelled(aInstall) {
+ callCallback(uri, USER_CANCELLED);
+ },
+
+ onDownloadFailed: function buildNextInstall_onDownloadFailed(aInstall) {
+ if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
+ callCallback(uri, CANT_READ_ARCHIVE);
+ else
+ callCallback(uri, DOWNLOAD_ERROR);
+ },
+
+ onInstallFailed: function buildNextInstall_onInstallFailed(aInstall) {
+ callCallback(uri, EXECUTION_ERROR);
+ },
+
+ onInstallEnded: function buildNextInstall_onInstallEnded(aInstall, aStatus) {
+ callCallback(uri, SUCCESS);
+ }
+ });
+ }
+ }
+ else if (aCallback) {
+ aCallback.onInstallEnded(uri, UNSUPPORTED_TYPE);
+ }
+ buildNextInstall();
+ }, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, aBrowser);
+ }
+ buildNextInstall();
+
+ return retval;
+ },
+
+ notify: function AMC_notify(aTimer) {
+ AddonManagerPrivate.backgroundUpdateTimerHandler();
+ },
+
+ /**
+ * messageManager callback function.
+ *
+ * Listens to requests from child processes for InstallTrigger
+ * activity, and sends back callbacks.
+ */
+ receiveMessage: function AMC_receiveMessage(aMessage) {
+ let payload = aMessage.data;
+
+ switch (aMessage.name) {
+ case MSG_INSTALL_ENABLED:
+ return AddonManager.isInstallEnabled(payload.mimetype);
+
+ case MSG_INSTALL_ADDONS: {
+ let callback = null;
+ if (payload.callbackID != -1) {
+ callback = {
+ onInstallEnded: function ITP_callback(url, status) {
+ gParentMM.broadcastAsyncMessage(MSG_INSTALL_CALLBACK, {
+ callbackID: payload.callbackID,
+ url: url,
+ status: status
+ });
+ },
+ };
+ }
+
+ return this.installAddonsFromWebpage(payload.mimetype,
+ aMessage.target, payload.triggeringPrincipal, payload.uris,
+ payload.hashes, payload.names, payload.icons, callback);
+ }
+ }
+ },
+
+ classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
+ _xpcom_factory: {
+ createInstance: function AMC_createInstance(aOuter, aIid) {
+ if (aOuter != null)
+ throw Components.Exception("Component does not support aggregation",
+ Cr.NS_ERROR_NO_AGGREGATION);
+
+ if (!gSingleton)
+ gSingleton = new amManager();
+ return gSingleton.QueryInterface(aIid);
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIAddonManager,
+ Ci.amIWebInstaller,
+ Ci.nsITimerCallback,
+ Ci.nsIObserver,
+ Ci.nsIMessageListener])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amManager]);
diff --git a/toolkit/mozapps/extensions/amContentHandler.js b/toolkit/mozapps/extensions/amContentHandler.js
new file mode 100644
index 000000000..708f670b1
--- /dev/null
+++ b/toolkit/mozapps/extensions/amContentHandler.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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const XPI_CONTENT_TYPE = "application/x-xpinstall";
+const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function amContentHandler() {
+}
+
+amContentHandler.prototype = {
+ /**
+ * Handles a new request for an application/x-xpinstall file.
+ *
+ * @param aMimetype
+ * The mimetype of the file
+ * @param aContext
+ * The context passed to nsIChannel.asyncOpen
+ * @param aRequest
+ * The nsIRequest dealing with the content
+ */
+ handleContent: function XCH_handleContent(aMimetype, aContext, aRequest) {
+ if (aMimetype != XPI_CONTENT_TYPE)
+ throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
+
+ if (!(aRequest instanceof Ci.nsIChannel))
+ throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
+
+ let uri = aRequest.URI;
+
+ let window = null;
+ let callbacks = aRequest.notificationCallbacks ?
+ aRequest.notificationCallbacks :
+ aRequest.loadGroup.notificationCallbacks;
+ if (callbacks)
+ window = callbacks.getInterface(Ci.nsIDOMWindow);
+
+ aRequest.cancel(Cr.NS_BINDING_ABORTED);
+
+ let installs = {
+ uris: [uri.spec],
+ hashes: [null],
+ names: [null],
+ icons: [null],
+ mimetype: XPI_CONTENT_TYPE,
+ triggeringPrincipal: aRequest.loadInfo.triggeringPrincipal,
+ callbackID: -1
+ };
+
+ if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+ // When running in the main process this might be a frame inside an
+ // in-content UI page, walk up to find the first frame element in a chrome
+ // privileged document
+ let element = window.frameElement;
+ let ssm = Services.scriptSecurityManager;
+ while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
+ element = element.ownerDocument.defaultView.frameElement;
+
+ if (element) {
+ let listener = Cc["@mozilla.org/addons/integration;1"].
+ getService(Ci.nsIMessageListener);
+ listener.wrappedJSObject.receiveMessage({
+ name: MSG_INSTALL_ADDONS,
+ target: element,
+ data: installs,
+ });
+ return;
+ }
+ }
+
+ // Fall back to sending through the message manager
+ let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+
+ messageManager.sendAsyncMessage(MSG_INSTALL_ADDONS, installs);
+ },
+
+ classID: Components.ID("{7beb3ba8-6ec3-41b4-b67c-da89b8518922}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]),
+
+ log : function XCH_log(aMsg) {
+ let msg = "amContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg);
+ Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
+ logStringMessage(msg);
+ dump(msg + "\n");
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amContentHandler]);
diff --git a/toolkit/mozapps/extensions/amIAddonManager.idl b/toolkit/mozapps/extensions/amIAddonManager.idl
new file mode 100644
index 000000000..58a58b62d
--- /dev/null
+++ b/toolkit/mozapps/extensions/amIAddonManager.idl
@@ -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/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+
+/**
+ * A service to make some AddonManager functionality available to C++ callers.
+ * Javascript callers should still use AddonManager.jsm directly.
+ */
+[scriptable, function, uuid(7b45d82d-7ad5-48d7-9b05-f32eb9818cd4)]
+interface amIAddonManager : nsISupports
+{
+ /**
+ * Synchronously map a URI to the corresponding Addon ID.
+ *
+ * Mappable URIs are limited to in-application resources belonging to the
+ * add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
+ * but do not include URIs from meta data, such as the add-on homepage.
+ *
+ * @param aURI
+ * The nsIURI to map
+ * @return
+ * true if the URI has been mapped successfully to an Addon ID
+ */
+ boolean mapURIToAddonID(in nsIURI aURI, out AUTF8String aID);
+};
diff --git a/toolkit/mozapps/extensions/amIAddonPathService.idl b/toolkit/mozapps/extensions/amIAddonPathService.idl
new file mode 100644
index 000000000..863689858
--- /dev/null
+++ b/toolkit/mozapps/extensions/amIAddonPathService.idl
@@ -0,0 +1,29 @@
+/* -*- 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"
+
+/**
+ * This service maps file system paths where add-ons reside to the ID
+ * of the add-on. Paths are added by the add-on manager. They can
+ * looked up by anyone.
+ */
+[scriptable, uuid(fcd9e270-dfb1-11e3-8b68-0800200c9a66)]
+interface amIAddonPathService : nsISupports
+{
+ /**
+ * Given a path to a file, return the ID of the add-on that the file belongs
+ * to. Returns an empty string if there is no add-on there. Note that if an
+ * add-on is located at /a/b/c, then looking up the path /a/b/c/d will return
+ * that add-on.
+ */
+ AString findAddonId(in AString path);
+
+ /**
+ * Call this function to inform the service that the given file system path is
+ * associated with the given add-on ID.
+ */
+ void insertPath(in AString path, in AString addonId);
+};
diff --git a/toolkit/mozapps/extensions/amIWebInstallListener.idl b/toolkit/mozapps/extensions/amIWebInstallListener.idl
new file mode 100644
index 000000000..eed108097
--- /dev/null
+++ b/toolkit/mozapps/extensions/amIWebInstallListener.idl
@@ -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/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMElement;
+interface nsIURI;
+interface nsIVariant;
+
+/**
+ * amIWebInstallInfo is used by the default implementation of
+ * amIWebInstallListener to communicate with the running application and allow
+ * it to warn the user about blocked installs and start the installs running.
+ */
+[scriptable, uuid(fa0b47a3-f819-47ac-bc66-4bd1d7f67b1d)]
+interface amIWebInstallInfo : nsISupports
+{
+ readonly attribute nsIDOMElement browser;
+ readonly attribute nsIURI originatingURI;
+ readonly attribute nsIVariant installs;
+
+ /**
+ * Starts all installs.
+ */
+ void install();
+};
+
+/**
+ * The registered amIWebInstallListener is used to notify about new installs
+ * triggered by websites. The default implementation displays a confirmation
+ * dialog when add-ons are ready to install and uses the observer service to
+ * notify when installations are blocked.
+ */
+[scriptable, uuid(d9240d4b-6b3a-4cad-b402-de6c93337e0c)]
+interface amIWebInstallListener : nsISupports
+{
+ /**
+ * Called when installation by websites is currently disabled.
+ *
+ * @param aBrowser
+ * The browser that triggered the installs
+ * @param aUri
+ * The URI of the site that triggered the installs
+ * @param aInstalls
+ * The AddonInstalls that were blocked
+ * @param aCount
+ * The number of AddonInstalls
+ */
+ void onWebInstallDisabled(in nsIDOMElement aBrowser, in nsIURI aUri,
+ [array, size_is(aCount)] in nsIVariant aInstalls,
+ [optional] in uint32_t aCount);
+
+ /**
+ * Called when the website is not allowed to directly prompt the user to
+ * install add-ons.
+ *
+ * @param aBrowser
+ * The browser that triggered the installs
+ * @param aUri
+ * The URI of the site that triggered the installs
+ * @param aInstalls
+ * The AddonInstalls that were blocked
+ * @param aCount
+ * The number of AddonInstalls
+ * @return true if the caller should start the installs
+ */
+ boolean onWebInstallBlocked(in nsIDOMElement aBrowser, in nsIURI aUri,
+ [array, size_is(aCount)] in nsIVariant aInstalls,
+ [optional] in uint32_t aCount);
+
+ /**
+ * Called when a website wants to ask the user to install add-ons.
+ *
+ * @param aBrowser
+ * The browser that triggered the installs
+ * @param aUri
+ * The URI of the site that triggered the installs
+ * @param aInstalls
+ * The AddonInstalls that were requested
+ * @param aCount
+ * The number of AddonInstalls
+ * @return true if the caller should start the installs
+ */
+ boolean onWebInstallRequested(in nsIDOMElement aBrowser, in nsIURI aUri,
+ [array, size_is(aCount)] in nsIVariant aInstalls,
+ [optional] in uint32_t aCount);
+};
+
+[scriptable, uuid(a80b89ad-bb1a-4c43-9cb7-3ae656556f78)]
+interface amIWebInstallListener2 : nsISupports
+{
+ /**
+ * Called when a non-same-origin resource attempted to initiate an install.
+ * Installs will have already been cancelled and cannot be restarted.
+ *
+ * @param aBrowser
+ * The browser that triggered the installs
+ * @param aUri
+ * The URI of the site that triggered the installs
+ * @param aInstalls
+ * The AddonInstalls that were blocked
+ * @param aCount
+ * The number of AddonInstalls
+ */
+ boolean onWebInstallOriginBlocked(in nsIDOMElement aBrowser, in nsIURI aUri,
+ [array, size_is(aCount)] in nsIVariant aInstalls,
+ [optional] in uint32_t aCount);
+};
+
+/**
+ * amIWebInstallPrompt is used, if available, by the default implementation of
+ * amIWebInstallInfo to display a confirmation UI to the user before running
+ * installs.
+ */
+[scriptable, uuid(386906f1-4d18-45bf-bc81-5dcd68e42c3b)]
+interface amIWebInstallPrompt : nsISupports
+{
+ /**
+ * Get a confirmation that the user wants to start the installs.
+ *
+ * @param aBrowser
+ * The browser that triggered the installs
+ * @param aUri
+ * The URI of the site that triggered the installs
+ * @param aInstalls
+ * The AddonInstalls that were requested
+ * @param aCount
+ * The number of AddonInstalls
+ */
+ void confirm(in nsIDOMElement aBrowser, in nsIURI aUri,
+ [array, size_is(aCount)] in nsIVariant aInstalls,
+ [optional] in uint32_t aCount);
+};
diff --git a/toolkit/mozapps/extensions/amIWebInstaller.idl b/toolkit/mozapps/extensions/amIWebInstaller.idl
new file mode 100644
index 000000000..6c5ebca67
--- /dev/null
+++ b/toolkit/mozapps/extensions/amIWebInstaller.idl
@@ -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/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMElement;
+interface nsIVariant;
+interface nsIURI;
+
+/**
+ * A callback function used to notify webpages when a requested install has
+ * ended.
+ *
+ * NOTE: This is *not* the same as InstallListener.
+ */
+[scriptable, function, uuid(bb22f5c0-3ca1-48f6-873c-54e87987700f)]
+interface amIInstallCallback : nsISupports
+{
+ /**
+ * Called when an install completes or fails.
+ *
+ * @param aUrl
+ * The url of the add-on being installed
+ * @param aStatus
+ * 0 if the install was successful or negative if not
+ */
+ void onInstallEnded(in AString aUrl, in int32_t aStatus);
+};
+
+
+/**
+ * This interface is used to allow webpages to start installing add-ons.
+ */
+[scriptable, uuid(658d6c09-15e0-4688-bee8-8551030472a9)]
+interface amIWebInstaller : nsISupports
+{
+ /**
+ * Checks if installation is enabled for a webpage.
+ *
+ * @param aMimetype
+ * The mimetype for the add-on to be installed
+ * @param referer
+ * The URL of the webpage trying to install an add-on
+ * @return true if installation is enabled
+ */
+ boolean isInstallEnabled(in AString aMimetype, in nsIURI aReferer);
+
+ /**
+ * Installs an array of add-ons at the request of a webpage
+ *
+ * @param aMimetype
+ * The mimetype for the add-ons
+ * @param aBrowser
+ * The browser installing the add-ons.
+ * @param aReferer
+ * The URI for the webpage installing the add-ons
+ * @param aUris
+ * The URIs of add-ons to be installed
+ * @param aHashes
+ * The hashes for the add-ons to be installed
+ * @param aNames
+ * The names for the add-ons to be installed
+ * @param aIcons
+ * The icons for the add-ons to be installed
+ * @param aCallback
+ * An optional callback to notify about installation success and
+ * failure
+ * @param aInstallCount
+ * An optional argument including the number of add-ons to install
+ * @return true if the installation was successfully started
+ */
+ boolean installAddonsFromWebpage(in AString aMimetype,
+ in nsIDOMElement aBrowser,
+ in nsIURI aReferer,
+ [array, size_is(aInstallCount)] in wstring aUris,
+ [array, size_is(aInstallCount)] in wstring aHashes,
+ [array, size_is(aInstallCount)] in wstring aNames,
+ [array, size_is(aInstallCount)] in wstring aIcons,
+ [optional] in amIInstallCallback aCallback,
+ [optional] in uint32_t aInstallCount);
+};
diff --git a/toolkit/mozapps/extensions/amInstallTrigger.js b/toolkit/mozapps/extensions/amInstallTrigger.js
new file mode 100644
index 000000000..b83cbe60b
--- /dev/null
+++ b/toolkit/mozapps/extensions/amInstallTrigger.js
@@ -0,0 +1,230 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+
+const XPINSTALL_MIMETYPE = "application/x-xpinstall";
+
+const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
+const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
+
+
+let log = Log.repository.getLogger("AddonManager.InstallTrigger");
+log.level = Log.Level[Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"];
+
+function CallbackObject(id, callback, urls, mediator) {
+ this.id = id;
+ this.callback = callback;
+ this.urls = new Set(urls);
+ this.callCallback = function(url, status) {
+ try {
+ this.callback(url, status);
+ }
+ catch (e) {
+ log.warn("InstallTrigger callback threw an exception: " + e);
+ }
+
+ this.urls.delete(url);
+ if (this.urls.size == 0)
+ mediator._callbacks.delete(id);
+ };
+}
+
+function RemoteMediator(windowID) {
+ this._windowID = windowID;
+ this._lastCallbackID = 0;
+ this._callbacks = new Map();
+ this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"]
+ .getService(Ci.nsISyncMessageSender);
+ this.mm.addWeakMessageListener(MSG_INSTALL_CALLBACK, this);
+}
+
+RemoteMediator.prototype = {
+ receiveMessage: function(message) {
+ if (message.name == MSG_INSTALL_CALLBACK) {
+ let payload = message.data;
+ let callbackHandler = this._callbacks.get(payload.callbackID);
+ if (callbackHandler) {
+ callbackHandler.callCallback(payload.url, payload.status);
+ }
+ }
+ },
+
+ enabled: function(url) {
+ let params = {
+ mimetype: XPINSTALL_MIMETYPE
+ };
+ return this.mm.sendSyncMessage(MSG_INSTALL_ENABLED, params)[0];
+ },
+
+ install: function(installs, principal, callback, window) {
+ let callbackID = this._addCallback(callback, installs.uris);
+
+ installs.mimetype = XPINSTALL_MIMETYPE;
+ installs.triggeringPrincipal = principal;
+ installs.callbackID = callbackID;
+
+ if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+ // When running in the main process this might be a frame inside an
+ // in-content UI page, walk up to find the first frame element in a chrome
+ // privileged document
+ let element = window.frameElement;
+ let ssm = Services.scriptSecurityManager;
+ while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
+ element = element.ownerDocument.defaultView.frameElement;
+
+ if (element) {
+ let listener = Cc["@mozilla.org/addons/integration;1"].
+ getService(Ci.nsIMessageListener);
+ return listener.wrappedJSObject.receiveMessage({
+ name: MSG_INSTALL_ADDONS,
+ target: element,
+ data: installs,
+ });
+ }
+ }
+
+ // Fall back to sending through the message manager
+ let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+
+ return messageManager.sendSyncMessage(MSG_INSTALL_ADDONS, installs)[0];
+ },
+
+ _addCallback: function(callback, urls) {
+ if (!callback || typeof callback != "function")
+ return -1;
+
+ let callbackID = this._windowID + "-" + ++this._lastCallbackID;
+ let callbackObject = new CallbackObject(callbackID, callback, urls, this);
+ this._callbacks.set(callbackID, callbackObject);
+ return callbackID;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference])
+};
+
+
+function InstallTrigger() {
+}
+
+InstallTrigger.prototype = {
+ // Here be magic. We've declared ourselves as providing the
+ // nsIDOMGlobalPropertyInitializer interface, and are registered in the
+ // "JavaScript-global-property" category in the XPCOM category manager. This
+ // means that for newly created windows, XPCOM will createinstance this
+ // object, and then call init, passing in the window for which we need to
+ // provide an instance. We then initialize ourselves and return the webidl
+ // version of this object using the webidl-provided _create method, which
+ // XPCOM will then duly expose as a property value on the window. All this
+ // indirection is necessary because webidl does not (yet) support statics
+ // (bug 863952). See bug 926712 for more details about this implementation.
+ init: function(window) {
+ this._window = window;
+ this._principal = window.document.nodePrincipal;
+ this._url = window.document.documentURIObject;
+
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+ let utils = window.getInterface(Components.interfaces.nsIDOMWindowUtils);
+ this._mediator = new RemoteMediator(utils.currentInnerWindowID);
+
+ return window.InstallTriggerImpl._create(window, this);
+ },
+
+ enabled: function() {
+ return this._mediator.enabled(this._url.spec);
+ },
+
+ updateEnabled: function() {
+ return this.enabled();
+ },
+
+ install: function(installs, callback) {
+ let installData = {
+ uris: [],
+ hashes: [],
+ names: [],
+ icons: [],
+ };
+
+ for (let name of Object.keys(installs)) {
+ let item = installs[name];
+ if (typeof item === "string") {
+ item = { URL: item };
+ }
+ if (!item.URL) {
+ throw new this._window.DOMError("Error", "Missing URL property for '" + name + "'");
+ }
+
+ let url = this._resolveURL(item.URL);
+ if (!this._checkLoadURIFromScript(url)) {
+ throw new this._window.DOMError("SecurityError", "Insufficient permissions to install: " + url.spec);
+ }
+
+ let iconUrl = null;
+ if (item.IconURL) {
+ iconUrl = this._resolveURL(item.IconURL);
+ if (!this._checkLoadURIFromScript(iconUrl)) {
+ iconUrl = null; // If page can't load the icon, just ignore it
+ }
+ }
+
+ installData.uris.push(url.spec);
+ installData.hashes.push(item.Hash || null);
+ installData.names.push(name);
+ installData.icons.push(iconUrl ? iconUrl.spec : null);
+ }
+
+ return this._mediator.install(installData, this._principal, callback, this._window);
+ },
+
+ startSoftwareUpdate: function(url, flags) {
+ let filename = Services.io.newURI(url, null, null)
+ .QueryInterface(Ci.nsIURL)
+ .filename;
+ let args = {};
+ args[filename] = { "URL": url };
+ return this.install(args);
+ },
+
+ installChrome: function(type, url, skin) {
+ return this.startSoftwareUpdate(url);
+ },
+
+ _resolveURL: function (url) {
+ return Services.io.newURI(url, null, this._url);
+ },
+
+ _checkLoadURIFromScript: function(uri) {
+ let secman = Services.scriptSecurityManager;
+ try {
+ secman.checkLoadURIWithPrincipal(this._principal,
+ uri,
+ secman.DISALLOW_INHERIT_PRINCIPAL);
+ return true;
+ }
+ catch(e) {
+ return false;
+ }
+ },
+
+ classID: Components.ID("{9df8ef2b-94da-45c9-ab9f-132eb55fddf1}"),
+ contractID: "@mozilla.org/addons/installtrigger;1",
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIDOMGlobalPropertyInitializer])
+};
+
+
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InstallTrigger]);
diff --git a/toolkit/mozapps/extensions/amWebInstallListener.js b/toolkit/mozapps/extensions/amWebInstallListener.js
new file mode 100644
index 000000000..901beef07
--- /dev/null
+++ b/toolkit/mozapps/extensions/amWebInstallListener.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/. */
+
+/**
+ * This is a default implementation of amIWebInstallListener that should work
+ * for most applications but can be overriden. It notifies the observer service
+ * about blocked installs. For normal installs it pops up an install
+ * confirmation when all the add-ons have been downloaded.
+ */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PromptUtils", "resource://gre/modules/SharedPromptUtils.jsm");
+
+const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
+
+// Installation can begin from any of these states
+const READY_STATES = [
+ AddonManager.STATE_AVAILABLE,
+ AddonManager.STATE_DOWNLOAD_FAILED,
+ AddonManager.STATE_INSTALL_FAILED,
+ AddonManager.STATE_CANCELLED
+];
+
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.weblistener";
+
+// Create a new logger for use by the Addons Web Listener
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+function notifyObservers(aTopic, aBrowser, aUri, aInstalls) {
+ let info = {
+ browser: aBrowser,
+ originatingURI: aUri,
+ installs: aInstalls,
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
+ };
+ Services.obs.notifyObservers(info, aTopic, null);
+}
+
+/**
+ * Creates a new installer to monitor downloads and prompt to install when
+ * ready
+ *
+ * @param aBrowser
+ * The browser that started the installations
+ * @param aUrl
+ * The URL that started the installations
+ * @param aInstalls
+ * An array of AddonInstalls
+ */
+function Installer(aBrowser, aUrl, aInstalls) {
+ this.browser = aBrowser;
+ this.url = aUrl;
+ this.downloads = aInstalls;
+ this.installed = [];
+
+ notifyObservers("addon-install-started", aBrowser, aUrl, aInstalls);
+
+ aInstalls.forEach(function(aInstall) {
+ aInstall.addListener(this);
+
+ // Start downloading if it hasn't already begun
+ if (READY_STATES.indexOf(aInstall.state) != -1)
+ aInstall.install();
+ }, this);
+
+ this.checkAllDownloaded();
+}
+
+Installer.prototype = {
+ browser: null,
+ downloads: null,
+ installed: null,
+ isDownloading: true,
+
+ /**
+ * Checks if all downloads are now complete and if so prompts to install.
+ */
+ checkAllDownloaded: function Installer_checkAllDownloaded() {
+ // Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
+ // installs.
+ if (!this.isDownloading)
+ return;
+
+ var failed = [];
+ var installs = [];
+
+ for (let install of this.downloads) {
+ switch (install.state) {
+ case AddonManager.STATE_AVAILABLE:
+ case AddonManager.STATE_DOWNLOADING:
+ // Exit early if any add-ons haven't started downloading yet or are
+ // still downloading
+ return;
+ case AddonManager.STATE_DOWNLOAD_FAILED:
+ failed.push(install);
+ break;
+ case AddonManager.STATE_DOWNLOADED:
+ // App disabled items are not compatible and so fail to install
+ if (install.addon.appDisabled)
+ failed.push(install);
+ else
+ installs.push(install);
+
+ if (install.linkedInstalls) {
+ install.linkedInstalls.forEach(function(aInstall) {
+ aInstall.addListener(this);
+ // App disabled items are not compatible and so fail to install
+ if (aInstall.addon.appDisabled)
+ failed.push(aInstall);
+ else
+ installs.push(aInstall);
+ }, this);
+ }
+ break;
+ case AddonManager.STATE_CANCELLED:
+ // Just ignore cancelled downloads
+ break;
+ default:
+ logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
+ install.state);
+ }
+ }
+
+ this.isDownloading = false;
+ this.downloads = installs;
+
+ if (failed.length > 0) {
+ // Stop listening and cancel any installs that are failed because of
+ // compatibility reasons.
+ failed.forEach(function(aInstall) {
+ if (aInstall.state == AddonManager.STATE_DOWNLOADED) {
+ aInstall.removeListener(this);
+ aInstall.cancel();
+ }
+ }, this);
+ notifyObservers("addon-install-failed", this.browser, this.url, failed);
+ }
+
+ // If none of the downloads were successful then exit early
+ if (this.downloads.length == 0)
+ return;
+
+ // Check for a custom installation prompt that may be provided by the
+ // applicaton
+ if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
+ try {
+ let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
+ getService(Ci.amIWebInstallPrompt);
+ prompt.confirm(this.browser, this.url, this.downloads, this.downloads.length);
+ return;
+ }
+ catch (e) {}
+ }
+
+ let args = {};
+ args.url = this.url;
+ args.installs = this.downloads;
+ args.wrappedJSObject = args;
+
+ try {
+ Cc["@mozilla.org/base/telemetry;1"].
+ getService(Ci.nsITelemetry).
+ getHistogramById("SECURITY_UI").
+ add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
+ let parentWindow = null;
+ if (this.browser) {
+ parentWindow = this.browser.ownerDocument.defaultView;
+ PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", this.browser);
+ }
+ Services.ww.openWindow(parentWindow, URI_XPINSTALL_DIALOG,
+ null, "chrome,modal,centerscreen", args);
+ } catch (e) {
+ logger.warn("Exception showing install confirmation dialog", e);
+ this.downloads.forEach(function(aInstall) {
+ aInstall.removeListener(this);
+ // Cancel the installs, as currently there is no way to make them fail
+ // from here.
+ aInstall.cancel();
+ }, this);
+ notifyObservers("addon-install-cancelled", this.browser, this.url,
+ this.downloads);
+ }
+ },
+
+ /**
+ * Checks if all installs are now complete and if so notifies observers.
+ */
+ checkAllInstalled: function Installer_checkAllInstalled() {
+ var failed = [];
+
+ for (let install of this.downloads) {
+ switch(install.state) {
+ case AddonManager.STATE_DOWNLOADED:
+ case AddonManager.STATE_INSTALLING:
+ // Exit early if any add-ons haven't started installing yet or are
+ // still installing
+ return;
+ case AddonManager.STATE_INSTALL_FAILED:
+ failed.push(install);
+ break;
+ }
+ }
+
+ this.downloads = null;
+
+ if (failed.length > 0)
+ notifyObservers("addon-install-failed", this.browser, this.url, failed);
+
+ if (this.installed.length > 0)
+ notifyObservers("addon-install-complete", this.browser, this.url, this.installed);
+ this.installed = null;
+ },
+
+ onDownloadCancelled: function Installer_onDownloadCancelled(aInstall) {
+ aInstall.removeListener(this);
+ this.checkAllDownloaded();
+ },
+
+ onDownloadFailed: function Installer_onDownloadFailed(aInstall) {
+ aInstall.removeListener(this);
+ this.checkAllDownloaded();
+ },
+
+ onDownloadEnded: function Installer_onDownloadEnded(aInstall) {
+ this.checkAllDownloaded();
+ return false;
+ },
+
+ onInstallCancelled: function Installer_onInstallCancelled(aInstall) {
+ aInstall.removeListener(this);
+ this.checkAllInstalled();
+ },
+
+ onInstallFailed: function Installer_onInstallFailed(aInstall) {
+ aInstall.removeListener(this);
+ this.checkAllInstalled();
+ },
+
+ onInstallEnded: function Installer_onInstallEnded(aInstall) {
+ aInstall.removeListener(this);
+ this.installed.push(aInstall);
+
+ // If installing a theme that is disabled and can be enabled then enable it
+ if (aInstall.addon.type == "theme" &&
+ aInstall.addon.userDisabled == true &&
+ aInstall.addon.appDisabled == false) {
+ aInstall.addon.userDisabled = false;
+ }
+
+ this.checkAllInstalled();
+ }
+};
+
+function extWebInstallListener() {
+}
+
+extWebInstallListener.prototype = {
+ /**
+ * @see amIWebInstallListener.idl
+ */
+ onWebInstallDisabled: function extWebInstallListener_onWebInstallDisabled(aBrowser, aUri, aInstalls) {
+ let info = {
+ browser: aBrowser,
+ originatingURI: aUri,
+ installs: aInstalls,
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
+ };
+ Services.obs.notifyObservers(info, "addon-install-disabled", null);
+ },
+
+ /**
+ * @see amIWebInstallListener.idl
+ */
+ onWebInstallOriginBlocked: function extWebInstallListener_onWebInstallOriginBlocked(aBrowser, aUri, aInstalls) {
+ let info = {
+ browser: aBrowser,
+ originatingURI: aUri,
+ installs: aInstalls,
+
+ install: function onWebInstallBlocked_install() {
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
+ };
+ Services.obs.notifyObservers(info, "addon-install-origin-blocked", null);
+
+ return false;
+ },
+
+ /**
+ * @see amIWebInstallListener.idl
+ */
+ onWebInstallBlocked: function extWebInstallListener_onWebInstallBlocked(aBrowser, aUri, aInstalls) {
+ let info = {
+ browser: aBrowser,
+ originatingURI: aUri,
+ installs: aInstalls,
+
+ install: function onWebInstallBlocked_install() {
+ new Installer(this.browser, this.originatingURI, this.installs);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
+ };
+ Services.obs.notifyObservers(info, "addon-install-blocked", null);
+
+ return false;
+ },
+
+ /**
+ * @see amIWebInstallListener.idl
+ */
+ onWebInstallRequested: function extWebInstallListener_onWebInstallRequested(aBrowser, aUri, aInstalls) {
+ new Installer(aBrowser, aUri, aInstalls);
+
+ // We start the installs ourself
+ return false;
+ },
+
+ classDescription: "XPI Install Handler",
+ contractID: "@mozilla.org/addons/web-install-listener;1",
+ classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallListener,
+ Ci.amIWebInstallListener2])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([extWebInstallListener]);
diff --git a/toolkit/mozapps/extensions/content/OpenH264-license.txt b/toolkit/mozapps/extensions/content/OpenH264-license.txt
new file mode 100644
index 000000000..ad37989b8
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/OpenH264-license.txt
@@ -0,0 +1,59 @@
+-------------------------------------------------------
+About The Cisco-Provided Binary of OpenH264 Video Codec
+-------------------------------------------------------
+
+Cisco provides this program under the terms of the BSD license.
+
+Additionally, this binary is licensed under Cisco’s AVC/H.264 Patent Portfolio License from MPEG LA, at no cost to you, provided that the requirements and conditions shown below in the AVC/H.264 Patent Portfolio sections are met.
+
+As with all AVC/H.264 codecs, you may also obtain your own patent license from MPEG LA or from the individual patent owners, or proceed at your own risk. Your rights from Cisco under the BSD license are not affected by this choice.
+
+For more information on the OpenH264 binary licensing, please see the OpenH264 FAQ found at http://www.openh264.org/faq.html#binary
+
+A corresponding source code to this binary program is available under the same BSD terms, which can be found at http://www.openh264.org
+
+-----------
+BSD License
+-----------
+
+Copyright © 2014 Cisco Systems, Inc.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-----------------------------------------
+AVC/H.264 Patent Portfolio License Notice
+-----------------------------------------
+
+The binary form of this Software is distributed by Cisco under the AVC/H.264 Patent Portfolio License from MPEG LA, and is subject to the following requirements, which may or may not be applicable to your use of this software:
+
+THIS PRODUCT IS LICENSED UNDER THE AVC PATENT PORTFOLIO LICENSE FOR THE PERSONAL USE OF A CONSUMER OR OTHER USES IN WHICH IT DOES NOT RECEIVE REMUNERATION TO (i) ENCODE VIDEO IN COMPLIANCE WITH THE AVC STANDARD (“AVC VIDEO”) AND/OR (ii) DECODE AVC VIDEO THAT WAS ENCODED BY A CONSUMER ENGAGED IN A PERSONAL ACTIVITY AND/OR WAS OBTAINED FROM A VIDEO PROVIDER LICENSED TO PROVIDE AVC VIDEO. NO LICENSE IS GRANTED OR SHALL BE IMPLIED FOR ANY OTHER USE. ADDITIONAL INFORMATION MAY BE OBTAINED FROM MPEG LA, L.L.C. SEE HTTP://WWW.MPEGLA.COM
+
+Accordingly, please be advised that content providers and broadcasters using AVC/H.264 in their service may be required to obtain a separate use license from MPEG LA, referred to as "(b) sublicenses" in the SUMMARY OF AVC/H.264 LICENSE TERMS from MPEG LA found at http://www.openh264.org/mpegla
+
+---------------------------------------------
+AVC/H.264 Patent Portfolio License Conditions
+---------------------------------------------
+
+In addition, the Cisco-provided binary of this Software is licensed under Cisco's license from MPEG LA only if the following conditions are met:
+
+1. The Cisco-provided binary is separately downloaded to an end user’s device, and not integrated into or combined with third party software prior to being downloaded to the end user’s device;
+
+2. The end user must have the ability to control (e.g., to enable, disable, or re-enable) the use of the Cisco-provided binary;
+
+3. Third party software, in the location where end users can control the use of the Cisco-provided binary, must display the following text:
+
+ "OpenH264 Video Codec provided by Cisco Systems, Inc."
+
+4. Any third-party software that makes use of the Cisco-provided binary must reproduce all of the above text, as well as this last condition, in the EULA and/or in another location where licensing information is to be presented to the end user.
+
+
+
+ v1.0
diff --git a/toolkit/mozapps/extensions/content/about.js b/toolkit/mozapps/extensions/content/about.js
new file mode 100644
index 000000000..49ca4acc1
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/about.js
@@ -0,0 +1,97 @@
+// -*- 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 strict";
+
+function init() {
+ var addon = window.arguments[0];
+ var extensionsStrings = document.getElementById("extensionsStrings");
+
+ document.documentElement.setAttribute("addontype", addon.type);
+
+ if (addon.iconURL) {
+ var extensionIcon = document.getElementById("extensionIcon");
+ extensionIcon.src = addon.iconURL;
+ }
+
+ document.title = extensionsStrings.getFormattedString("aboutWindowTitle", [addon.name]);
+ var extensionName = document.getElementById("extensionName");
+ extensionName.textContent = addon.name;
+
+ var extensionVersion = document.getElementById("extensionVersion");
+ if (addon.version)
+ extensionVersion.setAttribute("value", extensionsStrings.getFormattedString("aboutWindowVersionString", [addon.version]));
+ else
+ extensionVersion.hidden = true;
+
+ var extensionDescription = document.getElementById("extensionDescription");
+ if (addon.description)
+ extensionDescription.textContent = addon.description;
+ else
+ extensionDescription.hidden = true;
+
+ var numDetails = 0;
+
+ var extensionCreator = document.getElementById("extensionCreator");
+ if (addon.creator) {
+ extensionCreator.setAttribute("value", addon.creator);
+ numDetails++;
+ } else {
+ extensionCreator.hidden = true;
+ var extensionCreatorLabel = document.getElementById("extensionCreatorLabel");
+ extensionCreatorLabel.hidden = true;
+ }
+
+ var extensionHomepage = document.getElementById("extensionHomepage");
+ var homepageURL = addon.homepageURL;
+ if (homepageURL) {
+ extensionHomepage.setAttribute("homepageURL", homepageURL);
+ extensionHomepage.setAttribute("tooltiptext", homepageURL);
+ numDetails++;
+ } else {
+ extensionHomepage.hidden = true;
+ }
+
+ numDetails += appendToList("extensionDevelopers", "developersBox", addon.developers);
+ numDetails += appendToList("extensionTranslators", "translatorsBox", addon.translators);
+ numDetails += appendToList("extensionContributors", "contributorsBox", addon.contributors);
+
+ if (numDetails == 0) {
+ var groove = document.getElementById("groove");
+ groove.hidden = true;
+ var extensionDetailsBox = document.getElementById("extensionDetailsBox");
+ extensionDetailsBox.hidden = true;
+ }
+
+ var acceptButton = document.documentElement.getButton("accept");
+ acceptButton.label = extensionsStrings.getString("aboutWindowCloseButton");
+
+ setTimeout(sizeToContent, 0);
+}
+
+function appendToList(aHeaderId, aNodeId, aItems) {
+ var header = document.getElementById(aHeaderId);
+ var node = document.getElementById(aNodeId);
+
+ if (!aItems || aItems.length == 0) {
+ header.hidden = true;
+ return 0;
+ }
+
+ for (let currentItem of aItems) {
+ var label = document.createElement("label");
+ label.textContent = currentItem;
+ label.setAttribute("class", "contributor");
+ node.appendChild(label);
+ }
+
+ return aItems.length;
+}
+
+function loadHomepage(aEvent) {
+ window.close();
+ openURL(aEvent.target.getAttribute("homepageURL"));
+}
diff --git a/toolkit/mozapps/extensions/content/about.xul b/toolkit/mozapps/extensions/content/about.xul
new file mode 100644
index 000000000..6effcf37a
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/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/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/about.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://mozapps/locale/extensions/about.dtd">
+
+<dialog id="genericAbout"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="init();"
+ buttons="accept"
+ buttoniconaccept="close"
+ onaccept="close();">
+
+ <script type="application/javascript" src="chrome://mozapps/content/extensions/about.js"/>
+ <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
+
+ <stringbundleset id="aboutSet">
+ <stringbundle id="extensionsStrings" src="chrome://mozapps/locale/extensions/extensions.properties"/>
+ </stringbundleset>
+
+ <vbox id="clientBox" flex="1">
+ <hbox class="basic-info">
+ <vbox pack="center">
+ <image id="extensionIcon"/>
+ </vbox>
+ <vbox flex="1">
+ <label id="extensionName"/>
+ <label id="extensionVersion" crop="end"/>
+ </vbox>
+ </hbox>
+ <description id="extensionDescription" class="boxIndent"/>
+
+ <separator id="groove" class="groove"/>
+
+ <vbox id="extensionDetailsBox" flex="1">
+ <label id="extensionCreatorLabel" class="sectionTitle">&creator.label;</label>
+ <hbox id="creatorBox" class="boxIndent">
+ <label id="extensionCreator" flex="1" crop="end"/>
+ <label id="extensionHomepage" onclick="if (event.button == 0) { loadHomepage(event); }"
+ class="text-link" value="&homepage.label;"/>
+ </hbox>
+
+ <label id="extensionDevelopers" class="sectionTitle">&developers.label;</label>
+ <vbox flex="1" id="developersBox" class="boxIndent"/>
+ <label id="extensionTranslators" class="sectionTitle">&translators.label;</label>
+ <vbox flex="1" id="translatorsBox" class="boxIndent"/>
+ <label id="extensionContributors" class="sectionTitle">&contributors.label;</label>
+ <vbox flex="1" id="contributorsBox" class="boxIndent"/>
+ </vbox>
+ </vbox>
+
+</dialog>
diff --git a/toolkit/mozapps/extensions/content/blocklist.css b/toolkit/mozapps/extensions/content/blocklist.css
new file mode 100644
index 000000000..cb48005a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/blocklist.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/. */
+
+.hardBlockedAddon {
+ -moz-binding: url("chrome://mozapps/content/extensions/blocklist.xml#hardblockedaddon");
+}
+
+.softBlockedAddon {
+ -moz-binding: url("chrome://mozapps/content/extensions/blocklist.xml#softblockedaddon");
+}
diff --git a/toolkit/mozapps/extensions/content/blocklist.js b/toolkit/mozapps/extensions/content/blocklist.js
new file mode 100644
index 000000000..6b47fd652
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/blocklist.js
@@ -0,0 +1,72 @@
+// -*- 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 strict";
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gArgs;
+
+function init() {
+ var hasHardBlocks = false;
+ var hasSoftBlocks = false;
+ gArgs = window.arguments[0].wrappedJSObject;
+
+ // NOTE: We use strings from the "updates.properties" bundleset to change the
+ // text on the "Cancel" button to "Restart Later". (bug 523784)
+ let bundle = Services.strings.
+ createBundle("chrome://mozapps/locale/update/updates.properties");
+ let cancelButton = document.documentElement.getButton("cancel");
+ cancelButton.setAttribute("label", bundle.GetStringFromName("restartLaterButton"));
+ cancelButton.setAttribute("accesskey",
+ bundle.GetStringFromName("restartLaterButton.accesskey"));
+
+ var richlist = document.getElementById("addonList");
+ var list = gArgs.list;
+ list.sort(function listSort(a, b) { return String.localeCompare(a.name, b.name); });
+ for (let listItem of list) {
+ let item = document.createElement("richlistitem");
+ item.setAttribute("name", listItem.name);
+ item.setAttribute("version", listItem.version);
+ item.setAttribute("icon", listItem.icon);
+ if (listItem.blocked) {
+ item.setAttribute("class", "hardBlockedAddon");
+ hasHardBlocks = true;
+ }
+ else {
+ item.setAttribute("class", "softBlockedAddon");
+ hasSoftBlocks = true;
+ }
+ richlist.appendChild(item);
+ }
+
+ if (hasHardBlocks && hasSoftBlocks)
+ document.getElementById("bothMessage").hidden = false;
+ else if (hasHardBlocks)
+ document.getElementById("hardBlockMessage").hidden = false;
+ else
+ document.getElementById("softBlockMessage").hidden = false;
+
+ var link = document.getElementById("moreInfo");
+ if (list.length == 1 && list[0].url) {
+ link.setAttribute("href", list[0].url);
+ }
+ else {
+ var url = Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL");
+ link.setAttribute("href", url);
+ }
+}
+
+function finish(shouldRestartNow) {
+ gArgs.restart = shouldRestartNow;
+ var list = gArgs.list;
+ var items = document.getElementById("addonList").childNodes;
+ for (let i = 0; i < list.length; i++) {
+ if (!list[i].blocked)
+ list[i].disable = items[i].checked;
+ }
+ return true;
+}
diff --git a/toolkit/mozapps/extensions/content/blocklist.xml b/toolkit/mozapps/extensions/content/blocklist.xml
new file mode 100644
index 000000000..74474392f
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/blocklist.xml
@@ -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 bindings [
+ <!ENTITY % blocklistDTD SYSTEM "chrome://mozapps/locale/extensions/blocklist.dtd" >
+ %blocklistDTD;
+]>
+
+<bindings id="blocklistBindings"
+ 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="hardblockedaddon">
+ <content align="start">
+ <xul:image xbl:inherits="src=icon"/>
+ <xul:vbox flex="1">
+ <xul:hbox class="addon-name-version">
+ <xul:label class="addonName" crop="end" xbl:inherits="value=name"/>
+ <xul:label class="addonVersion" xbl:inherits="value=version"/>
+ </xul:hbox>
+ <xul:hbox>
+ <xul:spacer flex="1"/>
+ <xul:label class="blockedLabel" value="&blocklist.blocked.label;"/>
+ </xul:hbox>
+ </xul:vbox>
+ </content>
+ </binding>
+
+ <binding id="softblockedaddon">
+ <content align="start">
+ <xul:image xbl:inherits="src=icon"/>
+ <xul:vbox flex="1">
+ <xul:hbox class="addon-name-version">
+ <xul:label class="addonName" crop="end" xbl:inherits="value=name"/>
+ <xul:label class="addonVersion" xbl:inherits="value=version"/>
+ </xul:hbox>
+ <xul:hbox>
+ <xul:spacer flex="1"/>
+ <xul:checkbox class="disableCheckbox" checked="true" label="&blocklist.checkbox.label;"/>
+ </xul:hbox>
+ </xul:vbox>
+ </content>
+ <implementation>
+ <field name="_checkbox">
+ document.getAnonymousElementByAttribute(this, "class", "disableCheckbox")
+ </field>
+ <property name="checked" readonly="true">
+ <getter>
+ return this._checkbox.checked;
+ </getter>
+ </property>
+ </implementation>
+ </binding>
+</bindings>
diff --git a/toolkit/mozapps/extensions/content/blocklist.xul b/toolkit/mozapps/extensions/content/blocklist.xul
new file mode 100644
index 000000000..240d9e4e1
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/blocklist.xul
@@ -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://global/skin/"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/blocklist.css"?>
+<?xml-stylesheet href="chrome://mozapps/content/extensions/blocklist.css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/blocklist.dtd">
+%extensionsDTD;
+]>
+
+<dialog windowtype="Addons:Blocklist" title="&blocklist.title;" align="stretch"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="init();" ondialogaccept="return finish(true)"
+ ondialogcancel="return finish(false)"
+ buttons="accept,cancel" style="&blocklist.style;"
+ buttonlabelaccept="&blocklist.accept.label;"
+ buttonaccesskeyaccept="&blocklist.accept.accesskey;">
+
+ <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+ <script type="application/javascript" src="chrome://mozapps/content/extensions/blocklist.js"/>
+
+ <hbox align="stretch" flex="1">
+ <vbox pack="start">
+ <image class="error-icon"/>
+ </vbox>
+ <vbox flex="1">
+ <label>&blocklist.summary;</label>
+ <separator class="thin"/>
+ <richlistbox id="addonList" flex="1"/>
+ <separator class="thin"/>
+ <description id="bothMessage" hidden="true" class="bold">&blocklist.softandhard;</description>
+ <description id="hardBlockMessage" hidden="true" class="bold">&blocklist.hardblocked;</description>
+ <description id="softBlockMessage" hidden="true" class="bold">&blocklist.softblocked;</description>
+ <hbox pack="start">
+ <label id="moreInfo" class="text-link" value="&blocklist.moreinfo;"/>
+ </hbox>
+ </vbox>
+ </hbox>
+</dialog>
diff --git a/toolkit/mozapps/extensions/content/eula.js b/toolkit/mozapps/extensions/content/eula.js
new file mode 100644
index 000000000..a05f7fe1c
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/eula.js
@@ -0,0 +1,21 @@
+// -*- 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 strict";
+
+function Startup() {
+ var bundle = document.getElementById("extensionsStrings");
+ var addon = window.arguments[0].addon;
+
+ document.documentElement.setAttribute("addontype", addon.type);
+
+ if (addon.iconURL)
+ document.getElementById("icon").src = addon.iconURL;
+
+ var label = document.createTextNode(bundle.getFormattedString("eulaHeader", [addon.name]));
+ document.getElementById("heading").appendChild(label);
+ document.getElementById("eula").value = addon.eula;
+}
diff --git a/toolkit/mozapps/extensions/content/eula.xul b/toolkit/mozapps/extensions/content/eula.xul
new file mode 100644
index 000000000..10e657951
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/eula.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/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/eula.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
+%extensionsDTD;
+]>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&eula.title;" width="&eula.width;" height="&eula.height;"
+ buttons="accept,cancel" buttonlabelaccept="&eula.accept;"
+ ondialogaccept="window.arguments[0].accepted = true"
+ onload="Startup();">
+
+ <script type="application/javascript" src="chrome://mozapps/content/extensions/eula.js"/>
+
+ <stringbundleset id="extensionsSet">
+ <stringbundle id="extensionsStrings" src="chrome://mozapps/locale/extensions/extensions.properties"/>
+ </stringbundleset>
+
+ <hbox id="heading-container">
+ <image id="icon"/>
+ <label id="heading" flex="1"/>
+ </hbox>
+
+ <textbox id="eula" multiline="true" readonly="true" flex="1"/>
+</dialog>
diff --git a/toolkit/mozapps/extensions/content/extensions.css b/toolkit/mozapps/extensions/content/extensions.css
new file mode 100644
index 000000000..41c140565
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/extensions.css
@@ -0,0 +1,288 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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;
+}
+
+#categories {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#categories-list");
+}
+
+.category {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#category");
+}
+
+.sort-controls {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#sorters");
+}
+
+.addon[status="installed"] {
+ -moz-box-orient: vertical;
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-generic");
+}
+
+.addon[status="installing"] {
+ -moz-box-orient: vertical;
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-installing");
+}
+
+.addon[pending="uninstall"] {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-uninstalled");
+}
+
+.creator {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#creator-link");
+}
+
+.translators {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#translators-list");
+}
+
+.meta-rating {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#rating");
+}
+
+.download-progress, .download-progress[mode="undetermined"] {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#download-progress");
+}
+
+.install-status {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#install-status");
+}
+
+.detail-row {
+ -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#detail-row");
+}
+
+.text-list {
+ white-space: pre-line;
+ -moz-user-select: element;
+}
+
+setting, row[unsupported="true"] {
+ display: none;
+}
+
+setting[type="bool"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-bool");
+}
+
+setting[type="bool"][localized="true"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-localized-bool");
+}
+
+setting[type="bool"]:not([learnmore]) .preferences-learnmore {
+ visibility: collapse;
+}
+
+setting[type="boolint"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-boolint");
+}
+
+setting[type="integer"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer");
+}
+
+setting[type="integer"]:not([size]) textbox {
+ -moz-box-flex: 1;
+}
+
+setting[type="control"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control");
+}
+
+setting[type="string"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string");
+}
+
+setting[type="color"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-color");
+}
+
+setting[type="file"],
+setting[type="directory"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-path");
+}
+
+setting[type="radio"],
+setting[type="menulist"] {
+ display: -moz-grid-line;
+ -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-multi");
+}
+
+#addonitem-popup > menuitem[disabled="true"] {
+ display: none;
+}
+
+#addonitem-popup[addontype="theme"] > #menuitem_enableItem,
+#addonitem-popup[addontype="theme"] > #menuitem_disableItem,
+#addonitem-popup:not([addontype="theme"]) > #menuitem_enableTheme,
+#addonitem-popup:not([addontype="theme"]) > #menuitem_disableTheme {
+ display: none;
+}
+
+#header-searching:not([active]) {
+ visibility: hidden;
+}
+
+#search-list[local="false"] > .addon[remote="false"],
+#search-list[remote="false"] > .addon[remote="true"] {
+ visibility: collapse;
+}
+
+#detail-view {
+ overflow: auto;
+}
+
+.addon:not([notification="warning"]) .warning,
+.addon:not([notification="error"]) .error,
+.addon:not([notification="info"]) .info,
+.addon:not([pending]) .pending,
+.addon:not([upgrade="true"]) .update-postfix,
+.addon:not([native="true"]) .nativeAddon,
+.addon:not([native="false"]) .compatAddon,
+.addon[active="true"] .disabled-postfix,
+.addon[pending="install"] .update-postfix,
+.addon[pending="install"] .disabled-postfix,
+#detail-view:not([notification="warning"]) .warning,
+#detail-view:not([notification="error"]) .error,
+#detail-view:not([notification="info"]) .info,
+#detail-view:not([pending]) .pending,
+#detail-view:not([upgrade="true"]) .update-postfix,
+#detail-view[active="true"] .disabled-postfix,
+#detail-view[loading] .detail-view-container,
+#detail-view:not([loading]) .alert-container,
+.detail-row:not([value]),
+#search-list[remote="false"] #search-allresults-link {
+ display: none;
+}
+
+#addons-page:not([warning]) #list-view > .global-warning-container {
+ display: none;
+}
+#addon-list .date-updated {
+ display: none;
+}
+
+.view-pane:not(#updates-view) .addon .relnotes-toggle,
+.view-pane:not(#updates-view) .addon .include-update,
+#updates-view:not([updatetype="available"]) .addon .include-update,
+#updates-view[updatetype="available"] .addon .update-available-notice {
+ display: none;
+}
+
+#addons-page:not([warning]) .global-warning,
+#addons-page:not([warning="safemode"]) .global-warning-safemode,
+#addons-page:not([warning="checkcompatibility"]) .global-warning-checkcompatibility,
+#addons-page:not([warning="updatesecurity"]) .global-warning-updatesecurity {
+ display: none;
+}
+
+/* Plugins aren't yet disabled by safemode (bug 342333),
+ so don't show that warning when viewing plugins. */
+#addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
+#addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning {
+ display: none;
+}
+
+#addons-page .view-pane:not([type="plugin"]) .plugin-info-container {
+ display: none;
+}
+
+#addons-page .view-pane:not([type="experiment"]) .experiment-info-container {
+ display: none;
+}
+
+.addon .relnotes {
+ -moz-user-select: text;
+}
+#detail-name, #detail-desc, #detail-fulldesc {
+ -moz-user-select: text;
+}
+
+/* Make sure we're not animating hidden images. See bug 623739. */
+#view-port:not([selectedIndex="0"]) #discover-view .loading,
+#discover-view:not([selectedIndex="0"]) .loading {
+ display: none;
+}
+
+/* Elements in unselected richlistitems cannot be focused */
+richlistitem:not([selected]) * {
+ -moz-user-focus: ignore;
+}
+
+#header-search {
+ width: 22em;
+}
+
+#header-utils-btn {
+ -moz-user-focus: normal;
+}
+
+.discover-button[disabled="true"] {
+ display: none;
+}
+
+#experiments-learn-more[disabled="true"] {
+ display: none;
+}
+
+#experiments-change-telemetry[disabled="true"] {
+ display: none;
+}
+
+.view-pane[type="experiment"] .error,
+.view-pane[type="experiment"] .warning,
+.view-pane[type="experiment"] .addon:not([pending="uninstall"]) .pending,
+.view-pane[type="experiment"] .disabled-postfix,
+.view-pane[type="experiment"] .update-postfix,
+.view-pane[type="experiment"] .version,
+#detail-view[type="experiment"] .alert-container,
+#detail-view[type="experiment"] #detail-version,
+#detail-view[type="experiment"] #detail-creator {
+ display: none;
+}
+
+.view-pane:not([type="experiment"]) .experiment-container,
+.view-pane:not([type="experiment"]) #detail-experiment-container {
+ display: none;
+}
+
+.addon[type="experiment"][status="installing"] .experiment-time,
+.addon[type="experiment"][status="installing"] .experiment-state {
+ display: none;
+}
+
+/* Indicator style for extension target application */
+.addon[native] .nativeIndicator {
+ margin-left: 5pt;
+ padding-bottom: 1pt;
+}
+.addon[native][active="false"] .nativeIndicator {
+ opacity: 0.4;
+}
+.addon[native] .nativeAddon {
+ color: #3366FF;
+}
+.addon[native] .compatAddon {
+ color: #FF6600;
+}
+
+/* Translators for Language Pack details */
+.translators > label {
+ -moz-margin-start: 0px;
+ -moz-margin-end: 0px;
+}
diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js
new file mode 100644
index 000000000..a799eeebb
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -0,0 +1,3659 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/DownloadUtils.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+ "resource://gre/modules/PluralForm.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function () {
+ return Cu.import("resource://gre/modules/devtools/ToolboxProcess.jsm", {}).
+ BrowserToolboxProcess;
+});
+XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
+ "resource:///modules/experiments/Experiments.jsm");
+
+const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
+const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
+const PREF_XPI_ENABLED = "xpinstall.enabled";
+const PREF_MAXRESULTS = "extensions.getAddons.maxResults";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
+const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
+const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
+const PREF_ADDON_DEBUGGING_ENABLED = "devtools.chrome.enabled";
+const PREF_REMOTE_DEBUGGING_ENABLED = "devtools.debugger.remote-enabled";
+
+const LOADING_MSG_DELAY = 100;
+
+const SEARCH_SCORE_MULTIPLIER_NAME = 2;
+const SEARCH_SCORE_MULTIPLIER_DESCRIPTION = 2;
+
+// Use integers so search scores are sortable by nsIXULSortService
+const SEARCH_SCORE_MATCH_WHOLEWORD = 10;
+const SEARCH_SCORE_MATCH_WORDBOUNDRY = 6;
+const SEARCH_SCORE_MATCH_SUBSTRING = 3;
+
+const UPDATES_RECENT_TIMESPAN = 2 * 24 * 3600000; // 2 days (in milliseconds)
+const UPDATES_RELEASENOTES_TRANSFORMFILE = "chrome://mozapps/content/extensions/updateinfo.xsl";
+
+const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
+
+var gViewDefault = "addons://discover/";
+
+var gStrings = {};
+XPCOMUtils.defineLazyServiceGetter(gStrings, "bundleSvc",
+ "@mozilla.org/intl/stringbundle;1",
+ "nsIStringBundleService");
+
+XPCOMUtils.defineLazyGetter(gStrings, "brand", function brandLazyGetter() {
+ return this.bundleSvc.createBundle("chrome://branding/locale/brand.properties");
+});
+XPCOMUtils.defineLazyGetter(gStrings, "ext", function extLazyGetter() {
+ return this.bundleSvc.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
+});
+XPCOMUtils.defineLazyGetter(gStrings, "dl", function dlLazyGetter() {
+ return this.bundleSvc.createBundle("chrome://mozapps/locale/downloads/downloads.properties");
+});
+
+XPCOMUtils.defineLazyGetter(gStrings, "brandShortName", function brandShortNameLazyGetter() {
+ return this.brand.GetStringFromName("brandShortName");
+});
+XPCOMUtils.defineLazyGetter(gStrings, "appVersion", function appVersionLazyGetter() {
+ return Services.appinfo.version;
+});
+
+document.addEventListener("load", initialize, true);
+window.addEventListener("unload", shutdown, false);
+
+var gPendingInitializations = 1;
+this.__defineGetter__("gIsInitializing", function gIsInitializingGetter() gPendingInitializations > 0);
+
+function initialize(event) {
+ // XXXbz this listener gets _all_ load events for all nodes in the
+ // document... but relies on not being called "too early".
+ if (event.target instanceof XMLStylesheetProcessingInstruction) {
+ return;
+ }
+ document.removeEventListener("load", initialize, true);
+
+ let globalCommandSet = document.getElementById("globalCommandSet");
+ globalCommandSet.addEventListener("command", function(event) {
+ gViewController.doCommand(event.target.id);
+ });
+
+ let viewCommandSet = document.getElementById("viewCommandSet");
+ viewCommandSet.addEventListener("commandupdate", function(event) {
+ gViewController.updateCommands();
+ });
+ viewCommandSet.addEventListener("command", function(event) {
+ gViewController.doCommand(event.target.id);
+ });
+
+ let detailScreenshot = document.getElementById("detail-screenshot");
+ detailScreenshot.addEventListener("load", function(event) {
+ this.removeAttribute("loading");
+ });
+ detailScreenshot.addEventListener("error", function(event) {
+ this.setAttribute("loading", "error");
+ });
+
+ let addonPage = document.getElementById("addons-page");
+ addonPage.addEventListener("dragenter", function(event) {
+ gDragDrop.onDragOver(event);
+ });
+ addonPage.addEventListener("dragover", function(event) {
+ gDragDrop.onDragOver(event);
+ });
+ addonPage.addEventListener("drop", function(event) {
+ gDragDrop.onDrop(event);
+ });
+ addonPage.addEventListener("keypress", function(event) {
+ gHeader.onKeyPress(event);
+ });
+
+ if (!isDiscoverEnabled()) {
+ gViewDefault = "addons://list/extension";
+ }
+
+ gViewController.initialize();
+ gCategories.initialize();
+ gHeader.initialize();
+ gEventManager.initialize();
+ Services.obs.addObserver(sendEMPong, "EM-ping", false);
+ Services.obs.notifyObservers(window, "EM-loaded", "");
+
+ // If the initial view has already been selected (by a call to loadView from
+ // the above notifications) then bail out now
+ if (gViewController.initialViewSelected)
+ return;
+
+ // If there is a history state to restore then use that
+ if (window.history.state) {
+ gViewController.updateState(window.history.state);
+ return;
+ }
+
+ // Default to the last selected category
+ var view = gCategories.node.value;
+
+ // Allow passing in a view through the window arguments
+ if ("arguments" in window && window.arguments.length > 0 &&
+ window.arguments[0] !== null && "view" in window.arguments[0]) {
+ view = window.arguments[0].view;
+ }
+
+ gViewController.loadInitialView(view);
+
+ Services.prefs.addObserver(PREF_ADDON_DEBUGGING_ENABLED, debuggingPrefChanged, false);
+ Services.prefs.addObserver(PREF_REMOTE_DEBUGGING_ENABLED, debuggingPrefChanged, false);
+}
+
+function notifyInitialized() {
+ if (!gIsInitializing)
+ return;
+
+ gPendingInitializations--;
+ if (!gIsInitializing) {
+ var event = document.createEvent("Events");
+ event.initEvent("Initialized", true, true);
+ document.dispatchEvent(event);
+ }
+}
+
+function shutdown() {
+ gCategories.shutdown();
+ gSearchView.shutdown();
+ gEventManager.shutdown();
+ gViewController.shutdown();
+ Services.obs.removeObserver(sendEMPong, "EM-ping");
+ Services.prefs.removeObserver(PREF_ADDON_DEBUGGING_ENABLED, debuggingPrefChanged);
+ Services.prefs.removeObserver(PREF_REMOTE_DEBUGGING_ENABLED, debuggingPrefChanged);
+}
+
+function sendEMPong(aSubject, aTopic, aData) {
+ Services.obs.notifyObservers(window, "EM-pong", "");
+}
+
+// Used by external callers to load a specific view into the manager
+function loadView(aViewId) {
+ if (!gViewController.initialViewSelected) {
+ // The caller opened the window and immediately loaded the view so it
+ // should be the initial history entry
+
+ gViewController.loadInitialView(aViewId);
+ } else {
+ gViewController.loadView(aViewId);
+ }
+}
+
+function isDiscoverEnabled() {
+ if (Services.prefs.getPrefType(PREF_DISCOVERURL) == Services.prefs.PREF_INVALID)
+ return false;
+
+ try {
+ if (!Services.prefs.getBoolPref(PREF_DISCOVER_ENABLED))
+ return false;
+ } catch (e) {}
+
+ try {
+ if (!Services.prefs.getBoolPref(PREF_XPI_ENABLED))
+ return false;
+ } catch (e) {}
+
+ return true;
+}
+
+function getExperimentEndDate(aAddon) {
+ if (!("@mozilla.org/browser/experiments-service;1" in Cc)) {
+ return 0;
+ }
+
+ if (!aAddon.isActive) {
+ return aAddon.endDate;
+ }
+
+ let experiment = Experiments.instance().getActiveExperiment();
+ if (!experiment) {
+ return 0;
+ }
+
+ return experiment.endDate;
+}
+
+/**
+ * Obtain the main DOMWindow for the current context.
+ */
+function getMainWindow() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+}
+
+function getBrowserElement() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+}
+
+/**
+ * Obtain the DOMWindow that can open a preferences pane.
+ *
+ * This is essentially "get the browser chrome window" with the added check
+ * that the supposed browser chrome window is capable of opening a preferences
+ * pane.
+ *
+ * This may return null if we can't find the browser chrome window.
+ */
+function getMainWindowWithPreferencesPane() {
+ let mainWindow = getMainWindow();
+ if (mainWindow && "openAdvancedPreferences" in mainWindow) {
+ return mainWindow;
+ } else {
+ return null;
+ }
+}
+
+/**
+ * A wrapper around the HTML5 session history service that allows the browser
+ * back/forward controls to work within the manager
+ */
+var HTML5History = {
+ get index() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .sessionHistory.index;
+ },
+
+ get canGoBack() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .canGoBack;
+ },
+
+ get canGoForward() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .canGoForward;
+ },
+
+ back: function HTML5History_back() {
+ window.history.back();
+ gViewController.updateCommand("cmd_back");
+ gViewController.updateCommand("cmd_forward");
+ },
+
+ forward: function HTML5History_forward() {
+ window.history.forward();
+ gViewController.updateCommand("cmd_back");
+ gViewController.updateCommand("cmd_forward");
+ },
+
+ pushState: function HTML5History_pushState(aState) {
+ window.history.pushState(aState, document.title);
+ },
+
+ replaceState: function HTML5History_replaceState(aState) {
+ window.history.replaceState(aState, document.title);
+ },
+
+ popState: function HTML5History_popState() {
+ function onStatePopped(aEvent) {
+ window.removeEventListener("popstate", onStatePopped, true);
+ // TODO To ensure we can't go forward again we put an additional entry
+ // for the current state into the history. Ideally we would just strip
+ // the history but there doesn't seem to be a way to do that. Bug 590661
+ window.history.pushState(aEvent.state, document.title);
+ }
+ window.addEventListener("popstate", onStatePopped, true);
+ window.history.back();
+ gViewController.updateCommand("cmd_back");
+ gViewController.updateCommand("cmd_forward");
+ }
+};
+
+/**
+ * A wrapper around a fake history service
+ */
+var FakeHistory = {
+ pos: 0,
+ states: [null],
+
+ get index() {
+ return this.pos;
+ },
+
+ get canGoBack() {
+ return this.pos > 0;
+ },
+
+ get canGoForward() {
+ return (this.pos + 1) < this.states.length;
+ },
+
+ back: function FakeHistory_back() {
+ if (this.pos == 0)
+ throw Components.Exception("Cannot go back from this point");
+
+ this.pos--;
+ gViewController.updateState(this.states[this.pos]);
+ gViewController.updateCommand("cmd_back");
+ gViewController.updateCommand("cmd_forward");
+ },
+
+ forward: function FakeHistory_forward() {
+ if ((this.pos + 1) >= this.states.length)
+ throw Components.Exception("Cannot go forward from this point");
+
+ this.pos++;
+ gViewController.updateState(this.states[this.pos]);
+ gViewController.updateCommand("cmd_back");
+ gViewController.updateCommand("cmd_forward");
+ },
+
+ pushState: function FakeHistory_pushState(aState) {
+ this.pos++;
+ this.states.splice(this.pos, this.states.length);
+ this.states.push(aState);
+ },
+
+ replaceState: function FakeHistory_replaceState(aState) {
+ this.states[this.pos] = aState;
+ },
+
+ popState: function FakeHistory_popState() {
+ if (this.pos == 0)
+ throw Components.Exception("Cannot popState from this view");
+
+ this.states.splice(this.pos, this.states.length);
+ this.pos--;
+
+ gViewController.updateState(this.states[this.pos]);
+ gViewController.updateCommand("cmd_back");
+ gViewController.updateCommand("cmd_forward");
+ }
+};
+
+// If the window has a session history then use the HTML5 History wrapper
+// otherwise use our fake history implementation
+if (window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .sessionHistory) {
+ var gHistory = HTML5History;
+}
+else {
+ gHistory = FakeHistory;
+}
+
+var gEventManager = {
+ _listeners: {},
+ _installListeners: [],
+
+ initialize: function gEM_initialize() {
+ var self = this;
+ const ADDON_EVENTS = ["onEnabling", "onEnabled", "onDisabling",
+ "onDisabled", "onUninstalling", "onUninstalled",
+ "onInstalled", "onOperationCancelled",
+ "onUpdateAvailable", "onUpdateFinished",
+ "onCompatibilityUpdateAvailable",
+ "onPropertyChanged"];
+ for (let evt of ADDON_EVENTS) {
+ let event = evt;
+ self[event] = function initialize_delegateAddonEvent(...aArgs) {
+ self.delegateAddonEvent(event, aArgs);
+ };
+ }
+
+ const INSTALL_EVENTS = ["onNewInstall", "onDownloadStarted",
+ "onDownloadEnded", "onDownloadFailed",
+ "onDownloadProgress", "onDownloadCancelled",
+ "onInstallStarted", "onInstallEnded",
+ "onInstallFailed", "onInstallCancelled",
+ "onExternalInstall"];
+ for (let evt of INSTALL_EVENTS) {
+ let event = evt;
+ self[event] = function initialize_delegateInstallEvent(...aArgs) {
+ self.delegateInstallEvent(event, aArgs);
+ };
+ }
+
+ AddonManager.addManagerListener(this);
+ AddonManager.addInstallListener(this);
+ AddonManager.addAddonListener(this);
+
+ this.refreshGlobalWarning();
+ this.refreshAutoUpdateDefault();
+
+ var contextMenu = document.getElementById("addonitem-popup");
+ contextMenu.addEventListener("popupshowing", function contextMenu_onPopupshowing() {
+ var addon = gViewController.currentViewObj.getSelectedAddon();
+ contextMenu.setAttribute("addontype", addon.type);
+
+ var menuSep = document.getElementById("addonitem-menuseparator");
+ var countMenuItemsBeforeSep = 0;
+ for (let child of contextMenu.children) {
+ if (child == menuSep) {
+ break;
+ }
+ if (child.nodeName == "menuitem" &&
+ gViewController.isCommandEnabled(child.command)) {
+ countMenuItemsBeforeSep++;
+ }
+ }
+
+ // Hide the separator if there are no visible menu items before it
+ menuSep.hidden = (countMenuItemsBeforeSep == 0);
+
+ }, false);
+ },
+
+ shutdown: function gEM_shutdown() {
+ AddonManager.removeManagerListener(this);
+ AddonManager.removeInstallListener(this);
+ AddonManager.removeAddonListener(this);
+ },
+
+ registerAddonListener: function gEM_registerAddonListener(aListener, aAddonId) {
+ if (!(aAddonId in this._listeners))
+ this._listeners[aAddonId] = [];
+ else if (this._listeners[aAddonId].indexOf(aListener) != -1)
+ return;
+ this._listeners[aAddonId].push(aListener);
+ },
+
+ unregisterAddonListener: function gEM_unregisterAddonListener(aListener, aAddonId) {
+ if (!(aAddonId in this._listeners))
+ return;
+ var index = this._listeners[aAddonId].indexOf(aListener);
+ if (index == -1)
+ return;
+ this._listeners[aAddonId].splice(index, 1);
+ },
+
+ registerInstallListener: function gEM_registerInstallListener(aListener) {
+ if (this._installListeners.indexOf(aListener) != -1)
+ return;
+ this._installListeners.push(aListener);
+ },
+
+ unregisterInstallListener: function gEM_unregisterInstallListener(aListener) {
+ var i = this._installListeners.indexOf(aListener);
+ if (i == -1)
+ return;
+ this._installListeners.splice(i, 1);
+ },
+
+ delegateAddonEvent: function gEM_delegateAddonEvent(aEvent, aParams) {
+ var addon = aParams.shift();
+ if (!(addon.id in this._listeners))
+ return;
+
+ var listeners = this._listeners[addon.id];
+ for (let listener of listeners) {
+ if (!(aEvent in listener))
+ continue;
+ try {
+ listener[aEvent].apply(listener, aParams);
+ } catch(e) {
+ // this shouldn't be fatal
+ Cu.reportError(e);
+ }
+ }
+ },
+
+ delegateInstallEvent: function gEM_delegateInstallEvent(aEvent, aParams) {
+ var existingAddon = aEvent == "onExternalInstall" ? aParams[1] : aParams[0].existingAddon;
+ // If the install is an update then send the event to all listeners
+ // registered for the existing add-on
+ if (existingAddon)
+ this.delegateAddonEvent(aEvent, [existingAddon].concat(aParams));
+
+ for (let listener of this._installListeners) {
+ if (!(aEvent in listener))
+ continue;
+ try {
+ listener[aEvent].apply(listener, aParams);
+ } catch(e) {
+ // this shouldn't be fatal
+ Cu.reportError(e);
+ }
+ }
+ },
+
+ refreshGlobalWarning: function gEM_refreshGlobalWarning() {
+ var page = document.getElementById("addons-page");
+
+ if (Services.appinfo.inSafeMode) {
+ page.setAttribute("warning", "safemode");
+ return;
+ }
+
+ if (AddonManager.checkUpdateSecurityDefault &&
+ !AddonManager.checkUpdateSecurity) {
+ page.setAttribute("warning", "updatesecurity");
+ return;
+ }
+
+ if (!AddonManager.checkCompatibility) {
+ page.setAttribute("warning", "checkcompatibility");
+ return;
+ }
+
+ page.removeAttribute("warning");
+ },
+
+ refreshAutoUpdateDefault: function gEM_refreshAutoUpdateDefault() {
+ var updateEnabled = AddonManager.updateEnabled;
+ var autoUpdateDefault = AddonManager.autoUpdateDefault;
+
+ // The checkbox needs to reflect that both prefs need to be true
+ // for updates to be checked for and applied automatically
+ document.getElementById("utils-autoUpdateDefault")
+ .setAttribute("checked", updateEnabled && autoUpdateDefault);
+
+ document.getElementById("utils-resetAddonUpdatesToAutomatic").hidden = !autoUpdateDefault;
+ document.getElementById("utils-resetAddonUpdatesToManual").hidden = autoUpdateDefault;
+ },
+
+ onCompatibilityModeChanged: function gEM_onCompatibilityModeChanged() {
+ this.refreshGlobalWarning();
+ },
+
+ onCheckUpdateSecurityChanged: function gEM_onCheckUpdateSecurityChanged() {
+ this.refreshGlobalWarning();
+ },
+
+ onUpdateModeChanged: function gEM_onUpdateModeChanged() {
+ this.refreshAutoUpdateDefault();
+ }
+};
+
+
+var gViewController = {
+ viewPort: null,
+ currentViewId: "",
+ currentViewObj: null,
+ currentViewRequest: 0,
+ viewObjects: {},
+ viewChangeCallback: null,
+ initialViewSelected: false,
+ lastHistoryIndex: -1,
+
+ initialize: function gVC_initialize() {
+ this.viewPort = document.getElementById("view-port");
+
+ this.viewObjects["search"] = gSearchView;
+ this.viewObjects["discover"] = gDiscoverView;
+ this.viewObjects["list"] = gListView;
+ this.viewObjects["detail"] = gDetailView;
+ this.viewObjects["updates"] = gUpdatesView;
+
+ for each (let view in this.viewObjects)
+ view.initialize();
+
+ window.controllers.appendController(this);
+
+ window.addEventListener("popstate",
+ function window_onStatePopped(e) {
+ gViewController.updateState(e.state);
+ },
+ false);
+ },
+
+ shutdown: function gVC_shutdown() {
+ if (this.currentViewObj)
+ this.currentViewObj.hide();
+ this.currentViewRequest = 0;
+
+ for each(let view in this.viewObjects) {
+ if ("shutdown" in view) {
+ try {
+ view.shutdown();
+ } catch(e) {
+ // this shouldn't be fatal
+ Cu.reportError(e);
+ }
+ }
+ }
+
+ window.controllers.removeController(this);
+ },
+
+ updateState: function gVC_updateState(state) {
+ try {
+ this.loadViewInternal(state.view, state.previousView, state);
+ this.lastHistoryIndex = gHistory.index;
+ }
+ catch (e) {
+ // The attempt to load the view failed, try moving further along history
+ if (this.lastHistoryIndex > gHistory.index) {
+ if (gHistory.canGoBack)
+ gHistory.back();
+ else
+ gViewController.replaceView(gViewDefault);
+ } else {
+ if (gHistory.canGoForward)
+ gHistory.forward();
+ else
+ gViewController.replaceView(gViewDefault);
+ }
+ }
+ },
+
+ parseViewId: function gVC_parseViewId(aViewId) {
+ var matchRegex = /^addons:\/\/([^\/]+)\/(.*)$/;
+ var [,viewType, viewParam] = aViewId.match(matchRegex) || [];
+ return {type: viewType, param: decodeURIComponent(viewParam)};
+ },
+
+ get isLoading() {
+ return !this.currentViewObj || this.currentViewObj.node.hasAttribute("loading");
+ },
+
+ loadView: function gVC_loadView(aViewId) {
+ var isRefresh = false;
+ if (aViewId == this.currentViewId) {
+ if (this.isLoading)
+ return;
+ if (!("refresh" in this.currentViewObj))
+ return;
+ if (!this.currentViewObj.canRefresh())
+ return;
+ isRefresh = true;
+ }
+
+ var state = {
+ view: aViewId,
+ previousView: this.currentViewId
+ };
+ if (!isRefresh) {
+ gHistory.pushState(state);
+ this.lastHistoryIndex = gHistory.index;
+ }
+ this.loadViewInternal(aViewId, this.currentViewId, state);
+ },
+
+ // Replaces the existing view with a new one, rewriting the current history
+ // entry to match.
+ replaceView: function gVC_replaceView(aViewId) {
+ if (aViewId == this.currentViewId)
+ return;
+
+ var state = {
+ view: aViewId,
+ previousView: null
+ };
+ gHistory.replaceState(state);
+ this.loadViewInternal(aViewId, null, state);
+ },
+
+ loadInitialView: function gVC_loadInitialView(aViewId) {
+ var state = {
+ view: aViewId,
+ previousView: null
+ };
+ gHistory.replaceState(state);
+
+ this.loadViewInternal(aViewId, null, state);
+ this.initialViewSelected = true;
+ notifyInitialized();
+ },
+
+ loadViewInternal: function gVC_loadViewInternal(aViewId, aPreviousView, aState) {
+ var view = this.parseViewId(aViewId);
+
+ if (!view.type || !(view.type in this.viewObjects))
+ throw Components.Exception("Invalid view: " + view.type);
+
+ var viewObj = this.viewObjects[view.type];
+ if (!viewObj.node)
+ throw Components.Exception("Root node doesn't exist for '" + view.type + "' view");
+
+ if (this.currentViewObj && aViewId != aPreviousView) {
+ try {
+ let canHide = this.currentViewObj.hide();
+ if (canHide === false)
+ return;
+ this.viewPort.selectedPanel.removeAttribute("loading");
+ } catch (e) {
+ // this shouldn't be fatal
+ Cu.reportError(e);
+ }
+ }
+
+ gCategories.select(aViewId, aPreviousView);
+
+ this.currentViewId = aViewId;
+ this.currentViewObj = viewObj;
+
+ this.viewPort.selectedPanel = this.currentViewObj.node;
+ this.viewPort.selectedPanel.setAttribute("loading", "true");
+ this.currentViewObj.node.focus();
+
+ if (aViewId == aPreviousView)
+ this.currentViewObj.refresh(view.param, ++this.currentViewRequest, aState);
+ else
+ this.currentViewObj.show(view.param, ++this.currentViewRequest, aState);
+ },
+
+ // Moves back in the document history and removes the current history entry
+ popState: function gVC_popState(aCallback) {
+ this.viewChangeCallback = aCallback;
+ gHistory.popState();
+ },
+
+ notifyViewChanged: function gVC_notifyViewChanged() {
+ this.viewPort.selectedPanel.removeAttribute("loading");
+
+ if (this.viewChangeCallback) {
+ this.viewChangeCallback();
+ this.viewChangeCallback = null;
+ }
+
+ var event = document.createEvent("Events");
+ event.initEvent("ViewChanged", true, true);
+ this.currentViewObj.node.dispatchEvent(event);
+ },
+
+ commands: {
+ cmd_back: {
+ isEnabled: function cmd_back_isEnabled() {
+ return gHistory.canGoBack;
+ },
+ doCommand: function cmd_back_doCommand() {
+ gHistory.back();
+ }
+ },
+
+ cmd_forward: {
+ isEnabled: function cmd_forward_isEnabled() {
+ return gHistory.canGoForward;
+ },
+ doCommand: function cmd_forward_doCommand() {
+ gHistory.forward();
+ }
+ },
+
+ cmd_focusSearch: {
+ isEnabled: () => true,
+ doCommand: function cmd_focusSearch_doCommand() {
+ gHeader.focusSearchBox();
+ }
+ },
+
+ cmd_restartApp: {
+ isEnabled: function cmd_restartApp_isEnabled() true,
+ doCommand: function cmd_restartApp_doCommand() {
+ let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
+ createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+ "restart");
+ if (cancelQuit.data)
+ return; // somebody canceled our quit request
+
+ let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
+ getService(Ci.nsIAppStartup);
+ appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+ }
+ },
+
+ cmd_enableCheckCompatibility: {
+ isEnabled: function cmd_enableCheckCompatibility_isEnabled() true,
+ doCommand: function cmd_enableCheckCompatibility_doCommand() {
+ AddonManager.checkCompatibility = true;
+ }
+ },
+
+ cmd_enableUpdateSecurity: {
+ isEnabled: function cmd_enableUpdateSecurity_isEnabled() true,
+ doCommand: function cmd_enableUpdateSecurity_doCommand() {
+ AddonManager.checkUpdateSecurity = true;
+ }
+ },
+
+/* Plugincheck service is currently N/A for Pale Moon
+ cmd_pluginCheck: {
+ isEnabled: function cmd_pluginCheck_isEnabled() true,
+ doCommand: function cmd_pluginCheck_doCommand() {
+ openURL(Services.urlFormatter.formatURLPref("plugins.update.url"));
+ }
+ },
+*/
+
+ cmd_toggleAutoUpdateDefault: {
+ isEnabled: function cmd_toggleAutoUpdateDefault_isEnabled() true,
+ doCommand: function cmd_toggleAutoUpdateDefault_doCommand() {
+ if (!AddonManager.updateEnabled || !AddonManager.autoUpdateDefault) {
+ // One or both of the prefs is false, i.e. the checkbox is not checked.
+ // Now toggle both to true. If the user wants us to auto-update
+ // add-ons, we also need to auto-check for updates.
+ AddonManager.updateEnabled = true;
+ AddonManager.autoUpdateDefault = true;
+ } else {
+ // Both prefs are true, i.e. the checkbox is checked.
+ // Toggle the auto pref to false, but don't touch the enabled check.
+ AddonManager.autoUpdateDefault = false;
+ }
+ }
+ },
+
+ cmd_resetAddonAutoUpdate: {
+ isEnabled: function cmd_resetAddonAutoUpdate_isEnabled() true,
+ doCommand: function cmd_resetAddonAutoUpdate_doCommand() {
+ AddonManager.getAllAddons(function cmd_resetAddonAutoUpdate_getAllAddons(aAddonList) {
+ for (let addon of aAddonList) {
+ if ("applyBackgroundUpdates" in addon)
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+ }
+ });
+ }
+ },
+
+ cmd_goToDiscoverPane: {
+ isEnabled: function cmd_goToDiscoverPane_isEnabled() {
+ return gDiscoverView.enabled;
+ },
+ doCommand: function cmd_goToDiscoverPane_doCommand() {
+ gViewController.loadView("addons://discover/");
+ }
+ },
+
+ cmd_goToRecentUpdates: {
+ isEnabled: function cmd_goToRecentUpdates_isEnabled() true,
+ doCommand: function cmd_goToRecentUpdates_doCommand() {
+ gViewController.loadView("addons://updates/recent");
+ }
+ },
+
+ cmd_goToAvailableUpdates: {
+ isEnabled: function cmd_goToAvailableUpdates_isEnabled() true,
+ doCommand: function cmd_goToAvailableUpdates_doCommand() {
+ gViewController.loadView("addons://updates/available");
+ }
+ },
+
+ cmd_showItemDetails: {
+ isEnabled: function cmd_showItemDetails_isEnabled(aAddon) {
+ return !!aAddon && (gViewController.currentViewObj != gDetailView);
+ },
+ doCommand: function cmd_showItemDetails_doCommand(aAddon, aScrollToPreferences) {
+ gViewController.loadView("addons://detail/" +
+ encodeURIComponent(aAddon.id) +
+ (aScrollToPreferences ? "/preferences" : ""));
+ }
+ },
+
+ cmd_findAllUpdates: {
+ inProgress: false,
+ isEnabled: function cmd_findAllUpdates_isEnabled() !this.inProgress,
+ doCommand: function cmd_findAllUpdates_doCommand() {
+ this.inProgress = true;
+ gViewController.updateCommand("cmd_findAllUpdates");
+ document.getElementById("updates-noneFound").hidden = true;
+ document.getElementById("updates-progress").hidden = false;
+ document.getElementById("updates-manualUpdatesFound-btn").hidden = true;
+
+ var pendingChecks = 0;
+ var numUpdated = 0;
+ var numManualUpdates = 0;
+ var restartNeeded = false;
+ var self = this;
+
+ function updateStatus() {
+ if (pendingChecks > 0)
+ return;
+
+ self.inProgress = false;
+ gViewController.updateCommand("cmd_findAllUpdates");
+ document.getElementById("updates-progress").hidden = true;
+ gUpdatesView.maybeRefresh();
+
+ if (numManualUpdates > 0 && numUpdated == 0) {
+ document.getElementById("updates-manualUpdatesFound-btn").hidden = false;
+ return;
+ }
+
+ if (numUpdated == 0) {
+ document.getElementById("updates-noneFound").hidden = false;
+ return;
+ }
+
+ if (restartNeeded) {
+ document.getElementById("updates-downloaded").hidden = false;
+ document.getElementById("updates-restart-btn").hidden = false;
+ } else {
+ document.getElementById("updates-installed").hidden = false;
+ }
+ }
+
+ var updateInstallListener = {
+ onDownloadFailed: function cmd_findAllUpdates_downloadFailed() {
+ pendingChecks--;
+ updateStatus();
+ },
+ onInstallFailed: function cmd_findAllUpdates_installFailed() {
+ pendingChecks--;
+ updateStatus();
+ },
+ onInstallEnded: function cmd_findAllUpdates_installEnded(aInstall, aAddon) {
+ pendingChecks--;
+ numUpdated++;
+ if (isPending(aInstall.existingAddon, "upgrade"))
+ restartNeeded = true;
+ updateStatus();
+ }
+ };
+
+ var updateCheckListener = {
+ onUpdateAvailable: function cmd_findAllUpdates_updateAvailable(aAddon, aInstall) {
+ gEventManager.delegateAddonEvent("onUpdateAvailable",
+ [aAddon, aInstall]);
+ if (AddonManager.shouldAutoUpdate(aAddon)) {
+ aInstall.addListener(updateInstallListener);
+ aInstall.install();
+ } else {
+ pendingChecks--;
+ numManualUpdates++;
+ updateStatus();
+ }
+ },
+ onNoUpdateAvailable: function cmd_findAllUpdates_noUpdateAvailable(aAddon) {
+ pendingChecks--;
+ updateStatus();
+ },
+ onUpdateFinished: function cmd_findAllUpdates_updateFinished(aAddon, aError) {
+ gEventManager.delegateAddonEvent("onUpdateFinished",
+ [aAddon, aError]);
+ }
+ };
+
+ AddonManager.getAddonsByTypes(null, function cmd_findAllUpdates_getAddonsByTypes(aAddonList) {
+ for (let addon of aAddonList) {
+ if (addon.permissions & AddonManager.PERM_CAN_UPGRADE) {
+ pendingChecks++;
+ addon.findUpdates(updateCheckListener,
+ AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ }
+ }
+
+ if (pendingChecks == 0)
+ updateStatus();
+ });
+ }
+ },
+
+ cmd_findItemUpdates: {
+ isEnabled: function cmd_findItemUpdates_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return hasPermission(aAddon, "upgrade");
+ },
+ doCommand: function cmd_findItemUpdates_doCommand(aAddon) {
+ var listener = {
+ onUpdateAvailable: function cmd_findItemUpdates_updateAvailable(aAddon, aInstall) {
+ gEventManager.delegateAddonEvent("onUpdateAvailable",
+ [aAddon, aInstall]);
+ if (AddonManager.shouldAutoUpdate(aAddon))
+ aInstall.install();
+ },
+ onNoUpdateAvailable: function cmd_findItemUpdates_noUpdateAvailable(aAddon) {
+ gEventManager.delegateAddonEvent("onNoUpdateAvailable",
+ [aAddon]);
+ }
+ };
+ gEventManager.delegateAddonEvent("onCheckingUpdate", [aAddon]);
+ aAddon.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ }
+ },
+
+ cmd_debugItem: {
+ doCommand: function cmd_debugItem_doCommand(aAddon) {
+ BrowserToolboxProcess.init({ addonID: aAddon.id });
+ },
+
+ isEnabled: function cmd_debugItem_isEnabled(aAddon) {
+ let debuggerEnabled = Services.prefs.
+ getBoolPref(PREF_ADDON_DEBUGGING_ENABLED);
+ let remoteEnabled = Services.prefs.
+ getBoolPref(PREF_REMOTE_DEBUGGING_ENABLED);
+ return aAddon && aAddon.isDebuggable && debuggerEnabled && remoteEnabled;
+ }
+ },
+
+ cmd_showItemPreferences: {
+ isEnabled: function cmd_showItemPreferences_isEnabled(aAddon) {
+ if (!aAddon ||
+ (!aAddon.isActive && !aAddon.isGMPlugin) ||
+ !aAddon.optionsURL) {
+ return false;
+ }
+ if (gViewController.currentViewObj == gDetailView &&
+ aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE) {
+ return false;
+ }
+ if (aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_INFO)
+ return false;
+ return true;
+ },
+ doCommand: function cmd_showItemPreferences_doCommand(aAddon) {
+ if (aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE) {
+ gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);
+ return;
+ }
+ var optionsURL = aAddon.optionsURL;
+ if (aAddon.optionsType == AddonManager.OPTIONS_TYPE_TAB &&
+ openOptionsInTab(optionsURL)) {
+ return;
+ }
+ var windows = Services.wm.getEnumerator(null);
+ while (windows.hasMoreElements()) {
+ var win = windows.getNext();
+ if (win.closed) {
+ continue;
+ }
+ if (win.document.documentURI == optionsURL) {
+ win.focus();
+ return;
+ }
+ }
+ var features = "chrome,titlebar,toolbar,centerscreen";
+ try {
+ var instantApply = Services.prefs.getBoolPref("browser.preferences.instantApply");
+ features += instantApply ? ",dialog=no" : ",modal";
+ } catch (e) {
+ features += ",modal";
+ }
+ openDialog(optionsURL, "", features);
+ }
+ },
+
+ cmd_showItemAbout: {
+ isEnabled: function cmd_showItemAbout_isEnabled(aAddon) {
+ // XXXunf This may be applicable to install items too. See bug 561260
+ return !!aAddon;
+ },
+ doCommand: function cmd_showItemAbout_doCommand(aAddon) {
+ var aboutURL = aAddon.aboutURL;
+ if (aboutURL)
+ openDialog(aboutURL, "", "chrome,centerscreen,modal", aAddon);
+ else
+ openDialog("chrome://mozapps/content/extensions/about.xul",
+ "", "chrome,centerscreen,modal", aAddon);
+ }
+ },
+
+ cmd_enableItem: {
+ isEnabled: function cmd_enableItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ let addonType = AddonManager.addonTypes[aAddon.type];
+ return (!(addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
+ hasPermission(aAddon, "enable"));
+ },
+ doCommand: function cmd_enableItem_doCommand(aAddon) {
+ aAddon.userDisabled = false;
+ },
+ getTooltip: function cmd_enableItem_getTooltip(aAddon) {
+ if (!aAddon)
+ return "";
+ if (aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE)
+ return gStrings.ext.GetStringFromName("enableAddonRestartRequiredTooltip");
+ return gStrings.ext.GetStringFromName("enableAddonTooltip");
+ }
+ },
+
+ cmd_disableItem: {
+ isEnabled: function cmd_disableItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ let addonType = AddonManager.addonTypes[aAddon.type];
+ return (!(addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
+ hasPermission(aAddon, "disable"));
+ },
+ doCommand: function cmd_disableItem_doCommand(aAddon) {
+ aAddon.userDisabled = true;
+ },
+ getTooltip: function cmd_disableItem_getTooltip(aAddon) {
+ if (!aAddon)
+ return "";
+ if (aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE)
+ return gStrings.ext.GetStringFromName("disableAddonRestartRequiredTooltip");
+ return gStrings.ext.GetStringFromName("disableAddonTooltip");
+ }
+ },
+
+ cmd_installItem: {
+ isEnabled: function cmd_installItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return aAddon.install && aAddon.install.state == AddonManager.STATE_AVAILABLE;
+ },
+ doCommand: function cmd_installItem_doCommand(aAddon) {
+ function doInstall() {
+ gViewController.currentViewObj.getListItemForID(aAddon.id)._installStatus.installRemote();
+ }
+
+ if (gViewController.currentViewObj == gDetailView)
+ gViewController.popState(doInstall);
+ else
+ doInstall();
+ }
+ },
+
+ cmd_purchaseItem: {
+ isEnabled: function cmd_purchaseItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return !!aAddon.purchaseURL;
+ },
+ doCommand: function cmd_purchaseItem_doCommand(aAddon) {
+ openURL(aAddon.purchaseURL);
+ }
+ },
+
+ cmd_uninstallItem: {
+ isEnabled: function cmd_uninstallItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return hasPermission(aAddon, "uninstall");
+ },
+ doCommand: function cmd_uninstallItem_doCommand(aAddon) {
+ if (gViewController.currentViewObj != gDetailView) {
+ aAddon.uninstall();
+ return;
+ }
+
+ gViewController.popState(function cmd_uninstallItem_popState() {
+ gViewController.currentViewObj.getListItemForID(aAddon.id).uninstall();
+ });
+ },
+ getTooltip: function cmd_uninstallItem_getTooltip(aAddon) {
+ if (!aAddon)
+ return "";
+ if (aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL)
+ return gStrings.ext.GetStringFromName("uninstallAddonRestartRequiredTooltip");
+ return gStrings.ext.GetStringFromName("uninstallAddonTooltip");
+ }
+ },
+
+ cmd_cancelUninstallItem: {
+ isEnabled: function cmd_cancelUninstallItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return isPending(aAddon, "uninstall");
+ },
+ doCommand: function cmd_cancelUninstallItem_doCommand(aAddon) {
+ aAddon.cancelUninstall();
+ }
+ },
+
+ cmd_installFromFile: {
+ isEnabled: function cmd_installFromFile_isEnabled() true,
+ doCommand: function cmd_installFromFile_doCommand() {
+ const nsIFilePicker = Ci.nsIFilePicker;
+ var fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(nsIFilePicker);
+ fp.init(window,
+ gStrings.ext.GetStringFromName("installFromFile.dialogTitle"),
+ nsIFilePicker.modeOpenMultiple);
+ try {
+ fp.appendFilter(gStrings.ext.GetStringFromName("installFromFile.filterName"),
+ "*.xpi;*.jar");
+ fp.appendFilters(nsIFilePicker.filterAll);
+ } catch (e) { }
+
+ if (fp.show() != nsIFilePicker.returnOK)
+ return;
+
+ var files = fp.files;
+ var installs = [];
+
+ function buildNextInstall() {
+ if (!files.hasMoreElements()) {
+ if (installs.length > 0) {
+ // Display the normal install confirmation for the installs
+ let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"].
+ getService(Ci.amIWebInstallListener);
+ webInstaller.onWebInstallRequested(getBrowserElement(),
+ document.documentURIObject,
+ installs, installs.length);
+ }
+ return;
+ }
+
+ var file = files.getNext();
+ AddonManager.getInstallForFile(file, function cmd_installFromFile_getInstallForFile(aInstall) {
+ installs.push(aInstall);
+ buildNextInstall();
+ });
+ }
+
+ buildNextInstall();
+ }
+ },
+
+ cmd_cancelOperation: {
+ isEnabled: function cmd_cancelOperation_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return aAddon.pendingOperations != AddonManager.PENDING_NONE;
+ },
+ doCommand: function cmd_cancelOperation_doCommand(aAddon) {
+ if (isPending(aAddon, "install")) {
+ aAddon.install.cancel();
+ } else if (isPending(aAddon, "upgrade")) {
+ aAddon.pendingUpgrade.install.cancel();
+ } else if (isPending(aAddon, "uninstall")) {
+ aAddon.cancelUninstall();
+ } else if (isPending(aAddon, "enable")) {
+ aAddon.userDisabled = true;
+ } else if (isPending(aAddon, "disable")) {
+ aAddon.userDisabled = false;
+ }
+ }
+ },
+
+ cmd_contribute: {
+ isEnabled: function cmd_contribute_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ return ("contributionURL" in aAddon && aAddon.contributionURL);
+ },
+ doCommand: function cmd_contribute_doCommand(aAddon) {
+ openURL(aAddon.contributionURL);
+ }
+ },
+
+ cmd_askToActivateItem: {
+ isEnabled: function cmd_askToActivateItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ let addonType = AddonManager.addonTypes[aAddon.type];
+ return ((addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
+ hasPermission(aAddon, "ask_to_activate"));
+ },
+ doCommand: function cmd_askToActivateItem_doCommand(aAddon) {
+ aAddon.userDisabled = AddonManager.STATE_ASK_TO_ACTIVATE;
+ }
+ },
+
+ cmd_alwaysActivateItem: {
+ isEnabled: function cmd_alwaysActivateItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ let addonType = AddonManager.addonTypes[aAddon.type];
+ return ((addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
+ hasPermission(aAddon, "enable"));
+ },
+ doCommand: function cmd_alwaysActivateItem_doCommand(aAddon) {
+ aAddon.userDisabled = false;
+ }
+ },
+
+ cmd_neverActivateItem: {
+ isEnabled: function cmd_neverActivateItem_isEnabled(aAddon) {
+ if (!aAddon)
+ return false;
+ let addonType = AddonManager.addonTypes[aAddon.type];
+ return ((addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
+ hasPermission(aAddon, "disable"));
+ },
+ doCommand: function cmd_neverActivateItem_doCommand(aAddon) {
+ aAddon.userDisabled = true;
+ }
+ },
+
+ cmd_experimentsLearnMore: {
+ isEnabled: function cmd_experimentsLearnMore_isEnabled() {
+ let mainWindow = getMainWindow();
+ return mainWindow && "switchToTabHavingURI" in mainWindow;
+ },
+ doCommand: function cmd_experimentsLearnMore_doCommand() {
+ let url = Services.prefs.getCharPref("toolkit.telemetry.infoURL");
+ openOptionsInTab(url);
+ },
+ },
+
+ cmd_experimentsOpenTelemetryPreferences: {
+ isEnabled: function cmd_experimentsOpenTelemetryPreferences_isEnabled() {
+ return !!getMainWindowWithPreferencesPane();
+ },
+ doCommand: function cmd_experimentsOpenTelemetryPreferences_doCommand() {
+ let mainWindow = getMainWindowWithPreferencesPane();
+ mainWindow.openAdvancedPreferences("dataChoicesTab");
+ },
+ },
+ },
+
+ supportsCommand: function gVC_supportsCommand(aCommand) {
+ return (aCommand in this.commands);
+ },
+
+ isCommandEnabled: function gVC_isCommandEnabled(aCommand) {
+ if (!this.supportsCommand(aCommand))
+ return false;
+ var addon = this.currentViewObj.getSelectedAddon();
+ return this.commands[aCommand].isEnabled(addon);
+ },
+
+ updateCommands: function gVC_updateCommands() {
+ // wait until the view is initialized
+ if (!this.currentViewObj)
+ return;
+ var addon = this.currentViewObj.getSelectedAddon();
+ for (let commandId in this.commands)
+ this.updateCommand(commandId, addon);
+ },
+
+ updateCommand: function gVC_updateCommand(aCommandId, aAddon) {
+ if (typeof aAddon == "undefined")
+ aAddon = this.currentViewObj.getSelectedAddon();
+ var cmd = this.commands[aCommandId];
+ var cmdElt = document.getElementById(aCommandId);
+ cmdElt.setAttribute("disabled", !cmd.isEnabled(aAddon));
+ if ("getTooltip" in cmd) {
+ let tooltip = cmd.getTooltip(aAddon);
+ if (tooltip)
+ cmdElt.setAttribute("tooltiptext", tooltip);
+ else
+ cmdElt.removeAttribute("tooltiptext");
+ }
+ },
+
+ doCommand: function gVC_doCommand(aCommand, aAddon) {
+ if (!this.supportsCommand(aCommand))
+ return;
+ var cmd = this.commands[aCommand];
+ if (!aAddon)
+ aAddon = this.currentViewObj.getSelectedAddon();
+ if (!cmd.isEnabled(aAddon))
+ return;
+ cmd.doCommand(aAddon);
+ },
+
+ onEvent: function gVC_onEvent() {}
+};
+
+function hasInlineOptions(aAddon) {
+ return (aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE ||
+ aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_INFO);
+}
+
+function openOptionsInTab(optionsURL) {
+ let mainWindow = getMainWindow();
+ if ("switchToTabHavingURI" in mainWindow) {
+ mainWindow.switchToTabHavingURI(optionsURL, true);
+ return true;
+ }
+ return false;
+}
+
+function formatDate(aDate) {
+ return Cc["@mozilla.org/intl/scriptabledateformat;1"]
+ .getService(Ci.nsIScriptableDateFormat)
+ .FormatDate("",
+ Ci.nsIScriptableDateFormat.dateFormatLong,
+ aDate.getFullYear(),
+ aDate.getMonth() + 1,
+ aDate.getDate()
+ );
+}
+
+
+function hasPermission(aAddon, aPerm) {
+ var perm = AddonManager["PERM_CAN_" + aPerm.toUpperCase()];
+ return !!(aAddon.permissions & perm);
+}
+
+
+function isPending(aAddon, aAction) {
+ var action = AddonManager["PENDING_" + aAction.toUpperCase()];
+ return !!(aAddon.pendingOperations & action);
+}
+
+function isInState(aInstall, aState) {
+ var state = AddonManager["STATE_" + aState.toUpperCase()];
+ return aInstall.state == state;
+}
+
+function shouldShowVersionNumber(aAddon) {
+ if (!aAddon.version)
+ return false;
+
+ // The version number is hidden for lightweight themes.
+ if (aAddon.type == "theme")
+ return !/@personas\.mozilla\.org$/.test(aAddon.id);
+
+ return true;
+}
+
+function createItem(aObj, aIsInstall, aIsRemote) {
+ let item = document.createElement("richlistitem");
+
+ item.setAttribute("class", "addon addon-view");
+ item.setAttribute("name", aObj.name);
+ item.setAttribute("type", aObj.type);
+ item.setAttribute("remote", !!aIsRemote);
+
+ if (aIsInstall) {
+ item.mInstall = aObj;
+
+ if (aObj.state != AddonManager.STATE_INSTALLED) {
+ item.setAttribute("status", "installing");
+ return item;
+ }
+ aObj = aObj.addon;
+ }
+
+ item.mAddon = aObj;
+
+ item.setAttribute("status", "installed");
+
+ // set only attributes needed for sorting and XBL binding,
+ // the binding handles the rest
+ item.setAttribute("value", aObj.id);
+
+ if (aObj.type == "experiment") {
+ item.endDate = getExperimentEndDate(aObj);
+ }
+
+ return item;
+}
+
+function sortElements(aElements, aSortBy, aAscending) {
+ // aSortBy is an Array of attributes to sort by, in decending
+ // order of priority.
+
+ const DATE_FIELDS = ["updateDate"];
+ const NUMERIC_FIELDS = ["size", "relevancescore", "purchaseAmount"];
+
+ // We're going to group add-ons into the following buckets:
+ //
+ // enabledInstalled
+ // * Enabled
+ // * Incompatible but enabled because compatibility checking is off
+ // * Waiting to be installed
+ // * Waiting to be enabled
+ //
+ // pendingDisable
+ // * Waiting to be disabled
+ //
+ // pendingUninstall
+ // * Waiting to be removed
+ //
+ // disabledIncompatibleBlocked
+ // * Disabled
+ // * Incompatible
+ // * Blocklisted
+
+ const UISTATE_ORDER = ["enabled", "askToActivate", "pendingDisable",
+ "pendingUninstall", "disabled"];
+
+ function dateCompare(a, b) {
+ var aTime = a.getTime();
+ var bTime = b.getTime();
+ if (aTime < bTime)
+ return -1;
+ if (aTime > bTime)
+ return 1;
+ return 0;
+ }
+
+ function numberCompare(a, b) {
+ return a - b;
+ }
+
+ function stringCompare(a, b) {
+ return a.localeCompare(b);
+ }
+
+ function uiStateCompare(a, b) {
+ // If we're in descending order, swap a and b, because
+ // we don't ever want to have descending uiStates
+ if (!aAscending)
+ [a, b] = [b, a];
+
+ return (UISTATE_ORDER.indexOf(a) - UISTATE_ORDER.indexOf(b));
+ }
+
+ function getValue(aObj, aKey) {
+ if (!aObj)
+ return null;
+
+ if (aObj.hasAttribute(aKey))
+ return aObj.getAttribute(aKey);
+
+ var addon = aObj.mAddon || aObj.mInstall;
+ var addonType = aObj.mAddon && AddonManager.addonTypes[aObj.mAddon.type];
+
+ if (!addon)
+ return null;
+
+ if (aKey == "uiState") {
+ if (addon.pendingOperations == AddonManager.PENDING_DISABLE)
+ return "pendingDisable";
+ if (addon.pendingOperations == AddonManager.PENDING_UNINSTALL)
+ return "pendingUninstall";
+ if (!addon.isActive &&
+ (addon.pendingOperations != AddonManager.PENDING_ENABLE &&
+ addon.pendingOperations != AddonManager.PENDING_INSTALL))
+ return "disabled";
+ if (addonType && (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
+ addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE)
+ return "askToActivate";
+ else
+ return "enabled";
+ }
+
+ return addon[aKey];
+ }
+
+ // aSortFuncs will hold the sorting functions that we'll
+ // use per element, in the correct order.
+ var aSortFuncs = [];
+
+ for (let i = 0; i < aSortBy.length; i++) {
+ var sortBy = aSortBy[i];
+
+ aSortFuncs[i] = stringCompare;
+
+ if (sortBy == "uiState")
+ aSortFuncs[i] = uiStateCompare;
+ else if (DATE_FIELDS.indexOf(sortBy) != -1)
+ aSortFuncs[i] = dateCompare;
+ else if (NUMERIC_FIELDS.indexOf(sortBy) != -1)
+ aSortFuncs[i] = numberCompare;
+ }
+
+
+ aElements.sort(function elementsSort(a, b) {
+ if (!aAscending)
+ [a, b] = [b, a];
+
+ for (let i = 0; i < aSortFuncs.length; i++) {
+ var sortBy = aSortBy[i];
+ var aValue = getValue(a, sortBy);
+ var bValue = getValue(b, sortBy);
+
+ if (!aValue && !bValue)
+ return 0;
+ if (!aValue)
+ return -1;
+ if (!bValue)
+ return 1;
+ if (aValue != bValue) {
+ var result = aSortFuncs[i](aValue, bValue);
+
+ if (result != 0)
+ return result;
+ }
+ }
+
+ // If we got here, then all values of a and b
+ // must have been equal.
+ return 0;
+
+ });
+}
+
+function sortList(aList, aSortBy, aAscending) {
+ var elements = Array.slice(aList.childNodes, 0);
+ sortElements(elements, [aSortBy], aAscending);
+
+ while (aList.listChild)
+ aList.removeChild(aList.lastChild);
+
+ for (let element of elements)
+ aList.appendChild(element);
+}
+
+function getAddonsAndInstalls(aType, aCallback) {
+ let addons = null, installs = null;
+ let types = (aType != null) ? [aType] : null;
+
+ AddonManager.getAddonsByTypes(types, function getAddonsAndInstalls_getAddonsByTypes(aAddonsList) {
+ addons = aAddonsList;
+ if (installs != null)
+ aCallback(addons, installs);
+ });
+
+ AddonManager.getInstallsByTypes(types, function getAddonsAndInstalls_getInstallsByTypes(aInstallsList) {
+ // skip over upgrade installs and non-active installs
+ installs = aInstallsList.filter(function installsFilter(aInstall) {
+ return !(aInstall.existingAddon ||
+ aInstall.state == AddonManager.STATE_AVAILABLE);
+ });
+
+ if (addons != null)
+ aCallback(addons, installs)
+ });
+}
+
+function doPendingUninstalls(aListBox) {
+ // Uninstalling add-ons can mutate the list so find the add-ons first then
+ // uninstall them
+ var items = [];
+ var listitem = aListBox.firstChild;
+ while (listitem) {
+ if (listitem.getAttribute("pending") == "uninstall" &&
+ !(listitem.opRequiresRestart("uninstall")))
+ items.push(listitem.mAddon);
+ listitem = listitem.nextSibling;
+ }
+
+ for (let addon of items)
+ addon.uninstall();
+}
+
+var gCategories = {
+ node: null,
+ _search: null,
+
+ initialize: function gCategories_initialize() {
+ this.node = document.getElementById("categories");
+ this._search = this.get("addons://search/");
+
+ var types = AddonManager.addonTypes;
+ for (var type in types)
+ this.onTypeAdded(types[type]);
+
+ AddonManager.addTypeListener(this);
+
+ try {
+ this.node.value = Services.prefs.getCharPref(PREF_UI_LASTCATEGORY);
+ } catch (e) { }
+
+ // If there was no last view or no existing category matched the last view
+ // then the list will default to selecting the search category and we never
+ // want to show that as the first view so switch to the default category
+ if (!this.node.selectedItem || this.node.selectedItem == this._search)
+ this.node.value = gViewDefault;
+
+ var self = this;
+ this.node.addEventListener("select", function node_onSelected() {
+ self.maybeHideSearch();
+ gViewController.loadView(self.node.selectedItem.value);
+ }, false);
+
+ this.node.addEventListener("click", function node_onClicked(aEvent) {
+ var selectedItem = self.node.selectedItem;
+ if (aEvent.target.localName == "richlistitem" &&
+ aEvent.target == selectedItem) {
+ var viewId = selectedItem.value;
+
+ if (gViewController.parseViewId(viewId).type == "search") {
+ viewId += encodeURIComponent(gHeader.searchQuery);
+ }
+
+ gViewController.loadView(viewId);
+ }
+ }, false);
+ },
+
+ shutdown: function gCategories_shutdown() {
+ AddonManager.removeTypeListener(this);
+ },
+
+ _insertCategory: function gCategories_insertCategory(aId, aName, aView, aPriority, aStartHidden) {
+ // If this category already exists then don't re-add it
+ if (document.getElementById("category-" + aId))
+ return;
+
+ var category = document.createElement("richlistitem");
+ category.setAttribute("id", "category-" + aId);
+ category.setAttribute("value", aView);
+ category.setAttribute("class", "category");
+ category.setAttribute("name", aName);
+ category.setAttribute("tooltiptext", aName);
+ category.setAttribute("priority", aPriority);
+ category.setAttribute("hidden", aStartHidden);
+
+ var node;
+ for (node of this.node.children) {
+ var nodePriority = parseInt(node.getAttribute("priority"));
+ // If the new type's priority is higher than this one then this is the
+ // insertion point
+ if (aPriority < nodePriority)
+ break;
+ // If the new type's priority is lower than this one then this is isn't
+ // the insertion point
+ if (aPriority > nodePriority)
+ continue;
+ // If the priorities are equal and the new type's name is earlier
+ // alphabetically then this is the insertion point
+ if (String.localeCompare(aName, node.getAttribute("name")) < 0)
+ break;
+ }
+
+ this.node.insertBefore(category, node);
+ },
+
+ _removeCategory: function gCategories_removeCategory(aId) {
+ var category = document.getElementById("category-" + aId);
+ if (!category)
+ return;
+
+ // If this category is currently selected then switch to the default view
+ if (this.node.selectedItem == category)
+ gViewController.replaceView(gViewDefault);
+
+ this.node.removeChild(category);
+ },
+
+ onTypeAdded: function gCategories_onTypeAdded(aType) {
+ // Ignore types that we don't have a view object for
+ if (!(aType.viewType in gViewController.viewObjects))
+ return;
+
+ var aViewId = "addons://" + aType.viewType + "/" + aType.id;
+
+ var startHidden = false;
+ if (aType.flags & AddonManager.TYPE_UI_HIDE_EMPTY) {
+ var prefName = PREF_UI_TYPE_HIDDEN.replace("%TYPE%", aType.id);
+ try {
+ startHidden = Services.prefs.getBoolPref(prefName);
+ }
+ catch (e) {
+ // Default to hidden
+ startHidden = true;
+ }
+
+ var self = this;
+ gPendingInitializations++;
+ getAddonsAndInstalls(aType.id, function onTypeAdded_getAddonsAndInstalls(aAddonsList, aInstallsList) {
+ var hidden = (aAddonsList.length == 0 && aInstallsList.length == 0);
+ var item = self.get(aViewId);
+
+ // Don't load view that is becoming hidden
+ if (hidden && aViewId == gViewController.currentViewId)
+ gViewController.loadView(gViewDefault);
+
+ item.hidden = hidden;
+ Services.prefs.setBoolPref(prefName, hidden);
+
+ if (aAddonsList.length > 0 || aInstallsList.length > 0) {
+ notifyInitialized();
+ return;
+ }
+
+ gEventManager.registerInstallListener({
+ onDownloadStarted: function gCategories_onDownloadStarted(aInstall) {
+ this._maybeShowCategory(aInstall);
+ },
+
+ onInstallStarted: function gCategories_onInstallStarted(aInstall) {
+ this._maybeShowCategory(aInstall);
+ },
+
+ onInstallEnded: function gCategories_onInstallEnded(aInstall, aAddon) {
+ this._maybeShowCategory(aAddon);
+ },
+
+ onExternalInstall: function gCategories_onExternalInstall(aAddon, aExistingAddon, aRequiresRestart) {
+ this._maybeShowCategory(aAddon);
+ },
+
+ _maybeShowCategory: function gCategories_maybeShowCategory(aAddon) {
+ if (aType.id == aAddon.type) {
+ self.get(aViewId).hidden = false;
+ Services.prefs.setBoolPref(prefName, false);
+ gEventManager.unregisterInstallListener(this);
+ }
+ }
+ });
+
+ notifyInitialized();
+ });
+ }
+
+ this._insertCategory(aType.id, aType.name, aViewId, aType.uiPriority,
+ startHidden);
+ },
+
+ onTypeRemoved: function gCategories_onTypeRemoved(aType) {
+ this._removeCategory(aType.id);
+ },
+
+ get selected() {
+ return this.node.selectedItem ? this.node.selectedItem.value : null;
+ },
+
+ select: function gCategories_select(aId, aPreviousView) {
+ var view = gViewController.parseViewId(aId);
+ if (view.type == "detail" && aPreviousView) {
+ aId = aPreviousView;
+ view = gViewController.parseViewId(aPreviousView);
+ }
+
+ Services.prefs.setCharPref(PREF_UI_LASTCATEGORY, aId);
+
+ if (this.node.selectedItem &&
+ this.node.selectedItem.value == aId) {
+ this.node.selectedItem.hidden = false;
+ this.node.selectedItem.disabled = false;
+ return;
+ }
+
+ if (view.type == "search")
+ var item = this._search;
+ else
+ var item = this.get(aId);
+
+ if (item) {
+ item.hidden = false;
+ item.disabled = false;
+ this.node.suppressOnSelect = true;
+ this.node.selectedItem = item;
+ this.node.suppressOnSelect = false;
+ this.node.ensureElementIsVisible(item);
+
+ this.maybeHideSearch();
+ }
+ },
+
+ get: function gCategories_get(aId) {
+ var items = document.getElementsByAttribute("value", aId);
+ if (items.length)
+ return items[0];
+ return null;
+ },
+
+ setBadge: function gCategories_setBadge(aId, aCount) {
+ let item = this.get(aId);
+ if (item)
+ item.badgeCount = aCount;
+ },
+
+ maybeHideSearch: function gCategories_maybeHideSearch() {
+ var view = gViewController.parseViewId(this.node.selectedItem.value);
+ this._search.disabled = view.type != "search";
+ }
+};
+
+
+var gHeader = {
+ _search: null,
+ _dest: "",
+
+ initialize: function gHeader_initialize() {
+ this._search = document.getElementById("header-search");
+
+ this._search.addEventListener("command", function search_onCommand(aEvent) {
+ var query = aEvent.target.value;
+ if (query.length == 0)
+ return;
+
+ gViewController.loadView("addons://search/" + encodeURIComponent(query));
+ }, false);
+
+ function updateNavButtonVisibility() {
+ var shouldShow = gHeader.shouldShowNavButtons;
+ document.getElementById("back-btn").hidden = !shouldShow;
+ document.getElementById("forward-btn").hidden = !shouldShow;
+ }
+
+ window.addEventListener("focus", function window_onFocus(aEvent) {
+ if (aEvent.target == window)
+ updateNavButtonVisibility();
+ }, false);
+
+ updateNavButtonVisibility();
+ },
+
+ focusSearchBox: function gHeader_focusSearchBox() {
+ this._search.focus();
+ },
+
+ onKeyPress: function gHeader_onKeyPress(aEvent) {
+ if (String.fromCharCode(aEvent.charCode) == "/") {
+ this.focusSearchBox();
+ return;
+ }
+
+ // XXXunf Temporary until bug 371900 is fixed.
+ let key = document.getElementById("focusSearch").getAttribute("key");
+#ifdef XP_MACOSX
+ let keyModifier = aEvent.metaKey;
+#else
+ let keyModifier = aEvent.ctrlKey;
+#endif
+ if (String.fromCharCode(aEvent.charCode) == key && keyModifier) {
+ this.focusSearchBox();
+ return;
+ }
+ },
+
+ get shouldShowNavButtons() {
+ var docshellItem = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem);
+
+ // If there is no outer frame then make the buttons visible
+ if (docshellItem.rootTreeItem == docshellItem)
+ return true;
+
+ var outerWin = docshellItem.rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ var outerDoc = outerWin.document;
+ var node = outerDoc.getElementById("back-button");
+ // If the outer frame has no back-button then make the buttons visible
+ if (!node)
+ return true;
+
+ // If the back-button or any of its parents are hidden then make the buttons
+ // visible
+ while (node != outerDoc) {
+ var style = outerWin.getComputedStyle(node, "");
+ if (style.display == "none")
+ return true;
+ if (style.visibility != "visible")
+ return true;
+ node = node.parentNode;
+ }
+
+ return false;
+ },
+
+ get searchQuery() {
+ return this._search.value;
+ },
+
+ set searchQuery(aQuery) {
+ this._search.value = aQuery;
+ },
+};
+
+
+var gDiscoverView = {
+ node: null,
+ enabled: true,
+ // Set to true after the view is first shown. If initialization completes
+ // after this then it must also load the discover homepage
+ loaded: false,
+ _browser: null,
+ _loading: null,
+ _error: null,
+ homepageURL: null,
+ _loadListeners: [],
+
+ initialize: function gDiscoverView_initialize() {
+ this.enabled = isDiscoverEnabled();
+ if (!this.enabled) {
+ gCategories.get("addons://discover/").hidden = true;
+ return;
+ }
+
+ this.node = document.getElementById("discover-view");
+ this._loading = document.getElementById("discover-loading");
+ this._error = document.getElementById("discover-error");
+ this._browser = document.getElementById("discover-browser");
+
+ let compatMode = "normal";
+ if (!AddonManager.checkCompatibility)
+ compatMode = "ignore";
+ else if (AddonManager.strictCompatibility)
+ compatMode = "strict";
+
+ var url = Services.prefs.getCharPref(PREF_DISCOVERURL);
+ url = url.replace("%COMPATIBILITY_MODE%", compatMode);
+ url = Services.urlFormatter.formatURL(url);
+
+ var self = this;
+
+ function setURL(aURL) {
+ try {
+ self.homepageURL = Services.io.newURI(aURL, null, null);
+ } catch (e) {
+ self.showError();
+ notifyInitialized();
+ return;
+ }
+
+ self._browser.homePage = self.homepageURL.spec;
+ self._browser.addProgressListener(self);
+
+ if (self.loaded)
+ self._loadURL(self.homepageURL.spec, false, notifyInitialized);
+ else
+ notifyInitialized();
+ }
+
+ if (Services.prefs.getBoolPref(PREF_GETADDONS_CACHE_ENABLED) == false) {
+ setURL(url);
+ return;
+ }
+
+ gPendingInitializations++;
+ AddonManager.getAllAddons(function initialize_getAllAddons(aAddons) {
+ var list = {};
+ for (let addon of aAddons) {
+ var prefName = PREF_GETADDONS_CACHE_ID_ENABLED.replace("%ID%",
+ addon.id);
+ try {
+ if (!Services.prefs.getBoolPref(prefName))
+ continue;
+ } catch (e) { }
+ list[addon.id] = {
+ name: addon.name,
+ version: addon.version,
+ type: addon.type,
+ userDisabled: addon.userDisabled,
+ isCompatible: addon.isCompatible,
+ isBlocklisted: addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED
+ }
+ }
+
+ setURL(url + "#" + JSON.stringify(list));
+ });
+ },
+
+ destroy: function gDiscoverView_destroy() {
+ try {
+ this._browser.removeProgressListener(this);
+ }
+ catch (e) {
+ // Ignore the case when the listener wasn't already registered
+ }
+ },
+
+ show: function gDiscoverView_show(aParam, aRequest, aState, aIsRefresh) {
+ gViewController.updateCommands();
+
+ // If we're being told to load a specific URL then just do that
+ if (aState && "url" in aState) {
+ this.loaded = true;
+ this._loadURL(aState.url);
+ }
+
+ // If the view has loaded before and still at the homepage (if refreshing),
+ // and the error page is not visible then there is nothing else to do
+ if (this.loaded && this.node.selectedPanel != this._error &&
+ (!aIsRefresh || (this._browser.currentURI &&
+ this._browser.currentURI.spec == this._browser.homePage))) {
+ gViewController.notifyViewChanged();
+ return;
+ }
+
+ this.loaded = true;
+
+ // No homepage means initialization isn't complete, the browser will get
+ // loaded once initialization is complete
+ if (!this.homepageURL) {
+ this._loadListeners.push(gViewController.notifyViewChanged.bind(gViewController));
+ return;
+ }
+
+ this._loadURL(this.homepageURL.spec, aIsRefresh,
+ gViewController.notifyViewChanged.bind(gViewController));
+ },
+
+ canRefresh: function gDiscoverView_canRefresh() {
+ if (this._browser.currentURI &&
+ this._browser.currentURI.spec == this._browser.homePage)
+ return false;
+ return true;
+ },
+
+ refresh: function gDiscoverView_refresh(aParam, aRequest, aState) {
+ this.show(aParam, aRequest, aState, true);
+ },
+
+ hide: function gDiscoverView_hide() { },
+
+ showError: function gDiscoverView_showError() {
+ this.node.selectedPanel = this._error;
+ },
+
+ _loadURL: function gDiscoverView_loadURL(aURL, aKeepHistory, aCallback) {
+ if (this._browser.currentURI.spec == aURL) {
+ if (aCallback)
+ aCallback();
+ return;
+ }
+
+ if (aCallback)
+ this._loadListeners.push(aCallback);
+
+ var flags = 0;
+ if (!aKeepHistory)
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
+
+ this._browser.loadURIWithFlags(aURL, flags);
+ },
+
+ onLocationChange: function gDiscoverView_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+ // Ignore the about:blank load
+ if (aLocation.spec == "about:blank")
+ return;
+
+ // When using the real session history the inner-frame will update the
+ // session history automatically, if using the fake history though it must
+ // be manually updated
+ if (gHistory == FakeHistory) {
+ var docshell = aWebProgress.QueryInterface(Ci.nsIDocShell);
+
+ var state = {
+ view: "addons://discover/",
+ url: aLocation.spec
+ };
+
+ var replaceHistory = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY << 16;
+ if (docshell.loadType & replaceHistory)
+ gHistory.replaceState(state);
+ else
+ gHistory.pushState(state);
+ gViewController.lastHistoryIndex = gHistory.index;
+ }
+
+ gViewController.updateCommands();
+
+ // If the hostname is the same as the new location's host and either the
+ // default scheme is insecure or the new location is secure then continue
+ // with the load
+ if (aLocation.host == this.homepageURL.host &&
+ (!this.homepageURL.schemeIs("https") || aLocation.schemeIs("https")))
+ return;
+
+ // Canceling the request will send an error to onStateChange which will show
+ // the error page
+ aRequest.cancel(Components.results.NS_BINDING_ABORTED);
+ },
+
+ onSecurityChange: function gDiscoverView_onSecurityChange(aWebProgress, aRequest, aState) {
+ // Don't care about security if the page is not https
+ if (!this.homepageURL.schemeIs("https"))
+ return;
+
+ // If the request was secure then it is ok
+ if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
+ return;
+
+ // Canceling the request will send an error to onStateChange which will show
+ // the error page
+ aRequest.cancel(Components.results.NS_BINDING_ABORTED);
+ },
+
+ onStateChange: function gDiscoverView_onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+ let transferStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
+ Ci.nsIWebProgressListener.STATE_IS_REQUEST |
+ Ci.nsIWebProgressListener.STATE_TRANSFERRING;
+ // Once transferring begins show the content
+ if (aStateFlags & transferStart)
+ this.node.selectedPanel = this._browser;
+
+ // Only care about the network events
+ if (!(aStateFlags & (Ci.nsIWebProgressListener.STATE_IS_NETWORK)))
+ return;
+
+ // If this is the start of network activity then show the loading page
+ if (aStateFlags & (Ci.nsIWebProgressListener.STATE_START))
+ this.node.selectedPanel = this._loading;
+
+ // Ignore anything except stop events
+ if (!(aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP)))
+ return;
+
+ // Consider the successful load of about:blank as still loading
+ if (aRequest instanceof Ci.nsIChannel && aRequest.URI.spec == "about:blank")
+ return;
+
+ // If there was an error loading the page or the new hostname is not the
+ // same as the default hostname or the default scheme is secure and the new
+ // scheme is insecure then show the error page
+ const NS_ERROR_PARSED_DATA_CACHED = 0x805D0021;
+ if (!(Components.isSuccessCode(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED) ||
+ (aRequest && aRequest instanceof Ci.nsIHttpChannel && !aRequest.requestSucceeded)) {
+ this.showError();
+ } else {
+ // Got a successful load, make sure the browser is visible
+ this.node.selectedPanel = this._browser;
+ gViewController.updateCommands();
+ }
+
+ var listeners = this._loadListeners;
+ this._loadListeners = [];
+
+ for (let listener of listeners)
+ listener();
+ },
+
+ onProgressChange: function gDiscoverView_onProgressChange() { },
+ onStatusChange: function gDiscoverView_onStatusChange() { },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference]),
+
+ getSelectedAddon: function gDiscoverView_getSelectedAddon() null
+};
+
+
+var gCachedAddons = {};
+
+var gSearchView = {
+ node: null,
+ _filter: null,
+ _sorters: null,
+ _loading: null,
+ _listBox: null,
+ _emptyNotice: null,
+ _allResultsLink: null,
+ _lastQuery: null,
+ _lastRemoteTotal: 0,
+ _pendingSearches: 0,
+
+ initialize: function gSearchView_initialize() {
+ this.node = document.getElementById("search-view");
+ this._filter = document.getElementById("search-filter-radiogroup");
+ this._sorters = document.getElementById("search-sorters");
+ this._sorters.handler = this;
+ this._loading = document.getElementById("search-loading");
+ this._listBox = document.getElementById("search-list");
+ this._emptyNotice = document.getElementById("search-list-empty");
+ this._allResultsLink = document.getElementById("search-allresults-link");
+
+ if (!AddonManager.isInstallEnabled("application/x-xpinstall"))
+ this._filter.hidden = true;
+
+ var self = this;
+ this._listBox.addEventListener("keydown", function listbox_onKeydown(aEvent) {
+ if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
+ var item = self._listBox.selectedItem;
+ if (item)
+ item.showInDetailView();
+ }
+ }, false);
+
+ this._filter.addEventListener("command", function filter_onCommand() self.updateView(), false);
+ },
+
+ shutdown: function gSearchView_shutdown() {
+ if (AddonRepository.isSearching)
+ AddonRepository.cancelSearch();
+ },
+
+ get isSearching() {
+ return this._pendingSearches > 0;
+ },
+
+ show: function gSearchView_show(aQuery, aRequest) {
+ gEventManager.registerInstallListener(this);
+
+ this.showEmptyNotice(false);
+ this.showAllResultsLink(0);
+ this.showLoading(true);
+ this._sorters.showprice = false;
+
+ gHeader.searchQuery = aQuery;
+ aQuery = aQuery.trim().toLocaleLowerCase();
+ if (this._lastQuery == aQuery) {
+ this.updateView();
+ gViewController.notifyViewChanged();
+ return;
+ }
+ this._lastQuery = aQuery;
+
+ if (AddonRepository.isSearching)
+ AddonRepository.cancelSearch();
+
+ while (this._listBox.firstChild.localName == "richlistitem")
+ this._listBox.removeChild(this._listBox.firstChild);
+
+ var self = this;
+ gCachedAddons = {};
+ this._pendingSearches = 2;
+ this._sorters.setSort("relevancescore", false);
+
+ var elements = [];
+
+ function createSearchResults(aObjsList, aIsInstall, aIsRemote) {
+ for (let index in aObjsList) {
+ let obj = aObjsList[index];
+ let score = aObjsList.length - index;
+ if (!aIsRemote && aQuery.length > 0) {
+ score = self.getMatchScore(obj, aQuery);
+ if (score == 0)
+ continue;
+ }
+
+ let item = createItem(obj, aIsInstall, aIsRemote);
+ item.setAttribute("relevancescore", score);
+ if (aIsRemote) {
+ gCachedAddons[obj.id] = obj;
+ if (obj.purchaseURL)
+ self._sorters.showprice = true;
+ }
+
+ elements.push(item);
+ }
+ }
+
+ function finishSearch(createdCount) {
+ if (elements.length > 0) {
+ sortElements(elements, [self._sorters.sortBy], self._sorters.ascending);
+ for (let element of elements)
+ self._listBox.insertBefore(element, self._listBox.lastChild);
+ self.updateListAttributes();
+ }
+
+ self._pendingSearches--;
+ self.updateView();
+
+ if (!self.isSearching)
+ gViewController.notifyViewChanged();
+ }
+
+ getAddonsAndInstalls(null, function show_getAddonsAndInstalls(aAddons, aInstalls) {
+ if (gViewController && aRequest != gViewController.currentViewRequest)
+ return;
+
+ createSearchResults(aAddons, false, false);
+ createSearchResults(aInstalls, true, false);
+ finishSearch();
+ });
+
+ var maxRemoteResults = 0;
+ try {
+ maxRemoteResults = Services.prefs.getIntPref(PREF_MAXRESULTS);
+ } catch(e) {}
+
+ if (maxRemoteResults <= 0) {
+ finishSearch(0);
+ return;
+ }
+
+ AddonRepository.searchAddons(aQuery, maxRemoteResults, {
+ searchFailed: function show_SearchFailed() {
+ if (gViewController && aRequest != gViewController.currentViewRequest)
+ return;
+
+ self._lastRemoteTotal = 0;
+
+ // XXXunf Better handling of AMO search failure. See bug 579502
+ finishSearch(0); // Silently fail
+ },
+
+ searchSucceeded: function show_SearchSucceeded(aAddonsList, aAddonCount, aTotalResults) {
+ if (gViewController && aRequest != gViewController.currentViewRequest)
+ return;
+
+ if (aTotalResults > maxRemoteResults)
+ self._lastRemoteTotal = aTotalResults;
+ else
+ self._lastRemoteTotal = 0;
+
+ var createdCount = createSearchResults(aAddonsList, false, true);
+ finishSearch(createdCount);
+ }
+ });
+ },
+
+ showLoading: function gSearchView_showLoading(aLoading) {
+ this._loading.hidden = !aLoading;
+ this._listBox.hidden = aLoading;
+ },
+
+ updateView: function gSearchView_updateView() {
+ var showLocal = this._filter.value == "local";
+
+ if (!showLocal && !AddonManager.isInstallEnabled("application/x-xpinstall"))
+ showLocal = true;
+
+ this._listBox.setAttribute("local", showLocal);
+ this._listBox.setAttribute("remote", !showLocal);
+
+ this.showLoading(this.isSearching && !showLocal);
+ if (!this.isSearching) {
+ var isEmpty = true;
+ var results = this._listBox.getElementsByTagName("richlistitem");
+ for (let result of results) {
+ var isRemote = (result.getAttribute("remote") == "true");
+ if ((isRemote && !showLocal) || (!isRemote && showLocal)) {
+ isEmpty = false;
+ break;
+ }
+ }
+
+ this.showEmptyNotice(isEmpty);
+ this.showAllResultsLink(this._lastRemoteTotal);
+ }
+
+ gViewController.updateCommands();
+ },
+
+ hide: function gSearchView_hide() {
+ gEventManager.unregisterInstallListener(this);
+ doPendingUninstalls(this._listBox);
+ },
+
+ getMatchScore: function gSearchView_getMatchScore(aObj, aQuery) {
+ var score = 0;
+ score += this.calculateMatchScore(aObj.name, aQuery,
+ SEARCH_SCORE_MULTIPLIER_NAME);
+ score += this.calculateMatchScore(aObj.description, aQuery,
+ SEARCH_SCORE_MULTIPLIER_DESCRIPTION);
+ return score;
+ },
+
+ calculateMatchScore: function gSearchView_calculateMatchScore(aStr, aQuery, aMultiplier) {
+ var score = 0;
+ if (!aStr || aQuery.length == 0)
+ return score;
+
+ aStr = aStr.trim().toLocaleLowerCase();
+ var haystack = aStr.split(/\s+/);
+ var needles = aQuery.split(/\s+/);
+
+ for (let needle of needles) {
+ for (let hay of haystack) {
+ if (hay == needle) {
+ // matching whole words is best
+ score += SEARCH_SCORE_MATCH_WHOLEWORD;
+ } else {
+ let i = hay.indexOf(needle);
+ if (i == 0) // matching on word boundries is also good
+ score += SEARCH_SCORE_MATCH_WORDBOUNDRY;
+ else if (i > 0) // substring matches not so good
+ score += SEARCH_SCORE_MATCH_SUBSTRING;
+ }
+ }
+ }
+
+ // give progressively higher score for longer queries, since longer queries
+ // are more likely to be unique and therefore more relevant.
+ if (needles.length > 1 && aStr.indexOf(aQuery) != -1)
+ score += needles.length;
+
+ return score * aMultiplier;
+ },
+
+ showEmptyNotice: function gSearchView_showEmptyNotice(aShow) {
+ this._emptyNotice.hidden = !aShow;
+ this._listBox.hidden = aShow;
+ },
+
+ showAllResultsLink: function gSearchView_showAllResultsLink(aTotalResults) {
+ if (aTotalResults == 0) {
+ this._allResultsLink.hidden = true;
+ return;
+ }
+
+ var linkStr = gStrings.ext.GetStringFromName("showAllSearchResults");
+ linkStr = PluralForm.get(aTotalResults, linkStr);
+ linkStr = linkStr.replace("#1", aTotalResults);
+ this._allResultsLink.setAttribute("value", linkStr);
+
+ this._allResultsLink.setAttribute("href",
+ AddonRepository.getSearchURL(this._lastQuery));
+ this._allResultsLink.hidden = false;
+ },
+
+ updateListAttributes: function gSearchView_updateListAttributes() {
+ var item = this._listBox.querySelector("richlistitem[remote='true'][first]");
+ if (item)
+ item.removeAttribute("first");
+ item = this._listBox.querySelector("richlistitem[remote='true'][last]");
+ if (item)
+ item.removeAttribute("last");
+ var items = this._listBox.querySelectorAll("richlistitem[remote='true']");
+ if (items.length > 0) {
+ items[0].setAttribute("first", true);
+ items[items.length - 1].setAttribute("last", true);
+ }
+
+ item = this._listBox.querySelector("richlistitem:not([remote='true'])[first]");
+ if (item)
+ item.removeAttribute("first");
+ item = this._listBox.querySelector("richlistitem:not([remote='true'])[last]");
+ if (item)
+ item.removeAttribute("last");
+ items = this._listBox.querySelectorAll("richlistitem:not([remote='true'])");
+ if (items.length > 0) {
+ items[0].setAttribute("first", true);
+ items[items.length - 1].setAttribute("last", true);
+ }
+
+ },
+
+ onSortChanged: function gSearchView_onSortChanged(aSortBy, aAscending) {
+ var footer = this._listBox.lastChild;
+ this._listBox.removeChild(footer);
+
+ sortList(this._listBox, aSortBy, aAscending);
+ this.updateListAttributes();
+
+ this._listBox.appendChild(footer);
+ },
+
+ onDownloadCancelled: function gSearchView_onDownloadCancelled(aInstall) {
+ this.removeInstall(aInstall);
+ },
+
+ onInstallCancelled: function gSearchView_onInstallCancelled(aInstall) {
+ this.removeInstall(aInstall);
+ },
+
+ removeInstall: function gSearchView_removeInstall(aInstall) {
+ for (let item of this._listBox.childNodes) {
+ if (item.mInstall == aInstall) {
+ this._listBox.removeChild(item);
+ return;
+ }
+ }
+ },
+
+ getSelectedAddon: function gSearchView_getSelectedAddon() {
+ var item = this._listBox.selectedItem;
+ if (item)
+ return item.mAddon;
+ return null;
+ },
+
+ getListItemForID: function gSearchView_getListItemForID(aId) {
+ var listitem = this._listBox.firstChild;
+ while (listitem) {
+ if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId)
+ return listitem;
+ listitem = listitem.nextSibling;
+ }
+ return null;
+ }
+};
+
+
+var gListView = {
+ node: null,
+ _listBox: null,
+ _emptyNotice: null,
+ _type: null,
+
+ initialize: function gListView_initialize() {
+ this.node = document.getElementById("list-view");
+ this._listBox = document.getElementById("addon-list");
+ this._emptyNotice = document.getElementById("addon-list-empty");
+
+ var self = this;
+ this._listBox.addEventListener("keydown", function listbox_onKeydown(aEvent) {
+ if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
+ var item = self._listBox.selectedItem;
+ if (item)
+ item.showInDetailView();
+ }
+ }, false);
+ },
+
+ show: function gListView_show(aType, aRequest) {
+ if (!(aType in AddonManager.addonTypes))
+ throw Components.Exception("Attempting to show unknown type " + aType, Cr.NS_ERROR_INVALID_ARG);
+
+ this._type = aType;
+ this.node.setAttribute("type", aType);
+ this.showEmptyNotice(false);
+
+ while (this._listBox.itemCount > 0)
+ this._listBox.removeItemAt(0);
+
+ if (aType == "plugin") {
+ navigator.plugins.refresh(false);
+ }
+
+ var self = this;
+ getAddonsAndInstalls(aType, function show_getAddonsAndInstalls(aAddonsList, aInstallsList) {
+ if (gViewController && aRequest != gViewController.currentViewRequest)
+ return;
+
+ var elements = [];
+
+ for (let addonItem of aAddonsList)
+ elements.push(createItem(addonItem));
+
+ for (let installItem of aInstallsList)
+ elements.push(createItem(installItem, true));
+
+ self.showEmptyNotice(elements.length == 0);
+ if (elements.length > 0) {
+ sortElements(elements, ["uiState", "name"], true);
+ for (let element of elements)
+ self._listBox.appendChild(element);
+ }
+
+ gEventManager.registerInstallListener(self);
+ gViewController.updateCommands();
+ gViewController.notifyViewChanged();
+ });
+ },
+
+ hide: function gListView_hide() {
+ gEventManager.unregisterInstallListener(this);
+ doPendingUninstalls(this._listBox);
+ },
+
+ showEmptyNotice: function gListView_showEmptyNotice(aShow) {
+ this._emptyNotice.hidden = !aShow;
+ },
+
+ onSortChanged: function gListView_onSortChanged(aSortBy, aAscending) {
+ sortList(this._listBox, aSortBy, aAscending);
+ },
+
+ onExternalInstall: function gListView_onExternalInstall(aAddon, aExistingAddon, aRequiresRestart) {
+ // The existing list item will take care of upgrade installs
+ if (aExistingAddon)
+ return;
+
+ this.addItem(aAddon);
+ },
+
+ onDownloadStarted: function gListView_onDownloadStarted(aInstall) {
+ this.addItem(aInstall, true);
+ },
+
+ onInstallStarted: function gListView_onInstallStarted(aInstall) {
+ this.addItem(aInstall, true);
+ },
+
+ onDownloadCancelled: function gListView_onDownloadCancelled(aInstall) {
+ this.removeItem(aInstall, true);
+ },
+
+ onInstallCancelled: function gListView_onInstallCancelled(aInstall) {
+ this.removeItem(aInstall, true);
+ },
+
+ onInstallEnded: function gListView_onInstallEnded(aInstall) {
+ // Remove any install entries for upgrades, their status will appear against
+ // the existing item
+ if (aInstall.existingAddon)
+ this.removeItem(aInstall, true);
+
+ if (aInstall.addon.type == "experiment") {
+ let item = this.getListItemForID(aInstall.addon.id);
+ if (item) {
+ item.endDate = getExperimentEndDate(aInstall.addon);
+ }
+ }
+ },
+
+ addItem: function gListView_addItem(aObj, aIsInstall) {
+ if (aObj.type != this._type)
+ return;
+
+ if (aIsInstall && aObj.existingAddon)
+ return;
+
+ let prop = aIsInstall ? "mInstall" : "mAddon";
+ for (let item of this._listBox.childNodes) {
+ if (item[prop] == aObj)
+ return;
+ }
+
+ let item = createItem(aObj, aIsInstall);
+ this._listBox.insertBefore(item, this._listBox.firstChild);
+ this.showEmptyNotice(false);
+ },
+
+ removeItem: function gListView_removeItem(aObj, aIsInstall) {
+ let prop = aIsInstall ? "mInstall" : "mAddon";
+
+ for (let item of this._listBox.childNodes) {
+ if (item[prop] == aObj) {
+ this._listBox.removeChild(item);
+ this.showEmptyNotice(this._listBox.itemCount == 0);
+ return;
+ }
+ }
+ },
+
+ getSelectedAddon: function gListView_getSelectedAddon() {
+ var item = this._listBox.selectedItem;
+ if (item)
+ return item.mAddon;
+ return null;
+ },
+
+ getListItemForID: function gListView_getListItemForID(aId) {
+ var listitem = this._listBox.firstChild;
+ while (listitem) {
+ if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId)
+ return listitem;
+ listitem = listitem.nextSibling;
+ }
+ return null;
+ }
+};
+
+
+var gDetailView = {
+ node: null,
+ _addon: null,
+ _loadingTimer: null,
+ _autoUpdate: null,
+
+ initialize: function gDetailView_initialize() {
+ this.node = document.getElementById("detail-view");
+
+ this._autoUpdate = document.getElementById("detail-autoUpdate");
+
+ var self = this;
+ this._autoUpdate.addEventListener("command", function autoUpdate_onCommand() {
+ self._addon.applyBackgroundUpdates = self._autoUpdate.value;
+ }, true);
+ },
+
+ shutdown: function gDetailView_shutdown() {
+ AddonManager.removeManagerListener(this);
+ },
+
+ onUpdateModeChanged: function gDetailView_onUpdateModeChanged() {
+ this.onPropertyChanged(["applyBackgroundUpdates"]);
+ },
+
+ _updateView: function gDetailView_updateView(aAddon, aIsRemote, aScrollToPreferences) {
+ AddonManager.addManagerListener(this);
+ this.clearLoading();
+
+ this._addon = aAddon;
+ gEventManager.registerAddonListener(this, aAddon.id);
+ gEventManager.registerInstallListener(this);
+
+ this.node.setAttribute("type", aAddon.type);
+
+ // If the search category isn't selected then make sure to select the
+ // correct category
+ if (gCategories.selected != "addons://search/")
+ gCategories.select("addons://list/" + aAddon.type);
+
+ document.getElementById("detail-name").textContent = aAddon.name;
+ var icon = aAddon.icon64URL ? aAddon.icon64URL : aAddon.iconURL;
+ document.getElementById("detail-icon").src = icon ? icon : "";
+ document.getElementById("detail-creator").setCreator(aAddon.creator, aAddon.homepageURL);
+ document.getElementById("detail-translators").setTranslators(aAddon.translators, aAddon.type);
+
+ var version = document.getElementById("detail-version");
+ if (shouldShowVersionNumber(aAddon)) {
+ version.hidden = false;
+ version.value = aAddon.version;
+ } else {
+ version.hidden = true;
+ }
+
+ var screenshot = document.getElementById("detail-screenshot");
+ if (aAddon.screenshots && aAddon.screenshots.length > 0) {
+ if (aAddon.screenshots[0].thumbnailURL) {
+ screenshot.src = aAddon.screenshots[0].thumbnailURL;
+ screenshot.width = aAddon.screenshots[0].thumbnailWidth;
+ screenshot.height = aAddon.screenshots[0].thumbnailHeight;
+ } else {
+ screenshot.src = aAddon.screenshots[0].url;
+ screenshot.width = aAddon.screenshots[0].width;
+ screenshot.height = aAddon.screenshots[0].height;
+ }
+ screenshot.setAttribute("loading", "true");
+ screenshot.hidden = false;
+ } else {
+ screenshot.hidden = true;
+ }
+
+ var desc = document.getElementById("detail-desc");
+ desc.textContent = aAddon.description;
+
+ var fullDesc = document.getElementById("detail-fulldesc");
+ if (aAddon.fullDescription) {
+ // The following is part of an awful hack to include the licenses for GMP
+ // plugins without having bug 624602 fixed yet, and intentionally ignores
+ // localisation.
+ if (aAddon.isGMPlugin) {
+ fullDesc.innerHTML = aAddon.fullDescription;
+ } else {
+ fullDesc.textContent = aAddon.fullDescription;
+ }
+
+ fullDesc.hidden = false;
+ } else {
+ fullDesc.hidden = true;
+ }
+
+ var contributions = document.getElementById("detail-contributions");
+ if ("contributionURL" in aAddon && aAddon.contributionURL) {
+ contributions.hidden = false;
+ var amount = document.getElementById("detail-contrib-suggested");
+ if (aAddon.contributionAmount) {
+ amount.value = gStrings.ext.formatStringFromName("contributionAmount2",
+ [aAddon.contributionAmount],
+ 1);
+ amount.hidden = false;
+ } else {
+ amount.hidden = true;
+ }
+ } else {
+ contributions.hidden = true;
+ }
+
+ if ("purchaseURL" in aAddon && aAddon.purchaseURL) {
+ var purchase = document.getElementById("detail-purchase-btn");
+ purchase.label = gStrings.ext.formatStringFromName("cmd.purchaseAddon.label",
+ [aAddon.purchaseDisplayAmount],
+ 1);
+ purchase.accesskey = gStrings.ext.GetStringFromName("cmd.purchaseAddon.accesskey");
+ }
+
+ var updateDateRow = document.getElementById("detail-dateUpdated");
+ if (aAddon.updateDate) {
+ var date = formatDate(aAddon.updateDate);
+ updateDateRow.value = date;
+ } else {
+ updateDateRow.value = null;
+ }
+
+ // TODO if the add-on was downloaded from releases.mozilla.org link to the
+ // AMO profile (bug 590344)
+ if (false) {
+ document.getElementById("detail-repository-row").hidden = false;
+ document.getElementById("detail-homepage-row").hidden = true;
+ var repository = document.getElementById("detail-repository");
+ repository.value = aAddon.homepageURL;
+ repository.href = aAddon.homepageURL;
+ } else if (aAddon.homepageURL) {
+ document.getElementById("detail-repository-row").hidden = true;
+ document.getElementById("detail-homepage-row").hidden = false;
+ var homepage = document.getElementById("detail-homepage");
+ homepage.value = aAddon.homepageURL;
+ homepage.href = aAddon.homepageURL;
+ } else {
+ document.getElementById("detail-repository-row").hidden = true;
+ document.getElementById("detail-homepage-row").hidden = true;
+ }
+
+ var rating = document.getElementById("detail-rating");
+ if (aAddon.averageRating) {
+ rating.averageRating = aAddon.averageRating;
+ rating.hidden = false;
+ } else {
+ rating.hidden = true;
+ }
+
+ var reviews = document.getElementById("detail-reviews");
+ if (aAddon.reviewURL) {
+ var text = gStrings.ext.GetStringFromName("numReviews");
+ text = PluralForm.get(aAddon.reviewCount, text)
+ text = text.replace("#1", aAddon.reviewCount);
+ reviews.value = text;
+ reviews.hidden = false;
+ reviews.href = aAddon.reviewURL;
+ } else {
+ reviews.hidden = true;
+ }
+
+ document.getElementById("detail-rating-row").hidden = !aAddon.averageRating && !aAddon.reviewURL;
+
+ var sizeRow = document.getElementById("detail-size");
+ if (aAddon.size && aIsRemote) {
+ let [size, unit] = DownloadUtils.convertByteUnits(parseInt(aAddon.size));
+ let formatted = gStrings.dl.GetStringFromName("doneSize");
+ formatted = formatted.replace("#1", size).replace("#2", unit);
+ sizeRow.value = formatted;
+ } else {
+ sizeRow.value = null;
+ }
+
+ var downloadsRow = document.getElementById("detail-downloads");
+ if (aAddon.totalDownloads && aIsRemote) {
+ var downloads = aAddon.totalDownloads;
+ downloadsRow.value = downloads;
+ } else {
+ downloadsRow.value = null;
+ }
+
+ var canUpdate = !aIsRemote && hasPermission(aAddon, "upgrade");
+ document.getElementById("detail-updates-row").hidden = !canUpdate;
+
+ if ("applyBackgroundUpdates" in aAddon) {
+ this._autoUpdate.hidden = false;
+ this._autoUpdate.value = aAddon.applyBackgroundUpdates;
+ let hideFindUpdates = AddonManager.shouldAutoUpdate(this._addon);
+ document.getElementById("detail-findUpdates-btn").hidden = hideFindUpdates;
+ } else {
+ this._autoUpdate.hidden = true;
+ document.getElementById("detail-findUpdates-btn").hidden = false;
+ }
+
+ document.getElementById("detail-prefs-btn").hidden = !aIsRemote &&
+ !gViewController.commands.cmd_showItemPreferences.isEnabled(aAddon);
+
+ var gridRows = document.querySelectorAll("#detail-grid rows row");
+ let first = true;
+ for (let gridRow of gridRows) {
+ if (first && window.getComputedStyle(gridRow, null).getPropertyValue("display") != "none") {
+ gridRow.setAttribute("first-row", true);
+ first = false;
+ } else {
+ gridRow.removeAttribute("first-row");
+ }
+ }
+
+ if (this._addon.type == "experiment") {
+ let prefix = "details.experiment.";
+ let active = this._addon.isActive;
+
+ let stateKey = prefix + "state." + (active ? "active" : "complete");
+ let node = document.getElementById("detail-experiment-state");
+ node.value = gStrings.ext.GetStringFromName(stateKey);
+
+ let now = Date.now();
+ let end = getExperimentEndDate(this._addon);
+ let days = Math.abs(end - now) / (24 * 60 * 60 * 1000);
+
+ let timeKey = prefix + "time.";
+ let timeMessage;
+ if (days < 1) {
+ timeKey += (active ? "endsToday" : "endedToday");
+ timeMessage = gStrings.ext.GetStringFromName(timeKey);
+ } else {
+ timeKey += (active ? "daysRemaining" : "daysPassed");
+ days = Math.round(days);
+ let timeString = gStrings.ext.GetStringFromName(timeKey);
+ timeMessage = PluralForm.get(days, timeString)
+ .replace("#1", days);
+ }
+
+ document.getElementById("detail-experiment-time").value = timeMessage;
+ }
+
+ this.fillSettingsRows(aScrollToPreferences, (function updateView_fillSettingsRows() {
+ this.updateState();
+ gViewController.notifyViewChanged();
+ }).bind(this));
+ },
+
+ show: function gDetailView_show(aAddonId, aRequest) {
+ let index = aAddonId.indexOf("/preferences");
+ let scrollToPreferences = false;
+ if (index >= 0) {
+ aAddonId = aAddonId.substring(0, index);
+ scrollToPreferences = true;
+ }
+
+ var self = this;
+ this._loadingTimer = setTimeout(function loadTimeOutTimer() {
+ self.node.setAttribute("loading-extended", true);
+ }, LOADING_MSG_DELAY);
+
+ var view = gViewController.currentViewId;
+
+ AddonManager.getAddonByID(aAddonId, function show_getAddonByID(aAddon) {
+ if (gViewController && aRequest != gViewController.currentViewRequest)
+ return;
+
+ if (aAddon) {
+ self._updateView(aAddon, false, scrollToPreferences);
+ return;
+ }
+
+ // Look for an add-on pending install
+ AddonManager.getAllInstalls(function show_getAllInstalls(aInstalls) {
+ for (let install of aInstalls) {
+ if (install.state == AddonManager.STATE_INSTALLED &&
+ install.addon.id == aAddonId) {
+ self._updateView(install.addon, false);
+ return;
+ }
+ }
+
+ if (aAddonId in gCachedAddons) {
+ self._updateView(gCachedAddons[aAddonId], true);
+ return;
+ }
+
+ // This might happen due to session restore restoring us back to an
+ // add-on that doesn't exist but otherwise shouldn't normally happen.
+ // Either way just revert to the default view.
+ gViewController.replaceView(gViewDefault);
+ });
+ });
+ },
+
+ hide: function gDetailView_hide() {
+ AddonManager.removeManagerListener(this);
+ this.clearLoading();
+ if (this._addon) {
+ if (hasInlineOptions(this._addon)) {
+ Services.obs.notifyObservers(document,
+ AddonManager.OPTIONS_NOTIFICATION_HIDDEN,
+ this._addon.id);
+ }
+
+ gEventManager.unregisterAddonListener(this, this._addon.id);
+ gEventManager.unregisterInstallListener(this);
+ this._addon = null;
+
+ // Flush the preferences to disk so they survive any crash
+ if (this.node.getElementsByTagName("setting").length)
+ Services.prefs.savePrefFile(null);
+ }
+ },
+
+ updateState: function gDetailView_updateState() {
+ gViewController.updateCommands();
+
+ var pending = this._addon.pendingOperations;
+ if (pending != AddonManager.PENDING_NONE) {
+ this.node.removeAttribute("notification");
+
+ var pending = null;
+ const PENDING_OPERATIONS = ["enable", "disable", "install", "uninstall",
+ "upgrade"];
+ for (let op of PENDING_OPERATIONS) {
+ if (isPending(this._addon, op))
+ pending = op;
+ }
+
+ this.node.setAttribute("pending", pending);
+ document.getElementById("detail-pending").textContent = gStrings.ext.formatStringFromName(
+ "details.notification." + pending,
+ [this._addon.name, gStrings.brandShortName], 2
+ );
+ } else {
+ this.node.removeAttribute("pending");
+
+ if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ this.node.setAttribute("notification", "error");
+ document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
+ "details.notification.blocked",
+ [this._addon.name], 1
+ );
+ var errorLink = document.getElementById("detail-error-link");
+ errorLink.value = gStrings.ext.GetStringFromName("details.notification.blocked.link");
+ errorLink.href = this._addon.blocklistURL;
+ errorLink.hidden = false;
+ } else if (!this._addon.isCompatible && (AddonManager.checkCompatibility ||
+ (this._addon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) {
+ this.node.setAttribute("notification", "warning");
+ document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
+ "details.notification.incompatible",
+ [this._addon.name, gStrings.brandShortName, gStrings.appVersion], 3
+ );
+ document.getElementById("detail-warning-link").hidden = true;
+ } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) {
+ this.node.setAttribute("notification", "warning");
+ document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
+ "details.notification.softblocked",
+ [this._addon.name], 1
+ );
+ var warningLink = document.getElementById("detail-warning-link");
+ warningLink.value = gStrings.ext.GetStringFromName("details.notification.softblocked.link");
+ warningLink.href = this._addon.blocklistURL;
+ warningLink.hidden = false;
+ } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
+ this.node.setAttribute("notification", "warning");
+ document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
+ "details.notification.outdated",
+ [this._addon.name], 1
+ );
+ var warningLink = document.getElementById("detail-warning-link");
+ warningLink.value = gStrings.ext.GetStringFromName("details.notification.outdated.link");
+ warningLink.href = Services.urlFormatter.formatURLPref("plugins.update.url");
+ warningLink.hidden = false;
+ } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
+ this.node.setAttribute("notification", "error");
+ document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
+ "details.notification.vulnerableUpdatable",
+ [this._addon.name], 1
+ );
+ var errorLink = document.getElementById("detail-error-link");
+ errorLink.value = gStrings.ext.GetStringFromName("details.notification.vulnerableUpdatable.link");
+ errorLink.href = this._addon.blocklistURL;
+ errorLink.hidden = false;
+ } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
+ this.node.setAttribute("notification", "error");
+ document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
+ "details.notification.vulnerableNoUpdate",
+ [this._addon.name], 1
+ );
+ var errorLink = document.getElementById("detail-error-link");
+ errorLink.value = gStrings.ext.GetStringFromName("details.notification.vulnerableNoUpdate.link");
+ errorLink.href = this._addon.blocklistURL;
+ errorLink.hidden = false;
+ } else if (this._addon.isGMPlugin && !this._addon.isInstalled &&
+ this._addon.isActive) {
+ this.node.setAttribute("notification", "warning");
+ let warning = document.getElementById("detail-warning");
+ warning.textContent =
+ gStrings.ext.formatStringFromName("details.notification.gmpPending",
+ [this._addon.name], 1);
+ } else {
+ this.node.removeAttribute("notification");
+ }
+ }
+
+ let menulist = document.getElementById("detail-state-menulist");
+ let addonType = AddonManager.addonTypes[this._addon.type];
+ if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) {
+ let askItem = document.getElementById("detail-ask-to-activate-menuitem");
+ let alwaysItem = document.getElementById("detail-always-activate-menuitem");
+ let neverItem = document.getElementById("detail-never-activate-menuitem");
+ let hasActivatePermission =
+ ["ask_to_activate", "enable", "disable"].some(perm => hasPermission(this._addon, perm));
+
+ if (!this._addon.isActive) {
+ menulist.selectedItem = neverItem;
+ } else if (this._addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE) {
+ menulist.selectedItem = askItem;
+ } else {
+ menulist.selectedItem = alwaysItem;
+ }
+
+ menulist.disabled = !hasActivatePermission;
+ menulist.hidden = false;
+ menulist.classList.add('no-auto-hide');
+ } else {
+ menulist.hidden = true;
+ }
+
+ this.node.setAttribute("active", this._addon.isActive);
+ },
+
+ clearLoading: function gDetailView_clearLoading() {
+ if (this._loadingTimer) {
+ clearTimeout(this._loadingTimer);
+ this._loadingTimer = null;
+ }
+
+ this.node.removeAttribute("loading-extended");
+ },
+
+ emptySettingsRows: function gDetailView_emptySettingsRows() {
+ var lastRow = document.getElementById("detail-downloads");
+ var rows = lastRow.parentNode;
+ while (lastRow.nextSibling)
+ rows.removeChild(rows.lastChild);
+ },
+
+ fillSettingsRows: function gDetailView_fillSettingsRows(aScrollToPreferences, aCallback) {
+ this.emptySettingsRows();
+ if (!hasInlineOptions(this._addon)) {
+ if (aCallback)
+ aCallback();
+ return;
+ }
+
+ // This function removes and returns the text content of aNode without
+ // removing any child elements. Removing the text nodes ensures any XBL
+ // bindings apply properly.
+ function stripTextNodes(aNode) {
+ var text = '';
+ for (var i = 0; i < aNode.childNodes.length; i++) {
+ if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
+ text += aNode.childNodes[i].textContent;
+ aNode.removeChild(aNode.childNodes[i--]);
+ } else {
+ text += stripTextNodes(aNode.childNodes[i]);
+ }
+ }
+ return text;
+ }
+
+ var rows = document.getElementById("detail-downloads").parentNode;
+
+ try {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", this._addon.optionsURL, true);
+ xhr.responseType = "xml";
+ xhr.onload = (function fillSettingsRows_onload() {
+ var xml = xhr.responseXML;
+ var settings = xml.querySelectorAll(":root > setting");
+
+ var firstSetting = null;
+ for (var setting of settings) {
+
+ var desc = stripTextNodes(setting).trim();
+ if (!setting.hasAttribute("desc"))
+ setting.setAttribute("desc", desc);
+
+ var type = setting.getAttribute("type");
+ if (type == "file" || type == "directory")
+ setting.setAttribute("fullpath", "true");
+
+ setting = document.importNode(setting, true);
+ var style = setting.getAttribute("style");
+ if (style) {
+ setting.removeAttribute("style");
+ setting.setAttribute("style", style);
+ }
+
+ rows.appendChild(setting);
+ var visible = window.getComputedStyle(setting, null).getPropertyValue("display") != "none";
+ if (!firstSetting && visible) {
+ setting.setAttribute("first-row", true);
+ firstSetting = setting;
+ }
+ }
+
+ // Ensure the page has loaded and force the XBL bindings to be synchronously applied,
+ // then notify observers.
+ if (gViewController.viewPort.selectedPanel.hasAttribute("loading")) {
+ gDetailView.node.addEventListener("ViewChanged", function viewChangedEventListener() {
+ gDetailView.node.removeEventListener("ViewChanged", viewChangedEventListener, false);
+ if (firstSetting)
+ firstSetting.clientTop;
+ Services.obs.notifyObservers(document,
+ AddonManager.OPTIONS_NOTIFICATION_DISPLAYED,
+ gDetailView._addon.id);
+ if (aScrollToPreferences)
+ gDetailView.scrollToPreferencesRows();
+ }, false);
+ } else {
+ if (firstSetting)
+ firstSetting.clientTop;
+ Services.obs.notifyObservers(document,
+ AddonManager.OPTIONS_NOTIFICATION_DISPLAYED,
+ this._addon.id);
+ if (aScrollToPreferences)
+ gDetailView.scrollToPreferencesRows();
+ }
+ if (aCallback)
+ aCallback();
+ }).bind(this);
+ xhr.onerror = function fillSettingsRows_onerror(aEvent) {
+ Cu.reportError("Error " + aEvent.target.status +
+ " occurred while receiving " + this._addon.optionsURL);
+ if (aCallback)
+ aCallback();
+ };
+ xhr.send();
+ } catch(e) {
+ Cu.reportError(e);
+ if (aCallback)
+ aCallback();
+ }
+ },
+
+ scrollToPreferencesRows: function gDetailView_scrollToPreferencesRows() {
+ // We find this row, rather than remembering it from above,
+ // in case it has been changed by the observers.
+ let firstRow = gDetailView.node.querySelector('setting[first-row="true"]');
+ if (firstRow) {
+ let top = firstRow.boxObject.y;
+ top -= parseInt(window.getComputedStyle(firstRow, null).getPropertyValue("margin-top"));
+
+ let detailViewBoxObject = gDetailView.node.boxObject;
+ top -= detailViewBoxObject.y;
+
+ detailViewBoxObject.scrollTo(0, top);
+ }
+ },
+
+ getSelectedAddon: function gDetailView_getSelectedAddon() {
+ return this._addon;
+ },
+
+ onEnabling: function gDetailView_onEnabling() {
+ this.updateState();
+ },
+
+ onEnabled: function gDetailView_onEnabled() {
+ this.updateState();
+ this.fillSettingsRows();
+ },
+
+ onDisabling: function gDetailView_onDisabling(aNeedsRestart) {
+ this.updateState();
+ if (!aNeedsRestart && hasInlineOptions(this._addon)) {
+ Services.obs.notifyObservers(document,
+ AddonManager.OPTIONS_NOTIFICATION_HIDDEN,
+ this._addon.id);
+ }
+ },
+
+ onDisabled: function gDetailView_onDisabled() {
+ this.updateState();
+ this.emptySettingsRows();
+ },
+
+ onUninstalling: function gDetailView_onUninstalling() {
+ this.updateState();
+ },
+
+ onUninstalled: function gDetailView_onUninstalled() {
+ gViewController.popState();
+ },
+
+ onOperationCancelled: function gDetailView_onOperationCancelled() {
+ this.updateState();
+ },
+
+ onPropertyChanged: function gDetailView_onPropertyChanged(aProperties) {
+ if (aProperties.indexOf("applyBackgroundUpdates") != -1) {
+ this._autoUpdate.value = this._addon.applyBackgroundUpdates;
+ let hideFindUpdates = AddonManager.shouldAutoUpdate(this._addon);
+ document.getElementById("detail-findUpdates-btn").hidden = hideFindUpdates;
+ }
+
+ if (aProperties.indexOf("appDisabled") != -1 ||
+ aProperties.indexOf("userDisabled") != -1)
+ this.updateState();
+ },
+
+ onExternalInstall: function gDetailView_onExternalInstall(aAddon, aExistingAddon, aNeedsRestart) {
+ // Only care about upgrades for the currently displayed add-on
+ if (!aExistingAddon || aExistingAddon.id != this._addon.id)
+ return;
+
+ if (!aNeedsRestart)
+ this._updateView(aAddon, false);
+ else
+ this.updateState();
+ },
+
+ onInstallCancelled: function gDetailView_onInstallCancelled(aInstall) {
+ if (aInstall.addon.id == this._addon.id)
+ gViewController.popState();
+ }
+};
+
+
+var gUpdatesView = {
+ node: null,
+ _listBox: null,
+ _emptyNotice: null,
+ _sorters: null,
+ _updateSelected: null,
+ _categoryItem: null,
+
+ initialize: function gUpdatesView_initialize() {
+ this.node = document.getElementById("updates-view");
+ this._listBox = document.getElementById("updates-list");
+ this._emptyNotice = document.getElementById("updates-list-empty");
+ this._sorters = document.getElementById("updates-sorters");
+ this._sorters.handler = this;
+
+ this._categoryItem = gCategories.get("addons://updates/available");
+
+ this._updateSelected = document.getElementById("update-selected-btn");
+ this._updateSelected.addEventListener("command", function updateSelected_onCommand() {
+ gUpdatesView.installSelected();
+ }, false);
+
+ this.updateAvailableCount(true);
+
+ AddonManager.addAddonListener(this);
+ AddonManager.addInstallListener(this);
+ },
+
+ shutdown: function gUpdatesView_shutdown() {
+ AddonManager.removeAddonListener(this);
+ AddonManager.removeInstallListener(this);
+ },
+
+ show: function gUpdatesView_show(aType, aRequest) {
+ document.getElementById("empty-availableUpdates-msg").hidden = aType != "available";
+ document.getElementById("empty-recentUpdates-msg").hidden = aType != "recent";
+ this.showEmptyNotice(false);
+
+ while (this._listBox.itemCount > 0)
+ this._listBox.removeItemAt(0);
+
+ this.node.setAttribute("updatetype", aType);
+ if (aType == "recent")
+ this._showRecentUpdates(aRequest);
+ else
+ this._showAvailableUpdates(false, aRequest);
+ },
+
+ hide: function gUpdatesView_hide() {
+ this._updateSelected.hidden = true;
+ this._categoryItem.disabled = this._categoryItem.badgeCount == 0;
+ doPendingUninstalls(this._listBox);
+ },
+
+ _showRecentUpdates: function gUpdatesView_showRecentUpdates(aRequest) {
+ var self = this;
+ AddonManager.getAllAddons(function showRecentUpdates_getAllAddons(aAddonsList) {
+ if (gViewController && aRequest != gViewController.currentViewRequest)
+ return;
+
+ var elements = [];
+ let threshold = Date.now() - UPDATES_RECENT_TIMESPAN;
+ for (let addon of aAddonsList) {
+ if (!addon.updateDate || addon.updateDate.getTime() < threshold)
+ continue;
+
+ elements.push(createItem(addon));
+ }
+
+ self.showEmptyNotice(elements.length == 0);
+ if (elements.length > 0) {
+ sortElements(elements, [self._sorters.sortBy], self._sorters.ascending);
+ for (let element of elements)
+ self._listBox.appendChild(element);
+ }
+
+ gViewController.notifyViewChanged();
+ });
+ },
+
+ _showAvailableUpdates: function gUpdatesView_showAvailableUpdates(aIsRefresh, aRequest) {
+ /* Disable the Update Selected button so it can't get clicked
+ before everything is initialized asynchronously.
+ It will get re-enabled by maybeDisableUpdateSelected(). */
+ this._updateSelected.disabled = true;
+
+ var self = this;
+ AddonManager.getAllInstalls(function showAvailableUpdates_getAllInstalls(aInstallsList) {
+ if (!aIsRefresh && gViewController && aRequest &&
+ aRequest != gViewController.currentViewRequest)
+ return;
+
+ if (aIsRefresh) {
+ self.showEmptyNotice(false);
+ self._updateSelected.hidden = true;
+
+ while (self._listBox.itemCount > 0)
+ self._listBox.removeItemAt(0);
+ }
+
+ var elements = [];
+
+ for (let install of aInstallsList) {
+ if (!self.isManualUpdate(install))
+ continue;
+
+ let item = createItem(install.existingAddon);
+ item.setAttribute("upgrade", true);
+ item.addEventListener("IncludeUpdateChanged", function item_onIncludeUpdateChanged() {
+ self.maybeDisableUpdateSelected();
+ }, false);
+ elements.push(item);
+ }
+
+ self.showEmptyNotice(elements.length == 0);
+ if (elements.length > 0) {
+ self._updateSelected.hidden = false;
+ sortElements(elements, [self._sorters.sortBy], self._sorters.ascending);
+ for (let element of elements)
+ self._listBox.appendChild(element);
+ }
+
+ // ensure badge count is in sync
+ self._categoryItem.badgeCount = self._listBox.itemCount;
+
+ gViewController.notifyViewChanged();
+ });
+ },
+
+ showEmptyNotice: function gUpdatesView_showEmptyNotice(aShow) {
+ this._emptyNotice.hidden = !aShow;
+ },
+
+ isManualUpdate: function gUpdatesView_isManualUpdate(aInstall, aOnlyAvailable) {
+ var isManual = aInstall.existingAddon &&
+ !AddonManager.shouldAutoUpdate(aInstall.existingAddon);
+ if (isManual && aOnlyAvailable)
+ return isInState(aInstall, "available");
+ return isManual;
+ },
+
+ maybeRefresh: function gUpdatesView_maybeRefresh() {
+ if (gViewController.currentViewId == "addons://updates/available")
+ this._showAvailableUpdates(true);
+ this.updateAvailableCount();
+ },
+
+ updateAvailableCount: function gUpdatesView_updateAvailableCount(aInitializing) {
+ if (aInitializing)
+ gPendingInitializations++;
+ var self = this;
+ AddonManager.getAllInstalls(function updateAvailableCount_getAllInstalls(aInstallsList) {
+ var count = aInstallsList.filter(function installListFilter(aInstall) {
+ return self.isManualUpdate(aInstall, true);
+ }).length;
+ self._categoryItem.disabled = gViewController.currentViewId != "addons://updates/available" &&
+ count == 0;
+ self._categoryItem.badgeCount = count;
+ if (aInitializing)
+ notifyInitialized();
+ });
+ },
+
+ maybeDisableUpdateSelected: function gUpdatesView_maybeDisableUpdateSelected() {
+ for (let item of this._listBox.childNodes) {
+ if (item.includeUpdate) {
+ this._updateSelected.disabled = false;
+ return;
+ }
+ }
+ this._updateSelected.disabled = true;
+ },
+
+ installSelected: function gUpdatesView_installSelected() {
+ for (let item of this._listBox.childNodes) {
+ if (item.includeUpdate)
+ item.upgrade();
+ }
+
+ this._updateSelected.disabled = true;
+ },
+
+ getSelectedAddon: function gUpdatesView_getSelectedAddon() {
+ var item = this._listBox.selectedItem;
+ if (item)
+ return item.mAddon;
+ return null;
+ },
+
+ getListItemForID: function gUpdatesView_getListItemForID(aId) {
+ var listitem = this._listBox.firstChild;
+ while (listitem) {
+ if (listitem.mAddon.id == aId)
+ return listitem;
+ listitem = listitem.nextSibling;
+ }
+ return null;
+ },
+
+ onSortChanged: function gUpdatesView_onSortChanged(aSortBy, aAscending) {
+ sortList(this._listBox, aSortBy, aAscending);
+ },
+
+ onNewInstall: function gUpdatesView_onNewInstall(aInstall) {
+ if (!this.isManualUpdate(aInstall))
+ return;
+ this.maybeRefresh();
+ },
+
+ onInstallStarted: function gUpdatesView_onInstallStarted(aInstall) {
+ this.updateAvailableCount();
+ },
+
+ onInstallCancelled: function gUpdatesView_onInstallCancelled(aInstall) {
+ if (!this.isManualUpdate(aInstall))
+ return;
+ this.maybeRefresh();
+ },
+
+ onPropertyChanged: function gUpdatesView_onPropertyChanged(aAddon, aProperties) {
+ if (aProperties.indexOf("applyBackgroundUpdates") != -1)
+ this.updateAvailableCount();
+ }
+};
+
+function debuggingPrefChanged() {
+ gViewController.updateState();
+ gViewController.updateCommands();
+ gViewController.notifyViewChanged();
+}
+
+var gDragDrop = {
+ onDragOver: function gDragDrop_onDragOver(aEvent) {
+ var types = aEvent.dataTransfer.types;
+ if (types.contains("text/uri-list") ||
+ types.contains("text/x-moz-url") ||
+ types.contains("application/x-moz-file"))
+ aEvent.preventDefault();
+ },
+
+ onDrop: function gDragDrop_onDrop(aEvent) {
+ var dataTransfer = aEvent.dataTransfer;
+ var urls = [];
+
+ // Convert every dropped item into a url
+ for (var i = 0; i < dataTransfer.mozItemCount; i++) {
+ var url = dataTransfer.mozGetDataAt("text/uri-list", i);
+ if (url) {
+ urls.push(url);
+ continue;
+ }
+
+ url = dataTransfer.mozGetDataAt("text/x-moz-url", i);
+ if (url) {
+ urls.push(url.split("\n")[0]);
+ continue;
+ }
+
+ var file = dataTransfer.mozGetDataAt("application/x-moz-file", i);
+ if (file) {
+ urls.push(Services.io.newFileURI(file).spec);
+ continue;
+ }
+ }
+
+ var pos = 0;
+ var installs = [];
+
+ function buildNextInstall() {
+ if (pos == urls.length) {
+ if (installs.length > 0) {
+ // Display the normal install confirmation for the installs
+ let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"].
+ getService(Ci.amIWebInstallListener);
+ webInstaller.onWebInstallRequested(getBrowserElement(),
+ document.documentURIObject,
+ installs, installs.length);
+ }
+ return;
+ }
+
+ AddonManager.getInstallForURL(urls[pos++], function onDrop_getInstallForURL(aInstall) {
+ installs.push(aInstall);
+ buildNextInstall();
+ }, "application/x-xpinstall");
+ }
+
+ buildNextInstall();
+
+ aEvent.preventDefault();
+ }
+};
diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml
new file mode 100644
index 000000000..9c15902b5
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -0,0 +1,2108 @@
+<?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 page [
+<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
+%extensionsDTD;
+<!ENTITY % aboutDTD SYSTEM "chrome://mozapps/locale/extensions/about.dtd">
+%aboutDTD;
+]>
+
+<bindings id="addonBindings"
+ 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">
+
+
+ <!-- Rating - displays current/average rating, allows setting user rating -->
+ <binding id="rating">
+ <content>
+ <xul:image class="star"
+ onmouseover="document.getBindingParent(this)._hover(1);"
+ onclick="document.getBindingParent(this).userRating = 1;"/>
+ <xul:image class="star"
+ onmouseover="document.getBindingParent(this)._hover(2);"
+ onclick="document.getBindingParent(this).userRating = 2;"/>
+ <xul:image class="star"
+ onmouseover="document.getBindingParent(this)._hover(3);"
+ onclick="document.getBindingParent(this).userRating = 3;"/>
+ <xul:image class="star"
+ onmouseover="document.getBindingParent(this)._hover(4);"
+ onclick="document.getBindingParent(this).userRating = 4;"/>
+ <xul:image class="star"
+ onmouseover="document.getBindingParent(this)._hover(5);"
+ onclick="document.getBindingParent(this).userRating = 5;"/>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ this._updateStars();
+ ]]></constructor>
+
+ <property name="stars" readonly="true">
+ <getter><![CDATA[
+ return document.getAnonymousNodes(this);
+ ]]></getter>
+ </property>
+
+ <property name="averageRating">
+ <getter><![CDATA[
+ if (this.hasAttribute("averagerating"))
+ return this.getAttribute("averagerating");
+ return -1;
+ ]]></getter>
+ <setter><![CDATA[
+ this.setAttribute("averagerating", val);
+ if (this.showRating == "average")
+ this._updateStars();
+ ]]></setter>
+ </property>
+
+ <property name="userRating">
+ <getter><![CDATA[
+ if (this.hasAttribute("userrating"))
+ return this.getAttribute("userrating");
+ return -1;
+ ]]></getter>
+ <setter><![CDATA[
+ if (this.showRating != "user")
+ return;
+ this.setAttribute("userrating", val);
+ if (this.showRating == "user")
+ this._updateStars();
+ ]]></setter>
+ </property>
+
+ <property name="showRating">
+ <getter><![CDATA[
+ if (this.hasAttribute("showrating"))
+ return this.getAttribute("showrating");
+ return "average";
+ ]]></getter>
+ <setter><![CDATA[
+ if (val != "average" || val != "user")
+ throw Components.Exception("Invalid value", Components.results.NS_ERROR_ILLEGAL_VALUE);
+ this.setAttribute("showrating", val);
+ this._updateStars();
+ ]]></setter>
+ </property>
+
+ <method name="_updateStars">
+ <body><![CDATA[
+ var stars = this.stars;
+ var rating = this[this.showRating + "Rating"];
+ // average ratings can be non-whole numbers, round them so they
+ // match to their closest star
+ rating = Math.round(rating);
+ for (let i = 0; i < stars.length; i++)
+ stars[i].setAttribute("on", rating > i);
+ ]]></body>
+ </method>
+
+ <method name="_hover">
+ <parameter name="aScore"/>
+ <body><![CDATA[
+ if (this.showRating != "user")
+ return;
+ var stars = this.stars;
+ for (let i = 0; i < stars.length; i++)
+ stars[i].setAttribute("on", i <= (aScore -1));
+ ]]></body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+ <handler event="mouseout">
+ this._updateStars();
+ </handler>
+ </handlers>
+ </binding>
+
+ <!-- Download progress - shows graphical progress of download and any
+ related status message. -->
+ <binding id="download-progress">
+ <content>
+ <xul:stack flex="1">
+ <xul:hbox flex="1">
+ <xul:hbox class="start-cap"/>
+ <xul:progressmeter anonid="progress" class="progress" flex="1"
+ min="0" max="100"/>
+ <xul:hbox class="end-cap"/>
+ </xul:hbox>
+ <xul:hbox class="status-container">
+ <xul:spacer flex="1"/>
+ <xul:label anonid="status" class="status"/>
+ <xul:spacer flex="1"/>
+ <xul:button anonid="cancel-btn" class="cancel"
+ tooltiptext="&progress.cancel.tooltip;"
+ oncommand="document.getBindingParent(this).cancel();"/>
+ </xul:hbox>
+ </xul:stack>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ var progress = 0;
+ if (this.hasAttribute("progress"))
+ progress = parseInt(this.getAttribute("progress"));
+ this.progress = progress;
+ ]]></constructor>
+
+ <field name="_progress">
+ document.getAnonymousElementByAttribute(this, "anonid", "progress");
+ </field>
+ <field name="_cancel">
+ document.getAnonymousElementByAttribute(this, "anonid", "cancel-btn");
+ </field>
+ <field name="_status">
+ document.getAnonymousElementByAttribute(this, "anonid", "status");
+ </field>
+
+ <property name="progress">
+ <getter><![CDATA[
+ return this._progress.value;
+ ]]></getter>
+ <setter><![CDATA[
+ this._progress.value = val;
+ if (val == this._progress.max)
+ this.setAttribute("complete", true);
+ else
+ this.removeAttribute("complete");
+ ]]></setter>
+ </property>
+
+ <property name="maxProgress">
+ <getter><![CDATA[
+ return this._progress.max;
+ ]]></getter>
+ <setter><![CDATA[
+ if (val == -1) {
+ this._progress.mode = "undetermined";
+ } else {
+ this._progress.mode = "determined";
+ this._progress.max = val;
+ }
+ this.setAttribute("mode", this._progress.mode);
+ ]]></setter>
+ </property>
+
+ <property name="status">
+ <getter><![CDATA[
+ return this._status.value;
+ ]]></getter>
+ <setter><![CDATA[
+ this._status.value = val;
+ ]]></setter>
+ </property>
+
+ <method name="cancel">
+ <body><![CDATA[
+ this.mInstall.cancel();
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Sorters - displays and controls the sort state of a list. -->
+ <binding id="sorters">
+ <content orient="horizontal">
+ <xul:button anonid="name-btn" class="sorter"
+ label="&sort.name.label;" tooltiptext="&sort.name.tooltip;"
+ oncommand="this.parentNode._handleChange('name');"/>
+ <xul:button anonid="date-btn" class="sorter"
+ label="&sort.dateUpdated.label;"
+ tooltiptext="&sort.dateUpdated.tooltip;"
+ oncommand="this.parentNode._handleChange('updateDate');"/>
+ <xul:button anonid="price-btn" class="sorter" hidden="true"
+ label="&sort.price.label;"
+ tooltiptext="&sort.price.tooltip;"
+ oncommand="this.parentNode._handleChange('purchaseAmount');"/>
+ <xul:button anonid="relevance-btn" class="sorter" hidden="true"
+ label="&sort.relevance.label;"
+ tooltiptext="&sort.relevance.tooltip;"
+ oncommand="this.parentNode._handleChange('relevancescore');"/>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ if (!this.hasAttribute("sortby"))
+ this.setAttribute("sortby", "name");
+
+ if (this.getAttribute("showrelevance") == "true")
+ this._btnRelevance.hidden = false;
+
+ if (this.getAttribute("showprice") == "true")
+ this._btnPrice.hidden = false;
+
+ this._refreshState();
+ ]]></constructor>
+
+ <field name="handler">null</field>
+ <field name="_btnName">
+ document.getAnonymousElementByAttribute(this, "anonid", "name-btn");
+ </field>
+ <field name="_btnDate">
+ document.getAnonymousElementByAttribute(this, "anonid", "date-btn");
+ </field>
+ <field name="_btnPrice">
+ document.getAnonymousElementByAttribute(this, "anonid", "price-btn");
+ </field>
+ <field name="_btnRelevance">
+ document.getAnonymousElementByAttribute(this, "anonid", "relevance-btn");
+ </field>
+
+ <property name="sortBy">
+ <getter><![CDATA[
+ return this.getAttribute("sortby");
+ ]]></getter>
+ <setter><![CDATA[
+ if (val != this.sortBy) {
+ this.setAttribute("sortBy", val);
+ this._refreshState();
+ }
+ ]]></setter>
+ </property>
+
+ <property name="ascending">
+ <getter><![CDATA[
+ return (this.getAttribute("ascending") == "true");
+ ]]></getter>
+ <setter><![CDATA[
+ val = !!val;
+ if (val != this.ascending) {
+ this.setAttribute("ascending", val);
+ this._refreshState();
+ }
+ ]]></setter>
+ </property>
+
+ <property name="showrelevance">
+ <getter><![CDATA[
+ return (this.getAttribute("showrelevance") == "true");
+ ]]></getter>
+ <setter><![CDATA[
+ val = !!val;
+ this.setAttribute("showrelevance", val);
+ this._btnRelevance.hidden = !val;
+ ]]></setter>
+ </property>
+
+ <property name="showprice">
+ <getter><![CDATA[
+ return (this.getAttribute("showprice") == "true");
+ ]]></getter>
+ <setter><![CDATA[
+ val = !!val;
+ this.setAttribute("showprice", val);
+ this._btnPrice.hidden = !val;
+ ]]></setter>
+ </property>
+
+ <method name="setSort">
+ <parameter name="aSort"/>
+ <parameter name="aAscending"/>
+ <body><![CDATA[
+ var sortChanged = false;
+ if (aSort != this.sortBy) {
+ this.setAttribute("sortby", aSort);
+ sortChanged = true;
+ }
+
+ aAscending = !!aAscending;
+ if (this.ascending != aAscending) {
+ this.setAttribute("ascending", aAscending);
+ sortChanged = true;
+ }
+
+ if (sortChanged)
+ this._refreshState();
+ ]]></body>
+ </method>
+
+ <method name="_handleChange">
+ <parameter name="aSort"/>
+ <body><![CDATA[
+ const ASCENDING_SORT_FIELDS = ["name", "purchaseAmount"];
+
+ // Toggle ascending if sort by is not changing, otherwise
+ // name sorting defaults to ascending, others to descending
+ if (aSort == this.sortBy)
+ this.ascending = !this.ascending;
+ else
+ this.setSort(aSort, ASCENDING_SORT_FIELDS.indexOf(aSort) >= 0);
+ ]]></body>
+ </method>
+
+ <method name="_refreshState">
+ <body><![CDATA[
+ var sortBy = this.sortBy;
+ var checkState = this.ascending ? 2 : 1;
+
+ if (sortBy == "name") {
+ this._btnName.checkState = checkState;
+ this._btnName.checked = true;
+ } else {
+ this._btnName.checkState = 0;
+ this._btnName.checked = false;
+ }
+
+ if (sortBy == "updateDate") {
+ this._btnDate.checkState = checkState;
+ this._btnDate.checked = true;
+ } else {
+ this._btnDate.checkState = 0;
+ this._btnDate.checked = false;
+ }
+
+ if (sortBy == "purchaseAmount") {
+ this._btnPrice.checkState = checkState;
+ this._btnPrice.checked = true;
+ } else {
+ this._btnPrice.checkState = 0;
+ this._btnPrice.checked = false;
+ }
+
+ if (sortBy == "relevancescore") {
+ this._btnRelevance.checkState = checkState;
+ this._btnRelevance.checked = true;
+ } else {
+ this._btnRelevance.checkState = 0;
+ this._btnRelevance.checked = false;
+ }
+
+ if (this.handler && "onSortChanged" in this.handler)
+ this.handler.onSortChanged(sortBy, this.ascending);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Categories list - displays the list of categories on the left pane. -->
+ <binding id="categories-list"
+ extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
+ <implementation>
+ <!-- This needs to be overridden to allow the fancy animation while not
+ allowing that item to be selected when hiding. -->
+ <method name="_canUserSelect">
+ <parameter name="aItem"/>
+ <body>
+ <![CDATA[
+ if (aItem.hasAttribute("disabled") &&
+ aItem.getAttribute("disabled") == "true")
+ return false;
+ var style = document.defaultView.getComputedStyle(aItem, "");
+ return style.display != "none" && style.visibility == "visible";
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Category item - an item in the category list. -->
+ <binding id="category"
+ extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <content align="center">
+ <xul:image anonid="icon" class="category-icon"/>
+ <xul:label anonid="name" class="category-name" flex="1" xbl:inherits="value=name"/>
+ <xul:label anonid="badge" class="category-badge" xbl:inherits="value=count"/>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ if (!this.hasAttribute("count"))
+ this.setAttribute("count", 0);
+ ]]></constructor>
+
+ <property name="badgeCount">
+ <getter><![CDATA[
+ return this.getAttribute("count");
+ ]]></getter>
+ <setter><![CDATA[
+ if (this.getAttribute("count") == val)
+ return;
+
+ this.setAttribute("count", val);
+ var event = document.createEvent("Events");
+ event.initEvent("CategoryBadgeUpdated", true, true);
+ this.dispatchEvent(event);
+ ]]></setter>
+ </property>
+ </implementation>
+ </binding>
+
+
+ <!-- Creator link - Name of a user/developer, providing a link if relevant. -->
+ <binding id="creator-link">
+ <content>
+ <xul:label anonid="label" value="&addon.createdBy.label;"/>
+ <xul:label anonid="creator-link" class="creator-link text-link"/>
+ <xul:label anonid="creator-name" class="creator-name"/>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ if (this.hasAttribute("nameonly") &&
+ this.getAttribute("nameonly") == "true") {
+ this._label.hidden = true;
+ }
+ ]]></constructor>
+
+ <field name="_label">
+ document.getAnonymousElementByAttribute(this, "anonid", "label");
+ </field>
+ <field name="_creatorLink">
+ document.getAnonymousElementByAttribute(this, "anonid", "creator-link");
+ </field>
+ <field name="_creatorName">
+ document.getAnonymousElementByAttribute(this, "anonid", "creator-name");
+ </field>
+
+ <method name="setCreator">
+ <parameter name="aCreator"/>
+ <parameter name="aHomepageURL"/>
+ <body><![CDATA[
+ if (!aCreator) {
+ this.collapsed = true;
+ return;
+ }
+ this.collapsed = false;
+ var url = aCreator.url || aHomepageURL;
+ var showLink = !!url;
+ if (showLink) {
+ this._creatorLink.value = aCreator.name;
+ this._creatorLink.href = url;
+ } else {
+ this._creatorName.value = aCreator.name;
+ }
+ this._creatorLink.hidden = !showLink;
+ this._creatorName.hidden = showLink;
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Translators list - Names of a translators of Language Pack. -->
+ <binding id="translators-list">
+ <content>
+ <xul:label anonid="label" value="&translators.label;" class="sectionTitle"/>
+ <xul:vbox flex="1" anonid="translatorsBox" class="boxIndent"/>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ if (this.hasAttribute("nameonly") &&
+ this.getAttribute("nameonly") == "true") {
+ this._label.hidden = true;
+ }
+ ]]></constructor>
+
+ <field name="_label">
+ document.getAnonymousElementByAttribute(this, "anonid", "label");
+ </field>
+ <field name="_translatorsBox">
+ document.getAnonymousElementByAttribute(this, "anonid", "translatorsBox");
+ </field>
+
+ <method name="setTranslators">
+ <parameter name="aTranslators"/>
+ <parameter name="aType"/>
+ <body><![CDATA[
+ if (aType != "locale" || !aTranslators || aTranslators.length == 0) {
+ this.collapsed = true;
+ return;
+ }
+ this.collapsed = false;
+ while (this._translatorsBox.firstChild) {
+ this._translatorsBox.removeChild(this._translatorsBox.firstChild);
+ }
+ for (let currentItem of aTranslators) {
+ var label = document.createElement("label");
+ label.textContent = currentItem;
+ label.setAttribute("class", "contributor");
+ this._translatorsBox.appendChild(label);
+ }
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Install status - Displays the status of an install/upgrade. -->
+ <binding id="install-status">
+ <content>
+ <xul:label anonid="message"/>
+ <xul:progressmeter anonid="progress" class="download-progress"/>
+ <xul:button anonid="purchase-remote-btn" hidden="true"
+ class="addon-control"
+ oncommand="document.getBindingParent(this).purchaseRemote();"/>
+ <xul:button anonid="install-remote-btn" hidden="true"
+ class="addon-control install" label="&addon.install.label;"
+ tooltiptext="&addon.install.tooltip;"
+ oncommand="document.getBindingParent(this).installRemote();"/>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ if (this.mInstall)
+ this.initWithInstall(this.mInstall);
+ else if (this.mControl.mAddon.install)
+ this.initWithInstall(this.mControl.mAddon.install);
+ else
+ this.refreshState();
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ if (this.mInstall)
+ this.mInstall.removeListener(this);
+ ]]></destructor>
+
+ <field name="_message">
+ document.getAnonymousElementByAttribute(this, "anonid", "message");
+ </field>
+ <field name="_progress">
+ document.getAnonymousElementByAttribute(this, "anonid", "progress");
+ </field>
+ <field name="_purchaseRemote">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "purchase-remote-btn");
+ </field>
+ <field name="_installRemote">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "install-remote-btn");
+ </field>
+ <field name="_restartNeeded">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "restart-needed");
+ </field>
+ <field name="_undo">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "undo-btn");
+ </field>
+
+ <method name="initWithInstall">
+ <parameter name="aInstall"/>
+ <body><![CDATA[
+ if (this.mInstall) {
+ this.mInstall.removeListener(this);
+ this.mInstall = null;
+ }
+ this.mInstall = aInstall;
+ this._progress.mInstall = aInstall;
+ this.refreshState();
+ this.mInstall.addListener(this);
+ ]]></body>
+ </method>
+
+ <method name="refreshState">
+ <body><![CDATA[
+ var showInstallRemote = false;
+ var showPurchase = false;
+
+ if (this.mInstall) {
+
+ switch (this.mInstall.state) {
+ case AddonManager.STATE_AVAILABLE:
+ if (this.mControl.getAttribute("remote") != "true")
+ break;
+
+ this._progress.hidden = true;
+ showInstallRemote = true;
+ break;
+ case AddonManager.STATE_DOWNLOADING:
+ this.showMessage("installDownloading");
+ break;
+ case AddonManager.STATE_CHECKING:
+ this.showMessage("installVerifying");
+ break;
+ case AddonManager.STATE_DOWNLOADED:
+ this.showMessage("installDownloaded");
+ break;
+ case AddonManager.STATE_DOWNLOAD_FAILED:
+ // XXXunf expose what error occured (bug 553487)
+ this.showMessage("installDownloadFailed", true);
+ break;
+ case AddonManager.STATE_INSTALLING:
+ this.showMessage("installInstalling");
+ break;
+ case AddonManager.STATE_INSTALL_FAILED:
+ // XXXunf expose what error occured (bug 553487)
+ this.showMessage("installFailed", true);
+ break;
+ case AddonManager.STATE_CANCELLED:
+ this.showMessage("installCancelled", true);
+ break;
+ }
+
+ } else if (this.mControl.mAddon.purchaseURL) {
+ this._progress.hidden = true;
+ showPurchase = true;
+ this._purchaseRemote.label =
+ gStrings.ext.formatStringFromName("addon.purchase.label",
+ [this.mControl.mAddon.purchaseDisplayAmount], 1);
+ this._purchaseRemote.tooltiptext =
+ gStrings.ext.GetStringFromName("addon.purchase.tooltip");
+ }
+
+ this._purchaseRemote.hidden = !showPurchase;
+ this._installRemote.hidden = !showInstallRemote;
+
+ if ("refreshInfo" in this.mControl)
+ this.mControl.refreshInfo();
+ ]]></body>
+ </method>
+
+ <method name="showMessage">
+ <parameter name="aMsgId"/>
+ <parameter name="aHideProgress"/>
+ <body><![CDATA[
+ this._message.setAttribute("hidden", !aHideProgress);
+ this._progress.setAttribute("hidden", !!aHideProgress);
+
+ var msg = gStrings.ext.GetStringFromName(aMsgId);
+ if (aHideProgress)
+ this._message.value = msg;
+ else
+ this._progress.status = msg;
+ ]]></body>
+ </method>
+
+ <method name="purchaseRemote">
+ <body><![CDATA[
+ openURL(this.mControl.mAddon.purchaseURL);
+ ]]></body>
+ </method>
+
+ <method name="installRemote">
+ <body><![CDATA[
+ if (this.mControl.getAttribute("remote") != "true")
+ return;
+
+ if (this.mControl.mAddon.eula) {
+ var data = {
+ addon: this.mControl.mAddon,
+ accepted: false
+ };
+ window.openDialog("chrome://mozapps/content/extensions/eula.xul", "_blank",
+ "chrome,dialog,modal,centerscreen,resizable=no", data);
+ if (!data.accepted)
+ return;
+ }
+
+ delete this.mControl.mAddon;
+ this.mControl.mInstall = this.mInstall;
+ this.mControl.setAttribute("status", "installing");
+ this.mInstall.install();
+ ]]></body>
+ </method>
+
+ <method name="undoAction">
+ <body><![CDATA[
+ if (!this.mAddon)
+ return;
+ var pending = this.mAddon.pendingOperations;
+ if (pending & AddonManager.PENDING_ENABLE)
+ this.mAddon.userDisabled = true;
+ else if (pending & AddonManager.PENDING_DISABLE)
+ this.mAddon.userDisabled = false;
+ this.refreshState();
+ ]]></body>
+ </method>
+
+ <method name="onDownloadStarted">
+ <body><![CDATA[
+ this.refreshState();
+ ]]></body>
+ </method>
+
+ <method name="onDownloadEnded">
+ <body><![CDATA[
+ this.refreshState();
+ ]]></body>
+ </method>
+
+ <method name="onDownloadFailed">
+ <body><![CDATA[
+ this.refreshState();
+ ]]></body>
+ </method>
+
+ <method name="onDownloadProgress">
+ <body><![CDATA[
+ this._progress.maxProgress = this.mInstall.maxProgress;
+ this._progress.progress = this.mInstall.progress;
+ ]]></body>
+ </method>
+
+ <method name="onInstallStarted">
+ <body><![CDATA[
+ this._progress.progress = 0;
+ this.refreshState();
+ ]]></body>
+ </method>
+
+ <method name="onInstallEnded">
+ <body><![CDATA[
+ this.refreshState();
+ if ("onInstallCompleted" in this.mControl)
+ this.mControl.onInstallCompleted();
+ ]]></body>
+ </method>
+
+ <method name="onInstallFailed">
+ <body><![CDATA[
+ this.refreshState();
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Addon - base - parent binding of any item representing an addon. -->
+ <binding id="addon-base"
+ extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <implementation>
+ <method name="hasPermission">
+ <parameter name="aPerm"/>
+ <body><![CDATA[
+ var perm = AddonManager["PERM_CAN_" + aPerm.toUpperCase()];
+ return !!(this.mAddon.permissions & perm);
+ ]]></body>
+ </method>
+
+ <method name="opRequiresRestart">
+ <parameter name="aOperation"/>
+ <body><![CDATA[
+ var operation = AddonManager["OP_NEEDS_RESTART_" + aOperation.toUpperCase()];
+ return !!(this.mAddon.operationsRequiringRestart & operation);
+ ]]></body>
+ </method>
+
+ <method name="typeHasFlag">
+ <parameter name="aFlag"/>
+ <body><![CDATA[
+ let flag = AddonManager["TYPE_" + aFlag];
+ let type = AddonManager.addonTypes[this.mAddon.type];
+
+ return !!(type.flags & flag);
+ ]]></body>
+ </method>
+
+ <method name="isPending">
+ <parameter name="aAction"/>
+ <body><![CDATA[
+ var action = AddonManager["PENDING_" + aAction.toUpperCase()];
+ return !!(this.mAddon.pendingOperations & action);
+ ]]></body>
+ </method>
+
+ <method name="onUninstalled">
+ <body><![CDATA[
+ this.parentNode.removeChild(this);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Addon - generic - A normal addon item, or an update to one -->
+ <binding id="addon-generic"
+ extends="chrome://mozapps/content/extensions/extensions.xml#addon-base">
+ <content>
+ <xul:hbox anonid="warning-container"
+ class="warning">
+ <xul:image class="warning-icon"/>
+ <xul:label anonid="warning" flex="1"/>
+ <xul:label anonid="warning-link" class="text-link"/>
+ <xul:button anonid="warning-btn" class="button-link"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </xul:hbox>
+ <xul:hbox anonid="error-container"
+ class="error">
+ <xul:image class="error-icon"/>
+ <xul:label anonid="error" flex="1"/>
+ <xul:label anonid="error-link" class="text-link"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </xul:hbox>
+ <xul:hbox anonid="pending-container"
+ class="pending">
+ <xul:image class="pending-icon"/>
+ <xul:label anonid="pending" flex="1"/>
+ <xul:button anonid="restart-btn" class="button-link"
+ label="&addon.restartNow.label;"
+ oncommand="document.getBindingParent(this).restart();"/>
+ <xul:button anonid="undo-btn" class="button-link"
+ label="&addon.undoAction.label;"
+ tooltipText="&addon.undoAction.tooltip;"
+ oncommand="document.getBindingParent(this).undo();"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </xul:hbox>
+
+ <xul:hbox class="content-container">
+ <xul:vbox class="icon-container">
+ <xul:image anonid="icon" class="icon"/>
+ </xul:vbox>
+ <xul:vbox class="content-inner-container" flex="1">
+ <xul:hbox class="basicinfo-container">
+ <xul:hbox class="name-container">
+ <xul:label anonid="name" class="name" crop="end" flex="1"
+ xbl:inherits="value=name,tooltiptext=name"/>
+ <xul:label anonid="version" class="version"/>
+ <xul:label class="nativeIndicator nativeAddon" value="●" tooltiptext="&addon.nativeAddon;"/>
+ <xul:label class="nativeIndicator compatAddon" value="●" tooltiptext="&addon.compatAddon;"/>
+ <xul:label class="disabled-postfix" value="&addon.disabled.postfix;"/>
+ <xul:label class="update-postfix" value="&addon.update.postfix;"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to make the name crop -->
+ </xul:hbox>
+ <xul:label anonid="date-updated" class="date-updated"
+ unknown="&addon.unknownDate;"/>
+ </xul:hbox>
+ <xul:hbox class="experiment-container">
+ <svg width="6" height="6" viewBox="0 0 6 6" version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ class="experiment-bullet-container">
+ <circle cx="3" cy="3" r="3" class="experiment-bullet"/>
+ </svg>
+ <xul:label anonid="experiment-state" class="experiment-state"/>
+ <xul:label anonid="experiment-time" class="experiment-time"/>
+ </xul:hbox>
+
+ <xul:hbox class="advancedinfo-container" flex="1">
+ <xul:vbox class="description-outer-container" flex="1">
+ <xul:hbox class="description-container">
+ <xul:label anonid="description" class="description" crop="end" flex="1"/>
+ <xul:button anonid="details-btn" class="details button-link"
+ label="&addon.details.label;"
+ tooltiptext="&addon.details.tooltip;"
+ oncommand="document.getBindingParent(this).showInDetailView();"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to make the description crop -->
+ </xul:hbox>
+ <xul:vbox anonid="relnotes-container" class="relnotes-container">
+ <xul:label class="relnotes-header" value="&addon.releaseNotes.label;"/>
+ <xul:label anonid="relnotes-loading" value="&addon.loadingReleaseNotes.label;"/>
+ <xul:label anonid="relnotes-error" hidden="true"
+ value="&addon.errorLoadingReleaseNotes.label;"/>
+ <xul:vbox anonid="relnotes" class="relnotes"/>
+ </xul:vbox>
+ <xul:hbox class="relnotes-toggle-container">
+ <xul:button anonid="relnotes-toggle-btn" class="relnotes-toggle"
+ hidden="true" label="&cmd.showReleaseNotes.label;"
+ tooltiptext="&cmd.showReleaseNotes.tooltip;"
+ showlabel="&cmd.showReleaseNotes.label;"
+ showtooltip="&cmd.showReleaseNotes.tooltip;"
+ hidelabel="&cmd.hideReleaseNotes.label;"
+ hidetooltip="&cmd.hideReleaseNotes.tooltip;"
+ oncommand="document.getBindingParent(this).toggleReleaseNotes();"/>
+ </xul:hbox>
+ </xul:vbox>
+ <xul:vbox class="status-control-wrapper">
+ <xul:hbox class="status-container">
+ <xul:hbox anonid="checking-update" hidden="true">
+ <xul:image class="spinner"/>
+ <xul:label value="&addon.checkingForUpdates.label;"/>
+ </xul:hbox>
+ <xul:vbox anonid="update-available" class="update-available"
+ hidden="true">
+ <xul:checkbox anonid="include-update" class="include-update"
+ label="&addon.includeUpdate.label;" checked="true"
+ oncommand="document.getBindingParent(this).onIncludeUpdateChanged();"/>
+ <xul:hbox class="update-info-container">
+ <xul:label class="update-available-notice"
+ value="&addon.updateAvailable.label;"/>
+ <xul:button anonid="update-btn" class="addon-control update"
+ label="&addon.updateNow.label;"
+ tooltiptext="&addon.updateNow.tooltip;"
+ oncommand="document.getBindingParent(this).upgrade();"/>
+ </xul:hbox>
+ </xul:vbox>
+ <xul:hbox anonid="install-status" class="install-status"
+ hidden="true"/>
+ </xul:hbox>
+ <xul:hbox anonid="control-container" class="control-container">
+ <xul:button anonid="preferences-btn"
+ class="addon-control preferences"
+#ifdef XP_WIN
+ label="&cmd.showPreferencesWin.label;"
+ tooltiptext="&cmd.showPreferencesWin.tooltip;"
+#else
+ label="&cmd.showPreferencesUnix.label;"
+ tooltiptext="&cmd.showPreferencesUnix.tooltip;"
+#endif
+ oncommand="document.getBindingParent(this).showPreferences();"/>
+ <!-- label="&cmd.debugAddon.label;" -->
+ <xul:button anonid="debug-btn" class="addon-control debug"
+ label="&cmd.debugAddon.label;"
+ oncommand="document.getBindingParent(this).debug();"/>
+
+ <xul:button anonid="enable-btn" class="addon-control enable"
+ label="&cmd.enableAddon.label;"
+ oncommand="document.getBindingParent(this).userDisabled = false;"/>
+ <xul:button anonid="disable-btn" class="addon-control disable"
+ label="&cmd.disableAddon.label;"
+ oncommand="document.getBindingParent(this).userDisabled = true;"/>
+ <xul:button anonid="remove-btn" class="addon-control remove"
+ label="&cmd.uninstallAddon.label;"
+ oncommand="document.getBindingParent(this).uninstall();"/>
+ <xul:menulist anonid="state-menulist"
+ class="addon-control state"
+ tooltiptext="&cmd.stateMenu.tooltip;">
+ <xul:menupopup>
+ <xul:menuitem anonid="ask-to-activate-menuitem"
+ class="addon-control"
+ label="&cmd.askToActivate.label;"
+ tooltiptext="&cmd.askToActivate.tooltip;"
+ oncommand="document.getBindingParent(this).userDisabled = AddonManager.STATE_ASK_TO_ACTIVATE;"/>
+ <xul:menuitem anonid="always-activate-menuitem"
+ class="addon-control"
+ label="&cmd.alwaysActivate.label;"
+ tooltiptext="&cmd.alwaysActivate.tooltip;"
+ oncommand="document.getBindingParent(this).userDisabled = false;"/>
+ <xul:menuitem anonid="never-activate-menuitem"
+ class="addon-control"
+ label="&cmd.neverActivate.label;"
+ tooltiptext="&cmd.neverActivate.tooltip;"
+ oncommand="document.getBindingParent(this).userDisabled = true;"/>
+ </xul:menupopup>
+ </xul:menulist>
+ </xul:hbox>
+ </xul:vbox>
+ </xul:hbox>
+ </xul:vbox>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ this._installStatus.mControl = this;
+
+ this.setAttribute("contextmenu", "addonitem-popup");
+
+ this._showStatus("none");
+
+ this._initWithAddon(this.mAddon);
+
+ gEventManager.registerAddonListener(this, this.mAddon.id);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ gEventManager.unregisterAddonListener(this, this.mAddon.id);
+ ]]></destructor>
+
+ <field name="_warningContainer">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "warning-container");
+ </field>
+ <field name="_warning">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "warning");
+ </field>
+ <field name="_warningLink">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "warning-link");
+ </field>
+ <field name="_warningBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "warning-btn");
+ </field>
+ <field name="_errorContainer">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "error-container");
+ </field>
+ <field name="_error">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "error");
+ </field>
+ <field name="_errorLink">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "error-link");
+ </field>
+ <field name="_pendingContainer">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "pending-container");
+ </field>
+ <field name="_pending">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "pending");
+ </field>
+ <field name="_infoContainer">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "info-container");
+ </field>
+ <field name="_info">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "info");
+ </field>
+ <field name="_version">
+ document.getAnonymousElementByAttribute(this, "anonid", "version");
+ </field>
+ <field name="_experimentState">
+ document.getAnonymousElementByAttribute(this, "anonid", "experiment-state");
+ </field>
+ <field name="_experimentTime">
+ document.getAnonymousElementByAttribute(this, "anonid", "experiment-time");
+ </field>
+ <field name="_icon">
+ document.getAnonymousElementByAttribute(this, "anonid", "icon");
+ </field>
+ <field name="_dateUpdated">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "date-updated");
+ </field>
+ <field name="_description">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "description");
+ </field>
+ <field name="_stateMenulist">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "state-menulist");
+ </field>
+ <field name="_askToActivateMenuitem">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "ask-to-activate-menuitem");
+ </field>
+ <field name="_alwaysActivateMenuitem">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "always-activate-menuitem");
+ </field>
+ <field name="_neverActivateMenuitem">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "never-activate-menuitem");
+ </field>
+ <field name="_preferencesBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "preferences-btn");
+ </field>
+ <field name="_enableBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "enable-btn");
+ </field>
+ <field name="_debugBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "debug-btn");
+ </field>
+ <field name="_disableBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "disable-btn");
+ </field>
+ <field name="_removeBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "remove-btn");
+ </field>
+ <field name="_updateBtn">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "update-btn");
+ </field>
+ <field name="_controlContainer">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "control-container");
+ </field>
+ <field name="_installStatus">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "install-status");
+ </field>
+ <field name="_checkingUpdate">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "checking-update");
+ </field>
+ <field name="_updateAvailable">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "update-available");
+ </field>
+ <field name="_includeUpdate">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "include-update");
+ </field>
+ <field name="_relNotesLoaded">false</field>
+ <field name="_relNotesToggle">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "relnotes-toggle-btn");
+ </field>
+ <field name="_relNotesLoading">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "relnotes-loading");
+ </field>
+ <field name="_relNotesError">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "relnotes-error");
+ </field>
+ <field name="_relNotesContainer">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "relnotes-container");
+ </field>
+ <field name="_relNotes">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "relnotes");
+ </field>
+
+ <property name="userDisabled">
+ <getter><![CDATA[
+ return this.mAddon.userDisabled;
+ ]]></getter>
+ <setter><![CDATA[
+ this.mAddon.userDisabled = val;
+ ]]></setter>
+ </property>
+
+ <property name="includeUpdate">
+ <getter><![CDATA[
+ return this._includeUpdate.checked && !!this.mManualUpdate;
+ ]]></getter>
+ <setter><![CDATA[
+ //XXXunf Eventually, we'll want to persist this for individual
+ // updates - see bug 594619.
+ this._includeUpdate.checked = !!val;
+ ]]></setter>
+ </property>
+
+ <method name="_initWithAddon">
+ <parameter name="aAddon"/>
+ <body><![CDATA[
+ this.mAddon = aAddon;
+
+ this._installStatus.mAddon = this.mAddon;
+ this._updateDates();
+ this._updateState();
+
+ this.setAttribute("name", aAddon.name);
+
+ var iconURL = this.mAddon.iconURL;
+ if (iconURL)
+ this._icon.src = iconURL;
+ else
+ this._icon.src = "";
+
+ if (shouldShowVersionNumber(this.mAddon))
+ this._version.value = this.mAddon.version;
+ else
+ this._version.hidden = true;
+
+ if (this.mAddon.description)
+ this._description.value = this.mAddon.description;
+ else
+ this._description.hidden = true;
+
+ if (!("applyBackgroundUpdates" in this.mAddon) ||
+ (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE ||
+ (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT &&
+ !AddonManager.autoUpdateDefault))) {
+ var self = this;
+ AddonManager.getAllInstalls(function(aInstallsList) {
+ // This can return after the binding has been destroyed,
+ // so try to detect that and return early
+ if (!("onNewInstall" in self))
+ return;
+ for (let install of aInstallsList) {
+ if (install.existingAddon &&
+ install.existingAddon.id == self.mAddon.id &&
+ install.state == AddonManager.STATE_AVAILABLE) {
+ self.onNewInstall(install);
+ self.onIncludeUpdateChanged();
+ }
+ }
+ });
+ }
+ ]]></body>
+ </method>
+
+ <method name="_showStatus">
+ <parameter name="aType"/>
+ <body><![CDATA[
+ this._controlContainer.hidden = aType != "none" &&
+ !(aType == "update-available" && !this.hasAttribute("upgrade"));
+
+ this._installStatus.hidden = aType != "progress";
+ if (aType == "progress")
+ this._installStatus.refreshState();
+ this._checkingUpdate.hidden = aType != "checking-update";
+ this._updateAvailable.hidden = aType != "update-available";
+ this._relNotesToggle.hidden = !(this.mManualUpdate ?
+ this.mManualUpdate.releaseNotesURI :
+ this.mAddon.releaseNotesURI);
+ ]]></body>
+ </method>
+
+ <method name="_updateDates">
+ <body><![CDATA[
+ function formatDate(aDate) {
+ return Cc["@mozilla.org/intl/scriptabledateformat;1"]
+ .getService(Ci.nsIScriptableDateFormat)
+ .FormatDate("",
+ Ci.nsIScriptableDateFormat.dateFormatLong,
+ aDate.getFullYear(),
+ aDate.getMonth() + 1,
+ aDate.getDate()
+ );
+ }
+
+ if (this.mAddon.updateDate)
+ this._dateUpdated.value = formatDate(this.mAddon.updateDate);
+ else
+ this._dateUpdated.value = this._dateUpdated.getAttribute("unknown");
+ ]]></body>
+ </method>
+
+ <method name="_updateState">
+ <body><![CDATA[
+ if (this.parentNode.selectedItem == this)
+ gViewController.updateCommands();
+
+ var pending = this.mAddon.pendingOperations;
+ if (pending != AddonManager.PENDING_NONE) {
+ this.removeAttribute("notification");
+
+ var pending = null;
+ const PENDING_OPERATIONS = ["enable", "disable", "install",
+ "uninstall", "upgrade"];
+ for (let op of PENDING_OPERATIONS) {
+ if (this.isPending(op))
+ pending = op;
+ }
+
+ this.setAttribute("pending", pending);
+ this._pending.textContent = gStrings.ext.formatStringFromName(
+ "notification." + pending,
+ [this.mAddon.name, gStrings.brandShortName], 2
+ );
+ } else {
+ this.removeAttribute("pending");
+
+ var isUpgrade = this.hasAttribute("upgrade");
+ var install = this._installStatus.mInstall;
+
+ if (install && install.state == AddonManager.STATE_DOWNLOAD_FAILED) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.downloadError",
+ [this.mAddon.name], 1
+ );
+ this._warningBtn.label = gStrings.ext.GetStringFromName("notification.downloadError.retry");
+ this._warningBtn.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
+ this._warningBtn.setAttribute("oncommand", "document.getBindingParent(this).retryInstall();");
+ this._warningBtn.hidden = false;
+ this._warningLink.hidden = true;
+ } else if (install && install.state == AddonManager.STATE_INSTALL_FAILED) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.installError",
+ [this.mAddon.name], 1
+ );
+ this._warningBtn.label = gStrings.ext.GetStringFromName("notification.installError.retry");
+ this._warningBtn.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
+ this._warningBtn.setAttribute("oncommand", "document.getBindingParent(this).retryInstall();");
+ this._warningBtn.hidden = false;
+ this._warningLink.hidden = true;
+ } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ this.setAttribute("notification", "error");
+ this._error.textContent = gStrings.ext.formatStringFromName(
+ "notification.blocked",
+ [this.mAddon.name], 1
+ );
+ this._errorLink.value = gStrings.ext.GetStringFromName("notification.blocked.link");
+ this._errorLink.href = this.mAddon.blocklistURL;
+ this._errorLink.hidden = false;
+ } else if (this.mAddon.jetsdk) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.jetsdk",
+ [gStrings.brandShortName, gStrings.appVersion], 2
+ );
+ this._warningLink.hidden = true;
+ this._warningBtn.hidden = true;
+ } else if ((!isUpgrade && !this.mAddon.isCompatible) && (AddonManager.checkCompatibility
+ || (this.mAddon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.incompatible",
+ [this.mAddon.name, gStrings.brandShortName, gStrings.appVersion], 3
+ );
+ this._warningLink.hidden = true;
+ this._warningBtn.hidden = true;
+ } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.softblocked",
+ [this.mAddon.name], 1
+ );
+ this._warningLink.value = gStrings.ext.GetStringFromName("notification.softblocked.link");
+ this._warningLink.href = this.mAddon.blocklistURL;
+ this._warningLink.hidden = false;
+ this._warningBtn.hidden = true;
+ } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.outdated",
+ [this.mAddon.name], 1
+ );
+ this._warningLink.value = gStrings.ext.GetStringFromName("notification.outdated.link");
+ this._warningLink.href = Services.urlFormatter.formatURLPref("plugins.update.url");
+ this._warningLink.hidden = false;
+ this._warningBtn.hidden = true;
+ } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
+ this.setAttribute("notification", "error");
+ this._error.textContent = gStrings.ext.formatStringFromName(
+ "notification.vulnerableUpdatable",
+ [this.mAddon.name], 1
+ );
+ this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableUpdatable.link");
+ this._errorLink.href = this.mAddon.blocklistURL;
+ this._errorLink.hidden = false;
+ } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
+ this.setAttribute("notification", "error");
+ this._error.textContent = gStrings.ext.formatStringFromName(
+ "notification.vulnerableNoUpdate",
+ [this.mAddon.name], 1
+ );
+ this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableNoUpdate.link");
+ this._errorLink.href = this.mAddon.blocklistURL;
+ this._errorLink.hidden = false;
+ } else if (this.mAddon.isGMPlugin && !this.mAddon.isInstalled &&
+ this.mAddon.isActive) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent =
+ gStrings.ext.formatStringFromName("notification.gmpPending",
+ [this.mAddon.name], 1);
+ } else {
+ this.removeAttribute("notification");
+ if (this.mAddon.type == "extension")
+ this.setAttribute("native", this.mAddon.native);
+ }
+ }
+
+ this._preferencesBtn.hidden = (!this.mAddon.optionsURL) ||
+ this.mAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_INFO;
+
+ if (this.typeHasFlag("SUPPORTS_ASK_TO_ACTIVATE")) {
+ this._enableBtn.disabled = true;
+ this._disableBtn.disabled = true;
+ this._askToActivateMenuitem.disabled = !this.hasPermission("ask_to_activate");
+ this._alwaysActivateMenuitem.disabled = !this.hasPermission("enable");
+ this._neverActivateMenuitem.disabled = !this.hasPermission("disable");
+ if (!this.mAddon.isActive) {
+ this._stateMenulist.selectedItem = this._neverActivateMenuitem;
+ } else if (this.mAddon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE) {
+ this._stateMenulist.selectedItem = this._askToActivateMenuitem;
+ } else {
+ this._stateMenulist.selectedItem = this._alwaysActivateMenuitem;
+ }
+ let hasActivatePermission =
+ ["ask_to_activate", "enable", "disable"].some(perm => this.hasPermission(perm));
+ this._stateMenulist.disabled = !hasActivatePermission;
+ this._stateMenulist.hidden = false;
+ this._stateMenulist.classList.add('no-auto-hide');
+ } else {
+ this._stateMenulist.hidden = true;
+ if (this.hasPermission("enable")) {
+ this._enableBtn.hidden = false;
+ let tooltip = gViewController.commands["cmd_enableItem"]
+ .getTooltip(this.mAddon);
+ this._enableBtn.setAttribute("tooltiptext", tooltip);
+ } else {
+ this._enableBtn.hidden = true;
+ }
+
+ if (this.hasPermission("disable")) {
+ this._disableBtn.hidden = false;
+ let tooltip = gViewController.commands["cmd_disableItem"]
+ .getTooltip(this.mAddon);
+ this._disableBtn.setAttribute("tooltiptext", tooltip);
+ } else {
+ this._disableBtn.hidden = true;
+ }
+ }
+
+ if (this.hasPermission("uninstall")) {
+ this._removeBtn.hidden = false;
+ let tooltip = gViewController.commands["cmd_uninstallItem"]
+ .getTooltip(this.mAddon);
+ this._removeBtn.setAttribute("tooltiptext", tooltip);
+ } else {
+ this._removeBtn.hidden = true;
+ }
+
+ this.setAttribute("active", this.mAddon.isActive);
+
+ var showProgress = this.mAddon.purchaseURL || (this.mAddon.install &&
+ this.mAddon.install.state != AddonManager.STATE_INSTALLED);
+ this._showStatus(showProgress ? "progress" : "none");
+
+ let debuggable = this.mAddon.isDebuggable &&
+ Services.prefs.getBoolPref('devtools.chrome.enabled') &&
+ Services.prefs.getBoolPref('devtools.debugger.remote-enabled');
+
+ this._debugBtn.disabled = this._debugBtn.hidden = !debuggable
+
+ if (this.mAddon.type == "experiment") {
+ this.removeAttribute("notification");
+ let prefix = "experiment.";
+ let active = this.mAddon.isActive;
+
+ if (!showProgress) {
+ let stateKey = prefix + "state." + (active ? "active" : "complete");
+ this._experimentState.value = gStrings.ext.GetStringFromName(stateKey);
+
+ let now = Date.now();
+ let end = this.endDate;
+ let days = Math.abs(end - now) / (24 * 60 * 60 * 1000);
+
+ let timeKey = prefix + "time.";
+ let timeMessage;
+
+ if (days < 1) {
+ timeKey += (active ? "endsToday" : "endedToday");
+ timeMessage = gStrings.ext.GetStringFromName(timeKey);
+ } else {
+ timeKey += (active ? "daysRemaining" : "daysPassed");
+ days = Math.round(days);
+ let timeString = gStrings.ext.GetStringFromName(timeKey);
+ timeMessage = PluralForm.get(days, timeString)
+ .replace("#1", days);
+ }
+
+ this._experimentTime.value = timeMessage;
+ }
+ }
+ ]]></body>
+ </method>
+
+ <method name="_updateUpgradeInfo">
+ <body><![CDATA[
+ // Only update the version string if we're displaying the upgrade info
+ if (this.hasAttribute("upgrade") && shouldShowVersionNumber(this.mAddon))
+ this._version.value = this.mManualUpdate.version;
+ ]]></body>
+ </method>
+
+ <method name="_fetchReleaseNotes">
+ <parameter name="aURI"/>
+ <body><![CDATA[
+ var self = this;
+ if (!aURI || this._relNotesLoaded) {
+ sendToggleEvent();
+ return;
+ }
+
+ var relNotesData = null, transformData = null;
+
+ this._relNotesLoaded = true;
+ this._relNotesLoading.hidden = false;
+ this._relNotesError.hidden = true;
+
+ function sendToggleEvent() {
+ var event = document.createEvent("Events");
+ event.initEvent("RelNotesToggle", true, true);
+ self.dispatchEvent(event);
+ }
+
+ function showRelNotes() {
+ if (!relNotesData || !transformData)
+ return;
+
+ self._relNotesLoading.hidden = true;
+
+ var processor = Components.classes["@mozilla.org/document-transformer;1?type=xslt"]
+ .createInstance(Components.interfaces.nsIXSLTProcessor);
+ processor.flags |= Components.interfaces.nsIXSLTProcessorPrivate.DISABLE_ALL_LOADS;
+
+ processor.importStylesheet(transformData);
+ var fragment = processor.transformToFragment(relNotesData, document);
+ self._relNotes.appendChild(fragment);
+ if (self.hasAttribute("show-relnotes")) {
+ var container = self._relNotesContainer;
+ container.style.height = container.scrollHeight + "px";
+ }
+ sendToggleEvent();
+ }
+
+ function handleError() {
+ dataReq.abort();
+ styleReq.abort();
+ self._relNotesLoading.hidden = true;
+ self._relNotesError.hidden = false;
+ self._relNotesLoaded = false; // allow loading to be re-tried
+ sendToggleEvent();
+ }
+
+ function handleResponse(aEvent) {
+ var req = aEvent.target;
+ var ct = req.getResponseHeader("content-type");
+ if ((!ct || ct.indexOf("text/html") < 0) &&
+ req.responseXML &&
+ req.responseXML.documentElement.namespaceURI != XMLURI_PARSE_ERROR) {
+ if (req == dataReq)
+ relNotesData = req.responseXML;
+ else
+ transformData = req.responseXML;
+ showRelNotes();
+ } else {
+ handleError();
+ }
+ }
+
+ var dataReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Components.interfaces.nsIXMLHttpRequest);
+ dataReq.open("GET", aURI.spec, true);
+ dataReq.addEventListener("load", handleResponse, false);
+ dataReq.addEventListener("error", handleError, false);
+ dataReq.send(null);
+
+ var styleReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Components.interfaces.nsIXMLHttpRequest);
+ styleReq.open("GET", UPDATES_RELEASENOTES_TRANSFORMFILE, true);
+ styleReq.addEventListener("load", handleResponse, false);
+ styleReq.addEventListener("error", handleError, false);
+ styleReq.send(null);
+ ]]></body>
+ </method>
+
+ <method name="toggleReleaseNotes">
+ <body><![CDATA[
+ if (this.hasAttribute("show-relnotes")) {
+ this._relNotesContainer.style.height = "0px";
+ this.removeAttribute("show-relnotes");
+ this._relNotesToggle.setAttribute(
+ "label",
+ this._relNotesToggle.getAttribute("showlabel")
+ );
+ this._relNotesToggle.setAttribute(
+ "tooltiptext",
+ this._relNotesToggle.getAttribute("showtooltip")
+ );
+ var event = document.createEvent("Events");
+ event.initEvent("RelNotesToggle", true, true);
+ this.dispatchEvent(event);
+ } else {
+ this._relNotesContainer.style.height = this._relNotesContainer.scrollHeight +
+ "px";
+ this.setAttribute("show-relnotes", true);
+ this._relNotesToggle.setAttribute(
+ "label",
+ this._relNotesToggle.getAttribute("hidelabel")
+ );
+ this._relNotesToggle.setAttribute(
+ "tooltiptext",
+ this._relNotesToggle.getAttribute("hidetooltip")
+ );
+ var uri = this.mManualUpdate ?
+ this.mManualUpdate.releaseNotesURI :
+ this.mAddon.releaseNotesURI;
+ this._fetchReleaseNotes(uri);
+ }
+ ]]></body>
+ </method>
+
+ <method name="restart">
+ <body><![CDATA[
+ gViewController.commands["cmd_restartApp"].doCommand();
+ ]]></body>
+ </method>
+
+ <method name="undo">
+ <body><![CDATA[
+ gViewController.commands["cmd_cancelOperation"].doCommand(this.mAddon);
+ ]]></body>
+ </method>
+
+ <method name="uninstall">
+ <body><![CDATA[
+ // If uninstalling does not require a restart and the type doesn't
+ // support undoing of restartless uninstalls, then we fake it by
+ // just disabling it it, and doing the real uninstall later.
+ if (!this.opRequiresRestart("uninstall") &&
+ !this.typeHasFlag("SUPPORTS_UNDO_RESTARTLESS_UNINSTALL")) {
+ this.setAttribute("wasDisabled", this.mAddon.userDisabled);
+
+ // We must set userDisabled to true first, this will call
+ // _updateState which will clear any pending attribute set.
+ this.mAddon.userDisabled = true;
+
+ // This won't update any other add-on manager views (bug 582002)
+ this.setAttribute("pending", "uninstall");
+ } else {
+ this.mAddon.uninstall(true);
+ }
+ ]]></body>
+ </method>
+
+ <method name="debug">
+ <body><![CDATA[
+ gViewController.doCommand("cmd_debugItem", this.mAddon);
+ ]]></body>
+ </method>
+
+ <method name="showPreferences">
+ <body><![CDATA[
+ gViewController.doCommand("cmd_showItemPreferences", this.mAddon);
+ ]]></body>
+ </method>
+
+ <method name="upgrade">
+ <body><![CDATA[
+ var install = this.mManualUpdate;
+ delete this.mManualUpdate;
+ install.install();
+ ]]></body>
+ </method>
+
+ <method name="retryInstall">
+ <body><![CDATA[
+ var install = this._installStatus.mInstall;
+ if (!install)
+ return;
+ if (install.state != AddonManager.STATE_DOWNLOAD_FAILED &&
+ install.state != AddonManager.STATE_INSTALL_FAILED)
+ return;
+ install.install();
+ ]]></body>
+ </method>
+
+ <method name="showInDetailView">
+ <body><![CDATA[
+ gViewController.loadView("addons://detail/" +
+ encodeURIComponent(this.mAddon.id));
+ ]]></body>
+ </method>
+
+ <method name="onIncludeUpdateChanged">
+ <body><![CDATA[
+ var event = document.createEvent("Events");
+ event.initEvent("IncludeUpdateChanged", true, true);
+ this.dispatchEvent(event);
+ ]]></body>
+ </method>
+
+ <method name="onEnabling">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onEnabled">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onDisabling">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onDisabled">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onUninstalling">
+ <parameter name="aRestartRequired"/>
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onOperationCancelled">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onPropertyChanged">
+ <parameter name="aProperties"/>
+ <body><![CDATA[
+ if (aProperties.indexOf("appDisabled") != -1 ||
+ aProperties.indexOf("userDisabled") != -1)
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onNoUpdateAvailable">
+ <body><![CDATA[
+ this._showStatus("none");
+ ]]></body>
+ </method>
+
+ <method name="onCheckingUpdate">
+ <body><![CDATA[
+ this._showStatus("checking-update");
+ ]]></body>
+ </method>
+
+ <method name="onCompatibilityUpdateAvailable">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onExternalInstall">
+ <parameter name="aAddon"/>
+ <parameter name="aExistingAddon"/>
+ <parameter name="aNeedsRestart"/>
+ <body><![CDATA[
+ if (aExistingAddon.id != this.mAddon.id)
+ return;
+
+ // If the install completed without needing a restart then switch to
+ // using the new Addon
+ if (!aNeedsRestart)
+ this._initWithAddon(aAddon);
+ else
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onNewInstall">
+ <parameter name="aInstall"/>
+ <body><![CDATA[
+ if (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE)
+ return;
+ if (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT &&
+ AddonManager.autoUpdateDefault)
+ return;
+
+ this.mManualUpdate = aInstall;
+ this._showStatus("update-available");
+ this._updateUpgradeInfo();
+ ]]></body>
+ </method>
+
+ <method name="onDownloadStarted">
+ <parameter name="aInstall"/>
+ <body><![CDATA[
+ this._updateState();
+ this._showStatus("progress");
+ this._installStatus.initWithInstall(aInstall);
+ ]]></body>
+ </method>
+
+ <method name="onInstallStarted">
+ <parameter name="aInstall"/>
+ <body><![CDATA[
+ this._updateState();
+ this._showStatus("progress");
+ this._installStatus.initWithInstall(aInstall);
+ ]]></body>
+ </method>
+
+ <method name="onInstallEnded">
+ <parameter name="aInstall"/>
+ <parameter name="aAddon"/>
+ <body><![CDATA[
+ // If the install completed without needing a restart then switch to
+ // using the new Addon
+ if (!(aAddon.pendingOperations & AddonManager.PENDING_INSTALL))
+ this._initWithAddon(aAddon);
+ else
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onDownloadFailed">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onInstallFailed">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+
+ <method name="onInstallCancelled">
+ <body><![CDATA[
+ this._updateState();
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="click" button="0"><![CDATA[
+ switch (event.detail) {
+ case 1:
+ // Prevent double-click where the UI changes on the first click
+ this._lastClickTarget = event.originalTarget;
+ break;
+ case 2:
+ if (event.originalTarget.localName != 'button' &&
+ !event.originalTarget.classList.contains('text-link') &&
+ event.originalTarget == this._lastClickTarget) {
+ this.showInDetailView();
+ }
+ break;
+ }
+ ]]></handler>
+ </handlers>
+ </binding>
+
+
+ <!-- Addon - uninstalled - An uninstalled addon that can be re-installed. -->
+ <binding id="addon-uninstalled"
+ extends="chrome://mozapps/content/extensions/extensions.xml#addon-base">
+ <content>
+ <xul:hbox class="pending">
+ <xul:image class="pending-icon"/>
+ <xul:label anonid="notice" flex="1"/>
+ <xul:button anonid="restart-btn" class="button-link"
+ label="&addon.restartNow.label;"
+ command="cmd_restartApp"/>
+ <xul:button anonid="undo-btn" class="button-link"
+ label="&addon.undoRemove.label;"
+ tooltiptext="&addon.undoRemove.tooltip;"
+ oncommand="document.getBindingParent(this).cancelUninstall();"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ this._notice.textContent = gStrings.ext.formatStringFromName("uninstallNotice",
+ [this.mAddon.name],
+ 1);
+
+ if (!this.opRequiresRestart("uninstall"))
+ this._restartBtn.setAttribute("hidden", true);
+
+ gEventManager.registerAddonListener(this, this.mAddon.id);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ gEventManager.unregisterAddonListener(this, this.mAddon.id);
+ ]]></destructor>
+
+ <field name="_notice" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "notice");
+ </field>
+ <field name="_restartBtn" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "restart-btn");
+ </field>
+
+ <method name="cancelUninstall">
+ <body><![CDATA[
+ // This assumes that disabling does not require a restart when
+ // uninstalling doesn't. Things will still work if not, the add-on
+ // will just still be active until finally getting uninstalled.
+
+ if (this.isPending("uninstall"))
+ this.mAddon.cancelUninstall();
+ else if (this.getAttribute("wasDisabled") != "true")
+ this.mAddon.userDisabled = false;
+
+ this.removeAttribute("pending");
+ ]]></body>
+ </method>
+
+ <method name="onOperationCancelled">
+ <body><![CDATA[
+ if (!this.isPending("uninstall"))
+ this.removeAttribute("pending");
+ ]]></body>
+ </method>
+
+ <method name="onExternalInstall">
+ <parameter name="aAddon"/>
+ <parameter name="aExistingAddon"/>
+ <parameter name="aNeedsRestart"/>
+ <body><![CDATA[
+ if (aExistingAddon.id != this.mAddon.id)
+ return;
+
+ // Make sure any newly installed add-on has the correct disabled state
+ if (this.hasAttribute("wasDisabled"))
+ aAddon.userDisabled = this.getAttribute("wasDisabled") == "true";
+
+ // If the install completed without needing a restart then switch to
+ // using the new Addon
+ if (!aNeedsRestart)
+ this.mAddon = aAddon;
+
+ this.removeAttribute("pending");
+ ]]></body>
+ </method>
+
+ <method name="onInstallStarted">
+ <parameter name="aInstall"/>
+ <body><![CDATA[
+ // Make sure any newly installed add-on has the correct disabled state
+ if (this.hasAttribute("wasDisabled"))
+ aInstall.addon.userDisabled = this.getAttribute("wasDisabled") == "true";
+ ]]></body>
+ </method>
+
+ <method name="onInstallEnded">
+ <parameter name="aInstall"/>
+ <parameter name="aAddon"/>
+ <body><![CDATA[
+ // If the install completed without needing a restart then switch to
+ // using the new Addon
+ if (!(aAddon.pendingOperations & AddonManager.PENDING_INSTALL))
+ this.mAddon = aAddon;
+
+ this.removeAttribute("pending");
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <!-- Addon - installing - an addon item that is currently being installed -->
+ <binding id="addon-installing"
+ extends="chrome://mozapps/content/extensions/extensions.xml#addon-base">
+ <content>
+ <xul:hbox anonid="warning-container" class="warning">
+ <xul:image class="warning-icon"/>
+ <xul:label anonid="warning" flex="1"/>
+ <xul:button anonid="warning-link" class="button-link"
+ oncommand="document.getBindingParent(this).retryInstall();"/>
+ <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </xul:hbox>
+ <xul:hbox class="content-container">
+ <xul:vbox class="icon-outer-container">
+ <xul:vbox class="icon-container">
+ <xul:image anonid="icon" class="icon"/>
+ </xul:vbox>
+ </xul:vbox>
+ <xul:vbox class="fade name-outer-container" flex="1">
+ <xul:hbox class="name-container">
+ <xul:label anonid="name" class="name" crop="end"/>
+ <xul:label anonid="version" class="version" hidden="true"/>
+ </xul:hbox>
+ </xul:vbox>
+ <xul:vbox class="install-status-container">
+ <xul:hbox anonid="install-status" class="install-status"/>
+ </xul:vbox>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ this._installStatus.mControl = this;
+ this._installStatus.mInstall = this.mInstall;
+ this.refreshInfo();
+ ]]></constructor>
+
+ <field name="_icon">
+ document.getAnonymousElementByAttribute(this, "anonid", "icon");
+ </field>
+ <field name="_name">
+ document.getAnonymousElementByAttribute(this, "anonid", "name");
+ </field>
+ <field name="_version">
+ document.getAnonymousElementByAttribute(this, "anonid", "version");
+ </field>
+ <field name="_warning">
+ document.getAnonymousElementByAttribute(this, "anonid", "warning");
+ </field>
+ <field name="_warningLink">
+ document.getAnonymousElementByAttribute(this, "anonid", "warning-link");
+ </field>
+ <field name="_installStatus">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "install-status");
+ </field>
+
+ <method name="onInstallCompleted">
+ <body><![CDATA[
+ this.mAddon = this.mInstall.addon;
+ this.setAttribute("name", this.mAddon.name);
+ this.setAttribute("value", this.mAddon.id);
+ this.setAttribute("status", "installed");
+ ]]></body>
+ </method>
+
+ <method name="refreshInfo">
+ <body><![CDATA[
+ this.mAddon = this.mAddon || this.mInstall.addon;
+ if (this.mAddon) {
+ this._icon.src = this.mAddon.iconURL ||
+ (this.mInstall ? this.mInstall.iconURL : "");
+ this._name.value = this.mAddon.name;
+
+ if (this.mAddon.version) {
+ this._version.value = this.mAddon.version;
+ this._version.hidden = false;
+ } else {
+ this._version.hidden = true;
+ }
+
+ } else {
+ this._icon.src = this.mInstall.iconURL;
+ // AddonInstall.name isn't always available - fallback to filename
+ if (this.mInstall.name) {
+ this._name.value = this.mInstall.name;
+ } else if (this.mInstall.sourceURI) {
+ var url = Components.classes["@mozilla.org/network/standard-url;1"]
+ .createInstance(Components.interfaces.nsIStandardURL);
+ url.init(url.URLTYPE_STANDARD, 80, this.mInstall.sourceURI.spec,
+ null, null);
+ url.QueryInterface(Components.interfaces.nsIURL);
+ this._name.value = url.fileName;
+ }
+
+ if (this.mInstall.version) {
+ this._version.value = this.mInstall.version;
+ this._version.hidden = false;
+ } else {
+ this._version.hidden = true;
+ }
+ }
+
+ if (this.mInstall.state == AddonManager.STATE_DOWNLOAD_FAILED) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.downloadError",
+ [this._name.value], 1
+ );
+ this._warningLink.label = gStrings.ext.GetStringFromName("notification.downloadError.retry");
+ this._warningLink.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
+ } else if (this.mInstall.state == AddonManager.STATE_INSTALL_FAILED) {
+ this.setAttribute("notification", "warning");
+ this._warning.textContent = gStrings.ext.formatStringFromName(
+ "notification.installError",
+ [this._name.value], 1
+ );
+ this._warningLink.label = gStrings.ext.GetStringFromName("notification.installError.retry");
+ this._warningLink.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
+ } else {
+ this.removeAttribute("notification");
+ }
+ ]]></body>
+ </method>
+
+ <method name="retryInstall">
+ <body><![CDATA[
+ this.mInstall.install();
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="detail-row">
+ <content>
+ <xul:label class="detail-row-label" xbl:inherits="value=label"/>
+ <xul:label class="detail-row-value" xbl:inherits="value"/>
+ </content>
+
+ <implementation>
+ <property name="value">
+ <getter><![CDATA[
+ return this.getAttribute("value");
+ ]]></getter>
+ <setter><![CDATA[
+ if (!val)
+ this.removeAttribute("value");
+ else
+ this.setAttribute("value", val);
+ ]]></setter>
+ </property>
+ </implementation>
+ </binding>
+
+</bindings>
diff --git a/toolkit/mozapps/extensions/content/extensions.xul b/toolkit/mozapps/extensions/content/extensions.xul
new file mode 100644
index 000000000..c1a8edc86
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/extensions.xul
@@ -0,0 +1,689 @@
+<?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://mozapps/content/extensions/extensions.css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/extensions.css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/about.css"?>
+
+<!DOCTYPE page [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
+%extensionsDTD;
+]>
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xhtml="http://www.w3.org/1999/xhtml"
+ id="addons-page" title="&addons.windowTitle;"
+ role="application" windowtype="Addons:Manager"
+ disablefastfind="true">
+
+ <xhtml:link rel="shortcut icon"
+ href="chrome://mozapps/skin/extensions/extensionGeneric-16.png"/>
+
+ <script type="application/javascript"
+ src="chrome://mozapps/content/extensions/extensions.js"/>
+ <script type="application/javascript"
+ src="chrome://global/content/contentAreaUtils.js"/>
+
+ <popupset>
+ <!-- menu for an addon item -->
+ <menupopup id="addonitem-popup">
+ <menuitem id="menuitem_showDetails" command="cmd_showItemDetails"
+ default="true" label="&cmd.showDetails.label;"
+ accesskey="&cmd.showDetails.accesskey;"/>
+ <menuitem id="menuitem_enableItem" command="cmd_enableItem"
+ label="&cmd.enableAddon.label;"
+ accesskey="&cmd.enableAddon.accesskey;"/>
+ <menuitem id="menuitem_disableItem" command="cmd_disableItem"
+ label="&cmd.disableAddon.label;"
+ accesskey="&cmd.disableAddon.accesskey;"/>
+ <menuitem id="menuitem_enableTheme" command="cmd_enableItem"
+ label="&cmd.enableTheme.label;"
+ accesskey="&cmd.enableTheme.accesskey;"/>
+ <menuitem id="menuitem_disableTheme" command="cmd_disableItem"
+ label="&cmd.disableTheme.label;"
+ accesskey="&cmd.disableTheme.accesskey;"/>
+ <menuitem id="menuitem_installItem" command="cmd_installItem"
+ label="&cmd.installAddon.label;"
+ accesskey="&cmd.installAddon.accesskey;"/>
+ <menuitem id="menuitem_uninstallItem" command="cmd_uninstallItem"
+ label="&cmd.uninstallAddon.label;"
+ accesskey="&cmd.uninstallAddon.accesskey;"/>
+ <menuitem id="menuitem_debugItem" command="cmd_debugItem"
+ label="&cmd.debugAddon.label;"/>
+ <menuseparator id="addonitem-menuseparator" />
+ <menuitem id="menuitem_preferences" command="cmd_showItemPreferences"
+#ifdef XP_WIN
+ label="&cmd.preferencesWin.label;"
+ accesskey="&cmd.preferencesWin.accesskey;"/>
+#else
+ label="&cmd.preferencesUnix.label;"
+ accesskey="&cmd.preferencesUnix.accesskey;"/>
+#endif
+ <menuitem id="menuitem_findUpdates" command="cmd_findItemUpdates"
+ label="&cmd.findUpdates.label;"
+ accesskey="&cmd.findUpdates.accesskey;"/>
+ <menuitem id="menuitem_about" command="cmd_showItemAbout"
+ label="&cmd.about.label;"
+ accesskey="&cmd.about.accesskey;"/>
+ </menupopup>
+ </popupset>
+
+ <!-- global commands - these act on all addons, or affect the addons manager
+ in some other way -->
+ <commandset id="globalCommandSet">
+ <command id="cmd_focusSearch"/>
+ <command id="cmd_findAllUpdates"/>
+ <command id="cmd_restartApp"/>
+ <command id="cmd_goToDiscoverPane"/>
+ <command id="cmd_goToRecentUpdates"/>
+ <command id="cmd_goToAvailableUpdates"/>
+ <command id="cmd_installFromFile"/>
+ <command id="cmd_back"/>
+ <command id="cmd_forward"/>
+ <command id="cmd_enableCheckCompatibility"/>
+<!-- <command id="cmd_pluginCheck"/> -->
+ <command id="cmd_enableUpdateSecurity"/>
+ <command id="cmd_toggleAutoUpdateDefault"/>
+ <command id="cmd_resetAddonAutoUpdate"/>
+ <command id="cmd_experimentsLearnMore"/>
+ <command id="cmd_experimentsOpenTelemetryPreferences"/>
+ </commandset>
+
+ <!-- view commands - these act on the selected addon -->
+ <commandset id="viewCommandSet"
+ events="richlistbox-select" commandupdater="true">
+ <command id="cmd_showItemDetails"/>
+ <command id="cmd_findItemUpdates"/>
+ <command id="cmd_showItemPreferences"/>
+ <command id="cmd_showItemAbout"/>
+ <command id="cmd_debugItem"/>
+ <command id="cmd_enableItem"/>
+ <command id="cmd_disableItem"/>
+ <command id="cmd_installItem"/>
+ <command id="cmd_purchaseItem"/>
+ <command id="cmd_uninstallItem"/>
+ <command id="cmd_cancelUninstallItem"/>
+ <command id="cmd_cancelOperation"/>
+ <command id="cmd_contribute"/>
+ <command id="cmd_askToActivateItem"/>
+ <command id="cmd_alwaysActivateItem"/>
+ <command id="cmd_neverActivateItem"/>
+ </commandset>
+
+ <keyset>
+ <!-- XXXunf Disabled until bug 371900 is fixed. -->
+ <key id="focusSearch" key="&search.commandkey;" modifiers="accel"
+ disabled="true"/>
+ </keyset>
+
+ <!-- main header -->
+ <hbox id="header" align="center">
+ <toolbarbutton id="back-btn" class="nav-button header-button" command="cmd_back"
+ tooltiptext="&cmd.back.tooltip;" hidden="true" disabled="true"/>
+ <toolbarbutton id="forward-btn" class="nav-button header-button" command="cmd_forward"
+ tooltiptext="&cmd.forward.tooltip;" hidden="true" disabled="true"/>
+ <spacer flex="1"/>
+ <hbox id="updates-container" align="center">
+ <image class="spinner"/>
+ <label id="updates-noneFound" hidden="true"
+ value="&updates.noneFound.label;"/>
+ <button id="updates-manualUpdatesFound-btn" class="button-link"
+ hidden="true" label="&updates.manualUpdatesFound.label;"
+ command="cmd_goToAvailableUpdates"/>
+ <label id="updates-progress" hidden="true"
+ value="&updates.updating.label;"/>
+ <label id="updates-installed" hidden="true"
+ value="&updates.installed.label;"/>
+ <label id="updates-downloaded" hidden="true"
+ value="&updates.downloaded.label;"/>
+ <button id="updates-restart-btn" class="button-link" hidden="true"
+ label="&updates.restart.label;"
+ command="cmd_restartApp"/>
+ </hbox>
+ <toolbarbutton id="header-utils-btn" class="header-button" type="menu"
+ tooltiptext="&toolsMenu.tooltip;">
+ <menupopup id="utils-menu">
+ <menuitem id="utils-updateNow"
+ label="&updates.checkForUpdates.label;"
+ accesskey="&updates.checkForUpdates.accesskey;"
+ command="cmd_findAllUpdates"/>
+ <menuitem id="utils-viewUpdates"
+ label="&updates.viewUpdates.label;"
+ accesskey="&updates.viewUpdates.accesskey;"
+ command="cmd_goToRecentUpdates"/>
+ <menuseparator id="utils-installFromFile-separator"/>
+ <menuitem id="utils-installFromFile"
+ label="&installAddonFromFile.label;"
+ accesskey="&installAddonFromFile.accesskey;"
+ command="cmd_installFromFile"/>
+ <menuseparator/>
+ <menuitem id="utils-autoUpdateDefault"
+ label="&updates.updateAddonsAutomatically.label;"
+ accesskey="&updates.updateAddonsAutomatically.accesskey;"
+ type="checkbox" autocheck="false"
+ command="cmd_toggleAutoUpdateDefault"/>
+ <menuitem id="utils-resetAddonUpdatesToAutomatic"
+ label="&updates.resetUpdatesToAutomatic.label;"
+ accesskey="&updates.resetUpdatesToAutomatic.accesskey;"
+ command="cmd_resetAddonAutoUpdate"/>
+ <menuitem id="utils-resetAddonUpdatesToManual"
+ label="&updates.resetUpdatesToManual.label;"
+ accesskey="&updates.resetUpdatesToManual.accesskey;"
+ command="cmd_resetAddonAutoUpdate"/>
+ </menupopup>
+ </toolbarbutton>
+ <textbox id="header-search" type="search" searchbutton="true"
+ searchbuttonlabel="&search.buttonlabel;"
+ placeholder="&search.placeholder;"/>
+ </hbox>
+
+ <hbox flex="1">
+
+ <!-- category list -->
+ <richlistbox id="categories">
+ <richlistitem id="category-search" value="addons://search/"
+ class="category"
+ name="&view.search.label;" priority="0"
+ tooltiptext="&view.search.label;" disabled="true"/>
+ <richlistitem id="category-discover" value="addons://discover/"
+ class="category"
+ name="&view.discover.label;" priority="1000"
+ tooltiptext="&view.discover.label;"/>
+ <richlistitem id="category-availableUpdates" value="addons://updates/available"
+ class="category"
+ name="&view.availableUpdates.label;" priority="100000"
+ tooltiptext="&view.availableUpdates.label;"
+ disabled="true"/>
+ <richlistitem id="category-recentUpdates" value="addons://updates/recent"
+ class="category"
+ name="&view.recentUpdates.label;" priority="101000"
+ tooltiptext="&view.recentUpdates.label;" disabled="true"/>
+ </richlistbox>
+
+ <box id="view-port-container" class="main-content" flex="1">
+
+ <!-- view port -->
+ <deck id="view-port" flex="1" selectedIndex="0">
+
+ <!-- discover view -->
+ <deck id="discover-view" flex="1" class="view-pane" selectedIndex="0" tabindex="0">
+ <vbox id="discover-loading" align="center" pack="stretch" flex="1" class="alert-container">
+ <spacer class="alert-spacer-before"/>
+ <hbox class="alert loading" align="center">
+ <image/>
+ <label value="&loading.label;"/>
+ </hbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <vbox id="discover-error" align="center" pack="stretch" flex="1" class="alert-container">
+ <spacer class="alert-spacer-before"/>
+ <hbox>
+ <spacer class="discover-spacer-before"/>
+ <hbox class="alert" align="center">
+ <image class="discover-logo"/>
+ <vbox flex="1" align="stretch">
+ <label class="discover-title">&discover.title;</label>
+ <description class="discover-description">&discover.description2;</description>
+ <description class="discover-footer">&discover.footer;</description>
+ </vbox>
+ </hbox>
+ <spacer class="discover-spacer-after"/>
+ </hbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <browser id="discover-browser" type="content" flex="1"
+ disablehistory="true" homepage="about:blank"/>
+ </deck>
+
+ <!-- search view -->
+ <vbox id="search-view" flex="1" class="view-pane" tabindex="0">
+ <hbox class="view-header global-warning-container" align="center">
+ <!-- global warnings -->
+ <hbox class="global-warning" flex="1">
+ <hbox class="global-warning-safemode" flex="1" align="center"
+ tooltiptext="&warning.safemode.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.safemode.label;"/>
+ </hbox>
+ <hbox class="global-warning-checkcompatibility" flex="1" align="center"
+ tooltiptext="&warning.checkcompatibility.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.checkcompatibility.label;"/>
+ </hbox>
+ <button class="button-link global-warning-checkcompatibility"
+ label="&warning.checkcompatibility.enable.label;"
+ tooltiptext="&warning.checkcompatibility.enable.tooltip;"
+ command="cmd_enableCheckCompatibility"/>
+ <hbox class="global-warning-updatesecurity" flex="1" align="center"
+ tooltiptext="&warning.updatesecurity.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.updatesecurity.label;"/>
+ </hbox>
+ <button class="button-link global-warning-updatesecurity"
+ label="&warning.updatesecurity.enable.label;"
+ tooltiptext="&warning.updatesecurity.enable.tooltip;"
+ command="cmd_enableUpdateSecurity"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ <spacer flex="1"/>
+ <hbox id="search-sorters" class="sort-controls"
+ showrelevance="true" sortby="relevancescore" ascending="false"/>
+ </hbox>
+ <hbox id="search-filter" align="center">
+ <label id="search-filter-label" value="&search.filter2.label;"/>
+ <radiogroup id="search-filter-radiogroup" orient="horizontal"
+ align="center" persist="value" value="remote">
+ <radio id="search-filter-local" class="search-filter-radio"
+ label="&search.filter2.installed.label;" value="local"
+ tooltiptext="&search.filter2.installed.tooltip;"/>
+ <radio id="search-filter-remote" class="search-filter-radio"
+ label="&search.filter2.available.label;" value="remote"
+ tooltiptext="&search.filter2.available.tooltip;"/>
+ </radiogroup>
+ </hbox>
+ <vbox id="search-loading" class="alert-container"
+ flex="1" hidden="true">
+ <spacer class="alert-spacer-before"/>
+ <hbox class="alert loading" align="center">
+ <image/>
+ <label value="&loading.label;"/>
+ </hbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <vbox id="search-list-empty" class="alert-container"
+ flex="1" hidden="true">
+ <spacer class="alert-spacer-before"/>
+ <vbox class="alert">
+ <label value="&listEmpty.search.label;"/>
+ <button class="discover-button"
+ id="discover-button-search"
+ label="&listEmpty.button.label;"
+ command="cmd_goToDiscoverPane"/>
+ </vbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <richlistbox id="search-list" class="list" flex="1">
+ <hbox pack="center">
+ <label id="search-allresults-link" class="text-link"/>
+ </hbox>
+ </richlistbox>
+ </vbox>
+
+ <!-- list view -->
+ <vbox id="list-view" flex="1" class="view-pane" align="stretch" tabindex="0">
+ <hbox class="view-header global-warning-container">
+ <!-- global warnings -->
+ <hbox class="global-warning" flex="1">
+ <hbox class="global-warning-safemode" flex="1" align="center"
+ tooltiptext="&warning.safemode.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.safemode.label;"/>
+ </hbox>
+ <hbox class="global-warning-checkcompatibility" flex="1" align="center"
+ tooltiptext="&warning.checkcompatibility.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.checkcompatibility.label;"/>
+ </hbox>
+ <button class="button-link global-warning-checkcompatibility"
+ label="&warning.checkcompatibility.enable.label;"
+ tooltiptext="&warning.checkcompatibility.enable.tooltip;"
+ command="cmd_enableCheckCompatibility"/>
+ <hbox class="global-warning-updatesecurity" flex="1" align="center"
+ tooltiptext="&warning.updatesecurity.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.updatesecurity.label;"/>
+ </hbox>
+ <button class="button-link global-warning-updatesecurity"
+ label="&warning.updatesecurity.enable.label;"
+ tooltiptext="&warning.updatesecurity.enable.tooltip;"
+ command="cmd_enableUpdateSecurity"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ </hbox>
+<!-- <hbox class="view-header global-info-container plugin-info-container">
+ <hbox class="global-info" flex="1" align="center">
+ <button class="button-link global-info-plugincheck"
+ label="&info.plugincheck.label;"
+ tooltiptext="&info.plugincheck.tooltip;"
+ command="cmd_pluginCheck"/>
+ <spacer flex="5000"/>
+ </hbox>
+ </hbox> -->
+ <hbox class="view-header global-info-container experiment-info-container">
+ <hbox class="global-info" flex="1" align="center">
+ <label value="&experiment.info.label;"/>
+ <button id="experiments-learn-more"
+ label="&experiment.info.learnmore;"
+ tooltiptext="&experiment.info.learnmore;"
+ accesskey="&experiment.info.learnmore.accesskey;"
+ command="cmd_experimentsLearnMore"/>
+ <button id="experiments-change-telemetry"
+ label="&experiment.info.changetelemetry;"
+ tooltiptext="&experiment.info.changetelemetry;"
+ accesskey="&experiment.info.changetelemetry.accesskey;"
+ command="cmd_experimentsOpenTelemetryPreferences"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap. -->
+ </hbox>
+ </hbox>
+ <vbox id="addon-list-empty" class="alert-container"
+ flex="1" hidden="true">
+ <spacer class="alert-spacer-before"/>
+ <vbox class="alert">
+ <label value="&listEmpty.installed.label;"/>
+ <button class="discover-button"
+ id="discover-button-install"
+ label="&listEmpty.button.label;"
+ command="cmd_goToDiscoverPane"/>
+ </vbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <richlistbox id="addon-list" class="list" flex="1"/>
+ </vbox>
+
+ <!-- updates view -->
+ <vbox id="updates-view" flex="1" class="view-pane" tabindex="0">
+ <hbox class="view-header global-warning-container" align="center">
+ <!-- global warnings -->
+ <hbox class="global-warning" flex="1">
+ <hbox class="global-warning-safemode" flex="1" align="center"
+ tooltiptext="&warning.safemode.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.safemode.label;"/>
+ </hbox>
+ <hbox class="global-warning-checkcompatibility" flex="1" align="center"
+ tooltiptext="&warning.checkcompatibility.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.checkcompatibility.label;"/>
+ </hbox>
+ <button class="button-link global-warning-checkcompatibility"
+ label="&warning.checkcompatibility.enable.label;"
+ tooltiptext="&warning.checkcompatibility.enable.tooltip;"
+ command="cmd_enableCheckCompatibility"/>
+ <hbox class="global-warning-updatesecurity" flex="1" align="center"
+ tooltiptext="&warning.updatesecurity.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.updatesecurity.label;"/>
+ </hbox>
+ <button class="button-link global-warning-updatesecurity"
+ label="&warning.updatesecurity.enable.label;"
+ tooltiptext="&warning.updatesecurity.enable.tooltip;"
+ command="cmd_enableUpdateSecurity"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ <spacer flex="1"/>
+ <hbox id="updates-sorters" class="sort-controls" sortby="updateDate"
+ ascending="false"/>
+ </hbox>
+ <vbox id="updates-list-empty" class="alert-container"
+ flex="1" hidden="true">
+ <spacer class="alert-spacer-before"/>
+ <vbox class="alert">
+ <label id="empty-availableUpdates-msg" value="&listEmpty.availableUpdates.label;"/>
+ <label id="empty-recentUpdates-msg" value="&listEmpty.recentUpdates.label;"/>
+ <button label="&listEmpty.findUpdates.label;"
+ command="cmd_findAllUpdates"/>
+ </vbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <hbox id="update-actions" pack="center">
+ <button id="update-selected-btn" hidden="true"
+ label="&updates.updateSelected.label;"
+ tooltiptext="&updates.updateSelected.tooltip;"/>
+ </hbox>
+ <richlistbox id="updates-list" class="list" flex="1"/>
+ </vbox>
+
+ <!-- detail view -->
+ <scrollbox id="detail-view" flex="1" class="view-pane addon-view" orient="vertical" tabindex="0"
+ role="document">
+ <!-- global warnings -->
+ <hbox class="global-warning-container global-warning">
+ <hbox class="global-warning-safemode" flex="1" align="center"
+ tooltiptext="&warning.safemode.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.safemode.label;"/>
+ </hbox>
+ <hbox class="global-warning-checkcompatibility" flex="1" align="center"
+ tooltiptext="&warning.checkcompatibility.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.checkcompatibility.label;"/>
+ </hbox>
+ <button class="button-link global-warning-checkcompatibility"
+ label="&warning.checkcompatibility.enable.label;"
+ tooltiptext="&warning.checkcompatibility.enable.tooltip;"
+ command="cmd_enableCheckCompatibility"/>
+ <hbox class="global-warning-updatesecurity" flex="1" align="center"
+ tooltiptext="&warning.updatesecurity.label;">
+ <image class="warning-icon"/>
+ <label class="global-warning-text" flex="1" crop="end"
+ value="&warning.updatesecurity.label;"/>
+ </hbox>
+ <button class="button-link global-warning-updatesecurity"
+ label="&warning.updatesecurity.enable.label;"
+ tooltiptext="&warning.updatesecurity.enable.tooltip;"
+ command="cmd_enableUpdateSecurity"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ <hbox flex="1">
+ <spacer flex="1"/>
+ <!-- "loading" splash screen -->
+ <vbox class="alert-container">
+ <spacer class="alert-spacer-before"/>
+ <hbox class="alert loading">
+ <image/>
+ <label value="&loading.label;"/>
+ </hbox>
+ <spacer class="alert-spacer-after"/>
+ </vbox>
+ <!-- actual detail view -->
+ <vbox class="detail-view-container" flex="3" contextmenu="addonitem-popup">
+ <vbox id="detail-notifications">
+ <hbox id="warning-container" align="center" class="warning">
+ <image class="warning-icon"/>
+ <label id="detail-warning" flex="1"/>
+ <label id="detail-warning-link" class="text-link"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ <hbox id="error-container" align="center" class="error">
+ <image class="error-icon"/>
+ <label id="detail-error" flex="1"/>
+ <label id="detail-error-link" class="text-link"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ <hbox id="pending-container" align="center" class="pending">
+ <image class="pending-icon"/>
+ <label id="detail-pending" flex="1"/>
+ <button id="detail-restart-btn" class="button-link"
+ label="&addon.restartNow.label;"
+ command="cmd_restartApp"/>
+ <button id="detail-undo-btn" class="button-link"
+ label="&addon.undoAction.label;"
+ tooltipText="&addon.undoAction.tooltip;"
+ command="cmd_cancelOperation"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
+ </hbox>
+ </vbox>
+ <hbox align="start">
+ <vbox id="detail-icon-container" align="end">
+ <image id="detail-icon" class="icon"/>
+ </vbox>
+ <vbox flex="1">
+ <vbox id="detail-summary">
+ <hbox id="detail-name-container" class="name-container"
+ align="start">
+ <label id="detail-name" flex="1"/>
+ <label id="detail-version"/>
+ <label class="disabled-postfix" value="&addon.disabled.postfix;"/>
+ <label class="update-postfix" value="&addon.update.postfix;"/>
+ <spacer flex="5000"/> <!-- Necessary to allow the name to wrap -->
+ </hbox>
+ <label id="detail-creator" class="creator"/>
+ <label id="detail-translators" class="translators"/>
+ </vbox>
+ <hbox id="detail-experiment-container">
+ <svg width="8" height="8" viewBox="0 0 8 8" version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ id="detail-experiment-bullet-container">
+ <circle cx="4" cy="4" r="4" id="detail-experiment-bullet"/>
+ </svg>
+ <label id="detail-experiment-state"/>
+ <label id="detail-experiment-time"/>
+ </hbox>
+ <hbox id="detail-desc-container" align="start">
+ <vbox pack="center"> <!-- Necessary to work around bug 394738 -->
+ <image id="detail-screenshot" hidden="true"/>
+ </vbox>
+ <vbox flex="1">
+ <description id="detail-desc"/>
+ <description id="detail-fulldesc"/>
+ </vbox>
+ </hbox>
+ <vbox id="detail-contributions">
+ <description id="detail-contrib-description">
+ &detail.contributions.description;
+ </description>
+ <hbox align="center">
+ <label id="detail-contrib-suggested"/>
+ <spacer flex="1"/>
+ <button id="detail-contrib-btn"
+ label="&cmd.contribute.label;"
+ accesskey="&cmd.contribute.accesskey;"
+ tooltiptext="&cmd.contribute.tooltip;"
+ command="cmd_contribute"/>
+ </hbox>
+ </vbox>
+ <grid id="detail-grid">
+ <columns>
+ <column flex="1"/>
+ <column flex="2"/>
+ </columns>
+ <rows id="detail-rows">
+ <row class="detail-row-complex" id="detail-updates-row">
+ <label class="detail-row-label" value="&detail.updateType;"/>
+ <hbox align="center">
+ <radiogroup id="detail-autoUpdate" orient="horizontal">
+ <!-- The values here need to match the values of
+ AddonManager.AUTOUPDATE_* -->
+ <radio label="&detail.updateDefault.label;"
+ tooltiptext="&detail.updateDefault.tooltip;"
+ value="1"/>
+ <radio label="&detail.updateAutomatic.label;"
+ tooltiptext="&detail.updateAutomatic.tooltip;"
+ value="2"/>
+ <radio label="&detail.updateManual.label;"
+ tooltiptext="&detail.updateManual.tooltip;"
+ value="0"/>
+ </radiogroup>
+ <button id="detail-findUpdates-btn" class="button-link"
+ label="&detail.checkForUpdates.label;"
+ accesskey="&detail.checkForUpdates.accesskey;"
+ tooltiptext="&detail.checkForUpdates.tooltip;"
+ command="cmd_findItemUpdates"/>
+ </hbox>
+ </row>
+ <row class="detail-row" id="detail-dateUpdated" label="&detail.lastupdated.label;"/>
+ <row class="detail-row-complex" id="detail-homepage-row" label="&detail.home;">
+ <label class="detail-row-label" value="&detail.home;"/>
+ <label id="detail-homepage" class="detail-row-value text-link" crop="end"/>
+ </row>
+ <row class="detail-row-complex" id="detail-repository-row" label="&detail.repository;">
+ <label class="detail-row-label" value="&detail.repository;"/>
+ <label id="detail-repository" class="detail-row-value text-link"/>
+ </row>
+ <row class="detail-row" id="detail-size" label="&detail.size;"/>
+ <row class="detail-row-complex" id="detail-rating-row">
+ <label class="detail-row-label" value="&rating2.label;"/>
+ <hbox>
+ <label id="detail-rating" class="meta-value meta-rating"
+ showrating="average"/>
+ <label id="detail-reviews" class="text-link"/>
+ </hbox>
+ </row>
+ <row class="detail-row" id="detail-downloads" label="&detail.numberOfDownloads.label;"/>
+ </rows>
+ </grid>
+ <hbox id="detail-controls">
+ <button id="detail-prefs-btn" class="addon-control preferences"
+#ifdef XP_WIN
+ label="&detail.showPreferencesWin.label;"
+ accesskey="&detail.showPreferencesWin.accesskey;"
+ tooltiptext="&detail.showPreferencesWin.tooltip;"
+#else
+ label="&detail.showPreferencesUnix.label;"
+ accesskey="&detail.showPreferencesUnix.accesskey;"
+ tooltiptext="&detail.showPreferencesUnix.tooltip;"
+#endif
+ command="cmd_showItemPreferences"/>
+ <spacer flex="1"/>
+ <button id="detail-debug-btn" class="addon-control debug"
+ label="Debug"
+ command="cmd_debugItem" />
+ <button id="detail-enable-btn" class="addon-control enable"
+ label="&cmd.enableAddon.label;"
+ accesskey="&cmd.enableAddon.accesskey;"
+ command="cmd_enableItem"/>
+ <button id="detail-disable-btn" class="addon-control disable"
+ label="&cmd.disableAddon.label;"
+ accesskey="&cmd.disableAddon.accesskey;"
+ command="cmd_disableItem"/>
+ <button id="detail-uninstall-btn" class="addon-control remove"
+ label="&cmd.uninstallAddon.label;"
+ accesskey="&cmd.uninstallAddon.accesskey;"
+ command="cmd_uninstallItem"/>
+ <button id="detail-purchase-btn" class="addon-control purchase"
+ command="cmd_purchaseItem"/>
+ <button id="detail-install-btn" class="addon-control install"
+ label="&cmd.installAddon.label;"
+ accesskey="&cmd.installAddon.accesskey;"
+ command="cmd_installItem"/>
+ <menulist id="detail-state-menulist"
+ crop="none" sizetopopup="always"
+ tooltiptext="&cmd.stateMenu.tooltip;">
+ <menupopup>
+ <menuitem id="detail-ask-to-activate-menuitem"
+ class="addon-control"
+ label="&cmd.askToActivate.label;"
+ tooltiptext="&cmd.askToActivate.tooltip;"
+ command="cmd_askToActivateItem"/>
+ <menuitem id="detail-always-activate-menuitem"
+ class="addon-control"
+ label="&cmd.alwaysActivate.label;"
+ tooltiptext="&cmd.alwaysActivate.tooltip;"
+ command="cmd_alwaysActivateItem"/>
+ <menuitem id="detail-never-activate-menuitem"
+ class="addon-control"
+ label="&cmd.neverActivate.label;"
+ tooltiptext="&cmd.neverActivate.tooltip;"
+ command="cmd_neverActivateItem"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </vbox>
+ </hbox>
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+ </scrollbox>
+
+ </deck>
+
+ </box>
+ </hbox>
+
+</page>
diff --git a/toolkit/mozapps/extensions/content/gmpPrefs.xul b/toolkit/mozapps/extensions/content/gmpPrefs.xul
new file mode 100644
index 000000000..ea7ee92fa
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/gmpPrefs.xul
@@ -0,0 +1,8 @@
+<?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 is intentionally empty and a dummy to let the GMPProvider
+ have a preferences button in the list view. -->
diff --git a/toolkit/mozapps/extensions/content/list.js b/toolkit/mozapps/extensions/content/list.js
new file mode 100644
index 000000000..aac87911e
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/list.js
@@ -0,0 +1,165 @@
+// -*- 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 kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const kDialog = "dialog";
+
+/**
+ * This dialog can be initialized from parameters supplied via window.arguments
+ * or it can be used to display blocklist notification and blocklist blocked
+ * installs via nsIDialogParamBlock as is done by nsIExtensionManager.
+ *
+ * When using this dialog with window.arguments it must be opened modally, the
+ * caller can inspect the user action after the dialog closes by inspecting the
+ * value of the |result| parameter on this object which is set to the dlgtype
+ * of the button used to close the dialog.
+ *
+ * window.arguments[0] is an array of strings to display in the tree. If the
+ * array is empty the tree will not be displayed.
+ * window.arguments[1] a JS Object with the following properties:
+ *
+ * title: A title string, to be displayed in the title bar of the dialog.
+ * message1: A message string, displayed above the addon list
+ * message2: A message string, displayed below the addon list
+ * message3: A bolded message string, displayed below the addon list
+ * moreInfoURL: An url for displaying more information
+ * iconClass : Can be one of the following values (default is alert-icon)
+ * alert-icon, error-icon, or question-icon
+ *
+ * If no value is supplied for message1, message2, message3, or moreInfoURL,
+ * the element is not displayed.
+ *
+ * buttons: {
+ * accept: { label: "A Label for the Accept button",
+ * focused: true },
+ * cancel: { label: "A Label for the Cancel button" },
+ * ...
+ * },
+ *
+ * result: The dlgtype of button that was used to dismiss the dialog.
+ */
+
+"use strict";
+
+var gButtons = { };
+
+function init() {
+ var de = document.documentElement;
+ var items = [];
+ if (window.arguments[0] instanceof Components.interfaces.nsIDialogParamBlock) {
+ // This is a warning about a blocklisted item the user is trying to install
+ var args = window.arguments[0];
+ var softblocked = args.GetInt(0) == 1 ? true : false;
+
+ var extensionsBundle = document.getElementById("extensionsBundle");
+ try {
+ var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
+ .getService(Components.interfaces.nsIURLFormatter);
+ var url = formatter.formatURLPref("extensions.blocklist.detailsURL");
+ }
+ catch (e) { }
+
+ var params = {
+ moreInfoURL: url,
+ };
+
+ if (softblocked) {
+ params.title = extensionsBundle.getString("softBlockedInstallTitle");
+ params.message1 = extensionsBundle.getFormattedString("softBlockedInstallMsg",
+ [args.GetString(0)]);
+ var accept = de.getButton("accept");
+ accept.label = extensionsBundle.getString("softBlockedInstallAcceptLabel");
+ accept.accessKey = extensionsBundle.getString("softBlockedInstallAcceptKey");
+ de.getButton("cancel").focus();
+ document.addEventListener("dialogaccept", allowInstall, false);
+ }
+ else {
+ params.title = extensionsBundle.getString("blocklistedInstallTitle2");
+ params.message1 = extensionsBundle.getFormattedString("blocklistedInstallMsg2",
+ [args.GetString(0)]);
+ de.buttons = "accept";
+ de.getButton("accept").focus();
+ }
+ }
+ else {
+ items = window.arguments[0];
+ params = window.arguments[1];
+ }
+
+ var addons = document.getElementById("addonsChildren");
+ if (items.length > 0)
+ document.getElementById("addonsTree").hidden = false;
+
+ // Fill the addons list
+ for (var item of items) {
+ var treeitem = document.createElementNS(kXULNS, "treeitem");
+ var treerow = document.createElementNS(kXULNS, "treerow");
+ var treecell = document.createElementNS(kXULNS, "treecell");
+ treecell.setAttribute("label", item);
+ treerow.appendChild(treecell);
+ treeitem.appendChild(treerow);
+ addons.appendChild(treeitem);
+ }
+
+ // Set the messages
+ var messages = ["message1", "message2", "message3"];
+ for (let messageEntry of messages) {
+ if (messageEntry in params) {
+ var message = document.getElementById(messageEntry);
+ message.hidden = false;
+ message.appendChild(document.createTextNode(params[messageEntry]));
+ }
+ }
+
+ document.getElementById("infoIcon").className =
+ params["iconClass"] ? "spaced " + params["iconClass"] : "spaced alert-icon";
+
+ if ("moreInfoURL" in params && params["moreInfoURL"]) {
+ message = document.getElementById("moreInfo");
+ message.value = extensionsBundle.getString("moreInfoText");
+ message.setAttribute("href", params["moreInfoURL"]);
+ document.getElementById("moreInfoBox").hidden = false;
+ }
+
+ // Set the window title
+ if ("title" in params)
+ document.title = params.title;
+
+ // Set up the buttons
+ if ("buttons" in params) {
+ gButtons = params.buttons;
+ var buttonString = "";
+ for (var buttonType in gButtons)
+ buttonString += "," + buttonType;
+ de.buttons = buttonString.substr(1);
+ for (buttonType in gButtons) {
+ var button = de.getButton(buttonType);
+ button.label = gButtons[buttonType].label;
+ if (gButtons[buttonType].focused)
+ button.focus();
+ document.addEventListener(kDialog + buttonType, handleButtonCommand, true);
+ }
+ }
+}
+
+function shutdown() {
+ for (var buttonType in gButtons)
+ document.removeEventListener(kDialog + buttonType, handleButtonCommand, true);
+}
+
+function allowInstall() {
+ var args = window.arguments[0];
+ args.SetInt(1, 1);
+}
+
+/**
+ * Watch for the user hitting one of the buttons to dismiss the dialog
+ * and report the result back to the caller through the |result| property on
+ * the arguments object.
+ */
+function handleButtonCommand(event) {
+ window.arguments[1].result = event.type.substr(kDialog.length);
+}
diff --git a/toolkit/mozapps/extensions/content/list.xul b/toolkit/mozapps/extensions/content/list.xul
new file mode 100644
index 000000000..65efeb6a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/list.xul
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<dialog id="addonList" windowtype="Addons:List"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onunload="shutdown();"
+ buttons="accept,cancel" onload="init();">
+
+ <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+ <script type="application/javascript"
+ src="chrome://mozapps/content/extensions/list.js"/>
+
+ <stringbundle id="extensionsBundle"
+ src="chrome://mozapps/locale/extensions/extensions.properties"/>
+ <stringbundle id="brandBundle"
+ src="chrome://branding/locale/brand.properties"/>
+
+ <hbox align="start">
+ <vbox>
+ <image id="infoIcon"/>
+ </vbox>
+ <vbox class="spaced" style="min-width: 20em; max-width: 40em">
+ <label id="message1" class="spaced" hidden="true"/>
+ <separator class="thin"/>
+ <tree id="addonsTree" rows="6" hidecolumnpicker="true" hidden="true" class="spaced">
+ <treecols style="max-width: 25em;">
+ <treecol flex="1" id="nameColumn" hideheader="true"/>
+ </treecols>
+ <treechildren id="addonsChildren"/>
+ </tree>
+ <label id="message2" class="spaced" hidden="true"/>
+ <label class="bold spaced" id="message3" hidden="true"/>
+ <hbox id="moreInfoBox" hidden="true">
+ <label id="moreInfo" class="text-link spaced"/>
+ <spacer flex="1"/>
+ </hbox>
+ </vbox>
+ </hbox>
+</dialog>
diff --git a/toolkit/mozapps/extensions/content/newaddon.js b/toolkit/mozapps/extensions/content/newaddon.js
new file mode 100644
index 000000000..2b2a54af5
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/newaddon.js
@@ -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/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+
+var gAddon = null;
+
+// If the user enables the add-on through some other UI close this window
+var EnableListener = {
+ onEnabling: function EnableListener_onEnabling(aAddon) {
+ if (aAddon.id == gAddon.id)
+ window.close();
+ }
+}
+AddonManager.addAddonListener(EnableListener);
+
+function initialize() {
+ // About URIs don't implement nsIURL so we have to find the query string
+ // manually
+ let spec = document.location.href;
+ let pos = spec.indexOf("?");
+ let query = "";
+ if (pos >= 0)
+ query = spec.substring(pos + 1);
+
+ // Just assume the query is "id=<id>"
+ let id = query.substring(3);
+ if (!id) {
+ window.location = "about:blank";
+ return;
+ }
+
+ let bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/newaddon.properties");
+
+ AddonManager.getAddonByID(id, function initialize_getAddonByID(aAddon) {
+ // If the add-on doesn't exist or it is already enabled or it cannot be
+ // enabled then this UI is useless, just close it. This shouldn't normally
+ // happen unless session restore restores the tab
+ if (!aAddon || !aAddon.userDisabled ||
+ !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) {
+ window.close();
+ return;
+ }
+
+ gAddon = aAddon;
+
+ document.getElementById("addon-info").setAttribute("type", aAddon.type);
+
+ let icon = document.getElementById("icon");
+ if (aAddon.icon64URL)
+ icon.src = aAddon.icon64URL;
+ else if (aAddon.iconURL)
+ icon.src = aAddon.iconURL;
+
+ let name = bundle.formatStringFromName("name", [aAddon.name, aAddon.version],
+ 2);
+ document.getElementById("name").value = name;
+
+ if (aAddon.creator) {
+ let creator = bundle.formatStringFromName("author", [aAddon.creator], 1);
+ document.getElementById("author").value = creator;
+ } else {
+ document.getElementById("author").hidden = true;
+ }
+
+ let uri = "getResourceURI" in aAddon ? aAddon.getResourceURI() : null;
+ let locationLabel = document.getElementById("location");
+ if (uri instanceof Ci.nsIFileURL) {
+ let location = bundle.formatStringFromName("location", [uri.file.path], 1);
+ locationLabel.value = location;
+ locationLabel.setAttribute("tooltiptext", location);
+ } else {
+ document.getElementById("location").hidden = true;
+ }
+
+ var event = document.createEvent("Events");
+ event.initEvent("AddonDisplayed", true, true);
+ document.dispatchEvent(event);
+ });
+}
+
+function unload() {
+ AddonManager.removeAddonListener(EnableListener);
+}
+
+function continueClicked() {
+ AddonManager.removeAddonListener(EnableListener);
+
+ if (document.getElementById("allow").checked) {
+ gAddon.userDisabled = false;
+
+ if (gAddon.pendingOperations & AddonManager.PENDING_ENABLE) {
+ document.getElementById("allow").disabled = true;
+ document.getElementById("buttonDeck").selectedPanel = document.getElementById("restartPanel");
+ return;
+ }
+ }
+
+ window.close();
+}
+
+function restartClicked() {
+ let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
+ createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+ "restart");
+ if (cancelQuit.data)
+ return; // somebody canceled our quit request
+
+ window.close();
+
+ let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
+ getService(Components.interfaces.nsIAppStartup);
+ appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+}
+
+function cancelClicked() {
+ gAddon.userDisabled = true;
+ AddonManager.addAddonListener(EnableListener);
+
+ document.getElementById("allow").disabled = false;
+ document.getElementById("buttonDeck").selectedPanel = document.getElementById("continuePanel");
+}
diff --git a/toolkit/mozapps/extensions/content/newaddon.xul b/toolkit/mozapps/extensions/content/newaddon.xul
new file mode 100644
index 000000000..0806f2799
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/newaddon.xul
@@ -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://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/newaddon.css"?>
+
+<!DOCTYPE page [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % newaddonDTD SYSTEM "chrome://mozapps/locale/extensions/newaddon.dtd">
+%newaddonDTD;
+]>
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xhtml="http://www.w3.org/1999/xhtml" title="&title;"
+ disablefastfind="true" id="addon-page" onload="initialize()"
+ onunload="unload()" role="application" align="stretch" pack="stretch">
+
+ <xhtml:link rel="shortcut icon" style="display: none"
+ href="chrome://mozapps/skin/extensions/extensionGeneric-16.png"/>
+
+ <script type="application/javascript"
+ src="chrome://mozapps/content/extensions/newaddon.js"/>
+
+ <scrollbox id="addon-scrollbox" align="center">
+ <spacer id="spacer-start"/>
+
+ <vbox id="addon-container" class="main-content">
+ <description>&intro;</description>
+
+ <hbox id="addon-info">
+ <image id="icon"/>
+ <vbox flex="1">
+ <label id="name"/>
+ <label id="author"/>
+ <label id="location" crop="end"/>
+ </vbox>
+ </hbox>
+
+ <hbox id="warning">
+ <image id="warning-icon"/>
+ <description flex="1">&warning;</description>
+ </hbox>
+
+ <checkbox id="allow" label="&allow;"/>
+ <description id="later">&later;</description>
+
+ <deck id="buttonDeck">
+ <hbox id="continuePanel">
+ <button id="continue-button" label="&continue;"
+ oncommand="continueClicked()"/>
+ </hbox>
+ <hbox id="restartPanel">
+ <spacer id="restartSpacer"/>
+ <description id="restartMessage" flex="1">&restartMessage;</description>
+ <button id="restart-button" label="&restartButton;" oncommand="restartClicked()"/>
+ <button id="cancel-button" label="&cancelButton;" oncommand="cancelClicked()"/>
+ </hbox>
+ </deck>
+ </vbox>
+
+ <spacer id="spacer-end"/>
+ </scrollbox>
+</page>
diff --git a/toolkit/mozapps/extensions/content/pluginPrefs.xul b/toolkit/mozapps/extensions/content/pluginPrefs.xul
new file mode 100644
index 000000000..c3fdbfa5b
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/pluginPrefs.xul
@@ -0,0 +1,20 @@
+<?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://pluginproblem/locale/pluginproblem.dtd">
+
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting type="control" title="&plugin.file;">
+ <label class="text-list" id="pluginLibraries"/>
+ </setting>
+ <setting type="control" title="&plugin.mimeTypes;">
+ <label class="text-list" id="pluginMimeTypes"/>
+ </setting>
+ <setting type="bool" pref="dom.ipc.plugins.flash.disable-protected-mode"
+ inverted="true" title="&plugin.flashProtectedMode.label;"
+ id="pluginEnableProtectedMode"
+ learnmore="https://support.mozilla.org/kb/flash-protected-mode-settings" />
+</vbox>
diff --git a/toolkit/mozapps/extensions/content/selectAddons.css b/toolkit/mozapps/extensions/content/selectAddons.css
new file mode 100644
index 000000000..636757721
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/selectAddons.css
@@ -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/. */
+
+#select .addon {
+ -moz-binding: url("chrome://mozapps/content/extensions/selectAddons.xml#addon-select");
+}
+
+#confirm .addon {
+ -moz-binding: url("chrome://mozapps/content/extensions/selectAddons.xml#addon-confirm");
+}
+
+#select-scrollbox,
+#confirm-scrollbox {
+ overflow-y: auto;
+ -moz-box-orient: vertical;
+}
+
+.addon:not([optionalupdate]) .addon-action-update,
+.addon[optionalupdate] .addon-action-message {
+ display: none;
+}
diff --git a/toolkit/mozapps/extensions/content/selectAddons.js b/toolkit/mozapps/extensions/content/selectAddons.js
new file mode 100644
index 000000000..33ab8a84e
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/selectAddons.js
@@ -0,0 +1,343 @@
+// -*- 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 strict";
+
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+var gView = null;
+
+function showView(aView) {
+ gView = aView;
+
+ gView.show();
+
+ // If the view's show method immediately showed a different view then don't
+ // do anything else
+ if (gView != aView)
+ return;
+
+ let viewNode = document.getElementById(gView.nodeID);
+ viewNode.parentNode.selectedPanel = viewNode;
+
+ // For testing dispatch an event when the view changes
+ var event = document.createEvent("Events");
+ event.initEvent("ViewChanged", true, true);
+ viewNode.dispatchEvent(event);
+}
+
+function showButtons(aCancel, aBack, aNext, aDone) {
+ document.getElementById("cancel").hidden = !aCancel;
+ document.getElementById("back").hidden = !aBack;
+ document.getElementById("next").hidden = !aNext;
+ document.getElementById("done").hidden = !aDone;
+}
+
+function isAddonDistroInstalled(aID) {
+ let branch = Services.prefs.getBranch("extensions.installedDistroAddon.");
+ if (!branch.prefHasUserValue(aID))
+ return false;
+
+ return branch.getBoolPref(aID);
+}
+
+function orderForScope(aScope) {
+ return aScope == AddonManager.SCOPE_PROFILE ? 1 : 0;
+}
+
+var gAddons = {};
+
+var gChecking = {
+ nodeID: "checking",
+
+ _progress: null,
+ _addonCount: 0,
+ _completeCount: 0,
+
+ show: function gChecking_show() {
+ showButtons(true, false, false, false);
+ this._progress = document.getElementById("checking-progress");
+
+ AddonManager.getAllAddons(aAddons => {
+ if (aAddons.length == 0) {
+ window.close();
+ return;
+ }
+
+ aAddons = aAddons.filter(function gChecking_filterAddons(aAddon) {
+ if (aAddon.type == "plugin" || aAddon.type == "service")
+ return false;
+
+ if (aAddon.type == "theme") {
+ // Don't show application shipped themes
+ if (aAddon.scope == AddonManager.SCOPE_APPLICATION)
+ return false;
+ // Don't show already disabled themes
+ if (aAddon.userDisabled)
+ return false;
+ }
+
+ return true;
+ });
+
+ this._addonCount = aAddons.length;
+ this._progress.value = 0;
+ this._progress.max = aAddons.length;
+ this._progress.mode = "determined";
+
+ AddonRepository.repopulateCache().then(() => {
+ for (let addonItem of aAddons) {
+ // Ignore disabled themes
+ if (addonItem.type != "theme" || !addonItem.userDisabled) {
+ gAddons[addonItem.id] = {
+ addon: addonItem,
+ install: null,
+ wasActive: addonItem.isActive
+ }
+ }
+
+ addonItem.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ }
+ });
+ });
+ },
+
+ onUpdateAvailable: function gChecking_onUpdateAvailable(aAddon, aInstall) {
+ // If the add-on can be upgraded then remember the new version
+ if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE)
+ gAddons[aAddon.id].install = aInstall;
+ },
+
+ onUpdateFinished: function gChecking_onUpdateFinished(aAddon, aError) {
+ this._completeCount++;
+ this._progress.value = this._completeCount;
+
+ if (this._completeCount < this._addonCount)
+ return;
+
+ var addons = [gAddons[id] for (id in gAddons)];
+
+ addons.sort(function sortAddons(a, b) {
+ let orderA = orderForScope(a.addon.scope);
+ let orderB = orderForScope(b.addon.scope);
+
+ if (orderA != orderB)
+ return orderA - orderB;
+
+ return String.localeCompare(a.addon.name, b.addon.name);
+ });
+
+ let rows = document.getElementById("select-rows");
+ let lastAddon = null;
+ for (let entry of addons) {
+ if (lastAddon &&
+ orderForScope(entry.addon.scope) != orderForScope(lastAddon.scope)) {
+ let separator = document.createElement("separator");
+ rows.appendChild(separator);
+ }
+
+ let row = document.createElement("row");
+ row.setAttribute("id", entry.addon.id);
+ row.setAttribute("class", "addon");
+ rows.appendChild(row);
+ row.setAddon(entry.addon, entry.install, entry.wasActive,
+ isAddonDistroInstalled(entry.addon.id));
+
+ if (entry.install)
+ entry.install.addListener(gUpdate);
+
+ lastAddon = entry.addon;
+ }
+
+ showView(gSelect);
+ }
+};
+
+var gSelect = {
+ nodeID: "select",
+
+ show: function gSelect_show() {
+ this.updateButtons();
+ },
+
+ updateButtons: function gSelect_updateButtons() {
+ for (let row = document.getElementById("select-rows").firstChild;
+ row; row = row.nextSibling) {
+ if (row.localName == "separator")
+ continue;
+
+ if (row.action) {
+ showButtons(false, false, true, false);
+ return;
+ }
+ }
+
+ showButtons(false, false, false, true);
+ },
+
+ next: function gSelect_next() {
+ showView(gConfirm);
+ },
+
+ done: function gSelect_done() {
+ window.close();
+ }
+};
+
+var gConfirm = {
+ nodeID: "confirm",
+
+ show: function gConfirm_show() {
+ showButtons(false, true, false, true);
+
+ let box = document.getElementById("confirm-scrollbox").firstChild;
+ while (box) {
+ box.hidden = true;
+ while (box.lastChild != box.firstChild)
+ box.removeChild(box.lastChild);
+ box = box.nextSibling;
+ }
+
+ for (let row = document.getElementById("select-rows").firstChild;
+ row; row = row.nextSibling) {
+ if (row.localName == "separator")
+ continue;
+
+ let action = row.action;
+ if (!action)
+ continue;
+
+ let list = document.getElementById(action + "-list");
+
+ list.hidden = false;
+ let item = document.createElement("hbox");
+ item.setAttribute("id", row._addon.id);
+ item.setAttribute("class", "addon");
+ item.setAttribute("type", row._addon.type);
+ item.setAttribute("name", row._addon.name);
+ if (action == "update" || action == "enable")
+ item.setAttribute("active", "true");
+ list.appendChild(item);
+
+ if (action == "update")
+ showButtons(false, true, true, false);
+ }
+ },
+
+ back: function gConfirm_back() {
+ showView(gSelect);
+ },
+
+ next: function gConfirm_next() {
+ showView(gUpdate);
+ },
+
+ done: function gConfirm_done() {
+ for (let row = document.getElementById("select-rows").firstChild;
+ row; row = row.nextSibling) {
+ if (row.localName != "separator")
+ row.apply();
+ }
+
+ window.close();
+ }
+}
+
+var gUpdate = {
+ nodeID: "update",
+
+ _progress: null,
+ _waitingCount: 0,
+ _completeCount: 0,
+ _errorCount: 0,
+
+ show: function gUpdate_show() {
+ showButtons(true, false, false, false);
+
+ this._progress = document.getElementById("update-progress");
+
+ for (let row = document.getElementById("select-rows").firstChild;
+ row; row = row.nextSibling) {
+ if (row.localName != "separator")
+ row.apply();
+ }
+
+ this._progress.mode = "determined";
+ this._progress.max = this._waitingCount;
+ this._progress.value = this._completeCount;
+ },
+
+ checkComplete: function gUpdate_checkComplete() {
+ this._progress.value = this._completeCount;
+ if (this._completeCount < this._waitingCount)
+ return;
+
+ if (this._errorCount > 0) {
+ showView(gErrors);
+ return;
+ }
+
+ window.close();
+ },
+
+ onDownloadStarted: function gUpdate_onDownloadStarted(aInstall) {
+ this._waitingCount++;
+ },
+
+ onDownloadFailed: function gUpdate_onDownloadFailed(aInstall) {
+ this._errorCount++;
+ this._completeCount++;
+ this.checkComplete();
+ },
+
+ onInstallFailed: function gUpdate_onInstallFailed(aInstall) {
+ this._errorCount++;
+ this._completeCount++;
+ this.checkComplete();
+ },
+
+ onInstallEnded: function gUpdate_onInstallEnded(aInstall) {
+ this._completeCount++;
+ this.checkComplete();
+ }
+};
+
+var gErrors = {
+ nodeID: "errors",
+
+ show: function gErrors_show() {
+ showButtons(false, false, false, true);
+ },
+
+ done: function gErrors_done() {
+ window.close();
+ }
+};
+
+window.addEventListener("load", function loadEventListener() {
+ showView(gChecking); }, false);
+
+// When closing the window cancel any pending or in-progress installs
+window.addEventListener("unload", function unloadEventListener() {
+ for (let id in gAddons) {
+ let entry = gAddons[id];
+ if (!entry.install)
+ return;
+
+ aEntry.install.removeListener(gUpdate);
+
+ if (entry.install.state != AddonManager.STATE_INSTALLED &&
+ entry.install.state != AddonManager.STATE_DOWNLOAD_FAILED &&
+ entry.install.state != AddonManager.STATE_INSTALL_FAILED) {
+ entry.install.cancel();
+ }
+ }
+}, false);
diff --git a/toolkit/mozapps/extensions/content/selectAddons.xml b/toolkit/mozapps/extensions/content/selectAddons.xml
new file mode 100644
index 000000000..dbfc0d400
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/selectAddons.xml
@@ -0,0 +1,235 @@
+<?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 [
+<!ENTITY % updateDTD SYSTEM "chrome://mozapps/locale/extensions/selectAddons.dtd">
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%updateDTD;
+%brandDTD;
+]>
+
+<bindings 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="addon-select">
+ <content>
+ <xul:hbox class="select-keep select-cell">
+ <xul:checkbox class="addon-keep-checkbox" anonid="keep"
+ xbl:inherits="tooltiptext=name"
+ oncommand="document.getBindingParent(this).keepChanged();"/>
+ </xul:hbox>
+ <xul:hbox class="select-icon select-cell">
+ <xul:image class="addon-icon" xbl:inherits="type"/>
+ </xul:hbox>
+ <xul:hbox class="select-name select-cell">
+ <xul:label class="addon-name" crop="end" style="&select.name.style;"
+ xbl:inherits="xbl:text=name"/>
+ </xul:hbox>
+ <xul:hbox class="select-action select-cell">
+ <xul:label class="addon-action-message" style="&select.action.style;"
+ xbl:inherits="xbl:text=action"/>
+ <xul:checkbox anonid="update" checked="true" class="addon-action-update"
+ oncommand="document.getBindingParent(this).updateChanged();"
+ style="&select.action.style;" xbl:inherits="label=action"/>
+ </xul:hbox>
+ <xul:hbox class="select-source select-cell">
+ <xul:label class="addon-source" xbl:inherits="xbl:text=source"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <field name="_addon"/>
+ <field name="_disabled"/>
+ <field name="_install"/>
+ <field name="_wasActive"/>
+ <field name="_keep">document.getAnonymousElementByAttribute(this, "anonid", "keep");</field>
+ <field name="_update">document.getAnonymousElementByAttribute(this, "anonid", "update");</field>
+ <field name="_strings">document.getElementById("strings");</field>
+
+ <property name="action" readonly="true">
+ <getter><![CDATA[
+ if (!this._keep.checked) {
+ if (this._wasActive)
+ return "disable";
+ else
+ return null;
+ }
+
+ if (this._addon.appDisabled && !this._install)
+ return "incompatible";
+
+ if (this._install && (AddonManager.shouldAutoUpdate(this._addon) || this._update.checked))
+ return "update";
+
+ if (this._addon.permissions & AddonManager.PERM_CAN_ENABLE)
+ return "enable";
+
+ return null;
+ ]]></getter>
+ </property>
+
+ <method name="setAddon">
+ <parameter name="aAddon"/>
+ <parameter name="aInstall"/>
+ <parameter name="aWasActive"/>
+ <parameter name="aDistroInstalled"/>
+ <body><![CDATA[
+ this._addon = aAddon;
+ this._install = aInstall;
+ this._wasActive = aWasActive;
+
+ this.setAttribute("name", aAddon.name);
+ this.setAttribute("type", aAddon.type);
+
+ // User and bundled add-ons default to staying enabled,
+ // others default to disabled.
+ switch (aAddon.scope) {
+ case AddonManager.SCOPE_PROFILE:
+ this._keep.checked = !aAddon.userDisabled;
+ if (aDistroInstalled)
+ this.setAttribute("source", this._strings.getString("source.bundled"));
+ else
+ this.setAttribute("source", this._strings.getString("source.profile"));
+ break;
+ default:
+ this._keep.checked = false;
+ this.setAttribute("source", this._strings.getString("source.other"));
+ }
+
+ this.updateAction();
+ ]]></body>
+ </method>
+
+ <method name="setActionMessage">
+ <body><![CDATA[
+ if (!this._keep.checked) {
+ // If the user no longer wants this add-on then it is either being
+ // disabled or nothing is changing. Don't complicate matters by
+ // talking about updates for this case
+
+ if (this._wasActive)
+ this.setAttribute("action", this._strings.getString("action.disabled"));
+ else
+ this.setAttribute("action", "");
+
+ this.removeAttribute("optionalupdate");
+ return;
+ }
+
+ if (this._addon.appDisabled && !this._install) {
+ // If the add-on is incompatible and there is no update available
+ // then it will remain disabled
+
+ this.setAttribute("action", this._strings.getString("action.incompatible"));
+
+ this.removeAttribute("optionalupdate");
+ return;
+ }
+
+ if (this._install) {
+ if (!AddonManager.shouldAutoUpdate(this._addon)) {
+ this.setAttribute("optionalupdate", "true");
+
+ // If manual updating for the add-on then display the right
+ // text depending on whether the update is required to make
+ // the add-on work or not
+ if (this._addon.appDisabled)
+ this.setAttribute("action", this._strings.getString("action.neededupdate"));
+ else
+ this.setAttribute("action", this._strings.getString("action.unneededupdate"));
+ return;
+ }
+
+ this.removeAttribute("optionalupdate");
+
+ // If the update is needed to make the add-on compatible then
+ // say so otherwise just say nothing about it
+ if (this._addon.appDisabled) {
+ this.setAttribute("action", this._strings.getString("action.autoupdate"));
+ return;
+ }
+ }
+ else {
+ this.removeAttribute("optionalupdate");
+ }
+
+ // If the add-on didn't used to be active and it now is (via a
+ // compatibility update) or it can be enabled then the action is to
+ // enable the add-on
+ if (!this._wasActive && (this._addon.isActive || this._addon.permissions & AddonManager.PERM_CAN_ENABLE)) {
+ this.setAttribute("action", this._strings.getString("action.enabled"));
+ return;
+ }
+
+ // In all other cases the add-on is simply remaining enabled
+ this.setAttribute("action", "");
+ ]]></body>
+ </method>
+
+ <method name="updateAction">
+ <body><![CDATA[
+ this.setActionMessage();
+ let installingUpdate = this._install &&
+ (AddonManager.shouldAutoUpdate(this._addon) ||
+ this._update.checked);
+
+ if (this._keep.checked && (!this._addon.appDisabled || installingUpdate))
+ this.setAttribute("active", "true");
+ else
+ this.removeAttribute("active");
+
+ gSelect.updateButtons();
+ ]]></body>
+ </method>
+
+ <method name="updateChanged">
+ <body><![CDATA[
+ this.updateAction();
+ ]]></body>
+ </method>
+
+ <method name="keepChanged">
+ <body><![CDATA[
+ this.updateAction();
+ ]]></body>
+ </method>
+
+ <method name="keep">
+ <body><![CDATA[
+ this._keep.checked = true;
+ this.keepChanged();
+ ]]></body>
+ </method>
+
+ <method name="disable">
+ <body><![CDATA[
+ this._keep.checked = false;
+ this.keepChanged();
+ ]]></body>
+ </method>
+
+ <method name="apply">
+ <body><![CDATA[
+ this._addon.userDisabled = !this._keep.checked;
+
+ if (!this._install || !this._keep.checked)
+ return;
+
+ if (AddonManager.shouldAutoUpdate(this._addon) || this._update.checked)
+ this._install.install();
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="addon-confirm">
+ <content>
+ <xul:image class="addon-icon" xbl:inherits="type"/>
+ <xul:label class="addon-name" xbl:inherits="xbl:text=name"/>
+ </content>
+ </binding>
+</bindings>
diff --git a/toolkit/mozapps/extensions/content/selectAddons.xul b/toolkit/mozapps/extensions/content/selectAddons.xul
new file mode 100644
index 000000000..0fa292ecf
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/selectAddons.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://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/content/extensions/selectAddons.css" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/selectAddons.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % updateDTD SYSTEM "chrome://mozapps/locale/extensions/selectAddons.dtd">
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%updateDTD;
+%brandDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="&upgrade.style;" id="select-window">
+
+ <script type="application/javascript" src="chrome://mozapps/content/extensions/selectAddons.js"/>
+ <stringbundle id="strings" src="chrome://mozapps/locale/extensions/selectAddons.properties"/>
+
+ <deck id="view-deck" flex="1" align="stretch" pack="stretch">
+ <vbox id="checking" align="stretch">
+ <vbox flex="1">
+ <label id="checking-heading" class="heading">&checking.heading;</label>
+ </vbox>
+ <progressmeter id="checking-progress" class="progress" mode="undetermined"/>
+ <vbox flex="1">
+ <label id="checking-progress-label" class="progress-label">&checking.progress.label;</label>
+ </vbox>
+ </vbox>
+
+ <vbox id="select" align="stretch">
+ <label id="select-heading" class="heading">&select.heading;</label>
+
+ <description id="select-description">&select.description;</description>
+
+ <vbox id="select-list" align="stretch" flex="1">
+ <hbox id="select-header">
+ <hbox class="select-keep select-cell" style="&select.keep.style;">
+ <label value="&select.keep;"/>
+ </hbox>
+ <hbox class="select-icon select-cell"/>
+ <hbox id="heading-name" class="select-name select-cell" style="&select.name.style;">
+ <label value="&select.name;"/>
+ </hbox>
+ <hbox id="heading-action" class="select-action select-cell" style="&select.action.style;">
+ <label value="&select.action;"/>
+ </hbox>
+ <hbox class="select-source select-cell" flex="1">
+ <label value="&select.source;"/>
+ </hbox>
+ </hbox>
+
+ <scrollbox id="select-scrollbox" flex="1">
+ <grid id="select-grid" flex="1">
+ <columns>
+ <column style="&select.keep.style;"/>
+ <column/>
+ <column id="column-name"/>
+ <column id="column-action" class="select-action"/>
+ <column class="select-source" flex="1"/>
+ </columns>
+
+ <rows id="select-rows"/>
+ </grid>
+ </scrollbox>
+ </vbox>
+ </vbox>
+
+ <vbox id="confirm" align="stretch">
+ <label id="confirm-heading" class="heading">&confirm.heading;</label>
+
+ <description id="confirm-description">&confirm.description;</description>
+
+ <scrollbox id="confirm-scrollbox" flex="1">
+ <vbox id="disable-list" class="action-list" hidden="true">
+ <label class="action-header">&action.disable.heading;</label>
+ </vbox>
+
+ <vbox id="incompatible-list" class="action-list" hidden="true">
+ <label class="action-header">&action.incompatible.heading;</label>
+ </vbox>
+
+ <vbox id="update-list" class="action-list" hidden="true">
+ <label class="action-header">&action.update.heading;</label>
+ </vbox>
+
+ <vbox id="enable-list" class="action-list" hidden="true">
+ <label class="action-header">&action.enable.heading;</label>
+ </vbox>
+ </scrollbox>
+ </vbox>
+
+ <vbox id="update" align="stretch">
+ <vbox flex="1">
+ <label id="update-heading" class="heading">&update.heading;</label>
+ </vbox>
+ <progressmeter id="update-progress" class="progress" mode="undetermined"/>
+ <vbox flex="1">
+ <label id="update-progress-label" class="progress-label">&update.progress.label;</label>
+ </vbox>
+ </vbox>
+
+ <vbox id="errors">
+ <vbox flex="1">
+ <label id="errors-heading" class="heading">&errors.heading;</label>
+ </vbox>
+ <description id="errors-description" value="&errors.description;"/>
+ <spacer flex="1"/>
+ </vbox>
+ </deck>
+
+ <hbox id="footer" align="center">
+ <label id="footer-label" flex="1">&footer.label;</label>
+ <button id="cancel" label="&cancel.label;" oncommand="window.close()"/>
+ <button id="back" label="&back.label;" oncommand="gView.back()" hidden="true"/>
+ <button id="next" label="&next.label;" oncommand="gView.next()" hidden="true"/>
+ <button id="done" label="&done.label;" oncommand="gView.done()" hidden="true"/>
+ </hbox>
+
+</window>
diff --git a/toolkit/mozapps/extensions/content/setting.xml b/toolkit/mozapps/extensions/content/setting.xml
new file mode 100644
index 000000000..c4eae1fd3
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/setting.xml
@@ -0,0 +1,508 @@
+<?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 page [
+<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
+%extensionsDTD;
+]>
+
+<bindings 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="settings">
+ <content orient="vertical">
+ <xul:label class="settings-title" xbl:inherits="xbl:text=label" flex="1"/>
+ <children/>
+ </content>
+ </binding>
+
+ <binding id="setting-base">
+ <implementation>
+ <constructor><![CDATA[
+ this.preferenceChanged();
+
+ this.addEventListener("keypress", function(event) {
+ event.stopPropagation();
+ }, false);
+
+ if (this.usePref)
+ Services.prefs.addObserver(this.pref, this._observer, true);
+ ]]></constructor>
+
+ <field name="_observer"><![CDATA[({
+ _self: this,
+
+ QueryInterface: function(aIID) {
+ const Ci = Components.interfaces;
+ if (aIID.equals(Ci.nsIObserver) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE);
+ },
+
+ observe: function(aSubject, aTopic, aPrefName) {
+ if (aTopic != "nsPref:changed")
+ return;
+
+ if (this._self.pref == aPrefName)
+ this._self.preferenceChanged();
+ }
+ })]]>
+ </field>
+
+ <method name="fireEvent">
+ <parameter name="eventName"/>
+ <parameter name="funcStr"/>
+ <body>
+ <![CDATA[
+ let body = funcStr || this.getAttribute(eventName);
+ if (!body)
+ return;
+
+ try {
+ let event = document.createEvent("Events");
+ event.initEvent(eventName, true, true);
+ let f = new Function("event", body);
+ f.call(this, event);
+ }
+ catch (e) {
+ Cu.reportError(e);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ // Should be code to set the from the preference input.value
+ throw Components.Exception("No valueFromPreference implementation",
+ Components.results.NS_ERROR_NOT_IMPLEMENTED);
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ // Should be code to set the input.value from the preference
+ throw Components.Exception("No valueToPreference implementation",
+ Components.results.NS_ERROR_NOT_IMPLEMENTED);
+ ]]>
+ </body>
+ </method>
+
+ <method name="inputChanged">
+ <body>
+ <![CDATA[
+ if (this.usePref && !this._updatingInput) {
+ this.valueToPreference();
+ this.fireEvent("oninputchanged");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="preferenceChanged">
+ <body>
+ <![CDATA[
+ if (this.usePref) {
+ this._updatingInput = true;
+ try {
+ this.valueFromPreference();
+ this.fireEvent("onpreferencechanged");
+ } catch (e) {}
+ this._updatingInput = false;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="usePref" readonly="true" onget="return this.hasAttribute('pref');"/>
+ <property name="pref" readonly="true" onget="return this.getAttribute('pref');"/>
+ <property name="type" readonly="true" onget="return this.getAttribute('type');"/>
+ <property name="value" onget="return this.input.value;" onset="return this.input.value = val;"/>
+
+ <field name="_updatingInput">false</field>
+ <field name="input">document.getAnonymousElementByAttribute(this, "anonid", "input");</field>
+ <field name="settings">
+ this.parentNode.localName == "settings" ? this.parentNode : null;
+ </field>
+ </implementation>
+ </binding>
+
+ <binding id="setting-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ <xul:label class="preferences-learnmore text-link"
+ onclick="document.getBindingParent(this).openLearnMore()">&setting.learnmore;</xul:label>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:checkbox anonid="input" xbl:inherits="disabled,onlabel,offlabel,label=checkboxlabel" oncommand="inputChanged();"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ let val = Services.prefs.getBoolPref(this.pref);
+ this.value = this.inverted ? !val : val;
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ let val = this.value;
+ Services.prefs.setBoolPref(this.pref, this.inverted ? !val : val);
+ ]]>
+ </body>
+ </method>
+
+ <property name="value" onget="return this.input.checked;" onset="return this.input.setChecked(val);"/>
+ <property name="inverted" readonly="true" onget="return this.getAttribute('inverted');"/>
+
+ <method name="openLearnMore">
+ <body>
+ <![CDATA[
+ window.open(this.getAttribute("learnmore"), "_blank");
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="setting-boolint" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
+ <implementation>
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ let val = Services.prefs.getIntPref(this.pref);
+ this.value = (val == this.getAttribute("on"));
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ Services.prefs.setIntPref(this.pref, this.getAttribute(this.value ? "on" : "off"));
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="setting-localized-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
+ <implementation>
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ let val = Services.prefs.getComplexValue(this.pref, Components.interfaces.nsIPrefLocalizedString).data;
+ if(this.inverted) val = !val;
+ this.value = (val == "true");
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ let val = this.value;
+ if(this.inverted) val = !val;
+ let pref = Components.classes["@mozilla.org/pref-localizedstring;1"].createInstance(Components.interfaces.nsIPrefLocalizedString);
+ pref.data = this.inverted ? (!val).toString() : val.toString();
+ Services.prefs.setComplexValue(this.pref, Components.interfaces.nsIPrefLocalizedString, pref);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="setting-integer" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:textbox type="number" anonid="input" oninput="inputChanged();" onchange="inputChanged();"
+ xbl:inherits="disabled,emptytext,min,max,increment,hidespinbuttons,wraparound,size"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ let val = Services.prefs.getIntPref(this.pref);
+ this.value = val;
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ Services.prefs.setIntPref(this.pref, this.value);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="setting-control" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <children/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="setting-string" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:textbox anonid="input" flex="1" oninput="inputChanged();"
+ xbl:inherits="disabled,emptytext,type=inputtype,min,max,increment,hidespinbuttons,decimalplaces,wraparound"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ const nsISupportsString = Components.interfaces.nsISupportsString;
+ this.value = Services.prefs.getComplexValue(this.pref, nsISupportsString).data;
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ const nsISupportsString = Components.interfaces.nsISupportsString;
+ let iss = Components.classes["@mozilla.org/supports-string;1"].createInstance(nsISupportsString);
+ iss.data = this.value;
+ Services.prefs.setComplexValue(this.pref, nsISupportsString, iss);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="setting-color" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:colorpicker type="button" anonid="input" xbl:inherits="disabled" onchange="document.getBindingParent(this).inputChanged();"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ // We must wait for the colorpicker's binding to be applied before setting the value
+ if (!this.input.color)
+ this.input.initialize();
+ this.value = Services.prefs.getCharPref(this.pref);
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ Services.prefs.setCharPref(this.pref, this.value);
+ ]]>
+ </body>
+ </method>
+
+ <property name="value" onget="return this.input.color;" onset="return this.input.color = val;"/>
+ </implementation>
+ </binding>
+
+ <binding id="setting-path" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:button type="button" anonid="button" label="&settings.path.button.label;" xbl:inherits="disabled" oncommand="showPicker();"/>
+ <xul:label anonid="input" flex="1" crop="center" xbl:inherits="disabled"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="showPicker">
+ <body>
+ <![CDATA[
+ var filePicker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ filePicker.init(window, this.getAttribute("title"),
+ this.type == "file" ? Ci.nsIFilePicker.modeOpen : Ci.nsIFilePicker.modeGetFolder);
+ if (this.value) {
+ try {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(this.value);
+ filePicker.displayDirectory = this.type == "file" ? file.parent : file;
+ if (this.type == "file") {
+ filePicker.defaultString = file.leafName;
+ }
+ } catch (e) {}
+ }
+ if (filePicker.show() != Ci.nsIFilePicker.returnCancel) {
+ this.value = filePicker.file.path;
+ this.inputChanged();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ this.value = Services.prefs.getCharPref(this.pref);
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ Services.prefs.setCharPref(this.pref, this.value);
+ ]]>
+ </body>
+ </method>
+
+ <field name="_value"></field>
+
+ <property name="value">
+ <getter>
+ <![CDATA[
+ return this._value;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ this._value = val;
+ let label = "";
+ if (val) {
+ try {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(val);
+ label = this.hasAttribute("fullpath") ? file.path : file.leafName;
+ } catch (e) {}
+ }
+ this.input.tooltipText = val;
+ return this.input.value = label;
+ ]]>
+ </setter>
+ </property>
+ </implementation>
+ </binding>
+
+ <binding id="setting-multi" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <children includes="radiogroup|menulist"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.control.addEventListener("command", this.inputChanged.bind(this), false);
+ ]]>
+ </constructor>
+
+ <method name="valueFromPreference">
+ <body>
+ <![CDATA[
+ let val;
+ switch (Services.prefs.getPrefType(this.pref)) {
+ case Ci.nsIPrefBranch.PREF_STRING:
+ val = Services.prefs.getCharPref(this.pref);
+ break;
+ case Ci.nsIPrefBranch.PREF_INT:
+ val = Services.prefs.getIntPref(this.pref);
+ break;
+ case Ci.nsIPrefBranch.PREF_BOOL:
+ val = Services.prefs.getBoolPref(this.pref).toString();
+ break;
+ default:
+ return;
+ }
+
+ if ("itemCount" in this.control) {
+ for (let i = 0; i < this.control.itemCount; i++) {
+ if (this.control.getItemAtIndex(i).value == val) {
+ this.control.selectedIndex = i;
+ break;
+ }
+ }
+ } else {
+ this.control.setAttribute("value", val);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="valueToPreference">
+ <body>
+ <![CDATA[
+ // We might not have a pref already set, so we guess the type from the value attribute
+ let val = this.control.selectedItem.value;
+ if (val == "true" || val == "false")
+ Services.prefs.setBoolPref(this.pref, val == "true");
+ else if (/^-?\d+$/.test(val))
+ Services.prefs.setIntPref(this.pref, val);
+ else
+ Services.prefs.setCharPref(this.pref, val);
+ ]]>
+ </body>
+ </method>
+
+ <field name="control">this.getElementsByTagName(this.getAttribute("type") == "radio" ? "radiogroup" : "menulist")[0];</field>
+ </implementation>
+ </binding>
+</bindings>
diff --git a/toolkit/mozapps/extensions/content/update.js b/toolkit/mozapps/extensions/content/update.js
new file mode 100644
index 000000000..4e44b4f5e
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/update.js
@@ -0,0 +1,663 @@
+// -*- 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 UI is only opened from the Extension Manager when the app is upgraded.
+
+"use strict";
+
+const PREF_UPDATE_EXTENSIONS_ENABLED = "extensions.update.enabled";
+const PREF_XPINSTALL_ENABLED = "xpinstall.enabled";
+
+// timeout (in milliseconds) to wait for response to the metadata ping
+const METADATA_TIMEOUT = 30000;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", "resource://gre/modules/addons/AddonRepository.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
+let logger = null;
+
+var gUpdateWizard = {
+ // When synchronizing app compatibility info this contains all installed
+ // add-ons. When checking for compatible versions this contains only
+ // incompatible add-ons.
+ addons: [],
+ // Contains a Set of IDs for add-on that were disabled by the application update.
+ affectedAddonIDs: null,
+ // The add-ons that we found updates available for
+ addonsToUpdate: [],
+ shouldSuggestAutoChecking: false,
+ shouldAutoCheck: false,
+ xpinstallEnabled: true,
+ xpinstallLocked: false,
+ // cached AddonInstall entries for add-ons we might want to update,
+ // keyed by add-on ID
+ addonInstalls: new Map(),
+ shuttingDown: false,
+ // Count the add-ons disabled by this update, enabled/disabled by
+ // metadata checks, and upgraded.
+ disabled: 0,
+ metadataEnabled: 0,
+ metadataDisabled: 0,
+ upgraded: 0,
+ upgradeFailed: 0,
+ upgradeDeclined: 0,
+
+ init: function gUpdateWizard_init()
+ {
+ logger = Log.repository.getLogger("addons.update-dialog");
+ // XXX could we pass the addons themselves rather than the IDs?
+ this.affectedAddonIDs = new Set(window.arguments[0]);
+
+ try {
+ this.shouldSuggestAutoChecking =
+ !Services.prefs.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
+ }
+ catch (e) {
+ }
+
+ try {
+ this.xpinstallEnabled = Services.prefs.getBoolPref(PREF_XPINSTALL_ENABLED);
+ this.xpinstallLocked = Services.prefs.prefIsLocked(PREF_XPINSTALL_ENABLED);
+ }
+ catch (e) {
+ }
+
+ if (Services.io.offline)
+ document.documentElement.currentPage = document.getElementById("offline");
+ else
+ document.documentElement.currentPage = document.getElementById("versioninfo");
+ },
+
+ onWizardFinish: function gUpdateWizard_onWizardFinish ()
+ {
+ if (this.shouldSuggestAutoChecking)
+ Services.prefs.setBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED, this.shouldAutoCheck);
+ },
+
+ _setUpButton: function gUpdateWizard_setUpButton(aButtonID, aButtonKey, aDisabled)
+ {
+ var strings = document.getElementById("updateStrings");
+ var button = document.documentElement.getButton(aButtonID);
+ if (aButtonKey) {
+ button.label = strings.getString(aButtonKey);
+ try {
+ button.setAttribute("accesskey", strings.getString(aButtonKey + "Accesskey"));
+ }
+ catch (e) {
+ }
+ }
+ button.disabled = aDisabled;
+ },
+
+ setButtonLabels: function gUpdateWizard_setButtonLabels(aBackButton, aBackButtonIsDisabled,
+ aNextButton, aNextButtonIsDisabled,
+ aCancelButton, aCancelButtonIsDisabled)
+ {
+ this._setUpButton("back", aBackButton, aBackButtonIsDisabled);
+ this._setUpButton("next", aNextButton, aNextButtonIsDisabled);
+ this._setUpButton("cancel", aCancelButton, aCancelButtonIsDisabled);
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Update Errors
+ errorItems: [],
+
+ checkForErrors: function gUpdateWizard_checkForErrors(aElementIDToShow)
+ {
+ if (this.errorItems.length > 0)
+ document.getElementById(aElementIDToShow).hidden = false;
+ },
+
+ onWizardClose: function gUpdateWizard_onWizardClose(aEvent)
+ {
+ return this.onWizardCancel();
+ },
+
+ onWizardCancel: function gUpdateWizard_onWizardCancel()
+ {
+ gUpdateWizard.shuttingDown = true;
+ // Allow add-ons to continue downloading and installing
+ // in the background, though some may require a later restart
+ // Pages that are waiting for user input go into the background
+ // on cancel
+ if (gMismatchPage.waiting) {
+ logger.info("Dialog closed in mismatch page");
+ if (gUpdateWizard.addonInstalls.size > 0) {
+ gInstallingPage.startInstalls([i for ([, i] of gUpdateWizard.addonInstalls)]);
+ }
+ return true;
+ }
+
+ // Pages that do asynchronous things will just keep running and check
+ // gUpdateWizard.shuttingDown to trigger background behaviour
+ if (!gInstallingPage.installing) {
+ logger.info("Dialog closed while waiting for updated compatibility information");
+ }
+ else {
+ logger.info("Dialog closed while downloading and installing updates");
+ }
+ return true;
+ }
+};
+
+var gOfflinePage = {
+ onPageAdvanced: function gOfflinePage_onPageAdvanced()
+ {
+ Services.io.offline = false;
+ return true;
+ },
+
+ toggleOffline: function gOfflinePage_toggleOffline()
+ {
+ var nextbtn = document.documentElement.getButton("next");
+ nextbtn.disabled = !nextbtn.disabled;
+ }
+}
+
+// Addon listener to count addons enabled/disabled by metadata checks
+let listener = {
+ onDisabled: function listener_onDisabled(aAddon) {
+ gUpdateWizard.affectedAddonIDs.add(aAddon.id);
+ gUpdateWizard.metadataDisabled++;
+ },
+ onEnabled: function listener_onEnabled(aAddon) {
+ gUpdateWizard.affectedAddonIDs.delete(aAddon.id);
+ gUpdateWizard.metadataEnabled++;
+ }
+};
+
+var gVersionInfoPage = {
+ _completeCount: 0,
+ _totalCount: 0,
+ _versionInfoDone: false,
+ onPageShow: Task.async(function* gVersionInfoPage_onPageShow() {
+ gUpdateWizard.setButtonLabels(null, true,
+ "nextButtonText", true,
+ "cancelButtonText", false);
+
+ gUpdateWizard.disabled = gUpdateWizard.affectedAddonIDs.size;
+
+ // Ensure compatibility overrides are up to date before checking for
+ // individual addon updates.
+ AddonManager.addAddonListener(listener);
+ if (AddonRepository.isMetadataStale()) {
+ // Do the metadata ping, listening for any newly enabled/disabled add-ons.
+ yield AddonRepository.repopulateCache(METADATA_TIMEOUT);
+ if (gUpdateWizard.shuttingDown) {
+ logger.debug("repopulateCache completed after dialog closed");
+ }
+ }
+ // Fetch the add-ons that are still affected by this update.
+ let idlist = [id for (id of gUpdateWizard.affectedAddonIDs)];
+ if (idlist.length < 1) {
+ gVersionInfoPage.onAllUpdatesFinished();
+ return;
+ }
+
+ logger.debug("Fetching affected addons " + idlist.toSource());
+ let fetchedAddons = yield new Promise((resolve, reject) =>
+ AddonManager.getAddonsByIDs(idlist, resolve));
+ // We shouldn't get nulls here, but let's be paranoid...
+ gUpdateWizard.addons = [a for (a of fetchedAddons) if (a)];
+ if (gUpdateWizard.addons.length < 1) {
+ gVersionInfoPage.onAllUpdatesFinished();
+ return;
+ }
+
+ gVersionInfoPage._totalCount = gUpdateWizard.addons.length;
+
+ for (let addon of gUpdateWizard.addons) {
+ logger.debug("VersionInfo Finding updates for ${id}", addon);
+ addon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ }
+ }),
+
+ onAllUpdatesFinished: function gVersionInfoPage_onAllUpdatesFinished() {
+ AddonManager.removeAddonListener(listener);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_disabled",
+ gUpdateWizard.disabled);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_enabled",
+ gUpdateWizard.metadataEnabled);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_disabled",
+ gUpdateWizard.metadataDisabled);
+ // Record 0 for these here in case we exit early; values will be replaced
+ // later if we actually upgrade any.
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded", 0);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed", 0);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined", 0);
+ // Filter out any add-ons that are now enabled.
+ logger.debug("VersionInfo updates finished: found " +
+ [addon.id + ":" + addon.appDisabled for (addon of gUpdateWizard.addons)].toSource());
+ let filteredAddons = [];
+ for (let a of gUpdateWizard.addons) {
+ if (a.appDisabled) {
+ logger.debug("Continuing with add-on " + a.id);
+ filteredAddons.push(a);
+ }
+ else if (gUpdateWizard.addonInstalls.has(a.id)) {
+ gUpdateWizard.addonInstalls.get(a.id).cancel();
+ gUpdateWizard.addonInstalls.delete(a.id);
+ }
+ }
+ gUpdateWizard.addons = filteredAddons;
+
+ if (gUpdateWizard.shuttingDown) {
+ // jump directly to updating auto-update add-ons in the background
+ if (gUpdateWizard.addonInstalls.size > 0) {
+ gInstallingPage.startInstalls([i for ([, i] of gUpdateWizard.addonInstalls)]);
+ }
+ return;
+ }
+
+ if (filteredAddons.length > 0) {
+ if (!gUpdateWizard.xpinstallEnabled && gUpdateWizard.xpinstallLocked) {
+ document.documentElement.currentPage = document.getElementById("adminDisabled");
+ return;
+ }
+ document.documentElement.currentPage = document.getElementById("mismatch");
+ }
+ else {
+ logger.info("VersionInfo: No updates require further action");
+ // VersionInfo compatibility updates resolved all compatibility problems,
+ // close this window and continue starting the application...
+ //XXX Bug 314754 - We need to use setTimeout to close the window due to
+ // the EM using xmlHttpRequest when checking for updates.
+ setTimeout(close, 0);
+ }
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ // UpdateListener
+ onUpdateFinished: function gVersionInfoPage_onUpdateFinished(aAddon, status) {
+ ++this._completeCount;
+
+ if (status != AddonManager.UPDATE_STATUS_NO_ERROR) {
+ logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
+ " failed for " + aAddon.id + ": " + status);
+ gUpdateWizard.errorItems.push(aAddon);
+ }
+ else {
+ logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
+ " finished for " + aAddon.id);
+ }
+
+ // If we're not in the background, just make a list of add-ons that have
+ // updates available
+ if (!gUpdateWizard.shuttingDown) {
+ // If we're still in the update check window and the add-on is now active
+ // then it won't have been disabled by startup
+ if (aAddon.active) {
+ AddonManagerPrivate.removeStartupChange(AddonManager.STARTUP_CHANGE_DISABLED, aAddon.id);
+ gUpdateWizard.metadataEnabled++;
+ }
+
+ // Update the status text and progress bar
+ var updateStrings = document.getElementById("updateStrings");
+ var statusElt = document.getElementById("versioninfo.status");
+ var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
+ statusElt.setAttribute("value", statusString);
+
+ // Update the status text and progress bar
+ var progress = document.getElementById("versioninfo.progress");
+ progress.mode = "normal";
+ progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
+ }
+
+ if (this._completeCount == this._totalCount)
+ this.onAllUpdatesFinished();
+ },
+
+ onUpdateAvailable: function gVersionInfoPage_onUpdateAvailable(aAddon, aInstall) {
+ logger.debug("VersionInfo got an install for " + aAddon.id + ": " + aAddon.version);
+ gUpdateWizard.addonInstalls.set(aAddon.id, aInstall);
+ },
+};
+
+var gMismatchPage = {
+ waiting: false,
+
+ onPageShow: function gMismatchPage_onPageShow()
+ {
+ gMismatchPage.waiting = true;
+ gUpdateWizard.setButtonLabels(null, true,
+ "mismatchCheckNow", false,
+ "mismatchDontCheck", false);
+ document.documentElement.getButton("next").focus();
+
+ var incompatible = document.getElementById("mismatch.incompatible");
+ for (let addon of gUpdateWizard.addons) {
+ var listitem = document.createElement("listitem");
+ listitem.setAttribute("label", addon.name + " " + addon.version);
+ incompatible.appendChild(listitem);
+ }
+ }
+};
+
+var gUpdatePage = {
+ _totalCount: 0,
+ _completeCount: 0,
+ onPageShow: function gUpdatePage_onPageShow()
+ {
+ gMismatchPage.waiting = false;
+ gUpdateWizard.setButtonLabels(null, true,
+ "nextButtonText", true,
+ "cancelButtonText", false);
+ document.documentElement.getButton("next").focus();
+
+ gUpdateWizard.errorItems = [];
+
+ this._totalCount = gUpdateWizard.addons.length;
+ for (let addon of gUpdateWizard.addons) {
+ logger.debug("UpdatePage requesting update for " + addon.id);
+ // Redundant call to find updates again here when we already got them
+ // in the VersionInfo page: https://bugzilla.mozilla.org/show_bug.cgi?id=960597
+ addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ }
+ },
+
+ onAllUpdatesFinished: function gUpdatePage_onAllUpdatesFinished() {
+ if (gUpdateWizard.shuttingDown)
+ return;
+
+ var nextPage = document.getElementById("noupdates");
+ if (gUpdateWizard.addonsToUpdate.length > 0)
+ nextPage = document.getElementById("found");
+ document.documentElement.currentPage = nextPage;
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ // UpdateListener
+ onUpdateAvailable: function gUpdatePage_onUpdateAvailable(aAddon, aInstall) {
+ logger.debug("UpdatePage got an update for " + aAddon.id + ": " + aAddon.version);
+ gUpdateWizard.addonsToUpdate.push(aInstall);
+ },
+
+ onUpdateFinished: function gUpdatePage_onUpdateFinished(aAddon, status) {
+ if (status != AddonManager.UPDATE_STATUS_NO_ERROR)
+ gUpdateWizard.errorItems.push(aAddon);
+
+ ++this._completeCount;
+
+ if (!gUpdateWizard.shuttingDown) {
+ // Update the status text and progress bar
+ var updateStrings = document.getElementById("updateStrings");
+ var statusElt = document.getElementById("checking.status");
+ var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
+ statusElt.setAttribute("value", statusString);
+
+ var progress = document.getElementById("checking.progress");
+ progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
+ }
+
+ if (this._completeCount == this._totalCount)
+ this.onAllUpdatesFinished()
+ },
+};
+
+var gFoundPage = {
+ onPageShow: function gFoundPage_onPageShow()
+ {
+ gUpdateWizard.setButtonLabels(null, true,
+ "installButtonText", false,
+ null, false);
+
+ var foundUpdates = document.getElementById("found.updates");
+ var itemCount = gUpdateWizard.addonsToUpdate.length;
+ for (let install of gUpdateWizard.addonsToUpdate) {
+ let listItem = foundUpdates.appendItem(install.name + " " + install.version);
+ listItem.setAttribute("type", "checkbox");
+ listItem.setAttribute("checked", "true");
+ listItem.install = install;
+ }
+
+ if (!gUpdateWizard.xpinstallEnabled) {
+ document.getElementById("xpinstallDisabledAlert").hidden = false;
+ document.getElementById("enableXPInstall").focus();
+ document.documentElement.getButton("next").disabled = true;
+ }
+ else {
+ document.documentElement.getButton("next").focus();
+ document.documentElement.getButton("next").disabled = false;
+ }
+ },
+
+ toggleXPInstallEnable: function gFoundPage_toggleXPInstallEnable(aEvent)
+ {
+ var enabled = aEvent.target.checked;
+ gUpdateWizard.xpinstallEnabled = enabled;
+ var pref = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ pref.setBoolPref(PREF_XPINSTALL_ENABLED, enabled);
+ this.updateNextButton();
+ },
+
+ updateNextButton: function gFoundPage_updateNextButton()
+ {
+ if (!gUpdateWizard.xpinstallEnabled) {
+ document.documentElement.getButton("next").disabled = true;
+ return;
+ }
+
+ var oneChecked = false;
+ var foundUpdates = document.getElementById("found.updates");
+ var updates = foundUpdates.getElementsByTagName("listitem");
+ for (let update of updates) {
+ if (!update.checked)
+ continue;
+ oneChecked = true;
+ break;
+ }
+
+ gUpdateWizard.setButtonLabels(null, true,
+ "installButtonText", true,
+ null, false);
+ document.getElementById("found").setAttribute("next", "installing");
+ document.documentElement.getButton("next").disabled = !oneChecked;
+ }
+};
+
+var gInstallingPage = {
+ _installs : [],
+ _errors : [],
+ _strings : null,
+ _currentInstall : -1,
+ _installing : false,
+
+ // Initialize fields we need for installing and tracking progress,
+ // and start iterating through the installations
+ startInstalls: function gInstallingPage_startInstalls(aInstallList) {
+ if (!gUpdateWizard.xpinstallEnabled) {
+ return;
+ }
+
+ logger.debug("Start installs for "
+ + [i.existingAddon.id for (i of aInstallList)].toSource());
+ this._errors = [];
+ this._installs = aInstallList;
+ this._installing = true;
+ this.startNextInstall();
+ },
+
+ onPageShow: function gInstallingPage_onPageShow()
+ {
+ gUpdateWizard.setButtonLabels(null, true,
+ "nextButtonText", true,
+ null, true);
+
+ var foundUpdates = document.getElementById("found.updates");
+ var updates = foundUpdates.getElementsByTagName("listitem");
+ let toInstall = [];
+ for (let update of updates) {
+ if (!update.checked) {
+ logger.info("User chose to cancel update of " + update.label);
+ gUpdateWizard.upgradeDeclined++;
+ update.install.cancel();
+ continue;
+ }
+ toInstall.push(update.install);
+ }
+ this._strings = document.getElementById("updateStrings");
+
+ this.startInstalls(toInstall);
+ },
+
+ startNextInstall: function gInstallingPage_startNextInstall() {
+ if (this._currentInstall >= 0) {
+ this._installs[this._currentInstall].removeListener(this);
+ }
+
+ this._currentInstall++;
+
+ if (this._installs.length == this._currentInstall) {
+ Services.obs.notifyObservers(null, "TEST:all-updates-done", null);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded",
+ gUpdateWizard.upgraded);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed",
+ gUpdateWizard.upgradeFailed);
+ AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined",
+ gUpdateWizard.upgradeDeclined);
+ this._installing = false;
+ if (gUpdateWizard.shuttingDown) {
+ return;
+ }
+ var nextPage = this._errors.length > 0 ? "installerrors" : "finished";
+ document.getElementById("installing").setAttribute("next", nextPage);
+ document.documentElement.advance();
+ return;
+ }
+
+ let install = this._installs[this._currentInstall];
+
+ if (gUpdateWizard.shuttingDown && !AddonManager.shouldAutoUpdate(install.existingAddon)) {
+ logger.debug("Don't update " + install.existingAddon.id + " in background");
+ gUpdateWizard.upgradeDeclined++;
+ install.cancel();
+ this.startNextInstall();
+ return;
+ }
+ install.addListener(this);
+ install.install();
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ // InstallListener
+ onDownloadStarted: function gInstallingPage_onDownloadStarted(aInstall) {
+ if (gUpdateWizard.shuttingDown) {
+ return;
+ }
+ var strings = document.getElementById("updateStrings");
+ var label = strings.getFormattedString("downloadingPrefix", [aInstall.name]);
+ var actionItem = document.getElementById("actionItem");
+ actionItem.value = label;
+ },
+
+ onDownloadProgress: function gInstallingPage_onDownloadProgress(aInstall) {
+ if (gUpdateWizard.shuttingDown) {
+ return;
+ }
+ var downloadProgress = document.getElementById("downloadProgress");
+ downloadProgress.value = Math.ceil(100 * aInstall.progress / aInstall.maxProgress);
+ },
+
+ onDownloadEnded: function gInstallingPage_onDownloadEnded(aInstall) {
+ },
+
+ onDownloadFailed: function gInstallingPage_onDownloadFailed(aInstall) {
+ this._errors.push(aInstall);
+
+ gUpdateWizard.upgradeFailed++;
+ this.startNextInstall();
+ },
+
+ onInstallStarted: function gInstallingPage_onInstallStarted(aInstall) {
+ if (gUpdateWizard.shuttingDown) {
+ return;
+ }
+ var strings = document.getElementById("updateStrings");
+ var label = strings.getFormattedString("installingPrefix", [aInstall.name]);
+ var actionItem = document.getElementById("actionItem");
+ actionItem.value = label;
+ },
+
+ onInstallEnded: function gInstallingPage_onInstallEnded(aInstall, aAddon) {
+ if (!gUpdateWizard.shuttingDown) {
+ // Remember that this add-on was updated during startup
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
+ aAddon.id);
+ }
+
+ gUpdateWizard.upgraded++;
+ this.startNextInstall();
+ },
+
+ onInstallFailed: function gInstallingPage_onInstallFailed(aInstall) {
+ this._errors.push(aInstall);
+
+ gUpdateWizard.upgradeFailed++;
+ this.startNextInstall();
+ }
+};
+
+var gInstallErrorsPage = {
+ onPageShow: function gInstallErrorsPage_onPageShow()
+ {
+ gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
+ document.documentElement.getButton("finish").focus();
+ },
+};
+
+// Displayed when there are incompatible add-ons and the xpinstall.enabled
+// pref is false and locked.
+var gAdminDisabledPage = {
+ onPageShow: function gAdminDisabledPage_onPageShow()
+ {
+ gUpdateWizard.setButtonLabels(null, true, null, true,
+ "cancelButtonText", true);
+ document.documentElement.getButton("finish").focus();
+ }
+};
+
+// Displayed when selected add-on updates have been installed without error.
+// There can still be add-ons that are not compatible and don't have an update.
+var gFinishedPage = {
+ onPageShow: function gFinishedPage_onPageShow()
+ {
+ gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
+ document.documentElement.getButton("finish").focus();
+
+ if (gUpdateWizard.shouldSuggestAutoChecking) {
+ document.getElementById("finishedCheckDisabled").hidden = false;
+ gUpdateWizard.shouldAutoCheck = true;
+ }
+ else
+ document.getElementById("finishedCheckEnabled").hidden = false;
+
+ document.documentElement.getButton("finish").focus();
+ }
+};
+
+// Displayed when there are incompatible add-ons and there are no available
+// updates.
+var gNoUpdatesPage = {
+ onPageShow: function gNoUpdatesPage_onPageLoad(aEvent)
+ {
+ gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
+ if (gUpdateWizard.shouldSuggestAutoChecking) {
+ document.getElementById("noupdatesCheckDisabled").hidden = false;
+ gUpdateWizard.shouldAutoCheck = true;
+ }
+ else
+ document.getElementById("noupdatesCheckEnabled").hidden = false;
+
+ gUpdateWizard.checkForErrors("updateCheckErrorNotFound");
+ document.documentElement.getButton("finish").focus();
+ }
+};
diff --git a/toolkit/mozapps/extensions/content/update.xul b/toolkit/mozapps/extensions/content/update.xul
new file mode 100644
index 000000000..094651fa5
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/update.xul
@@ -0,0 +1,196 @@
+<?xml version="1.0"?>
+
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/update.css" type="text/css"?>
+
+<!DOCTYPE wizard [
+<!ENTITY % updateDTD SYSTEM "chrome://mozapps/locale/extensions/update.dtd">
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%updateDTD;
+%brandDTD;
+]>
+
+<wizard id="updateWizard"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&updateWizard.title;"
+ windowtype="Addons:Compatibility"
+ branded="true"
+ onload="gUpdateWizard.init();"
+ onwizardfinish="gUpdateWizard.onWizardFinish();"
+ onwizardcancel="return gUpdateWizard.onWizardCancel();"
+ onclose="return gUpdateWizard.onWizardClose(event);"
+ buttons="accept,cancel">
+
+ <script type="application/javascript" src="chrome://mozapps/content/extensions/update.js"/>
+
+ <stringbundleset id="updateSet">
+ <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="updateStrings" src="chrome://mozapps/locale/extensions/update.properties"/>
+ </stringbundleset>
+
+ <wizardpage id="dummy" pageid="dummy"/>
+
+ <wizardpage id="offline" pageid="offline" next="versioninfo"
+ label="&offline.title;"
+ onpageadvanced="return gOfflinePage.onPageAdvanced();">
+ <description>&offline.description;</description>
+ <checkbox id="toggleOffline"
+ checked="true"
+ label="&offline.toggleOffline.label;"
+ accesskey="&offline.toggleOffline.accesskey;"
+ oncommand="gOfflinePage.toggleOffline();"/>
+ </wizardpage>
+
+ <wizardpage id="versioninfo" pageid="versioninfo" next="mismatch"
+ label="&versioninfo.wizard.title;"
+ onpageshow="gVersionInfoPage.onPageShow();">
+ <label>&versioninfo.top.label;</label>
+ <separator class="thin"/>
+ <progressmeter id="versioninfo.progress" mode="undetermined"/>
+ <hbox align="center">
+ <image id="versioninfo.throbber" class="throbber"/>
+ <label flex="1" id="versioninfo.status" crop="right">&versioninfo.waiting;</label>
+ </hbox>
+ <separator/>
+ </wizardpage>
+
+ <wizardpage id="mismatch" pageid="mismatch" next="checking"
+ label="&mismatch.win.title;"
+ onpageshow="gMismatchPage.onPageShow();">
+ <label>&mismatch.top.label;</label>
+ <separator class="thin"/>
+ <listbox id="mismatch.incompatible" flex="1"/>
+ <separator class="thin"/>
+ <label>&mismatch.bottom.label;</label>
+ </wizardpage>
+
+ <wizardpage id="checking" pageid="checking" next="noupdates"
+ label="&checking.wizard.title;"
+ onpageshow="gUpdatePage.onPageShow();">
+ <label>&checking.top.label;</label>
+ <separator class="thin"/>
+ <progressmeter id="checking.progress"/>
+ <hbox align="center">
+ <image id="checking.throbber" class="throbber"/>
+ <label id="checking.status" flex="1" crop="right">&checking.status;</label>
+ </hbox>
+ </wizardpage>
+
+ <wizardpage id="noupdates" pageid="noupdates"
+ label="&noupdates.wizard.title;"
+ onpageshow="gNoUpdatesPage.onPageShow();">
+ <description>&noupdates.intro.desc;</description>
+ <separator class="thin"/>
+ <hbox id="updateCheckErrorNotFound" class="alertBox" hidden="true" align="top">
+ <image id="alert"/>
+ <description flex="1">&noupdates.error.desc;</description>
+ </hbox>
+ <separator class="thin"/>
+ <description id="noupdatesCheckEnabled" hidden="true">
+ &noupdates.checkEnabled.desc;
+ </description>
+ <vbox id="noupdatesCheckDisabled" hidden="true">
+ <description>&finished.checkDisabled.desc;</description>
+ <checkbox label="&enableChecking.label;" checked="true"
+ oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
+ </vbox>
+ <separator flex="1"/>
+#ifndef XP_MACOSX
+ <label>&clickFinish.label;</label>
+#else
+ <label>&clickFinish.labelMac;</label>
+#endif
+ <separator class="thin"/>
+ </wizardpage>
+
+ <wizardpage id="found" pageid="found" next="installing"
+ label="&found.wizard.title;"
+ onpageshow="gFoundPage.onPageShow();">
+ <label>&found.top.label;</label>
+ <separator class="thin"/>
+ <listbox id="found.updates" flex="1" seltype="multiple"
+ onclick="gFoundPage.updateNextButton();"/>
+ <separator class="thin"/>
+ <vbox align="left" id="xpinstallDisabledAlert" hidden="true">
+ <description>&found.disabledXPinstall.label;</description>
+ <checkbox label="&found.enableXPInstall.label;"
+ id="enableXPInstall"
+ accesskey="&found.enableXPInstall.accesskey;"
+ oncommand="gFoundPage.toggleXPInstallEnable(event);"/>
+ </vbox>
+ </wizardpage>
+
+ <wizardpage id="installing" pageid="installing" next="finished"
+ label="&installing.wizard.title;"
+ onpageshow="gInstallingPage.onPageShow();">
+ <label>&installing.top.label;</label>
+ <progressmeter id="downloadProgress"/>
+ <hbox align="center">
+ <image id="installing.throbber" class="throbber"/>
+ <label id="actionItem" flex="1" crop="right"/>
+ </hbox>
+ <separator/>
+ </wizardpage>
+
+ <wizardpage id="installerrors" pageid="installerrors"
+ label="&installerrors.wizard.title;"
+ onpageshow="gInstallErrorsPage.onPageShow();">
+ <hbox align="top" class="alertBox">
+ <description flex="1">&installerrors.intro.label;</description>
+ </hbox>
+ <separator flex="1"/>
+#ifndef XP_MACOSX
+ <label>&clickFinish.label;</label>
+#else
+ <label>&clickFinish.labelMac;</label>
+#endif
+ <separator class="thin"/>
+ </wizardpage>
+
+ <wizardpage id="adminDisabled" pageid="adminDisabled"
+ label="&adminDisabled.wizard.title;"
+ onpageshow="gAdminDisabledPage.onPageShow();">
+ <separator/>
+ <hbox class="alertBox" align="top">
+ <image id="alert"/>
+ <description flex="1">&adminDisabled.warning.label;</description>
+ </hbox>
+ <separator flex="1"/>
+#ifndef XP_MACOSX
+ <label>&clickFinish.label;</label>
+#else
+ <label>&clickFinish.labelMac;</label>
+#endif
+ <separator class="thin"/>
+ </wizardpage>
+
+ <wizardpage id="finished" pageid="finished"
+ label="&finished.wizard.title;"
+ onpageshow="gFinishedPage.onPageShow();">
+
+ <label>&finished.top.label;</label>
+ <separator/>
+ <description id="finishedCheckEnabled" hidden="true">
+ &finished.checkEnabled.desc;
+ </description>
+ <vbox id="finishedCheckDisabled" hidden="true">
+ <description>&finished.checkDisabled.desc;</description>
+ <checkbox label="&enableChecking.label;" checked="true"
+ oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
+ </vbox>
+ <separator flex="1"/>
+#ifndef XP_MACOSX
+ <label>&clickFinish.label;</label>
+#else
+ <label>&clickFinish.labelMac;</label>
+#endif
+ <separator class="thin"/>
+ </wizardpage>
+
+</wizard>
+
diff --git a/toolkit/mozapps/extensions/content/updateinfo.xsl b/toolkit/mozapps/extensions/content/updateinfo.xsl
new file mode 100644
index 000000000..5fcccd6d7
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/updateinfo.xsl
@@ -0,0 +1,41 @@
+<?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/. -->
+
+<xsl:stylesheet version="1.0" xmlns:xhtml="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <!-- Any elements not otherwise specified will be stripped but the contents
+ will be displayed. All attributes are stripped from copied elements. -->
+
+ <!-- Block these elements and their contents -->
+ <xsl:template match="xhtml:head|xhtml:script|xhtml:style">
+ </xsl:template>
+
+ <!-- Allowable styling elements -->
+ <xsl:template match="xhtml:b|xhtml:i|xhtml:em|xhtml:strong|xhtml:u|xhtml:q|xhtml:sub|xhtml:sup|xhtml:code">
+ <xsl:copy><xsl:apply-templates/></xsl:copy>
+ </xsl:template>
+
+ <!-- Allowable block formatting elements -->
+ <xsl:template match="xhtml:h1|xhtml:h2|xhtml:h3|xhtml:p|xhtml:div|xhtml:blockquote|xhtml:pre">
+ <xsl:copy><xsl:apply-templates/></xsl:copy>
+ </xsl:template>
+
+ <!-- Allowable list formatting elements -->
+ <xsl:template match="xhtml:ul|xhtml:ol|xhtml:li|xhtml:dl|xhtml:dt|xhtml:dd">
+ <xsl:copy><xsl:apply-templates/></xsl:copy>
+ </xsl:template>
+
+ <!-- These elements are copied and their contents dropped -->
+ <xsl:template match="xhtml:br|xhtml:hr">
+ <xsl:copy/>
+ </xsl:template>
+
+ <!-- The root document -->
+ <xsl:template match="/">
+ <xhtml:body><xsl:apply-templates/></xhtml:body>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/toolkit/mozapps/extensions/content/xpinstallConfirm.css b/toolkit/mozapps/extensions/content/xpinstallConfirm.css
new file mode 100644
index 000000000..583facfec
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/xpinstallConfirm.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/. */
+
+installitem {
+ -moz-binding: url("chrome://mozapps/content/xpinstall/xpinstallItem.xml#installitem");
+ display: -moz-box;
+}
diff --git a/toolkit/mozapps/extensions/content/xpinstallConfirm.js b/toolkit/mozapps/extensions/content/xpinstallConfirm.js
new file mode 100644
index 000000000..678e37001
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/xpinstallConfirm.js
@@ -0,0 +1,196 @@
+// -*- 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 XPInstallConfirm = {};
+
+XPInstallConfirm.init = function XPInstallConfirm_init()
+{
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+ var _installCountdown;
+ var _installCountdownInterval;
+ var _focused;
+ var _timeout;
+
+ // Default to cancelling the install when the window unloads
+ XPInstallConfirm._installOK = false;
+
+ var bundle = document.getElementById("xpinstallConfirmStrings");
+
+ let args = window.arguments[0].wrappedJSObject;
+
+ // If all installs have already been cancelled in some way then just close
+ // the window
+ if (args.installs.every(i => i.state != AddonManager.STATE_DOWNLOADED)) {
+ window.close();
+ return;
+ }
+
+ var _installCountdownLength = 5;
+ try {
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ var delay_in_milliseconds = prefs.getIntPref("security.dialog_enable_delay");
+ _installCountdownLength = Math.round(delay_in_milliseconds / 500);
+ } catch (ex) { }
+
+ var itemList = document.getElementById("itemList");
+
+ let installMap = new WeakMap();
+ let installListener = {
+ onDownloadCancelled: function(install) {
+ itemList.removeChild(installMap.get(install));
+ if (--numItemsToInstall == 0)
+ window.close();
+ }
+ };
+
+ var numItemsToInstall = args.installs.length;
+ for (let install of args.installs) {
+ var installItem = document.createElement("installitem");
+ itemList.appendChild(installItem);
+
+ installItem.name = install.addon.name;
+ installItem.url = install.sourceURI.spec;
+ var icon = install.iconURL;
+ if (icon)
+ installItem.icon = icon;
+ var type = install.type;
+ if (type)
+ installItem.type = type;
+ if (install.certName) {
+ installItem.cert = bundle.getFormattedString("signed", [install.certName]);
+ }
+ else {
+ installItem.cert = bundle.getString("unverified");
+ }
+ installItem.signed = install.certName ? "true" : "false";
+
+ installMap.set(install, installItem);
+ install.addListener(installListener);
+ }
+
+ var introString = bundle.getString("itemWarnIntroSingle");
+ if (numItemsToInstall > 4)
+ introString = bundle.getFormattedString("itemWarnIntroMultiple", [numItemsToInstall]);
+ var textNode = document.createTextNode(introString);
+ var introNode = document.getElementById("itemWarningIntro");
+ while (introNode.hasChildNodes())
+ introNode.removeChild(introNode.firstChild);
+ introNode.appendChild(textNode);
+
+ var okButton = document.documentElement.getButton("accept");
+ okButton.focus();
+
+ function okButtonCountdown() {
+ _installCountdown -= 1;
+
+ if (_installCountdown < 1) {
+ okButton.label = bundle.getString("installButtonLabel");
+ okButton.disabled = false;
+ clearInterval(_installCountdownInterval);
+ }
+ else
+ okButton.label = bundle.getFormattedString("installButtonDisabledLabel", [_installCountdown]);
+ }
+
+ function myfocus() {
+ // Clear the timeout if it exists so only the last one will be used.
+ if (_timeout)
+ clearTimeout(_timeout);
+
+ // Use setTimeout since the last focus or blur event to fire is the one we
+ // want
+ _timeout = setTimeout(setWidgetsAfterFocus, 0);
+ }
+
+ function setWidgetsAfterFocus() {
+ if (_focused)
+ return;
+
+ _installCountdown = _installCountdownLength;
+ _installCountdownInterval = setInterval(okButtonCountdown, 500);
+ okButton.label = bundle.getFormattedString("installButtonDisabledLabel", [_installCountdown]);
+ _focused = true;
+ }
+
+ function myblur() {
+ // Clear the timeout if it exists so only the last one will be used.
+ if (_timeout)
+ clearTimeout(_timeout);
+
+ // Use setTimeout since the last focus or blur event to fire is the one we
+ // want
+ _timeout = setTimeout(setWidgetsAfterBlur, 0);
+ }
+
+ function setWidgetsAfterBlur() {
+ if (!_focused)
+ return;
+
+ // Set _installCountdown to the inital value set in setWidgetsAfterFocus
+ // plus 1 so when the window is focused there is immediate ui feedback.
+ _installCountdown = _installCountdownLength + 1;
+ okButton.label = bundle.getFormattedString("installButtonDisabledLabel", [_installCountdown]);
+ okButton.disabled = true;
+ clearInterval(_installCountdownInterval);
+ _focused = false;
+ }
+
+ function myUnload() {
+ if (_installCountdownLength > 0) {
+ document.removeEventListener("focus", myfocus, true);
+ document.removeEventListener("blur", myblur, true);
+ }
+ window.removeEventListener("unload", myUnload, false);
+
+ for (let install of args.installs)
+ install.removeListener(installListener);
+
+ // Now perform the desired action - either install the
+ // addons or cancel the installations
+ if (XPInstallConfirm._installOK) {
+ for (let install of args.installs)
+ install.install();
+ }
+ else {
+ for (let install of args.installs) {
+ if (install.state != AddonManager.STATE_CANCELLED)
+ install.cancel();
+ }
+ }
+ }
+
+ window.addEventListener("unload", myUnload, false);
+
+ if (_installCountdownLength > 0) {
+ document.addEventListener("focus", myfocus, true);
+ document.addEventListener("blur", myblur, true);
+
+ okButton.disabled = true;
+ setWidgetsAfterFocus();
+ }
+ else
+ okButton.label = bundle.getString("installButtonLabel");
+}
+
+XPInstallConfirm.onOK = function XPInstallConfirm_onOk()
+{
+ Components.classes["@mozilla.org/base/telemetry;1"].
+ getService(Components.interfaces.nsITelemetry).
+ getHistogramById("SECURITY_UI").
+ add(Components.interfaces.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
+ // Perform the install or cancel after the window has unloaded
+ XPInstallConfirm._installOK = true;
+ return true;
+}
+
+XPInstallConfirm.onCancel = function XPInstallConfirm_onCancel()
+{
+ // Perform the install or cancel after the window has unloaded
+ XPInstallConfirm._installOK = false;
+ return true;
+}
diff --git a/toolkit/mozapps/extensions/content/xpinstallConfirm.xul b/toolkit/mozapps/extensions/content/xpinstallConfirm.xul
new file mode 100644
index 000000000..f1c29eb73
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/xpinstallConfirm.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://mozapps/content/xpinstall/xpinstallConfirm.css" type="text/css"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/xpinstall/xpinstallConfirm.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://mozapps/locale/xpinstall/xpinstallConfirm.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="xpinstallConfirm" title="&dialog.title;" style="&dialog.style;"
+ windowtype="Addons:Install"
+ onload="XPInstallConfirm.init()"
+ ondialogaccept="return XPInstallConfirm.onOK();"
+ ondialogcancel="return XPInstallConfirm.onCancel();">
+
+ <script src="chrome://mozapps/content/xpinstall/xpinstallConfirm.js" type="application/javascript"/>
+
+ <stringbundle id="xpinstallConfirmStrings"
+ src="chrome://mozapps/locale/xpinstall/xpinstallConfirm.properties"/>
+
+ <vbox flex="1" id="dialogContentBox">
+ <hbox id="xpinstallheader" align="start">
+ <image class="alert-icon"/>
+ <vbox flex="1">
+ <description class="warning">&warningPrimary.label;</description>
+ <description>&warningSecondary.label;</description>
+ </vbox>
+ </hbox>
+ <label id="itemWarningIntro"/>
+ <vbox id="itemList" class="listbox" flex="1" style="overflow: auto;"/>
+ </vbox>
+
+</dialog>
diff --git a/toolkit/mozapps/extensions/content/xpinstallItem.xml b/toolkit/mozapps/extensions/content/xpinstallItem.xml
new file mode 100644
index 000000000..5146af84f
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/xpinstallItem.xml
@@ -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/. -->
+
+
+<!DOCTYPE bindings SYSTEM "chrome://mozapps/locale/xpinstall/xpinstallConfirm.dtd">
+
+<bindings id="xpinstallItemBindings"
+ 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="installitem">
+ <resources>
+ <stylesheet src="chrome://mozapps/skin/xpinstall/xpinstallConfirm.css"/>
+ </resources>
+ <content>
+ <xul:hbox flex="1">
+ <xul:vbox align="center" pack="center" class="xpinstallIconContainer">
+ <xul:image class="xpinstallItemIcon" xbl:inherits="src=icon"/>
+ </xul:vbox>
+ <xul:vbox flex="1" pack="center">
+ <xul:hbox class="xpinstallItemNameRow" align="center">
+ <xul:label class="xpinstallItemName" xbl:inherits="value=name" crop="right"/>
+ <xul:label class="xpinstallItemSigned" xbl:inherits="value=cert,signed"/>
+ </xul:hbox>
+ <xul:hbox class="xpinstallItemDetailsRow" align="center">
+ <xul:textbox class="xpinstallItemURL" xbl:inherits="value=url" flex="1" readonly="true" crop="right"/>
+ </xul:hbox>
+ </xul:vbox>
+ </xul:hbox>
+ </content>
+ <implementation>
+ <property name="name" onset="this.setAttribute('name', val); return val;"
+ onget="return this.getAttribute('name');"/>
+ <property name="cert" onset="this.setAttribute('cert', val); return val;"
+ onget="return this.getAttribute('cert');"/>
+ <property name="signed" onset="this.setAttribute('signed', val); return val;"
+ onget="return this.getAttribute('signed');"/>
+ <property name="url" onset="this.setAttribute('url', val); return val;"
+ onget="return this.getAttribute('url');"/>
+ <property name="icon" onset="this.setAttribute('icon', val); return val;"
+ onget="return this.getAttribute('icon');"/>
+ <property name="type" onset="this.setAttribute('type', val); return val;"
+ onget="return this.getAttribute('type');"/>
+ </implementation>
+ </binding>
+
+</bindings>
+
diff --git a/toolkit/mozapps/extensions/extensions.manifest b/toolkit/mozapps/extensions/extensions.manifest
new file mode 100644
index 000000000..f0f00545f
--- /dev/null
+++ b/toolkit/mozapps/extensions/extensions.manifest
@@ -0,0 +1,20 @@
+component {66354bc9-7ed1-4692-ae1d-8da97d6b205e} nsBlocklistService.js
+contract @mozilla.org/extensions/blocklist;1 {66354bc9-7ed1-4692-ae1d-8da97d6b205e}
+category profile-after-change nsBlocklistService @mozilla.org/extensions/blocklist;1
+#ifndef MOZ_WIDGET_GONK
+category update-timer nsBlocklistService @mozilla.org/extensions/blocklist;1,getService,blocklist-background-update-timer,extensions.blocklist.interval,86400
+component {4399533d-08d1-458c-a87a-235f74451cfa} addonManager.js
+contract @mozilla.org/addons/integration;1 {4399533d-08d1-458c-a87a-235f74451cfa}
+category update-timer addonManager @mozilla.org/addons/integration;1,getService,addon-background-update-timer,extensions.update.interval,86400
+component {7beb3ba8-6ec3-41b4-b67c-da89b8518922} amContentHandler.js
+contract @mozilla.org/uriloader/content-handler;1?type=application/x-xpinstall {7beb3ba8-6ec3-41b4-b67c-da89b8518922}
+component {0f38e086-89a3-40a5-8ffc-9b694de1d04a} amWebInstallListener.js
+contract @mozilla.org/addons/web-install-listener;1 {0f38e086-89a3-40a5-8ffc-9b694de1d04a}
+component {9df8ef2b-94da-45c9-ab9f-132eb55fddf1} amInstallTrigger.js
+contract @mozilla.org/addons/installtrigger;1 {9df8ef2b-94da-45c9-ab9f-132eb55fddf1}
+category JavaScript-global-property InstallTrigger @mozilla.org/addons/installtrigger;1
+#ifndef MOZ_WIDGET_ANDROID
+category addon-provider-module PluginProvider resource://gre/modules/addons/PluginProvider.jsm
+#endif
+category addon-provider-module GMPProvider resource://gre/modules/addons/GMPProvider.jsm
+#endif
diff --git a/toolkit/mozapps/extensions/internal/AddonLogging.jsm b/toolkit/mozapps/extensions/internal/AddonLogging.jsm
new file mode 100644
index 000000000..362439bae
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/AddonLogging.jsm
@@ -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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const KEY_PROFILEDIR = "ProfD";
+const FILE_EXTENSIONS_LOG = "extensions.log";
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+
+const LOGGER_FILE_PERM = parseInt("666", 8);
+
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+this.EXPORTED_SYMBOLS = [ "LogManager" ];
+
+var gDebugLogEnabled = false;
+
+function formatLogMessage(aType, aName, aStr, aException) {
+ let message = aType.toUpperCase() + " " + aName + ": " + aStr;
+ if (aException) {
+ if (typeof aException == "number")
+ return message + ": " + Components.Exception("", aException).name;
+
+ message = message + ": " + aException;
+ // instanceOf doesn't work here, let's duck type
+ if (aException.fileName)
+ message = message + " (" + aException.fileName + ":" + aException.lineNumber + ")";
+
+ if (aException.message == "too much recursion")
+ dump(message + "\n" + aException.stack + "\n");
+ }
+ return message;
+}
+
+function getStackDetails(aException) {
+ // Defensively wrap all this to ensure that failing to get the message source
+ // doesn't stop the message from being logged
+ try {
+ if (aException) {
+ if (aException instanceof Ci.nsIException) {
+ return {
+ sourceName: aException.filename,
+ lineNumber: aException.lineNumber
+ };
+ }
+
+ if (typeof aException == "object") {
+ return {
+ sourceName: aException.fileName,
+ lineNumber: aException.lineNumber
+ };
+ }
+ }
+
+ let stackFrame = Components.stack.caller.caller.caller;
+ return {
+ sourceName: stackFrame.filename,
+ lineNumber: stackFrame.lineNumber
+ };
+ }
+ catch (e) {
+ return {
+ sourceName: null,
+ lineNumber: 0
+ };
+ }
+}
+
+function AddonLogger(aName) {
+ this.name = aName;
+}
+
+AddonLogger.prototype = {
+ name: null,
+
+ error: function AddonLogger_error(aStr, aException) {
+ let message = formatLogMessage("error", this.name, aStr, aException);
+
+ let stack = getStackDetails(aException);
+
+ let consoleMessage = Cc["@mozilla.org/scripterror;1"].
+ createInstance(Ci.nsIScriptError);
+ consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0,
+ Ci.nsIScriptError.errorFlag, "component javascript");
+ Services.console.logMessage(consoleMessage);
+
+ // Always dump errors, in case the Console Service isn't listening yet
+ dump("*** " + message + "\n");
+
+ try {
+ var tstamp = new Date();
+ var logfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_LOG]);
+ var stream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ stream.init(logfile, 0x02 | 0x08 | 0x10, LOGGER_FILE_PERM, 0); // write, create, append
+ var writer = Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(Ci.nsIConverterOutputStream);
+ writer.init(stream, "UTF-8", 0, 0x0000);
+ writer.writeString(tstamp.toLocaleFormat("%Y-%m-%d %H:%M:%S ") +
+ message + " at " + stack.sourceName + ":" +
+ stack.lineNumber + "\n");
+ writer.close();
+ }
+ catch (e) { }
+ },
+
+ warn: function AddonLogger_warn(aStr, aException) {
+ let message = formatLogMessage("warn", this.name, aStr, aException);
+
+ let stack = getStackDetails(aException);
+
+ let consoleMessage = Cc["@mozilla.org/scripterror;1"].
+ createInstance(Ci.nsIScriptError);
+ consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0,
+ Ci.nsIScriptError.warningFlag, "component javascript");
+ Services.console.logMessage(consoleMessage);
+
+ if (gDebugLogEnabled)
+ dump("*** " + message + "\n");
+ },
+
+ log: function AddonLogger_log(aStr, aException) {
+ if (gDebugLogEnabled) {
+ let message = formatLogMessage("log", this.name, aStr, aException);
+ dump("*** " + message + "\n");
+ Services.console.logStringMessage(message);
+ }
+ }
+};
+
+this.LogManager = {
+ getLogger: function LogManager_getLogger(aName, aTarget) {
+ let logger = new AddonLogger(aName);
+
+ if (aTarget) {
+ ["error", "warn", "log"].forEach(function(name) {
+ let fname = name.toUpperCase();
+ delete aTarget[fname];
+ aTarget[fname] = function LogManager_targetName(aStr, aException) {
+ logger[name](aStr, aException);
+ };
+ });
+ }
+
+ return logger;
+ }
+};
+
+var PrefObserver = {
+ init: function PrefObserver_init() {
+ Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
+ Services.obs.addObserver(this, "xpcom-shutdown", false);
+ this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
+ },
+
+ observe: function PrefObserver_observe(aSubject, aTopic, aData) {
+ if (aTopic == "xpcom-shutdown") {
+ Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
+ Services.obs.removeObserver(this, "xpcom-shutdown");
+ }
+ else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
+ try {
+ gDebugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
+ }
+ catch (e) {
+ gDebugLogEnabled = false;
+ }
+ }
+ }
+};
+
+PrefObserver.init();
diff --git a/toolkit/mozapps/extensions/internal/AddonRepository.jsm b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
new file mode 100644
index 000000000..adcecbee7
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -0,0 +1,2031 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
+ "resource://gre/modules/DeferredSave.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository_SQLiteMigrator",
+ "resource://gre/modules/addons/AddonRepository_SQLiteMigrator.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+
+
+this.EXPORTED_SYMBOLS = [ "AddonRepository" ];
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+const PREF_GETADDONS_CACHE_TYPES = "extensions.getAddons.cache.types";
+const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled"
+const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_GETADDONS_BYIDS_PERFORMANCE = "extensions.getAddons.getWithPerformance.url";
+const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL";
+const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
+const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const PREF_GETADDONS_DB_SCHEMA = "extensions.getAddons.databaseSchema"
+
+const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
+const PREF_METADATA_UPDATETHRESHOLD_SEC = "extensions.getAddons.cache.updateThreshold";
+const DEFAULT_METADATA_UPDATETHRESHOLD_SEC = 172800; // two days
+
+const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
+
+const API_VERSION = "1.5";
+const DEFAULT_CACHE_TYPES = "extension,theme,locale,dictionary";
+
+const KEY_PROFILEDIR = "ProfD";
+const FILE_DATABASE = "addons.json";
+const DB_SCHEMA = 5;
+const DB_MIN_JSON_SCHEMA = 5;
+const DB_BATCH_TIMEOUT_MS = 50;
+
+const BLANK_DB = function() {
+ return {
+ addons: new Map(),
+ schema: DB_SCHEMA
+ };
+}
+
+const TOOLKIT_ID = "toolkit@mozilla.org";
+#ifdef MOZ_PHOENIX_EXTENSIONS
+const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+#endif
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.repository";
+
+// Create a new logger for use by the Addons Repository
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+// A map between XML keys to AddonSearchResult keys for string values
+// that require no extra parsing from XML
+const STRING_KEY_MAP = {
+ name: "name",
+ version: "version",
+ homepage: "homepageURL",
+ support: "supportURL"
+};
+
+// A map between XML keys to AddonSearchResult keys for string values
+// that require parsing from HTML
+const HTML_KEY_MAP = {
+ summary: "description",
+ description: "fullDescription",
+ developer_comments: "developerComments",
+ eula: "eula"
+};
+
+// A map between XML keys to AddonSearchResult keys for integer values
+// that require no extra parsing from XML
+const INTEGER_KEY_MAP = {
+ total_downloads: "totalDownloads",
+ weekly_downloads: "weeklyDownloads",
+ daily_users: "dailyUsers"
+};
+
+// Wrap the XHR factory so that tests can override with a mock
+let XHRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1",
+ "nsIXMLHttpRequest");
+
+function convertHTMLToPlainText(html) {
+ if (!html)
+ return html;
+ var converter = Cc["@mozilla.org/widget/htmlformatconverter;1"].
+ createInstance(Ci.nsIFormatConverter);
+
+ var input = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ input.data = html.replace(/\n/g, "<br>");
+
+ var output = {};
+ converter.convert("text/html", input, input.data.length, "text/unicode",
+ output, {});
+
+ if (output.value instanceof Ci.nsISupportsString)
+ return output.value.data.replace(/\r\n/g, "\n");
+ return html;
+}
+
+function getAddonsToCache(aIds, aCallback) {
+ try {
+ var types = Services.prefs.getCharPref(PREF_GETADDONS_CACHE_TYPES);
+ }
+ catch (e) { }
+ if (!types)
+ types = DEFAULT_CACHE_TYPES;
+
+ types = types.split(",");
+
+ AddonManager.getAddonsByIDs(aIds, function getAddonsToCache_getAddonsByIDs(aAddons) {
+ let enabledIds = [];
+ for (var i = 0; i < aIds.length; i++) {
+ var preference = PREF_GETADDONS_CACHE_ID_ENABLED.replace("%ID%", aIds[i]);
+ try {
+ if (!Services.prefs.getBoolPref(preference))
+ continue;
+ } catch(e) {
+ // If the preference doesn't exist caching is enabled by default
+ }
+
+ // The add-ons manager may not know about this ID yet if it is a pending
+ // install. In that case we'll just cache it regardless
+ if (aAddons[i] && (types.indexOf(aAddons[i].type) == -1))
+ continue;
+
+ enabledIds.push(aIds[i]);
+ }
+
+ aCallback(enabledIds);
+ });
+}
+
+function AddonSearchResult(aId) {
+ this.id = aId;
+ this.icons = {};
+ this._unsupportedProperties = {};
+}
+
+AddonSearchResult.prototype = {
+ /**
+ * The ID of the add-on
+ */
+ id: null,
+
+ /**
+ * The add-on type (e.g. "extension" or "theme")
+ */
+ type: null,
+
+ /**
+ * The name of the add-on
+ */
+ name: null,
+
+ /**
+ * The version of the add-on
+ */
+ version: null,
+
+ /**
+ * The creator of the add-on
+ */
+ creator: null,
+
+ /**
+ * The developers of the add-on
+ */
+ developers: null,
+
+ /**
+ * A short description of the add-on
+ */
+ description: null,
+
+ /**
+ * The full description of the add-on
+ */
+ fullDescription: null,
+
+ /**
+ * The developer comments for the add-on. This includes any information
+ * that may be helpful to end users that isn't necessarily applicable to
+ * the add-on description (e.g. known major bugs)
+ */
+ developerComments: null,
+
+ /**
+ * The end-user licensing agreement (EULA) of the add-on
+ */
+ eula: null,
+
+ /**
+ * The url of the add-on's icon
+ */
+ get iconURL() {
+ return this.icons && this.icons[32];
+ },
+
+ /**
+ * The URLs of the add-on's icons, as an object with icon size as key
+ */
+ icons: null,
+
+ /**
+ * An array of screenshot urls for the add-on
+ */
+ screenshots: null,
+
+ /**
+ * The homepage for the add-on
+ */
+ homepageURL: null,
+
+ /**
+ * The homepage for the add-on
+ */
+ learnmoreURL: null,
+
+ /**
+ * The support URL for the add-on
+ */
+ supportURL: null,
+
+ /**
+ * The contribution url of the add-on
+ */
+ contributionURL: null,
+
+ /**
+ * The suggested contribution amount
+ */
+ contributionAmount: null,
+
+ /**
+ * The URL to visit in order to purchase the add-on
+ */
+ purchaseURL: null,
+
+ /**
+ * The numerical cost of the add-on in some currency, for sorting purposes
+ * only
+ */
+ purchaseAmount: null,
+
+ /**
+ * The display cost of the add-on, for display purposes only
+ */
+ purchaseDisplayAmount: null,
+
+ /**
+ * The rating of the add-on, 0-5
+ */
+ averageRating: null,
+
+ /**
+ * The number of reviews for this add-on
+ */
+ reviewCount: null,
+
+ /**
+ * The URL to the list of reviews for this add-on
+ */
+ reviewURL: null,
+
+ /**
+ * The total number of times the add-on was downloaded
+ */
+ totalDownloads: null,
+
+ /**
+ * The number of times the add-on was downloaded the current week
+ */
+ weeklyDownloads: null,
+
+ /**
+ * The number of daily users for the add-on
+ */
+ dailyUsers: null,
+
+ /**
+ * AddonInstall object generated from the add-on XPI url
+ */
+ install: null,
+
+ /**
+ * nsIURI storing where this add-on was installed from
+ */
+ sourceURI: null,
+
+ /**
+ * The status of the add-on in the repository (e.g. 4 = "Public")
+ */
+ repositoryStatus: null,
+
+ /**
+ * The size of the add-on's files in bytes. For an add-on that have not yet
+ * been downloaded this may be an estimated value.
+ */
+ size: null,
+
+ /**
+ * The Date that the add-on was most recently updated
+ */
+ updateDate: null,
+
+ /**
+ * True or false depending on whether the add-on is compatible with the
+ * current version of the application
+ */
+ isCompatible: true,
+
+ /**
+ * True or false depending on whether the add-on is compatible with the
+ * current platform
+ */
+ isPlatformCompatible: true,
+
+ /**
+ * Array of AddonCompatibilityOverride objects, that describe overrides for
+ * compatibility with an application versions.
+ **/
+ compatibilityOverrides: null,
+
+ /**
+ * True if the add-on has a secure means of updating
+ */
+ providesUpdatesSecurely: true,
+
+ /**
+ * The current blocklist state of the add-on
+ */
+ blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
+
+ /**
+ * True if this add-on cannot be used in the application based on version
+ * compatibility, dependencies and blocklisting
+ */
+ appDisabled: false,
+
+ /**
+ * True if the user wants this add-on to be disabled
+ */
+ userDisabled: false,
+
+ /**
+ * Indicates what scope the add-on is installed in, per profile, user,
+ * system or application
+ */
+ scope: AddonManager.SCOPE_PROFILE,
+
+ /**
+ * True if the add-on is currently functional
+ */
+ isActive: true,
+
+ /**
+ * A bitfield holding all of the current operations that are waiting to be
+ * performed for this add-on
+ */
+ pendingOperations: AddonManager.PENDING_NONE,
+
+ /**
+ * A bitfield holding all the the operations that can be performed on
+ * this add-on
+ */
+ permissions: 0,
+
+ /**
+ * Tests whether this add-on is known to be compatible with a
+ * particular application and platform version.
+ *
+ * @param appVersion
+ * An application version to test against
+ * @param platformVersion
+ * A platform version to test against
+ * @return Boolean representing if the add-on is compatible
+ */
+ isCompatibleWith: function ASR_isCompatibleWith(aAppVerison, aPlatformVersion) {
+ return true;
+ },
+
+ /**
+ * Starts an update check for this add-on. This will perform
+ * asynchronously and deliver results to the given listener.
+ *
+ * @param aListener
+ * An UpdateListener for the update process
+ * @param aReason
+ * A reason code for performing the update
+ * @param aAppVersion
+ * An application version to check for updates for
+ * @param aPlatformVersion
+ * A platform version to check for updates for
+ */
+ findUpdates: function ASR_findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
+ if ("onNoCompatibilityUpdateAvailable" in aListener)
+ aListener.onNoCompatibilityUpdateAvailable(this);
+ if ("onNoUpdateAvailable" in aListener)
+ aListener.onNoUpdateAvailable(this);
+ if ("onUpdateFinished" in aListener)
+ aListener.onUpdateFinished(this);
+ },
+
+ toJSON: function() {
+ let json = {};
+
+ for (let [property, value] of Iterator(this)) {
+ if (property.startsWith("_") ||
+ typeof(value) === "function")
+ continue;
+
+ try {
+ switch (property) {
+ case "sourceURI":
+ json.sourceURI = value ? value.spec : "";
+ break;
+
+ case "updateDate":
+ json.updateDate = value ? value.getTime() : "";
+ break;
+
+ default:
+ json[property] = value;
+ }
+ } catch (ex) {
+ logger.warn("Error writing property value for " + property);
+ }
+ }
+
+ for (let [property, value] of Iterator(this._unsupportedProperties)) {
+ if (!property.startsWith("_"))
+ json[property] = value;
+ }
+
+ return json;
+ }
+}
+
+/**
+ * The add-on repository is a source of add-ons that can be installed. It can
+ * be searched in three ways. The first takes a list of IDs and returns a
+ * list of the corresponding add-ons. The second returns a list of add-ons that
+ * come highly recommended. This list should change frequently. The third is to
+ * search for specific search terms entered by the user. Searches are
+ * asynchronous and results should be passed to the provided callback object
+ * when complete. The results passed to the callback should only include add-ons
+ * that are compatible with the current application and are not already
+ * installed.
+ */
+this.AddonRepository = {
+ /**
+ * Whether caching is currently enabled
+ */
+ get cacheEnabled() {
+ // Act as though caching is disabled if there was an unrecoverable error
+ // openning the database.
+ if (!AddonDatabase.databaseOk) {
+ logger.warn("Cache is disabled because database is not OK");
+ return false;
+ }
+
+ let preference = PREF_GETADDONS_CACHE_ENABLED;
+ let enabled = false;
+ try {
+ enabled = Services.prefs.getBoolPref(preference);
+ } catch(e) {
+ logger.warn("cacheEnabled: Couldn't get pref: " + preference);
+ }
+
+ return enabled;
+ },
+
+ // A cache of the add-ons stored in the database
+ _addons: null,
+
+ // Whether a search is currently in progress
+ _searching: false,
+
+ // XHR associated with the current request
+ _request: null,
+
+ /*
+ * Addon search results callback object that contains two functions
+ *
+ * searchSucceeded - Called when a search has suceeded.
+ *
+ * @param aAddons
+ * An array of the add-on results. In the case of searching for
+ * specific terms the ordering of results may be determined by
+ * the search provider.
+ * @param aAddonCount
+ * The length of aAddons
+ * @param aTotalResults
+ * The total results actually available in the repository
+ *
+ *
+ * searchFailed - Called when an error occurred when performing a search.
+ */
+ _callback: null,
+
+ // Maximum number of results to return
+ _maxResults: null,
+
+ /**
+ * Shut down AddonRepository
+ * return: promise{integer} resolves with the result of flushing
+ * the AddonRepository database
+ */
+ shutdown: function AddonRepo_shutdown() {
+ this.cancelSearch();
+
+ this._addons = null;
+ return AddonDatabase.shutdown(false);
+ },
+
+ metadataAge: function() {
+ let now = Math.round(Date.now() / 1000);
+
+ let lastUpdate = 0;
+ try {
+ lastUpdate = Services.prefs.getIntPref(PREF_METADATA_LASTUPDATE);
+ } catch (e) {}
+
+ // Handle clock jumps
+ if (now < lastUpdate) {
+ return now;
+ }
+ return now - lastUpdate;
+ },
+
+ isMetadataStale: function AddonRepo_isMetadataStale() {
+ let threshold = DEFAULT_METADATA_UPDATETHRESHOLD_SEC;
+ try {
+ threshold = Services.prefs.getIntPref(PREF_METADATA_UPDATETHRESHOLD_SEC);
+ } catch (e) {}
+ return (this.metadataAge() > threshold);
+ },
+
+ /**
+ * Asynchronously get a cached add-on by id. The add-on (or null if the
+ * add-on is not found) is passed to the specified callback. If caching is
+ * disabled, null is passed to the specified callback.
+ *
+ * @param aId
+ * The id of the add-on to get
+ * @param aCallback
+ * The callback to pass the result back to
+ */
+ getCachedAddonByID: Task.async(function* (aId, aCallback) {
+ if (!aId || !this.cacheEnabled) {
+ aCallback(null);
+ return;
+ }
+
+ function getAddon(aAddons) {
+ aCallback(aAddons.get(aId) || null);
+ }
+
+ if (this._addons == null) {
+ AddonDatabase.retrieveStoredData().then(aAddons => {
+ this._addons = aAddons;
+ getAddon(aAddons);
+ });
+
+ return;
+ }
+
+ getAddon(this._addons);
+ }),
+
+ /**
+ * Asynchronously repopulate cache so it only contains the add-ons
+ * corresponding to the specified ids. If caching is disabled,
+ * the cache is completely removed.
+ *
+ * @param aTimeout
+ * (Optional) timeout in milliseconds to abandon the XHR request
+ * if we have not received a response from the server.
+ * @return Promise{null}
+ * Resolves when the metadata ping is complete
+ */
+ repopulateCache: function(aTimeout) {
+ return this._repopulateCacheInternal(false, aTimeout);
+ },
+
+ /*
+ * Clear and delete the AddonRepository database
+ * @return Promise{null} resolves when the database is deleted
+ */
+ _clearCache: function () {
+ this._addons = null;
+ return AddonDatabase.delete().then(() =>
+ new Promise((resolve, reject) =>
+ AddonManagerPrivate.updateAddonRepositoryData(resolve))
+ );
+ },
+
+ _repopulateCacheInternal: Task.async(function* (aSendPerformance, aTimeout) {
+ let allAddons = yield new Promise((resolve, reject) =>
+ AddonManager.getAllAddons(resolve));
+
+ // Completely remove cache if caching is not enabled
+ if (!this.cacheEnabled) {
+ logger.debug("Clearing cache because it is disabled");
+ return this._clearCache();
+ }
+
+ // Tycho: let ids = [a.id for (a of allAddons)];
+ let ids = [];
+ for (let a of allAddons) {
+ ids.push(a.id);
+ }
+
+ logger.debug("Repopulate add-on cache with " + ids.toSource());
+
+ let self = this;
+ let addonsToCache = yield new Promise((resolve, reject) =>
+ getAddonsToCache(ids, resolve));
+
+ // Completely remove cache if there are no add-ons to cache
+ if (addonsToCache.length == 0) {
+ logger.debug("Clearing cache because 0 add-ons were requested");
+ return this._clearCache();
+ }
+
+ yield new Promise((resolve, reject) =>
+ self._beginGetAddons(addonsToCache, {
+ searchSucceeded: function repopulateCacheInternal_searchSucceeded(aAddons) {
+ self._addons = new Map();
+ for (let addon of aAddons) {
+ self._addons.set(addon.id, addon);
+ }
+ AddonDatabase.repopulate(aAddons, resolve);
+ },
+ searchFailed: function repopulateCacheInternal_searchFailed() {
+ logger.warn("Search failed when repopulating cache");
+ resolve();
+ }
+ }, aSendPerformance, aTimeout));
+
+ // Always call AddonManager updateAddonRepositoryData after we refill the cache
+ yield new Promise((resolve, reject) =>
+ AddonManagerPrivate.updateAddonRepositoryData(resolve));
+ }),
+
+ /**
+ * Asynchronously add add-ons to the cache corresponding to the specified
+ * ids. If caching is disabled, the cache is unchanged and the callback is
+ * immediately called if it is defined.
+ *
+ * @param aIds
+ * The array of add-on ids to add to the cache
+ * @param aCallback
+ * The optional callback to call once complete
+ */
+ cacheAddons: function AddonRepo_cacheAddons(aIds, aCallback) {
+ logger.debug("cacheAddons: enabled " + this.cacheEnabled + " IDs " + aIds.toSource());
+ if (!this.cacheEnabled) {
+ if (aCallback)
+ aCallback();
+ return;
+ }
+
+ let self = this;
+ getAddonsToCache(aIds, function cacheAddons_getAddonsToCache(aAddons) {
+ // If there are no add-ons to cache, act as if caching is disabled
+ if (aAddons.length == 0) {
+ if (aCallback)
+ aCallback();
+ return;
+ }
+
+ self.getAddonsByIDs(aAddons, {
+ searchSucceeded: function cacheAddons_searchSucceeded(aAddons) {
+ for (let addon of aAddons) {
+ self._addons.set(addon.id, addon);
+ }
+ AddonDatabase.insertAddons(aAddons, aCallback);
+ },
+ searchFailed: function cacheAddons_searchFailed() {
+ logger.warn("Search failed when adding add-ons to cache");
+ if (aCallback)
+ aCallback();
+ }
+ });
+ });
+ },
+
+ /**
+ * The homepage for visiting this repository. If the corresponding preference
+ * is not defined, defaults to about:blank.
+ */
+ get homepageURL() {
+ let url = this._formatURLPref(PREF_GETADDONS_BROWSEADDONS, {});
+ return (url != null) ? url : "about:blank";
+ },
+
+ /**
+ * Returns whether this instance is currently performing a search. New
+ * searches will not be performed while this is the case.
+ */
+ get isSearching() {
+ return this._searching;
+ },
+
+ /**
+ * The url that can be visited to see recommended add-ons in this repository.
+ * If the corresponding preference is not defined, defaults to about:blank.
+ */
+ getRecommendedURL: function AddonRepo_getRecommendedURL() {
+ let url = this._formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED, {});
+ return (url != null) ? url : "about:blank";
+ },
+
+ /**
+ * Retrieves the url that can be visited to see search results for the given
+ * terms. If the corresponding preference is not defined, defaults to
+ * about:blank.
+ *
+ * @param aSearchTerms
+ * Search terms used to search the repository
+ */
+ getSearchURL: function AddonRepo_getSearchURL(aSearchTerms) {
+ let url = this._formatURLPref(PREF_GETADDONS_BROWSESEARCHRESULTS, {
+ TERMS : encodeURIComponent(aSearchTerms)
+ });
+ return (url != null) ? url : "about:blank";
+ },
+
+ /**
+ * Cancels the search in progress. If there is no search in progress this
+ * does nothing.
+ */
+ cancelSearch: function AddonRepo_cancelSearch() {
+ this._searching = false;
+ if (this._request) {
+ this._request.abort();
+ this._request = null;
+ }
+ this._callback = null;
+ },
+
+ /**
+ * Begins a search for add-ons in this repository by ID. Results will be
+ * passed to the given callback.
+ *
+ * @param aIDs
+ * The array of ids to search for
+ * @param aCallback
+ * The callback to pass results to
+ */
+ getAddonsByIDs: function AddonRepo_getAddonsByIDs(aIDs, aCallback) {
+ return this._beginGetAddons(aIDs, aCallback, false);
+ },
+
+ /**
+ * Begins a search of add-ons, potentially sending performance data.
+ *
+ * @param aIDs
+ * Array of ids to search for.
+ * @param aCallback
+ * Function to pass results to.
+ * @param aSendPerformance
+ * Boolean indicating whether to send performance data with the
+ * request.
+ * @param aTimeout
+ * (Optional) timeout in milliseconds to abandon the XHR request
+ * if we have not received a response from the server.
+ */
+ _beginGetAddons: function(aIDs, aCallback, aSendPerformance, aTimeout) {
+ let ids = aIDs.slice(0);
+
+ let params = {
+ API_VERSION : API_VERSION,
+ IDS : ids.map(encodeURIComponent).join(',')
+ };
+
+ let pref = PREF_GETADDONS_BYIDS;
+
+ if (aSendPerformance) {
+ let type = Services.prefs.getPrefType(PREF_GETADDONS_BYIDS_PERFORMANCE);
+ if (type == Services.prefs.PREF_STRING) {
+ pref = PREF_GETADDONS_BYIDS_PERFORMANCE;
+
+ let startupInfo = Cc["@mozilla.org/toolkit/app-startup;1"].
+ getService(Ci.nsIAppStartup).
+ getStartupInfo();
+
+ params.TIME_MAIN = "";
+ params.TIME_FIRST_PAINT = "";
+ params.TIME_SESSION_RESTORED = "";
+ if (startupInfo.process) {
+ if (startupInfo.main) {
+ params.TIME_MAIN = startupInfo.main - startupInfo.process;
+ }
+ if (startupInfo.firstPaint) {
+ params.TIME_FIRST_PAINT = startupInfo.firstPaint -
+ startupInfo.process;
+ }
+ if (startupInfo.sessionRestored) {
+ params.TIME_SESSION_RESTORED = startupInfo.sessionRestored -
+ startupInfo.process;
+ }
+ }
+ }
+ }
+
+ let url = this._formatURLPref(pref, params);
+
+ let self = this;
+ function handleResults(aElements, aTotalResults, aCompatData) {
+ // Don't use this._parseAddons() so that, for example,
+ // incompatible add-ons are not filtered out
+ let results = [];
+ for (let i = 0; i < aElements.length && results.length < self._maxResults; i++) {
+ let result = self._parseAddon(aElements[i], null, aCompatData);
+ if (result == null)
+ continue;
+
+ // Ignore add-on if it wasn't actually requested
+ let idIndex = ids.indexOf(result.addon.id);
+ if (idIndex == -1)
+ continue;
+
+ // Ignore add-on if the add-on manager doesn't know about its type:
+ if (!(result.addon.type in AddonManager.addonTypes)) {
+ continue;
+ }
+
+ results.push(result);
+ // Ignore this add-on from now on
+ ids.splice(idIndex, 1);
+ }
+
+ // Include any compatibility overrides for addons not hosted by the
+ // remote repository.
+ for each (let addonCompat in aCompatData) {
+ if (addonCompat.hosted)
+ continue;
+
+ let addon = new AddonSearchResult(addonCompat.id);
+ // Compatibility overrides can only be for extensions.
+ addon.type = "extension";
+ addon.compatibilityOverrides = addonCompat.compatRanges;
+ let result = {
+ addon: addon,
+ xpiURL: null,
+ xpiHash: null
+ };
+ results.push(result);
+ }
+
+ // aTotalResults irrelevant
+ self._reportSuccess(results, -1);
+ }
+
+ this._beginSearch(url, ids.length, aCallback, handleResults, aTimeout);
+ },
+
+ /**
+ * Performs the daily background update check.
+ *
+ * This API both searches for the add-on IDs specified and sends performance
+ * data. It is meant to be called as part of the daily update ping. It should
+ * not be used for any other purpose. Use repopulateCache instead.
+ *
+ * @return Promise{null} Resolves when the metadata update is complete.
+ */
+ backgroundUpdateCheck: function () {
+ return this._repopulateCacheInternal(true);
+ },
+
+ /**
+ * Begins a search for recommended add-ons in this repository. Results will
+ * be passed to the given callback.
+ *
+ * @param aMaxResults
+ * The maximum number of results to return
+ * @param aCallback
+ * The callback to pass results to
+ */
+ retrieveRecommendedAddons: function AddonRepo_retrieveRecommendedAddons(aMaxResults, aCallback) {
+ let url = this._formatURLPref(PREF_GETADDONS_GETRECOMMENDED, {
+ API_VERSION : API_VERSION,
+
+ // Get twice as many results to account for potential filtering
+ MAX_RESULTS : 2 * aMaxResults
+ });
+
+ let self = this;
+ function handleResults(aElements, aTotalResults) {
+ self._getLocalAddonIds(function retrieveRecommendedAddons_getLocalAddonIds(aLocalAddonIds) {
+ // aTotalResults irrelevant
+ self._parseAddons(aElements, -1, aLocalAddonIds);
+ });
+ }
+
+ this._beginSearch(url, aMaxResults, aCallback, handleResults);
+ },
+
+ /**
+ * Begins a search for add-ons in this repository. Results will be passed to
+ * the given callback.
+ *
+ * @param aSearchTerms
+ * The terms to search for
+ * @param aMaxResults
+ * The maximum number of results to return
+ * @param aCallback
+ * The callback to pass results to
+ */
+ searchAddons: function AddonRepo_searchAddons(aSearchTerms, aMaxResults, aCallback) {
+ let compatMode = "normal";
+ if (!AddonManager.checkCompatibility)
+ compatMode = "ignore";
+ else if (AddonManager.strictCompatibility)
+ compatMode = "strict";
+
+ let substitutions = {
+ API_VERSION : API_VERSION,
+ TERMS : encodeURIComponent(aSearchTerms),
+ // Get twice as many results to account for potential filtering
+ MAX_RESULTS : 2 * aMaxResults,
+ COMPATIBILITY_MODE : compatMode,
+ };
+
+ let url = this._formatURLPref(PREF_GETADDONS_GETSEARCHRESULTS, substitutions);
+
+ let self = this;
+ function handleResults(aElements, aTotalResults) {
+ self._getLocalAddonIds(function searchAddons_getLocalAddonIds(aLocalAddonIds) {
+ self._parseAddons(aElements, aTotalResults, aLocalAddonIds);
+ });
+ }
+
+ this._beginSearch(url, aMaxResults, aCallback, handleResults);
+ },
+
+ // Posts results to the callback
+ _reportSuccess: function AddonRepo_reportSuccess(aResults, aTotalResults) {
+ this._searching = false;
+ this._request = null;
+ // The callback may want to trigger a new search so clear references early
+ // Tycho: let addons = [result.addon for each(result in aResults)];
+ let addons = [];
+ for each(let result in aResults) {
+ addons.push(result.addon);
+ }
+
+ let callback = this._callback;
+ this._callback = null;
+ callback.searchSucceeded(addons, addons.length, aTotalResults);
+ },
+
+ // Notifies the callback of a failure
+ _reportFailure: function AddonRepo_reportFailure() {
+ this._searching = false;
+ this._request = null;
+ // The callback may want to trigger a new search so clear references early
+ let callback = this._callback;
+ this._callback = null;
+ callback.searchFailed();
+ },
+
+ // Get descendant by unique tag name. Returns null if not unique tag name.
+ _getUniqueDescendant: function AddonRepo_getUniqueDescendant(aElement, aTagName) {
+ let elementsList = aElement.getElementsByTagName(aTagName);
+ return (elementsList.length == 1) ? elementsList[0] : null;
+ },
+
+ // Get direct descendant by unique tag name.
+ // Returns null if not unique tag name.
+ _getUniqueDirectDescendant: function AddonRepo_getUniqueDirectDescendant(aElement, aTagName) {
+ let elementsList = Array.filter(aElement.children,
+ function arrayFiltering(aChild) aChild.tagName == aTagName);
+ return (elementsList.length == 1) ? elementsList[0] : null;
+ },
+
+ // Parse out trimmed text content. Returns null if text content empty.
+ _getTextContent: function AddonRepo_getTextContent(aElement) {
+ let textContent = aElement.textContent.trim();
+ return (textContent.length > 0) ? textContent : null;
+ },
+
+ // Parse out trimmed text content of a descendant with the specified tag name
+ // Returns null if the parsing unsuccessful.
+ _getDescendantTextContent: function AddonRepo_getDescendantTextContent(aElement, aTagName) {
+ let descendant = this._getUniqueDescendant(aElement, aTagName);
+ return (descendant != null) ? this._getTextContent(descendant) : null;
+ },
+
+ // Parse out trimmed text content of a direct descendant with the specified
+ // tag name.
+ // Returns null if the parsing unsuccessful.
+ _getDirectDescendantTextContent: function AddonRepo_getDirectDescendantTextContent(aElement, aTagName) {
+ let descendant = this._getUniqueDirectDescendant(aElement, aTagName);
+ return (descendant != null) ? this._getTextContent(descendant) : null;
+ },
+
+ /*
+ * Creates an AddonSearchResult by parsing an <addon> element
+ *
+ * @param aElement
+ * The <addon> element to parse
+ * @param aSkip
+ * Object containing ids and sourceURIs of add-ons to skip.
+ * @param aCompatData
+ * Array of parsed addon_compatibility elements to accosiate with the
+ * resulting AddonSearchResult. Optional.
+ * @return Result object containing the parsed AddonSearchResult, xpiURL and
+ * xpiHash if the parsing was successful. Otherwise returns null.
+ */
+ _parseAddon: function AddonRepo_parseAddon(aElement, aSkip, aCompatData) {
+ let skipIDs = (aSkip && aSkip.ids) ? aSkip.ids : [];
+ let skipSourceURIs = (aSkip && aSkip.sourceURIs) ? aSkip.sourceURIs : [];
+
+ let guid = this._getDescendantTextContent(aElement, "guid");
+ if (guid == null || skipIDs.indexOf(guid) != -1)
+ return null;
+
+ let addon = new AddonSearchResult(guid);
+ let result = {
+ addon: addon,
+ xpiURL: null,
+ xpiHash: null
+ };
+
+ if (aCompatData && guid in aCompatData)
+ addon.compatibilityOverrides = aCompatData[guid].compatRanges;
+
+ let self = this;
+ for (let node = aElement.firstChild; node; node = node.nextSibling) {
+ if (!(node instanceof Ci.nsIDOMElement))
+ continue;
+
+ let localName = node.localName;
+
+ // Handle case where the wanted string value is located in text content
+ // but only if the content is not empty
+ if (localName in STRING_KEY_MAP) {
+ addon[STRING_KEY_MAP[localName]] = this._getTextContent(node) || addon[STRING_KEY_MAP[localName]];
+ continue;
+ }
+
+ // Handle case where the wanted string value is html located in text content
+ if (localName in HTML_KEY_MAP) {
+ addon[HTML_KEY_MAP[localName]] = convertHTMLToPlainText(this._getTextContent(node));
+ continue;
+ }
+
+ // Handle case where the wanted integer value is located in text content
+ if (localName in INTEGER_KEY_MAP) {
+ let value = parseInt(this._getTextContent(node));
+ if (value >= 0)
+ addon[INTEGER_KEY_MAP[localName]] = value;
+ continue;
+ }
+
+ // Handle cases that aren't as simple as grabbing the text content
+ switch (localName) {
+ case "type":
+ // Map AMO's type id to corresponding string
+ // https://github.com/mozilla/olympia/blob/master/apps/constants/base.py#L127
+ // These definitions need to be updated whenever AMO adds a new type.
+ let id = parseInt(node.getAttribute("id"));
+ switch (id) {
+ case 1:
+ addon.type = "extension";
+ break;
+ case 2:
+ addon.type = "theme";
+ break;
+ case 3:
+ addon.type = "dictionary";
+ break;
+ case 4:
+ addon.type = "search";
+ break;
+ case 5:
+ case 6:
+ addon.type = "locale";
+ break;
+ case 7:
+ addon.type = "plugin";
+ break;
+ case 8:
+ addon.type = "api";
+ break;
+ case 9:
+ addon.type = "lightweight-theme";
+ break;
+ case 11:
+ addon.type = "webapp";
+ break;
+ default:
+ logger.info("Unknown type id " + id + " found when parsing response for GUID " + guid);
+ }
+ break;
+ case "authors":
+ let authorNodes = node.getElementsByTagName("author");
+ for (let authorNode of authorNodes) {
+ let name = self._getDescendantTextContent(authorNode, "name");
+ let link = self._getDescendantTextContent(authorNode, "link");
+ if (name == null || link == null)
+ continue;
+
+ let author = new AddonManagerPrivate.AddonAuthor(name, link);
+ if (addon.creator == null)
+ addon.creator = author;
+ else {
+ if (addon.developers == null)
+ addon.developers = [];
+
+ addon.developers.push(author);
+ }
+ }
+ break;
+ case "previews":
+ let previewNodes = node.getElementsByTagName("preview");
+ for (let previewNode of previewNodes) {
+ let full = self._getUniqueDescendant(previewNode, "full");
+ if (full == null)
+ continue;
+
+ let fullURL = self._getTextContent(full);
+ let fullWidth = full.getAttribute("width");
+ let fullHeight = full.getAttribute("height");
+
+ let thumbnailURL, thumbnailWidth, thumbnailHeight;
+ let thumbnail = self._getUniqueDescendant(previewNode, "thumbnail");
+ if (thumbnail) {
+ thumbnailURL = self._getTextContent(thumbnail);
+ thumbnailWidth = thumbnail.getAttribute("width");
+ thumbnailHeight = thumbnail.getAttribute("height");
+ }
+ let caption = self._getDescendantTextContent(previewNode, "caption");
+ let screenshot = new AddonManagerPrivate.AddonScreenshot(fullURL, fullWidth, fullHeight,
+ thumbnailURL, thumbnailWidth,
+ thumbnailHeight, caption);
+
+ if (addon.screenshots == null)
+ addon.screenshots = [];
+
+ if (previewNode.getAttribute("primary") == 1)
+ addon.screenshots.unshift(screenshot);
+ else
+ addon.screenshots.push(screenshot);
+ }
+ break;
+ case "learnmore":
+ addon.learnmoreURL = this._getTextContent(node);
+ addon.homepageURL = addon.homepageURL || addon.learnmoreURL;
+ break;
+ case "contribution_data":
+ let meetDevelopers = this._getDescendantTextContent(node, "meet_developers");
+ let suggestedAmount = this._getDescendantTextContent(node, "suggested_amount");
+ if (meetDevelopers != null) {
+ addon.contributionURL = meetDevelopers;
+ addon.contributionAmount = suggestedAmount;
+ }
+ break
+ case "payment_data":
+ let link = this._getDescendantTextContent(node, "link");
+ let amountTag = this._getUniqueDescendant(node, "amount");
+ let amount = parseFloat(amountTag.getAttribute("amount"));
+ let displayAmount = this._getTextContent(amountTag);
+ if (link != null && amount != null && displayAmount != null) {
+ addon.purchaseURL = link;
+ addon.purchaseAmount = amount;
+ addon.purchaseDisplayAmount = displayAmount;
+ }
+ break
+ case "rating":
+ let averageRating = parseInt(this._getTextContent(node));
+ if (averageRating >= 0)
+ addon.averageRating = Math.min(5, averageRating);
+ break;
+ case "reviews":
+ let url = this._getTextContent(node);
+ let num = parseInt(node.getAttribute("num"));
+ if (url != null && num >= 0) {
+ addon.reviewURL = url;
+ addon.reviewCount = num;
+ }
+ break;
+ case "status":
+ let repositoryStatus = parseInt(node.getAttribute("id"));
+ if (!isNaN(repositoryStatus))
+ addon.repositoryStatus = repositoryStatus;
+ break;
+ case "all_compatible_os":
+ let nodes = node.getElementsByTagName("os");
+ addon.isPlatformCompatible = Array.some(nodes, function parseAddon_platformCompatFilter(aNode) {
+ let text = aNode.textContent.toLowerCase().trim();
+ return text == "all" || text == Services.appinfo.OS.toLowerCase();
+ });
+ break;
+ case "install":
+ // No os attribute means the xpi is compatible with any os
+ if (node.hasAttribute("os")) {
+ let os = node.getAttribute("os").trim().toLowerCase();
+ // If the os is not ALL and not the current OS then ignore this xpi
+ if (os != "all" && os != Services.appinfo.OS.toLowerCase())
+ break;
+ }
+
+ let xpiURL = this._getTextContent(node);
+ if (xpiURL == null)
+ break;
+
+ if (skipSourceURIs.indexOf(xpiURL) != -1)
+ return null;
+
+ result.xpiURL = xpiURL;
+ addon.sourceURI = NetUtil.newURI(xpiURL);
+
+ let size = parseInt(node.getAttribute("size"));
+ addon.size = (size >= 0) ? size : null;
+
+ let xpiHash = node.getAttribute("hash");
+ if (xpiHash != null)
+ xpiHash = xpiHash.trim();
+ result.xpiHash = xpiHash ? xpiHash : null;
+ break;
+ case "last_updated":
+ let epoch = parseInt(node.getAttribute("epoch"));
+ if (!isNaN(epoch))
+ addon.updateDate = new Date(1000 * epoch);
+ break;
+ case "icon":
+ addon.icons[node.getAttribute("size")] = this._getTextContent(node);
+ break;
+ }
+ }
+
+ return result;
+ },
+
+ _parseAddons: function AddonRepo_parseAddons(aElements, aTotalResults, aSkip) {
+ let self = this;
+ let results = [];
+
+ function isSameApplication(aAppNode) {
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ if (self._getTextContent(aAppNode) == Services.appinfo.ID ||
+ self._getTextContent(aAppNode) == FIREFOX_ID) {
+#else
+ if (self._getTextContent(aAppNode) == Services.appinfo.ID) {
+#endif
+ return true;
+ }
+ return false;
+ }
+
+ for (let i = 0; i < aElements.length && results.length < this._maxResults; i++) {
+ let element = aElements[i];
+
+ let tags = this._getUniqueDescendant(element, "compatible_applications");
+ if (tags == null)
+ continue;
+
+ let applications = tags.getElementsByTagName("appID");
+ let compatible = Array.some(applications, function parseAddons_applicationsCompatFilter(aAppNode) {
+ if (!isSameApplication(aAppNode))
+ return false;
+
+ let parent = aAppNode.parentNode;
+ let minVersion = self._getDescendantTextContent(parent, "min_version");
+ let maxVersion = self._getDescendantTextContent(parent, "max_version");
+ if (minVersion == null || maxVersion == null)
+ return false;
+
+ let currentVersion = Services.appinfo.version;
+ return (Services.vc.compare(minVersion, currentVersion) <= 0 &&
+ ((!AddonManager.strictCompatibility) ||
+ Services.vc.compare(currentVersion, maxVersion) <= 0));
+ });
+
+ // Ignore add-ons not compatible with this Application
+ if (!compatible) {
+ if (AddonManager.checkCompatibility)
+ continue;
+
+ if (!Array.some(applications, isSameApplication))
+ continue;
+ }
+
+ // Add-on meets all requirements, so parse out data.
+ // Don't pass in compatiblity override data, because that's only returned
+ // in GUID searches, which don't use _parseAddons().
+ let result = this._parseAddon(element, aSkip);
+ if (result == null)
+ continue;
+
+ // Ignore add-on missing a required attribute
+ let requiredAttributes = ["id", "name", "version", "type", "creator"];
+ if (requiredAttributes.some(function parseAddons_attributeFilter(aAttribute) !result.addon[aAttribute]))
+ continue;
+
+ // Ignore add-on with a type AddonManager doesn't understand:
+ if (!(result.addon.type in AddonManager.addonTypes))
+ continue;
+
+ // Add only if the add-on is compatible with the platform
+ if (!result.addon.isPlatformCompatible)
+ continue;
+
+ // Add only if there was an xpi compatible with this OS or there was a
+ // way to purchase the add-on
+ if (!result.xpiURL && !result.addon.purchaseURL)
+ continue;
+
+ result.addon.isCompatible = compatible;
+
+ results.push(result);
+ // Ignore this add-on from now on by adding it to the skip array
+ aSkip.ids.push(result.addon.id);
+ }
+
+ // Immediately report success if no AddonInstall instances to create
+ let pendingResults = results.length;
+ if (pendingResults == 0) {
+ this._reportSuccess(results, aTotalResults);
+ return;
+ }
+
+ // Create an AddonInstall for each result
+ results.forEach(function(aResult) {
+ let addon = aResult.addon;
+ let callback = function addonInstallCallback(aInstall) {
+ addon.install = aInstall;
+ pendingResults--;
+ if (pendingResults == 0)
+ self._reportSuccess(results, aTotalResults);
+ }
+
+ if (aResult.xpiURL) {
+ AddonManager.getInstallForURL(aResult.xpiURL, callback,
+ "application/x-xpinstall", aResult.xpiHash,
+ addon.name, addon.icons, addon.version);
+ }
+ else {
+ callback(null);
+ }
+ });
+ },
+
+ // Parses addon_compatibility nodes, that describe compatibility overrides.
+ _parseAddonCompatElement: function AddonRepo_parseAddonCompatElement(aResultObj, aElement) {
+ let guid = this._getDescendantTextContent(aElement, "guid");
+ if (!guid) {
+ logger.debug("Compatibility override is missing guid.");
+ return;
+ }
+
+ let compat = {id: guid};
+ compat.hosted = aElement.getAttribute("hosted") != "false";
+
+ function findMatchingAppRange(aNodes) {
+ let toolkitAppRange = null;
+ for (let node of aNodes) {
+ let appID = this._getDescendantTextContent(node, "appID");
+ if (appID != Services.appinfo.ID && appID != TOOLKIT_ID)
+ continue;
+
+ let minVersion = this._getDescendantTextContent(node, "min_version");
+ let maxVersion = this._getDescendantTextContent(node, "max_version");
+ if (minVersion == null || maxVersion == null)
+ continue;
+
+ let appRange = { appID: appID,
+ appMinVersion: minVersion,
+ appMaxVersion: maxVersion };
+
+ // Only use Toolkit app ranges if no ranges match the application ID.
+ if (appID == TOOLKIT_ID)
+ toolkitAppRange = appRange;
+ else
+ return appRange;
+ }
+ return toolkitAppRange;
+ }
+
+ function parseRangeNode(aNode) {
+ let type = aNode.getAttribute("type");
+ // Only "incompatible" (blacklisting) is supported for now.
+ if (type != "incompatible") {
+ logger.debug("Compatibility override of unsupported type found.");
+ return null;
+ }
+
+ let override = new AddonManagerPrivate.AddonCompatibilityOverride(type);
+
+ override.minVersion = this._getDirectDescendantTextContent(aNode, "min_version");
+ override.maxVersion = this._getDirectDescendantTextContent(aNode, "max_version");
+
+ if (!override.minVersion) {
+ logger.debug("Compatibility override is missing min_version.");
+ return null;
+ }
+ if (!override.maxVersion) {
+ logger.debug("Compatibility override is missing max_version.");
+ return null;
+ }
+
+ let appRanges = aNode.querySelectorAll("compatible_applications > application");
+ let appRange = findMatchingAppRange.bind(this)(appRanges);
+ if (!appRange) {
+ logger.debug("Compatibility override is missing a valid application range.");
+ return null;
+ }
+
+ override.appID = appRange.appID;
+ override.appMinVersion = appRange.appMinVersion;
+ override.appMaxVersion = appRange.appMaxVersion;
+
+ return override;
+ }
+
+ let rangeNodes = aElement.querySelectorAll("version_ranges > version_range");
+ compat.compatRanges = Array.map(rangeNodes, parseRangeNode.bind(this))
+ .filter(function compatRangesFilter(aItem) !!aItem);
+ if (compat.compatRanges.length == 0)
+ return;
+
+ aResultObj[compat.id] = compat;
+ },
+
+ // Parses addon_compatibility elements.
+ _parseAddonCompatData: function AddonRepo_parseAddonCompatData(aElements) {
+ let compatData = {};
+ Array.forEach(aElements, this._parseAddonCompatElement.bind(this, compatData));
+ return compatData;
+ },
+
+ // Begins a new search if one isn't currently executing
+ _beginSearch: function(aURI, aMaxResults, aCallback, aHandleResults, aTimeout) {
+ if (this._searching || aURI == null || aMaxResults <= 0) {
+ logger.warn("AddonRepository search failed: searching " + this._searching + " aURI " + aURI +
+ " aMaxResults " + aMaxResults);
+ aCallback.searchFailed();
+ return;
+ }
+
+ this._searching = true;
+ this._callback = aCallback;
+ this._maxResults = aMaxResults;
+
+ logger.debug("Requesting " + aURI);
+
+ this._request = new XHRequest();
+ this._request.mozBackgroundRequest = true;
+ this._request.open("GET", aURI, true);
+ this._request.overrideMimeType("text/xml");
+ if (aTimeout) {
+ this._request.timeout = aTimeout;
+ }
+
+ this._request.addEventListener("error", aEvent => this._reportFailure(), false);
+ this._request.addEventListener("timeout", aEvent => this._reportFailure(), false);
+ this._request.addEventListener("load", aEvent => {
+ logger.debug("Got metadata search load event");
+ let request = aEvent.target;
+ let responseXML = request.responseXML;
+
+ if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
+ (request.status != 200 && request.status != 0)) {
+ this._reportFailure();
+ return;
+ }
+
+ let documentElement = responseXML.documentElement;
+ let elements = documentElement.getElementsByTagName("addon");
+ let totalResults = elements.length;
+ let parsedTotalResults = parseInt(documentElement.getAttribute("total_results"));
+ // Parsed value of total results only makes sense if >= elements.length
+ if (parsedTotalResults >= totalResults)
+ totalResults = parsedTotalResults;
+
+ let compatElements = documentElement.getElementsByTagName("addon_compatibility");
+ let compatData = this._parseAddonCompatData(compatElements);
+
+ aHandleResults(elements, totalResults, compatData);
+ }, false);
+ this._request.send(null);
+ },
+
+ // Gets the id's of local add-ons, and the sourceURI's of local installs,
+ // passing the results to aCallback
+ _getLocalAddonIds: function AddonRepo_getLocalAddonIds(aCallback) {
+ let self = this;
+ let localAddonIds = {ids: null, sourceURIs: null};
+
+ AddonManager.getAllAddons(function getLocalAddonIds_getAllAddons(aAddons) {
+ // Tycho: localAddonIds.ids = [a.id for each (a in aAddons)];
+ localAddonIds.ids = [];
+
+ for each(let a in aAddons) {
+ localAddonIds.ids.push(a.id);
+ }
+
+ if (localAddonIds.sourceURIs)
+ aCallback(localAddonIds);
+ });
+
+ AddonManager.getAllInstalls(function getLocalAddonIds_getAllInstalls(aInstalls) {
+ localAddonIds.sourceURIs = [];
+ aInstalls.forEach(function(aInstall) {
+ if (aInstall.state != AddonManager.STATE_AVAILABLE)
+ localAddonIds.sourceURIs.push(aInstall.sourceURI.spec);
+ });
+
+ if (localAddonIds.ids)
+ aCallback(localAddonIds);
+ });
+ },
+
+ // Create url from preference, returning null if preference does not exist
+ _formatURLPref: function AddonRepo_formatURLPref(aPreference, aSubstitutions) {
+ let url = null;
+ try {
+ url = Services.prefs.getCharPref(aPreference);
+ } catch(e) {
+ logger.warn("_formatURLPref: Couldn't get pref: " + aPreference);
+ return null;
+ }
+
+ url = url.replace(/%([A-Z_]+)%/g, function urlSubstitution(aMatch, aKey) {
+ return (aKey in aSubstitutions) ? aSubstitutions[aKey] : aMatch;
+ });
+
+ return Services.urlFormatter.formatURL(url);
+ },
+
+ // Find a AddonCompatibilityOverride that matches a given aAddonVersion and
+ // application/platform version.
+ findMatchingCompatOverride: function AddonRepo_findMatchingCompatOverride(aAddonVersion,
+ aCompatOverrides,
+ aAppVersion,
+ aPlatformVersion) {
+ for (let override of aCompatOverrides) {
+
+ let appVersion = null;
+ if (override.appID == TOOLKIT_ID)
+ appVersion = aPlatformVersion || Services.appinfo.platformVersion;
+ else
+ appVersion = aAppVersion || Services.appinfo.version;
+
+ if (Services.vc.compare(override.minVersion, aAddonVersion) <= 0 &&
+ Services.vc.compare(aAddonVersion, override.maxVersion) <= 0 &&
+ Services.vc.compare(override.appMinVersion, appVersion) <= 0 &&
+ Services.vc.compare(appVersion, override.appMaxVersion) <= 0) {
+ return override;
+ }
+ }
+ return null;
+ },
+
+ flush: function() {
+ return AddonDatabase.flush();
+ }
+};
+
+var AddonDatabase = {
+ // false if there was an unrecoverable error opening the database
+ databaseOk: true,
+
+ connectionPromise: null,
+ // the in-memory database
+ DB: BLANK_DB(),
+
+ /**
+ * A getter to retrieve the path to the DB
+ */
+ get jsonFile() {
+ return OS.Path.join(OS.Constants.Path.profileDir, FILE_DATABASE);
+ },
+
+ /**
+ * Asynchronously opens a new connection to the database file.
+ *
+ * @return {Promise} a promise that resolves to the database.
+ */
+ openConnection: function() {
+ if (!this.connectionPromise) {
+ this.connectionPromise = Task.spawn(function*() {
+ this.DB = BLANK_DB();
+
+ let inputDB, schema;
+
+ try {
+ let data = yield OS.File.read(this.jsonFile, { encoding: "utf-8"})
+ inputDB = JSON.parse(data);
+
+ if (!inputDB.hasOwnProperty("addons") ||
+ !Array.isArray(inputDB.addons)) {
+ throw new Error("No addons array.");
+ }
+
+ if (!inputDB.hasOwnProperty("schema")) {
+ throw new Error("No schema specified.");
+ }
+
+ schema = parseInt(inputDB.schema, 10);
+
+ if (!Number.isInteger(schema) ||
+ schema < DB_MIN_JSON_SCHEMA) {
+ throw new Error("Invalid schema value.");
+ }
+ } catch (e if e instanceof OS.File.Error && e.becauseNoSuchFile) {
+ logger.debug("No " + FILE_DATABASE + " found.");
+
+ // Create a blank addons.json file
+ this._saveDBToDisk();
+
+ let dbSchema = 0;
+ try {
+ dbSchema = Services.prefs.getIntPref(PREF_GETADDONS_DB_SCHEMA);
+ } catch (e) {}
+
+ if (dbSchema < DB_MIN_JSON_SCHEMA) {
+ let results = yield new Promise((resolve, reject) => {
+ AddonRepository_SQLiteMigrator.migrate(resolve);
+ });
+
+ if (results.length) {
+ yield this._insertAddons(results);
+ }
+
+ Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
+ }
+
+ return this.DB;
+ } catch (e) {
+ logger.error("Malformed " + FILE_DATABASE + ": " + e);
+ this.databaseOk = false;
+
+ return this.DB;
+ }
+
+ Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
+
+ // We use _insertAddon manually instead of calling
+ // insertAddons to avoid the write to disk which would
+ // be a waste since this is the data that was just read.
+ for (let addon of inputDB.addons) {
+ this._insertAddon(addon);
+ }
+
+ return this.DB;
+ }.bind(this));
+ }
+
+ return this.connectionPromise;
+ },
+
+ /**
+ * A lazy getter for the database connection.
+ */
+ get connection() {
+ return this.openConnection();
+ },
+
+ /**
+ * Asynchronously shuts down the database connection and releases all
+ * cached objects
+ *
+ * @param aCallback
+ * An optional callback to call once complete
+ * @param aSkipFlush
+ * An optional boolean to skip flushing data to disk. Useful
+ * when the database is going to be deleted afterwards.
+ */
+ shutdown: function AD_shutdown(aSkipFlush) {
+ this.databaseOk = true;
+
+ if (!this.connectionPromise) {
+ return Promise.resolve();
+ }
+
+ this.connectionPromise = null;
+
+ if (aSkipFlush) {
+ return Promise.resolve();
+ } else {
+ return this.Writer.flush();
+ }
+ },
+
+ /**
+ * Asynchronously deletes the database, shutting down the connection
+ * first if initialized
+ *
+ * @param aCallback
+ * An optional callback to call once complete
+ * @return Promise{null} resolves when the database has been deleted
+ */
+ delete: function AD_delete(aCallback) {
+ this.DB = BLANK_DB();
+
+ this._deleting = this.Writer.flush()
+ .then(null, () => {})
+ // shutdown(true) never rejects
+ .then(() => this.shutdown(true))
+ .then(() => OS.File.remove(this.jsonFile, {}))
+ .then(null, error => logger.error("Unable to delete Addon Repository file " +
+ this.jsonFile, error))
+ .then(() => this._deleting = null)
+ .then(aCallback);
+ return this._deleting;
+ },
+
+ toJSON: function AD_toJSON() {
+ let json = {
+ schema: this.DB.schema,
+ addons: []
+ }
+
+ for (let [, value] of this.DB.addons)
+ json.addons.push(value);
+
+ return json;
+ },
+
+ /*
+ * This is a deferred task writer that is used
+ * to batch operations done within 50ms of each
+ * other and thus generating only one write to disk
+ */
+ get Writer() {
+ delete this.Writer;
+ this.Writer = new DeferredSave(
+ this.jsonFile,
+ () => { return JSON.stringify(this); },
+ DB_BATCH_TIMEOUT_MS
+ );
+ return this.Writer;
+ },
+
+ /**
+ * Flush any pending I/O on the addons.json file
+ * @return: Promise{null}
+ * Resolves when the pending I/O (writing out or deleting
+ * addons.json) completes
+ */
+ flush: function() {
+ if (this._deleting) {
+ return this._deleting;
+ }
+ return this.Writer.flush();
+ },
+
+ /**
+ * Asynchronously retrieve all add-ons from the database
+ * @return: Promise{Map}
+ * Resolves when the add-ons are retrieved from the database
+ */
+ retrieveStoredData: function (){
+ return this.openConnection().then(db => db.addons);
+ },
+
+ /**
+ * Asynchronously repopulates the database so it only contains the
+ * specified add-ons
+ *
+ * @param aAddons
+ * The array of add-ons to repopulate the database with
+ * @param aCallback
+ * An optional callback to call once complete
+ */
+ repopulate: function AD_repopulate(aAddons, aCallback) {
+ this.DB.addons.clear();
+ this.insertAddons(aAddons, function repopulate_insertAddons() {
+ let now = Math.round(Date.now() / 1000);
+ logger.debug("Cache repopulated, setting " + PREF_METADATA_LASTUPDATE + " to " + now);
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, now);
+ if (aCallback)
+ aCallback();
+ });
+ },
+
+ /**
+ * Asynchronously inserts an array of add-ons into the database
+ *
+ * @param aAddons
+ * The array of add-ons to insert
+ * @param aCallback
+ * An optional callback to call once complete
+ */
+ insertAddons: Task.async(function* (aAddons, aCallback) {
+ yield this.openConnection();
+ yield this._insertAddons(aAddons, aCallback);
+ }),
+
+ _insertAddons: Task.async(function* (aAddons, aCallback) {
+ for (let addon of aAddons) {
+ this._insertAddon(addon);
+ }
+
+ yield this._saveDBToDisk();
+ aCallback && aCallback();
+ }),
+
+ /**
+ * Inserts an individual add-on into the database. If the add-on already
+ * exists in the database (by id), then the specified add-on will not be
+ * inserted.
+ *
+ * @param aAddon
+ * The add-on to insert into the database
+ * @param aCallback
+ * The callback to call once complete
+ */
+ _insertAddon: function AD__insertAddon(aAddon) {
+ let newAddon = this._parseAddon(aAddon);
+ if (!newAddon ||
+ !newAddon.id ||
+ this.DB.addons.has(newAddon.id))
+ return;
+
+ this.DB.addons.set(newAddon.id, newAddon);
+ },
+
+ /*
+ * Creates an AddonSearchResult by parsing an object structure
+ * retrieved from the DB JSON representation.
+ *
+ * @param aObj
+ * The object to parse
+ * @return Returns an AddonSearchResult object.
+ */
+ _parseAddon: function (aObj) {
+ if (aObj instanceof AddonSearchResult)
+ return aObj;
+
+ let id = aObj.id;
+ if (!aObj.id)
+ return null;
+
+ let addon = new AddonSearchResult(id);
+
+ for (let [expectedProperty,] of Iterator(AddonSearchResult.prototype)) {
+ if (!(expectedProperty in aObj) ||
+ typeof(aObj[expectedProperty]) === "function")
+ continue;
+
+ let value = aObj[expectedProperty];
+
+ try {
+ switch (expectedProperty) {
+ case "sourceURI":
+ addon.sourceURI = value ? NetUtil.newURI(value) : null;
+ break;
+
+ case "creator":
+ addon.creator = value
+ ? this._makeDeveloper(value)
+ : null;
+ break;
+
+ case "updateDate":
+ addon.updateDate = value ? new Date(value) : null;
+ break;
+
+ case "developers":
+ if (!addon.developers) addon.developers = [];
+ for (let developer of value) {
+ addon.developers.push(this._makeDeveloper(developer));
+ }
+ break;
+
+ case "screenshots":
+ if (!addon.screenshots) addon.screenshots = [];
+ for (let screenshot of value) {
+ addon.screenshots.push(this._makeScreenshot(screenshot));
+ }
+ break;
+
+ case "compatibilityOverrides":
+ if (!addon.compatibilityOverrides) addon.compatibilityOverrides = [];
+ for (let override of value) {
+ addon.compatibilityOverrides.push(
+ this._makeCompatOverride(override)
+ );
+ }
+ break;
+
+ case "icons":
+ if (!addon.icons) addon.icons = {};
+ for (let [size, url] of Iterator(aObj.icons)) {
+ addon.icons[size] = url;
+ }
+ break;
+
+ case "iconURL":
+ break;
+
+ default:
+ addon[expectedProperty] = value;
+ }
+ } catch (ex) {
+ logger.warn("Error in parsing property value for " + expectedProperty + " | " + ex);
+ }
+
+ // delete property from obj to indicate we've already
+ // handled it. The remaining public properties will
+ // be stored separately and just passed through to
+ // be written back to the DB.
+ delete aObj[expectedProperty];
+ }
+
+ // Copy remaining properties to a separate object
+ // to prevent accidental access on downgraded versions.
+ // The properties will be merged in the same object
+ // prior to being written back through toJSON.
+ for (let remainingProperty of Object.keys(aObj)) {
+ switch (typeof(aObj[remainingProperty])) {
+ case "boolean":
+ case "number":
+ case "string":
+ case "object":
+ // these types are accepted
+ break;
+ default:
+ continue;
+ }
+
+ if (!remainingProperty.startsWith("_"))
+ addon._unsupportedProperties[remainingProperty] =
+ aObj[remainingProperty];
+ }
+
+ return addon;
+ },
+
+ /**
+ * Write the in-memory DB to disk, after waiting for
+ * the DB_BATCH_TIMEOUT_MS timeout.
+ *
+ * @return Promise A promise that resolves after the
+ * write to disk has completed.
+ */
+ _saveDBToDisk: function() {
+ return this.Writer.saveChanges().then(
+ null,
+ e => logger.error("SaveDBToDisk failed", e));
+ },
+
+ /**
+ * Make a developer object from a vanilla
+ * JS object from the JSON database
+ *
+ * @param aObj
+ * The JS object to use
+ * @return The created developer
+ */
+ _makeDeveloper: function (aObj) {
+ let name = aObj.name;
+ let url = aObj.url;
+ return new AddonManagerPrivate.AddonAuthor(name, url);
+ },
+
+ /**
+ * Make a screenshot object from a vanilla
+ * JS object from the JSON database
+ *
+ * @param aObj
+ * The JS object to use
+ * @return The created screenshot
+ */
+ _makeScreenshot: function (aObj) {
+ let url = aObj.url;
+ let width = aObj.width;
+ let height = aObj.height;
+ let thumbnailURL = aObj.thumbnailURL;
+ let thumbnailWidth = aObj.thumbnailWidth;
+ let thumbnailHeight = aObj.thumbnailHeight;
+ let caption = aObj.caption;
+ return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL,
+ thumbnailWidth, thumbnailHeight, caption);
+ },
+
+ /**
+ * Make a CompatibilityOverride from a vanilla
+ * JS object from the JSON database
+ *
+ * @param aObj
+ * The JS object to use
+ * @return The created CompatibilityOverride
+ */
+ _makeCompatOverride: function (aObj) {
+ let type = aObj.type;
+ let minVersion = aObj.minVersion;
+ let maxVersion = aObj.maxVersion;
+ let appID = aObj.appID;
+ let appMinVersion = aObj.appMinVersion;
+ let appMaxVersion = aObj.appMaxVersion;
+ return new AddonManagerPrivate.AddonCompatibilityOverride(type,
+ minVersion,
+ maxVersion,
+ appID,
+ appMinVersion,
+ appMaxVersion);
+ },
+};
diff --git a/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm b/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
new file mode 100644
index 000000000..128146bbe
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
@@ -0,0 +1,518 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+const KEY_PROFILEDIR = "ProfD";
+const FILE_DATABASE = "addons.sqlite";
+const LAST_DB_SCHEMA = 4;
+
+// Add-on properties present in the columns of the database
+const PROP_SINGLE = ["id", "type", "name", "version", "creator", "description",
+ "fullDescription", "developerComments", "eula",
+ "homepageURL", "supportURL", "contributionURL",
+ "contributionAmount", "averageRating", "reviewCount",
+ "reviewURL", "totalDownloads", "weeklyDownloads",
+ "dailyUsers", "sourceURI", "repositoryStatus", "size",
+ "updateDate"];
+
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.repository.sqlmigrator";
+
+// Create a new logger for use by the Addons Repository SQL Migrator
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+this.EXPORTED_SYMBOLS = ["AddonRepository_SQLiteMigrator"];
+
+
+this.AddonRepository_SQLiteMigrator = {
+
+ /**
+ * Migrates data from a previous SQLite version of the
+ * database to the JSON version.
+ *
+ * @param structFunctions an object that contains functions
+ * to create the various objects used
+ * in the new JSON format
+ * @param aCallback A callback to be called when migration
+ * finishes, with the results in an array
+ * @returns bool True if a migration will happen (DB was
+ * found and succesfully opened)
+ */
+ migrate: function(aCallback) {
+ if (!this._openConnection()) {
+ this._closeConnection();
+ aCallback([]);
+ return false;
+ }
+
+ logger.debug("Importing addon repository from previous " + FILE_DATABASE + " storage.");
+
+ this._retrieveStoredData((results) => {
+ this._closeConnection();
+ let resultArray = [addon for ([,addon] of Iterator(results))];
+ logger.debug(resultArray.length + " addons imported.")
+ aCallback(resultArray);
+ });
+
+ return true;
+ },
+
+ /**
+ * Synchronously opens a new connection to the database file.
+ *
+ * @return bool Whether the DB was opened successfully.
+ */
+ _openConnection: function AD_openConnection() {
+ delete this.connection;
+
+ let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
+ if (!dbfile.exists())
+ return false;
+
+ try {
+ this.connection = Services.storage.openUnsharedDatabase(dbfile);
+ } catch (e) {
+ return false;
+ }
+
+ this.connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE");
+
+ // Any errors in here should rollback
+ try {
+ this.connection.beginTransaction();
+
+ switch (this.connection.schemaVersion) {
+ case 0:
+ return false;
+
+ case 1:
+ logger.debug("Upgrading database schema to version 2");
+ this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN width INTEGER");
+ this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN height INTEGER");
+ this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailWidth INTEGER");
+ this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailHeight INTEGER");
+ case 2:
+ logger.debug("Upgrading database schema to version 3");
+ this.connection.createTable("compatibility_override",
+ "addon_internal_id INTEGER, " +
+ "num INTEGER, " +
+ "type TEXT, " +
+ "minVersion TEXT, " +
+ "maxVersion TEXT, " +
+ "appID TEXT, " +
+ "appMinVersion TEXT, " +
+ "appMaxVersion TEXT, " +
+ "PRIMARY KEY (addon_internal_id, num)");
+ case 3:
+ logger.debug("Upgrading database schema to version 4");
+ this.connection.createTable("icon",
+ "addon_internal_id INTEGER, " +
+ "size INTEGER, " +
+ "url TEXT, " +
+ "PRIMARY KEY (addon_internal_id, size)");
+ this._createIndices();
+ this._createTriggers();
+ this.connection.schemaVersion = LAST_DB_SCHEMA;
+ case LAST_DB_SCHEMA:
+ break;
+ default:
+ return false;
+ }
+ this.connection.commitTransaction();
+ } catch (e) {
+ logger.error("Failed to open " + FILE_DATABASE + ". Data import will not happen.", e);
+ this.logSQLError(this.connection.lastError, this.connection.lastErrorString);
+ this.connection.rollbackTransaction();
+ return false;
+ }
+
+ return true;
+ },
+
+ _closeConnection: function() {
+ for each (let stmt in this.asyncStatementsCache)
+ stmt.finalize();
+ this.asyncStatementsCache = {};
+
+ if (this.connection)
+ this.connection.asyncClose();
+
+ delete this.connection;
+ },
+
+ /**
+ * Asynchronously retrieve all add-ons from the database, and pass it
+ * to the specified callback
+ *
+ * @param aCallback
+ * The callback to pass the add-ons back to
+ */
+ _retrieveStoredData: function AD_retrieveStoredData(aCallback) {
+ let self = this;
+ let addons = {};
+
+ // Retrieve all data from the addon table
+ function getAllAddons() {
+ self.getAsyncStatement("getAllAddons").executeAsync({
+ handleResult: function getAllAddons_handleResult(aResults) {
+ let row = null;
+ while ((row = aResults.getNextRow())) {
+ let internal_id = row.getResultByName("internal_id");
+ addons[internal_id] = self._makeAddonFromAsyncRow(row);
+ }
+ },
+
+ handleError: self.asyncErrorLogger,
+
+ handleCompletion: function getAllAddons_handleCompletion(aReason) {
+ if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+ logger.error("Error retrieving add-ons from database. Returning empty results");
+ aCallback({});
+ return;
+ }
+
+ getAllDevelopers();
+ }
+ });
+ }
+
+ // Retrieve all data from the developer table
+ function getAllDevelopers() {
+ self.getAsyncStatement("getAllDevelopers").executeAsync({
+ handleResult: function getAllDevelopers_handleResult(aResults) {
+ let row = null;
+ while ((row = aResults.getNextRow())) {
+ let addon_internal_id = row.getResultByName("addon_internal_id");
+ if (!(addon_internal_id in addons)) {
+ logger.warn("Found a developer not linked to an add-on in database");
+ continue;
+ }
+
+ let addon = addons[addon_internal_id];
+ if (!addon.developers)
+ addon.developers = [];
+
+ addon.developers.push(self._makeDeveloperFromAsyncRow(row));
+ }
+ },
+
+ handleError: self.asyncErrorLogger,
+
+ handleCompletion: function getAllDevelopers_handleCompletion(aReason) {
+ if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+ logger.error("Error retrieving developers from database. Returning empty results");
+ aCallback({});
+ return;
+ }
+
+ getAllScreenshots();
+ }
+ });
+ }
+
+ // Retrieve all data from the screenshot table
+ function getAllScreenshots() {
+ self.getAsyncStatement("getAllScreenshots").executeAsync({
+ handleResult: function getAllScreenshots_handleResult(aResults) {
+ let row = null;
+ while ((row = aResults.getNextRow())) {
+ let addon_internal_id = row.getResultByName("addon_internal_id");
+ if (!(addon_internal_id in addons)) {
+ logger.warn("Found a screenshot not linked to an add-on in database");
+ continue;
+ }
+
+ let addon = addons[addon_internal_id];
+ if (!addon.screenshots)
+ addon.screenshots = [];
+ addon.screenshots.push(self._makeScreenshotFromAsyncRow(row));
+ }
+ },
+
+ handleError: self.asyncErrorLogger,
+
+ handleCompletion: function getAllScreenshots_handleCompletion(aReason) {
+ if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+ logger.error("Error retrieving screenshots from database. Returning empty results");
+ aCallback({});
+ return;
+ }
+
+ getAllCompatOverrides();
+ }
+ });
+ }
+
+ function getAllCompatOverrides() {
+ self.getAsyncStatement("getAllCompatOverrides").executeAsync({
+ handleResult: function getAllCompatOverrides_handleResult(aResults) {
+ let row = null;
+ while ((row = aResults.getNextRow())) {
+ let addon_internal_id = row.getResultByName("addon_internal_id");
+ if (!(addon_internal_id in addons)) {
+ logger.warn("Found a compatibility override not linked to an add-on in database");
+ continue;
+ }
+
+ let addon = addons[addon_internal_id];
+ if (!addon.compatibilityOverrides)
+ addon.compatibilityOverrides = [];
+ addon.compatibilityOverrides.push(self._makeCompatOverrideFromAsyncRow(row));
+ }
+ },
+
+ handleError: self.asyncErrorLogger,
+
+ handleCompletion: function getAllCompatOverrides_handleCompletion(aReason) {
+ if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+ logger.error("Error retrieving compatibility overrides from database. Returning empty results");
+ aCallback({});
+ return;
+ }
+
+ getAllIcons();
+ }
+ });
+ }
+
+ function getAllIcons() {
+ self.getAsyncStatement("getAllIcons").executeAsync({
+ handleResult: function getAllIcons_handleResult(aResults) {
+ let row = null;
+ while ((row = aResults.getNextRow())) {
+ let addon_internal_id = row.getResultByName("addon_internal_id");
+ if (!(addon_internal_id in addons)) {
+ logger.warn("Found an icon not linked to an add-on in database");
+ continue;
+ }
+
+ let addon = addons[addon_internal_id];
+ let { size, url } = self._makeIconFromAsyncRow(row);
+ addon.icons[size] = url;
+ if (size == 32)
+ addon.iconURL = url;
+ }
+ },
+
+ handleError: self.asyncErrorLogger,
+
+ handleCompletion: function getAllIcons_handleCompletion(aReason) {
+ if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+ logger.error("Error retrieving icons from database. Returning empty results");
+ aCallback({});
+ return;
+ }
+
+ let returnedAddons = {};
+ for each (let addon in addons)
+ returnedAddons[addon.id] = addon;
+ aCallback(returnedAddons);
+ }
+ });
+ }
+
+ // Begin asynchronous process
+ getAllAddons();
+ },
+
+ // A cache of statements that are used and need to be finalized on shutdown
+ asyncStatementsCache: {},
+
+ /**
+ * Gets a cached async statement or creates a new statement if it doesn't
+ * already exist.
+ *
+ * @param aKey
+ * A unique key to reference the statement
+ * @return a mozIStorageAsyncStatement for the SQL corresponding to the
+ * unique key
+ */
+ getAsyncStatement: function AD_getAsyncStatement(aKey) {
+ if (aKey in this.asyncStatementsCache)
+ return this.asyncStatementsCache[aKey];
+
+ let sql = this.queries[aKey];
+ try {
+ return this.asyncStatementsCache[aKey] = this.connection.createAsyncStatement(sql);
+ } catch (e) {
+ logger.error("Error creating statement " + aKey + " (" + sql + ")");
+ throw Components.Exception("Error creating statement " + aKey + " (" + sql + "): " + e,
+ e.result);
+ }
+ },
+
+ // The queries used by the database
+ queries: {
+ getAllAddons: "SELECT internal_id, id, type, name, version, " +
+ "creator, creatorURL, description, fullDescription, " +
+ "developerComments, eula, homepageURL, supportURL, " +
+ "contributionURL, contributionAmount, averageRating, " +
+ "reviewCount, reviewURL, totalDownloads, weeklyDownloads, " +
+ "dailyUsers, sourceURI, repositoryStatus, size, updateDate " +
+ "FROM addon",
+
+ getAllDevelopers: "SELECT addon_internal_id, name, url FROM developer " +
+ "ORDER BY addon_internal_id, num",
+
+ getAllScreenshots: "SELECT addon_internal_id, url, width, height, " +
+ "thumbnailURL, thumbnailWidth, thumbnailHeight, caption " +
+ "FROM screenshot ORDER BY addon_internal_id, num",
+
+ getAllCompatOverrides: "SELECT addon_internal_id, type, minVersion, " +
+ "maxVersion, appID, appMinVersion, appMaxVersion " +
+ "FROM compatibility_override " +
+ "ORDER BY addon_internal_id, num",
+
+ getAllIcons: "SELECT addon_internal_id, size, url FROM icon " +
+ "ORDER BY addon_internal_id, size",
+ },
+
+ /**
+ * Make add-on structure from an asynchronous row.
+ *
+ * @param aRow
+ * The asynchronous row to use
+ * @return The created add-on
+ */
+ _makeAddonFromAsyncRow: function AD__makeAddonFromAsyncRow(aRow) {
+ // This is intentionally not an AddonSearchResult object in order
+ // to allow AddonDatabase._parseAddon to parse it, same as if it
+ // was read from the JSON database.
+
+ let addon = { icons: {} };
+
+ for (let prop of PROP_SINGLE) {
+ addon[prop] = aRow.getResultByName(prop)
+ };
+
+ return addon;
+ },
+
+ /**
+ * Make a developer from an asynchronous row
+ *
+ * @param aRow
+ * The asynchronous row to use
+ * @return The created developer
+ */
+ _makeDeveloperFromAsyncRow: function AD__makeDeveloperFromAsyncRow(aRow) {
+ let name = aRow.getResultByName("name");
+ let url = aRow.getResultByName("url")
+ return new AddonManagerPrivate.AddonAuthor(name, url);
+ },
+
+ /**
+ * Make a screenshot from an asynchronous row
+ *
+ * @param aRow
+ * The asynchronous row to use
+ * @return The created screenshot
+ */
+ _makeScreenshotFromAsyncRow: function AD__makeScreenshotFromAsyncRow(aRow) {
+ let url = aRow.getResultByName("url");
+ let width = aRow.getResultByName("width");
+ let height = aRow.getResultByName("height");
+ let thumbnailURL = aRow.getResultByName("thumbnailURL");
+ let thumbnailWidth = aRow.getResultByName("thumbnailWidth");
+ let thumbnailHeight = aRow.getResultByName("thumbnailHeight");
+ let caption = aRow.getResultByName("caption");
+ return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL,
+ thumbnailWidth, thumbnailHeight, caption);
+ },
+
+ /**
+ * Make a CompatibilityOverride from an asynchronous row
+ *
+ * @param aRow
+ * The asynchronous row to use
+ * @return The created CompatibilityOverride
+ */
+ _makeCompatOverrideFromAsyncRow: function AD_makeCompatOverrideFromAsyncRow(aRow) {
+ let type = aRow.getResultByName("type");
+ let minVersion = aRow.getResultByName("minVersion");
+ let maxVersion = aRow.getResultByName("maxVersion");
+ let appID = aRow.getResultByName("appID");
+ let appMinVersion = aRow.getResultByName("appMinVersion");
+ let appMaxVersion = aRow.getResultByName("appMaxVersion");
+ return new AddonManagerPrivate.AddonCompatibilityOverride(type,
+ minVersion,
+ maxVersion,
+ appID,
+ appMinVersion,
+ appMaxVersion);
+ },
+
+ /**
+ * Make an icon from an asynchronous row
+ *
+ * @param aRow
+ * The asynchronous row to use
+ * @return An object containing the size and URL of the icon
+ */
+ _makeIconFromAsyncRow: function AD_makeIconFromAsyncRow(aRow) {
+ let size = aRow.getResultByName("size");
+ let url = aRow.getResultByName("url");
+ return { size: size, url: url };
+ },
+
+ /**
+ * A helper function to log an SQL error.
+ *
+ * @param aError
+ * The storage error code associated with the error
+ * @param aErrorString
+ * An error message
+ */
+ logSQLError: function AD_logSQLError(aError, aErrorString) {
+ logger.error("SQL error " + aError + ": " + aErrorString);
+ },
+
+ /**
+ * A helper function to log any errors that occur during async statements.
+ *
+ * @param aError
+ * A mozIStorageError to log
+ */
+ asyncErrorLogger: function AD_asyncErrorLogger(aError) {
+ logger.error("Async SQL error " + aError.result + ": " + aError.message);
+ },
+
+ /**
+ * Synchronously creates the triggers in the database.
+ */
+ _createTriggers: function AD__createTriggers() {
+ this.connection.executeSimpleSQL("DROP TRIGGER IF EXISTS delete_addon");
+ this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " +
+ "ON addon BEGIN " +
+ "DELETE FROM developer WHERE addon_internal_id=old.internal_id; " +
+ "DELETE FROM screenshot WHERE addon_internal_id=old.internal_id; " +
+ "DELETE FROM compatibility_override WHERE addon_internal_id=old.internal_id; " +
+ "DELETE FROM icon WHERE addon_internal_id=old.internal_id; " +
+ "END");
+ },
+
+ /**
+ * Synchronously creates the indices in the database.
+ */
+ _createIndices: function AD__createIndices() {
+ this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS developer_idx " +
+ "ON developer (addon_internal_id)");
+ this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS screenshot_idx " +
+ "ON screenshot (addon_internal_id)");
+ this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS compatibility_override_idx " +
+ "ON compatibility_override (addon_internal_id)");
+ this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS icon_idx " +
+ "ON icon (addon_internal_id)");
+ }
+}
diff --git a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
new file mode 100644
index 000000000..7e86fceab
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
@@ -0,0 +1,772 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 AddonUpdateChecker is responsible for retrieving the update information
+ * from an add-on's remote update manifest.
+ */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [ "AddonUpdateChecker" ];
+
+const TIMEOUT = 60 * 1000;
+const PREFIX_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
+const PREFIX_ITEM = "urn:mozilla:item:";
+const PREFIX_EXTENSION = "urn:mozilla:extension:";
+const PREFIX_THEME = "urn:mozilla:theme:";
+const TOOLKIT_ID = "toolkit@mozilla.org"
+#ifdef MOZ_PHOENIX_EXTENSIONS
+const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
+#endif
+const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
+
+const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
+ "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
+ "resource://gre/modules/addons/AddonRepository.jsm");
+
+// Shared code for suppressing bad cert dialogs.
+XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
+ let certUtils = {};
+ Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
+ return certUtils;
+});
+
+var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
+ getService(Ci.nsIRDFService);
+
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.update-checker";
+
+// Create a new logger for use by the Addons Update Checker
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+/**
+ * A serialisation method for RDF data that produces an identical string
+ * for matching RDF graphs.
+ * The serialisation is not complete, only assertions stemming from a given
+ * resource are included, multiple references to the same resource are not
+ * permitted, and the RDF prolog and epilog are not included.
+ * RDF Blob and Date literals are not supported.
+ */
+function RDFSerializer() {
+ this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"].
+ getService(Ci.nsIRDFContainerUtils);
+ this.resources = [];
+}
+
+RDFSerializer.prototype = {
+ INDENT: " ", // The indent used for pretty-printing
+ resources: null, // Array of the resources that have been found
+
+ /**
+ * Escapes characters from a string that should not appear in XML.
+ *
+ * @param aString
+ * The string to be escaped
+ * @return a string with all characters invalid in XML character data
+ * converted to entity references.
+ */
+ escapeEntities: function RDFS_escapeEntities(aString) {
+ aString = aString.replace(/&/g, "&amp;");
+ aString = aString.replace(/</g, "&lt;");
+ aString = aString.replace(/>/g, "&gt;");
+ return aString.replace(/"/g, "&quot;");
+ },
+
+ /**
+ * Serializes all the elements of an RDF container.
+ *
+ * @param aDs
+ * The RDF datasource
+ * @param aContainer
+ * The RDF container to output the child elements of
+ * @param aIndent
+ * The current level of indent for pretty-printing
+ * @return a string containing the serialized elements.
+ */
+ serializeContainerItems: function RDFS_serializeContainerItems(aDs, aContainer,
+ aIndent) {
+ var result = "";
+ var items = aContainer.GetElements();
+ while (items.hasMoreElements()) {
+ var item = items.getNext().QueryInterface(Ci.nsIRDFResource);
+ result += aIndent + "<RDF:li>\n"
+ result += this.serializeResource(aDs, item, aIndent + this.INDENT);
+ result += aIndent + "</RDF:li>\n"
+ }
+ return result;
+ },
+
+ /**
+ * Serializes all em:* (see EM_NS) properties of an RDF resource except for
+ * the em:signature property. As this serialization is to be compared against
+ * the manifest signature it cannot contain the em:signature property itself.
+ *
+ * @param aDs
+ * The RDF datasource
+ * @param aResource
+ * The RDF resource that contains the properties to serialize
+ * @param aIndent
+ * The current level of indent for pretty-printing
+ * @return a string containing the serialized properties.
+ * @throws if the resource contains a property that cannot be serialized
+ */
+ serializeResourceProperties: function RDFS_serializeResourceProperties(aDs,
+ aResource,
+ aIndent) {
+ var result = "";
+ var items = [];
+ var arcs = aDs.ArcLabelsOut(aResource);
+ while (arcs.hasMoreElements()) {
+ var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource);
+ if (arc.ValueUTF8.substring(0, PREFIX_NS_EM.length) != PREFIX_NS_EM)
+ continue;
+ var prop = arc.ValueUTF8.substring(PREFIX_NS_EM.length);
+ if (prop == "signature")
+ continue;
+
+ var targets = aDs.GetTargets(aResource, arc, true);
+ while (targets.hasMoreElements()) {
+ var target = targets.getNext();
+ if (target instanceof Ci.nsIRDFResource) {
+ var item = aIndent + "<em:" + prop + ">\n";
+ item += this.serializeResource(aDs, target, aIndent + this.INDENT);
+ item += aIndent + "</em:" + prop + ">\n";
+ items.push(item);
+ }
+ else if (target instanceof Ci.nsIRDFLiteral) {
+ items.push(aIndent + "<em:" + prop + ">" +
+ this.escapeEntities(target.Value) + "</em:" + prop + ">\n");
+ }
+ else if (target instanceof Ci.nsIRDFInt) {
+ items.push(aIndent + "<em:" + prop + " NC:parseType=\"Integer\">" +
+ target.Value + "</em:" + prop + ">\n");
+ }
+ else {
+ throw Components.Exception("Cannot serialize unknown literal type");
+ }
+ }
+ }
+ items.sort();
+ result += items.join("");
+ return result;
+ },
+
+ /**
+ * Recursively serializes an RDF resource and all resources it links to.
+ * This will only output EM_NS properties and will ignore any em:signature
+ * property.
+ *
+ * @param aDs
+ * The RDF datasource
+ * @param aResource
+ * The RDF resource to serialize
+ * @param aIndent
+ * The current level of indent for pretty-printing. If undefined no
+ * indent will be added
+ * @return a string containing the serialized resource.
+ * @throws if the RDF data contains multiple references to the same resource.
+ */
+ serializeResource: function RDFS_serializeResource(aDs, aResource, aIndent) {
+ if (this.resources.indexOf(aResource) != -1 ) {
+ // We cannot output multiple references to the same resource.
+ throw Components.Exception("Cannot serialize multiple references to " + aResource.Value);
+ }
+ if (aIndent === undefined)
+ aIndent = "";
+
+ this.resources.push(aResource);
+ var container = null;
+ var type = "Description";
+ if (this.cUtils.IsSeq(aDs, aResource)) {
+ type = "Seq";
+ container = this.cUtils.MakeSeq(aDs, aResource);
+ }
+ else if (this.cUtils.IsAlt(aDs, aResource)) {
+ type = "Alt";
+ container = this.cUtils.MakeAlt(aDs, aResource);
+ }
+ else if (this.cUtils.IsBag(aDs, aResource)) {
+ type = "Bag";
+ container = this.cUtils.MakeBag(aDs, aResource);
+ }
+
+ var result = aIndent + "<RDF:" + type;
+ if (!gRDF.IsAnonymousResource(aResource))
+ result += " about=\"" + this.escapeEntities(aResource.ValueUTF8) + "\"";
+ result += ">\n";
+
+ if (container)
+ result += this.serializeContainerItems(aDs, container, aIndent + this.INDENT);
+
+ result += this.serializeResourceProperties(aDs, aResource, aIndent + this.INDENT);
+
+ result += aIndent + "</RDF:" + type + ">\n";
+ return result;
+ }
+}
+
+/**
+ * Parses an RDF style update manifest into an array of update objects.
+ *
+ * @param aId
+ * The ID of the add-on being checked for updates
+ * @param aUpdateKey
+ * An optional update key for the add-on
+ * @param aRequest
+ * The XMLHttpRequest that has retrieved the update manifest
+ * @return an array of update objects
+ * @throws if the update manifest is invalid in any way
+ */
+function parseRDFManifest(aId, aUpdateKey, aRequest) {
+ function EM_R(aProp) {
+ return gRDF.GetResource(PREFIX_NS_EM + aProp);
+ }
+
+ function getValue(aLiteral) {
+ if (aLiteral instanceof Ci.nsIRDFLiteral)
+ return aLiteral.Value;
+ if (aLiteral instanceof Ci.nsIRDFResource)
+ return aLiteral.Value;
+ if (aLiteral instanceof Ci.nsIRDFInt)
+ return aLiteral.Value;
+ return null;
+ }
+
+ function getProperty(aDs, aSource, aProperty) {
+ return getValue(aDs.GetTarget(aSource, EM_R(aProperty), true));
+ }
+
+ function getBooleanProperty(aDs, aSource, aProperty) {
+ let propValue = aDs.GetTarget(aSource, EM_R(aProperty), true);
+ if (!propValue)
+ return undefined;
+ return getValue(propValue) == "true";
+ }
+
+ function getRequiredProperty(aDs, aSource, aProperty) {
+ let value = getProperty(aDs, aSource, aProperty);
+ if (!value)
+ throw Components.Exception("Update manifest is missing a required " + aProperty + " property.");
+ return value;
+ }
+
+ let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
+ createInstance(Ci.nsIRDFXMLParser);
+ let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
+ createInstance(Ci.nsIRDFDataSource);
+ rdfParser.parseString(ds, aRequest.channel.URI, aRequest.responseText);
+
+ // Differentiating between add-on types is deprecated
+ let extensionRes = gRDF.GetResource(PREFIX_EXTENSION + aId);
+ let themeRes = gRDF.GetResource(PREFIX_THEME + aId);
+ let itemRes = gRDF.GetResource(PREFIX_ITEM + aId);
+ let addonRes = ds.ArcLabelsOut(extensionRes).hasMoreElements() ? extensionRes
+ : ds.ArcLabelsOut(themeRes).hasMoreElements() ? themeRes
+ : itemRes;
+
+ // If we have an update key then the update manifest must be signed
+ if (aUpdateKey) {
+ let signature = getProperty(ds, addonRes, "signature");
+ if (!signature)
+ throw Components.Exception("Update manifest for " + aId + " does not contain a required signature");
+ let serializer = new RDFSerializer();
+ let updateString = null;
+
+ try {
+ updateString = serializer.serializeResource(ds, addonRes);
+ }
+ catch (e) {
+ throw Components.Exception("Failed to generate signed string for " + aId + ". Serializer threw " + e,
+ e.result);
+ }
+
+ let result = false;
+
+ try {
+ let verifier = Cc["@mozilla.org/security/datasignatureverifier;1"].
+ getService(Ci.nsIDataSignatureVerifier);
+ result = verifier.verifyData(updateString, signature, aUpdateKey);
+ }
+ catch (e) {
+ throw Components.Exception("The signature or updateKey for " + aId + " is malformed." +
+ "Verifier threw " + e, e.result);
+ }
+
+ if (!result)
+ throw Components.Exception("The signature for " + aId + " was not created by the add-on's updateKey");
+ }
+
+ let updates = ds.GetTarget(addonRes, EM_R("updates"), true);
+
+ // A missing updates property doesn't count as a failure, just as no avialable
+ // update information
+ if (!updates) {
+ logger.warn("Update manifest for " + aId + " did not contain an updates property");
+ return [];
+ }
+
+ if (!(updates instanceof Ci.nsIRDFResource))
+ throw Components.Exception("Missing updates property for " + addonRes.Value);
+
+ let cu = Cc["@mozilla.org/rdf/container-utils;1"].
+ getService(Ci.nsIRDFContainerUtils);
+ if (!cu.IsContainer(ds, updates))
+ throw Components.Exception("Updates property was not an RDF container");
+
+ let results = [];
+ let ctr = Cc["@mozilla.org/rdf/container;1"].
+ createInstance(Ci.nsIRDFContainer);
+ ctr.Init(ds, updates);
+ let items = ctr.GetElements();
+ while (items.hasMoreElements()) {
+ let item = items.getNext().QueryInterface(Ci.nsIRDFResource);
+ let version = getProperty(ds, item, "version");
+ if (!version) {
+ logger.warn("Update manifest is missing a required version property.");
+ continue;
+ }
+
+ logger.debug("Found an update entry for " + aId + " version " + version);
+
+ let targetApps = ds.GetTargets(item, EM_R("targetApplication"), true);
+ while (targetApps.hasMoreElements()) {
+ let targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
+
+ let appEntry = {};
+ try {
+ appEntry.id = getRequiredProperty(ds, targetApp, "id");
+ appEntry.minVersion = getRequiredProperty(ds, targetApp, "minVersion");
+ appEntry.maxVersion = getRequiredProperty(ds, targetApp, "maxVersion");
+ }
+ catch (e) {
+ logger.warn(e);
+ continue;
+ }
+
+ let result = {
+ id: aId,
+ version: version,
+ multiprocessCompatible: getBooleanProperty(ds, item, "multiprocessCompatible"),
+ updateURL: getProperty(ds, targetApp, "updateLink"),
+ updateHash: getProperty(ds, targetApp, "updateHash"),
+ updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
+ strictCompatibility: !!getBooleanProperty(ds, targetApp, "strictCompatibility"),
+ targetApplications: [appEntry]
+ };
+
+ if (result.updateURL && AddonManager.checkUpdateSecurity &&
+ result.updateURL.substring(0, 6) != "https:" &&
+ (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) {
+ logger.warn("updateLink " + result.updateURL + " is not secure and is not verified" +
+ " by a strong enough hash (needs to be sha1 or stronger).");
+ delete result.updateURL;
+ delete result.updateHash;
+ }
+ results.push(result);
+ }
+ }
+ return results;
+}
+
+/**
+ * Starts downloading an update manifest and then passes it to an appropriate
+ * parser to convert to an array of update objects
+ *
+ * @param aId
+ * The ID of the add-on being checked for updates
+ * @param aUpdateKey
+ * An optional update key for the add-on
+ * @param aUrl
+ * The URL of the update manifest
+ * @param aObserver
+ * An observer to pass results to
+ */
+function UpdateParser(aId, aUpdateKey, aUrl, aObserver) {
+ this.id = aId;
+ this.updateKey = aUpdateKey;
+ this.observer = aObserver;
+ this.url = aUrl;
+
+ let requireBuiltIn = true;
+ try {
+ requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
+ }
+ catch (e) {
+ }
+
+ logger.debug("Requesting " + aUrl);
+ try {
+ this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+ createInstance(Ci.nsIXMLHttpRequest);
+ this.request.open("GET", this.url, true);
+ this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
+ this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ // Prevent the request from writing to cache.
+ this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+ this.request.overrideMimeType("text/xml");
+ this.request.setRequestHeader("Moz-XPI-Update", "1", true);
+ this.request.timeout = TIMEOUT;
+ var self = this;
+ this.request.addEventListener("load", function loadEventListener(event) { self.onLoad() }, false);
+ this.request.addEventListener("error", function errorEventListener(event) { self.onError() }, false);
+ this.request.addEventListener("timeout", function timeoutEventListener(event) { self.onTimeout() }, false);
+ this.request.send(null);
+ }
+ catch (e) {
+ logger.error("Failed to request update manifest", e);
+ }
+}
+
+UpdateParser.prototype = {
+ id: null,
+ updateKey: null,
+ observer: null,
+ request: null,
+ url: null,
+
+ /**
+ * Called when the manifest has been successfully loaded.
+ */
+ onLoad: function UP_onLoad() {
+ let request = this.request;
+ this.request = null;
+ this._doneAt = new Error("place holder");
+
+ let requireBuiltIn = true;
+ try {
+ requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
+ }
+ catch (e) {
+ }
+
+ try {
+ CertUtils.checkCert(request.channel, !requireBuiltIn);
+ }
+ catch (e) {
+ this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
+ return;
+ }
+
+ if (!Components.isSuccessCode(request.status)) {
+ logger.warn("Request failed: " + this.url + " - " + request.status);
+ this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
+ return;
+ }
+
+ let channel = request.channel;
+ if (channel instanceof Ci.nsIHttpChannel && !channel.requestSucceeded) {
+ logger.warn("Request failed: " + this.url + " - " + channel.responseStatus +
+ ": " + channel.responseStatusText);
+ this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
+ return;
+ }
+
+ let xml = request.responseXML;
+ if (!xml || xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR) {
+ logger.warn("Update manifest was not valid XML");
+ this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
+ return;
+ }
+
+ // We currently only know about RDF update manifests
+ if (xml.documentElement.namespaceURI == PREFIX_NS_RDF) {
+ let results = null;
+
+ try {
+ results = parseRDFManifest(this.id, this.updateKey, request);
+ }
+ catch (e) {
+ logger.warn("onUpdateCheckComplete failed to parse RDF manifest", e);
+ this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
+ return;
+ }
+ if ("onUpdateCheckComplete" in this.observer) {
+ try {
+ this.observer.onUpdateCheckComplete(results);
+ }
+ catch (e) {
+ logger.warn("onUpdateCheckComplete notification failed", e);
+ }
+ }
+ else {
+ logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
+ }
+ return;
+ }
+
+ logger.warn("Update manifest had an unrecognised namespace: " + xml.documentElement.namespaceURI);
+ this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT);
+ },
+
+ /**
+ * Called when the request times out
+ */
+ onTimeout: function() {
+ this.request = null;
+ this._doneAt = new Error("Timed out");
+ logger.warn("Request for " + this.url + " timed out");
+ this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT);
+ },
+
+ /**
+ * Called when the manifest failed to load.
+ */
+ onError: function UP_onError() {
+ if (!Components.isSuccessCode(this.request.status)) {
+ logger.warn("Request failed: " + this.url + " - " + this.request.status);
+ }
+ else if (this.request.channel instanceof Ci.nsIHttpChannel) {
+ try {
+ if (this.request.channel.requestSucceeded) {
+ logger.warn("Request failed: " + this.url + " - " +
+ this.request.channel.responseStatus + ": " +
+ this.request.channel.responseStatusText);
+ }
+ }
+ catch (e) {
+ logger.warn("HTTP Request failed for an unknown reason");
+ }
+ }
+ else {
+ logger.warn("Request failed for an unknown reason");
+ }
+
+ this.request = null;
+ this._doneAt = new Error("UP_onError");
+
+ this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
+ },
+
+ /**
+ * Helper method to notify the observer that an error occured.
+ */
+ notifyError: function UP_notifyError(aStatus) {
+ if ("onUpdateCheckError" in this.observer) {
+ try {
+ this.observer.onUpdateCheckError(aStatus);
+ }
+ catch (e) {
+ logger.warn("onUpdateCheckError notification failed", e);
+ }
+ }
+ },
+
+ /**
+ * Called to cancel an in-progress update check.
+ */
+ cancel: function UP_cancel() {
+ if (!this.request) {
+ logger.error("Trying to cancel already-complete request", this._doneAt);
+ return;
+ }
+ this.request.abort();
+ this.request = null;
+ this._doneAt = new Error("UP_cancel");
+ this.notifyError(AddonUpdateChecker.ERROR_CANCELLED);
+ }
+};
+
+/**
+ * Tests if an update matches a version of the application or platform
+ *
+ * @param aUpdate
+ * The available update
+ * @param aAppVersion
+ * The application version to use
+ * @param aPlatformVersion
+ * The platform version to use
+ * @param aIgnoreMaxVersion
+ * Ignore maxVersion when testing if an update matches. Optional.
+ * @param aIgnoreStrictCompat
+ * Ignore strictCompatibility when testing if an update matches. Optional.
+ * @param aCompatOverrides
+ * AddonCompatibilityOverride objects to match against. Optional.
+ * @return true if the update is compatible with the application/platform
+ */
+function matchesVersions(aUpdate, aAppVersion, aPlatformVersion,
+ aIgnoreMaxVersion, aIgnoreStrictCompat,
+ aCompatOverrides) {
+ if (aCompatOverrides) {
+ let override = AddonRepository.findMatchingCompatOverride(aUpdate.version,
+ aCompatOverrides,
+ aAppVersion,
+ aPlatformVersion);
+ if (override && override.type == "incompatible")
+ return false;
+ }
+
+ if (aUpdate.strictCompatibility && !aIgnoreStrictCompat)
+ aIgnoreMaxVersion = false;
+
+ let result = false;
+ for (let app of aUpdate.targetApplications) {
+ if (app.id == Services.appinfo.ID) {
+ return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) &&
+ (aIgnoreMaxVersion || (Services.vc.compare(aAppVersion, app.maxVersion) <= 0));
+ }
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ if (app.id == FIREFOX_ID) {
+ return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) &&
+ (aIgnoreMaxVersion || (Services.vc.compare(aAppVersion, app.maxVersion) <= 0));
+ }
+#endif
+ if (app.id == TOOLKIT_ID) {
+ result = (Services.vc.compare(aPlatformVersion, app.minVersion) >= 0) &&
+ (aIgnoreMaxVersion || (Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0));
+ }
+ }
+ return result;
+}
+
+this.AddonUpdateChecker = {
+ // These must be kept in sync with AddonManager
+ // The update check timed out
+ ERROR_TIMEOUT: -1,
+ // There was an error while downloading the update information.
+ ERROR_DOWNLOAD_ERROR: -2,
+ // The update information was malformed in some way.
+ ERROR_PARSE_ERROR: -3,
+ // The update information was not in any known format.
+ ERROR_UNKNOWN_FORMAT: -4,
+ // The update information was not correctly signed or there was an SSL error.
+ ERROR_SECURITY_ERROR: -5,
+ // The update was cancelled
+ ERROR_CANCELLED: -6,
+
+ /**
+ * Retrieves the best matching compatibility update for the application from
+ * a list of available update objects.
+ *
+ * @param aUpdates
+ * An array of update objects
+ * @param aVersion
+ * The version of the add-on to get new compatibility information for
+ * @param aIgnoreCompatibility
+ * An optional parameter to get the first compatibility update that
+ * is compatible with any version of the application or toolkit
+ * @param aAppVersion
+ * The version of the application or null to use the current version
+ * @param aPlatformVersion
+ * The version of the platform or null to use the current version
+ * @param aIgnoreMaxVersion
+ * Ignore maxVersion when testing if an update matches. Optional.
+ * @param aIgnoreStrictCompat
+ * Ignore strictCompatibility when testing if an update matches. Optional.
+ * @return an update object if one matches or null if not
+ */
+ getCompatibilityUpdate: function AUC_getCompatibilityUpdate(aUpdates, aVersion,
+ aIgnoreCompatibility,
+ aAppVersion,
+ aPlatformVersion,
+ aIgnoreMaxVersion,
+ aIgnoreStrictCompat) {
+ if (!aAppVersion)
+ aAppVersion = Services.appinfo.version;
+ if (!aPlatformVersion)
+ aPlatformVersion = Services.appinfo.platformVersion;
+
+ for (let update of aUpdates) {
+ if (Services.vc.compare(update.version, aVersion) == 0) {
+ if (aIgnoreCompatibility) {
+ for (let targetApp of update.targetApplications) {
+ let id = targetApp.id;
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ if (id == Services.appinfo.ID || id == FIREFOX_ID ||
+ id == TOOLKIT_ID)
+#else
+ if (id == Services.appinfo.ID || id == TOOLKIT_ID)
+#endif
+ return update;
+ }
+ }
+ else if (matchesVersions(update, aAppVersion, aPlatformVersion,
+ aIgnoreMaxVersion, aIgnoreStrictCompat)) {
+ return update;
+ }
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Returns the newest available update from a list of update objects.
+ *
+ * @param aUpdates
+ * An array of update objects
+ * @param aAppVersion
+ * The version of the application or null to use the current version
+ * @param aPlatformVersion
+ * The version of the platform or null to use the current version
+ * @param aIgnoreMaxVersion
+ * When determining compatible updates, ignore maxVersion. Optional.
+ * @param aIgnoreStrictCompat
+ * When determining compatible updates, ignore strictCompatibility. Optional.
+ * @param aCompatOverrides
+ * Array of AddonCompatibilityOverride to take into account. Optional.
+ * @return an update object if one matches or null if not
+ */
+ getNewestCompatibleUpdate: function AUC_getNewestCompatibleUpdate(aUpdates,
+ aAppVersion,
+ aPlatformVersion,
+ aIgnoreMaxVersion,
+ aIgnoreStrictCompat,
+ aCompatOverrides) {
+ if (!aAppVersion)
+ aAppVersion = Services.appinfo.version;
+ if (!aPlatformVersion)
+ aPlatformVersion = Services.appinfo.platformVersion;
+
+ let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsIBlocklistService);
+
+ let newest = null;
+ for (let update of aUpdates) {
+ if (!update.updateURL)
+ continue;
+ let state = blocklist.getAddonBlocklistState(update, aAppVersion, aPlatformVersion);
+ if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
+ continue;
+ if ((newest == null || (Services.vc.compare(newest.version, update.version) < 0)) &&
+ matchesVersions(update, aAppVersion, aPlatformVersion,
+ aIgnoreMaxVersion, aIgnoreStrictCompat,
+ aCompatOverrides)) {
+ newest = update;
+ }
+ }
+ return newest;
+ },
+
+ /**
+ * Starts an update check.
+ *
+ * @param aId
+ * The ID of the add-on being checked for updates
+ * @param aUpdateKey
+ * An optional update key for the add-on
+ * @param aUrl
+ * The URL of the add-on's update manifest
+ * @param aObserver
+ * An observer to notify of results
+ * @return UpdateParser so that the caller can use UpdateParser.cancel() to shut
+ * down in-progress update requests
+ */
+ checkForUpdates: function AUC_checkForUpdates(aId, aUpdateKey, aUrl,
+ aObserver) {
+ return new UpdateParser(aId, aUpdateKey, aUrl, aObserver);
+ }
+};
diff --git a/toolkit/mozapps/extensions/internal/Content.js b/toolkit/mozapps/extensions/internal/Content.js
new file mode 100644
index 000000000..29c0ed8ce
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/Content.js
@@ -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/. */
+
+"use strict";
+
+(function() {
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+
+let nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
+ "initWithPath");
+
+const MSG_JAR_FLUSH = "AddonJarFlush";
+
+
+try {
+ if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
+ // Propagate JAR cache flush notifications across process boundaries.
+ addMessageListener(MSG_JAR_FLUSH, function jar_flushMessageListener(message) {
+ let file = new nsIFile(message.data);
+ Services.obs.notifyObservers(file, "flush-cache-entry", null);
+ });
+ }
+} catch(e) {
+ Cu.reportError(e);
+}
+
+})();
diff --git a/toolkit/mozapps/extensions/internal/GMPProvider.jsm b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
new file mode 100644
index 000000000..a55457f6e
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -0,0 +1,614 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/GMPUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(
+ this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(
+ this, "setTimeout", "resource://gre/modules/Timer.jsm");
+
+const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
+const STRING_TYPE_NAME = "type.%ID%.name";
+
+const SEC_IN_A_DAY = 24 * 60 * 60;
+// How long to wait after a user enabled EME before attempting to download CDMs.
+const GMP_CHECK_DELAY = 10 * 1000; // milliseconds
+
+const NS_GRE_DIR = "GreD";
+const CLEARKEY_PLUGIN_ID = "gmp-clearkey";
+const CLEARKEY_VERSION = "0.1";
+
+const GMP_LICENSE_INFO = "gmp_license_info";
+
+const GMP_PLUGINS = [
+ {
+ id: OPEN_H264_ID,
+ name: "openH264_name",
+ description: "openH264_description2",
+ // The following licenseURL is part of an awful hack to include the OpenH264
+ // license without having bug 624602 fixed yet, and intentionally ignores
+ // localisation.
+ licenseURL: "chrome://mozapps/content/extensions/OpenH264-license.txt",
+ homepageURL: "http://www.openh264.org/",
+ optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul"
+ },
+ {
+ id: EME_ADOBE_ID,
+ name: "eme-adobe_name",
+ description: "eme-adobe_description",
+ licenseURL: "http://help.adobe.com/en_US/primetime/drm/HTML5_CDM_EULA/index.html",
+ homepageURL: "http://help.adobe.com/en_US/primetime/drm/HTML5_CDM",
+ optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul",
+ isEME: true
+ }];
+
+XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
+ () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
+XPCOMUtils.defineLazyGetter(this, "gmpService",
+ () => Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(Ci.mozIGeckoMediaPluginChromeService));
+
+let messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager);
+
+let gLogger;
+let gLogAppenderDump = null;
+
+function configureLogging() {
+ if (!gLogger) {
+ gLogger = Log.repository.getLogger("Toolkit.GMP");
+ gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
+ }
+ gLogger.level = GMPPrefs.get(GMPPrefs.KEY_LOGGING_LEVEL, Log.Level.Warn);
+
+ let logDumping = GMPPrefs.get(GMPPrefs.KEY_LOGGING_DUMP, false);
+ if (logDumping != !!gLogAppenderDump) {
+ if (logDumping) {
+ gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter());
+ gLogger.addAppender(gLogAppenderDump);
+ } else {
+ gLogger.removeAppender(gLogAppenderDump);
+ gLogAppenderDump = null;
+ }
+ }
+}
+
+
+
+/**
+ * The GMPWrapper provides the info for the various GMP plugins to public
+ * callers through the API.
+ */
+function GMPWrapper(aPluginInfo) {
+ this._plugin = aPluginInfo;
+ this._log =
+ Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
+ "GMPWrapper(" +
+ this._plugin.id + ") ");
+ Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
+ this._plugin.id),
+ this.onPrefEnabledChanged, this);
+ Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
+ this._plugin.id),
+ this.onPrefVersionChanged, this);
+ if (this._plugin.isEME) {
+ Preferences.observe(GMPPrefs.KEY_EME_ENABLED,
+ this.onPrefEMEGlobalEnabledChanged, this);
+ messageManager.addMessageListener("EMEVideo:ContentMediaKeysRequest", this);
+ }
+}
+
+GMPWrapper.prototype = {
+ // An active task that checks for plugin updates and installs them.
+ _updateTask: null,
+ _gmpPath: null,
+ _isUpdateCheckPending: false,
+
+ optionsType: AddonManager.OPTIONS_TYPE_INLINE,
+ get optionsURL() { return this._plugin.optionsURL; },
+
+ set gmpPath(aPath) { this._gmpPath = aPath; },
+ get gmpPath() {
+ if (!this._gmpPath && this.isInstalled) {
+ this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
+ this._plugin.id,
+ GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION,
+ null, this._plugin.id));
+ }
+ return this._gmpPath;
+ },
+
+ get id() { return this._plugin.id; },
+ get type() { return "plugin"; },
+ get isGMPlugin() { return true; },
+ get name() { return this._plugin.name; },
+ get creator() { return null; },
+ get homepageURL() { return this._plugin.homepageURL; },
+
+ get description() { return this._plugin.description; },
+ get fullDescription() { return this._plugin.fullDescription; },
+
+ get version() { return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, null,
+ this._plugin.id); },
+
+ get isActive() { return !this.appDisabled && !this.userDisabled; },
+ get appDisabled() {
+ if (this._plugin.isEME && !GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true)) {
+ // If "media.eme.enabled" is false, all EME plugins are disabled.
+ return true;
+ }
+ return false;
+ },
+
+ get userDisabled() {
+ return !GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ENABLED, true, this._plugin.id);
+ },
+ set userDisabled(aVal) { GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ENABLED,
+ aVal === false,
+ this._plugin.id); },
+
+ get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; },
+ get size() { return 0; },
+ get scope() { return AddonManager.SCOPE_APPLICATION; },
+ get pendingOperations() { return AddonManager.PENDING_NONE; },
+
+ get operationsRequiringRestart() { return AddonManager.OP_NEEDS_RESTART_NONE },
+
+ get permissions() {
+ let permissions = 0;
+ if (!this.appDisabled) {
+ permissions |= AddonManager.PERM_CAN_UPGRADE;
+ permissions |= this.userDisabled ? AddonManager.PERM_CAN_ENABLE :
+ AddonManager.PERM_CAN_DISABLE;
+ }
+ return permissions;
+ },
+
+ get updateDate() {
+ let time = Number(GMPPrefs.get(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, null,
+ this._plugin.id));
+ if (time !== NaN && this.isInstalled) {
+ return new Date(time * 1000)
+ }
+ return null;
+ },
+
+ get isCompatible() {
+ return true;
+ },
+
+ get isPlatformCompatible() {
+ return true;
+ },
+
+ get providesUpdatesSecurely() {
+ return true;
+ },
+
+ get foreignInstall() {
+ return false;
+ },
+
+ isCompatibleWith: function(aAppVersion, aPlatformVersion) {
+ return true;
+ },
+
+ get applyBackgroundUpdates() {
+ if (!GMPPrefs.isSet(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id)) {
+ return AddonManager.AUTOUPDATE_DEFAULT;
+ }
+
+ return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id) ?
+ AddonManager.AUTOUPDATE_ENABLE : AddonManager.AUTOUPDATE_DISABLE;
+ },
+
+ set applyBackgroundUpdates(aVal) {
+ if (aVal == AddonManager.AUTOUPDATE_DEFAULT) {
+ GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id);
+ } else if (aVal == AddonManager.AUTOUPDATE_ENABLE) {
+ GMPPrefs.set(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id);
+ } else if (aVal == AddonManager.AUTOUPDATE_DISABLE) {
+ GMPPrefs.set(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, this._plugin.id);
+ }
+ },
+
+ findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ this._log.trace("findUpdates() - " + this._plugin.id + " - reason=" +
+ aReason);
+
+ AddonManagerPrivate.callNoUpdateListeners(this, aListener);
+
+ if (aReason === AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
+ if (!AddonManager.shouldAutoUpdate(this)) {
+ this._log.trace("findUpdates() - " + this._plugin.id +
+ " - no autoupdate");
+ return Promise.resolve(false);
+ }
+
+ let secSinceLastCheck =
+ Date.now() / 1000 - Preferences.get(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0);
+ if (secSinceLastCheck <= SEC_IN_A_DAY) {
+ this._log.trace("findUpdates() - " + this._plugin.id +
+ " - last check was less then a day ago");
+ return Promise.resolve(false);
+ }
+ } else if (aReason !== AddonManager.UPDATE_WHEN_USER_REQUESTED) {
+ this._log.trace("findUpdates() - " + this._plugin.id +
+ " - the given reason to update is not supported");
+ return Promise.resolve(false);
+ }
+
+ if (this._updateTask !== null) {
+ this._log.trace("findUpdates() - " + this._plugin.id +
+ " - update task already running");
+ return this._updateTask;
+ }
+
+ this._updateTask = Task.spawn(function* GMPProvider_updateTask() {
+ this._log.trace("findUpdates() - updateTask");
+ try {
+ let installManager = new GMPInstallManager();
+ let gmpAddons = yield installManager.checkForAddons();
+ let update = gmpAddons.find(function(aAddon) {
+ return aAddon.id === this._plugin.id;
+ }, this);
+ if (update && update.isValid && !update.isInstalled) {
+ this._log.trace("findUpdates() - found update for " +
+ this._plugin.id + ", installing");
+ yield installManager.installAddon(update);
+ } else {
+ this._log.trace("findUpdates() - no updates for " + this._plugin.id);
+ }
+ this._log.info("findUpdates() - updateTask succeeded for " +
+ this._plugin.id);
+ } catch (e) {
+ this._log.error("findUpdates() - updateTask for " + this._plugin.id +
+ " threw", e);
+ throw e;
+ } finally {
+ this._updateTask = null;
+ return true;
+ }
+ }.bind(this));
+
+ return this._updateTask;
+ },
+
+ get pluginMimeTypes() { return []; },
+ get pluginLibraries() {
+ if (this.isInstalled) {
+ let path = this.version;
+ return [path];
+ }
+ return [];
+ },
+ get pluginFullpath() {
+ if (this.isInstalled) {
+ let path = OS.Path.join(OS.Constants.Path.profileDir,
+ this._plugin.id,
+ this.version);
+ return [path];
+ }
+ return [];
+ },
+
+ get isInstalled() {
+ return this.version && this.version.length > 0;
+ },
+
+ _handleEnabledChanged: function() {
+ AddonManagerPrivate.callAddonListeners(this.isActive ?
+ "onEnabling" : "onDisabling",
+ this, false);
+ if (this._gmpPath) {
+ if (this.isActive) {
+ this._log.info("onPrefEnabledChanged() - adding gmp directory " +
+ this._gmpPath);
+ gmpService.addPluginDirectory(this._gmpPath);
+ } else {
+ this._log.info("onPrefEnabledChanged() - removing gmp directory " +
+ this._gmpPath);
+ gmpService.removePluginDirectory(this._gmpPath);
+ }
+ }
+ AddonManagerPrivate.callAddonListeners(this.isActive ?
+ "onEnabled" : "onDisabled",
+ this);
+ },
+
+ onPrefEMEGlobalEnabledChanged: function() {
+ AddonManagerPrivate.callAddonListeners("onPropertyChanged", this,
+ ["appDisabled"]);
+ if (this.appDisabled) {
+ this.uninstallPlugin();
+ } else {
+ AddonManagerPrivate.callInstallListeners("onExternalInstall", null, this,
+ null, false);
+ AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
+ AddonManagerPrivate.callAddonListeners("onInstalled", this);
+ this.checkForUpdates(GMP_CHECK_DELAY);
+ }
+ if (!this.userDisabled) {
+ this._handleEnabledChanged();
+ }
+ },
+
+ checkForUpdates: function(delay) {
+ if (this._isUpdateCheckPending) {
+ return;
+ }
+ this._isUpdateCheckPending = true;
+ GMPPrefs.reset(GMPPrefs.KEY_UPDATE_LAST_CHECK, null);
+ // Delay this in case the user changes his mind and doesn't want to
+ // enable EME after all.
+ setTimeout(() => {
+ if (!this.appDisabled) {
+ let gmpInstallManager = new GMPInstallManager();
+ // We don't really care about the results, if someone is interested
+ // they can check the log.
+ gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
+ }
+ this._isUpdateCheckPending = false;
+ }, delay);
+ },
+
+ receiveMessage: function({target: browser, data: data}) {
+ this._log.trace("receiveMessage() data=" + data);
+ let parsedData;
+ try {
+ parsedData = JSON.parse(data);
+ } catch(ex) {
+ this._log.error("Malformed EME video message with data: " + data);
+ return;
+ }
+ let {status: status, keySystem: keySystem} = parsedData;
+ if (status == "cdm-not-installed" || status == "cdm-insufficient-version") {
+ this.checkForUpdates(0);
+ }
+ },
+
+ onPrefEnabledChanged: function() {
+ if (!this._plugin.isEME || !this.appDisabled) {
+ this._handleEnabledChanged();
+ }
+ },
+
+ onPrefVersionChanged: function() {
+ AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
+ if (this._gmpPath) {
+ this._log.info("onPrefVersionChanged() - unregistering gmp directory " +
+ this._gmpPath);
+ gmpService.removeAndDeletePluginDirectory(this._gmpPath, true /* can defer */);
+ }
+ AddonManagerPrivate.callAddonListeners("onUninstalled", this);
+
+ AddonManagerPrivate.callInstallListeners("onExternalInstall", null, this,
+ null, false);
+ AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
+ this._gmpPath = null;
+ if (this.isInstalled) {
+ this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
+ this._plugin.id,
+ GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION,
+ null, this._plugin.id));
+ }
+ if (this._gmpPath && this.isActive) {
+ this._log.info("onPrefVersionChanged() - registering gmp directory " +
+ this._gmpPath);
+ gmpService.addPluginDirectory(this._gmpPath);
+ }
+ AddonManagerPrivate.callAddonListeners("onInstalled", this);
+ },
+
+ uninstallPlugin: function() {
+ AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
+ if (this.gmpPath) {
+ this._log.info("uninstallPlugin() - unregistering gmp directory " +
+ this.gmpPath);
+ gmpService.removeAndDeletePluginDirectory(this.gmpPath);
+ }
+ GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_VERSION, this.id);
+ AddonManagerPrivate.callAddonListeners("onUninstalled", this);
+ },
+
+ shutdown: function() {
+ Preferences.ignore(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
+ this._plugin.id),
+ this.onPrefEnabledChanged, this);
+ Preferences.ignore(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
+ this._plugin.id),
+ this.onPrefVersionChanged, this);
+ if (this._plugin.isEME) {
+ Preferences.ignore(GMPPrefs.KEY_EME_ENABLED,
+ this.onPrefEMEGlobalEnabledChanged, this);
+ messageManager.removeMessageListener("EMEVideo:ContentMediaKeysRequest", this);
+ }
+ return this._updateTask;
+ },
+};
+
+let GMPProvider = {
+ get name() { return "GMPProvider"; },
+
+ _plugins: null,
+
+ startup: function() {
+ configureLogging();
+ this._log = Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
+ "GMPProvider.");
+ let telemetry = {};
+ this.buildPluginList();
+ this.ensureProperCDMInstallState();
+
+ Preferences.observe(GMPPrefs.KEY_LOG_BASE, configureLogging);
+
+ for (let [id, plugin] of this._plugins) {
+ let wrapper = plugin.wrapper;
+ let gmpPath = wrapper.gmpPath;
+ let isEnabled = wrapper.isActive;
+ this._log.trace("startup - enabled=" + isEnabled + ", gmpPath=" +
+ gmpPath);
+
+ if (gmpPath && isEnabled) {
+ this._log.info("startup - adding gmp directory " + gmpPath);
+ try {
+ gmpService.addPluginDirectory(gmpPath);
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ this._log.warn("startup - adding gmp directory failed with " +
+ e.name + " - sandboxing not available?", e);
+ }
+ }
+
+ if (this.isEnabled) {
+ telemetry[id] = {
+ userDisabled: wrapper.userDisabled,
+ version: wrapper.version,
+ applyBackgroundUpdates: wrapper.applyBackgroundUpdates,
+ };
+ }
+ }
+
+ if (Preferences.get(GMPPrefs.KEY_EME_ENABLED, false)) {
+ try {
+ let greDir = Services.dirsvc.get(NS_GRE_DIR,
+ Ci.nsILocalFile);
+ let clearkeyPath = OS.Path.join(greDir.path,
+ CLEARKEY_PLUGIN_ID,
+ CLEARKEY_VERSION);
+ this._log.info("startup - adding clearkey CDM directory " +
+ clearkeyPath);
+ gmpService.addPluginDirectory(clearkeyPath);
+ } catch (e) {
+ this._log.warn("startup - adding clearkey CDM failed", e);
+ }
+ }
+
+ AddonManagerPrivate.setTelemetryDetails("GMP", telemetry);
+ },
+
+ shutdown: function() {
+ this._log.trace("shutdown");
+ Preferences.ignore(GMPPrefs.KEY_LOG_BASE, configureLogging);
+
+ let shutdownTask = Task.spawn(function* GMPProvider_shutdownTask() {
+ this._log.trace("shutdown - shutdownTask");
+ let shutdownSucceeded = true;
+
+ for (let plugin of this._plugins.values()) {
+ try {
+ yield plugin.wrapper.shutdown();
+ } catch (e) {
+ shutdownSucceeded = false;
+ }
+ }
+
+ this._plugins = null;
+
+ if (!shutdownSucceeded) {
+ throw new Error("Shutdown failed");
+ }
+ }.bind(this));
+
+ return shutdownTask;
+ },
+
+ getAddonByID: function(aId, aCallback) {
+ if (!this.isEnabled) {
+ aCallback(null);
+ return;
+ }
+
+ let plugin = this._plugins.get(aId);
+ if (plugin && !GMPUtils.isPluginHidden(plugin)) {
+ aCallback(plugin.wrapper);
+ } else {
+ aCallback(null);
+ }
+ },
+
+ getAddonsByTypes: function(aTypes, aCallback) {
+ if (!this.isEnabled ||
+ (aTypes && aTypes.indexOf("plugin") < 0)) {
+ aCallback([]);
+ return;
+ }
+
+ // Tycho:
+ // let results = [p.wrapper for ([id, p] of this._plugins)
+ // if (!GMPUtils.isPluginHidden(p))];
+ let results = [];
+ for (let [id, p] of this._plugins) {
+ if (!GMPUtils.isPluginHidden(p)) {
+ results.push(p.wrapper);
+ }
+ }
+
+ aCallback(results);
+ },
+
+ get isEnabled() {
+ return GMPPrefs.get(GMPPrefs.KEY_PROVIDER_ENABLED, false);
+ },
+
+ generateFullDescription: function(aLicenseURL, aLicenseInfo) {
+ return "<xhtml:a href=\"" + aLicenseURL + "\" target=\"_blank\">" +
+ aLicenseInfo + "</xhtml:a>."
+ },
+
+ buildPluginList: function() {
+ let licenseInfo = pluginsBundle.GetStringFromName(GMP_LICENSE_INFO);
+
+ this._plugins = new Map();
+ for (let aPlugin of GMP_PLUGINS) {
+ let plugin = {
+ id: aPlugin.id,
+ name: pluginsBundle.GetStringFromName(aPlugin.name),
+ description: pluginsBundle.GetStringFromName(aPlugin.description),
+ homepageURL: aPlugin.homepageURL,
+ optionsURL: aPlugin.optionsURL,
+ wrapper: null,
+ isEME: aPlugin.isEME,
+ };
+ if (aPlugin.licenseURL) {
+ plugin.fullDescription =
+ this.generateFullDescription(aPlugin.licenseURL, licenseInfo);
+ }
+ plugin.wrapper = new GMPWrapper(plugin);
+ this._plugins.set(plugin.id, plugin);
+ }
+ },
+
+ ensureProperCDMInstallState: function() {
+ if (!GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true)) {
+ for (let [id, plugin] of this._plugins) {
+ if (plugin.isEME && plugin.wrapper.isInstalled) {
+ gmpService.addPluginDirectory(plugin.wrapper.gmpPath);
+ plugin.wrapper.uninstallPlugin();
+ }
+ }
+ }
+ },
+};
+
+AddonManagerPrivate.registerProvider(GMPProvider, [
+ new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 6000,
+ AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE)
+]);
diff --git a/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
new file mode 100644
index 000000000..fccde9a81
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
@@ -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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["LightweightThemeImageOptimizer"];
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+ "resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+
+const ORIGIN_TOP_RIGHT = 1;
+const ORIGIN_BOTTOM_LEFT = 2;
+
+this.LightweightThemeImageOptimizer = {
+ optimize: function LWTIO_optimize(aThemeData, aScreen) {
+ let data = Utils.createCopy(aThemeData);
+ if (!data.headerURL) {
+ return data;
+ }
+
+ data.headerURL = ImageCropper.getCroppedImageURL(
+ data.headerURL, aScreen, ORIGIN_TOP_RIGHT);
+
+ if (data.footerURL) {
+ data.footerURL = ImageCropper.getCroppedImageURL(
+ data.footerURL, aScreen, ORIGIN_BOTTOM_LEFT);
+ }
+
+ return data;
+ },
+
+ purge: function LWTIO_purge() {
+ let dir = FileUtils.getDir("ProfD", ["lwtheme"]);
+ dir.followLinks = false;
+ try {
+ dir.remove(true);
+ } catch (e) {}
+ }
+};
+
+Object.freeze(LightweightThemeImageOptimizer);
+
+let ImageCropper = {
+ _inProgress: {},
+
+ getCroppedImageURL:
+ function ImageCropper_getCroppedImageURL(aImageURL, aScreen, aOrigin) {
+ // We can crop local files, only.
+ if (!aImageURL.startsWith("file://")) {
+ return aImageURL;
+ }
+
+ if (Services.prefs.getBoolPref("lightweightThemes.animation.enabled")) {
+ //Don't crop if animated
+ return aImageURL;
+ }
+
+ // Generate the cropped image's file name using its
+ // base name and the current screen size.
+ let uri = Services.io.newURI(aImageURL, null, null);
+ let file = uri.QueryInterface(Ci.nsIFileURL).file;
+
+ // Make sure the source file exists.
+ if (!file.exists()) {
+ return aImageURL;
+ }
+
+ let fileName = file.leafName + "-" + aScreen.width + "x" + aScreen.height;
+ let croppedFile = FileUtils.getFile("ProfD", ["lwtheme", fileName]);
+
+ // If we have a local file that is not in progress, return it.
+ if (croppedFile.exists() && !(croppedFile.path in this._inProgress)) {
+ let fileURI = Services.io.newFileURI(croppedFile);
+
+ // Copy the query part to avoid wrong caching.
+ fileURI.QueryInterface(Ci.nsIURL).query = uri.query;
+ return fileURI.spec;
+ }
+
+ // Crop the given image in the background.
+ this._crop(uri, croppedFile, aScreen, aOrigin);
+
+ // Return the original image while we're waiting for the cropped version
+ // to be written to disk.
+ return aImageURL;
+ },
+
+ _crop: function ImageCropper_crop(aURI, aTargetFile, aScreen, aOrigin) {
+ let inProgress = this._inProgress;
+ inProgress[aTargetFile.path] = true;
+
+ function resetInProgress() {
+ delete inProgress[aTargetFile.path];
+ }
+
+ ImageFile.read(aURI, function crop_readImageFile(aInputStream, aContentType) {
+ if (aInputStream && aContentType) {
+ let image = ImageTools.decode(aInputStream, aContentType);
+ if (image && image.width && image.height) {
+ let stream = ImageTools.encode(image, aScreen, aOrigin, aContentType);
+ if (stream) {
+ ImageFile.write(aTargetFile, stream, resetInProgress);
+ return;
+ }
+ }
+ }
+
+ resetInProgress();
+ });
+ }
+};
+
+let ImageFile = {
+ read: function ImageFile_read(aURI, aCallback) {
+ this._netUtil.asyncFetch2(
+ aURI,
+ function read_asyncFetch(aInputStream, aStatus, aRequest) {
+ if (Components.isSuccessCode(aStatus) && aRequest instanceof Ci.nsIChannel) {
+ let channel = aRequest.QueryInterface(Ci.nsIChannel);
+ aCallback(aInputStream, channel.contentType);
+ } else {
+ aCallback();
+ }
+ },
+ null, // aLoadingNode
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_NORMAL,
+ Ci.nsIContentPolicy.TYPE_IMAGE);
+ },
+
+ write: function ImageFile_write(aFile, aInputStream, aCallback) {
+ let fos = FileUtils.openSafeFileOutputStream(aFile);
+ this._netUtil.asyncCopy(aInputStream, fos, function write_asyncCopy(aResult) {
+ FileUtils.closeSafeFileOutputStream(fos);
+
+ // Remove the file if writing was not successful.
+ if (!Components.isSuccessCode(aResult)) {
+ try {
+ aFile.remove(false);
+ } catch (e) {}
+ }
+
+ aCallback();
+ });
+ }
+};
+
+XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil",
+ "resource://gre/modules/NetUtil.jsm", "NetUtil");
+
+let ImageTools = {
+ decode: function ImageTools_decode(aInputStream, aContentType) {
+ let outParam = {value: null};
+
+ try {
+ this._imgTools.decodeImageData(aInputStream, aContentType, outParam);
+ } catch (e) {}
+
+ return outParam.value;
+ },
+
+ encode: function ImageTools_encode(aImage, aScreen, aOrigin, aContentType) {
+ let stream;
+ let width = Math.min(aImage.width, aScreen.width);
+ let height = Math.min(aImage.height, aScreen.height);
+ let x = aOrigin == ORIGIN_TOP_RIGHT ? aImage.width - width : 0;
+
+ try {
+ stream = this._imgTools.encodeCroppedImage(aImage, aContentType, x, 0,
+ width, height);
+ } catch (e) {}
+
+ return stream;
+ }
+};
+
+XPCOMUtils.defineLazyServiceGetter(ImageTools, "_imgTools",
+ "@mozilla.org/image/tools;1", "imgITools");
+
+let Utils = {
+ createCopy: function Utils_createCopy(aData) {
+ let copy = {};
+ for (let [k, v] in Iterator(aData)) {
+ copy[k] = v;
+ }
+ return copy;
+ }
+};
diff --git a/toolkit/mozapps/extensions/internal/PluginProvider.jsm b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
new file mode 100644
index 000000000..04a4f9d7c
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -0,0 +1,595 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [];
+
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
+const STRING_TYPE_NAME = "type.%ID%.name";
+const LIST_UPDATED_TOPIC = "plugins-list-updated";
+const FLASH_MIME_TYPE = "application/x-shockwave-flash";
+
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.plugins";
+
+// Create a new logger for use by the Addons Plugin Provider
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+function getIDHashForString(aStr) {
+ // return the two-digit hexadecimal code for a byte
+ function toHexString(charCode)
+ ("0" + charCode.toString(16)).slice(-2);
+
+ let hasher = Cc["@mozilla.org/security/hash;1"].
+ createInstance(Ci.nsICryptoHash);
+ hasher.init(Ci.nsICryptoHash.MD5);
+ let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
+ createInstance(Ci.nsIStringInputStream);
+ stringStream.data = aStr ? aStr : "null";
+ hasher.updateFromStream(stringStream, -1);
+
+ // convert the binary hash data to a hex string.
+ let binary = hasher.finish(false);
+
+ // Tycho: let hash = [toHexString(binary.charCodeAt(i)) for (i in binary)].join("").toLowerCase();
+ let hash = [];
+
+ for (let i in binary) {
+ hash.push(toHexString(binary.charCodeAt(i)));
+ }
+
+ hash = hash.join("").toLowerCase();
+
+ return "{" + hash.substr(0, 8) + "-" +
+ hash.substr(8, 4) + "-" +
+ hash.substr(12, 4) + "-" +
+ hash.substr(16, 4) + "-" +
+ hash.substr(20) + "}";
+}
+
+var PluginProvider = {
+ get name() "PluginProvider",
+
+ // A dictionary mapping IDs to names and descriptions
+ plugins: null,
+
+ startup: function PL_startup() {
+ Services.obs.addObserver(this, LIST_UPDATED_TOPIC, false);
+ Services.obs.addObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
+ },
+
+ /**
+ * Called when the application is shutting down. Only necessary for tests
+ * to be able to simulate a shutdown.
+ */
+ shutdown: function PL_shutdown() {
+ this.plugins = null;
+ Services.obs.removeObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
+ Services.obs.removeObserver(this, LIST_UPDATED_TOPIC);
+ },
+
+ observe: function(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case AddonManager.OPTIONS_NOTIFICATION_DISPLAYED:
+ this.getAddonByID(aData, function PL_displayPluginInfo(plugin) {
+ if (!plugin)
+ return;
+
+ let libLabel = aSubject.getElementById("pluginLibraries");
+ libLabel.textContent = plugin.pluginLibraries.join(", ");
+
+ let typeLabel = aSubject.getElementById("pluginMimeTypes"), types = [];
+ for (let type of plugin.pluginMimeTypes) {
+ let extras = [type.description.trim(), type.suffixes].
+ filter(function(x) x).join(": ");
+ types.push(type.type + (extras ? " (" + extras + ")" : ""));
+ }
+ typeLabel.textContent = types.join(",\n");
+ let showProtectedModePref = canDisableFlashProtectedMode(plugin);
+ aSubject.getElementById("pluginEnableProtectedMode")
+ .setAttribute("collapsed", showProtectedModePref ? "" : "true");
+ });
+ break;
+ case LIST_UPDATED_TOPIC:
+ if (this.plugins)
+ this.updatePluginList();
+ break;
+ }
+ },
+
+ /**
+ * Creates a PluginWrapper for a plugin object.
+ */
+ buildWrapper: function PL_buildWrapper(aPlugin) {
+ return new PluginWrapper(aPlugin.id,
+ aPlugin.name,
+ aPlugin.description,
+ aPlugin.tags);
+ },
+
+ /**
+ * Called to get an Addon with a particular ID.
+ *
+ * @param aId
+ * The ID of the add-on to retrieve
+ * @param aCallback
+ * A callback to pass the Addon to
+ */
+ getAddonByID: function PL_getAddon(aId, aCallback) {
+ if (!this.plugins)
+ this.buildPluginList();
+
+ if (aId in this.plugins)
+ aCallback(this.buildWrapper(this.plugins[aId]));
+ else
+ aCallback(null);
+ },
+
+ /**
+ * Called to get Addons of a particular type.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types.
+ * @param callback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsByTypes: function PL_getAddonsByTypes(aTypes, aCallback) {
+ if (aTypes && aTypes.indexOf("plugin") < 0) {
+ aCallback([]);
+ return;
+ }
+
+ if (!this.plugins)
+ this.buildPluginList();
+
+ let results = [];
+
+ for (let id in this.plugins) {
+ this.getAddonByID(id, function(aAddon) {
+ results.push(aAddon);
+ });
+ }
+
+ aCallback(results);
+ },
+
+ /**
+ * Called to get Addons that have pending operations.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types
+ * @param aCallback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsWithOperationsByTypes: function PL_getAddonsWithOperationsByTypes(aTypes, aCallback) {
+ aCallback([]);
+ },
+
+ /**
+ * Called to get the current AddonInstalls, optionally restricting by type.
+ *
+ * @param aTypes
+ * An array of types or null to get all types
+ * @param aCallback
+ * A callback to pass the array of AddonInstalls to
+ */
+ getInstallsByTypes: function PL_getInstallsByTypes(aTypes, aCallback) {
+ aCallback([]);
+ },
+
+ /**
+ * Builds a list of the current plugins reported by the plugin host
+ *
+ * @return a dictionary of plugins indexed by our generated ID
+ */
+ getPluginList: function PL_getPluginList() {
+ let tags = Cc["@mozilla.org/plugin/host;1"].
+ getService(Ci.nsIPluginHost).
+ getPluginTags({});
+
+ let list = {};
+ let seenPlugins = {};
+ for (let tag of tags) {
+ if (!(tag.name in seenPlugins))
+ seenPlugins[tag.name] = {};
+ if (!(tag.description in seenPlugins[tag.name])) {
+ let plugin = {
+ id: getIDHashForString(tag.name + tag.description),
+ // XXX Flash name substitution like in browser-plugins.js, aboutPermissions.js, permissions.js
+ name: tag.name == "Shockwave Flash" ? "Adobe Flash" : tag.name,
+ description: tag.description,
+ tags: [tag]
+ };
+
+ seenPlugins[tag.name][tag.description] = plugin;
+ list[plugin.id] = plugin;
+ }
+ else {
+ seenPlugins[tag.name][tag.description].tags.push(tag);
+ }
+ }
+
+ return list;
+ },
+
+ /**
+ * Builds the list of known plugins from the plugin host
+ */
+ buildPluginList: function PL_buildPluginList() {
+ this.plugins = this.getPluginList();
+ },
+
+ /**
+ * Updates the plugins from the plugin host by comparing the current plugins
+ * to the last known list sending out any necessary API notifications for
+ * changes.
+ */
+ updatePluginList: function PL_updatePluginList() {
+ let newList = this.getPluginList();
+
+ // Tycho:
+ // let lostPlugins = [this.buildWrapper(this.plugins[id])
+ // for each (id in Object.keys(this.plugins)) if (!(id in newList))];
+
+ // let newPlugins = [this.buildWrapper(newList[id])
+ // for each (id in Object.keys(newList)) if (!(id in this.plugins))];
+
+ // let matchedIDs = [id for each (id in Object.keys(newList)) if (id in this.plugins)];
+
+ let lostPlugins = [];
+ let newPlugins = [];
+ let matchedIDs = [];
+
+ // lostPlugins
+ for each(let id in Object.keys(this.plugins)) {
+ if (!(id in newList)) {
+ lostPlugins.push(this.buildWrapper(this.plugins[id]));
+ }
+ }
+
+ // newPlugins and matchedIDs
+ for each(let id in Object.keys(newList)) {
+ if (!(id in this.plugins)) {
+ newPlugins.push(this.buildWrapper(newList[id]));
+ }
+
+ if (id in this.plugins) {
+ matchedIDs.push(id);
+ }
+ }
+
+
+ // The plugin host generates new tags for every plugin after a scan and
+ // if the plugin's filename has changed then the disabled state won't have
+ // been carried across, send out notifications for anything that has
+ // changed (see bug 830267).
+ let changedWrappers = [];
+ for (let id of matchedIDs) {
+ let oldWrapper = this.buildWrapper(this.plugins[id]);
+ let newWrapper = this.buildWrapper(newList[id]);
+
+ if (newWrapper.isActive != oldWrapper.isActive) {
+ AddonManagerPrivate.callAddonListeners(newWrapper.isActive ?
+ "onEnabling" : "onDisabling",
+ newWrapper, false);
+ changedWrappers.push(newWrapper);
+ }
+ }
+
+ // Notify about new installs
+ for (let plugin of newPlugins) {
+ AddonManagerPrivate.callInstallListeners("onExternalInstall", null,
+ plugin, null, false);
+ AddonManagerPrivate.callAddonListeners("onInstalling", plugin, false);
+ }
+
+ // Notify for any plugins that have vanished.
+ for (let plugin of lostPlugins)
+ AddonManagerPrivate.callAddonListeners("onUninstalling", plugin, false);
+
+ this.plugins = newList;
+
+ // Signal that new installs are complete
+ for (let plugin of newPlugins)
+ AddonManagerPrivate.callAddonListeners("onInstalled", plugin);
+
+ // Signal that enables/disables are complete
+ for (let wrapper of changedWrappers) {
+ AddonManagerPrivate.callAddonListeners(wrapper.isActive ?
+ "onEnabled" : "onDisabled",
+ wrapper);
+ }
+
+ // Signal that uninstalls are complete
+ for (let plugin of lostPlugins)
+ AddonManagerPrivate.callAddonListeners("onUninstalled", plugin);
+ }
+};
+
+function isFlashPlugin(aPlugin) {
+ for (let type of aPlugin.pluginMimeTypes) {
+ if (type.type == FLASH_MIME_TYPE) {
+ return true;
+ }
+ }
+ return false;
+}
+// Protected mode is win32-only, not win64
+function canDisableFlashProtectedMode(aPlugin) {
+ return isFlashPlugin(aPlugin) && Services.appinfo.XPCOMABI == "x86-msvc";
+}
+
+/**
+ * The PluginWrapper wraps a set of nsIPluginTags to provide the data visible to
+ * public callers through the API.
+ */
+function PluginWrapper(aId, aName, aDescription, aTags) {
+ let safedesc = aDescription.replace(/<\/?[a-z][^>]*>/gi, " ");
+ let homepageURL = null;
+ if (/<A\s+HREF=[^>]*>/i.test(aDescription))
+ homepageURL = /<A\s+HREF=["']?([^>"'\s]*)/i.exec(aDescription)[1];
+
+ this.__defineGetter__("id", function() aId);
+ this.__defineGetter__("type", function() "plugin");
+ this.__defineGetter__("name", function() aName);
+ this.__defineGetter__("creator", function() null);
+ this.__defineGetter__("description", function() safedesc);
+ this.__defineGetter__("version", function() aTags[0].version);
+ this.__defineGetter__("homepageURL", function() homepageURL);
+
+ this.__defineGetter__("isActive", function() !aTags[0].blocklisted && !aTags[0].disabled);
+ this.__defineGetter__("appDisabled", function() aTags[0].blocklisted);
+
+ this.__defineGetter__("userDisabled", function() {
+ if (aTags[0].disabled)
+ return true;
+
+ if ((Services.prefs.getBoolPref("plugins.click_to_play") && aTags[0].clicktoplay) ||
+ this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE ||
+ this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE)
+ return AddonManager.STATE_ASK_TO_ACTIVATE;
+
+ return false;
+ });
+
+ this.__defineSetter__("userDisabled", function(aVal) {
+ let previousVal = this.userDisabled;
+ if (aVal === previousVal)
+ return aVal;
+
+ for (let tag of aTags) {
+ if (aVal === true)
+ tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ else if (aVal === false)
+ tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ else if (aVal == AddonManager.STATE_ASK_TO_ACTIVATE)
+ tag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
+ }
+
+ // If 'userDisabled' was 'true' and we're going to a state that's not
+ // that, we're enabling, so call those listeners.
+ if (previousVal === true && aVal !== true) {
+ AddonManagerPrivate.callAddonListeners("onEnabling", this, false);
+ AddonManagerPrivate.callAddonListeners("onEnabled", this);
+ }
+
+ // If 'userDisabled' was not 'true' and we're going to a state where
+ // it is, we're disabling, so call those listeners.
+ if (previousVal !== true && aVal === true) {
+ AddonManagerPrivate.callAddonListeners("onDisabling", this, false);
+ AddonManagerPrivate.callAddonListeners("onDisabled", this);
+ }
+
+ // If the 'userDisabled' value involved AddonManager.STATE_ASK_TO_ACTIVATE,
+ // call the onPropertyChanged listeners.
+ if (previousVal == AddonManager.STATE_ASK_TO_ACTIVATE ||
+ aVal == AddonManager.STATE_ASK_TO_ACTIVATE) {
+ AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["userDisabled"]);
+ }
+
+ return aVal;
+ });
+
+
+ this.__defineGetter__("blocklistState", function() {
+ let bs = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsIBlocklistService);
+ return bs.getPluginBlocklistState(aTags[0]);
+ });
+
+ this.__defineGetter__("blocklistURL", function() {
+ let bs = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsIBlocklistService);
+ return bs.getPluginBlocklistURL(aTags[0]);
+ });
+
+ this.__defineGetter__("size", function() {
+ function getDirectorySize(aFile) {
+ let size = 0;
+ let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
+ let entry;
+ while ((entry = entries.nextFile)) {
+ if (entry.isSymlink() || !entry.isDirectory())
+ size += entry.fileSize;
+ else
+ size += getDirectorySize(entry);
+ }
+ entries.close();
+ return size;
+ }
+
+ let size = 0;
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ for (let tag of aTags) {
+ file.initWithPath(tag.fullpath);
+ if (file.isDirectory())
+ size += getDirectorySize(file);
+ else
+ size += file.fileSize;
+ }
+ return size;
+ });
+
+ this.__defineGetter__("pluginLibraries", function() {
+ let libs = [];
+ for (let tag of aTags)
+ libs.push(tag.filename);
+ return libs;
+ });
+
+ this.__defineGetter__("pluginFullpath", function() {
+ let paths = [];
+ for (let tag of aTags)
+ paths.push(tag.fullpath);
+ return paths;
+ })
+
+ this.__defineGetter__("pluginMimeTypes", function() {
+ let types = [];
+ for (let tag of aTags) {
+ let mimeTypes = tag.getMimeTypes({});
+ let mimeDescriptions = tag.getMimeDescriptions({});
+ let extensions = tag.getExtensions({});
+ for (let i = 0; i < mimeTypes.length; i++) {
+ let type = {};
+ type.type = mimeTypes[i];
+ type.description = mimeDescriptions[i];
+ type.suffixes = extensions[i];
+
+ types.push(type);
+ }
+ }
+ return types;
+ });
+
+ this.__defineGetter__("installDate", function() {
+ let date = 0;
+ for (let tag of aTags) {
+ date = Math.max(date, tag.lastModifiedTime);
+ }
+ return new Date(date);
+ });
+
+ this.__defineGetter__("scope", function() {
+ let path = aTags[0].fullpath;
+ // Plugins inside the application directory are in the application scope
+ let dir = Services.dirsvc.get("APlugns", Ci.nsIFile);
+ if (path.startsWith(dir.path))
+ return AddonManager.SCOPE_APPLICATION;
+
+ // Plugins inside the profile directory are in the profile scope
+ dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ if (path.startsWith(dir.path))
+ return AddonManager.SCOPE_PROFILE;
+
+ // Plugins anywhere else in the user's home are in the user scope,
+ // but not all platforms have a home directory.
+ try {
+ dir = Services.dirsvc.get("Home", Ci.nsIFile);
+ if (path.startsWith(dir.path))
+ return AddonManager.SCOPE_USER;
+ } catch (e if (e.result && e.result == Components.results.NS_ERROR_FAILURE)) {
+ // Do nothing: missing "Home".
+ }
+
+ // Any other locations are system scope
+ return AddonManager.SCOPE_SYSTEM;
+ });
+
+ this.__defineGetter__("pendingOperations", function() {
+ return AddonManager.PENDING_NONE;
+ });
+
+ this.__defineGetter__("operationsRequiringRestart", function() {
+ return AddonManager.OP_NEEDS_RESTART_NONE;
+ });
+
+ this.__defineGetter__("permissions", function() {
+ let permissions = 0;
+ if (aTags[0].isEnabledStateLocked) {
+ return permissions;
+ }
+ if (!this.appDisabled) {
+
+ if (this.userDisabled !== true)
+ permissions |= AddonManager.PERM_CAN_DISABLE;
+
+ let blocklistState = this.blocklistState;
+ let isCTPBlocklisted =
+ (blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE ||
+ blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+
+ if (this.userDisabled !== AddonManager.STATE_ASK_TO_ACTIVATE &&
+ (Services.prefs.getBoolPref("plugins.click_to_play") ||
+ isCTPBlocklisted)) {
+ permissions |= AddonManager.PERM_CAN_ASK_TO_ACTIVATE;
+ }
+
+ if (this.userDisabled !== false && !isCTPBlocklisted) {
+ permissions |= AddonManager.PERM_CAN_ENABLE;
+ }
+ }
+ return permissions;
+ });
+
+ this.__defineGetter__("optionsType", function() {
+ if (canDisableFlashProtectedMode(this)) {
+ return AddonManager.OPTIONS_TYPE_INLINE;
+ }
+ return AddonManager.OPTIONS_TYPE_INLINE_INFO;
+ });
+}
+
+PluginWrapper.prototype = {
+ optionsURL: "chrome://mozapps/content/extensions/pluginPrefs.xul",
+
+ get updateDate() {
+ return this.installDate;
+ },
+
+ get isCompatible() {
+ return true;
+ },
+
+ get isPlatformCompatible() {
+ return true;
+ },
+
+ get providesUpdatesSecurely() {
+ return true;
+ },
+
+ get foreignInstall() {
+ return true;
+ },
+
+ isCompatibleWith: function(aAppVerison, aPlatformVersion) {
+ return true;
+ },
+
+ findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ if ("onNoCompatibilityUpdateAvailable" in aListener)
+ aListener.onNoCompatibilityUpdateAvailable(this);
+ if ("onNoUpdateAvailable" in aListener)
+ aListener.onNoUpdateAvailable(this);
+ if ("onUpdateFinished" in aListener)
+ aListener.onUpdateFinished(this);
+ }
+};
+
+AddonManagerPrivate.registerProvider(PluginProvider, [
+ new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 6000,
+ AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE)
+]);
diff --git a/toolkit/mozapps/extensions/internal/SpellCheckDictionaryBootstrap.js b/toolkit/mozapps/extensions/internal/SpellCheckDictionaryBootstrap.js
new file mode 100644
index 000000000..f4f557fc2
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/SpellCheckDictionaryBootstrap.js
@@ -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/. */
+
+var hunspell, dir;
+
+function startup(data) {
+ hunspell = Components.classes["@mozilla.org/spellchecker/engine;1"]
+ .getService(Components.interfaces.mozISpellCheckingEngine);
+ dir = data.installPath.clone();
+ dir.append("dictionaries");
+ hunspell.addDirectory(dir);
+}
+
+function shutdown() {
+ hunspell.removeDirectory(dir);
+}
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
new file mode 100644
index 000000000..975448fcc
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -0,0 +1,7875 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = ["XPIProvider"];
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://gre/modules/Preferences.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
+ "resource://gre/modules/addons/AddonRepository.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ChromeManifestParser",
+ "resource://gre/modules/ChromeManifestParser.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
+ "resource://gre/modules/LightweightThemeManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils",
+ "resource://gre/modules/ZipUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
+ "resource://gre/modules/PermissionsUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
+ "resource://gre/modules/devtools/ToolboxProcess.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
+ "resource://gre/modules/devtools/Console.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "Blocklist",
+ "@mozilla.org/extensions/blocklist;1",
+ Ci.nsIBlocklistService);
+XPCOMUtils.defineLazyServiceGetter(this,
+ "ChromeRegistry",
+ "@mozilla.org/chrome/chrome-registry;1",
+ "nsIChromeRegistry");
+XPCOMUtils.defineLazyServiceGetter(this,
+ "ResProtocolHandler",
+ "@mozilla.org/network/protocol;1?name=resource",
+ "nsIResProtocolHandler");
+
+const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
+ "initWithPath");
+
+const PREF_DB_SCHEMA = "extensions.databaseSchema";
+const PREF_INSTALL_CACHE = "extensions.installCache";
+const PREF_XPI_STATE = "extensions.xpiState";
+const PREF_BOOTSTRAP_ADDONS = "extensions.bootstrappedAddons";
+const PREF_PENDING_OPERATIONS = "extensions.pendingOperations";
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_EM_DSS_ENABLED = "extensions.dss.enabled";
+const PREF_DSS_SWITCHPENDING = "extensions.dss.switchPending";
+const PREF_DSS_SKIN_TO_SELECT = "extensions.lastSelectedSkin";
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+const PREF_EM_UPDATE_URL = "extensions.update.url";
+const PREF_EM_UPDATE_BACKGROUND_URL = "extensions.update.background.url";
+const PREF_EM_ENABLED_ADDONS = "extensions.enabledAddons";
+const PREF_EM_EXTENSION_FORMAT = "extensions.";
+const PREF_EM_ENABLED_SCOPES = "extensions.enabledScopes";
+const PREF_EM_AUTO_DISABLED_SCOPES = "extensions.autoDisableScopes";
+const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI";
+const PREF_XPI_ENABLED = "xpinstall.enabled";
+const PREF_XPI_WHITELIST_REQUIRED = "xpinstall.whitelist.required";
+const PREF_XPI_DIRECT_WHITELISTED = "xpinstall.whitelist.directRequest";
+const PREF_XPI_FILE_WHITELISTED = "xpinstall.whitelist.fileRequest";
+const PREF_XPI_PERMISSIONS_BRANCH = "xpinstall.";
+const PREF_XPI_UNPACK = "extensions.alwaysUnpack";
+const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
+const PREF_INSTALL_REQUIRESECUREORIGIN = "extensions.install.requireSecureOrigin";
+const PREF_INSTALL_DISTRO_ADDONS = "extensions.installDistroAddons";
+const PREF_BRANCH_INSTALLED_ADDON = "extensions.installedDistroAddon.";
+const PREF_SHOWN_SELECTION_UI = "extensions.shownSelectionUI";
+const PREF_INTERPOSITION_ENABLED = "extensions.interposition.enabled";
+
+const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
+const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
+
+const PREF_CHECKCOMAT_THEMEOVERRIDE = "extensions.checkCompatibility.temporaryThemeOverride_minAppVersion";
+
+const URI_EXTENSION_SELECT_DIALOG = "chrome://mozapps/content/extensions/selectAddons.xul";
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
+
+const STRING_TYPE_NAME = "type.%ID%.name";
+
+const DIR_EXTENSIONS = "extensions";
+const DIR_STAGE = "staged";
+const DIR_XPI_STAGE = "staged-xpis";
+const DIR_TRASH = "trash";
+
+const FILE_DATABASE = "extensions.json";
+const FILE_OLD_CACHE = "extensions.cache";
+const FILE_INSTALL_MANIFEST = "install.rdf";
+const FILE_WEBEXT_MANIFEST = "manifest.json";
+const FILE_XPI_ADDONS_LIST = "extensions.ini";
+
+const KEY_PROFILEDIR = "ProfD";
+const KEY_APPDIR = "XCurProcD";
+const KEY_TEMPDIR = "TmpD";
+const KEY_APP_DISTRIBUTION = "XREAppDist";
+
+const KEY_APP_PROFILE = "app-profile";
+const KEY_APP_GLOBAL = "app-global";
+const KEY_APP_SYSTEM_LOCAL = "app-system-local";
+const KEY_APP_SYSTEM_SHARE = "app-system-share";
+const KEY_APP_SYSTEM_USER = "app-system-user";
+
+const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
+const XPI_PERMISSION = "install";
+
+const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";
+const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
+
+const TOOLKIT_ID = "toolkit@mozilla.org";
+#ifdef MOZ_PHOENIX_EXTENSIONS
+const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
+const FIREFOX_APPCOMPATVERSION = "27.9"
+#endif
+
+// The value for this is in Makefile.in
+#expand const DB_SCHEMA = __MOZ_EXTENSIONS_DB_SCHEMA__;
+const NOTIFICATION_TOOLBOXPROCESS_LOADED = "ToolboxProcessLoaded";
+
+// Properties that exist in the install manifest
+const PROP_METADATA = ["id", "version", "type", "internalName", "updateURL",
+ "updateKey", "optionsURL", "optionsType", "aboutURL",
+ "iconURL", "icon64URL"];
+const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
+const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"];
+const PROP_TARGETAPP = ["id", "minVersion", "maxVersion"];
+
+// Properties that should be migrated where possible from an old database. These
+// shouldn't include properties that can be read directly from install.rdf files
+// or calculated
+const DB_MIGRATE_METADATA= ["installDate", "userDisabled", "softDisabled",
+ "sourceURI", "applyBackgroundUpdates",
+ "releaseNotesURI", "foreignInstall", "syncGUID"];
+// Properties to cache and reload when an addon installation is pending
+const PENDING_INSTALL_METADATA =
+ ["syncGUID", "targetApplications", "userDisabled", "softDisabled",
+ "existingAddonID", "sourceURI", "releaseNotesURI", "installDate",
+ "updateDate", "applyBackgroundUpdates", "compatibilityOverrides"];
+
+// Note: When adding/changing/removing items here, remember to change the
+// DB schema version to ensure changes are picked up ASAP.
+const STATIC_BLOCKLIST_PATTERNS = [
+ { creator: "Mozilla Corp.",
+ level: Blocklist.STATE_BLOCKED,
+ blockID: "i162" },
+ { creator: "Mozilla.org",
+ level: Blocklist.STATE_BLOCKED,
+ blockID: "i162" }
+];
+
+
+const BOOTSTRAP_REASONS = {
+ APP_STARTUP : 1,
+ APP_SHUTDOWN : 2,
+ ADDON_ENABLE : 3,
+ ADDON_DISABLE : 4,
+ ADDON_INSTALL : 5,
+ ADDON_UNINSTALL : 6,
+ ADDON_UPGRADE : 7,
+ ADDON_DOWNGRADE : 8
+};
+
+// Map new string type identifiers to old style nsIUpdateItem types
+const TYPES = {
+ extension: 2,
+ theme: 4,
+ locale: 8,
+ multipackage: 32,
+ dictionary: 64,
+ experiment: 128,
+};
+
+const RESTARTLESS_TYPES = new Set([
+ "dictionary",
+ "experiment",
+ "locale",
+]);
+
+// Keep track of where we are in startup for telemetry
+// event happened during XPIDatabase.startup()
+const XPI_STARTING = "XPIStarting";
+// event happened after startup() but before the final-ui-startup event
+const XPI_BEFORE_UI_STARTUP = "BeforeFinalUIStartup";
+// event happened after final-ui-startup
+const XPI_AFTER_UI_STARTUP = "AfterFinalUIStartup";
+
+const COMPATIBLE_BY_DEFAULT_TYPES = {
+ extension: true,
+ dictionary: true
+};
+
+const MSG_JAR_FLUSH = "AddonJarFlush";
+
+var gGlobalScope = this;
+
+/**
+ * Valid IDs fit this pattern.
+ */
+var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i;
+
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.xpi";
+
+// Create a new logger for use by all objects in this Addons XPI Provider module
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+const LAZY_OBJECTS = ["XPIDatabase"];
+
+var gLazyObjectsLoaded = false;
+
+function loadLazyObjects() {
+ let scope = {};
+ scope.AddonInternal = AddonInternal;
+ scope.XPIProvider = XPIProvider;
+ scope.XPIStates = XPIStates;
+ Services.scriptloader.loadSubScript("resource://gre/modules/addons/XPIProviderUtils.js",
+ scope);
+
+ for (let name of LAZY_OBJECTS) {
+ delete gGlobalScope[name];
+ gGlobalScope[name] = scope[name];
+ }
+ gLazyObjectsLoaded = true;
+ return scope;
+}
+
+for (let name of LAZY_OBJECTS) {
+ Object.defineProperty(gGlobalScope, name, {
+ get: function lazyObjectGetter() {
+ let objs = loadLazyObjects();
+ return objs[name];
+ },
+ configurable: true
+ });
+}
+
+
+function findMatchingStaticBlocklistItem(aAddon) {
+ for (let item of STATIC_BLOCKLIST_PATTERNS) {
+ if ("creator" in item && typeof item.creator == "string") {
+ if ((aAddon.defaultLocale && aAddon.defaultLocale.creator == item.creator) ||
+ (aAddon.selectedLocale && aAddon.selectedLocale.creator == item.creator)) {
+ return item;
+ }
+ }
+ }
+ return null;
+}
+
+
+/**
+ * Sets permissions on a file
+ *
+ * @param aFile
+ * The file or directory to operate on.
+ * @param aPermissions
+ * The permisions to set
+ */
+function setFilePermissions(aFile, aPermissions) {
+ try {
+ aFile.permissions = aPermissions;
+ }
+ catch (e) {
+ logger.warn("Failed to set permissions " + aPermissions.toString(8) + " on " +
+ aFile.path, e);
+ }
+}
+
+/**
+ * A safe way to install a file or the contents of a directory to a new
+ * directory. The file or directory is moved or copied recursively and if
+ * anything fails an attempt is made to rollback the entire operation. The
+ * operation may also be rolled back to its original state after it has
+ * completed by calling the rollback method.
+ *
+ * Operations can be chained. Calling move or copy multiple times will remember
+ * the whole set and if one fails all of the operations will be rolled back.
+ */
+function SafeInstallOperation() {
+ this._installedFiles = [];
+ this._createdDirs = [];
+}
+
+SafeInstallOperation.prototype = {
+ _installedFiles: null,
+ _createdDirs: null,
+
+ _installFile: function SIO_installFile(aFile, aTargetDirectory, aCopy) {
+ let oldFile = aCopy ? null : aFile.clone();
+ let newFile = aFile.clone();
+ try {
+ if (aCopy)
+ newFile.copyTo(aTargetDirectory, null);
+ else
+ newFile.moveTo(aTargetDirectory, null);
+ }
+ catch (e) {
+ logger.error("Failed to " + (aCopy ? "copy" : "move") + " file " + aFile.path +
+ " to " + aTargetDirectory.path, e);
+ throw e;
+ }
+ this._installedFiles.push({ oldFile: oldFile, newFile: newFile });
+ },
+
+ _installDirectory: function SIO_installDirectory(aDirectory, aTargetDirectory, aCopy) {
+ let newDir = aTargetDirectory.clone();
+ newDir.append(aDirectory.leafName);
+ try {
+ newDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ }
+ catch (e) {
+ logger.error("Failed to create directory " + newDir.path, e);
+ throw e;
+ }
+ this._createdDirs.push(newDir);
+
+ // Use a snapshot of the directory contents to avoid possible issues with
+ // iterating over a directory while removing files from it (the YAFFS2
+ // embedded filesystem has this issue, see bug 772238), and to remove
+ // normal files before their resource forks on OSX (see bug 733436).
+ let entries = getDirectoryEntries(aDirectory, true);
+ entries.forEach(function(aEntry) {
+ try {
+ this._installDirEntry(aEntry, newDir, aCopy);
+ }
+ catch (e) {
+ logger.error("Failed to " + (aCopy ? "copy" : "move") + " entry " +
+ aEntry.path, e);
+ throw e;
+ }
+ }, this);
+
+ // If this is only a copy operation then there is nothing else to do
+ if (aCopy)
+ return;
+
+ // The directory should be empty by this point. If it isn't this will throw
+ // and all of the operations will be rolled back
+ try {
+ setFilePermissions(aDirectory, FileUtils.PERMS_DIRECTORY);
+ aDirectory.remove(false);
+ }
+ catch (e) {
+ logger.error("Failed to remove directory " + aDirectory.path, e);
+ throw e;
+ }
+
+ // Note we put the directory move in after all the file moves so the
+ // directory is recreated before all the files are moved back
+ this._installedFiles.push({ oldFile: aDirectory, newFile: newDir });
+ },
+
+ _installDirEntry: function SIO_installDirEntry(aDirEntry, aTargetDirectory, aCopy) {
+ let isDir = null;
+
+ try {
+ isDir = aDirEntry.isDirectory();
+ }
+ catch (e) {
+ // If the file has already gone away then don't worry about it, this can
+ // happen on OSX where the resource fork is automatically moved with the
+ // data fork for the file. See bug 733436.
+ if (e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
+ return;
+
+ logger.error("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
+ " to " + aTargetDirectory.path);
+ throw e;
+ }
+
+ try {
+ if (isDir)
+ this._installDirectory(aDirEntry, aTargetDirectory, aCopy);
+ else
+ this._installFile(aDirEntry, aTargetDirectory, aCopy);
+ }
+ catch (e) {
+ logger.error("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
+ " to " + aTargetDirectory.path);
+ throw e;
+ }
+ },
+
+ /**
+ * Moves a file or directory into a new directory. If an error occurs then all
+ * files that have been moved will be moved back to their original location.
+ *
+ * @param aFile
+ * The file or directory to be moved.
+ * @param aTargetDirectory
+ * The directory to move into, this is expected to be an empty
+ * directory.
+ */
+ moveUnder: function SIO_move(aFile, aTargetDirectory) {
+ try {
+ this._installDirEntry(aFile, aTargetDirectory, false);
+ }
+ catch (e) {
+ this.rollback();
+ throw e;
+ }
+ },
+
+ /**
+ * Renames a file to a new location. If an error occurs then all
+ * files that have been moved will be moved back to their original location.
+ *
+ * @param aOldLocation
+ * The old location of the file.
+ * @param aNewLocation
+ * The new location of the file.
+ */
+ moveTo: function(aOldLocation, aNewLocation) {
+ try {
+ let oldFile = aOldLocation.clone(), newFile = aNewLocation.clone();
+ oldFile.moveTo(newFile.parent, newFile.leafName);
+ this._installedFiles.push({ oldFile: oldFile, newFile: newFile, isMoveTo: true});
+ }
+ catch(e) {
+ this.rollback();
+ throw e;
+ }
+ },
+
+ /**
+ * Copies a file or directory into a new directory. If an error occurs then
+ * all new files that have been created will be removed.
+ *
+ * @param aFile
+ * The file or directory to be copied.
+ * @param aTargetDirectory
+ * The directory to copy into, this is expected to be an empty
+ * directory.
+ */
+ copy: function SIO_copy(aFile, aTargetDirectory) {
+ try {
+ this._installDirEntry(aFile, aTargetDirectory, true);
+ }
+ catch (e) {
+ this.rollback();
+ throw e;
+ }
+ },
+
+ /**
+ * Rolls back all the moves that this operation performed. If an exception
+ * occurs here then both old and new directories are left in an indeterminate
+ * state
+ */
+ rollback: function SIO_rollback() {
+ while (this._installedFiles.length > 0) {
+ let move = this._installedFiles.pop();
+ if (move.isMoveTo) {
+ move.newFile.moveTo(oldDir.parent, oldDir.leafName);
+ }
+ else if (move.newFile.isDirectory()) {
+ let oldDir = move.oldFile.parent.clone();
+ oldDir.append(move.oldFile.leafName);
+ oldDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ }
+ else if (!move.oldFile) {
+ // No old file means this was a copied file
+ move.newFile.remove(true);
+ }
+ else {
+ move.newFile.moveTo(move.oldFile.parent, null);
+ }
+ }
+
+ while (this._createdDirs.length > 0)
+ recursiveRemove(this._createdDirs.pop());
+ }
+};
+
+/**
+ * Gets the currently selected locale for display.
+ * @return the selected locale or "en-US" if none is selected
+ */
+function getLocale() {
+ if (Preferences.get(PREF_MATCH_OS_LOCALE, false))
+ return Services.locale.getLocaleComponentForUserAgent();
+ try {
+ let locale = Preferences.get(PREF_SELECTED_LOCALE, null, Ci.nsIPrefLocalizedString);
+ if (locale)
+ return locale;
+ }
+ catch (e) {}
+ return Preferences.get(PREF_SELECTED_LOCALE, "en-US");
+}
+
+/**
+ * Selects the closest matching locale from a list of locales.
+ *
+ * @param aLocales
+ * An array of locales
+ * @return the best match for the currently selected locale
+ */
+function findClosestLocale(aLocales) {
+ let appLocale = getLocale();
+
+ // Holds the best matching localized resource
+ var bestmatch = null;
+ // The number of locale parts it matched with
+ var bestmatchcount = 0;
+ // The number of locale parts in the match
+ var bestpartcount = 0;
+
+ var matchLocales = [appLocale.toLowerCase()];
+ /* If the current locale is English then it will find a match if there is
+ a valid match for en-US so no point searching that locale too. */
+ if (matchLocales[0].substring(0, 3) != "en-")
+ matchLocales.push("en-us");
+
+ for each (var locale in matchLocales) {
+ var lparts = locale.split("-");
+ for each (var localized in aLocales) {
+ for each (let found in localized.locales) {
+ found = found.toLowerCase();
+ // Exact match is returned immediately
+ if (locale == found)
+ return localized;
+
+ var fparts = found.split("-");
+ /* If we have found a possible match and this one isn't any longer
+ then we dont need to check further. */
+ if (bestmatch && fparts.length < bestmatchcount)
+ continue;
+
+ // Count the number of parts that match
+ var maxmatchcount = Math.min(fparts.length, lparts.length);
+ var matchcount = 0;
+ while (matchcount < maxmatchcount &&
+ fparts[matchcount] == lparts[matchcount])
+ matchcount++;
+
+ /* If we matched more than the last best match or matched the same and
+ this locale is less specific than the last best match. */
+ if (matchcount > bestmatchcount ||
+ (matchcount == bestmatchcount && fparts.length < bestpartcount)) {
+ bestmatch = localized;
+ bestmatchcount = matchcount;
+ bestpartcount = fparts.length;
+ }
+ }
+ }
+ // If we found a valid match for this locale return it
+ if (bestmatch)
+ return bestmatch;
+ }
+ return null;
+}
+
+/**
+ * Sets the userDisabled and softDisabled properties of an add-on based on what
+ * values those properties had for a previous instance of the add-on. The
+ * previous instance may be a previous install or in the case of an application
+ * version change the same add-on.
+ *
+ * NOTE: this may modify aNewAddon in place; callers should save the database if
+ * necessary
+ *
+ * @param aOldAddon
+ * The previous instance of the add-on
+ * @param aNewAddon
+ * The new instance of the add-on
+ * @param aAppVersion
+ * The optional application version to use when checking the blocklist
+ * or undefined to use the current application
+ * @param aPlatformVersion
+ * The optional platform version to use when checking the blocklist or
+ * undefined to use the current platform
+ */
+function applyBlocklistChanges(aOldAddon, aNewAddon, aOldAppVersion,
+ aOldPlatformVersion) {
+ // Copy the properties by default
+ aNewAddon.userDisabled = aOldAddon.userDisabled;
+ aNewAddon.softDisabled = aOldAddon.softDisabled;
+
+ let oldBlocklistState = Blocklist.getAddonBlocklistState(createWrapper(aOldAddon),
+ aOldAppVersion,
+ aOldPlatformVersion);
+ let newBlocklistState = Blocklist.getAddonBlocklistState(createWrapper(aNewAddon));
+
+ // If the blocklist state hasn't changed then the properties don't need to
+ // change
+ if (newBlocklistState == oldBlocklistState)
+ return;
+
+ if (newBlocklistState == Blocklist.STATE_SOFTBLOCKED) {
+ if (aNewAddon.type != "theme") {
+ // The add-on has become softblocked, set softDisabled if it isn't already
+ // userDisabled
+ aNewAddon.softDisabled = !aNewAddon.userDisabled;
+ }
+ else {
+ // Themes just get userDisabled to switch back to the default theme
+ aNewAddon.userDisabled = true;
+ }
+ }
+ else {
+ // If the new add-on is not softblocked then it cannot be softDisabled
+ aNewAddon.softDisabled = false;
+ }
+}
+
+/**
+ * Calculates whether an add-on should be appDisabled or not.
+ *
+ * @param aAddon
+ * The add-on to check
+ * @return true if the add-on should not be appDisabled
+ */
+function isUsableAddon(aAddon) {
+ // Hack to ensure the default theme is always usable
+ if (aAddon.type == "theme" && aAddon.internalName == XPIProvider.defaultSkin)
+ return true;
+
+ if (aAddon.jetsdk)
+ return false;
+
+ if (aAddon.blocklistState == Blocklist.STATE_BLOCKED)
+ return false;
+
+ if (AddonManager.checkUpdateSecurity && !aAddon.providesUpdatesSecurely)
+ return false;
+
+ if (!aAddon.isPlatformCompatible)
+ return false;
+
+ if (AddonManager.checkCompatibility) {
+ if (!aAddon.isCompatible)
+ return false;
+ }
+ else {
+ let app = aAddon.matchingTargetApplication;
+ if (!app)
+ return false;
+
+ // XXX Temporary solution to let applications opt-in to make themes safer
+ // following significant UI changes even if checkCompatibility=false has
+ // been set, until we get bug 962001.
+ if (aAddon.type == "theme" && app.id == Services.appinfo.ID) {
+ try {
+ let minCompatVersion = Services.prefs.getCharPref(PREF_CHECKCOMAT_THEMEOVERRIDE);
+ if (minCompatVersion &&
+ Services.vc.compare(minCompatVersion, app.maxVersion) > 0) {
+ return false;
+ }
+ } catch (e) {}
+ }
+ }
+
+ return true;
+}
+
+XPCOMUtils.defineLazyServiceGetter(this, "gRDF", "@mozilla.org/rdf/rdf-service;1",
+ Ci.nsIRDFService);
+
+function EM_R(aProperty) {
+ return gRDF.GetResource(PREFIX_NS_EM + aProperty);
+}
+
+function createAddonDetails(id, aAddon) {
+ return {
+ id: id || aAddon.id,
+ type: aAddon.type,
+ version: aAddon.version
+ };
+}
+
+/**
+ * Converts an RDF literal, resource or integer into a string.
+ *
+ * @param aLiteral
+ * The RDF object to convert
+ * @return a string if the object could be converted or null
+ */
+function getRDFValue(aLiteral) {
+ if (aLiteral instanceof Ci.nsIRDFLiteral)
+ return aLiteral.Value;
+ if (aLiteral instanceof Ci.nsIRDFResource)
+ return aLiteral.Value;
+ if (aLiteral instanceof Ci.nsIRDFInt)
+ return aLiteral.Value;
+ return null;
+}
+
+/**
+ * Gets an RDF property as a string
+ *
+ * @param aDs
+ * The RDF datasource to read the property from
+ * @param aResource
+ * The RDF resource to read the property from
+ * @param aProperty
+ * The property to read
+ * @return a string if the property existed or null
+ */
+function getRDFProperty(aDs, aResource, aProperty) {
+ return getRDFValue(aDs.GetTarget(aResource, EM_R(aProperty), true));
+}
+
+/**
+ * Reads an AddonInternal object from an RDF stream.
+ *
+ * @param aUri
+ * The URI that the manifest is being read from
+ * @param aStream
+ * An open stream to read the RDF from
+ * @return an AddonInternal object
+ * @throws if the install manifest in the RDF stream is corrupt or could not
+ * be read
+ */
+function loadManifestFromRDF(aUri, aStream) {
+ function getPropertyArray(aDs, aSource, aProperty) {
+ let values = [];
+ let targets = aDs.GetTargets(aSource, EM_R(aProperty), true);
+ while (targets.hasMoreElements())
+ values.push(getRDFValue(targets.getNext()));
+
+ return values;
+ }
+
+ /**
+ * Reads locale properties from either the main install manifest root or
+ * an em:localized section in the install manifest.
+ *
+ * @param aDs
+ * The nsIRDFDatasource to read from
+ * @param aSource
+ * The nsIRDFResource to read the properties from
+ * @param isDefault
+ * True if the locale is to be read from the main install manifest
+ * root
+ * @param aSeenLocales
+ * An array of locale names already seen for this install manifest.
+ * Any locale names seen as a part of this function will be added to
+ * this array
+ * @return an object containing the locale properties
+ */
+ function readLocale(aDs, aSource, isDefault, aSeenLocales) {
+ let locale = { };
+ if (!isDefault) {
+ locale.locales = [];
+ let targets = ds.GetTargets(aSource, EM_R("locale"), true);
+ while (targets.hasMoreElements()) {
+ let localeName = getRDFValue(targets.getNext());
+ if (!localeName) {
+ logger.warn("Ignoring empty locale in localized properties");
+ continue;
+ }
+ if (aSeenLocales.indexOf(localeName) != -1) {
+ logger.warn("Ignoring duplicate locale in localized properties");
+ continue;
+ }
+ aSeenLocales.push(localeName);
+ locale.locales.push(localeName);
+ }
+
+ if (locale.locales.length == 0) {
+ logger.warn("Ignoring localized properties with no listed locales");
+ return null;
+ }
+ }
+
+ PROP_LOCALE_SINGLE.forEach(function(aProp) {
+ locale[aProp] = getRDFProperty(aDs, aSource, aProp);
+ });
+
+ PROP_LOCALE_MULTI.forEach(function(aProp) {
+ // Don't store empty arrays
+ let props = getPropertyArray(aDs, aSource,
+ aProp.substring(0, aProp.length - 1));
+ if (props.length > 0)
+ locale[aProp] = props;
+ });
+
+ return locale;
+ }
+
+ let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
+ createInstance(Ci.nsIRDFXMLParser)
+ let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
+ createInstance(Ci.nsIRDFDataSource);
+ let listener = rdfParser.parseAsync(ds, aUri);
+ let channel = Cc["@mozilla.org/network/input-stream-channel;1"].
+ createInstance(Ci.nsIInputStreamChannel);
+ channel.setURI(aUri);
+ channel.contentStream = aStream;
+ channel.QueryInterface(Ci.nsIChannel);
+ channel.contentType = "text/xml";
+
+ listener.onStartRequest(channel, null);
+
+ try {
+ let pos = 0;
+ let count = aStream.available();
+ while (count > 0) {
+ listener.onDataAvailable(channel, null, aStream, pos, count);
+ pos += count;
+ count = aStream.available();
+ }
+ listener.onStopRequest(channel, null, Components.results.NS_OK);
+ }
+ catch (e) {
+ listener.onStopRequest(channel, null, e.result);
+ throw e;
+ }
+
+ let root = gRDF.GetResource(RDFURI_INSTALL_MANIFEST_ROOT);
+ let addon = new AddonInternal();
+ PROP_METADATA.forEach(function(aProp) {
+ addon[aProp] = getRDFProperty(ds, root, aProp);
+ });
+ addon.unpack = getRDFProperty(ds, root, "unpack") == "true";
+
+ if (!addon.type) {
+ addon.type = addon.internalName ? "theme" : "extension";
+ }
+ else {
+ let type = addon.type;
+ addon.type = null;
+ for (let name in TYPES) {
+ if (TYPES[name] == type) {
+ addon.type = name;
+ break;
+ }
+ }
+ }
+
+ if (!(addon.type in TYPES))
+ throw new Error("Install manifest specifies unknown type: " + addon.type);
+
+ if (addon.type != "multipackage") {
+ if (!addon.id)
+ throw new Error("No ID in install manifest");
+ if (!gIDTest.test(addon.id))
+ throw new Error("Illegal add-on ID " + addon.id);
+ if (!addon.version)
+ throw new Error("No version in install manifest");
+ }
+
+ addon.strictCompatibility = !(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||
+ getRDFProperty(ds, root, "strictCompatibility") == "true";
+
+ // Only read these properties for extensions.
+ if (addon.type == "extension") {
+ addon.bootstrap = getRDFProperty(ds, root, "bootstrap") == "true";
+ addon.multiprocessCompatible = getRDFProperty(ds, root, "multiprocessCompatible") == "true";
+ if (addon.optionsType &&
+ addon.optionsType != AddonManager.OPTIONS_TYPE_DIALOG &&
+ addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE &&
+ addon.optionsType != AddonManager.OPTIONS_TYPE_TAB &&
+ addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_INFO) {
+ throw new Error("Install manifest specifies unknown type: " + addon.optionsType);
+ }
+ }
+ else {
+ // Some add-on types are always restartless.
+ if (RESTARTLESS_TYPES.has(addon.type)) {
+ addon.bootstrap = true;
+ }
+
+ // Only extensions are allowed to provide an optionsURL, optionsType or aboutURL. For
+ // all other types they are silently ignored
+ addon.optionsURL = null;
+ addon.optionsType = null;
+ addon.aboutURL = null;
+
+ if (addon.type == "theme") {
+ if (!addon.internalName)
+ throw new Error("Themes must include an internalName property");
+ addon.skinnable = getRDFProperty(ds, root, "skinnable") == "true";
+ }
+ }
+
+ addon.defaultLocale = readLocale(ds, root, true);
+
+ let seenLocales = [];
+ addon.locales = [];
+ let targets = ds.GetTargets(root, EM_R("localized"), true);
+ while (targets.hasMoreElements()) {
+ let target = targets.getNext().QueryInterface(Ci.nsIRDFResource);
+ let locale = readLocale(ds, target, false, seenLocales);
+ if (locale)
+ addon.locales.push(locale);
+ }
+
+ let seenApplications = [];
+ addon.targetApplications = [];
+ targets = ds.GetTargets(root, EM_R("targetApplication"), true);
+ while (targets.hasMoreElements()) {
+ let target = targets.getNext().QueryInterface(Ci.nsIRDFResource);
+ let targetAppInfo = {};
+ PROP_TARGETAPP.forEach(function(aProp) {
+ targetAppInfo[aProp] = getRDFProperty(ds, target, aProp);
+ });
+ if (!targetAppInfo.id || !targetAppInfo.minVersion ||
+ !targetAppInfo.maxVersion) {
+ logger.warn("Ignoring invalid targetApplication entry in install manifest");
+ continue;
+ }
+ if (seenApplications.indexOf(targetAppInfo.id) != -1) {
+ logger.warn("Ignoring duplicate targetApplication entry for " + targetAppInfo.id +
+ " in install manifest");
+ continue;
+ }
+ seenApplications.push(targetAppInfo.id);
+ addon.targetApplications.push(targetAppInfo);
+ }
+
+ // Note that we don't need to check for duplicate targetPlatform entries since
+ // the RDF service coalesces them for us.
+ let targetPlatforms = getPropertyArray(ds, root, "targetPlatform");
+ addon.targetPlatforms = [];
+ targetPlatforms.forEach(function(aPlatform) {
+ let platform = {
+ os: null,
+ abi: null
+ };
+
+ let pos = aPlatform.indexOf("_");
+ if (pos != -1) {
+ platform.os = aPlatform.substring(0, pos);
+ platform.abi = aPlatform.substring(pos + 1);
+ }
+ else {
+ platform.os = aPlatform;
+ }
+
+ addon.targetPlatforms.push(platform);
+ });
+
+ // A theme's userDisabled value is true if the theme is not the selected skin
+ // or if there is an active lightweight theme. We ignore whether softblocking
+ // is in effect since it would change the active theme.
+ if (addon.type == "theme") {
+ addon.userDisabled = !!LightweightThemeManager.currentTheme ||
+ addon.internalName != XPIProvider.selectedSkin;
+ }
+ // Experiments are disabled by default. It is up to the Experiments Manager
+ // to enable them (it drives installation).
+ else if (addon.type == "experiment") {
+ addon.userDisabled = true;
+ }
+ else {
+ addon.userDisabled = false;
+ addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;
+ }
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+
+ // Experiments are managed and updated through an external "experiments
+ // manager." So disable some built-in mechanisms.
+ if (addon.type == "experiment") {
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ addon.updateURL = null;
+ addon.updateKey = null;
+
+ addon.targetApplications = [];
+ addon.targetPlatforms = [];
+ }
+
+ // Load the storage service before NSS (nsIRandomGenerator),
+ // to avoid a SQLite initialization error (bug 717904).
+ let storage = Services.storage;
+
+ // Generate random GUID used for Sync.
+ // This was lifted from util.js:makeGUID() from services-sync.
+ let guid = Cc["@mozilla.org/uuid-generator;1"]
+ .getService(Ci.nsIUUIDGenerator)
+ .generateUUID().toString();
+ addon.syncGUID = guid;
+
+ return addon;
+}
+
+/**
+ * Loads an AddonInternal object from an add-on extracted in a directory.
+ *
+ * @param aDir
+ * The nsIFile directory holding the add-on
+ * @return an AddonInternal object
+ * @throws if the directory does not contain a valid install manifest
+ */
+function loadManifestFromDir(aDir) {
+ function getFileSize(aFile) {
+ if (aFile.isSymlink())
+ return 0;
+
+ if (!aFile.isDirectory())
+ return aFile.fileSize;
+
+ let size = 0;
+ let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
+ let entry;
+ while ((entry = entries.nextFile))
+ size += getFileSize(entry);
+ entries.close();
+ return size;
+ }
+
+ let file = aDir.clone();
+ file.append(FILE_INSTALL_MANIFEST);
+ if (!file.exists() || !file.isFile())
+ throw new Error("Directory " + aDir.path + " does not contain a valid " +
+ "install manifest");
+
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fis.init(file, -1, -1, false);
+ let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
+ createInstance(Ci.nsIBufferedInputStream);
+ bis.init(fis, 4096);
+
+ try {
+ let addon = loadManifestFromRDF(Services.io.newFileURI(file), bis);
+ addon._sourceBundle = aDir.clone();
+ addon.size = getFileSize(aDir);
+
+ file = aDir.clone();
+ file.append("chrome.manifest");
+ let chromeManifest = ChromeManifestParser.parseSync(Services.io.newFileURI(file));
+ addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
+ "binary-component");
+
+ addon.appDisabled = !isUsableAddon(addon);
+ return addon;
+ }
+ finally {
+ bis.close();
+ fis.close();
+ }
+}
+
+/**
+ * Loads an AddonInternal object from an nsIZipReader for an add-on.
+ *
+ * @param aZipReader
+ * An open nsIZipReader for the add-on's files
+ * @return an AddonInternal object
+ * @throws if the XPI file does not contain a valid install manifest.
+ * Throws with |webext:true| if a WebExtension manifest was found
+ * to distinguish between WebExtensions and corrupt files.
+ */
+function loadManifestFromZipReader(aZipReader) {
+ let zis;
+ try {
+ zis = aZipReader.getInputStream(FILE_INSTALL_MANIFEST);
+ } catch (e) {
+ // We're going to throw here, but depending on whether we have a
+ // WebExtension manifest in the XPI, we'll throw with the webext flag.
+ try {
+ let zws = aZipReader.getInputStream(FILE_WEBEXT_MANIFEST);
+ zws.close();
+ } catch(e2) {
+ // We have neither an install manifest nor a WebExtension manifest;
+ // this means the extension file has a structural problem.
+ // Just pass the original error up the chain in that case.
+ throw {
+ name: e.name,
+ message: e.message
+ };
+ }
+ // If we get here, we have a WebExtension manifest but no install
+ // manifest. Pass the error up the chain with the webext flag.
+ throw {
+ name: e.name,
+ message: e.message,
+ webext: true
+ };
+ }
+
+ // We found an install manifest, so it's either a regular or hybrid
+ // extension. Continue processing.
+ let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
+ createInstance(Ci.nsIBufferedInputStream);
+ bis.init(zis, 4096);
+
+ try {
+ let uri = buildJarURI(aZipReader.file, FILE_INSTALL_MANIFEST);
+ let addon = loadManifestFromRDF(uri, bis);
+ addon._sourceBundle = aZipReader.file;
+
+ addon.size = 0;
+ let entries = aZipReader.findEntries(null);
+ while (entries.hasMore())
+ addon.size += aZipReader.getEntry(entries.getNext()).realSize;
+
+ // Binary components can only be loaded from unpacked addons.
+ if (addon.unpack) {
+ uri = buildJarURI(aZipReader.file, "chrome.manifest");
+ let chromeManifest = ChromeManifestParser.parseSync(uri);
+ addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
+ "binary-component");
+ } else {
+ addon.hasBinaryComponents = false;
+ }
+
+ // Set a boolean value whether the .xpi archive contains file related to old
+ // Mozilla Add-on SDK or contains file related to PMkit (or new Mozilla SDK),
+ // but extension is not directly targeting Pale Moon
+ if (aZipReader.hasEntry("harness-options.json")) {
+ addon.jetsdk = true;
+ } else if (aZipReader.hasEntry("package.json")) {
+ let app = addon.matchingTargetApplication;
+ if (app && app.id == Services.appinfo.ID) {
+ addon.jetsdk = false;
+ } else {
+ addon.jetsdk = true;
+ }
+ } else {
+ addon.jetsdk = false;
+ }
+
+ addon.appDisabled = !isUsableAddon(addon);
+ return addon;
+ }
+ finally {
+ bis.close();
+ zis.close();
+ }
+}
+
+/**
+ * Loads an AddonInternal object from an add-on in an XPI file.
+ *
+ * @param aXPIFile
+ * An nsIFile pointing to the add-on's XPI file
+ * @return an AddonInternal object
+ * @throws if the XPI file does not contain a valid install manifest
+ */
+function loadManifestFromZipFile(aXPIFile) {
+ let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipReader.open(aXPIFile);
+
+ return loadManifestFromZipReader(zipReader);
+ }
+ finally {
+ zipReader.close();
+ }
+}
+
+function loadManifestFromFile(aFile) {
+ if (aFile.isFile())
+ return loadManifestFromZipFile(aFile);
+ else
+ return loadManifestFromDir(aFile);
+}
+
+/**
+ * Gets an nsIURI for a file within another file, either a directory or an XPI
+ * file. If aFile is a directory then this will return a file: URI, if it is an
+ * XPI file then it will return a jar: URI.
+ *
+ * @param aFile
+ * The file containing the resources, must be either a directory or an
+ * XPI file
+ * @param aPath
+ * The path to find the resource at, "/" separated. If aPath is empty
+ * then the uri to the root of the contained files will be returned
+ * @return an nsIURI pointing at the resource
+ */
+function getURIForResourceInFile(aFile, aPath) {
+ if (aFile.isDirectory()) {
+ let resource = aFile.clone();
+ if (aPath) {
+ aPath.split("/").forEach(function(aPart) {
+ resource.append(aPart);
+ });
+ }
+ return NetUtil.newURI(resource);
+ }
+
+ return buildJarURI(aFile, aPath);
+}
+
+/**
+ * Creates a jar: URI for a file inside a ZIP file.
+ *
+ * @param aJarfile
+ * The ZIP file as an nsIFile
+ * @param aPath
+ * The path inside the ZIP file
+ * @return an nsIURI for the file
+ */
+function buildJarURI(aJarfile, aPath) {
+ let uri = Services.io.newFileURI(aJarfile);
+ uri = "jar:" + uri.spec + "!/" + aPath;
+ return NetUtil.newURI(uri);
+}
+
+/**
+ * Sends local and remote notifications to flush a JAR file cache entry
+ *
+ * @param aJarFile
+ * The ZIP/XPI/JAR file as a nsIFile
+ */
+function flushJarCache(aJarFile) {
+ Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null);
+ Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster)
+ .broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
+}
+
+function flushStartupCache() {
+ // Init this, so it will get the notification.
+ Services.obs.notifyObservers(null, "startupcache-invalidate", null);
+}
+
+/**
+ * Creates and returns a new unique temporary file. The caller should delete
+ * the file when it is no longer needed.
+ *
+ * @return an nsIFile that points to a randomly named, initially empty file in
+ * the OS temporary files directory
+ */
+function getTemporaryFile() {
+ let file = FileUtils.getDir(KEY_TEMPDIR, []);
+ let random = Math.random().toString(36).replace(/0./, '').substr(-3);
+ file.append("tmp-" + random + ".xpi");
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+
+ return file;
+}
+
+/**
+ * Verifies that a zip file's contents are all signed by the same principal.
+ * Directory entries and anything in the META-INF directory are not checked.
+ *
+ * @param aZip
+ * A nsIZipReader to check
+ * @param aCertificate
+ * The nsIX509Cert to compare against
+ * @return true if all the contents that should be signed were signed by the
+ * principal
+ */
+function verifyZipSigning(aZip, aCertificate) {
+ var count = 0;
+ var entries = aZip.findEntries(null);
+ while (entries.hasMore()) {
+ var entry = entries.getNext();
+ // Nothing in META-INF is in the manifest.
+ if (entry.substr(0, 9) == "META-INF/")
+ continue;
+ // Directory entries aren't in the manifest.
+ if (entry.substr(-1) == "/")
+ continue;
+ count++;
+ var entryCertificate = aZip.getSigningCert(entry);
+ if (!entryCertificate || !aCertificate.equals(entryCertificate)) {
+ return false;
+ }
+ }
+ return aZip.manifestEntriesCount == count;
+}
+
+/**
+ * Replaces %...% strings in an addon url (update and updateInfo) with
+ * appropriate values.
+ *
+ * @param aAddon
+ * The AddonInternal representing the add-on
+ * @param aUri
+ * The uri to escape
+ * @param aUpdateType
+ * An optional number representing the type of update, only applicable
+ * when creating a url for retrieving an update manifest
+ * @param aAppVersion
+ * The optional application version to use for %APP_VERSION%
+ * @return the appropriately escaped uri.
+ */
+function escapeAddonURI(aAddon, aUri, aUpdateType, aAppVersion)
+{
+ let uri = AddonManager.escapeAddonURI(aAddon, aUri, aAppVersion);
+
+ // If there is an updateType then replace the UPDATE_TYPE string
+ if (aUpdateType)
+ uri = uri.replace(/%UPDATE_TYPE%/g, aUpdateType);
+
+ // If this add-on has compatibility information for either the current
+ // application or toolkit then replace the ITEM_MAXAPPVERSION with the
+ // maxVersion
+ let app = aAddon.matchingTargetApplication;
+ if (app)
+ var maxVersion = app.maxVersion;
+ else
+ maxVersion = "";
+ uri = uri.replace(/%ITEM_MAXAPPVERSION%/g, maxVersion);
+
+ let compatMode = "normal";
+ if (!AddonManager.checkCompatibility)
+ compatMode = "ignore";
+ else if (AddonManager.strictCompatibility)
+ compatMode = "strict";
+ uri = uri.replace(/%COMPATIBILITY_MODE%/g, compatMode);
+
+ return uri;
+}
+
+function removeAsync(aFile) {
+ return Task.spawn(function () {
+ let info = null;
+ try {
+ info = yield OS.File.stat(aFile.path);
+ if (info.isDir)
+ yield OS.File.removeDir(aFile.path);
+ else
+ yield OS.File.remove(aFile.path);
+ }
+ catch (e if e instanceof OS.File.Error && e.becauseNoSuchFile) {
+ // The file has already gone away
+ return;
+ }
+ });
+}
+
+/**
+ * Recursively removes a directory or file fixing permissions when necessary.
+ *
+ * @param aFile
+ * The nsIFile to remove
+ */
+function recursiveRemove(aFile) {
+ let isDir = null;
+
+ try {
+ isDir = aFile.isDirectory();
+ }
+ catch (e) {
+ // If the file has already gone away then don't worry about it, this can
+ // happen on OSX where the resource fork is automatically moved with the
+ // data fork for the file. See bug 733436.
+ if (e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
+ return;
+ if (e.result == Cr.NS_ERROR_FILE_NOT_FOUND)
+ return;
+
+ throw e;
+ }
+
+ setFilePermissions(aFile, isDir ? FileUtils.PERMS_DIRECTORY
+ : FileUtils.PERMS_FILE);
+
+ try {
+ aFile.remove(true);
+ return;
+ }
+ catch (e) {
+ if (!aFile.isDirectory()) {
+ logger.error("Failed to remove file " + aFile.path, e);
+ throw e;
+ }
+ }
+
+ // Use a snapshot of the directory contents to avoid possible issues with
+ // iterating over a directory while removing files from it (the YAFFS2
+ // embedded filesystem has this issue, see bug 772238), and to remove
+ // normal files before their resource forks on OSX (see bug 733436).
+ let entries = getDirectoryEntries(aFile, true);
+ entries.forEach(recursiveRemove);
+
+ try {
+ aFile.remove(true);
+ }
+ catch (e) {
+ logger.error("Failed to remove empty directory " + aFile.path, e);
+ throw e;
+ }
+}
+
+/**
+ * Returns the timestamp and leaf file name of the most recently modified
+ * entry in a directory,
+ * or simply the file's own timestamp if it is not a directory.
+ * Also returns the total number of items (directories and files) visited in the scan
+ *
+ * @param aFile
+ * A non-null nsIFile object
+ * @return [File Name, Epoch time, items visited], as described above.
+ */
+function recursiveLastModifiedTime(aFile) {
+ try {
+ let modTime = aFile.lastModifiedTime;
+ let fileName = aFile.leafName;
+ if (aFile.isFile())
+ return [fileName, modTime, 1];
+
+ if (aFile.isDirectory()) {
+ let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
+ let entry;
+ let totalItems = 1;
+ while ((entry = entries.nextFile)) {
+ let [subName, subTime, items] = recursiveLastModifiedTime(entry);
+ totalItems += items;
+ if (subTime > modTime) {
+ modTime = subTime;
+ fileName = subName;
+ }
+ }
+ entries.close();
+ return [fileName, modTime, totalItems];
+ }
+ }
+ catch (e) {
+ logger.warn("Problem getting last modified time for " + aFile.path, e);
+ }
+
+ // If the file is something else, just ignore it.
+ return ["", 0, 0];
+}
+
+/**
+ * Gets a snapshot of directory entries.
+ *
+ * @param aDir
+ * Directory to look at
+ * @param aSortEntries
+ * True to sort entries by filename
+ * @return An array of nsIFile, or an empty array if aDir is not a readable directory
+ */
+function getDirectoryEntries(aDir, aSortEntries) {
+ let dirEnum;
+ try {
+ dirEnum = aDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
+ let entries = [];
+ while (dirEnum.hasMoreElements())
+ entries.push(dirEnum.nextFile);
+
+ if (aSortEntries) {
+ entries.sort(function sortDirEntries(a, b) {
+ return a.path > b.path ? -1 : 1;
+ });
+ }
+
+ return entries
+ }
+ catch (e) {
+ logger.warn("Can't iterate directory " + aDir.path, e);
+ return [];
+ }
+ finally {
+ if (dirEnum) {
+ dirEnum.close();
+ }
+ }
+}
+
+/**
+ * Wraps a function in an exception handler to protect against exceptions inside callbacks
+ * @param aFunction function(args...)
+ * @return function(args...), a function that takes the same arguments as aFunction
+ * and returns the same result unless aFunction throws, in which case it logs
+ * a warning and returns undefined.
+ */
+function makeSafe(aFunction) {
+ return function(...aArgs) {
+ try {
+ return aFunction(...aArgs);
+ }
+ catch(ex) {
+ logger.warn("XPIProvider callback failed", ex);
+ }
+ return undefined;
+ }
+}
+
+/**
+ * The on-disk state of an individual XPI, created from an Object
+ * as stored in the 'extensions.xpiState' pref.
+ */
+function XPIState(saved) {
+ for (let [short, long] of XPIState.prototype.fields) {
+ if (short in saved) {
+ this[long] = saved[short];
+ }
+ }
+}
+
+XPIState.prototype = {
+ fields: [['d', 'descriptor'],
+ ['e', 'enabled'],
+ ['v', 'version'],
+ ['st', 'scanTime'],
+ ['mt', 'manifestTime']],
+ /**
+ * Return the last modified time, based on enabled/disabled
+ */
+ get mtime() {
+ if (!this.enabled && ('manifestTime' in this) && this.manifestTime > this.scanTime) {
+ return this.manifestTime;
+ }
+ return this.scanTime;
+ },
+
+ toJSON() {
+ let json = {};
+ for (let [short, long] of XPIState.prototype.fields) {
+ if (long in this) {
+ json[short] = this[long];
+ }
+ }
+ return json;
+ },
+
+ /**
+ * Update the last modified time for an add-on on disk.
+ * @param aFile: nsIFile path of the add-on.
+ * @param aId: The add-on ID.
+ * @return True if the time stamp has changed.
+ */
+ getModTime(aFile, aId) {
+ let changed = false;
+ let scanStarted = Cu.now();
+ // For an unknown or enabled add-on, we do a full recursive scan.
+ if (!('scanTime' in this) || this.enabled) {
+ logger.debug('getModTime: Recursive scan of ' + aId);
+ let [modFile, modTime, items] = recursiveLastModifiedTime(aFile);
+ XPIProvider._mostRecentlyModifiedFile[aId] = modFile;
+ if (modTime != this.scanTime) {
+ this.scanTime = modTime;
+ changed = true;
+ }
+ }
+ // if the add-on is disabled, modified time is the install.rdf time, if any.
+ // If {path}/install.rdf doesn't exist, we assume this is a packed .xpi and use
+ // the time stamp of {path}
+ try {
+ // Get the install.rdf update time, if any.
+ // XXX This will eventually also need to check for package.json or whatever
+ // the new manifest is named.
+ let maniFile = aFile.clone();
+ maniFile.append(FILE_INSTALL_MANIFEST);
+ if (!(aId in XPIProvider._mostRecentlyModifiedFile)) {
+ XPIProvider._mostRecentlyModifiedFile[aId] = maniFile.leafName;
+ }
+ let maniTime = maniFile.lastModifiedTime;
+ if (maniTime != this.manifestTime) {
+ this.manifestTime = maniTime;
+ changed = true;
+ }
+ } catch (e) {
+ // No manifest
+ delete this.manifestTime;
+ try {
+ let dtime = aFile.lastModifiedTime;
+ if (dtime != this.scanTime) {
+ changed = true;
+ this.scanTime = dtime;
+ }
+ } catch (e) {
+ logger.warn("Can't get modified time of ${file}: ${e}", {file: aFile.path, e: e});
+ changed = true;
+ this.scanTime = 0;
+ }
+ }
+ return changed;
+ },
+
+ /**
+ * Update the XPIState to match an XPIDatabase entry; if 'enabled' is changed to true,
+ * update the last-modified time. This should probably be made async, but for now we
+ * don't want to maintain parallel sync and async versions of the scan.
+ * Caller is responsible for doing XPIStates.save() if necessary.
+ * @param aDBAddon The DBAddonInternal for this add-on.
+ * @param aUpdated The add-on was updated, so we must record new modified time.
+ */
+ syncWithDB(aDBAddon, aUpdated = false) {
+ logger.debug("Updating XPIState for " + JSON.stringify(aDBAddon));
+ // If the add-on changes from disabled to enabled, we should re-check the modified time.
+ // If this is a newly found add-on, it won't have an 'enabled' field but we
+ // did a full recursive scan in that case, so we don't need to do it again.
+ // We don't use aDBAddon.active here because it's not updated until after restart.
+ let mustGetMod = (aDBAddon.visible && !aDBAddon.disabled && !this.enabled);
+ this.enabled = (aDBAddon.visible && !aDBAddon.disabled);
+ this.version = aDBAddon.version;
+ // XXX Eventually also copy bootstrap, etc.
+ if (aUpdated || mustGetMod) {
+ this.getModTime(new nsIFile(this.descriptor), aDBAddon.id);
+ if (this.scanTime != aDBAddon.updateDate) {
+ aDBAddon.updateDate = this.scanTime;
+ XPIDatabase.saveChanges();
+ }
+ }
+ },
+};
+
+// Constructor for an ES6 Map that knows how to convert itself into a
+// regular object for toJSON().
+function SerializableMap() {
+ let m = new Map();
+ m.toJSON = function() {
+ let out = {}
+ for (let [key, val] of m) {
+ out[key] = val;
+ }
+ return out;
+ };
+ return m;
+}
+
+/**
+ * Keeps track of the state of XPI add-ons on the file system.
+ */
+this.XPIStates = {
+ // Map(location name -> Map(add-on ID -> XPIState))
+ db: null,
+
+ get size() {
+ if (!this.db) {
+ return 0;
+ }
+ let count = 0;
+ for (let location of this.db.values()) {
+ count += location.size;
+ }
+ return count;
+ },
+
+ /**
+ * Load extension state data from preferences.
+ */
+ loadExtensionState() {
+ let state = {};
+
+ // Clear out old directory state cache.
+ Preferences.reset(PREF_INSTALL_CACHE);
+
+ let cache = Preferences.get(PREF_XPI_STATE, "{}");
+ try {
+ state = JSON.parse(cache);
+ } catch (e) {
+ logger.warn("Error parsing extensions.xpiState ${state}: ${error}",
+ {state: cache, error: e});
+ }
+ logger.debug("Loaded add-on state from prefs: ${}", state);
+ return state;
+ },
+
+ /**
+ * Walk through all install locations, highest priority first,
+ * comparing the on-disk state of extensions to what is stored in prefs.
+ * @return true if anything has changed.
+ */
+ getInstallState() {
+ let oldState = this.loadExtensionState();
+ let changed = false;
+ this.db = new SerializableMap();
+
+ for (let location of XPIProvider.installLocations) {
+ // The list of add-on like file/directory names in the install location.
+ let addons = location.addonLocations;
+ // The results of scanning this location.
+ let foundAddons = new SerializableMap();
+
+ // What our old state thinks should be in this location.
+ let locState = {};
+ if (location.name in oldState) {
+ locState = oldState[location.name];
+ // We've seen this location.
+ delete oldState[location.name];
+ }
+
+ for (let file of addons) {
+ let id = location.getIDForLocation(file);
+
+ if (!(id in locState)) {
+ logger.debug("New add-on ${id} in ${location}", {id: id, location: location.name});
+ let xpiState = new XPIState({d: file.persistentDescriptor});
+ changed = xpiState.getModTime(file, id) || changed;
+ foundAddons.set(id, xpiState);
+ } else {
+ let xpiState = new XPIState(locState[id]);
+ // We found this add-on in the file system
+ delete locState[id];
+
+ changed = xpiState.getModTime(file, id) || changed;
+
+ if (file.persistentDescriptor != xpiState.descriptor) {
+ xpiState.descriptor = file.persistentDescriptor;
+ changed = true;
+ }
+ if (changed) {
+ logger.debug("Changed add-on ${id} in ${location}", {id: id, location: location.name});
+ }
+ foundAddons.set(id, xpiState);
+ }
+ }
+
+ // Anything left behind in oldState was removed from the file system.
+ for (let id in locState) {
+ changed = true;
+ break;
+ }
+ // If we found anything, add this location to our database.
+ if (foundAddons.size != 0) {
+ this.db.set(location.name, foundAddons);
+ }
+ }
+
+ // If there's anything left in oldState, an install location that held add-ons
+ // was removed from the browser configuration.
+ for (let location in oldState) {
+ changed = true;
+ break;
+ }
+
+ logger.debug("getInstallState changed: ${rv}, state: ${state}",
+ {rv: changed, state: this.db});
+ return changed;
+ },
+
+ /**
+ * Get the Map of XPI states for a particular location.
+ * @param aLocation The name of the install location.
+ * @return Map (id -> XPIState) or null if there are no add-ons in the location.
+ */
+ getLocation(aLocation) {
+ return this.db.get(aLocation);
+ },
+
+ /**
+ * Get the XPI state for a specific add-on in a location.
+ * If the state is not in our cache, return null.
+ * @param aLocation The name of the location where the add-on is installed.
+ * @param aId The add-on ID
+ * @return The XPIState entry for the add-on, or null.
+ */
+ getAddon(aLocation, aId) {
+ let location = this.db.get(aLocation);
+ if (!location) {
+ return null;
+ }
+ return location.get(aId);
+ },
+
+ /**
+ * Find the highest priority location of an add-on by ID and return the
+ * location and the XPIState.
+ * @param aId The add-on ID
+ * @return [locationName, XPIState] if the add-on is found, [undefined, undefined]
+ * if the add-on is not found.
+ */
+ findAddon(aId) {
+ // Fortunately the Map iterator returns in order of insertion, which is
+ // also our highest -> lowest priority order.
+ for (let [name, location] of this.db) {
+ if (location.has(aId)) {
+ return [name, location.get(aId)];
+ }
+ }
+ return [undefined, undefined];
+ },
+
+ /**
+ * Add a new XPIState for an add-on and synchronize it with the DBAddonInternal.
+ * @param aAddon DBAddonInternal for the new add-on.
+ */
+ addAddon(aAddon) {
+ let location = this.db.get(aAddon.location);
+ if (!location) {
+ // First add-on in this location.
+ location = new SerializableMap();
+ this.db.set(aAddon.location, location);
+ }
+ logger.debug("XPIStates adding add-on ${id} in ${location}: ${descriptor}", aAddon);
+ let xpiState = new XPIState({d: aAddon.descriptor});
+ location.set(aAddon.id, xpiState);
+ xpiState.syncWithDB(aAddon, true);
+ },
+
+ /**
+ * Save the current state of installed add-ons.
+ * XXX this *totally* should be a .json file using DeferredSave...
+ */
+ save() {
+ let cache = JSON.stringify(this.db);
+ Services.prefs.setCharPref(PREF_XPI_STATE, cache);
+ },
+
+ /**
+ * Remove the XPIState for an add-on and save the new state.
+ * @param aLocation The name of the add-on location.
+ * @param aId The ID of the add-on.
+ */
+ removeAddon(aLocation, aId) {
+ logger.debug("Removing XPIState for " + aLocation + ":" + aId);
+ let location = this.db.get(aLocation);
+ if (!location) {
+ return;
+ }
+ location.delete(aId);
+ if (location.size == 0) {
+ this.db.delete(aLocation);
+ }
+ this.save();
+ },
+};
+
+this.XPIProvider = {
+ get name() "XPIProvider",
+
+ // An array of known install locations
+ installLocations: null,
+ // A dictionary of known install locations by name
+ installLocationsByName: null,
+ // An array of currently active AddonInstalls
+ installs: null,
+ // The default skin for the application
+ defaultSkin: "classic/1.0",
+ // The current skin used by the application
+ currentSkin: null,
+ // The selected skin to be used by the application when it is restarted. This
+ // will be the same as currentSkin when it is the skin to be used when the
+ // application is restarted
+ selectedSkin: null,
+ // The value of the minCompatibleAppVersion preference
+ minCompatibleAppVersion: null,
+ // The value of the minCompatiblePlatformVersion preference
+ minCompatiblePlatformVersion: null,
+ // A dictionary of the file descriptors for bootstrappable add-ons by ID
+ bootstrappedAddons: {},
+ // A dictionary of JS scopes of loaded bootstrappable add-ons by ID
+ bootstrapScopes: {},
+ // True if the platform could have activated extensions
+ extensionsActive: false,
+ // True if all of the add-ons found during startup were installed in the
+ // application install location
+ allAppGlobal: true,
+ // A string listing the enabled add-ons for annotating crash reports
+ enabledAddons: null,
+ // Keep track of startup phases for telemetry
+ runPhase: XPI_STARTING,
+ // Keep track of the newest file in each add-on, in case we want to
+ // report it to telemetry.
+ _mostRecentlyModifiedFile: {},
+ // Per-addon telemetry information
+ _telemetryDetails: {},
+ // Experiments are disabled by default. Track ones that are locally enabled.
+ _enabledExperiments: null,
+ // A Map from an add-on install to its ID
+ _addonFileMap: new Map(),
+ // Flag to know if ToolboxProcess.jsm has already been loaded by someone or not
+ _toolboxProcessLoaded: false,
+ // Have we started shutting down bootstrap add-ons?
+ _closing: false,
+
+ // Keep track of in-progress operations that support cancel()
+ _inProgress: [],
+
+ doing: function XPI_doing(aCancellable) {
+ this._inProgress.push(aCancellable);
+ },
+
+ done: function XPI_done(aCancellable) {
+ let i = this._inProgress.indexOf(aCancellable);
+ if (i != -1) {
+ this._inProgress.splice(i, 1);
+ return true;
+ }
+ return false;
+ },
+
+ cancelAll: function XPI_cancelAll() {
+ // Cancelling one may alter _inProgress, so don't use a simple iterator
+ while (this._inProgress.length > 0) {
+ let c = this._inProgress.shift();
+ try {
+ c.cancel();
+ }
+ catch (e) {
+ logger.warn("Cancel failed", e);
+ }
+ }
+ },
+
+ /**
+ * Adds or updates a URI mapping for an Addon.id.
+ *
+ * Mappings should not be removed at any point. This is so that the mappings
+ * will be still valid after an add-on gets disabled or uninstalled, as
+ * consumers may still have URIs of (leaked) resources they want to map.
+ */
+ _addURIMapping: function XPI__addURIMapping(aID, aFile) {
+ logger.info("Mapping " + aID + " to " + aFile.path);
+ this._addonFileMap.set(aID, aFile.path);
+
+ let service = Cc["@mozilla.org/addon-path-service;1"].getService(Ci.amIAddonPathService);
+ service.insertPath(aFile.path, aID);
+ },
+
+ /**
+ * Resolve a URI back to physical file.
+ *
+ * Of course, this works only for URIs pointing to local resources.
+ *
+ * @param aURI
+ * URI to resolve
+ * @return
+ * resolved nsIFileURL
+ */
+ _resolveURIToFile: function XPI__resolveURIToFile(aURI) {
+ switch (aURI.scheme) {
+ case "jar":
+ case "file":
+ if (aURI instanceof Ci.nsIJARURI) {
+ return this._resolveURIToFile(aURI.JARFile);
+ }
+ return aURI;
+
+ case "chrome":
+ aURI = ChromeRegistry.convertChromeURL(aURI);
+ return this._resolveURIToFile(aURI);
+
+ case "resource":
+ aURI = Services.io.newURI(ResProtocolHandler.resolveURI(aURI), null,
+ null);
+ return this._resolveURIToFile(aURI);
+
+ case "view-source":
+ aURI = Services.io.newURI(aURI.path, null, null);
+ return this._resolveURIToFile(aURI);
+
+ case "about":
+ if (aURI.spec == "about:blank") {
+ // Do not attempt to map about:blank
+ return null;
+ }
+
+ let chan;
+ try {
+ chan = Services.io.newChannelFromURI2(aURI,
+ null, // aLoadingNode
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_NORMAL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ }
+ catch (ex) {
+ return null;
+ }
+ // Avoid looping
+ if (chan.URI.equals(aURI)) {
+ return null;
+ }
+ // We want to clone the channel URI to avoid accidentially keeping
+ // unnecessary references to the channel or implementation details
+ // around.
+ return this._resolveURIToFile(chan.URI.clone());
+
+ default:
+ return null;
+ }
+ },
+
+ /**
+ * Starts the XPI provider initializes the install locations and prefs.
+ *
+ * @param aAppChanged
+ * A tri-state value. Undefined means the current profile was created
+ * for this session, true means the profile already existed but was
+ * last used with an application with a different version number,
+ * false means that the profile was last used by this version of the
+ * application.
+ * @param aOldAppVersion
+ * The version of the application last run with this profile or null
+ * if it is a new profile or the version is unknown
+ * @param aOldPlatformVersion
+ * The version of the platform last run with this profile or null
+ * if it is a new profile or the version is unknown
+ */
+ startup: function XPI_startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
+ function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) {
+ try {
+ var dir = FileUtils.getDir(aKey, aPaths);
+ }
+ catch (e) {
+ // Some directories aren't defined on some platforms, ignore them
+ logger.debug("Skipping unavailable install location " + aName);
+ return;
+ }
+
+ try {
+ var location = new DirectoryInstallLocation(aName, dir, aScope, aLocked);
+ }
+ catch (e) {
+ logger.warn("Failed to add directory install location " + aName, e);
+ return;
+ }
+
+ XPIProvider.installLocations.push(location);
+ XPIProvider.installLocationsByName[location.name] = location;
+ }
+
+ function addRegistryInstallLocation(aName, aRootkey, aScope) {
+ try {
+ var location = new WinRegInstallLocation(aName, aRootkey, aScope);
+ }
+ catch (e) {
+ logger.warn("Failed to add registry install location " + aName, e);
+ return;
+ }
+
+ XPIProvider.installLocations.push(location);
+ XPIProvider.installLocationsByName[location.name] = location;
+ }
+
+ try {
+ AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
+
+ logger.debug("startup");
+ this.runPhase = XPI_STARTING;
+ this.installs = [];
+ this.installLocations = [];
+ this.installLocationsByName = {};
+ // Hook for tests to detect when saving database at shutdown time fails
+ this._shutdownError = null;
+ // Clear this at startup for xpcshell test restarts
+ this._telemetryDetails = {};
+ // Clear the set of enabled experiments (experiments disabled by default).
+ this._enabledExperiments = new Set();
+
+ let hasRegistry = ("nsIWindowsRegKey" in Ci);
+
+ let enabledScopes = Preferences.get(PREF_EM_ENABLED_SCOPES,
+ AddonManager.SCOPE_ALL);
+
+ // These must be in order of priority, highest to lowest,
+ // for processFileChanges etc. to work
+ // The profile location is always enabled
+ addDirectoryInstallLocation(KEY_APP_PROFILE, KEY_PROFILEDIR,
+ [DIR_EXTENSIONS],
+ AddonManager.SCOPE_PROFILE, false);
+
+ if (enabledScopes & AddonManager.SCOPE_USER) {
+ addDirectoryInstallLocation(KEY_APP_SYSTEM_USER, "XREUSysExt",
+ [Services.appinfo.ID],
+ AddonManager.SCOPE_USER, true);
+ if (hasRegistry) {
+ addRegistryInstallLocation("winreg-app-user",
+ Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ AddonManager.SCOPE_USER);
+ }
+ }
+
+ if (enabledScopes & AddonManager.SCOPE_APPLICATION) {
+ addDirectoryInstallLocation(KEY_APP_GLOBAL, KEY_APPDIR,
+ [DIR_EXTENSIONS],
+ AddonManager.SCOPE_APPLICATION, true);
+ }
+
+ if (enabledScopes & AddonManager.SCOPE_SYSTEM) {
+ addDirectoryInstallLocation(KEY_APP_SYSTEM_SHARE, "XRESysSExtPD",
+ [Services.appinfo.ID],
+ AddonManager.SCOPE_SYSTEM, true);
+ addDirectoryInstallLocation(KEY_APP_SYSTEM_LOCAL, "XRESysLExtPD",
+ [Services.appinfo.ID],
+ AddonManager.SCOPE_SYSTEM, true);
+ if (hasRegistry) {
+ addRegistryInstallLocation("winreg-app-global",
+ Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ AddonManager.SCOPE_SYSTEM);
+ }
+ }
+
+ let defaultPrefs = new Preferences({ defaultBranch: true });
+ this.defaultSkin = defaultPrefs.get(PREF_GENERAL_SKINS_SELECTEDSKIN,
+ "classic/1.0");
+ this.currentSkin = Preferences.get(PREF_GENERAL_SKINS_SELECTEDSKIN,
+ this.defaultSkin);
+ this.selectedSkin = this.currentSkin;
+ this.applyThemeChange();
+
+ this.minCompatibleAppVersion = Preferences.get(PREF_EM_MIN_COMPAT_APP_VERSION,
+ null);
+ this.minCompatiblePlatformVersion = Preferences.get(PREF_EM_MIN_COMPAT_PLATFORM_VERSION,
+ null);
+ this.enabledAddons = "";
+
+ Services.prefs.addObserver(PREF_EM_MIN_COMPAT_APP_VERSION, this, false);
+ Services.prefs.addObserver(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, this, false);
+ Services.obs.addObserver(this, NOTIFICATION_FLUSH_PERMISSIONS, false);
+ if (Cu.isModuleLoaded("resource://gre/modules/devtools/ToolboxProcess.jsm")) {
+ // If BrowserToolboxProcess is already loaded, set the boolean to true
+ // and do whatever is needed
+ this._toolboxProcessLoaded = true;
+ BrowserToolboxProcess.on("connectionchange",
+ this.onDebugConnectionChange.bind(this));
+ }
+ else {
+ // Else, wait for it to load
+ Services.obs.addObserver(this, NOTIFICATION_TOOLBOXPROCESS_LOADED, false);
+ }
+
+ let flushCaches = this.checkForChanges(aAppChanged, aOldAppVersion,
+ aOldPlatformVersion);
+
+ // Changes to installed extensions may have changed which theme is selected
+ this.applyThemeChange();
+
+ AddonManagerPrivate.markProviderSafe(this);
+
+ if (aAppChanged === undefined) {
+ // For new profiles we will never need to show the add-on selection UI
+ Services.prefs.setBoolPref(PREF_SHOWN_SELECTION_UI, true);
+ }
+ else if (aAppChanged && !this.allAppGlobal &&
+ Preferences.get(PREF_EM_SHOW_MISMATCH_UI, true)) {
+ if (!Preferences.get(PREF_SHOWN_SELECTION_UI, false)) {
+ // Flip a flag to indicate that we interrupted startup with an interactive prompt
+ Services.startup.interrupted = true;
+ // This *must* be modal as it has to block startup.
+ var features = "chrome,centerscreen,dialog,titlebar,modal";
+ Services.ww.openWindow(null, URI_EXTENSION_SELECT_DIALOG, "", features, null);
+ Services.prefs.setBoolPref(PREF_SHOWN_SELECTION_UI, true);
+ // Ensure any changes to the add-ons list are flushed to disk
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
+ !XPIDatabase.writeAddonsList());
+ }
+ else {
+ let addonsToUpdate = this.shouldForceUpdateCheck(aAppChanged);
+ if (addonsToUpdate) {
+ this.showUpgradeUI(addonsToUpdate);
+ flushCaches = true;
+ }
+ }
+ }
+
+ if (flushCaches) {
+ flushStartupCache();
+ // UI displayed early in startup (like the compatibility UI) may have
+ // caused us to cache parts of the skin or locale in memory. These must
+ // be flushed to allow extension provided skins and locales to take full
+ // effect
+ Services.obs.notifyObservers(null, "chrome-flush-skin-caches", null);
+ Services.obs.notifyObservers(null, "chrome-flush-caches", null);
+ }
+
+ this.enabledAddons = Preferences.get(PREF_EM_ENABLED_ADDONS, "");
+
+ if ("nsICrashReporter" in Ci &&
+ Services.appinfo instanceof Ci.nsICrashReporter) {
+ // Annotate the crash report with relevant add-on information.
+ try {
+ Services.appinfo.annotateCrashReport("Theme", this.currentSkin);
+ } catch (e) { }
+ try {
+ Services.appinfo.annotateCrashReport("EMCheckCompatibility",
+ AddonManager.checkCompatibility);
+ } catch (e) { }
+ this.addAddonsToCrashReporter();
+ }
+
+ try {
+ AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_begin");
+ for (let id in this.bootstrappedAddons) {
+ try {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.persistentDescriptor = this.bootstrappedAddons[id].descriptor;
+ let reason = BOOTSTRAP_REASONS.APP_STARTUP;
+ // Eventually set INSTALLED reason when a bootstrap addon
+ // is dropped in profile folder and automatically installed
+ if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
+ .indexOf(id) !== -1)
+ reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
+ this.callBootstrapMethod(createAddonDetails(id, this.bootstrappedAddons[id]),
+ file, "startup", reason);
+ }
+ catch (e) {
+ logger.error("Failed to load bootstrap addon " + id + " from " +
+ this.bootstrappedAddons[id].descriptor, e);
+ }
+ }
+ AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_end");
+ }
+ catch (e) {
+ logger.error("bootstrap startup failed", e);
+ AddonManagerPrivate.recordException("XPI-BOOTSTRAP", "startup failed", e);
+ }
+
+ // Let these shutdown a little earlier when they still have access to most
+ // of XPCOM
+ Services.obs.addObserver({
+ observe: function shutdownObserver(aSubject, aTopic, aData) {
+ XPIProvider._closing = true;
+ for (let id in XPIProvider.bootstrappedAddons) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.persistentDescriptor = XPIProvider.bootstrappedAddons[id].descriptor;
+ let addon = createAddonDetails(id, XPIProvider.bootstrappedAddons[id]);
+ XPIProvider.callBootstrapMethod(addon, file, "shutdown",
+ BOOTSTRAP_REASONS.APP_SHUTDOWN);
+ }
+ Services.obs.removeObserver(this, "quit-application-granted");
+ }
+ }, "quit-application-granted", false);
+
+ // Detect final-ui-startup for telemetry reporting
+ Services.obs.addObserver({
+ observe: function uiStartupObserver(aSubject, aTopic, aData) {
+ AddonManagerPrivate.recordTimestamp("XPI_finalUIStartup");
+ XPIProvider.runPhase = XPI_AFTER_UI_STARTUP;
+ Services.obs.removeObserver(this, "final-ui-startup");
+ }
+ }, "final-ui-startup", false);
+
+ AddonManagerPrivate.recordTimestamp("XPI_startup_end");
+
+ this.extensionsActive = true;
+ this.runPhase = XPI_BEFORE_UI_STARTUP;
+ }
+ catch (e) {
+ logger.error("startup failed", e);
+ AddonManagerPrivate.recordException("XPI", "startup failed", e);
+ }
+ },
+
+ /**
+ * Shuts down the database and releases all references.
+ * Return: Promise{integer} resolves / rejects with the result of
+ * flushing the XPI Database if it was loaded,
+ * 0 otherwise.
+ */
+ shutdown: function XPI_shutdown() {
+ logger.debug("shutdown");
+
+ // Stop anything we were doing asynchronously
+ this.cancelAll();
+
+ this.bootstrappedAddons = {};
+ this.bootstrapScopes = {};
+ this.enabledAddons = null;
+ this.allAppGlobal = true;
+
+ // If there are pending operations then we must update the list of active
+ // add-ons
+ if (Preferences.get(PREF_PENDING_OPERATIONS, false)) {
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_pending_ops", 1);
+ XPIDatabase.updateActiveAddons();
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
+ !XPIDatabase.writeAddonsList());
+ }
+
+ this.installs = null;
+ this.installLocations = null;
+ this.installLocationsByName = null;
+
+ // This is needed to allow xpcshell tests to simulate a restart
+ this.extensionsActive = false;
+ this._addonFileMap.clear();
+
+ if (gLazyObjectsLoaded) {
+ let done = XPIDatabase.shutdown();
+ done.then(
+ ret => {
+ logger.debug("Notifying XPI shutdown observers");
+ Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
+ },
+ err => {
+ logger.debug("Notifying XPI shutdown observers");
+ this._shutdownError = err;
+ Services.obs.notifyObservers(null, "xpi-provider-shutdown", err);
+ }
+ );
+ return done;
+ }
+ else {
+ logger.debug("Notifying XPI shutdown observers");
+ Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
+ }
+ },
+
+ /**
+ * Applies any pending theme change to the preferences.
+ */
+ applyThemeChange: function XPI_applyThemeChange() {
+ if (!Preferences.get(PREF_DSS_SWITCHPENDING, false))
+ return;
+
+ // Tell the Chrome Registry which Skin to select
+ try {
+ this.selectedSkin = Preferences.get(PREF_DSS_SKIN_TO_SELECT);
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN,
+ this.selectedSkin);
+ Services.prefs.clearUserPref(PREF_DSS_SKIN_TO_SELECT);
+ logger.debug("Changed skin to " + this.selectedSkin);
+ this.currentSkin = this.selectedSkin;
+ }
+ catch (e) {
+ logger.error("Error applying theme change", e);
+ }
+ Services.prefs.clearUserPref(PREF_DSS_SWITCHPENDING);
+ },
+
+ /**
+ * If the application has been upgraded and there are add-ons outside the
+ * application directory then we may need to synchronize compatibility
+ * information but only if the mismatch UI isn't disabled.
+ *
+ * @returns False if no update check is needed, otherwise an array of add-on
+ * IDs to check for updates. Array may be empty if no add-ons can be/need
+ * to be updated, but the metadata check needs to be performed.
+ */
+ shouldForceUpdateCheck: function XPI_shouldForceUpdateCheck(aAppChanged) {
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_metadata_age", AddonRepository.metadataAge());
+
+ let startupChanges = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
+ logger.debug("shouldForceUpdateCheck startupChanges: " + startupChanges.toSource());
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_disabled", startupChanges.length);
+
+ let forceUpdate = [];
+ if (startupChanges.length > 0) {
+ let addons = XPIDatabase.getAddons();
+ for (let addon of addons) {
+ if ((startupChanges.indexOf(addon.id) != -1) &&
+ (addon.permissions() & AddonManager.PERM_CAN_UPGRADE)) {
+ logger.debug("shouldForceUpdateCheck: can upgrade disabled add-on " + addon.id);
+ forceUpdate.push(addon.id);
+ }
+ }
+ }
+
+ if (AddonRepository.isMetadataStale()) {
+ logger.debug("shouldForceUpdateCheck: metadata is stale");
+ return forceUpdate;
+ }
+ if (forceUpdate.length > 0) {
+ return forceUpdate;
+ }
+
+ return false;
+ },
+
+ /**
+ * Shows the "Compatibility Updates" UI.
+ *
+ * @param aAddonIDs
+ * Array opf addon IDs that were disabled by the application update, and
+ * should therefore be checked for updates.
+ */
+ showUpgradeUI: function XPI_showUpgradeUI(aAddonIDs) {
+ logger.debug("XPI_showUpgradeUI: " + aAddonIDs.toSource());
+ // Flip a flag to indicate that we interrupted startup with an interactive prompt
+ Services.startup.interrupted = true;
+
+ var variant = Cc["@mozilla.org/variant;1"].
+ createInstance(Ci.nsIWritableVariant);
+ variant.setFromVariant(aAddonIDs);
+
+ // This *must* be modal as it has to block startup.
+ var features = "chrome,centerscreen,dialog,titlebar,modal";
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
+
+ // Ensure any changes to the add-ons list are flushed to disk
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
+ !XPIDatabase.writeAddonsList());
+ },
+
+ /**
+ * Persists changes to XPIProvider.bootstrappedAddons to its store (a pref).
+ */
+ persistBootstrappedAddons: function XPI_persistBootstrappedAddons() {
+ // Experiments are disabled upon app load, so don't persist references.
+ let filtered = {};
+ for (let id in this.bootstrappedAddons) {
+ let entry = this.bootstrappedAddons[id];
+ if (entry.type == "experiment") {
+ continue;
+ }
+
+ filtered[id] = entry;
+ }
+
+ Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
+ JSON.stringify(filtered));
+ },
+
+ /**
+ * Adds a list of currently active add-ons to the next crash report.
+ */
+ addAddonsToCrashReporter: function XPI_addAddonsToCrashReporter() {
+ if (!("nsICrashReporter" in Ci) ||
+ !(Services.appinfo instanceof Ci.nsICrashReporter))
+ return;
+
+ // In safe mode no add-ons are loaded so we should not include them in the
+ // crash report
+ if (Services.appinfo.inSafeMode)
+ return;
+
+ let data = this.enabledAddons;
+ for (let id in this.bootstrappedAddons) {
+ data += (data ? "," : "") + encodeURIComponent(id) + ":" +
+ encodeURIComponent(this.bootstrappedAddons[id].version);
+ }
+
+ try {
+ Services.appinfo.annotateCrashReport("Add-ons", data);
+ }
+ catch (e) { }
+
+ let TelemetrySession =
+ Cu.import("resource://gre/modules/TelemetrySession.jsm", {}).TelemetrySession;
+ TelemetrySession.setAddOns(data);
+ },
+
+ /**
+ * Check the staging directories of install locations for any add-ons to be
+ * installed or add-ons to be uninstalled.
+ *
+ * @param aManifests
+ * A dictionary to add detected install manifests to for the purpose
+ * of passing through updated compatibility information
+ * @return true if an add-on was installed or uninstalled
+ */
+ processPendingFileChanges: function XPI_processPendingFileChanges(aManifests) {
+ let changed = false;
+ this.installLocations.forEach(function(aLocation) {
+ aManifests[aLocation.name] = {};
+ // We can't install or uninstall anything in locked locations
+ if (aLocation.locked)
+ return;
+
+ let stagedXPIDir = aLocation.getXPIStagingDir();
+ let stagingDir = aLocation.getStagingDir();
+
+ if (stagedXPIDir.exists() && stagedXPIDir.isDirectory()) {
+ let entries = stagedXPIDir.directoryEntries
+ .QueryInterface(Ci.nsIDirectoryEnumerator);
+ while (entries.hasMoreElements()) {
+ let stageDirEntry = entries.nextFile;
+
+ if (!stageDirEntry.isDirectory()) {
+ logger.warn("Ignoring file in XPI staging directory: " + stageDirEntry.path);
+ continue;
+ }
+
+ // Find the last added XPI file in the directory
+ let stagedXPI = null;
+ var xpiEntries = stageDirEntry.directoryEntries
+ .QueryInterface(Ci.nsIDirectoryEnumerator);
+ while (xpiEntries.hasMoreElements()) {
+ let file = xpiEntries.nextFile;
+ if (file.isDirectory())
+ continue;
+
+ let extension = file.leafName;
+ extension = extension.substring(extension.length - 4);
+
+ if (extension != ".xpi" && extension != ".jar")
+ continue;
+
+ stagedXPI = file;
+ }
+ xpiEntries.close();
+
+ if (!stagedXPI)
+ continue;
+
+ let addon = null;
+ try {
+ addon = loadManifestFromZipFile(stagedXPI);
+ }
+ catch (e) {
+ logger.error("Unable to read add-on manifest from " + stagedXPI.path, e);
+ continue;
+ }
+
+ logger.debug("Migrating staged install of " + addon.id + " in " + aLocation.name);
+
+ if (addon.unpack || Preferences.get(PREF_XPI_UNPACK, false)) {
+ let targetDir = stagingDir.clone();
+ targetDir.append(addon.id);
+ try {
+ targetDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ }
+ catch (e) {
+ logger.error("Failed to create staging directory for add-on " + addon.id, e);
+ continue;
+ }
+
+ try {
+ ZipUtils.extractFiles(stagedXPI, targetDir);
+ }
+ catch (e) {
+ logger.error("Failed to extract staged XPI for add-on " + addon.id + " in " +
+ aLocation.name, e);
+ }
+ }
+ else {
+ try {
+ stagedXPI.moveTo(stagingDir, addon.id + ".xpi");
+ }
+ catch (e) {
+ logger.error("Failed to move staged XPI for add-on " + addon.id + " in " +
+ aLocation.name, e);
+ }
+ }
+ }
+ entries.close();
+ }
+
+ if (stagedXPIDir.exists()) {
+ try {
+ recursiveRemove(stagedXPIDir);
+ }
+ catch (e) {
+ // Non-critical, just saves some perf on startup if we clean this up.
+ logger.debug("Error removing XPI staging dir " + stagedXPIDir.path, e);
+ }
+ }
+
+ try {
+ if (!stagingDir || !stagingDir.exists() || !stagingDir.isDirectory())
+ return;
+ }
+ catch (e) {
+ logger.warn("Failed to find staging directory", e);
+ return;
+ }
+
+ let seenFiles = [];
+ // Use a snapshot of the directory contents to avoid possible issues with
+ // iterating over a directory while removing files from it (the YAFFS2
+ // embedded filesystem has this issue, see bug 772238), and to remove
+ // normal files before their resource forks on OSX (see bug 733436).
+ let stagingDirEntries = getDirectoryEntries(stagingDir, true);
+ for (let stageDirEntry of stagingDirEntries) {
+ let id = stageDirEntry.leafName;
+
+ let isDir;
+ try {
+ isDir = stageDirEntry.isDirectory();
+ }
+ catch (e if e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
+ // If the file has already gone away then don't worry about it, this
+ // can happen on OSX where the resource fork is automatically moved
+ // with the data fork for the file. See bug 733436.
+ continue;
+ }
+
+ if (!isDir) {
+ if (id.substring(id.length - 4).toLowerCase() == ".xpi") {
+ id = id.substring(0, id.length - 4);
+ }
+ else {
+ if (id.substring(id.length - 5).toLowerCase() != ".json") {
+ logger.warn("Ignoring file: " + stageDirEntry.path);
+ seenFiles.push(stageDirEntry.leafName);
+ }
+ continue;
+ }
+ }
+
+ // Check that the directory's name is a valid ID.
+ if (!gIDTest.test(id)) {
+ logger.warn("Ignoring directory whose name is not a valid add-on ID: " +
+ stageDirEntry.path);
+ seenFiles.push(stageDirEntry.leafName);
+ continue;
+ }
+
+ changed = true;
+
+ if (isDir) {
+ // Check if the directory contains an install manifest.
+ let manifest = stageDirEntry.clone();
+ manifest.append(FILE_INSTALL_MANIFEST);
+
+ // If the install manifest doesn't exist uninstall this add-on in this
+ // install location.
+ if (!manifest.exists()) {
+ logger.debug("Processing uninstall of " + id + " in " + aLocation.name);
+
+ try {
+ let addonFile = aLocation.getLocationForID(id);
+ let addonToUninstall = loadManifestFromFile(addonFile, aLocation);
+ if (addonToUninstall.bootstrap) {
+ this.callBootstrapMethod(addonToUninstall, addonToUninstall._sourceBundle,
+ "uninstall", BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ }
+ }
+ catch (e) {
+ logger.warn("Failed to call uninstall for " + id, e);
+ }
+
+ try {
+ aLocation.uninstallAddon(id);
+ seenFiles.push(stageDirEntry.leafName);
+ }
+ catch (e) {
+ logger.error("Failed to uninstall add-on " + id + " in " + aLocation.name, e);
+ }
+ // The file check later will spot the removal and cleanup the database
+ continue;
+ }
+ }
+
+ aManifests[aLocation.name][id] = null;
+ let existingAddonID = id;
+
+ let jsonfile = stagingDir.clone();
+ jsonfile.append(id + ".json");
+
+ try {
+ aManifests[aLocation.name][id] = loadManifestFromFile(stageDirEntry);
+ }
+ catch (e) {
+ logger.error("Unable to read add-on manifest from " + stageDirEntry.path, e);
+ // This add-on can't be installed so just remove it now
+ seenFiles.push(stageDirEntry.leafName);
+ seenFiles.push(jsonfile.leafName);
+ continue;
+ }
+
+ // Check for a cached metadata for this add-on, it may contain updated
+ // compatibility information
+ if (jsonfile.exists()) {
+ logger.debug("Found updated metadata for " + id + " in " + aLocation.name);
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ let json = Cc["@mozilla.org/dom/json;1"].
+ createInstance(Ci.nsIJSON);
+
+ try {
+ fis.init(jsonfile, -1, 0, 0);
+ let metadata = json.decodeFromStream(fis, jsonfile.fileSize);
+ aManifests[aLocation.name][id].importMetadata(metadata);
+ }
+ catch (e) {
+ // If some data can't be recovered from the cached metadata then it
+ // is unlikely to be a problem big enough to justify throwing away
+ // the install, just log and error and continue
+ logger.error("Unable to read metadata from " + jsonfile.path, e);
+ }
+ finally {
+ fis.close();
+ }
+ }
+ seenFiles.push(jsonfile.leafName);
+
+ existingAddonID = aManifests[aLocation.name][id].existingAddonID || id;
+
+ var oldBootstrap = null;
+ logger.debug("Processing install of " + id + " in " + aLocation.name);
+ if (existingAddonID in this.bootstrappedAddons) {
+ try {
+ var existingAddon = aLocation.getLocationForID(existingAddonID);
+ if (this.bootstrappedAddons[existingAddonID].descriptor ==
+ existingAddon.persistentDescriptor) {
+ oldBootstrap = this.bootstrappedAddons[existingAddonID];
+
+ // We'll be replacing a currently active bootstrapped add-on so
+ // call its uninstall method
+ let newVersion = aManifests[aLocation.name][id].version;
+ let oldVersion = oldBootstrap.version;
+ let uninstallReason = Services.vc.compare(oldVersion, newVersion) < 0 ?
+ BOOTSTRAP_REASONS.ADDON_UPGRADE :
+ BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
+
+ this.callBootstrapMethod(createAddonDetails(existingAddonID, oldBootstrap),
+ existingAddon, "uninstall", uninstallReason,
+ { newVersion: newVersion });
+ this.unloadBootstrapScope(existingAddonID);
+ flushStartupCache();
+ }
+ }
+ catch (e) {
+ }
+ }
+
+ try {
+ var addonInstallLocation = aLocation.installAddon(id, stageDirEntry,
+ existingAddonID);
+ if (aManifests[aLocation.name][id])
+ aManifests[aLocation.name][id]._sourceBundle = addonInstallLocation;
+ }
+ catch (e) {
+ logger.error("Failed to install staged add-on " + id + " in " + aLocation.name,
+ e);
+ // Re-create the staged install
+ AddonInstall.createStagedInstall(aLocation, stageDirEntry,
+ aManifests[aLocation.name][id]);
+ // Make sure not to delete the cached manifest json file
+ seenFiles.pop();
+
+ delete aManifests[aLocation.name][id];
+
+ if (oldBootstrap) {
+ // Re-install the old add-on
+ this.callBootstrapMethod(createAddonDetails(existingAddonID, oldBootstrap),
+ existingAddon, "install",
+ BOOTSTRAP_REASONS.ADDON_INSTALL);
+ }
+ continue;
+ }
+ }
+
+ try {
+ aLocation.cleanStagingDir(seenFiles);
+ }
+ catch (e) {
+ // Non-critical, just saves some perf on startup if we clean this up.
+ logger.debug("Error cleaning staging dir " + stagingDir.path, e);
+ }
+ }, this);
+ return changed;
+ },
+
+ /**
+ * Installs any add-ons located in the extensions directory of the
+ * application's distribution specific directory into the profile unless a
+ * newer version already exists or the user has previously uninstalled the
+ * distributed add-on.
+ *
+ * @param aManifests
+ * A dictionary to add new install manifests to to save having to
+ * reload them later
+ * @return true if any new add-ons were installed
+ */
+ installDistributionAddons: function XPI_installDistributionAddons(aManifests) {
+ let distroDir;
+ try {
+ distroDir = FileUtils.getDir(KEY_APP_DISTRIBUTION, [DIR_EXTENSIONS]);
+ }
+ catch (e) {
+ return false;
+ }
+
+ if (!distroDir.exists())
+ return false;
+
+ if (!distroDir.isDirectory())
+ return false;
+
+ let changed = false;
+ let profileLocation = this.installLocationsByName[KEY_APP_PROFILE];
+
+ let entries = distroDir.directoryEntries
+ .QueryInterface(Ci.nsIDirectoryEnumerator);
+ let entry;
+ while ((entry = entries.nextFile)) {
+
+ let id = entry.leafName;
+
+ if (entry.isFile()) {
+ if (id.substring(id.length - 4).toLowerCase() == ".xpi") {
+ id = id.substring(0, id.length - 4);
+ }
+ else {
+ logger.debug("Ignoring distribution add-on that isn't an XPI: " + entry.path);
+ continue;
+ }
+ }
+ else if (!entry.isDirectory()) {
+ logger.debug("Ignoring distribution add-on that isn't a file or directory: " +
+ entry.path);
+ continue;
+ }
+
+ if (!gIDTest.test(id)) {
+ logger.debug("Ignoring distribution add-on whose name is not a valid add-on ID: " +
+ entry.path);
+ continue;
+ }
+
+ let addon;
+ try {
+ addon = loadManifestFromFile(entry);
+ }
+ catch (e) {
+ logger.warn("File entry " + entry.path + " contains an invalid add-on", e);
+ continue;
+ }
+
+ if (addon.id != id) {
+ logger.warn("File entry " + entry.path + " contains an add-on with an " +
+ "incorrect ID")
+ continue;
+ }
+
+ let existingEntry = null;
+ try {
+ existingEntry = profileLocation.getLocationForID(id);
+ }
+ catch (e) {
+ }
+
+ if (existingEntry) {
+ let existingAddon;
+ try {
+ existingAddon = loadManifestFromFile(existingEntry);
+
+ if (Services.vc.compare(addon.version, existingAddon.version) <= 0)
+ continue;
+ }
+ catch (e) {
+ // Bad add-on in the profile so just proceed and install over the top
+ logger.warn("Profile contains an add-on with a bad or missing install " +
+ "manifest at " + existingEntry.path + ", overwriting", e);
+ }
+ }
+ else if (Preferences.get(PREF_BRANCH_INSTALLED_ADDON + id, false)) {
+ continue;
+ }
+
+ // Install the add-on
+ try {
+ profileLocation.installAddon(id, entry, null, true);
+ logger.debug("Installed distribution add-on " + id);
+
+ Services.prefs.setBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, true)
+
+ // aManifests may contain a copy of a newly installed add-on's manifest
+ // and we'll have overwritten that so instead cache our install manifest
+ // which will later be put into the database in processFileChanges
+ if (!(KEY_APP_PROFILE in aManifests))
+ aManifests[KEY_APP_PROFILE] = {};
+ aManifests[KEY_APP_PROFILE][id] = addon;
+ changed = true;
+ }
+ catch (e) {
+ logger.error("Failed to install distribution add-on " + entry.path, e);
+ }
+ }
+
+ entries.close();
+
+ return changed;
+ },
+
+ /**
+ * Compares the add-ons that are currently installed to those that were
+ * known to be installed when the application last ran and applies any
+ * changes found to the database. Also sends "startupcache-invalidate" signal to
+ * observerservice if it detects that data may have changed.
+ * Always called after XPIProviderUtils.js and extensions.json have been loaded.
+ *
+ * @param aManifests
+ * A dictionary of cached AddonInstalls for add-ons that have been
+ * installed
+ * @param aUpdateCompatibility
+ * true to update add-ons appDisabled property when the application
+ * version has changed
+ * @param aOldAppVersion
+ * The version of the application last run with this profile or null
+ * if it is a new profile or the version is unknown
+ * @param aOldPlatformVersion
+ * The version of the platform last run with this profile or null
+ * if it is a new profile or the version is unknown
+ * @return a boolean indicating if a change requiring flushing the caches was
+ * detected
+ */
+ processFileChanges: function XPI_processFileChanges(aManifests,
+ aUpdateCompatibility,
+ aOldAppVersion,
+ aOldPlatformVersion) {
+ let visibleAddons = {};
+ let oldBootstrappedAddons = this.bootstrappedAddons;
+ this.bootstrappedAddons = {};
+
+ /**
+ * Updates an add-on's metadata and determines if a restart of the
+ * application is necessary. This is called when either the add-on's
+ * install directory path or last modified time has changed.
+ *
+ * @param aInstallLocation
+ * The install location containing the add-on
+ * @param aOldAddon
+ * The AddonInternal as it appeared the last time the application
+ * ran
+ * @param aAddonState
+ * The new state of the add-on
+ * @return a boolean indicating if flushing caches is required to complete
+ * changing this add-on
+ */
+ function updateMetadata(aInstallLocation, aOldAddon, aAddonState) {
+ logger.debug("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
+
+ // Check if there is an updated install manifest for this add-on
+ let newAddon = aManifests[aInstallLocation.name][aOldAddon.id];
+
+ try {
+ // If not load it
+ if (!newAddon) {
+ let file = aInstallLocation.getLocationForID(aOldAddon.id);
+ newAddon = loadManifestFromFile(file);
+ applyBlocklistChanges(aOldAddon, newAddon);
+
+ // Carry over any pendingUninstall state to add-ons modified directly
+ // in the profile. This is important when the attempt to remove the
+ // add-on in processPendingFileChanges failed and caused an mtime
+ // change to the add-ons files.
+ newAddon.pendingUninstall = aOldAddon.pendingUninstall;
+ }
+
+ // The ID in the manifest that was loaded must match the ID of the old
+ // add-on.
+ if (newAddon.id != aOldAddon.id)
+ throw new Error("Incorrect id in install manifest for existing add-on " + aOldAddon.id);
+ }
+ catch (e) {
+ logger.warn("updateMetadata: Add-on " + aOldAddon.id + " is invalid", e);
+ XPIDatabase.removeAddonMetadata(aOldAddon);
+ XPIStates.removeAddon(aOldAddon.location, aOldAddon.id);
+ if (!aInstallLocation.locked)
+ aInstallLocation.uninstallAddon(aOldAddon.id);
+ else
+ logger.warn("Could not uninstall invalid item from locked install location");
+ // If this was an active add-on then we must force a restart
+ if (aOldAddon.active)
+ return true;
+
+ return false;
+ }
+
+ // Set the additional properties on the new AddonInternal
+ newAddon._installLocation = aInstallLocation;
+ newAddon.updateDate = aAddonState.mtime;
+ newAddon.visible = !(newAddon.id in visibleAddons);
+
+ // Update the database
+ let newDBAddon = XPIDatabase.updateAddonMetadata(aOldAddon, newAddon,
+ aAddonState.descriptor);
+ if (newDBAddon.visible) {
+ visibleAddons[newDBAddon.id] = newDBAddon;
+ // Remember add-ons that were changed during startup
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
+ newDBAddon.id);
+
+ // If this was the active theme and it is now disabled then enable the
+ // default theme
+ if (aOldAddon.active && newDBAddon.disabled)
+ XPIProvider.enableDefaultTheme();
+
+ // If the new add-on is bootstrapped and active then call its install method
+ if (newDBAddon.active && newDBAddon.bootstrap) {
+ // Startup cache must be flushed before calling the bootstrap script
+ flushStartupCache();
+
+ let installReason = Services.vc.compare(aOldAddon.version, newDBAddon.version) < 0 ?
+ BOOTSTRAP_REASONS.ADDON_UPGRADE :
+ BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
+
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.persistentDescriptor = aAddonState.descriptor;
+ XPIProvider.callBootstrapMethod(newDBAddon, file, "install",
+ installReason, { oldVersion: aOldAddon.version });
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Updates an add-on's descriptor for when the add-on has moved in the
+ * filesystem but hasn't changed in any other way.
+ *
+ * @param aInstallLocation
+ * The install location containing the add-on
+ * @param aOldAddon
+ * The AddonInternal as it appeared the last time the application
+ * ran
+ * @param aAddonState
+ * The new state of the add-on
+ * @return a boolean indicating if flushing caches is required to complete
+ * changing this add-on
+ */
+ function updateDescriptor(aInstallLocation, aOldAddon, aAddonState) {
+ logger.debug("Add-on " + aOldAddon.id + " moved to " + aAddonState.descriptor);
+
+ aOldAddon.descriptor = aAddonState.descriptor;
+ aOldAddon.visible = !(aOldAddon.id in visibleAddons);
+ XPIDatabase.saveChanges();
+
+ if (aOldAddon.visible) {
+ visibleAddons[aOldAddon.id] = aOldAddon;
+
+ if (aOldAddon.bootstrap && aOldAddon.active) {
+ let bootstrap = oldBootstrappedAddons[aOldAddon.id];
+ bootstrap.descriptor = aAddonState.descriptor;
+ XPIProvider.bootstrappedAddons[aOldAddon.id] = bootstrap;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Called when no change has been detected for an add-on's metadata. The
+ * add-on may have become visible due to other add-ons being removed or
+ * the add-on may need to be updated when the application version has
+ * changed.
+ *
+ * @param aInstallLocation
+ * The install location containing the add-on
+ * @param aOldAddon
+ * The AddonInternal as it appeared the last time the application
+ * ran
+ * @param aAddonState
+ * The new state of the add-on
+ * @return a boolean indicating if flushing caches is required to complete
+ * changing this add-on
+ */
+ function updateVisibilityAndCompatibility(aInstallLocation, aOldAddon,
+ aAddonState) {
+ let changed = false;
+
+ // This add-ons metadata has not changed but it may have become visible
+ if (!(aOldAddon.id in visibleAddons)) {
+ visibleAddons[aOldAddon.id] = aOldAddon;
+
+ if (!aOldAddon.visible) {
+ // Remember add-ons that were changed during startup.
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
+ aOldAddon.id);
+ XPIDatabase.makeAddonVisible(aOldAddon);
+
+ if (aOldAddon.bootstrap) {
+ // The add-on is bootstrappable so call its install script
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.persistentDescriptor = aAddonState.descriptor;
+ XPIProvider.callBootstrapMethod(aOldAddon, file,
+ "install",
+ BOOTSTRAP_REASONS.ADDON_INSTALL);
+
+ // If it should be active then mark it as active otherwise unload
+ // its scope
+ if (!aOldAddon.disabled) {
+ XPIDatabase.updateAddonActive(aOldAddon, true);
+ }
+ else {
+ XPIProvider.unloadBootstrapScope(newAddon.id);
+ }
+ }
+ else {
+ // Otherwise a restart is necessary
+ changed = true;
+ }
+ }
+ }
+
+ // App version changed, we may need to update the appDisabled property.
+ if (aUpdateCompatibility) {
+ let wasDisabled = aOldAddon.disabled;
+ let wasAppDisabled = aOldAddon.appDisabled;
+ let wasUserDisabled = aOldAddon.userDisabled;
+ let wasSoftDisabled = aOldAddon.softDisabled;
+
+ // This updates the addon's JSON cached data in place
+ applyBlocklistChanges(aOldAddon, aOldAddon, aOldAppVersion,
+ aOldPlatformVersion);
+ aOldAddon.appDisabled = !isUsableAddon(aOldAddon);
+
+ let isDisabled = aOldAddon.disabled;
+
+ // If either property has changed update the database.
+ if (wasAppDisabled != aOldAddon.appDisabled ||
+ wasUserDisabled != aOldAddon.userDisabled ||
+ wasSoftDisabled != aOldAddon.softDisabled) {
+ logger.debug("Add-on " + aOldAddon.id + " changed appDisabled state to " +
+ aOldAddon.appDisabled + ", userDisabled state to " +
+ aOldAddon.userDisabled + " and softDisabled state to " +
+ aOldAddon.softDisabled);
+ XPIDatabase.saveChanges();
+ }
+
+ // If this is a visible add-on and it has changed disabled state then we
+ // may need a restart or to update the bootstrap list.
+ if (aOldAddon.visible && wasDisabled != isDisabled) {
+ // Remember add-ons that became disabled or enabled by the application
+ // change
+ let change = isDisabled ? AddonManager.STARTUP_CHANGE_DISABLED
+ : AddonManager.STARTUP_CHANGE_ENABLED;
+ AddonManagerPrivate.addStartupChange(change, aOldAddon.id);
+ if (aOldAddon.bootstrap) {
+ // Update the add-ons active state
+ XPIDatabase.updateAddonActive(aOldAddon, !isDisabled);
+ }
+ else {
+ changed = true;
+ }
+ }
+ }
+
+ if (aOldAddon.visible && aOldAddon.active && aOldAddon.bootstrap) {
+ XPIProvider.bootstrappedAddons[aOldAddon.id] = {
+ version: aOldAddon.version,
+ type: aOldAddon.type,
+ descriptor: aAddonState.descriptor,
+ multiprocessCompatible: aOldAddon.multiprocessCompatible
+ };
+ }
+
+ return changed;
+ }
+
+ /**
+ * Called when an add-on has been removed.
+ *
+ * @param aOldAddon
+ * The AddonInternal as it appeared the last time the application
+ * ran
+ * @return a boolean indicating if flushing caches is required to complete
+ * changing this add-on
+ */
+ function removeMetadata(aOldAddon) {
+ // This add-on has disappeared
+ logger.debug("Add-on " + aOldAddon.id + " removed from " + aOldAddon.location);
+ XPIDatabase.removeAddonMetadata(aOldAddon);
+
+ // Remember add-ons that were uninstalled during startup
+ if (aOldAddon.visible) {
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED,
+ aOldAddon.id);
+ }
+ else if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
+ .indexOf(aOldAddon.id) != -1) {
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
+ aOldAddon.id);
+ }
+
+ if (aOldAddon.active) {
+ // Enable the default theme if the previously active theme has been
+ // removed
+ if (aOldAddon.type == "theme")
+ XPIProvider.enableDefaultTheme();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Called to add the metadata for an add-on in one of the install locations
+ * to the database. This can be called in three different cases. Either an
+ * add-on has been dropped into the location from outside of Firefox, or
+ * an add-on has been installed through the application, or the database
+ * has been upgraded or become corrupt and add-on data has to be reloaded
+ * into it.
+ *
+ * @param aInstallLocation
+ * The install location containing the add-on
+ * @param aId
+ * The ID of the add-on
+ * @param aAddonState
+ * The new state of the add-on
+ * @param aMigrateData
+ * If during startup the database had to be upgraded this will
+ * contain data that used to be held about this add-on
+ * @return a boolean indicating if flushing caches is required to complete
+ * changing this add-on
+ */
+ function addMetadata(aInstallLocation, aId, aAddonState, aMigrateData) {
+ logger.debug("New add-on " + aId + " installed in " + aInstallLocation.name);
+
+ let newAddon = null;
+ let sameVersion = false;
+ // Check the updated manifests lists for the install location, If there
+ // is no manifest for the add-on ID then newAddon will be undefined
+ if (aInstallLocation.name in aManifests)
+ newAddon = aManifests[aInstallLocation.name][aId];
+
+ // If we had staged data for this add-on or we aren't recovering from a
+ // corrupt database and we don't have migration data for this add-on then
+ // this must be a new install.
+ let isNewInstall = (!!newAddon) || (!XPIDatabase.activeBundles && !aMigrateData);
+
+ // If it's a new install and we haven't yet loaded the manifest then it
+ // must be something dropped directly into the install location
+ let isDetectedInstall = isNewInstall && !newAddon;
+
+ // Load the manifest if necessary and sanity check the add-on ID
+ try {
+ if (!newAddon) {
+ // Load the manifest from the add-on.
+ let file = aInstallLocation.getLocationForID(aId);
+ newAddon = loadManifestFromFile(file);
+ }
+ // The add-on in the manifest should match the add-on ID.
+ if (newAddon.id != aId) {
+ throw new Error("Invalid addon ID: expected addon ID " + aId +
+ ", found " + newAddon.id + " in manifest");
+ }
+ }
+ catch (e) {
+ logger.warn("addMetadata: Add-on " + aId + " is invalid", e);
+
+ // Remove the invalid add-on from the install location if the install
+ // location isn't locked, no restart will be necessary
+ if (!aInstallLocation.locked)
+ aInstallLocation.uninstallAddon(aId);
+ else
+ logger.warn("Could not uninstall invalid item from locked install location");
+ return false;
+ }
+
+ // Update the AddonInternal properties.
+ newAddon._installLocation = aInstallLocation;
+ newAddon.visible = !(newAddon.id in visibleAddons);
+ newAddon.installDate = aAddonState.mtime;
+ newAddon.updateDate = aAddonState.mtime;
+ newAddon.foreignInstall = isDetectedInstall;
+
+ if (aMigrateData) {
+ // If there is migration data then apply it.
+ logger.debug("Migrating data from old database");
+
+ DB_MIGRATE_METADATA.forEach(function(aProp) {
+ // A theme's disabled state is determined by the selected theme
+ // preference which is read in loadManifestFromRDF
+ if (aProp == "userDisabled" && newAddon.type == "theme")
+ return;
+
+ if (aProp in aMigrateData)
+ newAddon[aProp] = aMigrateData[aProp];
+ });
+
+ // Force all non-profile add-ons to be foreignInstalls since they can't
+ // have been installed through the API
+ newAddon.foreignInstall |= aInstallLocation.name != KEY_APP_PROFILE;
+
+ // Some properties should only be migrated if the add-on hasn't changed.
+ // The version property isn't a perfect check for this but covers the
+ // vast majority of cases.
+ if (aMigrateData.version == newAddon.version) {
+ logger.debug("Migrating compatibility info");
+ sameVersion = true;
+ if ("targetApplications" in aMigrateData)
+ newAddon.applyCompatibilityUpdate(aMigrateData, true);
+ }
+
+ // Since the DB schema has changed make sure softDisabled is correct
+ applyBlocklistChanges(newAddon, newAddon, aOldAppVersion,
+ aOldPlatformVersion);
+ }
+
+ // The default theme is never a foreign install
+ if (newAddon.type == "theme" && newAddon.internalName == XPIProvider.defaultSkin)
+ newAddon.foreignInstall = false;
+
+ if (isDetectedInstall && newAddon.foreignInstall) {
+ // If the add-on is a foreign install and is in a scope where add-ons
+ // that were dropped in should default to disabled then disable it
+ let disablingScopes = Preferences.get(PREF_EM_AUTO_DISABLED_SCOPES, 0);
+ if (aInstallLocation.scope & disablingScopes) {
+ logger.warn("Disabling foreign installed add-on " + newAddon.id + " in "
+ + aInstallLocation.name);
+ newAddon.userDisabled = true;
+ }
+ }
+
+ // If we have a list of what add-ons should be marked as active then use
+ // it to guess at migration data.
+ if (!isNewInstall && XPIDatabase.activeBundles) {
+ // For themes we know which is active by the current skin setting
+ if (newAddon.type == "theme")
+ newAddon.active = newAddon.internalName == XPIProvider.currentSkin;
+ else
+ newAddon.active = XPIDatabase.activeBundles.indexOf(aAddonState.descriptor) != -1;
+
+ // If the add-on wasn't active and it isn't already disabled in some way
+ // then it was probably either softDisabled or userDisabled
+ if (!newAddon.active && newAddon.visible && !newAddon.disabled) {
+ // If the add-on is softblocked then assume it is softDisabled
+ if (newAddon.blocklistState == Blocklist.STATE_SOFTBLOCKED)
+ newAddon.softDisabled = true;
+ else
+ newAddon.userDisabled = true;
+ }
+ }
+ else {
+ newAddon.active = (newAddon.visible && !newAddon.disabled);
+ }
+
+ let newDBAddon = XPIDatabase.addAddonMetadata(newAddon, aAddonState.descriptor);
+
+ if (newDBAddon.visible) {
+ // Remember add-ons that were first detected during startup.
+ if (isDetectedInstall) {
+ // If a copy from a higher priority location was removed then this
+ // add-on has changed
+ if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_UNINSTALLED)
+ .indexOf(newDBAddon.id) != -1) {
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
+ newDBAddon.id);
+ }
+ else {
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_INSTALLED,
+ newDBAddon.id);
+ }
+ }
+
+ // Note if any visible add-on is not in the application install location
+ if (newDBAddon._installLocation.name != KEY_APP_GLOBAL)
+ XPIProvider.allAppGlobal = false;
+
+ visibleAddons[newDBAddon.id] = newDBAddon;
+
+ let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL;
+ let extraParams = {};
+
+ // Copy add-on details (enabled, bootstrap, version, etc) to XPIState.
+ aAddonState.syncWithDB(newDBAddon);
+
+ // If we're hiding a bootstrapped add-on then call its uninstall method
+ if (newDBAddon.id in oldBootstrappedAddons) {
+ let oldBootstrap = oldBootstrappedAddons[newDBAddon.id];
+ extraParams.oldVersion = oldBootstrap.version;
+ XPIProvider.bootstrappedAddons[newDBAddon.id] = oldBootstrap;
+
+ // If the old version is the same as the new version, or we're
+ // recovering from a corrupt DB, don't call uninstall and install
+ // methods.
+ if (sameVersion || !isNewInstall) {
+ logger.debug("addMetadata: early return, sameVersion " + sameVersion
+ + ", isNewInstall " + isNewInstall);
+ return false;
+ }
+
+ installReason = Services.vc.compare(oldBootstrap.version, newDBAddon.version) < 0 ?
+ BOOTSTRAP_REASONS.ADDON_UPGRADE :
+ BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
+
+ let oldAddonFile = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsIFile);
+ oldAddonFile.persistentDescriptor = oldBootstrap.descriptor;
+
+ XPIProvider.callBootstrapMethod(createAddonDetails(newDBAddon.id, oldBootstrap),
+ oldAddonFile, "uninstall", installReason,
+ { newVersion: newDBAddon.version });
+
+ XPIProvider.unloadBootstrapScope(newDBAddon.id);
+
+ // If the new add-on is bootstrapped then we must flush the caches
+ // before calling the new bootstrap script
+ if (newDBAddon.bootstrap)
+ flushStartupCache();
+ }
+
+ if (!newDBAddon.bootstrap)
+ return true;
+
+ // Visible bootstrapped add-ons need to have their install method called
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.persistentDescriptor = aAddonState.descriptor;
+ XPIProvider.callBootstrapMethod(newDBAddon, file,
+ "install", installReason, extraParams);
+ if (!newDBAddon.active)
+ XPIProvider.unloadBootstrapScope(newDBAddon.id);
+ }
+
+ return false;
+ }
+
+ let changed = false;
+
+ // Get all the add-ons in the existing DB and Map them into Sets by install location
+ let allDBAddons = new Map();
+ for (let a of XPIDatabase.getAddons()) {
+ let locationSet = allDBAddons.get(a.location);
+ if (!locationSet) {
+ locationSet = new Set();
+ allDBAddons.set(a.location, locationSet);
+ }
+ locationSet.add(a);
+ }
+
+ for (let installLocation of this.installLocations) {
+ // Get all the on-disk XPI states for this location, and keep track of which
+ // ones we see in the database.
+ let states = XPIStates.getLocation(installLocation.name);
+ let seen = new Set();
+ // Iterate through the add-ons installed the last time the application
+ // ran
+ let dbAddons = allDBAddons.get(installLocation.name);
+ if (dbAddons) {
+ // we've processed this location
+ allDBAddons.delete(installLocation.name);
+
+ logger.debug("processFileChanges reconciling DB for location ${l} state ${s} db ${d}",
+ {l: installLocation.name, s: states, d: [for (a of dbAddons) a.id]});
+ for (let aOldAddon of dbAddons) {
+ // If a version of this add-on has been installed in an higher
+ // priority install location then count it as changed
+ if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
+ .indexOf(aOldAddon.id) != -1) {
+ AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
+ aOldAddon.id);
+ }
+
+ // Check if the add-on is still installed
+ let xpiState = states && states.get(aOldAddon.id);
+ if (xpiState) {
+ // in this block, the add-on is in both XPIStates and the DB
+ seen.add(xpiState);
+
+ // The add-on has changed if the modification time has changed, or
+ // we have an updated manifest for it. Also reload the metadata for
+ // add-ons in the application directory when the application version
+ // has changed
+ if (aOldAddon.id in aManifests[installLocation.name] ||
+ aOldAddon.updateDate != xpiState.mtime ||
+ (aUpdateCompatibility && installLocation.name == KEY_APP_GLOBAL)) {
+ changed = updateMetadata(installLocation, aOldAddon, xpiState) ||
+ changed;
+ }
+ else if (aOldAddon.descriptor != xpiState.descriptor) {
+ changed = updateDescriptor(installLocation, aOldAddon, xpiState) ||
+ changed;
+ }
+ else {
+ changed = updateMetadata(installLocation, aOldAddon, xpiState) ||
+ changed;
+
+ changed = updateVisibilityAndCompatibility(installLocation,
+ aOldAddon, xpiState) ||
+ changed;
+ }
+ if (aOldAddon.visible && aOldAddon._installLocation.name != KEY_APP_GLOBAL)
+ XPIProvider.allAppGlobal = false;
+ // Copy add-on details (enabled, bootstrap, version, etc) to XPIState.
+ xpiState.syncWithDB(aOldAddon);
+ }
+ else {
+ // The add-on is in the DB, but not in xpiState (and thus not on disk).
+ changed = removeMetadata(aOldAddon) || changed;
+ }
+ }
+ }
+
+ // Any add-on in our current location that we haven't seen needs to
+ // be added to the database.
+ // Get the migration data for this install location so we can include that as
+ // we add, in case this is a database upgrade or rebuild.
+ let locMigrateData = {};
+ if (XPIDatabase.migrateData && installLocation.name in XPIDatabase.migrateData)
+ locMigrateData = XPIDatabase.migrateData[installLocation.name];
+ if (states) {
+ for (let [id, xpiState] of states) {
+ if (!seen.has(xpiState)) {
+ changed = addMetadata(installLocation, id, xpiState,
+ (locMigrateData[id] || null)) || changed;
+ }
+ }
+ }
+ }
+
+ // Anything left in allDBAddons is a location where the database contains add-ons,
+ // but the browser is no longer configured to use that location. The metadata for those
+ // add-ons must be removed from the database.
+ for (let [locationName, addons] of allDBAddons) {
+ logger.debug("Removing orphaned DB add-on entries from " + locationName);
+ for (let a of addons) {
+ logger.debug("Remove ${location}:${id}", a);
+ changed = removeMetadata(a) || changed;
+ }
+ }
+
+ XPIStates.save();
+ this.persistBootstrappedAddons();
+
+ // Clear out any cached migration data.
+ XPIDatabase.migrateData = null;
+
+ return changed;
+ },
+
+ /**
+ * Imports the xpinstall permissions from preferences into the permissions
+ * manager for the user to change later.
+ */
+ importPermissions: function XPI_importPermissions() {
+ PermissionsUtils.importFromPrefs(PREF_XPI_PERMISSIONS_BRANCH,
+ XPI_PERMISSION);
+ },
+
+ /**
+ * Checks for any changes that have occurred since the last time the
+ * application was launched.
+ *
+ * @param aAppChanged
+ * A tri-state value. Undefined means the current profile was created
+ * for this session, true means the profile already existed but was
+ * last used with an application with a different version number,
+ * false means that the profile was last used by this version of the
+ * application.
+ * @param aOldAppVersion
+ * The version of the application last run with this profile or null
+ * if it is a new profile or the version is unknown
+ * @param aOldPlatformVersion
+ * The version of the platform last run with this profile or null
+ * if it is a new profile or the version is unknown
+ * @return true if a change requiring a restart was detected
+ */
+ checkForChanges: function XPI_checkForChanges(aAppChanged, aOldAppVersion,
+ aOldPlatformVersion) {
+ logger.debug("checkForChanges");
+
+ // Keep track of whether and why we need to open and update the database at
+ // startup time.
+ let updateReasons = [];
+ if (aAppChanged) {
+ updateReasons.push("appChanged");
+ }
+
+ // Load the list of bootstrapped add-ons first so processFileChanges can
+ // modify it
+ // XXX eventually get rid of bootstrappedAddons
+ try {
+ this.bootstrappedAddons = JSON.parse(Preferences.get(PREF_BOOTSTRAP_ADDONS,
+ "{}"));
+ } catch (e) {
+ logger.warn("Error parsing enabled bootstrapped extensions cache", e);
+ }
+
+ // First install any new add-ons into the locations, if there are any
+ // changes then we must update the database with the information in the
+ // install locations
+ let manifests = {};
+ let updated = this.processPendingFileChanges(manifests);
+ if (updated) {
+ updateReasons.push("pendingFileChanges");
+ }
+
+ // This will be true if the previous session made changes that affect the
+ // active state of add-ons but didn't commit them properly (normally due
+ // to the application crashing)
+ let hasPendingChanges = Preferences.get(PREF_PENDING_OPERATIONS);
+ if (hasPendingChanges) {
+ updateReasons.push("hasPendingChanges");
+ }
+
+ // If the application has changed then check for new distribution add-ons
+ if (aAppChanged !== false &&
+ Preferences.get(PREF_INSTALL_DISTRO_ADDONS, true))
+ {
+ updated = this.installDistributionAddons(manifests);
+ if (updated) {
+ updateReasons.push("installDistributionAddons");
+ }
+ }
+
+ // Telemetry probe added around getInstallState() to check perf
+ let telemetryCaptureTime = Cu.now();
+ let installChanged = XPIStates.getInstallState();
+ let telemetry = Services.telemetry;
+ telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Math.round(Cu.now() - telemetryCaptureTime));
+ if (installChanged) {
+ updateReasons.push("directoryState");
+ }
+
+ let haveAnyAddons = (XPIStates.size > 0);
+
+ // If the schema appears to have changed then we should update the database
+ if (DB_SCHEMA != Preferences.get(PREF_DB_SCHEMA, 0)) {
+ // If we don't have any add-ons, just update the pref, since we don't need to
+ // write the database
+ if (!haveAnyAddons) {
+ logger.debug("Empty XPI database, setting schema version preference to " + DB_SCHEMA);
+ Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA);
+ }
+ else {
+ updateReasons.push("schemaChanged");
+ }
+ }
+
+ // If the database doesn't exist and there are add-ons installed then we
+ // must update the database however if there are no add-ons then there is
+ // no need to update the database.
+ let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
+ if (!dbFile.exists() && haveAnyAddons) {
+ updateReasons.push("needNewDatabase");
+ }
+
+ // XXX This will go away when we fold bootstrappedAddons into XPIStates.
+ if (updateReasons.length == 0) {
+ let bootstrapDescriptors = new Set([for (b of Object.keys(this.bootstrappedAddons))
+ this.bootstrappedAddons[b].descriptor]);
+
+ for (let location of XPIStates.db.values()) {
+ for (let state of location.values()) {
+ bootstrapDescriptors.delete(state.descriptor);
+ }
+ }
+
+ if (bootstrapDescriptors.size > 0) {
+ logger.warn("Bootstrap state is invalid (missing add-ons: "
+ + [for (b of bootstrapDescriptors) b] + ")");
+ updateReasons.push("missingBootstrapAddon");
+ }
+ }
+
+ // Catch and log any errors during the main startup
+ try {
+ let extensionListChanged = false;
+ // If the database needs to be updated then open it and then update it
+ // from the filesystem
+ if (updateReasons.length > 0) {
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_load_reasons", updateReasons);
+ XPIDatabase.syncLoadDB(false);
+ try {
+ extensionListChanged = this.processFileChanges(manifests,
+ aAppChanged,
+ aOldAppVersion,
+ aOldPlatformVersion);
+ }
+ catch (e) {
+ logger.error("Failed to process extension changes at startup", e);
+ }
+ }
+
+ if (aAppChanged) {
+ // When upgrading the app and using a custom skin make sure it is still
+ // compatible otherwise switch back the default
+ if (this.currentSkin != this.defaultSkin) {
+ let oldSkin = XPIDatabase.getVisibleAddonForInternalName(this.currentSkin);
+ if (!oldSkin || oldSkin.disabled)
+ this.enableDefaultTheme();
+ }
+
+ // When upgrading remove the old extensions cache to force older
+ // versions to rescan the entire list of extensions
+ try {
+ let oldCache = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_CACHE], true);
+ if (oldCache.exists())
+ oldCache.remove(true);
+ }
+ catch (e) {
+ logger.warn("Unable to remove old extension cache " + oldCache.path, e);
+ }
+ }
+
+ // If the application crashed before completing any pending operations then
+ // we should perform them now.
+ if (extensionListChanged || hasPendingChanges) {
+ logger.debug("Updating database with changes to installed add-ons");
+ XPIDatabase.updateActiveAddons();
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
+ !XPIDatabase.writeAddonsList());
+ Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
+ JSON.stringify(this.bootstrappedAddons));
+ return true;
+ }
+
+ logger.debug("No changes found");
+ }
+ catch (e) {
+ logger.error("Error during startup file checks", e);
+ }
+
+ // Check that the add-ons list still exists
+ let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
+ true);
+ // the addons list file should exist if and only if we have add-ons installed
+ if (addonsList.exists() != haveAnyAddons) {
+ logger.debug("Add-ons list is invalid, rebuilding");
+ XPIDatabase.writeAddonsList();
+ }
+
+ return false;
+ },
+
+ /**
+ * Called to test whether this provider supports installing a particular
+ * mimetype.
+ *
+ * @param aMimetype
+ * The mimetype to check for
+ * @return true if the mimetype is application/x-xpinstall
+ */
+ supportsMimetype: function XPI_supportsMimetype(aMimetype) {
+ return aMimetype == "application/x-xpinstall";
+ },
+
+ /**
+ * Called to test whether installing XPI add-ons is enabled.
+ *
+ * @return true if installing is enabled
+ */
+ isInstallEnabled: function XPI_isInstallEnabled() {
+ // Default to enabled if the preference does not exist
+ return Preferences.get(PREF_XPI_ENABLED, true);
+ },
+
+ /**
+ * Called to test whether installing XPI add-ons by direct URL requests is
+ * whitelisted.
+ *
+ * @return true if installing by direct requests is whitelisted
+ */
+ isDirectRequestWhitelisted: function XPI_isDirectRequestWhitelisted() {
+ // Default to whitelisted if the preference does not exist.
+ return Preferences.get(PREF_XPI_DIRECT_WHITELISTED, true);
+ },
+
+ /**
+ * Called to test whether installing XPI add-ons from file referrers is
+ * whitelisted.
+ *
+ * @return true if installing from file referrers is whitelisted
+ */
+ isFileRequestWhitelisted: function XPI_isFileRequestWhitelisted() {
+ // Default to whitelisted if the preference does not exist.
+ return Preferences.get(PREF_XPI_FILE_WHITELISTED, true);
+ },
+
+ /**
+ * Called to test whether installing XPI add-ons from a URI is allowed.
+ *
+ * @param aInstallingPrincipal
+ * The nsIPrincipal that initiated the install
+ * @return true if installing is allowed
+ */
+ isInstallAllowed: function XPI_isInstallAllowed(aInstallingPrincipal) {
+ if (!this.isInstallEnabled())
+ return false;
+
+ let uri = aInstallingPrincipal.URI;
+
+ // Direct requests without a referrer are either whitelisted or blocked.
+ if (!uri)
+ return this.isDirectRequestWhitelisted();
+
+ // Local referrers can be whitelisted.
+ if (this.isFileRequestWhitelisted() &&
+ (uri.schemeIs("chrome") || uri.schemeIs("file")))
+ return true;
+
+ this.importPermissions();
+
+ let permission = Services.perms.testPermissionFromPrincipal(aInstallingPrincipal, XPI_PERMISSION);
+ if (permission == Ci.nsIPermissionManager.DENY_ACTION)
+ return false;
+
+ let requireWhitelist = Preferences.get(PREF_XPI_WHITELIST_REQUIRED, true);
+ if (requireWhitelist && (permission != Ci.nsIPermissionManager.ALLOW_ACTION))
+ return false;
+
+ let requireSecureOrigin = Preferences.get(PREF_INSTALL_REQUIRESECUREORIGIN, true);
+ let safeSchemes = ["https", "chrome", "file"];
+ if (requireSecureOrigin && safeSchemes.indexOf(uri.scheme) == -1)
+ return false;
+
+ return true;
+ },
+
+ /**
+ * Called to get an AddonInstall to download and install an add-on from a URL.
+ *
+ * @param aUrl
+ * The URL to be installed
+ * @param aHash
+ * A hash for the install
+ * @param aName
+ * A name for the install
+ * @param aIcons
+ * Icon URLs for the install
+ * @param aVersion
+ * A version for the install
+ * @param aBrowser
+ * The browser performing the install
+ * @param aCallback
+ * A callback to pass the AddonInstall to
+ */
+ getInstallForURL: function XPI_getInstallForURL(aUrl, aHash, aName, aIcons,
+ aVersion, aBrowser, aCallback) {
+ AddonInstall.createDownload(function getInstallForURL_createDownload(aInstall) {
+ aCallback(aInstall.wrapper);
+ }, aUrl, aHash, aName, aIcons, aVersion, aBrowser);
+ },
+
+ /**
+ * Called to get an AddonInstall to install an add-on from a local file.
+ *
+ * @param aFile
+ * The file to be installed
+ * @param aCallback
+ * A callback to pass the AddonInstall to
+ */
+ getInstallForFile: function XPI_getInstallForFile(aFile, aCallback) {
+ AddonInstall.createInstall(function getInstallForFile_createInstall(aInstall) {
+ if (aInstall)
+ aCallback(aInstall.wrapper);
+ else
+ aCallback(null);
+ }, aFile);
+ },
+
+ /**
+ * Removes an AddonInstall from the list of active installs.
+ *
+ * @param install
+ * The AddonInstall to remove
+ */
+ removeActiveInstall: function XPI_removeActiveInstall(aInstall) {
+ let where = this.installs.indexOf(aInstall);
+ if (where == -1) {
+ logger.warn("removeActiveInstall: could not find active install for "
+ + aInstall.sourceURI.spec);
+ return;
+ }
+ this.installs.splice(where, 1);
+ },
+
+ /**
+ * Called to get an Addon with a particular ID.
+ *
+ * @param aId
+ * The ID of the add-on to retrieve
+ * @param aCallback
+ * A callback to pass the Addon to
+ */
+ getAddonByID: function XPI_getAddonByID(aId, aCallback) {
+ XPIDatabase.getVisibleAddonForID (aId, function getAddonByID_getVisibleAddonForID(aAddon) {
+ aCallback(createWrapper(aAddon));
+ });
+ },
+
+ /**
+ * Called to get Addons of a particular type.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types.
+ * @param aCallback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsByTypes: function XPI_getAddonsByTypes(aTypes, aCallback) {
+ XPIDatabase.getVisibleAddons(aTypes, function getAddonsByTypes_getVisibleAddons(aAddons) {
+ // Tycho: aCallback([createWrapper(a) for each (a in aAddons)]);
+ let result = [];
+ for each(let a in aAddons) {
+ result.push(createWrapper(a));
+ }
+ aCallback(result);
+ });
+ },
+
+ /**
+ * Obtain an Addon having the specified Sync GUID.
+ *
+ * @param aGUID
+ * String GUID of add-on to retrieve
+ * @param aCallback
+ * A callback to pass the Addon to. Receives null if not found.
+ */
+ getAddonBySyncGUID: function XPI_getAddonBySyncGUID(aGUID, aCallback) {
+ XPIDatabase.getAddonBySyncGUID(aGUID, function getAddonBySyncGUID_getAddonBySyncGUID(aAddon) {
+ aCallback(createWrapper(aAddon));
+ });
+ },
+
+ /**
+ * Called to get Addons that have pending operations.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types
+ * @param aCallback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsWithOperationsByTypes:
+ function XPI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
+ XPIDatabase.getVisibleAddonsWithPendingOperations(aTypes,
+ function getAddonsWithOpsByTypes_getVisibleAddonsWithPendingOps(aAddons) {
+ // Tycho: let results = [createWrapper(a) for each (a in aAddons)];
+ let results = [];
+ for each(let a in aAddons) {
+ results.push(createWrapper(a));
+ }
+
+ XPIProvider.installs.forEach(function(aInstall) {
+ if (aInstall.state == AddonManager.STATE_INSTALLED &&
+ !(aInstall.addon.inDatabase))
+ results.push(createWrapper(aInstall.addon));
+ });
+ aCallback(results);
+ });
+ },
+
+ /**
+ * Called to get the current AddonInstalls, optionally limiting to a list of
+ * types.
+ *
+ * @param aTypes
+ * An array of types or null to get all types
+ * @param aCallback
+ * A callback to pass the array of AddonInstalls to
+ */
+ getInstallsByTypes: function XPI_getInstallsByTypes(aTypes, aCallback) {
+ let results = [];
+ this.installs.forEach(function(aInstall) {
+ if (!aTypes || aTypes.indexOf(aInstall.type) >= 0)
+ results.push(aInstall.wrapper);
+ });
+ aCallback(results);
+ },
+
+ /**
+ * Synchronously map a URI to the corresponding Addon ID.
+ *
+ * Mappable URIs are limited to in-application resources belonging to the
+ * add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
+ * but do not include URIs from meta data, such as the add-on homepage.
+ *
+ * @param aURI
+ * nsIURI to map or null
+ * @return string containing the Addon ID
+ * @see AddonManager.mapURIToAddonID
+ * @see amIAddonManager.mapURIToAddonID
+ */
+ mapURIToAddonID: function XPI_mapURIToAddonID(aURI) {
+ let resolved = this._resolveURIToFile(aURI);
+ if (!resolved || !(resolved instanceof Ci.nsIFileURL))
+ return null;
+
+ for (let [id, path] of this._addonFileMap) {
+ if (resolved.file.path.startsWith(path))
+ return id;
+ }
+
+ return null;
+ },
+
+ /**
+ * Called when a new add-on has been enabled when only one add-on of that type
+ * can be enabled.
+ *
+ * @param aId
+ * The ID of the newly enabled add-on
+ * @param aType
+ * The type of the newly enabled add-on
+ * @param aPendingRestart
+ * true if the newly enabled add-on will only become enabled after a
+ * restart
+ */
+ addonChanged: function XPI_addonChanged(aId, aType, aPendingRestart) {
+ // We only care about themes in this provider
+ if (aType != "theme")
+ return;
+
+ if (!aId) {
+ // Fallback to the default theme when no theme was enabled
+ this.enableDefaultTheme();
+ return;
+ }
+
+ // Look for the previously enabled theme and find the internalName of the
+ // currently selected theme
+ let previousTheme = null;
+ let newSkin = this.defaultSkin;
+ let addons = XPIDatabase.getAddonsByType("theme");
+ addons.forEach(function(aTheme) {
+ if (!aTheme.visible)
+ return;
+ if (aTheme.id == aId)
+ newSkin = aTheme.internalName;
+ else if (aTheme.userDisabled == false && !aTheme.pendingUninstall)
+ previousTheme = aTheme;
+ }, this);
+
+ if (aPendingRestart) {
+ Services.prefs.setBoolPref(PREF_DSS_SWITCHPENDING, true);
+ Services.prefs.setCharPref(PREF_DSS_SKIN_TO_SELECT, newSkin);
+ }
+ else if (newSkin == this.currentSkin) {
+ try {
+ Services.prefs.clearUserPref(PREF_DSS_SWITCHPENDING);
+ }
+ catch (e) { }
+ try {
+ Services.prefs.clearUserPref(PREF_DSS_SKIN_TO_SELECT);
+ }
+ catch (e) { }
+ }
+ else {
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, newSkin);
+ this.currentSkin = newSkin;
+ }
+ this.selectedSkin = newSkin;
+
+ // Flush the preferences to disk so they don't get out of sync with the
+ // database
+ Services.prefs.savePrefFile(null);
+
+ // Mark the previous theme as disabled. This won't cause recursion since
+ // only enabled calls notifyAddonChanged.
+ if (previousTheme)
+ this.updateAddonDisabledState(previousTheme, true);
+ },
+
+ /**
+ * Update the appDisabled property for all add-ons.
+ */
+ updateAddonAppDisabledStates: function XPI_updateAddonAppDisabledStates() {
+ let addons = XPIDatabase.getAddons();
+ addons.forEach(function(aAddon) {
+ this.updateAddonDisabledState(aAddon);
+ }, this);
+ },
+
+ /**
+ * Update the repositoryAddon property for all add-ons.
+ *
+ * @param aCallback
+ * Function to call when operation is complete.
+ */
+ updateAddonRepositoryData: function XPI_updateAddonRepositoryData(aCallback) {
+ let self = this;
+ XPIDatabase.getVisibleAddons(null, function UARD_getVisibleAddonsCallback(aAddons) {
+ let pending = aAddons.length;
+ logger.debug("updateAddonRepositoryData found " + pending + " visible add-ons");
+ if (pending == 0) {
+ aCallback();
+ return;
+ }
+
+ function notifyComplete() {
+ if (--pending == 0)
+ aCallback();
+ }
+
+ for (let addon of aAddons) {
+ AddonRepository.getCachedAddonByID(addon.id,
+ function UARD_getCachedAddonCallback(aRepoAddon) {
+ if (aRepoAddon) {
+ logger.debug("updateAddonRepositoryData got info for " + addon.id);
+ addon._repositoryAddon = aRepoAddon;
+ addon.compatibilityOverrides = aRepoAddon.compatibilityOverrides;
+ self.updateAddonDisabledState(addon);
+ }
+
+ notifyComplete();
+ });
+ };
+ });
+ },
+
+ /**
+ * When the previously selected theme is removed this method will be called
+ * to enable the default theme.
+ */
+ enableDefaultTheme: function XPI_enableDefaultTheme() {
+ logger.debug("Activating default theme");
+ let addon = XPIDatabase.getVisibleAddonForInternalName(this.defaultSkin);
+ if (addon) {
+ if (addon.userDisabled) {
+ this.updateAddonDisabledState(addon, false);
+ }
+ else if (!this.extensionsActive) {
+ // During startup we may end up trying to enable the default theme when
+ // the database thinks it is already enabled (see f.e. bug 638847). In
+ // this case just force the theme preferences to be correct
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN,
+ addon.internalName);
+ this.currentSkin = this.selectedSkin = addon.internalName;
+ Preferences.reset(PREF_DSS_SKIN_TO_SELECT);
+ Preferences.reset(PREF_DSS_SWITCHPENDING);
+ }
+ else {
+ logger.warn("Attempting to activate an already active default theme");
+ }
+ }
+ else {
+ logger.warn("Unable to activate the default theme");
+ }
+ },
+
+ onDebugConnectionChange: function(aEvent, aWhat, aConnection) {
+ if (aWhat != "opened")
+ return;
+
+ for (let id of Object.keys(this.bootstrapScopes)) {
+ aConnection.setAddonOptions(id, { global: this.bootstrapScopes[id] });
+ }
+ },
+
+ /**
+ * Notified when a preference we're interested in has changed.
+ *
+ * @see nsIObserver
+ */
+ observe: function XPI_observe(aSubject, aTopic, aData) {
+ if (aTopic == NOTIFICATION_FLUSH_PERMISSIONS) {
+ if (!aData || aData == XPI_PERMISSION) {
+ this.importPermissions();
+ }
+ return;
+ }
+ else if (aTopic == NOTIFICATION_TOOLBOXPROCESS_LOADED) {
+ Services.obs.removeObserver(this, NOTIFICATION_TOOLBOXPROCESS_LOADED, false);
+ this._toolboxProcessLoaded = true;
+ BrowserToolboxProcess.on("connectionchange",
+ this.onDebugConnectionChange.bind(this));
+ }
+
+ if (aTopic == "nsPref:changed") {
+ switch (aData) {
+ case PREF_EM_MIN_COMPAT_APP_VERSION:
+ case PREF_EM_MIN_COMPAT_PLATFORM_VERSION:
+ this.minCompatibleAppVersion = Preferences.get(PREF_EM_MIN_COMPAT_APP_VERSION,
+ null);
+ this.minCompatiblePlatformVersion = Preferences.get(PREF_EM_MIN_COMPAT_PLATFORM_VERSION,
+ null);
+ this.updateAddonAppDisabledStates();
+ break;
+ }
+ }
+ },
+
+ /**
+ * Tests whether enabling an add-on will require a restart.
+ *
+ * @param aAddon
+ * The add-on to test
+ * @return true if the operation requires a restart
+ */
+ enableRequiresRestart: function XPI_enableRequiresRestart(aAddon) {
+ // If the platform couldn't have activated extensions then we can make
+ // changes without any restart.
+ if (!this.extensionsActive)
+ return false;
+
+ // If the application is in safe mode then any change can be made without
+ // restarting
+ if (Services.appinfo.inSafeMode)
+ return false;
+
+ // Anything that is active is already enabled
+ if (aAddon.active)
+ return false;
+
+ if (aAddon.type == "theme") {
+ // If dynamic theme switching is enabled then switching themes does not
+ // require a restart
+ if (Preferences.get(PREF_EM_DSS_ENABLED))
+ return false;
+
+ // If the theme is already the theme in use then no restart is necessary.
+ // This covers the case where the default theme is in use but a
+ // lightweight theme is considered active.
+ return aAddon.internalName != this.currentSkin;
+ }
+
+ return !aAddon.bootstrap;
+ },
+
+ /**
+ * Tests whether disabling an add-on will require a restart.
+ *
+ * @param aAddon
+ * The add-on to test
+ * @return true if the operation requires a restart
+ */
+ disableRequiresRestart: function XPI_disableRequiresRestart(aAddon) {
+ // If the platform couldn't have activated up extensions then we can make
+ // changes without any restart.
+ if (!this.extensionsActive)
+ return false;
+
+ // If the application is in safe mode then any change can be made without
+ // restarting
+ if (Services.appinfo.inSafeMode)
+ return false;
+
+ // Anything that isn't active is already disabled
+ if (!aAddon.active)
+ return false;
+
+ if (aAddon.type == "theme") {
+ // If dynamic theme switching is enabled then switching themes does not
+ // require a restart
+ if (Preferences.get(PREF_EM_DSS_ENABLED))
+ return false;
+
+ // Non-default themes always require a restart to disable since it will
+ // be switching from one theme to another or to the default theme and a
+ // lightweight theme.
+ if (aAddon.internalName != this.defaultSkin)
+ return true;
+
+ // The default theme requires a restart to disable if we are in the
+ // process of switching to a different theme. Note that this makes the
+ // disabled flag of operationsRequiringRestart incorrect for the default
+ // theme (it will be false most of the time). Bug 520124 would be required
+ // to fix it. For the UI this isn't a problem since we never try to
+ // disable or uninstall the default theme.
+ return this.selectedSkin != this.currentSkin;
+ }
+
+ return !aAddon.bootstrap;
+ },
+
+ /**
+ * Tests whether installing an add-on will require a restart.
+ *
+ * @param aAddon
+ * The add-on to test
+ * @return true if the operation requires a restart
+ */
+ installRequiresRestart: function XPI_installRequiresRestart(aAddon) {
+ // If the platform couldn't have activated up extensions then we can make
+ // changes without any restart.
+ if (!this.extensionsActive)
+ return false;
+
+ // If the application is in safe mode then any change can be made without
+ // restarting
+ if (Services.appinfo.inSafeMode)
+ return false;
+
+ // Add-ons that are already installed don't require a restart to install.
+ // This wouldn't normally be called for an already installed add-on (except
+ // for forming the operationsRequiringRestart flags) so is really here as
+ // a safety measure.
+ if (aAddon.inDatabase)
+ return false;
+
+ // If we have an AddonInstall for this add-on then we can see if there is
+ // an existing installed add-on with the same ID
+ if ("_install" in aAddon && aAddon._install) {
+ // If there is an existing installed add-on and uninstalling it would
+ // require a restart then installing the update will also require a
+ // restart
+ let existingAddon = aAddon._install.existingAddon;
+ if (existingAddon && this.uninstallRequiresRestart(existingAddon))
+ return true;
+ }
+
+ // If the add-on is not going to be active after installation then it
+ // doesn't require a restart to install.
+ if (aAddon.disabled)
+ return false;
+
+ // Themes will require a restart (even if dynamic switching is enabled due
+ // to some caching issues) and non-bootstrapped add-ons will require a
+ // restart
+ return aAddon.type == "theme" || !aAddon.bootstrap;
+ },
+
+ /**
+ * Tests whether uninstalling an add-on will require a restart.
+ *
+ * @param aAddon
+ * The add-on to test
+ * @return true if the operation requires a restart
+ */
+ uninstallRequiresRestart: function XPI_uninstallRequiresRestart(aAddon) {
+ // If the platform couldn't have activated up extensions then we can make
+ // changes without any restart.
+ if (!this.extensionsActive)
+ return false;
+
+ // If the application is in safe mode then any change can be made without
+ // restarting
+ if (Services.appinfo.inSafeMode)
+ return false;
+
+ // If the add-on can be disabled without a restart then it can also be
+ // uninstalled without a restart
+ return this.disableRequiresRestart(aAddon);
+ },
+
+ /**
+ * Loads a bootstrapped add-on's bootstrap.js into a sandbox and the reason
+ * values as constants in the scope. This will also add information about the
+ * add-on to the bootstrappedAddons dictionary and notify the crash reporter
+ * that new add-ons have been loaded.
+ *
+ * @param aId
+ * The add-on's ID
+ * @param aFile
+ * The nsIFile for the add-on
+ * @param aVersion
+ * The add-on's version
+ * @param aType
+ * The type for the add-on
+ * @param aMultiprocessCompatible
+ * Boolean indicating whether the add-on is compatible with electrolysis.
+ * @return a JavaScript scope
+ */
+ loadBootstrapScope: function XPI_loadBootstrapScope(aId, aFile, aVersion, aType,
+ aMultiprocessCompatible) {
+ // Mark the add-on as active for the crash reporter before loading
+ this.bootstrappedAddons[aId] = {
+ version: aVersion,
+ type: aType,
+ descriptor: aFile.persistentDescriptor,
+ multiprocessCompatible: aMultiprocessCompatible
+ };
+ this.persistBootstrappedAddons();
+ this.addAddonsToCrashReporter();
+
+ // Locales only contain chrome and can't have bootstrap scripts
+ if (aType == "locale") {
+ this.bootstrapScopes[aId] = null;
+ return;
+ }
+
+ logger.debug("Loading bootstrap scope from " + aFile.path);
+
+ let principal = Cc["@mozilla.org/systemprincipal;1"].
+ createInstance(Ci.nsIPrincipal);
+ if (!aMultiprocessCompatible && Preferences.get(PREF_INTERPOSITION_ENABLED, false)) {
+ let interposition = Cc["@mozilla.org/addons/multiprocess-shims;1"].
+ getService(Ci.nsIAddonInterposition);
+ Cu.setAddonInterposition(aId, interposition);
+ }
+
+ if (!aFile.exists()) {
+ this.bootstrapScopes[aId] =
+ new Cu.Sandbox(principal, { sandboxName: aFile.path,
+ wantGlobalProperties: ["indexedDB"],
+ addonId: aId,
+ metadata: { addonID: aId } });
+ logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
+ return;
+ }
+
+ let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
+ if (aType == "dictionary")
+ uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
+
+ this.bootstrapScopes[aId] =
+ new Cu.Sandbox(principal, { sandboxName: uri,
+ wantGlobalProperties: ["indexedDB"],
+ addonId: aId,
+ metadata: { addonID: aId, URI: uri } });
+
+ let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ createInstance(Ci.mozIJSSubScriptLoader);
+
+ try {
+ // Copy the reason values from the global object into the bootstrap scope.
+ for (let name in BOOTSTRAP_REASONS)
+ this.bootstrapScopes[aId][name] = BOOTSTRAP_REASONS[name];
+
+ // Add other stuff that extensions want.
+ const features = [ "Worker", "ChromeWorker" ];
+
+ for (let feature of features)
+ this.bootstrapScopes[aId][feature] = gGlobalScope[feature];
+
+ // Define a console for the add-on
+ this.bootstrapScopes[aId]["console"] = new ConsoleAPI({ consoleID: "addon/" + aId });
+
+ // As we don't want our caller to control the JS version used for the
+ // bootstrap file, we run loadSubScript within the context of the
+ // sandbox with the latest JS version set explicitly.
+ this.bootstrapScopes[aId].__SCRIPT_URI_SPEC__ = uri;
+ Components.utils.evalInSandbox(
+ "Components.classes['@mozilla.org/moz/jssubscript-loader;1'] \
+ .createInstance(Components.interfaces.mozIJSSubScriptLoader) \
+ .loadSubScript(__SCRIPT_URI_SPEC__);", this.bootstrapScopes[aId], "ECMAv5");
+ }
+ catch (e) {
+ logger.warn("Error loading bootstrap.js for " + aId, e);
+ }
+
+ // Only access BrowserToolboxProcess if ToolboxProcess.jsm has been
+ // initialized as otherwise, when it will be initialized, all addons'
+ // globals will be added anyways
+ if (this._toolboxProcessLoaded) {
+ BrowserToolboxProcess.setAddonOptions(aId, { global: this.bootstrapScopes[aId] });
+ }
+ },
+
+ /**
+ * Unloads a bootstrap scope by dropping all references to it and then
+ * updating the list of active add-ons with the crash reporter.
+ *
+ * @param aId
+ * The add-on's ID
+ */
+ unloadBootstrapScope: function XPI_unloadBootstrapScope(aId) {
+ // In case the add-on was not multiprocess-compatible, deregister
+ // any interpositions for it.
+ Cu.setAddonInterposition(aId, null);
+
+ delete this.bootstrapScopes[aId];
+ delete this.bootstrappedAddons[aId];
+ this.persistBootstrappedAddons();
+ this.addAddonsToCrashReporter();
+
+ // Only access BrowserToolboxProcess if ToolboxProcess.jsm has been
+ // initialized as otherwise, there won't be any addon globals added to it
+ if (this._toolboxProcessLoaded) {
+ BrowserToolboxProcess.setAddonOptions(aId, { global: null });
+ }
+ },
+
+ /**
+ * Calls a bootstrap method for an add-on.
+ *
+ * @param aAddon
+ * An object representing the add-on, with `id`, `type` and `version`
+ * @param aFile
+ * The nsIFile for the add-on
+ * @param aMethod
+ * The name of the bootstrap method to call
+ * @param aReason
+ * The reason flag to pass to the bootstrap's startup method
+ * @param aExtraParams
+ * An object of additional key/value pairs to pass to the method in
+ * the params argument
+ */
+ callBootstrapMethod: function XPI_callBootstrapMethod(aAddon, aFile, aMethod, aReason, aExtraParams) {
+ // Never call any bootstrap methods in safe mode
+ if (Services.appinfo.inSafeMode)
+ return;
+
+ if (!aAddon.id || !aAddon.version || !aAddon.type) {
+ logger.error(new Error("aAddon must include an id, version, and type"));
+ return;
+ }
+
+ let timeStart = new Date();
+ if (aMethod == "startup") {
+ logger.debug("Registering manifest for " + aFile.path);
+ Components.manager.addBootstrappedManifestLocation(aFile);
+ }
+
+ try {
+ // Load the scope if it hasn't already been loaded
+ if (!(aAddon.id in this.bootstrapScopes))
+ this.loadBootstrapScope(aAddon.id, aFile, aAddon.version, aAddon.type,
+ aAddon.multiprocessCompatible || false);
+
+ // Nothing to call for locales
+ if (aAddon.type == "locale")
+ return;
+
+ if (!(aMethod in this.bootstrapScopes[aAddon.id])) {
+ logger.warn("Add-on " + aAddon.id + " is missing bootstrap method " + aMethod);
+ return;
+ }
+
+ let params = {
+ id: aAddon.id,
+ version: aAddon.version,
+ installPath: aFile.clone(),
+ resourceURI: getURIForResourceInFile(aFile, "")
+ };
+
+ if (aExtraParams) {
+ for (let key in aExtraParams) {
+ params[key] = aExtraParams[key];
+ }
+ }
+
+ logger.debug("Calling bootstrap method " + aMethod + " on " + aAddon.id + " version " +
+ aAddon.version);
+ try {
+ this.bootstrapScopes[aAddon.id][aMethod](params, aReason);
+ }
+ catch (e) {
+ logger.warn("Exception running bootstrap method " + aMethod + " on " + aAddon.id, e);
+ }
+ }
+ finally {
+ if (aMethod == "shutdown" && aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
+ logger.debug("Removing manifest for " + aFile.path);
+ Components.manager.removeBootstrappedManifestLocation(aFile);
+
+ let manifest = getURIForResourceInFile(aFile, "chrome.manifest");
+ for (let line of ChromeManifestParser.parseSync(manifest)) {
+ if (line.type == "resource") {
+ ResProtocolHandler.setSubstitution(line.args[0], null);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Updates the disabled state for an add-on. Its appDisabled property will be
+ * calculated and if the add-on is changed the database will be saved and
+ * appropriate notifications will be sent out to the registered AddonListeners.
+ *
+ * @param aAddon
+ * The DBAddonInternal to update
+ * @param aUserDisabled
+ * Value for the userDisabled property. If undefined the value will
+ * not change
+ * @param aSoftDisabled
+ * Value for the softDisabled property. If undefined the value will
+ * not change. If true this will force userDisabled to be true
+ * @throws if addon is not a DBAddonInternal
+ */
+ updateAddonDisabledState: function XPI_updateAddonDisabledState(aAddon,
+ aUserDisabled,
+ aSoftDisabled) {
+ if (!(aAddon.inDatabase))
+ throw new Error("Can only update addon states for installed addons.");
+ if (aUserDisabled !== undefined && aSoftDisabled !== undefined) {
+ throw new Error("Cannot change userDisabled and softDisabled at the " +
+ "same time");
+ }
+
+ if (aUserDisabled === undefined) {
+ aUserDisabled = aAddon.userDisabled;
+ }
+ else if (!aUserDisabled) {
+ // If enabling the add-on then remove softDisabled
+ aSoftDisabled = false;
+ }
+
+ // If not changing softDisabled or the add-on is already userDisabled then
+ // use the existing value for softDisabled
+ if (aSoftDisabled === undefined || aUserDisabled)
+ aSoftDisabled = aAddon.softDisabled;
+
+ let appDisabled = !isUsableAddon(aAddon);
+ // No change means nothing to do here
+ if (aAddon.userDisabled == aUserDisabled &&
+ aAddon.appDisabled == appDisabled &&
+ aAddon.softDisabled == aSoftDisabled)
+ return;
+
+ let wasDisabled = aAddon.disabled;
+ let isDisabled = aUserDisabled || aSoftDisabled || appDisabled;
+
+ // If appDisabled changes but addon.disabled doesn't,
+ // no onDisabling/onEnabling is sent - so send a onPropertyChanged.
+ let appDisabledChanged = aAddon.appDisabled != appDisabled;
+
+ // Update the properties in the database.
+ // We never persist this for experiments because the disabled flags
+ // are controlled by the Experiments Manager.
+ if (aAddon.type != "experiment") {
+ XPIDatabase.setAddonProperties(aAddon, {
+ userDisabled: aUserDisabled,
+ appDisabled: appDisabled,
+ softDisabled: aSoftDisabled
+ });
+ }
+
+ if (appDisabledChanged) {
+ AddonManagerPrivate.callAddonListeners("onPropertyChanged",
+ aAddon,
+ ["appDisabled"]);
+ }
+
+ // If the add-on is not visible or the add-on is not changing state then
+ // there is no need to do anything else
+ if (!aAddon.visible || (wasDisabled == isDisabled))
+ return;
+
+ // Flag that active states in the database need to be updated on shutdown
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
+
+ let wrapper = createWrapper(aAddon);
+ // Have we just gone back to the current state?
+ if (isDisabled != aAddon.active) {
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled", wrapper);
+ }
+ else {
+ if (isDisabled) {
+ var needsRestart = this.disableRequiresRestart(aAddon);
+ AddonManagerPrivate.callAddonListeners("onDisabling", wrapper,
+ needsRestart);
+ }
+ else {
+ needsRestart = this.enableRequiresRestart(aAddon);
+ AddonManagerPrivate.callAddonListeners("onEnabling", wrapper,
+ needsRestart);
+ }
+
+ if (!needsRestart) {
+ XPIDatabase.updateAddonActive(aAddon, !isDisabled);
+ if (isDisabled) {
+ if (aAddon.bootstrap) {
+ let file = aAddon._installLocation.getLocationForID(aAddon.id);
+ this.callBootstrapMethod(aAddon, file, "shutdown",
+ BOOTSTRAP_REASONS.ADDON_DISABLE);
+ this.unloadBootstrapScope(aAddon.id);
+ }
+ AddonManagerPrivate.callAddonListeners("onDisabled", wrapper);
+ }
+ else {
+ if (aAddon.bootstrap) {
+ let file = aAddon._installLocation.getLocationForID(aAddon.id);
+ this.callBootstrapMethod(aAddon, file, "startup",
+ BOOTSTRAP_REASONS.ADDON_ENABLE);
+ }
+ AddonManagerPrivate.callAddonListeners("onEnabled", wrapper);
+ }
+ }
+ }
+
+ // Sync with XPIStates.
+ let xpiState = XPIStates.getAddon(aAddon.location, aAddon.id);
+ if (xpiState) {
+ xpiState.syncWithDB(aAddon);
+ XPIStates.save();
+ } else {
+ // There should always be an xpiState
+ logger.warn("No XPIState for ${id} in ${location}", aAddon);
+ }
+
+ // Notify any other providers that a new theme has been enabled
+ if (aAddon.type == "theme" && !isDisabled)
+ AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, needsRestart);
+ },
+
+ /**
+ * Uninstalls an add-on, immediately if possible or marks it as pending
+ * uninstall if not.
+ *
+ * @param aAddon
+ * The DBAddonInternal to uninstall
+ * @param aForcePending
+ * Force this addon into the pending uninstall state, even if
+ * it isn't marked as requiring a restart (used e.g. while the
+ * add-on manager is open and offering an "undo" button)
+ * @throws if the addon cannot be uninstalled because it is in an install
+ * location that does not allow it
+ */
+ uninstallAddon: function XPI_uninstallAddon(aAddon, aForcePending) {
+ if (!(aAddon.inDatabase))
+ throw new Error("Cannot uninstall addon " + aAddon.id + " because it is not installed");
+
+ if (aAddon._installLocation.locked)
+ throw new Error("Cannot uninstall addon " + aAddon.id
+ + " from locked install location " + aAddon._installLocation.name);
+
+ // Inactive add-ons don't require a restart to uninstall
+ let requiresRestart = this.uninstallRequiresRestart(aAddon);
+
+ // if makePending is true, we don't actually apply the uninstall,
+ // we just mark the addon as having a pending uninstall
+ let makePending = aForcePending || requiresRestart;
+
+ if (makePending && aAddon.pendingUninstall)
+ throw new Error("Add-on is already marked to be uninstalled");
+
+ if ("_hasResourceCache" in aAddon)
+ aAddon._hasResourceCache = new Map();
+
+ if (aAddon._updateCheck) {
+ logger.debug("Cancel in-progress update check for " + aAddon.id);
+ aAddon._updateCheck.cancel();
+ }
+
+ let wasPending = aAddon.pendingUninstall;
+
+ if (makePending) {
+ // We create an empty directory in the staging directory to indicate
+ // that an uninstall is necessary on next startup.
+ let stage = aAddon._installLocation.getStagingDir();
+ stage.append(aAddon.id);
+ if (!stage.exists())
+ stage.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ XPIDatabase.setAddonProperties(aAddon, {
+ pendingUninstall: true
+ });
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
+ let xpiState = XPIStates.getAddon(aAddon.location, aAddon.id);
+ if (xpiState) {
+ xpiState.enabled = false;
+ XPIStates.save();
+ } else {
+ logger.warn("Can't find XPI state while uninstalling ${id} from ${location}", aAddon);
+ }
+ }
+
+ // If the add-on is not visible then there is no need to notify listeners.
+ if (!aAddon.visible)
+ return;
+
+ let wrapper = createWrapper(aAddon);
+
+ // If the add-on wasn't already pending uninstall then notify listeners.
+ if (!wasPending) {
+ // Passing makePending as the requiresRestart parameter is a little
+ // strange as in some cases this operation can complete without a restart
+ // so really this is now saying that the uninstall isn't going to happen
+ // immediately but will happen later.
+ AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper,
+ makePending);
+ }
+
+ // Reveal the highest priority add-on with the same ID
+ function revealAddon(aAddon) {
+ XPIDatabase.makeAddonVisible(aAddon);
+
+ let wrappedAddon = createWrapper(aAddon);
+ AddonManagerPrivate.callAddonListeners("onInstalling", wrappedAddon, false);
+
+ if (!aAddon.disabled && !XPIProvider.enableRequiresRestart(aAddon)) {
+ XPIDatabase.updateAddonActive(aAddon, true);
+ }
+
+ if (aAddon.bootstrap) {
+ let file = aAddon._installLocation.getLocationForID(aAddon.id);
+ XPIProvider.callBootstrapMethod(aAddon, file,
+ "install", BOOTSTRAP_REASONS.ADDON_INSTALL);
+
+ if (aAddon.active) {
+ XPIProvider.callBootstrapMethod(aAddon, file,
+ "startup", BOOTSTRAP_REASONS.ADDON_INSTALL);
+ }
+ else {
+ XPIProvider.unloadBootstrapScope(aAddon.id);
+ }
+ }
+
+ // We always send onInstalled even if a restart is required to enable
+ // the revealed add-on
+ AddonManagerPrivate.callAddonListeners("onInstalled", wrappedAddon);
+ }
+
+ function findAddonAndReveal(aId) {
+ let [locationName, ] = XPIStates.findAddon(aId);
+ if (locationName) {
+ XPIDatabase.getAddonInLocation(aId, locationName, revealAddon);
+ }
+ }
+
+ if (!makePending) {
+ if (aAddon.bootstrap) {
+ let file = aAddon._installLocation.getLocationForID(aAddon.id);
+ if (aAddon.active) {
+ this.callBootstrapMethod(aAddon, file, "shutdown",
+ BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ }
+
+ this.callBootstrapMethod(aAddon, file, "uninstall",
+ BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ this.unloadBootstrapScope(aAddon.id);
+ flushStartupCache();
+ }
+ aAddon._installLocation.uninstallAddon(aAddon.id);
+ XPIDatabase.removeAddonMetadata(aAddon);
+ XPIStates.removeAddon(aAddon.location, aAddon.id);
+ AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
+
+ findAddonAndReveal(aAddon.id);
+ }
+ else if (aAddon.bootstrap && aAddon.active && !this.disableRequiresRestart(aAddon)) {
+ this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown",
+ BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ this.unloadBootstrapScope(aAddon.id);
+ XPIDatabase.updateAddonActive(aAddon, false);
+ }
+
+ // Notify any other providers that a new theme has been enabled
+ if (aAddon.type == "theme" && aAddon.active)
+ AddonManagerPrivate.notifyAddonChanged(null, aAddon.type, requiresRestart);
+ },
+
+ /**
+ * Cancels the pending uninstall of an add-on.
+ *
+ * @param aAddon
+ * The DBAddonInternal to cancel uninstall for
+ */
+ cancelUninstallAddon: function XPI_cancelUninstallAddon(aAddon) {
+ if (!(aAddon.inDatabase))
+ throw new Error("Can only cancel uninstall for installed addons.");
+
+ if (!aAddon.pendingUninstall)
+ throw new Error("Add-on is not marked to be uninstalled");
+
+ aAddon._installLocation.cleanStagingDir([aAddon.id]);
+
+ XPIDatabase.setAddonProperties(aAddon, {
+ pendingUninstall: false
+ });
+
+ if (!aAddon.visible)
+ return;
+
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
+
+ // TODO hide hidden add-ons (bug 557710)
+ let wrapper = createWrapper(aAddon);
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled", wrapper);
+
+ if (aAddon.bootstrap && !aAddon.disabled && !this.enableRequiresRestart(aAddon)) {
+ this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "startup",
+ BOOTSTRAP_REASONS.ADDON_INSTALL);
+ XPIDatabase.updateAddonActive(aAddon, true);
+ }
+
+ // Notify any other providers that this theme is now enabled again.
+ if (aAddon.type == "theme" && aAddon.active)
+ AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, false);
+ }
+};
+
+function getHashStringForCrypto(aCrypto) {
+ // return the two-digit hexadecimal code for a byte
+ let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
+
+ // convert the binary hash data to a hex string.
+ let binary = aCrypto.finish(false);
+ let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)))
+ return hash.join("").toLowerCase();
+}
+
+/**
+ * Instantiates an AddonInstall.
+ *
+ * @param aInstallLocation
+ * The install location the add-on will be installed into
+ * @param aUrl
+ * The nsIURL to get the add-on from. If this is an nsIFileURL then
+ * the add-on will not need to be downloaded
+ * @param aHash
+ * An optional hash for the add-on
+ * @param aReleaseNotesURI
+ * An optional nsIURI of release notes for the add-on
+ * @param aExistingAddon
+ * The add-on this install will update if known
+ * @param aBrowser
+ * The browser performing the install
+ * @throws if the url is the url of a local file and the hash does not match
+ * or the add-on does not contain an valid install manifest
+ */
+function AddonInstall(aInstallLocation, aUrl, aHash, aReleaseNotesURI,
+ aExistingAddon, aBrowser) {
+ this.wrapper = new AddonInstallWrapper(this);
+ this.installLocation = aInstallLocation;
+ this.sourceURI = aUrl;
+ this.releaseNotesURI = aReleaseNotesURI;
+ if (aHash) {
+ let hashSplit = aHash.toLowerCase().split(":");
+ this.originalHash = {
+ algorithm: hashSplit[0],
+ data: hashSplit[1]
+ };
+ }
+ this.hash = this.originalHash;
+ this.browser = aBrowser;
+ this.listeners = [];
+ this.icons = {};
+ this.existingAddon = aExistingAddon;
+ this.error = 0;
+ this.window = aBrowser ? aBrowser.contentWindow : null;
+
+ // Giving each instance of AddonInstall a reference to the logger.
+ this.logger = logger;
+}
+
+AddonInstall.prototype = {
+ installLocation: null,
+ wrapper: null,
+ stream: null,
+ crypto: null,
+ originalHash: null,
+ hash: null,
+ browser: null,
+ badCertHandler: null,
+ listeners: null,
+ restartDownload: false,
+
+ name: null,
+ type: null,
+ version: null,
+ icons: null,
+ releaseNotesURI: null,
+ sourceURI: null,
+ file: null,
+ ownsTempFile: false,
+ certificate: null,
+ certName: null,
+
+ linkedInstalls: null,
+ existingAddon: null,
+ addon: null,
+
+ state: null,
+ error: null,
+ progress: null,
+ maxProgress: null,
+
+ /**
+ * Initialises this install to be a staged install waiting to be applied
+ *
+ * @param aManifest
+ * The cached manifest for the staged install
+ */
+ initStagedInstall: function AI_initStagedInstall(aManifest) {
+ this.name = aManifest.name;
+ this.type = aManifest.type;
+ this.version = aManifest.version;
+ this.icons = aManifest.icons;
+ this.releaseNotesURI = aManifest.releaseNotesURI ?
+ NetUtil.newURI(aManifest.releaseNotesURI) :
+ null
+ this.sourceURI = aManifest.sourceURI ?
+ NetUtil.newURI(aManifest.sourceURI) :
+ null;
+ this.file = null;
+ this.addon = aManifest;
+
+ this.state = AddonManager.STATE_INSTALLED;
+
+ XPIProvider.installs.push(this);
+ },
+
+ /**
+ * Initialises this install to be an install from a local file.
+ *
+ * @param aCallback
+ * The callback to pass the initialised AddonInstall to
+ */
+ initLocalInstall: function AI_initLocalInstall(aCallback) {
+ aCallback = makeSafe(aCallback);
+ this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file;
+
+ if (!this.file.exists()) {
+ logger.warn("XPI file " + this.file.path + " does not exist");
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = AddonManager.ERROR_NETWORK_FAILURE;
+ aCallback(this);
+ return;
+ }
+
+ this.state = AddonManager.STATE_DOWNLOADED;
+ this.progress = this.file.fileSize;
+ this.maxProgress = this.file.fileSize;
+
+ if (this.hash) {
+ let crypto = Cc["@mozilla.org/security/hash;1"].
+ createInstance(Ci.nsICryptoHash);
+ try {
+ crypto.initWithString(this.hash.algorithm);
+ }
+ catch (e) {
+ logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = AddonManager.ERROR_INCORRECT_HASH;
+ aCallback(this);
+ return;
+ }
+
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fis.init(this.file, -1, -1, false);
+ crypto.updateFromStream(fis, this.file.fileSize);
+ let calculatedHash = getHashStringForCrypto(crypto);
+ if (calculatedHash != this.hash.data) {
+ logger.warn("File hash (" + calculatedHash + ") did not match provided hash (" +
+ this.hash.data + ")");
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = AddonManager.ERROR_INCORRECT_HASH;
+ aCallback(this);
+ return;
+ }
+ }
+
+ try {
+ let self = this;
+ this.loadManifest(function initLocalInstall_loadManifest() {
+ XPIDatabase.getVisibleAddonForID(self.addon.id, function initLocalInstall_getVisibleAddon(aAddon) {
+ self.existingAddon = aAddon;
+ if (aAddon)
+ applyBlocklistChanges(aAddon, self.addon);
+ self.addon.updateDate = Date.now();
+ self.addon.installDate = aAddon ? aAddon.installDate : self.addon.updateDate;
+
+ if (!self.addon.isCompatible) {
+ // TODO Should we send some event here?
+ self.state = AddonManager.STATE_CHECKING;
+ new UpdateChecker(self.addon, {
+ onUpdateFinished: function updateChecker_onUpdateFinished(aAddon) {
+ self.state = AddonManager.STATE_DOWNLOADED;
+ XPIProvider.installs.push(self);
+ AddonManagerPrivate.callInstallListeners("onNewInstall",
+ self.listeners,
+ self.wrapper);
+
+ aCallback(self);
+ }
+ }, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
+ }
+ else {
+ XPIProvider.installs.push(self);
+ AddonManagerPrivate.callInstallListeners("onNewInstall",
+ self.listeners,
+ self.wrapper);
+
+ aCallback(self);
+ }
+ });
+ });
+ }
+ catch (e) {
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ if (e.webext) {
+ logger.warn("WebExtension XPI", e);
+ this.error = AddonManager.ERROR_WEBEXT_FILE;
+ } else {
+ logger.warn("Invalid XPI", e);
+ this.error = AddonManager.ERROR_CORRUPT_FILE;
+ }
+ aCallback(this);
+ return;
+ }
+ },
+
+ /**
+ * Initialises this install to be a download from a remote url.
+ *
+ * @param aCallback
+ * The callback to pass the initialised AddonInstall to
+ * @param aName
+ * An optional name for the add-on
+ * @param aType
+ * An optional type for the add-on
+ * @param aIcons
+ * Optional icons for the add-on
+ * @param aVersion
+ * An optional version for the add-on
+ */
+ initAvailableDownload: function AI_initAvailableDownload(aName, aType, aIcons, aVersion, aCallback) {
+ this.state = AddonManager.STATE_AVAILABLE;
+ this.name = aName;
+ this.type = aType;
+ this.version = aVersion;
+ this.icons = aIcons;
+ this.progress = 0;
+ this.maxProgress = -1;
+
+ XPIProvider.installs.push(this);
+ AddonManagerPrivate.callInstallListeners("onNewInstall", this.listeners,
+ this.wrapper);
+
+ makeSafe(aCallback)(this);
+ },
+
+ /**
+ * Starts installation of this add-on from whatever state it is currently at
+ * if possible.
+ *
+ * @throws if installation cannot proceed from the current state
+ */
+ install: function AI_install() {
+ switch (this.state) {
+ case AddonManager.STATE_AVAILABLE:
+ this.startDownload();
+ break;
+ case AddonManager.STATE_DOWNLOADED:
+ this.startInstall();
+ break;
+ case AddonManager.STATE_DOWNLOAD_FAILED:
+ case AddonManager.STATE_INSTALL_FAILED:
+ case AddonManager.STATE_CANCELLED:
+ this.removeTemporaryFile();
+ this.state = AddonManager.STATE_AVAILABLE;
+ this.error = 0;
+ this.progress = 0;
+ this.maxProgress = -1;
+ this.hash = this.originalHash;
+ XPIProvider.installs.push(this);
+ this.startDownload();
+ break;
+ case AddonManager.STATE_DOWNLOADING:
+ case AddonManager.STATE_CHECKING:
+ case AddonManager.STATE_INSTALLING:
+ // Installation is already running
+ return;
+ default:
+ throw new Error("Cannot start installing from this state");
+ }
+ },
+
+ /**
+ * Cancels installation of this add-on.
+ *
+ * @throws if installation cannot be cancelled from the current state
+ */
+ cancel: function AI_cancel() {
+ switch (this.state) {
+ case AddonManager.STATE_DOWNLOADING:
+ if (this.channel) {
+ logger.debug("Cancelling download of " + this.sourceURI.spec);
+ this.channel.cancel(Cr.NS_BINDING_ABORTED);
+ }
+ break;
+ case AddonManager.STATE_AVAILABLE:
+ case AddonManager.STATE_DOWNLOADED:
+ logger.debug("Cancelling download of " + this.sourceURI.spec);
+ this.state = AddonManager.STATE_CANCELLED;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
+ this.listeners, this.wrapper);
+ this.removeTemporaryFile();
+ break;
+ case AddonManager.STATE_INSTALLED:
+ logger.debug("Cancelling install of " + this.addon.id);
+ let xpi = this.installLocation.getStagingDir();
+ xpi.append(this.addon.id + ".xpi");
+ flushJarCache(xpi);
+ this.installLocation.cleanStagingDir([this.addon.id, this.addon.id + ".xpi",
+ this.addon.id + ".json"]);
+ this.state = AddonManager.STATE_CANCELLED;
+ XPIProvider.removeActiveInstall(this);
+
+ if (this.existingAddon) {
+ delete this.existingAddon.pendingUpgrade;
+ this.existingAddon.pendingUpgrade = null;
+ }
+
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled", createWrapper(this.addon));
+
+ AddonManagerPrivate.callInstallListeners("onInstallCancelled",
+ this.listeners, this.wrapper);
+ break;
+ default:
+ throw new Error("Cannot cancel install of " + this.sourceURI.spec +
+ " from this state (" + this.state + ")");
+ }
+ },
+
+ /**
+ * Adds an InstallListener for this instance if the listener is not already
+ * registered.
+ *
+ * @param aListener
+ * The InstallListener to add
+ */
+ addListener: function AI_addListener(aListener) {
+ if (!this.listeners.some(function addListener_matchListener(i) { return i == aListener; }))
+ this.listeners.push(aListener);
+ },
+
+ /**
+ * Removes an InstallListener for this instance if it is registered.
+ *
+ * @param aListener
+ * The InstallListener to remove
+ */
+ removeListener: function AI_removeListener(aListener) {
+ this.listeners = this.listeners.filter(function removeListener_filterListener(i) {
+ return i != aListener;
+ });
+ },
+
+ /**
+ * Removes the temporary file owned by this AddonInstall if there is one.
+ */
+ removeTemporaryFile: function AI_removeTemporaryFile() {
+ // Only proceed if this AddonInstall owns its XPI file
+ if (!this.ownsTempFile) {
+ this.logger.debug("removeTemporaryFile: " + this.sourceURI.spec + " does not own temp file");
+ return;
+ }
+
+ try {
+ this.logger.debug("removeTemporaryFile: " + this.sourceURI.spec + " removing temp file " +
+ this.file.path);
+ this.file.remove(true);
+ this.ownsTempFile = false;
+ }
+ catch (e) {
+ this.logger.warn("Failed to remove temporary file " + this.file.path + " for addon " +
+ this.sourceURI.spec,
+ e);
+ }
+ },
+
+ /**
+ * Updates the sourceURI and releaseNotesURI values on the Addon being
+ * installed by this AddonInstall instance.
+ */
+ updateAddonURIs: function AI_updateAddonURIs() {
+ this.addon.sourceURI = this.sourceURI.spec;
+ if (this.releaseNotesURI)
+ this.addon.releaseNotesURI = this.releaseNotesURI.spec;
+ },
+
+ /**
+ * Loads add-on manifests from a multi-package XPI file. Each of the
+ * XPI and JAR files contained in the XPI will be extracted. Any that
+ * do not contain valid add-ons will be ignored. The first valid add-on will
+ * be installed by this AddonInstall instance, the rest will have new
+ * AddonInstall instances created for them.
+ *
+ * @param aZipReader
+ * An open nsIZipReader for the multi-package XPI's files. This will
+ * be closed before this method returns.
+ * @param aCallback
+ * A function to call when all of the add-on manifests have been
+ * loaded. Because this loadMultipackageManifests is an internal API
+ * we don't exception-wrap this callback
+ */
+ _loadMultipackageManifests: function AI_loadMultipackageManifests(aZipReader,
+ aCallback) {
+ let files = [];
+ let entries = aZipReader.findEntries("(*.[Xx][Pp][Ii]|*.[Jj][Aa][Rr])");
+ while (entries.hasMore()) {
+ let entryName = entries.getNext();
+ var target = getTemporaryFile();
+ try {
+ aZipReader.extract(entryName, target);
+ files.push(target);
+ }
+ catch (e) {
+ logger.warn("Failed to extract " + entryName + " from multi-package " +
+ "XPI", e);
+ target.remove(false);
+ }
+ }
+
+ aZipReader.close();
+
+ if (files.length == 0) {
+ throw new Error("Multi-package XPI does not contain any packages " +
+ "to install");
+ }
+
+ let addon = null;
+
+ // Find the first file that has a valid install manifest and use it for
+ // the add-on that this AddonInstall instance will install.
+ while (files.length > 0) {
+ this.removeTemporaryFile();
+ this.file = files.shift();
+ this.ownsTempFile = true;
+ try {
+ addon = loadManifestFromZipFile(this.file);
+ break;
+ }
+ catch (e) {
+ logger.warn(this.file.leafName + " cannot be installed from multi-package " +
+ "XPI", e);
+ }
+ }
+
+ if (!addon) {
+ // No valid add-on was found
+ aCallback();
+ return;
+ }
+
+ this.addon = addon;
+
+ this.updateAddonURIs();
+
+ this.addon._install = this;
+ this.name = this.addon.selectedLocale.name || this.addon.defaultLocale.name;
+ this.type = this.addon.type;
+ this.version = this.addon.version;
+
+ // Setting the iconURL to something inside the XPI locks the XPI and
+ // makes it impossible to delete on Windows.
+ //let newIcon = createWrapper(this.addon).iconURL;
+ //if (newIcon)
+ // this.iconURL = newIcon;
+
+ // Create new AddonInstall instances for every remaining file
+ if (files.length > 0) {
+ this.linkedInstalls = [];
+ let count = 0;
+ let self = this;
+ files.forEach(function(file) {
+ AddonInstall.createInstall(function loadMultipackageManifests_createInstall(aInstall) {
+ // Ignore bad add-ons (createInstall will have logged the error)
+ if (aInstall.state == AddonManager.STATE_DOWNLOAD_FAILED) {
+ // Manually remove the temporary file
+ file.remove(true);
+ }
+ else {
+ // Make the new install own its temporary file
+ aInstall.ownsTempFile = true;
+
+ self.linkedInstalls.push(aInstall)
+
+ aInstall.sourceURI = self.sourceURI;
+ aInstall.releaseNotesURI = self.releaseNotesURI;
+ aInstall.updateAddonURIs();
+ }
+
+ count++;
+ if (count == files.length)
+ aCallback();
+ }, file);
+ }, this);
+ }
+ else {
+ aCallback();
+ }
+ },
+
+ /**
+ * Called after the add-on is a local file and the signature and install
+ * manifest can be read.
+ *
+ * @param aCallback
+ * A function to call when the manifest has been loaded
+ * @throws if the add-on does not contain a valid install manifest or the
+ * XPI is incorrectly signed
+ */
+ loadManifest: function AI_loadManifest(aCallback) {
+ aCallback = makeSafe(aCallback);
+ let self = this;
+ function addRepositoryData(aAddon) {
+ // Try to load from the existing cache first
+ AddonRepository.getCachedAddonByID(aAddon.id, function loadManifest_getCachedAddonByID(aRepoAddon) {
+ if (aRepoAddon) {
+ aAddon._repositoryAddon = aRepoAddon;
+ self.name = self.name || aAddon._repositoryAddon.name;
+ aAddon.compatibilityOverrides = aRepoAddon.compatibilityOverrides;
+ aAddon.appDisabled = !isUsableAddon(aAddon);
+ aCallback();
+ return;
+ }
+
+ // It wasn't there so try to re-download it
+ AddonRepository.cacheAddons([aAddon.id], function loadManifest_cacheAddons() {
+ AddonRepository.getCachedAddonByID(aAddon.id, function loadManifest_getCachedAddonByID(aRepoAddon) {
+ aAddon._repositoryAddon = aRepoAddon;
+ self.name = self.name || aAddon._repositoryAddon.name;
+ aAddon.compatibilityOverrides = aRepoAddon ?
+ aRepoAddon.compatibilityOverrides :
+ null;
+ aAddon.appDisabled = !isUsableAddon(aAddon);
+ aCallback();
+ });
+ });
+ });
+ }
+
+ let zipreader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipreader.open(this.file);
+ }
+ catch (e) {
+ zipreader.close();
+ throw e;
+ }
+
+ let x509 = zipreader.getSigningCert(null);
+ if (x509) {
+ logger.debug("Verifying XPI signature");
+ if (verifyZipSigning(zipreader, x509)) {
+ this.certificate = x509;
+ if (this.certificate.commonName.length > 0) {
+ this.certName = this.certificate.commonName;
+ } else {
+ this.certName = this.certificate.organization;
+ }
+ } else {
+ zipreader.close();
+ throw new Error("XPI is incorrectly signed");
+ }
+ }
+
+ try {
+ this.addon = loadManifestFromZipReader(zipreader);
+ }
+ catch (e) {
+ zipreader.close();
+ throw e;
+ }
+
+ if (this.addon.type == "multipackage") {
+ this._loadMultipackageManifests(zipreader, function loadManifest_loadMultipackageManifests() {
+ addRepositoryData(self.addon);
+ });
+ return;
+ }
+
+ zipreader.close();
+
+ this.updateAddonURIs();
+
+ this.addon._install = this;
+ this.name = this.addon.selectedLocale.name || this.addon.defaultLocale.name;
+ this.type = this.addon.type;
+ this.version = this.addon.version;
+
+ // Setting the iconURL to something inside the XPI locks the XPI and
+ // makes it impossible to delete on Windows.
+ //let newIcon = createWrapper(this.addon).iconURL;
+ //if (newIcon)
+ // this.iconURL = newIcon;
+
+ addRepositoryData(this.addon);
+ },
+
+ observe: function AI_observe(aSubject, aTopic, aData) {
+ // Network is going offline
+ this.cancel();
+ },
+
+ /**
+ * Starts downloading the add-on's XPI file.
+ */
+ startDownload: function AI_startDownload() {
+ this.state = AddonManager.STATE_DOWNLOADING;
+ if (!AddonManagerPrivate.callInstallListeners("onDownloadStarted",
+ this.listeners, this.wrapper)) {
+ logger.debug("onDownloadStarted listeners cancelled installation of addon " + this.sourceURI.spec);
+ this.state = AddonManager.STATE_CANCELLED;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
+ this.listeners, this.wrapper)
+ return;
+ }
+
+ // If a listener changed our state then do not proceed with the download
+ if (this.state != AddonManager.STATE_DOWNLOADING)
+ return;
+
+ if (this.channel) {
+ // A previous download attempt hasn't finished cleaning up yet, signal
+ // that it should restart when complete
+ logger.debug("Waiting for previous download to complete");
+ this.restartDownload = true;
+ return;
+ }
+
+ this.openChannel();
+ },
+
+ openChannel: function AI_openChannel() {
+ this.restartDownload = false;
+
+ try {
+ this.file = getTemporaryFile();
+ this.ownsTempFile = true;
+ this.stream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ this.stream.init(this.file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
+ FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE, 0);
+ }
+ catch (e) {
+ logger.warn("Failed to start download for addon " + this.sourceURI.spec, e);
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = AddonManager.ERROR_FILE_ACCESS;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadFailed",
+ this.listeners, this.wrapper);
+ return;
+ }
+
+ let listener = Cc["@mozilla.org/network/stream-listener-tee;1"].
+ createInstance(Ci.nsIStreamListenerTee);
+ listener.init(this, this.stream);
+ try {
+ Components.utils.import("resource://gre/modules/CertUtils.jsm");
+ let requireBuiltIn = Preferences.get(PREF_INSTALL_REQUIREBUILTINCERTS, true);
+ this.badCertHandler = new BadCertHandler(!requireBuiltIn);
+
+ this.channel = NetUtil.newChannel2(this.sourceURI,
+ null,
+ null,
+ null, // aLoadingNode
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_NORMAL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ this.channel.notificationCallbacks = this;
+ if (this.channel instanceof Ci.nsIHttpChannel) {
+ this.channel.setRequestHeader("Moz-XPI-Update", "1", true);
+ if (this.channel instanceof Ci.nsIHttpChannelInternal)
+ this.channel.forceAllowThirdPartyCookie = true;
+ }
+ this.channel.asyncOpen(listener, null);
+
+ Services.obs.addObserver(this, "network:offline-about-to-go-offline", false);
+ }
+ catch (e) {
+ logger.warn("Failed to start download for addon " + this.sourceURI.spec, e);
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = AddonManager.ERROR_NETWORK_FAILURE;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadFailed",
+ this.listeners, this.wrapper);
+ }
+ },
+
+ /**
+ * Update the crypto hasher with the new data and call the progress listeners.
+ *
+ * @see nsIStreamListener
+ */
+ onDataAvailable: function AI_onDataAvailable(aRequest, aContext, aInputstream,
+ aOffset, aCount) {
+ this.crypto.updateFromStream(aInputstream, aCount);
+ this.progress += aCount;
+ if (!AddonManagerPrivate.callInstallListeners("onDownloadProgress",
+ this.listeners, this.wrapper)) {
+ // TODO cancel the download and make it available again (bug 553024)
+ }
+ },
+
+ /**
+ * Check the redirect response for a hash of the target XPI and verify that
+ * we don't end up on an insecure channel.
+ *
+ * @see nsIChannelEventSink
+ */
+ asyncOnChannelRedirect: function AI_asyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, aCallback) {
+ if (!this.hash && aOldChannel.originalURI.schemeIs("https") &&
+ aOldChannel instanceof Ci.nsIHttpChannel) {
+ try {
+ let hashStr = aOldChannel.getResponseHeader("X-Target-Digest");
+ let hashSplit = hashStr.toLowerCase().split(":");
+ this.hash = {
+ algorithm: hashSplit[0],
+ data: hashSplit[1]
+ };
+ }
+ catch (e) {
+ }
+ }
+
+ // Verify that we don't end up on an insecure channel if we haven't got a
+ // hash to verify with (see bug 537761 for discussion)
+ if (!this.hash)
+ this.badCertHandler.asyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, aCallback);
+ else
+ aCallback.onRedirectVerifyCallback(Cr.NS_OK);
+
+ this.channel = aNewChannel;
+ },
+
+ /**
+ * This is the first chance to get at real headers on the channel.
+ *
+ * @see nsIStreamListener
+ */
+ onStartRequest: function AI_onStartRequest(aRequest, aContext) {
+ this.crypto = Cc["@mozilla.org/security/hash;1"].
+ createInstance(Ci.nsICryptoHash);
+ if (this.hash) {
+ try {
+ this.crypto.initWithString(this.hash.algorithm);
+ }
+ catch (e) {
+ logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = AddonManager.ERROR_INCORRECT_HASH;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadFailed",
+ this.listeners, this.wrapper);
+ aRequest.cancel(Cr.NS_BINDING_ABORTED);
+ return;
+ }
+ }
+ else {
+ // We always need something to consume data from the inputstream passed
+ // to onDataAvailable so just create a dummy cryptohasher to do that.
+ this.crypto.initWithString("sha1");
+ }
+
+ this.progress = 0;
+ if (aRequest instanceof Ci.nsIChannel) {
+ try {
+ this.maxProgress = aRequest.contentLength;
+ }
+ catch (e) {
+ }
+ logger.debug("Download started for " + this.sourceURI.spec + " to file " +
+ this.file.path);
+ }
+ },
+
+ /**
+ * The download is complete.
+ *
+ * @see nsIStreamListener
+ */
+ onStopRequest: function AI_onStopRequest(aRequest, aContext, aStatus) {
+ this.stream.close();
+ this.channel = null;
+ this.badCerthandler = null;
+ Services.obs.removeObserver(this, "network:offline-about-to-go-offline");
+
+ // If the download was cancelled then update the state and send events
+ if (aStatus == Cr.NS_BINDING_ABORTED) {
+ if (this.state == AddonManager.STATE_DOWNLOADING) {
+ logger.debug("Cancelled download of " + this.sourceURI.spec);
+ this.state = AddonManager.STATE_CANCELLED;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
+ this.listeners, this.wrapper);
+ // If a listener restarted the download then there is no need to
+ // remove the temporary file
+ if (this.state != AddonManager.STATE_CANCELLED)
+ return;
+ }
+
+ this.removeTemporaryFile();
+ if (this.restartDownload)
+ this.openChannel();
+ return;
+ }
+
+ logger.debug("Download of " + this.sourceURI.spec + " completed.");
+
+ if (Components.isSuccessCode(aStatus)) {
+ if (!(aRequest instanceof Ci.nsIHttpChannel) || aRequest.requestSucceeded) {
+ if (!this.hash && (aRequest instanceof Ci.nsIChannel)) {
+ try {
+ checkCert(aRequest,
+ !Preferences.get(PREF_INSTALL_REQUIREBUILTINCERTS, true));
+ }
+ catch (e) {
+ this.downloadFailed(AddonManager.ERROR_NETWORK_FAILURE, e);
+ return;
+ }
+ }
+
+ // convert the binary hash data to a hex string.
+ let calculatedHash = getHashStringForCrypto(this.crypto);
+ this.crypto = null;
+ if (this.hash && calculatedHash != this.hash.data) {
+ this.downloadFailed(AddonManager.ERROR_INCORRECT_HASH,
+ "Downloaded file hash (" + calculatedHash +
+ ") did not match provided hash (" + this.hash.data + ")");
+ return;
+ }
+ try {
+ let self = this;
+ this.loadManifest(function onStopRequest_loadManifest() {
+ if (self.addon.isCompatible) {
+ self.downloadCompleted();
+ }
+ else {
+ // TODO Should we send some event here (bug 557716)?
+ self.state = AddonManager.STATE_CHECKING;
+ new UpdateChecker(self.addon, {
+ onUpdateFinished: function onStopRequest_onUpdateFinished(aAddon) {
+ self.downloadCompleted();
+ }
+ }, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
+ }
+ });
+ }
+ catch (e) {
+ if (e.webext) {
+ this.downloadFailed(AddonManager.ERROR_WEBEXT_FILE, e);
+ } else {
+ this.downloadFailed(AddonManager.ERROR_CORRUPT_FILE, e);
+ }
+ }
+ }
+ else {
+ if (aRequest instanceof Ci.nsIHttpChannel)
+ this.downloadFailed(AddonManager.ERROR_NETWORK_FAILURE,
+ aRequest.responseStatus + " " +
+ aRequest.responseStatusText);
+ else
+ this.downloadFailed(AddonManager.ERROR_NETWORK_FAILURE, aStatus);
+ }
+ }
+ else {
+ this.downloadFailed(AddonManager.ERROR_NETWORK_FAILURE, aStatus);
+ }
+ },
+
+ /**
+ * Notify listeners that the download failed.
+ *
+ * @param aReason
+ * Something to log about the failure
+ * @param error
+ * The error code to pass to the listeners
+ */
+ downloadFailed: function AI_downloadFailed(aReason, aError) {
+ logger.warn("Download of " + this.sourceURI.spec + " failed", aError);
+ this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ this.error = aReason;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onDownloadFailed", this.listeners,
+ this.wrapper);
+
+ // If the listener hasn't restarted the download then remove any temporary
+ // file
+ if (this.state == AddonManager.STATE_DOWNLOAD_FAILED) {
+ logger.debug("downloadFailed: removing temp file for " + this.sourceURI.spec);
+ this.removeTemporaryFile();
+ }
+ else
+ logger.debug("downloadFailed: listener changed AddonInstall state for " +
+ this.sourceURI.spec + " to " + this.state);
+ },
+
+ /**
+ * Notify listeners that the download completed.
+ */
+ downloadCompleted: function AI_downloadCompleted() {
+ let self = this;
+ XPIDatabase.getVisibleAddonForID(this.addon.id, function downloadCompleted_getVisibleAddonForID(aAddon) {
+ if (aAddon)
+ self.existingAddon = aAddon;
+
+ self.state = AddonManager.STATE_DOWNLOADED;
+ self.addon.updateDate = Date.now();
+
+ if (self.existingAddon) {
+ self.addon.existingAddonID = self.existingAddon.id;
+ self.addon.installDate = self.existingAddon.installDate;
+ applyBlocklistChanges(self.existingAddon, self.addon);
+ }
+ else {
+ self.addon.installDate = self.addon.updateDate;
+ }
+
+ if (AddonManagerPrivate.callInstallListeners("onDownloadEnded",
+ self.listeners,
+ self.wrapper)) {
+ // If a listener changed our state then do not proceed with the install
+ if (self.state != AddonManager.STATE_DOWNLOADED)
+ return;
+
+ self.install();
+
+ if (self.linkedInstalls) {
+ self.linkedInstalls.forEach(function(aInstall) {
+ aInstall.install();
+ });
+ }
+ }
+ });
+ },
+
+ // TODO This relies on the assumption that we are always installing into the
+ // highest priority install location so the resulting add-on will be visible
+ // overriding any existing copy in another install location (bug 557710).
+ /**
+ * Installs the add-on into the install location.
+ */
+ startInstall: function AI_startInstall() {
+ this.state = AddonManager.STATE_INSTALLING;
+ if (!AddonManagerPrivate.callInstallListeners("onInstallStarted",
+ this.listeners, this.wrapper)) {
+ this.state = AddonManager.STATE_DOWNLOADED;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callInstallListeners("onInstallCancelled",
+ this.listeners, this.wrapper)
+ return;
+ }
+
+ // Find and cancel any pending installs for the same add-on in the same
+ // install location
+ for (let aInstall of XPIProvider.installs) {
+ if (aInstall.state == AddonManager.STATE_INSTALLED &&
+ aInstall.installLocation == this.installLocation &&
+ aInstall.addon.id == this.addon.id) {
+ logger.debug("Cancelling previous pending install of " + aInstall.addon.id);
+ aInstall.cancel();
+ }
+ }
+
+ let isUpgrade = this.existingAddon &&
+ this.existingAddon._installLocation == this.installLocation;
+ let requiresRestart = XPIProvider.installRequiresRestart(this.addon);
+
+ logger.debug("Starting install of " + this.addon.id + " from " + this.sourceURI.spec);
+ AddonManagerPrivate.callAddonListeners("onInstalling",
+ createWrapper(this.addon),
+ requiresRestart);
+
+ let stagingDir = this.installLocation.getStagingDir();
+ let stagedAddon = stagingDir.clone();
+
+ Task.spawn((function() {
+ let installedUnpacked = 0;
+ yield this.installLocation.requestStagingDir();
+
+ // Remove any staged items for this add-on
+ stagedAddon.append(this.addon.id);
+ yield removeAsync(stagedAddon);
+ stagedAddon.leafName = this.addon.id + ".xpi";
+ yield removeAsync(stagedAddon);
+
+ // First stage the file regardless of whether restarting is necessary
+ if (this.addon.unpack || Preferences.get(PREF_XPI_UNPACK, false)) {
+ logger.debug("Addon " + this.addon.id + " will be installed as " +
+ "an unpacked directory");
+ stagedAddon.leafName = this.addon.id;
+ yield OS.File.makeDir(stagedAddon.path);
+ yield ZipUtils.extractFilesAsync(this.file, stagedAddon);
+ installedUnpacked = 1;
+ }
+ else {
+ logger.debug("Addon " + this.addon.id + " will be installed as " +
+ "a packed xpi");
+ stagedAddon.leafName = this.addon.id + ".xpi";
+ yield OS.File.copy(this.file.path, stagedAddon.path);
+ }
+
+ if (requiresRestart) {
+ // Point the add-on to its extracted files as the xpi may get deleted
+ this.addon._sourceBundle = stagedAddon;
+
+ // Cache the AddonInternal as it may have updated compatibility info
+ let stagedJSON = stagedAddon.clone();
+ stagedJSON.leafName = this.addon.id + ".json";
+ if (stagedJSON.exists())
+ stagedJSON.remove(true);
+ let stream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(Ci.nsIConverterOutputStream);
+
+ try {
+ stream.init(stagedJSON, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
+ FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
+ 0);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ converter.writeString(JSON.stringify(this.addon));
+ }
+ finally {
+ converter.close();
+ stream.close();
+ }
+
+ logger.debug("Staged install of " + this.addon.id + " from " + this.sourceURI.spec + " ready; waiting for restart.");
+ this.state = AddonManager.STATE_INSTALLED;
+ if (isUpgrade) {
+ delete this.existingAddon.pendingUpgrade;
+ this.existingAddon.pendingUpgrade = this.addon;
+ }
+ AddonManagerPrivate.callInstallListeners("onInstallEnded",
+ this.listeners, this.wrapper,
+ createWrapper(this.addon));
+ }
+ else {
+ // The install is completed so it should be removed from the active list
+ XPIProvider.removeActiveInstall(this);
+
+ // TODO We can probably reduce the number of DB operations going on here
+ // We probably also want to support rolling back failed upgrades etc.
+ // See bug 553015.
+
+ // Deactivate and remove the old add-on as necessary
+ let reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
+ if (this.existingAddon) {
+ if (Services.vc.compare(this.existingAddon.version, this.addon.version) < 0)
+ reason = BOOTSTRAP_REASONS.ADDON_UPGRADE;
+ else
+ reason = BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
+
+ if (this.existingAddon.bootstrap) {
+ let file = this.existingAddon._installLocation
+ .getLocationForID(this.existingAddon.id);
+ if (this.existingAddon.active) {
+ XPIProvider.callBootstrapMethod(this.existingAddon, file,
+ "shutdown", reason,
+ { newVersion: this.addon.version });
+ }
+
+ XPIProvider.callBootstrapMethod(this.existingAddon, file,
+ "uninstall", reason,
+ { newVersion: this.addon.version });
+ XPIProvider.unloadBootstrapScope(this.existingAddon.id);
+ flushStartupCache();
+ }
+
+ if (!isUpgrade && this.existingAddon.active) {
+ XPIDatabase.updateAddonActive(this.existingAddon, false);
+ }
+ }
+
+ // Install the new add-on into its final location
+ let existingAddonID = this.existingAddon ? this.existingAddon.id : null;
+ let file = this.installLocation.installAddon(this.addon.id, stagedAddon,
+ existingAddonID);
+
+ // Update the metadata in the database
+ this.addon._sourceBundle = file;
+ this.addon._installLocation = this.installLocation;
+ this.addon.visible = true;
+
+ if (isUpgrade) {
+ this.addon = XPIDatabase.updateAddonMetadata(this.existingAddon, this.addon,
+ file.persistentDescriptor);
+ let state = XPIStates.getAddon(this.installLocation.name, this.addon.id);
+ if (state) {
+ state.syncWithDB(this.addon, true);
+ } else {
+ logger.warn("Unexpected missing XPI state for add-on ${id}", this.addon);
+ }
+ }
+ else {
+ this.addon.active = (this.addon.visible && !this.addon.disabled);
+ this.addon = XPIDatabase.addAddonMetadata(this.addon, file.persistentDescriptor);
+ XPIStates.addAddon(this.addon);
+ this.addon.installDate = this.addon.updateDate;
+ XPIDatabase.saveChanges();
+ }
+ XPIStates.save();
+
+ let extraParams = {};
+ if (this.existingAddon) {
+ extraParams.oldVersion = this.existingAddon.version;
+ }
+
+ if (this.addon.bootstrap) {
+ XPIProvider.callBootstrapMethod(this.addon, file, "install",
+ reason, extraParams);
+ }
+
+ AddonManagerPrivate.callAddonListeners("onInstalled",
+ createWrapper(this.addon));
+
+ logger.debug("Install of " + this.sourceURI.spec + " completed.");
+ this.state = AddonManager.STATE_INSTALLED;
+ AddonManagerPrivate.callInstallListeners("onInstallEnded",
+ this.listeners, this.wrapper,
+ createWrapper(this.addon));
+
+ if (this.addon.bootstrap) {
+ if (this.addon.active) {
+ XPIProvider.callBootstrapMethod(this.addon, file, "startup",
+ reason, extraParams);
+ }
+ else {
+ // XXX this makes it dangerous to do some things in onInstallEnded
+ // listeners because important cleanup hasn't been done yet
+ XPIProvider.unloadBootstrapScope(this.addon.id);
+ }
+ }
+ }
+ }).bind(this)).then(null, (e) => {
+ logger.warn("Failed to install " + this.file.path + " from " + this.sourceURI.spec, e);
+ if (stagedAddon.exists())
+ recursiveRemove(stagedAddon);
+ this.state = AddonManager.STATE_INSTALL_FAILED;
+ this.error = AddonManager.ERROR_FILE_ACCESS;
+ XPIProvider.removeActiveInstall(this);
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled",
+ createWrapper(this.addon));
+ AddonManagerPrivate.callInstallListeners("onInstallFailed",
+ this.listeners,
+ this.wrapper);
+ }).then(() => {
+ this.removeTemporaryFile();
+ return this.installLocation.releaseStagingDir();
+ });
+ },
+
+ getInterface: function AI_getInterface(iid) {
+ if (iid.equals(Ci.nsIAuthPrompt2)) {
+ let win = this.window;
+ if (!win && this.browser)
+ win = this.browser.ownerDocument.defaultView;
+
+ let factory = Cc["@mozilla.org/prompter;1"].
+ getService(Ci.nsIPromptFactory);
+ let prompt = factory.getPrompt(win, Ci.nsIAuthPrompt2);
+
+ if (this.browser && this.browser.isRemoteBrowser && prompt instanceof Ci.nsILoginManagerPrompter)
+ prompt.setE10sData(this.browser, null);
+
+ return prompt;
+ }
+ else if (iid.equals(Ci.nsIChannelEventSink)) {
+ return this;
+ }
+
+ return this.badCertHandler.getInterface(iid);
+ }
+}
+
+/**
+ * Creates a new AddonInstall for an already staged install. Used when
+ * installing the staged install failed for some reason.
+ *
+ * @param aDir
+ * The directory holding the staged install
+ * @param aManifest
+ * The cached manifest for the install
+ */
+AddonInstall.createStagedInstall = function AI_createStagedInstall(aInstallLocation, aDir, aManifest) {
+ let url = Services.io.newFileURI(aDir);
+
+ let install = new AddonInstall(aInstallLocation, aDir);
+ install.initStagedInstall(aManifest);
+};
+
+/**
+ * Creates a new AddonInstall to install an add-on from a local file. Installs
+ * always go into the profile install location.
+ *
+ * @param aCallback
+ * The callback to pass the new AddonInstall to
+ * @param aFile
+ * The file to install
+ */
+AddonInstall.createInstall = function AI_createInstall(aCallback, aFile) {
+ let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
+ let url = Services.io.newFileURI(aFile);
+
+ try {
+ let install = new AddonInstall(location, url);
+ install.initLocalInstall(aCallback);
+ }
+ catch(e) {
+ logger.error("Error creating install", e);
+ makeSafe(aCallback)(null);
+ }
+};
+
+/**
+ * Creates a new AddonInstall to download and install a URL.
+ *
+ * @param aCallback
+ * The callback to pass the new AddonInstall to
+ * @param aUri
+ * The URI to download
+ * @param aHash
+ * A hash for the add-on
+ * @param aName
+ * A name for the add-on
+ * @param aIcons
+ * An icon URLs for the add-on
+ * @param aVersion
+ * A version for the add-on
+ * @param aBrowser
+ * The browser performing the install
+ */
+AddonInstall.createDownload = function AI_createDownload(aCallback, aUri, aHash, aName, aIcons,
+ aVersion, aBrowser) {
+ let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
+ let url = NetUtil.newURI(aUri);
+
+ let install = new AddonInstall(location, url, aHash, null, null, aBrowser);
+ if (url instanceof Ci.nsIFileURL)
+ install.initLocalInstall(aCallback);
+ else
+ install.initAvailableDownload(aName, null, aIcons, aVersion, aCallback);
+};
+
+/**
+ * Creates a new AddonInstall for an update.
+ *
+ * @param aCallback
+ * The callback to pass the new AddonInstall to
+ * @param aAddon
+ * The add-on being updated
+ * @param aUpdate
+ * The metadata about the new version from the update manifest
+ */
+AddonInstall.createUpdate = function AI_createUpdate(aCallback, aAddon, aUpdate) {
+ let url = NetUtil.newURI(aUpdate.updateURL);
+ let releaseNotesURI = null;
+ try {
+ if (aUpdate.updateInfoURL)
+ releaseNotesURI = NetUtil.newURI(escapeAddonURI(aAddon, aUpdate.updateInfoURL));
+ }
+ catch (e) {
+ // If the releaseNotesURI cannot be parsed then just ignore it.
+ }
+
+ let install = new AddonInstall(aAddon._installLocation, url,
+ aUpdate.updateHash, releaseNotesURI, aAddon);
+ if (url instanceof Ci.nsIFileURL) {
+ install.initLocalInstall(aCallback);
+ }
+ else {
+ install.initAvailableDownload(aAddon.selectedLocale.name ?
+ aAddon.selectedLocale.name : aAddon.defaultLocale.name,
+ aAddon.type, aAddon.icons, aUpdate.version, aCallback);
+ }
+};
+
+/**
+ * Creates a wrapper for an AddonInstall that only exposes the public API
+ *
+ * @param install
+ * The AddonInstall to create a wrapper for
+ */
+function AddonInstallWrapper(aInstall) {
+#ifdef MOZ_EM_DEBUG
+ this.__defineGetter__("__AddonInstallInternal__", function AIW_debugGetter() {
+ return aInstall;
+ });
+#endif
+
+ ["name", "type", "version", "icons", "releaseNotesURI", "file", "state", "error",
+ "progress", "maxProgress", "certificate", "certName"].forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AIW_propertyGetter() aInstall[aProp]);
+ }, this);
+
+ this.__defineGetter__("iconURL", function AIW_iconURL() aInstall.icons[32]);
+
+ this.__defineGetter__("existingAddon", function AIW_existingAddonGetter() {
+ return createWrapper(aInstall.existingAddon);
+ });
+ this.__defineGetter__("addon", function AIW_addonGetter() createWrapper(aInstall.addon));
+ this.__defineGetter__("sourceURI", function AIW_sourceURIGetter() aInstall.sourceURI);
+
+ this.__defineGetter__("linkedInstalls", function AIW_linkedInstallsGetter() {
+ if (!aInstall.linkedInstalls)
+ return null;
+ // Tycho: return [i.wrapper for each (i in aInstall.linkedInstalls)];
+ let result = [];
+ for each (let i in aInstall.linkedInstalls) {
+ result.push(i.wrapper);
+ }
+
+ return result;
+ });
+
+ this.install = function AIW_install() {
+ aInstall.install();
+ }
+
+ this.cancel = function AIW_cancel() {
+ aInstall.cancel();
+ }
+
+ this.addListener = function AIW_addListener(listener) {
+ aInstall.addListener(listener);
+ }
+
+ this.removeListener = function AIW_removeListener(listener) {
+ aInstall.removeListener(listener);
+ }
+}
+
+AddonInstallWrapper.prototype = {};
+
+/**
+ * Creates a new update checker.
+ *
+ * @param aAddon
+ * The add-on to check for updates
+ * @param aListener
+ * An UpdateListener to notify of updates
+ * @param aReason
+ * The reason for the update check
+ * @param aAppVersion
+ * An optional application version to check for updates for
+ * @param aPlatformVersion
+ * An optional platform version to check for updates for
+ * @throws if the aListener or aReason arguments are not valid
+ */
+function UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion) {
+ if (!aListener || !aReason)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
+
+ this.addon = aAddon;
+ aAddon._updateCheck = this;
+ XPIProvider.doing(this);
+ this.listener = aListener;
+ this.appVersion = aAppVersion;
+ this.platformVersion = aPlatformVersion;
+ this.syncCompatibility = (aReason == AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+
+ let updateURL = aAddon.updateURL;
+ if (!updateURL) {
+ if (aReason == AddonManager.UPDATE_WHEN_PERIODIC_UPDATE &&
+ Services.prefs.getPrefType(PREF_EM_UPDATE_BACKGROUND_URL) == Services.prefs.PREF_STRING) {
+ updateURL = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
+ } else {
+ updateURL = Services.prefs.getCharPref(PREF_EM_UPDATE_URL);
+ }
+ }
+
+ const UPDATE_TYPE_COMPATIBILITY = 32;
+ const UPDATE_TYPE_NEWVERSION = 64;
+
+ aReason |= UPDATE_TYPE_COMPATIBILITY;
+ if ("onUpdateAvailable" in this.listener)
+ aReason |= UPDATE_TYPE_NEWVERSION;
+
+ let url = escapeAddonURI(aAddon, updateURL, aReason, aAppVersion);
+ this._parser = AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey,
+ url, this);
+}
+
+UpdateChecker.prototype = {
+ addon: null,
+ listener: null,
+ appVersion: null,
+ platformVersion: null,
+ syncCompatibility: null,
+
+ /**
+ * Calls a method on the listener passing any number of arguments and
+ * consuming any exceptions.
+ *
+ * @param aMethod
+ * The method to call on the listener
+ */
+ callListener: function UC_callListener(aMethod, ...aArgs) {
+ if (!(aMethod in this.listener))
+ return;
+
+ try {
+ this.listener[aMethod].apply(this.listener, aArgs);
+ }
+ catch (e) {
+ logger.warn("Exception calling UpdateListener method " + aMethod, e);
+ }
+ },
+
+ /**
+ * Called when AddonUpdateChecker completes the update check
+ *
+ * @param updates
+ * The list of update details for the add-on
+ */
+ onUpdateCheckComplete: function UC_onUpdateCheckComplete(aUpdates) {
+ XPIProvider.done(this.addon._updateCheck);
+ this.addon._updateCheck = null;
+ let AUC = AddonUpdateChecker;
+
+ let ignoreMaxVersion = false;
+ let ignoreStrictCompat = false;
+ if (!AddonManager.checkCompatibility) {
+ ignoreMaxVersion = true;
+ ignoreStrictCompat = true;
+ } else if (this.addon.type in COMPATIBLE_BY_DEFAULT_TYPES &&
+ !AddonManager.strictCompatibility &&
+ !this.addon.strictCompatibility &&
+ !this.addon.hasBinaryComponents) {
+ ignoreMaxVersion = true;
+ }
+
+ // Always apply any compatibility update for the current version
+ let compatUpdate = AUC.getCompatibilityUpdate(aUpdates, this.addon.version,
+ this.syncCompatibility,
+ null, null,
+ ignoreMaxVersion,
+ ignoreStrictCompat);
+ // Apply the compatibility update to the database
+ if (compatUpdate)
+ this.addon.applyCompatibilityUpdate(compatUpdate, this.syncCompatibility);
+
+ // If the request is for an application or platform version that is
+ // different to the current application or platform version then look for a
+ // compatibility update for those versions.
+ if ((this.appVersion &&
+ Services.vc.compare(this.appVersion, Services.appinfo.version) != 0) ||
+ (this.platformVersion &&
+ Services.vc.compare(this.platformVersion, Services.appinfo.platformVersion) != 0)) {
+ compatUpdate = AUC.getCompatibilityUpdate(aUpdates, this.addon.version,
+ false, this.appVersion,
+ this.platformVersion,
+ ignoreMaxVersion,
+ ignoreStrictCompat);
+ }
+
+ if (compatUpdate)
+ this.callListener("onCompatibilityUpdateAvailable", createWrapper(this.addon));
+ else
+ this.callListener("onNoCompatibilityUpdateAvailable", createWrapper(this.addon));
+
+ function sendUpdateAvailableMessages(aSelf, aInstall) {
+ if (aInstall) {
+ aSelf.callListener("onUpdateAvailable", createWrapper(aSelf.addon),
+ aInstall.wrapper);
+ }
+ else {
+ aSelf.callListener("onNoUpdateAvailable", createWrapper(aSelf.addon));
+ }
+ aSelf.callListener("onUpdateFinished", createWrapper(aSelf.addon),
+ AddonManager.UPDATE_STATUS_NO_ERROR);
+ }
+
+ let compatOverrides = AddonManager.strictCompatibility ?
+ null :
+ this.addon.compatibilityOverrides;
+
+ let update = AUC.getNewestCompatibleUpdate(aUpdates,
+ this.appVersion,
+ this.platformVersion,
+ ignoreMaxVersion,
+ ignoreStrictCompat,
+ compatOverrides);
+
+ if (update && Services.vc.compare(this.addon.version, update.version) < 0) {
+ for (let currentInstall of XPIProvider.installs) {
+ // Skip installs that don't match the available update
+ if (currentInstall.existingAddon != this.addon ||
+ currentInstall.version != update.version)
+ continue;
+
+ // If the existing install has not yet started downloading then send an
+ // available update notification. If it is already downloading then
+ // don't send any available update notification
+ if (currentInstall.state == AddonManager.STATE_AVAILABLE) {
+ logger.debug("Found an existing AddonInstall for " + this.addon.id);
+ sendUpdateAvailableMessages(this, currentInstall);
+ }
+ else
+ sendUpdateAvailableMessages(this, null);
+ return;
+ }
+
+ let self = this;
+ AddonInstall.createUpdate(function onUpdateCheckComplete_createUpdate(aInstall) {
+ sendUpdateAvailableMessages(self, aInstall);
+ }, this.addon, update);
+ }
+ else {
+ sendUpdateAvailableMessages(this, null);
+ }
+ },
+
+ /**
+ * Called when AddonUpdateChecker fails the update check
+ *
+ * @param aError
+ * An error status
+ */
+ onUpdateCheckError: function UC_onUpdateCheckError(aError) {
+ XPIProvider.done(this.addon._updateCheck);
+ this.addon._updateCheck = null;
+ this.callListener("onNoCompatibilityUpdateAvailable", createWrapper(this.addon));
+ this.callListener("onNoUpdateAvailable", createWrapper(this.addon));
+ this.callListener("onUpdateFinished", createWrapper(this.addon), aError);
+ },
+
+ /**
+ * Called to cancel an in-progress update check
+ */
+ cancel: function UC_cancel() {
+ let parser = this._parser;
+ if (parser) {
+ this._parser = null;
+ // This will call back to onUpdateCheckError with a CANCELLED error
+ parser.cancel();
+ }
+ }
+};
+
+/**
+ * The AddonInternal is an internal only representation of add-ons. It may
+ * have come from the database (see DBAddonInternal in XPIProviderUtils.jsm)
+ * or an install manifest.
+ */
+function AddonInternal() {
+}
+
+AddonInternal.prototype = {
+ _selectedLocale: null,
+ active: false,
+ visible: false,
+ userDisabled: false,
+ appDisabled: false,
+ softDisabled: false,
+ sourceURI: null,
+ releaseNotesURI: null,
+ foreignInstall: false,
+
+ get selectedLocale() {
+ if (this._selectedLocale)
+ return this._selectedLocale;
+ let locale = findClosestLocale(this.locales);
+ this._selectedLocale = locale ? locale : this.defaultLocale;
+ return this._selectedLocale;
+ },
+
+ get providesUpdatesSecurely() {
+ return !!(this.updateKey || !this.updateURL ||
+ this.updateURL.substring(0, 6) == "https:");
+ },
+
+ get isCompatible() {
+ return this.isCompatibleWith();
+ },
+
+ get disabled() {
+ return (this.userDisabled || this.appDisabled || this.softDisabled);
+ },
+
+ get isPlatformCompatible() {
+ if (this.targetPlatforms.length == 0)
+ return true;
+
+ let matchedOS = false;
+
+ // If any targetPlatform matches the OS and contains an ABI then we will
+ // only match a targetPlatform that contains both the current OS and ABI
+ let needsABI = false;
+
+ // Some platforms do not specify an ABI, test against null in that case.
+ let abi = null;
+ try {
+ abi = Services.appinfo.XPCOMABI;
+ }
+ catch (e) { }
+
+ // Something is causing errors in here
+ try {
+ for (let platform of this.targetPlatforms) {
+ if (platform.os == Services.appinfo.OS) {
+ if (platform.abi) {
+ needsABI = true;
+ if (platform.abi === abi)
+ return true;
+ }
+ else {
+ matchedOS = true;
+ }
+ }
+ }
+ } catch (e) {
+ let message = "Problem with addon " + this.id + " targetPlatforms "
+ + JSON.stringify(this.targetPlatforms);
+ logger.error(message, e);
+ AddonManagerPrivate.recordException("XPI", message, e);
+ // don't trust this add-on
+ return false;
+ }
+
+ return matchedOS && !needsABI;
+ },
+
+ isCompatibleWith: function AddonInternal_isCompatibleWith(aAppVersion, aPlatformVersion) {
+ // Experiments are installed through an external mechanism that
+ // limits target audience to compatible clients. We trust it knows what
+ // it's doing and skip compatibility checks.
+ //
+ // This decision does forfeit defense in depth. If the experiments system
+ // is ever wrong about targeting an add-on to a specific application
+ // or platform, the client will likely see errors.
+ if (this.type == "experiment") {
+ return true;
+ }
+
+ let app = this.matchingTargetApplication;
+ if (!app)
+ return false;
+
+ if (!aAppVersion)
+ aAppVersion = Services.appinfo.version;
+ if (!aPlatformVersion)
+ aPlatformVersion = Services.appinfo.platformVersion;
+
+ this.native = false;
+
+ let version;
+ if (app.id == Services.appinfo.ID) {
+ version = aAppVersion;
+ this.native = true;
+ }
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ else if (app.id == FIREFOX_ID) {
+ version = FIREFOX_APPCOMPATVERSION;
+ if (this.type == "locale")
+ //Never allow language packs in Firefox compatibility mode
+ return false;
+ }
+#endif
+ else if (app.id == TOOLKIT_ID)
+ version = aPlatformVersion
+
+ // Only extensions and dictionaries can be compatible by default; themes
+ // and language packs always use strict compatibility checking.
+ if (this.type in COMPATIBLE_BY_DEFAULT_TYPES &&
+ !AddonManager.strictCompatibility && !this.strictCompatibility &&
+ !this.hasBinaryComponents) {
+
+ // The repository can specify compatibility overrides.
+ // Note: For now, only blacklisting is supported by overrides.
+ if (this._repositoryAddon &&
+ this._repositoryAddon.compatibilityOverrides) {
+ let overrides = this._repositoryAddon.compatibilityOverrides;
+ let override = AddonRepository.findMatchingCompatOverride(this.version,
+ overrides);
+ if (override && override.type == "incompatible")
+ return false;
+ }
+
+ // Extremely old extensions should not be compatible by default.
+ let minCompatVersion;
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ if (app.id == Services.appinfo.ID || app.id == FIREFOX_ID)
+#else
+ if (app.id == Services.appinfo.ID)
+#endif
+ minCompatVersion = XPIProvider.minCompatibleAppVersion;
+ else if (app.id == TOOLKIT_ID)
+ minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
+
+ if (minCompatVersion &&
+ Services.vc.compare(minCompatVersion, app.maxVersion) > 0)
+ return false;
+
+ return Services.vc.compare(version, app.minVersion) >= 0;
+ }
+
+ return (Services.vc.compare(version, app.minVersion) >= 0) &&
+ (Services.vc.compare(version, app.maxVersion) <= 0)
+ },
+
+ get matchingTargetApplication() {
+ let app = null;
+ for (let targetApp of this.targetApplications) {
+ if (targetApp.id == Services.appinfo.ID)
+ return targetApp;
+ if (targetApp.id == TOOLKIT_ID)
+ app = targetApp;
+ }
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ //Special case: check for Firefox TargetApps. this has to be done AFTER
+ //the initial check to make sure appinfo.ID is preferred, even if
+ //Firefox is listed before it in the install manifest.
+ for (let targetApp of this.targetApplications) {
+ if (targetApp.id == FIREFOX_ID) //Firefox GUID
+ return targetApp;
+ }
+#endif
+ // Return toolkit ID if toolkit.
+ return app;
+ },
+
+ get blocklistState() {
+ let staticItem = findMatchingStaticBlocklistItem(this);
+ if (staticItem)
+ return staticItem.level;
+
+ return Blocklist.getAddonBlocklistState(createWrapper(this));
+ },
+
+ get blocklistURL() {
+ let staticItem = findMatchingStaticBlocklistItem(this);
+ if (staticItem) {
+ let url = Services.urlFormatter.formatURLPref("extensions.blocklist.itemURL");
+ return url.replace(/%blockID%/g, staticItem.blockID);
+ }
+
+ return Blocklist.getAddonBlocklistURL(createWrapper(this));
+ },
+
+ applyCompatibilityUpdate: function AddonInternal_applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
+ if (this.strictCompatibility) {
+ return;
+ }
+ this.targetApplications.forEach(function(aTargetApp) {
+ aUpdate.targetApplications.forEach(function(aUpdateTarget) {
+ if (aTargetApp.id == aUpdateTarget.id && (aSyncCompatibility ||
+ Services.vc.compare(aTargetApp.maxVersion, aUpdateTarget.maxVersion) < 0)) {
+ aTargetApp.minVersion = aUpdateTarget.minVersion;
+ aTargetApp.maxVersion = aUpdateTarget.maxVersion;
+ }
+ });
+ });
+ if (aUpdate.multiprocessCompatible !== undefined)
+ this.multiprocessCompatible = aUpdate.multiprocessCompatible;
+ this.appDisabled = !isUsableAddon(this);
+ },
+
+ /**
+ * getDataDirectory tries to execute the callback with two arguments:
+ * 1) the path of the data directory within the profile,
+ * 2) any exception generated from trying to build it.
+ */
+ getDataDirectory: function(callback) {
+ let parentPath = OS.Path.join(OS.Constants.Path.profileDir, "extension-data");
+ let dirPath = OS.Path.join(parentPath, this.id);
+
+ Task.spawn(function*() {
+ yield OS.File.makeDir(parentPath, {ignoreExisting: true});
+ yield OS.File.makeDir(dirPath, {ignoreExisting: true});
+ }).then(() => callback(dirPath, null),
+ e => callback(dirPath, e));
+ },
+
+ /**
+ * toJSON is called by JSON.stringify in order to create a filtered version
+ * of this object to be serialized to a JSON file. A new object is returned
+ * with copies of all non-private properties. Functions, getters and setters
+ * are not copied.
+ *
+ * @param aKey
+ * The key that this object is being serialized as in the JSON.
+ * Unused here since this is always the main object serialized
+ *
+ * @return an object containing copies of the properties of this object
+ * ignoring private properties, functions, getters and setters
+ */
+ toJSON: function AddonInternal_toJSON(aKey) {
+ let obj = {};
+ for (let prop in this) {
+ // Ignore private properties
+ if (prop.substring(0, 1) == "_")
+ continue;
+
+ // Ignore getters
+ if (this.__lookupGetter__(prop))
+ continue;
+
+ // Ignore setters
+ if (this.__lookupSetter__(prop))
+ continue;
+
+ // Ignore functions
+ if (typeof this[prop] == "function")
+ continue;
+
+ obj[prop] = this[prop];
+ }
+
+ return obj;
+ },
+
+ /**
+ * When an add-on install is pending its metadata will be cached in a file.
+ * This method reads particular properties of that metadata that may be newer
+ * than that in the install manifest, like compatibility information.
+ *
+ * @param aObj
+ * A JS object containing the cached metadata
+ */
+ importMetadata: function AddonInternal_importMetaData(aObj) {
+ PENDING_INSTALL_METADATA.forEach(function(aProp) {
+ if (!(aProp in aObj))
+ return;
+
+ this[aProp] = aObj[aProp];
+ }, this);
+
+ // Compatibility info may have changed so update appDisabled
+ this.appDisabled = !isUsableAddon(this);
+ },
+
+ permissions: function AddonInternal_permissions() {
+ let permissions = 0;
+
+ // Add-ons that aren't installed cannot be modified in any way
+ if (!(this.inDatabase))
+ return permissions;
+
+ // Experiments can only be uninstalled. An uninstall reflects the user
+ // intent of "disable this experiment." This is partially managed by the
+ // experiments manager.
+ if (this.type == "experiment") {
+ return AddonManager.PERM_CAN_UNINSTALL;
+ }
+
+ if (!this.appDisabled) {
+ if (this.userDisabled || this.softDisabled) {
+ permissions |= AddonManager.PERM_CAN_ENABLE;
+ }
+ else if (this.type != "theme") {
+ permissions |= AddonManager.PERM_CAN_DISABLE;
+ }
+ }
+
+ // Add-ons that are in locked install locations, or are pending uninstall
+ // cannot be upgraded or uninstalled
+ if (!this._installLocation.locked && !this.pendingUninstall) {
+ // Add-ons that are installed by a file link cannot be upgraded
+ if (!this._installLocation.isLinkedAddon(this.id)) {
+ permissions |= AddonManager.PERM_CAN_UPGRADE;
+ }
+
+ permissions |= AddonManager.PERM_CAN_UNINSTALL;
+ }
+
+ return permissions;
+ },
+};
+
+/**
+ * Creates an AddonWrapper for an AddonInternal.
+ *
+ * @param addon
+ * The AddonInternal to wrap
+ * @return an AddonWrapper or null if addon was null
+ */
+function createWrapper(aAddon) {
+ if (!aAddon)
+ return null;
+ if (!aAddon._wrapper) {
+ aAddon._hasResourceCache = new Map();
+ aAddon._wrapper = new AddonWrapper(aAddon);
+ }
+ return aAddon._wrapper;
+}
+
+/**
+ * The AddonWrapper wraps an Addon to provide the data visible to consumers of
+ * the public API.
+ */
+function AddonWrapper(aAddon) {
+#ifdef MOZ_EM_DEBUG
+ this.__defineGetter__("__AddonInternal__", function AW_debugGetter() {
+ return aAddon;
+ });
+#endif
+
+ function chooseValue(aObj, aProp) {
+ let repositoryAddon = aAddon._repositoryAddon;
+ let objValue = aObj[aProp];
+
+ if (repositoryAddon && (aProp in repositoryAddon) &&
+ (objValue === undefined || objValue === null)) {
+ return [repositoryAddon[aProp], true];
+ }
+
+ return [objValue, false];
+ }
+
+ ["id", "syncGUID", "version", "type", "isCompatible", "isPlatformCompatible",
+ "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
+ "softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
+ "strictCompatibility", "compatibilityOverrides", "updateURL",
+ "getDataDirectory", "multiprocessCompatible", "jetsdk", "native"].forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AddonWrapper_propertyGetter() aAddon[aProp]);
+ }, this);
+
+ ["fullDescription", "developerComments", "eula", "supportURL",
+ "contributionURL", "contributionAmount", "averageRating", "reviewCount",
+ "reviewURL", "totalDownloads", "weeklyDownloads", "dailyUsers",
+ "repositoryStatus"].forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AddonWrapper_repoPropertyGetter() {
+ if (aAddon._repositoryAddon)
+ return aAddon._repositoryAddon[aProp];
+
+ return null;
+ });
+ }, this);
+
+ this.__defineGetter__("aboutURL", function AddonWrapper_aboutURLGetter() {
+ return this.isActive ? aAddon["aboutURL"] : null;
+ });
+
+ ["installDate", "updateDate"].forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AddonWrapper_datePropertyGetter() new Date(aAddon[aProp]));
+ }, this);
+
+ ["sourceURI", "releaseNotesURI"].forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AddonWrapper_URIPropertyGetter() {
+ let [target, fromRepo] = chooseValue(aAddon, aProp);
+ if (!target)
+ return null;
+ if (fromRepo)
+ return target;
+ return NetUtil.newURI(target);
+ });
+ }, this);
+
+ this.__defineGetter__("optionsURL", function AddonWrapper_optionsURLGetter() {
+ if (this.isActive && aAddon.optionsURL)
+ return aAddon.optionsURL;
+
+ if (this.isActive && this.hasResource("options.xul"))
+ return this.getResourceURI("options.xul").spec;
+
+ return null;
+ }, this);
+
+ this.__defineGetter__("optionsType", function AddonWrapper_optionsTypeGetter() {
+ if (!this.isActive)
+ return null;
+
+ let hasOptionsXUL = this.hasResource("options.xul");
+ let hasOptionsURL = !!this.optionsURL;
+
+ if (aAddon.optionsType) {
+ switch (parseInt(aAddon.optionsType, 10)) {
+ case AddonManager.OPTIONS_TYPE_DIALOG:
+ case AddonManager.OPTIONS_TYPE_TAB:
+ return hasOptionsURL ? aAddon.optionsType : null;
+ case AddonManager.OPTIONS_TYPE_INLINE:
+ case AddonManager.OPTIONS_TYPE_INLINE_INFO:
+ return (hasOptionsXUL || hasOptionsURL) ? aAddon.optionsType : null;
+ }
+ return null;
+ }
+
+ if (hasOptionsXUL)
+ return AddonManager.OPTIONS_TYPE_INLINE;
+
+ if (hasOptionsURL)
+ return AddonManager.OPTIONS_TYPE_DIALOG;
+
+ return null;
+ }, this);
+
+ this.__defineGetter__("iconURL", function AddonWrapper_iconURLGetter() {
+ return this.icons[32] || undefined;
+ }, this);
+
+ this.__defineGetter__("icon64URL", function AddonWrapper_icon64URLGetter() {
+ return this.icons[64] || undefined;
+ }, this);
+
+ this.__defineGetter__("icons", function AddonWrapper_iconsGetter() {
+ let icons = {};
+ if (aAddon._repositoryAddon) {
+ for (let size in aAddon._repositoryAddon.icons) {
+ icons[size] = aAddon._repositoryAddon.icons[size];
+ }
+ }
+ if (this.isActive && aAddon.iconURL) {
+ icons[32] = aAddon.iconURL;
+ } else if (this.hasResource("icon.png")) {
+ icons[32] = this.getResourceURI("icon.png").spec;
+ }
+ if (this.isActive && aAddon.icon64URL) {
+ icons[64] = aAddon.icon64URL;
+ } else if (this.hasResource("icon64.png")) {
+ icons[64] = this.getResourceURI("icon64.png").spec;
+ }
+ Object.freeze(icons);
+ return icons;
+ }, this);
+
+ PROP_LOCALE_SINGLE.forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AddonWrapper_singleLocaleGetter() {
+ // Override XPI creator if repository creator is defined
+ if (aProp == "creator" &&
+ aAddon._repositoryAddon && aAddon._repositoryAddon.creator) {
+ return aAddon._repositoryAddon.creator;
+ }
+
+ let result = null;
+
+ if (aAddon.active) {
+ try {
+ let pref = PREF_EM_EXTENSION_FORMAT + aAddon.id + "." + aProp;
+ let value = Preferences.get(pref, null, Ci.nsIPrefLocalizedString);
+ if (value)
+ result = value;
+ }
+ catch (e) {
+ }
+ }
+
+ if (result == null) {
+ if (typeof aAddon.selectedLocale[aProp] == "string" && aAddon.selectedLocale[aProp].length)
+ [result, ] = chooseValue(aAddon.selectedLocale, aProp);
+ else
+ [result, ] = chooseValue(aAddon.defaultLocale, aProp);
+ }
+
+ if (aProp == "creator")
+ return result ? new AddonManagerPrivate.AddonAuthor(result) : null;
+
+ return result;
+ });
+ }, this);
+
+ PROP_LOCALE_MULTI.forEach(function(aProp) {
+ this.__defineGetter__(aProp, function AddonWrapper_multiLocaleGetter() {
+ let results = null;
+ let usedRepository = false;
+
+ if (aAddon.active) {
+ let pref = PREF_EM_EXTENSION_FORMAT + aAddon.id + "." +
+ aProp.substring(0, aProp.length - 1);
+ let list = Services.prefs.getChildList(pref, {});
+ if (list.length > 0) {
+ list.sort();
+ results = [];
+ list.forEach(function(aPref) {
+ let value = Preferences.get(aPref, null, Ci.nsIPrefLocalizedString);
+ if (value)
+ results.push(value);
+ });
+ }
+ }
+
+ if (results == null) {
+ if (aAddon.selectedLocale[aProp] instanceof Array && aAddon.selectedLocale[aProp].length)
+ [results, usedRepository] = chooseValue(aAddon.selectedLocale, aProp);
+ else
+ [results, usedRepository] = chooseValue(aAddon.defaultLocale, aProp);
+ }
+
+ if (results && !usedRepository) {
+ results = results.map(function mapResult(aResult) {
+ return new AddonManagerPrivate.AddonAuthor(aResult);
+ });
+ }
+
+ return results;
+ });
+ }, this);
+
+ this.__defineGetter__("screenshots", function AddonWrapper_screenshotsGetter() {
+ let repositoryAddon = aAddon._repositoryAddon;
+ if (repositoryAddon && ("screenshots" in repositoryAddon)) {
+ let repositoryScreenshots = repositoryAddon.screenshots;
+ if (repositoryScreenshots && repositoryScreenshots.length > 0)
+ return repositoryScreenshots;
+ }
+
+ if (aAddon.type == "theme" && this.hasResource("preview.png")) {
+ let url = this.getResourceURI("preview.png").spec;
+ return [new AddonManagerPrivate.AddonScreenshot(url)];
+ }
+
+ return null;
+ });
+
+ this.__defineGetter__("applyBackgroundUpdates", function AddonWrapper_applyBackgroundUpdatesGetter() {
+ return aAddon.applyBackgroundUpdates;
+ });
+ this.__defineSetter__("applyBackgroundUpdates", function AddonWrapper_applyBackgroundUpdatesSetter(val) {
+ if (this.type == "experiment") {
+ logger.warn("Setting applyBackgroundUpdates on an experiment is not supported.");
+ return;
+ }
+
+ if (val != AddonManager.AUTOUPDATE_DEFAULT &&
+ val != AddonManager.AUTOUPDATE_DISABLE &&
+ val != AddonManager.AUTOUPDATE_ENABLE) {
+ val = val ? AddonManager.AUTOUPDATE_DEFAULT :
+ AddonManager.AUTOUPDATE_DISABLE;
+ }
+
+ if (val == aAddon.applyBackgroundUpdates)
+ return val;
+
+ XPIDatabase.setAddonProperties(aAddon, {
+ applyBackgroundUpdates: val
+ });
+ AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]);
+
+ return val;
+ });
+
+ this.__defineSetter__("syncGUID", function AddonWrapper_syncGUIDGetter(val) {
+ if (aAddon.syncGUID == val)
+ return val;
+
+ if (aAddon.inDatabase)
+ XPIDatabase.setAddonSyncGUID(aAddon, val);
+
+ aAddon.syncGUID = val;
+
+ return val;
+ });
+
+ this.__defineGetter__("install", function AddonWrapper_installGetter() {
+ if (!("_install" in aAddon) || !aAddon._install)
+ return null;
+ return aAddon._install.wrapper;
+ });
+
+ this.__defineGetter__("pendingUpgrade", function AddonWrapper_pendingUpgradeGetter() {
+ return createWrapper(aAddon.pendingUpgrade);
+ });
+
+ this.__defineGetter__("scope", function AddonWrapper_scopeGetter() {
+ if (aAddon._installLocation)
+ return aAddon._installLocation.scope;
+
+ return AddonManager.SCOPE_PROFILE;
+ });
+
+ this.__defineGetter__("pendingOperations", function AddonWrapper_pendingOperationsGetter() {
+ let pending = 0;
+ if (!(aAddon.inDatabase)) {
+ // Add-on is pending install if there is no associated install (shouldn't
+ // happen here) or if the install is in the process of or has successfully
+ // completed the install. If an add-on is pending install then we ignore
+ // any other pending operations.
+ if (!aAddon._install || aAddon._install.state == AddonManager.STATE_INSTALLING ||
+ aAddon._install.state == AddonManager.STATE_INSTALLED)
+ return AddonManager.PENDING_INSTALL;
+ }
+ else if (aAddon.pendingUninstall) {
+ // If an add-on is pending uninstall then we ignore any other pending
+ // operations
+ return AddonManager.PENDING_UNINSTALL;
+ }
+
+ // Extensions have an intentional inconsistency between what the DB says is
+ // enabled and what we say to the ouside world. so we need to cover up that
+ // lie here as well.
+ if (aAddon.type != "experiment") {
+ if (aAddon.active && aAddon.disabled)
+ pending |= AddonManager.PENDING_DISABLE;
+ else if (!aAddon.active && !aAddon.disabled)
+ pending |= AddonManager.PENDING_ENABLE;
+ }
+
+ if (aAddon.pendingUpgrade)
+ pending |= AddonManager.PENDING_UPGRADE;
+
+ return pending;
+ });
+
+ this.__defineGetter__("operationsRequiringRestart", function AddonWrapper_operationsRequiringRestartGetter() {
+ let ops = 0;
+ if (XPIProvider.installRequiresRestart(aAddon))
+ ops |= AddonManager.OP_NEEDS_RESTART_INSTALL;
+ if (XPIProvider.uninstallRequiresRestart(aAddon))
+ ops |= AddonManager.OP_NEEDS_RESTART_UNINSTALL;
+ if (XPIProvider.enableRequiresRestart(aAddon))
+ ops |= AddonManager.OP_NEEDS_RESTART_ENABLE;
+ if (XPIProvider.disableRequiresRestart(aAddon))
+ ops |= AddonManager.OP_NEEDS_RESTART_DISABLE;
+
+ return ops;
+ });
+
+ this.__defineGetter__("isDebuggable", function AddonWrapper_isDebuggable() {
+ return this.isActive && aAddon.bootstrap;
+ });
+
+ this.__defineGetter__("permissions", function AddonWrapper_permisionsGetter() {
+ return aAddon.permissions();
+ });
+
+ this.__defineGetter__("isActive", function AddonWrapper_isActiveGetter() {
+ if (Services.appinfo.inSafeMode)
+ return false;
+ return aAddon.active;
+ });
+
+ this.__defineGetter__("userDisabled", function AddonWrapper_userDisabledGetter() {
+ if (XPIProvider._enabledExperiments.has(aAddon.id)) {
+ return false;
+ }
+
+ return aAddon.softDisabled || aAddon.userDisabled;
+ });
+ this.__defineSetter__("userDisabled", function AddonWrapper_userDisabledSetter(val) {
+ if (val == this.userDisabled) {
+ return val;
+ }
+
+ if (aAddon.type == "experiment") {
+ if (val) {
+ XPIProvider._enabledExperiments.delete(aAddon.id);
+ } else {
+ XPIProvider._enabledExperiments.add(aAddon.id);
+ }
+ }
+
+ if (aAddon.inDatabase) {
+ if (aAddon.type == "theme" && val) {
+ if (aAddon.internalName == XPIProvider.defaultSkin)
+ throw new Error("Cannot disable the default theme");
+ XPIProvider.enableDefaultTheme();
+ }
+ else {
+ XPIProvider.updateAddonDisabledState(aAddon, val);
+ }
+ }
+ else {
+ aAddon.userDisabled = val;
+ // When enabling remove the softDisabled flag
+ if (!val)
+ aAddon.softDisabled = false;
+ }
+
+ return val;
+ });
+
+ this.__defineSetter__("softDisabled", function AddonWrapper_softDisabledSetter(val) {
+ if (val == aAddon.softDisabled)
+ return val;
+
+ if (aAddon.inDatabase) {
+ // When softDisabling a theme just enable the active theme
+ if (aAddon.type == "theme" && val && !aAddon.userDisabled) {
+ if (aAddon.internalName == XPIProvider.defaultSkin)
+ throw new Error("Cannot disable the default theme");
+ XPIProvider.enableDefaultTheme();
+ }
+ else {
+ XPIProvider.updateAddonDisabledState(aAddon, undefined, val);
+ }
+ }
+ else {
+ // Only set softDisabled if not already disabled
+ if (!aAddon.userDisabled)
+ aAddon.softDisabled = val;
+ }
+
+ return val;
+ });
+
+ this.isCompatibleWith = function AddonWrapper_isCompatiblewith(aAppVersion, aPlatformVersion) {
+ return aAddon.isCompatibleWith(aAppVersion, aPlatformVersion);
+ };
+
+ this.uninstall = function AddonWrapper_uninstall(alwaysAllowUndo) {
+ XPIProvider.uninstallAddon(aAddon, alwaysAllowUndo);
+ };
+
+ this.cancelUninstall = function AddonWrapper_cancelUninstall() {
+ XPIProvider.cancelUninstallAddon(aAddon);
+ };
+
+ this.findUpdates = function AddonWrapper_findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
+ // Short-circuit updates for experiments because updates are handled
+ // through the Experiments Manager.
+ if (this.type == "experiment") {
+ AddonManagerPrivate.callNoUpdateListeners(this, aListener, aReason,
+ aAppVersion, aPlatformVersion);
+ return;
+ }
+
+ new UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion);
+ };
+
+ // Returns true if there was an update in progress, false if there was no update to cancel
+ this.cancelUpdate = function AddonWrapper_cancelUpdate() {
+ if (aAddon._updateCheck) {
+ aAddon._updateCheck.cancel();
+ return true;
+ }
+ return false;
+ };
+
+ this.hasResource = function AddonWrapper_hasResource(aPath) {
+ if (aAddon._hasResourceCache.has(aPath))
+ return aAddon._hasResourceCache.get(aPath);
+
+ let bundle = aAddon._sourceBundle.clone();
+
+ // Bundle may not exist any more if the addon has just been uninstalled,
+ // but explicitly first checking .exists() results in unneeded file I/O.
+ try {
+ var isDir = bundle.isDirectory();
+ } catch (e) {
+ aAddon._hasResourceCache.set(aPath, false);
+ return false;
+ }
+
+ if (isDir) {
+ if (aPath) {
+ aPath.split("/").forEach(function(aPart) {
+ bundle.append(aPart);
+ });
+ }
+ let result = bundle.exists();
+ aAddon._hasResourceCache.set(aPath, result);
+ return result;
+ }
+
+ let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipReader.open(bundle);
+ let result = zipReader.hasEntry(aPath);
+ aAddon._hasResourceCache.set(aPath, result);
+ return result;
+ }
+ catch (e) {
+ aAddon._hasResourceCache.set(aPath, false);
+ return false;
+ }
+ finally {
+ zipReader.close();
+ }
+ },
+
+ /**
+ * Returns a URI to the selected resource or to the add-on bundle if aPath
+ * is null. URIs to the bundle will always be file: URIs. URIs to resources
+ * will be file: URIs if the add-on is unpacked or jar: URIs if the add-on is
+ * still an XPI file.
+ *
+ * @param aPath
+ * The path in the add-on to get the URI for or null to get a URI to
+ * the file or directory the add-on is installed as.
+ * @return an nsIURI
+ */
+ this.getResourceURI = function AddonWrapper_getResourceURI(aPath) {
+ if (!aPath)
+ return NetUtil.newURI(aAddon._sourceBundle);
+
+ return getURIForResourceInFile(aAddon._sourceBundle, aPath);
+ }
+}
+
+/**
+ * An object which identifies a directory install location for add-ons. The
+ * location consists of a directory which contains the add-ons installed in the
+ * location.
+ *
+ * Each add-on installed in the location is either a directory containing the
+ * add-on's files or a text file containing an absolute path to the directory
+ * containing the add-ons files. The directory or text file must have the same
+ * name as the add-on's ID.
+ *
+ * There may also a special directory named "staged" which can contain
+ * directories with the same name as an add-on ID. If the directory is empty
+ * then it means the add-on will be uninstalled from this location during the
+ * next startup. If the directory contains the add-on's files then they will be
+ * installed during the next startup.
+ *
+ * @param aName
+ * The string identifier for the install location
+ * @param aDirectory
+ * The nsIFile directory for the install location
+ * @param aScope
+ * The scope of add-ons installed in this location
+ * @param aLocked
+ * true if add-ons cannot be installed, uninstalled or upgraded in the
+ * install location
+ */
+function DirectoryInstallLocation(aName, aDirectory, aScope, aLocked) {
+ this._name = aName;
+ this.locked = aLocked;
+ this._directory = aDirectory;
+ this._scope = aScope
+ this._IDToFileMap = {};
+ this._FileToIDMap = {};
+ this._linkedAddons = [];
+ this._stagingDirLock = 0;
+
+ if (!aDirectory.exists())
+ return;
+ if (!aDirectory.isDirectory())
+ throw new Error("Location must be a directory.");
+
+ this._readAddons();
+}
+
+DirectoryInstallLocation.prototype = {
+ _name : "",
+ _directory : null,
+ _IDToFileMap : null, // mapping from add-on ID to nsIFile
+ _FileToIDMap : null, // mapping from add-on path to add-on ID
+
+ /**
+ * Reads a directory linked to in a file.
+ *
+ * @param file
+ * The file containing the directory path
+ * @return An nsIFile object representing the linked directory.
+ */
+ _readDirectoryFromFile: function DirInstallLocation__readDirectoryFromFile(aFile) {
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fis.init(aFile, -1, -1, false);
+ let line = { value: "" };
+ if (fis instanceof Ci.nsILineInputStream)
+ fis.readLine(line);
+ fis.close();
+ if (line.value) {
+ let linkedDirectory = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsIFile);
+
+ try {
+ linkedDirectory.initWithPath(line.value);
+ }
+ catch (e) {
+ linkedDirectory.setRelativeDescriptor(aFile.parent, line.value);
+ }
+
+ if (!linkedDirectory.exists()) {
+ logger.warn("File pointer " + aFile.path + " points to " + linkedDirectory.path +
+ " which does not exist");
+ return null;
+ }
+
+ if (!linkedDirectory.isDirectory()) {
+ logger.warn("File pointer " + aFile.path + " points to " + linkedDirectory.path +
+ " which is not a directory");
+ return null;
+ }
+
+ return linkedDirectory;
+ }
+
+ logger.warn("File pointer " + aFile.path + " does not contain a path");
+ return null;
+ },
+
+ /**
+ * Finds all the add-ons installed in this location.
+ */
+ _readAddons: function DirInstallLocation__readAddons() {
+ // Use a snapshot of the directory contents to avoid possible issues with
+ // iterating over a directory while removing files from it (the YAFFS2
+ // embedded filesystem has this issue, see bug 772238).
+ let entries = getDirectoryEntries(this._directory);
+ for (let entry of entries) {
+ let id = entry.leafName;
+
+ if (id == DIR_STAGE || id == DIR_XPI_STAGE || id == DIR_TRASH)
+ continue;
+
+ let directLoad = false;
+ if (entry.isFile() &&
+ id.substring(id.length - 4).toLowerCase() == ".xpi") {
+ directLoad = true;
+ id = id.substring(0, id.length - 4);
+ }
+
+ if (!gIDTest.test(id)) {
+ logger.debug("Ignoring file entry whose name is not a valid add-on ID: " +
+ entry.path);
+ continue;
+ }
+
+ if (entry.isFile() && !directLoad) {
+ let newEntry = this._readDirectoryFromFile(entry);
+ if (!newEntry) {
+ logger.debug("Deleting stale pointer file " + entry.path);
+ try {
+ entry.remove(true);
+ }
+ catch (e) {
+ logger.warn("Failed to remove stale pointer file " + entry.path, e);
+ // Failing to remove the stale pointer file is ignorable
+ }
+ continue;
+ }
+
+ entry = newEntry;
+ this._linkedAddons.push(id);
+ }
+
+ this._IDToFileMap[id] = entry;
+ this._FileToIDMap[entry.path] = id;
+ XPIProvider._addURIMapping(id, entry);
+ }
+ },
+
+ /**
+ * Gets the name of this install location.
+ */
+ get name() {
+ return this._name;
+ },
+
+ /**
+ * Gets the scope of this install location.
+ */
+ get scope() {
+ return this._scope;
+ },
+
+ /**
+ * Gets an array of nsIFiles for add-ons installed in this location.
+ */
+ get addonLocations() {
+ let locations = [];
+ for (let id in this._IDToFileMap) {
+ locations.push(this._IDToFileMap[id].clone());
+ }
+ return locations;
+ },
+
+ /**
+ * Gets the staging directory to put add-ons that are pending install and
+ * uninstall into.
+ *
+ * @return an nsIFile
+ */
+ getStagingDir: function DirInstallLocation_getStagingDir() {
+ let dir = this._directory.clone();
+ dir.append(DIR_STAGE);
+ return dir;
+ },
+
+ requestStagingDir: function() {
+ this._stagingDirLock++;
+
+ if (this._stagingDirPromise)
+ return this._stagingDirPromise;
+
+ OS.File.makeDir(this._directory.path);
+ let stagepath = OS.Path.join(this._directory.path, DIR_STAGE);
+ return this._stagingDirPromise = OS.File.makeDir(stagepath).then(null, (e) => {
+ if (e instanceof OS.File.Error && e.becauseExists)
+ return;
+ logger.error("Failed to create staging directory", e);
+ throw e;
+ });
+ },
+
+ releaseStagingDir: function() {
+ this._stagingDirLock--;
+
+ if (this._stagingDirLock == 0) {
+ this._stagingDirPromise = null;
+ this.cleanStagingDir();
+ }
+
+ return Promise.resolve();
+ },
+
+ /**
+ * Removes the specified files or directories in the staging directory and
+ * then if the staging directory is empty attempts to remove it.
+ *
+ * @param aLeafNames
+ * An array of file or directory to remove from the directory, the
+ * array may be empty
+ */
+ cleanStagingDir: function(aLeafNames = []) {
+ let dir = this.getStagingDir();
+
+ for (let name of aLeafNames) {
+ let file = dir.clone();
+ file.append(name);
+ recursiveRemove(file);
+ }
+
+ if (this._stagingDirLock > 0)
+ return;
+
+ let dirEntries = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
+ try {
+ if (dirEntries.nextFile)
+ return;
+ }
+ finally {
+ dirEntries.close();
+ }
+
+ try {
+ setFilePermissions(dir, FileUtils.PERMS_DIRECTORY);
+ dir.remove(false);
+ }
+ catch (e) {
+ logger.warn("Failed to remove staging dir", e);
+ // Failing to remove the staging directory is ignorable
+ }
+ },
+
+ /**
+ * Gets the directory used by old versions for staging XPI and JAR files ready
+ * to be installed.
+ *
+ * @return an nsIFile
+ */
+ getXPIStagingDir: function DirInstallLocation_getXPIStagingDir() {
+ let dir = this._directory.clone();
+ dir.append(DIR_XPI_STAGE);
+ return dir;
+ },
+
+ /**
+ * Returns a directory that is normally on the same filesystem as the rest of
+ * the install location and can be used for temporarily storing files during
+ * safe move operations. Calling this method will delete the existing trash
+ * directory and its contents.
+ *
+ * @return an nsIFile
+ */
+ getTrashDir: function DirInstallLocation_getTrashDir() {
+ let trashDir = this._directory.clone();
+ trashDir.append(DIR_TRASH);
+ let trashDirExists = trashDir.exists();
+ try {
+ if (trashDirExists)
+ recursiveRemove(trashDir);
+ trashDirExists = false;
+ } catch (e) {
+ logger.warn("Failed to remove trash directory", e);
+ }
+ if (!trashDirExists)
+ trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ return trashDir;
+ },
+
+ /**
+ * Installs an add-on into the install location.
+ *
+ * @param aId
+ * The ID of the add-on to install
+ * @param aSource
+ * The source nsIFile to install from
+ * @param aExistingAddonID
+ * The ID of an existing add-on to uninstall at the same time
+ * @param aCopy
+ * If false the source files will be moved to the new location,
+ * otherwise they will only be copied
+ * @return an nsIFile indicating where the add-on was installed to
+ */
+ installAddon: function DirInstallLocation_installAddon(aId, aSource,
+ aExistingAddonID,
+ aCopy) {
+ let trashDir = this.getTrashDir();
+
+ let transaction = new SafeInstallOperation();
+
+ let self = this;
+ function moveOldAddon(aId) {
+ let file = self._directory.clone();
+ file.append(aId);
+
+ if (file.exists())
+ transaction.moveUnder(file, trashDir);
+
+ file = self._directory.clone();
+ file.append(aId + ".xpi");
+ if (file.exists()) {
+ flushJarCache(file);
+ transaction.moveUnder(file, trashDir);
+ }
+ }
+
+ // If any of these operations fails the finally block will clean up the
+ // temporary directory
+ try {
+ moveOldAddon(aId);
+ if (aExistingAddonID && aExistingAddonID != aId) {
+ moveOldAddon(aExistingAddonID);
+
+ {
+ // Move the data directories.
+ /* XXX ajvincent We can't use OS.File: installAddon isn't compatible
+ * with Promises, nor is SafeInstallOperation. Bug 945540 has been filed
+ * for porting to OS.File.
+ */
+ let oldDataDir = FileUtils.getDir(
+ KEY_PROFILEDIR, ["extension-data", aExistingAddonID], false, true
+ );
+
+ if (oldDataDir.exists()) {
+ let newDataDir = FileUtils.getDir(
+ KEY_PROFILEDIR, ["extension-data", aId], false, true
+ );
+ if (newDataDir.exists()) {
+ let trashData = trashDir.clone();
+ trashData.append("data-directory");
+ transaction.moveUnder(newDataDir, trashData);
+ }
+
+ transaction.moveTo(oldDataDir, newDataDir);
+ }
+ }
+ }
+
+ if (aCopy) {
+ transaction.copy(aSource, this._directory);
+ }
+ else {
+ if (aSource.isFile())
+ flushJarCache(aSource);
+
+ transaction.moveUnder(aSource, this._directory);
+ }
+ }
+ finally {
+ // It isn't ideal if this cleanup fails but it isn't worth rolling back
+ // the install because of it.
+ try {
+ recursiveRemove(trashDir);
+ }
+ catch (e) {
+ logger.warn("Failed to remove trash directory when installing " + aId, e);
+ }
+ }
+
+ let newFile = this._directory.clone();
+ newFile.append(aSource.leafName);
+ try {
+ newFile.lastModifiedTime = Date.now();
+ } catch (e) {
+ logger.warn("failed to set lastModifiedTime on " + newFile.path, e);
+ }
+ this._FileToIDMap[newFile.path] = aId;
+ this._IDToFileMap[aId] = newFile;
+ XPIProvider._addURIMapping(aId, newFile);
+
+ if (aExistingAddonID && aExistingAddonID != aId &&
+ aExistingAddonID in this._IDToFileMap) {
+ delete this._FileToIDMap[this._IDToFileMap[aExistingAddonID]];
+ delete this._IDToFileMap[aExistingAddonID];
+ }
+
+ return newFile;
+ },
+
+ /**
+ * Uninstalls an add-on from this location.
+ *
+ * @param aId
+ * The ID of the add-on to uninstall
+ * @throws if the ID does not match any of the add-ons installed
+ */
+ uninstallAddon: function DirInstallLocation_uninstallAddon(aId) {
+ let file = this._IDToFileMap[aId];
+ if (!file) {
+ logger.warn("Attempted to remove " + aId + " from " +
+ this._name + " but it was already gone");
+ return;
+ }
+
+ file = this._directory.clone();
+ file.append(aId);
+ if (!file.exists())
+ file.leafName += ".xpi";
+
+ if (!file.exists()) {
+ logger.warn("Attempted to remove " + aId + " from " +
+ this._name + " but it was already gone");
+
+ delete this._FileToIDMap[file.path];
+ delete this._IDToFileMap[aId];
+ return;
+ }
+
+ let trashDir = this.getTrashDir();
+
+ if (file.leafName != aId) {
+ logger.debug("uninstallAddon: flushing jar cache " + file.path + " for addon " + aId);
+ flushJarCache(file);
+ }
+
+ let transaction = new SafeInstallOperation();
+
+ try {
+ transaction.moveUnder(file, trashDir);
+ }
+ finally {
+ // It isn't ideal if this cleanup fails, but it is probably better than
+ // rolling back the uninstall at this point
+ try {
+ recursiveRemove(trashDir);
+ }
+ catch (e) {
+ logger.warn("Failed to remove trash directory when uninstalling " + aId, e);
+ }
+ }
+
+ delete this._FileToIDMap[file.path];
+ delete this._IDToFileMap[aId];
+ },
+
+ /**
+ * Gets the ID of the add-on installed in the given nsIFile.
+ *
+ * @param aFile
+ * The nsIFile to look in
+ * @return the ID
+ * @throws if the file does not represent an installed add-on
+ */
+ getIDForLocation: function DirInstallLocation_getIDForLocation(aFile) {
+ if (aFile.path in this._FileToIDMap)
+ return this._FileToIDMap[aFile.path];
+ throw new Error("Unknown add-on location " + aFile.path);
+ },
+
+ /**
+ * Gets the directory that the add-on with the given ID is installed in.
+ *
+ * @param aId
+ * The ID of the add-on
+ * @return The nsIFile
+ * @throws if the ID does not match any of the add-ons installed
+ */
+ getLocationForID: function DirInstallLocation_getLocationForID(aId) {
+ if (aId in this._IDToFileMap)
+ return this._IDToFileMap[aId].clone();
+ throw new Error("Unknown add-on ID " + aId);
+ },
+
+ /**
+ * Returns true if the given addon was installed in this location by a text
+ * file pointing to its real path.
+ *
+ * @param aId
+ * The ID of the addon
+ */
+ isLinkedAddon: function DirInstallLocation__isLinkedAddon(aId) {
+ return this._linkedAddons.indexOf(aId) != -1;
+ }
+};
+
+#ifdef XP_WIN
+/**
+ * An object that identifies a registry install location for add-ons. The location
+ * consists of a registry key which contains string values mapping ID to the
+ * path where an add-on is installed
+ *
+ * @param aName
+ * The string identifier of this Install Location.
+ * @param aRootKey
+ * The root key (one of the ROOT_KEY_ values from nsIWindowsRegKey).
+ * @param scope
+ * The scope of add-ons installed in this location
+ */
+function WinRegInstallLocation(aName, aRootKey, aScope) {
+ this.locked = true;
+ this._name = aName;
+ this._rootKey = aRootKey;
+ this._scope = aScope;
+ this._IDToFileMap = {};
+ this._FileToIDMap = {};
+
+ let path = this._appKeyPath + "\\Extensions";
+ let key = Cc["@mozilla.org/windows-registry-key;1"].
+ createInstance(Ci.nsIWindowsRegKey);
+
+ // Reading the registry may throw an exception, and that's ok. In error
+ // cases, we just leave ourselves in the empty state.
+ try {
+ key.open(this._rootKey, path, Ci.nsIWindowsRegKey.ACCESS_READ);
+ }
+ catch (e) {
+ return;
+ }
+
+ this._readAddons(key);
+ key.close();
+}
+
+WinRegInstallLocation.prototype = {
+ _name : "",
+ _rootKey : null,
+ _scope : null,
+ _IDToFileMap : null, // mapping from ID to nsIFile
+ _FileToIDMap : null, // mapping from path to ID
+
+ /**
+ * Retrieves the path of this Application's data key in the registry.
+ */
+ get _appKeyPath() {
+ let appVendor = Services.appinfo.vendor;
+ let appName = Services.appinfo.name;
+
+#ifdef MOZ_THUNDERBIRD
+ // XXX Thunderbird doesn't specify a vendor string
+ if (appVendor == "")
+ appVendor = "Mozilla";
+#endif
+
+ // XULRunner-based apps may intentionally not specify a vendor
+ if (appVendor != "")
+ appVendor += "\\";
+
+ return "SOFTWARE\\" + appVendor + appName;
+ },
+
+ /**
+ * Read the registry and build a mapping between ID and path for each
+ * installed add-on.
+ *
+ * @param key
+ * The key that contains the ID to path mapping
+ */
+ _readAddons: function RegInstallLocation__readAddons(aKey) {
+ let count = aKey.valueCount;
+ for (let i = 0; i < count; ++i) {
+ let id = aKey.getValueName(i);
+
+ let file = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsIFile);
+ file.initWithPath(aKey.readStringValue(id));
+
+ if (!file.exists()) {
+ logger.warn("Ignoring missing add-on in " + file.path);
+ continue;
+ }
+
+ this._IDToFileMap[id] = file;
+ this._FileToIDMap[file.path] = id;
+ XPIProvider._addURIMapping(id, file);
+ }
+ },
+
+ /**
+ * Gets the name of this install location.
+ */
+ get name() {
+ return this._name;
+ },
+
+ /**
+ * Gets the scope of this install location.
+ */
+ get scope() {
+ return this._scope;
+ },
+
+ /**
+ * Gets an array of nsIFiles for add-ons installed in this location.
+ */
+ get addonLocations() {
+ let locations = [];
+ for (let id in this._IDToFileMap) {
+ locations.push(this._IDToFileMap[id].clone());
+ }
+ return locations;
+ },
+
+ /**
+ * Gets the ID of the add-on installed in the given nsIFile.
+ *
+ * @param aFile
+ * The nsIFile to look in
+ * @return the ID
+ * @throws if the file does not represent an installed add-on
+ */
+ getIDForLocation: function RegInstallLocation_getIDForLocation(aFile) {
+ if (aFile.path in this._FileToIDMap)
+ return this._FileToIDMap[aFile.path];
+ throw new Error("Unknown add-on location");
+ },
+
+ /**
+ * Gets the nsIFile that the add-on with the given ID is installed in.
+ *
+ * @param aId
+ * The ID of the add-on
+ * @return the nsIFile
+ */
+ getLocationForID: function RegInstallLocation_getLocationForID(aId) {
+ if (aId in this._IDToFileMap)
+ return this._IDToFileMap[aId].clone();
+ throw new Error("Unknown add-on ID");
+ },
+
+ /**
+ * @see DirectoryInstallLocation
+ */
+ isLinkedAddon: function RegInstallLocation_isLinkedAddon(aId) {
+ return true;
+ }
+};
+#endif
+
+let addonTypes = [
+ new AddonManagerPrivate.AddonType("extension", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 4000,
+ AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
+ new AddonManagerPrivate.AddonType("theme", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 5000),
+ new AddonManagerPrivate.AddonType("dictionary", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 7000,
+ AddonManager.TYPE_UI_HIDE_EMPTY | AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
+ new AddonManagerPrivate.AddonType("locale", URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 8000,
+ AddonManager.TYPE_UI_HIDE_EMPTY | AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
+];
+
+// We only register experiments support if the application supports them.
+// Ideally, we would install an observer to watch the pref. Installing
+// an observer for this pref is not necessary here and may be buggy with
+// regards to registering this XPIProvider twice.
+if (Preferences.get("experiments.supported", false)) {
+ addonTypes.push(
+ new AddonManagerPrivate.AddonType("experiment",
+ URI_EXTENSION_STRINGS,
+ STRING_TYPE_NAME,
+ AddonManager.VIEW_TYPE_LIST, 11000,
+ AddonManager.TYPE_UI_HIDE_EMPTY | AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL));
+}
+
+AddonManagerPrivate.registerProvider(XPIProvider, addonTypes);
diff --git a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
new file mode 100644
index 000000000..d4798b726
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -0,0 +1,1481 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
+ "resource://gre/modules/addons/AddonRepository.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
+ "resource://gre/modules/DeferredSave.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.xpi-utils";
+
+// Create a new logger for use by the Addons XPI Provider Utils
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+const KEY_PROFILEDIR = "ProfD";
+const FILE_DATABASE = "extensions.sqlite";
+const FILE_JSON_DB = "extensions.json";
+const FILE_OLD_DATABASE = "extensions.rdf";
+const FILE_XPI_ADDONS_LIST = "extensions.ini";
+
+// The value for this is in Makefile.in
+#expand const DB_SCHEMA = __MOZ_EXTENSIONS_DB_SCHEMA__;
+
+// The last version of DB_SCHEMA implemented in SQLITE
+const LAST_SQLITE_DB_SCHEMA = 14;
+const PREF_DB_SCHEMA = "extensions.databaseSchema";
+const PREF_PENDING_OPERATIONS = "extensions.pendingOperations";
+const PREF_EM_ENABLED_ADDONS = "extensions.enabledAddons";
+const PREF_EM_DSS_ENABLED = "extensions.dss.enabled";
+
+// Properties that only exist in the database
+const DB_METADATA = ["syncGUID",
+ "installDate",
+ "updateDate",
+ "size",
+ "sourceURI",
+ "releaseNotesURI",
+ "applyBackgroundUpdates"];
+const DB_BOOL_METADATA = ["visible", "active", "userDisabled", "appDisabled",
+ "pendingUninstall", "bootstrap", "skinnable",
+ "softDisabled", "isForeignInstall",
+ "hasBinaryComponents", "strictCompatibility"];
+
+// Properties to save in JSON file
+const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
+ "internalName", "updateURL", "updateKey", "optionsURL",
+ "optionsType", "aboutURL", "iconURL", "icon64URL",
+ "defaultLocale", "visible", "active", "userDisabled",
+ "appDisabled", "pendingUninstall", "descriptor", "installDate",
+ "updateDate", "applyBackgroundUpdates", "bootstrap",
+ "skinnable", "size", "sourceURI", "releaseNotesURI",
+ "softDisabled", "foreignInstall", "hasBinaryComponents",
+ "strictCompatibility", "locales", "targetApplications",
+ "targetPlatforms", "multiprocessCompatible", "jetsdk", "native"];
+
+// Time to wait before async save of XPI JSON database, in milliseconds
+const ASYNC_SAVE_DELAY_MS = 20;
+
+const PREFIX_ITEM_URI = "urn:mozilla:item:";
+const RDFURI_ITEM_ROOT = "urn:mozilla:item:root"
+const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
+
+XPCOMUtils.defineLazyServiceGetter(this, "gRDF", "@mozilla.org/rdf/rdf-service;1",
+ Ci.nsIRDFService);
+
+function EM_R(aProperty) {
+ return gRDF.GetResource(PREFIX_NS_EM + aProperty);
+}
+
+/**
+ * Converts an RDF literal, resource or integer into a string.
+ *
+ * @param aLiteral
+ * The RDF object to convert
+ * @return a string if the object could be converted or null
+ */
+function getRDFValue(aLiteral) {
+ if (aLiteral instanceof Ci.nsIRDFLiteral)
+ return aLiteral.Value;
+ if (aLiteral instanceof Ci.nsIRDFResource)
+ return aLiteral.Value;
+ if (aLiteral instanceof Ci.nsIRDFInt)
+ return aLiteral.Value;
+ return null;
+}
+
+/**
+ * Gets an RDF property as a string
+ *
+ * @param aDs
+ * The RDF datasource to read the property from
+ * @param aResource
+ * The RDF resource to read the property from
+ * @param aProperty
+ * The property to read
+ * @return a string if the property existed or null
+ */
+function getRDFProperty(aDs, aResource, aProperty) {
+ return getRDFValue(aDs.GetTarget(aResource, EM_R(aProperty), true));
+}
+
+/**
+ * Asynchronously fill in the _repositoryAddon field for one addon
+ */
+function getRepositoryAddon(aAddon, aCallback) {
+ if (!aAddon) {
+ aCallback(aAddon);
+ return;
+ }
+ function completeAddon(aRepositoryAddon) {
+ aAddon._repositoryAddon = aRepositoryAddon;
+ aAddon.compatibilityOverrides = aRepositoryAddon ?
+ aRepositoryAddon.compatibilityOverrides :
+ null;
+ aCallback(aAddon);
+ }
+ AddonRepository.getCachedAddonByID(aAddon.id, completeAddon);
+}
+
+/**
+ * Wrap an API-supplied function in an exception handler to make it safe to call
+ */
+function makeSafe(aCallback) {
+ return function(...aArgs) {
+ try {
+ aCallback(...aArgs);
+ }
+ catch(ex) {
+ logger.warn("XPI Database callback failed", ex);
+ }
+ }
+}
+
+/**
+ * A helper method to asynchronously call a function on an array
+ * of objects, calling a callback when function(x) has been gathered
+ * for every element of the array.
+ * WARNING: not currently error-safe; if the async function does not call
+ * our internal callback for any of the array elements, asyncMap will not
+ * call the callback parameter.
+ *
+ * @param aObjects
+ * The array of objects to process asynchronously
+ * @param aMethod
+ * Function with signature function(object, function aCallback(f_of_object))
+ * @param aCallback
+ * Function with signature f([aMethod(object)]), called when all values
+ * are available
+ */
+function asyncMap(aObjects, aMethod, aCallback) {
+ var resultsPending = aObjects.length;
+ var results = []
+ if (resultsPending == 0) {
+ aCallback(results);
+ return;
+ }
+
+ function asyncMap_gotValue(aIndex, aValue) {
+ results[aIndex] = aValue;
+ if (--resultsPending == 0) {
+ aCallback(results);
+ }
+ }
+
+ aObjects.map(function asyncMap_each(aObject, aIndex, aArray) {
+ try {
+ aMethod(aObject, function asyncMap_callback(aResult) {
+ asyncMap_gotValue(aIndex, aResult);
+ });
+ }
+ catch (e) {
+ logger.warn("Async map function failed", e);
+ asyncMap_gotValue(aIndex, undefined);
+ }
+ });
+}
+
+/**
+ * A generator to synchronously return result rows from an mozIStorageStatement.
+ *
+ * @param aStatement
+ * The statement to execute
+ */
+function resultRows(aStatement) {
+ try {
+ while (stepStatement(aStatement))
+ yield aStatement.row;
+ }
+ finally {
+ aStatement.reset();
+ }
+}
+
+/**
+ * A helper function to log an SQL error.
+ *
+ * @param aError
+ * The storage error code associated with the error
+ * @param aErrorString
+ * An error message
+ */
+function logSQLError(aError, aErrorString) {
+ logger.error("SQL error " + aError + ": " + aErrorString);
+}
+
+/**
+ * A helper function to log any errors that occur during async statements.
+ *
+ * @param aError
+ * A mozIStorageError to log
+ */
+function asyncErrorLogger(aError) {
+ logSQLError(aError.result, aError.message);
+}
+
+/**
+ * A helper function to step a statement synchronously and log any error that
+ * occurs.
+ *
+ * @param aStatement
+ * A mozIStorageStatement to execute
+ */
+function stepStatement(aStatement) {
+ try {
+ return aStatement.executeStep();
+ }
+ catch (e) {
+ logSQLError(XPIDatabase.connection.lastError,
+ XPIDatabase.connection.lastErrorString);
+ throw e;
+ }
+}
+
+/**
+ * Copies properties from one object to another. If no target object is passed
+ * a new object will be created and returned.
+ *
+ * @param aObject
+ * An object to copy from
+ * @param aProperties
+ * An array of properties to be copied
+ * @param aTarget
+ * An optional target object to copy the properties to
+ * @return the object that the properties were copied onto
+ */
+function copyProperties(aObject, aProperties, aTarget) {
+ if (!aTarget)
+ aTarget = {};
+ aProperties.forEach(function(aProp) {
+ aTarget[aProp] = aObject[aProp];
+ });
+ return aTarget;
+}
+
+/**
+ * Copies properties from a mozIStorageRow to an object. If no target object is
+ * passed a new object will be created and returned.
+ *
+ * @param aRow
+ * A mozIStorageRow to copy from
+ * @param aProperties
+ * An array of properties to be copied
+ * @param aTarget
+ * An optional target object to copy the properties to
+ * @return the object that the properties were copied onto
+ */
+function copyRowProperties(aRow, aProperties, aTarget) {
+ if (!aTarget)
+ aTarget = {};
+ aProperties.forEach(function(aProp) {
+ aTarget[aProp] = aRow.getResultByName(aProp);
+ });
+ return aTarget;
+}
+
+/**
+ * The DBAddonInternal is a special AddonInternal that has been retrieved from
+ * the database. The constructor will initialize the DBAddonInternal with a set
+ * of fields, which could come from either the JSON store or as an
+ * XPIProvider.AddonInternal created from an addon's manifest
+ * @constructor
+ * @param aLoaded
+ * Addon data fields loaded from JSON or the addon manifest.
+ */
+function DBAddonInternal(aLoaded) {
+ copyProperties(aLoaded, PROP_JSON_FIELDS, this);
+
+ if (aLoaded._installLocation) {
+ this._installLocation = aLoaded._installLocation;
+ this.location = aLoaded._installLocation._name;
+ }
+ else if (aLoaded.location) {
+ this._installLocation = XPIProvider.installLocationsByName[this.location];
+ }
+
+ this._key = this.location + ":" + this.id;
+
+ try {
+ this._sourceBundle = this._installLocation.getLocationForID(this.id);
+ }
+ catch (e) {
+ // An exception will be thrown if the add-on appears in the database but
+ // not on disk. In general this should only happen during startup as
+ // this change is being detected.
+ }
+
+ XPCOMUtils.defineLazyGetter(this, "pendingUpgrade",
+ function DBA_pendingUpgradeGetter() {
+ for (let install of XPIProvider.installs) {
+ if (install.state == AddonManager.STATE_INSTALLED &&
+ !(install.addon.inDatabase) &&
+ install.addon.id == this.id &&
+ install.installLocation == this._installLocation) {
+ delete this.pendingUpgrade;
+ return this.pendingUpgrade = install.addon;
+ }
+ };
+ return null;
+ });
+}
+
+function DBAddonInternalPrototype()
+{
+ this.applyCompatibilityUpdate =
+ function(aUpdate, aSyncCompatibility) {
+ this.targetApplications.forEach(function(aTargetApp) {
+ aUpdate.targetApplications.forEach(function(aUpdateTarget) {
+ if (aTargetApp.id == aUpdateTarget.id && (aSyncCompatibility ||
+ Services.vc.compare(aTargetApp.maxVersion, aUpdateTarget.maxVersion) < 0)) {
+ aTargetApp.minVersion = aUpdateTarget.minVersion;
+ aTargetApp.maxVersion = aUpdateTarget.maxVersion;
+ XPIDatabase.saveChanges();
+ }
+ });
+ });
+ if (aUpdate.multiprocessCompatible !== undefined &&
+ aUpdate.multiprocessCompatible != this.multiprocessCompatible) {
+ this.multiprocessCompatible = aUpdate.multiprocessCompatible;
+ XPIDatabase.saveChanges();
+ }
+ XPIProvider.updateAddonDisabledState(this);
+ };
+
+ this.toJSON =
+ function() {
+ return copyProperties(this, PROP_JSON_FIELDS);
+ };
+
+ Object.defineProperty(this, "inDatabase",
+ { get: function() { return true; },
+ enumerable: true,
+ configurable: true });
+}
+DBAddonInternalPrototype.prototype = AddonInternal.prototype;
+
+DBAddonInternal.prototype = new DBAddonInternalPrototype();
+
+/**
+ * Internal interface: find an addon from an already loaded addonDB
+ */
+function _findAddon(addonDB, aFilter) {
+ for (let addon of addonDB.values()) {
+ if (aFilter(addon)) {
+ return addon;
+ }
+ }
+ return null;
+}
+
+/**
+ * Internal interface to get a filtered list of addons from a loaded addonDB
+ */
+function _filterDB(addonDB, aFilter) {
+ return [for (addon of addonDB.values()) if (aFilter(addon)) addon];
+}
+
+this.XPIDatabase = {
+ // true if the database connection has been opened
+ initialized: false,
+ // The database file
+ jsonFile: FileUtils.getFile(KEY_PROFILEDIR, [FILE_JSON_DB], true),
+ // Migration data loaded from an old version of the database.
+ migrateData: null,
+ // Active add-on directories loaded from extensions.ini and prefs at startup.
+ activeBundles: null,
+
+ // Saved error object if we fail to read an existing database
+ _loadError: null,
+
+ // Error reported by our most recent attempt to read or write the database, if any
+ get lastError() {
+ if (this._loadError)
+ return this._loadError;
+ if (this._deferredSave)
+ return this._deferredSave.lastError;
+ return null;
+ },
+
+ /**
+ * Mark the current stored data dirty, and schedule a flush to disk
+ */
+ saveChanges: function() {
+ if (!this.initialized) {
+ throw new Error("Attempt to use XPI database when it is not initialized");
+ }
+
+ if (XPIProvider._closing) {
+ // use an Error here so we get a stack trace.
+ let err = new Error("XPI database modified after shutdown began");
+ logger.warn(err);
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_late_stack", Log.stackTrace(err));
+ }
+
+ if (!this._deferredSave) {
+ this._deferredSave = new DeferredSave(this.jsonFile.path,
+ () => JSON.stringify(this),
+ ASYNC_SAVE_DELAY_MS);
+ }
+
+ let promise = this._deferredSave.saveChanges();
+ if (!this._schemaVersionSet) {
+ this._schemaVersionSet = true;
+ promise.then(
+ count => {
+ // Update the XPIDB schema version preference the first time we successfully
+ // save the database.
+ logger.debug("XPI Database saved, setting schema version preference to " + DB_SCHEMA);
+ Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA);
+ // Reading the DB worked once, so we don't need the load error
+ this._loadError = null;
+ },
+ error => {
+ // Need to try setting the schema version again later
+ this._schemaVersionSet = false;
+ logger.warn("Failed to save XPI database", error);
+ // this._deferredSave.lastError has the most recent error so we don't
+ // need this any more
+ this._loadError = null;
+ });
+ }
+ },
+
+ flush: function() {
+ // handle the "in memory only" and "saveChanges never called" cases
+ if (!this._deferredSave) {
+ return Promise.resolve(0);
+ }
+
+ return this._deferredSave.flush();
+ },
+
+ /**
+ * Converts the current internal state of the XPI addon database to
+ * a JSON.stringify()-ready structure
+ */
+ toJSON: function() {
+ if (!this.addonDB) {
+ // We never loaded the database?
+ throw new Error("Attempt to save database without loading it first");
+ }
+
+ let toSave = {
+ schemaVersion: DB_SCHEMA,
+ addons: [...this.addonDB.values()]
+ };
+ return toSave;
+ },
+
+ /**
+ * Pull upgrade information from an existing SQLITE database
+ *
+ * @return false if there is no SQLITE database
+ * true and sets this.migrateData to null if the SQLITE DB exists
+ * but does not contain useful information
+ * true and sets this.migrateData to
+ * {location: {id1:{addon1}, id2:{addon2}}, location2:{...}, ...}
+ * if there is useful information
+ */
+ getMigrateDataFromSQLITE: function XPIDB_getMigrateDataFromSQLITE() {
+ let connection = null;
+ let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
+ // Attempt to open the database
+ try {
+ connection = Services.storage.openUnsharedDatabase(dbfile);
+ }
+ catch (e) {
+ logger.warn("Failed to open sqlite database " + dbfile.path + " for upgrade", e);
+ return null;
+ }
+ logger.debug("Migrating data from sqlite");
+ let migrateData = this.getMigrateDataFromDatabase(connection);
+ connection.close();
+ return migrateData;
+ },
+
+ /**
+ * Synchronously opens and reads the database file, upgrading from old
+ * databases or making a new DB if needed.
+ *
+ * The possibilities, in order of priority, are:
+ * 1) Perfectly good, up to date database
+ * 2) Out of date JSON database needs to be upgraded => upgrade
+ * 3) JSON database exists but is mangled somehow => build new JSON
+ * 4) no JSON DB, but a useable SQLITE db we can upgrade from => upgrade
+ * 5) useless SQLITE DB => build new JSON
+ * 6) useable RDF DB => upgrade
+ * 7) useless RDF DB => build new JSON
+ * 8) Nothing at all => build new JSON
+ * @param aRebuildOnError
+ * A boolean indicating whether add-on information should be loaded
+ * from the install locations if the database needs to be rebuilt.
+ * (if false, caller is XPIProvider.checkForChanges() which will rebuild)
+ */
+ syncLoadDB: function XPIDB_syncLoadDB(aRebuildOnError) {
+ this.migrateData = null;
+ let fstream = null;
+ let data = "";
+ try {
+ let readTimer = AddonManagerPrivate.simpleTimer("XPIDB_syncRead_MS");
+ logger.debug("Opening XPI database " + this.jsonFile.path);
+ fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ fstream.init(this.jsonFile, -1, 0, 0);
+ let cstream = null;
+ try {
+ cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Components.interfaces.nsIConverterInputStream);
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ let str = {};
+ let read = 0;
+ do {
+ read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
+ data += str.value;
+ } while (read != 0);
+
+ readTimer.done();
+ this.parseDB(data, aRebuildOnError);
+ }
+ catch(e) {
+ logger.error("Failed to load XPI JSON data from profile", e);
+ let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
+ this.rebuildDatabase(aRebuildOnError);
+ rebuildTimer.done();
+ }
+ finally {
+ if (cstream)
+ cstream.close();
+ }
+ }
+ catch (e) {
+ if (e.result === Cr.NS_ERROR_FILE_NOT_FOUND) {
+ this.upgradeDB(aRebuildOnError);
+ }
+ else {
+ this.rebuildUnreadableDB(e, aRebuildOnError);
+ }
+ }
+ finally {
+ if (fstream)
+ fstream.close();
+ }
+ // If an async load was also in progress, resolve that promise with our DB;
+ // otherwise create a resolved promise
+ if (this._dbPromise) {
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_overlapped_load", 1);
+ this._dbPromise.resolve(this.addonDB);
+ }
+ else
+ this._dbPromise = Promise.resolve(this.addonDB);
+ },
+
+ /**
+ * Parse loaded data, reconstructing the database if the loaded data is not valid
+ * @param aRebuildOnError
+ * If true, synchronously reconstruct the database from installed add-ons
+ */
+ parseDB: function(aData, aRebuildOnError) {
+ let parseTimer = AddonManagerPrivate.simpleTimer("XPIDB_parseDB_MS");
+ try {
+ // dump("Loaded JSON:\n" + aData + "\n");
+ let inputAddons = JSON.parse(aData);
+ // Now do some sanity checks on our JSON db
+ if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
+ parseTimer.done();
+ // Content of JSON file is bad, need to rebuild from scratch
+ logger.error("bad JSON file contents");
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "badJSON");
+ let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildBadJSON_MS");
+ this.rebuildDatabase(aRebuildOnError);
+ rebuildTimer.done();
+ return;
+ }
+ if (inputAddons.schemaVersion != DB_SCHEMA) {
+ // Handle mismatched JSON schema version. For now, we assume
+ // compatibility for JSON data, though we throw away any fields we
+ // don't know about (bug 902956)
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError",
+ "schemaMismatch-" + inputAddons.schemaVersion);
+ logger.debug("JSON schema mismatch: expected " + DB_SCHEMA +
+ ", actual " + inputAddons.schemaVersion);
+ // When we rev the schema of the JSON database, we need to make sure we
+ // force the DB to save so that the DB_SCHEMA value in the JSON file and
+ // the preference are updated.
+ }
+ // If we got here, we probably have good data
+ // Make AddonInternal instances from the loaded data and save them
+ let addonDB = new Map();
+ for (let loadedAddon of inputAddons.addons) {
+ let newAddon = new DBAddonInternal(loadedAddon);
+ addonDB.set(newAddon._key, newAddon);
+ };
+ parseTimer.done();
+ this.addonDB = addonDB;
+ logger.debug("Successfully read XPI database");
+ this.initialized = true;
+ }
+ catch(e) {
+ // If we catch and log a SyntaxError from the JSON
+ // parser, the xpcshell test harness fails the test for us: bug 870828
+ parseTimer.done();
+ if (e.name == "SyntaxError") {
+ logger.error("Syntax error parsing saved XPI JSON data");
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "syntax");
+ }
+ else {
+ logger.error("Failed to load XPI JSON data from profile", e);
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "other");
+ }
+ let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
+ this.rebuildDatabase(aRebuildOnError);
+ rebuildTimer.done();
+ }
+ },
+
+ /**
+ * Upgrade database from earlier (sqlite or RDF) version if available
+ */
+ upgradeDB: function(aRebuildOnError) {
+ let upgradeTimer = AddonManagerPrivate.simpleTimer("XPIDB_upgradeDB_MS");
+ try {
+ let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA);
+ if (schemaVersion <= LAST_SQLITE_DB_SCHEMA) {
+ // we should have an older SQLITE database
+ logger.debug("Attempting to upgrade from SQLITE database");
+ this.migrateData = this.getMigrateDataFromSQLITE();
+ }
+ else {
+ // we've upgraded before but the JSON file is gone, fall through
+ // and rebuild from scratch
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "dbMissing");
+ }
+ }
+ catch(e) {
+ // No schema version pref means either a really old upgrade (RDF) or
+ // a new profile
+ this.migrateData = this.getMigrateDataFromRDF();
+ }
+
+ this.rebuildDatabase(aRebuildOnError);
+ upgradeTimer.done();
+ },
+
+ /**
+ * Reconstruct when the DB file exists but is unreadable
+ * (for example because read permission is denied)
+ */
+ rebuildUnreadableDB: function(aError, aRebuildOnError) {
+ let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildUnreadableDB_MS");
+ logger.warn("Extensions database " + this.jsonFile.path +
+ " exists but is not readable; rebuilding", aError);
+ // Remember the error message until we try and write at least once, so
+ // we know at shutdown time that there was a problem
+ this._loadError = aError;
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "unreadable");
+ this.rebuildDatabase(aRebuildOnError);
+ rebuildTimer.done();
+ },
+
+ /**
+ * Open and read the XPI database asynchronously, upgrading if
+ * necessary. If any DB load operation fails, we need to
+ * synchronously rebuild the DB from the installed extensions.
+ *
+ * @return Promise<Map> resolves to the Map of loaded JSON data stored
+ * in this.addonDB; never rejects.
+ */
+ asyncLoadDB: function XPIDB_asyncLoadDB() {
+ // Already started (and possibly finished) loading
+ if (this._dbPromise) {
+ return this._dbPromise;
+ }
+
+ logger.debug("Starting async load of XPI database " + this.jsonFile.path);
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_async_load", XPIProvider.runPhase);
+ let readOptions = {
+ outExecutionDuration: 0
+ };
+ return this._dbPromise = OS.File.read(this.jsonFile.path, null, readOptions).then(
+ byteArray => {
+ logger.debug("Async JSON file read took " + readOptions.outExecutionDuration + " MS");
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_asyncRead_MS",
+ readOptions.outExecutionDuration);
+ if (this._addonDB) {
+ logger.debug("Synchronous load completed while waiting for async load");
+ return this.addonDB;
+ }
+ logger.debug("Finished async read of XPI database, parsing...");
+ let decodeTimer = AddonManagerPrivate.simpleTimer("XPIDB_decode_MS");
+ let decoder = new TextDecoder();
+ let data = decoder.decode(byteArray);
+ decodeTimer.done();
+ this.parseDB(data, true);
+ return this.addonDB;
+ })
+ .then(null,
+ error => {
+ if (this._addonDB) {
+ logger.debug("Synchronous load completed while waiting for async load");
+ return this.addonDB;
+ }
+ if (error.becauseNoSuchFile) {
+ this.upgradeDB(true);
+ }
+ else {
+ // it's there but unreadable
+ this.rebuildUnreadableDB(error, true);
+ }
+ return this.addonDB;
+ });
+ },
+
+ /**
+ * Rebuild the database from addon install directories. If this.migrateData
+ * is available, uses migrated information for settings on the addons found
+ * during rebuild
+ * @param aRebuildOnError
+ * A boolean indicating whether add-on information should be loaded
+ * from the install locations if the database needs to be rebuilt.
+ * (if false, caller is XPIProvider.checkForChanges() which will rebuild)
+ */
+ rebuildDatabase: function XIPDB_rebuildDatabase(aRebuildOnError) {
+ this.addonDB = new Map();
+ this.initialized = true;
+
+ if (XPIStates.size == 0) {
+ // No extensions installed, so we're done
+ logger.debug("Rebuilding XPI database with no extensions");
+ return;
+ }
+
+ // If there is no migration data then load the list of add-on directories
+ // that were active during the last run
+ if (!this.migrateData)
+ this.activeBundles = this.getActiveBundles();
+
+ if (aRebuildOnError) {
+ logger.warn("Rebuilding add-ons database from installed extensions.");
+ try {
+ XPIProvider.processFileChanges({}, false);
+ }
+ catch (e) {
+ logger.error("Failed to rebuild XPI database from installed extensions", e);
+ }
+ // Make sure to update the active add-ons and add-ons list on shutdown
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
+ }
+ },
+
+ /**
+ * Gets the list of file descriptors of active extension directories or XPI
+ * files from the add-ons list. This must be loaded from disk since the
+ * directory service gives no easy way to get both directly. This list doesn't
+ * include themes as preferences already say which theme is currently active
+ *
+ * @return an array of persistent descriptors for the directories
+ */
+ getActiveBundles: function XPIDB_getActiveBundles() {
+ let bundles = [];
+
+ // non-bootstrapped extensions
+ let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
+ true);
+
+ if (!addonsList.exists())
+ // XXX Irving believes this is broken in the case where there is no
+ // extensions.ini but there are bootstrap extensions (e.g. Android)
+ return null;
+
+ try {
+ let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]
+ .getService(Ci.nsIINIParserFactory);
+ let parser = iniFactory.createINIParser(addonsList);
+ let keys = parser.getKeys("ExtensionDirs");
+
+ while (keys.hasMore())
+ bundles.push(parser.getString("ExtensionDirs", keys.getNext()));
+ }
+ catch (e) {
+ logger.warn("Failed to parse extensions.ini", e);
+ return null;
+ }
+
+ // Also include the list of active bootstrapped extensions
+ for (let id in XPIProvider.bootstrappedAddons)
+ bundles.push(XPIProvider.bootstrappedAddons[id].descriptor);
+
+ return bundles;
+ },
+
+ /**
+ * Retrieves migration data from the old extensions.rdf database.
+ *
+ * @return an object holding information about what add-ons were previously
+ * userDisabled and any updated compatibility information
+ */
+ getMigrateDataFromRDF: function XPIDB_getMigrateDataFromRDF(aDbWasMissing) {
+
+ // Migrate data from extensions.rdf
+ let rdffile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_DATABASE], true);
+ if (!rdffile.exists())
+ return null;
+
+ logger.debug("Migrating data from " + FILE_OLD_DATABASE);
+ let migrateData = {};
+
+ try {
+ let ds = gRDF.GetDataSourceBlocking(Services.io.newFileURI(rdffile).spec);
+ let root = Cc["@mozilla.org/rdf/container;1"].
+ createInstance(Ci.nsIRDFContainer);
+ root.Init(ds, gRDF.GetResource(RDFURI_ITEM_ROOT));
+ let elements = root.GetElements();
+
+ while (elements.hasMoreElements()) {
+ let source = elements.getNext().QueryInterface(Ci.nsIRDFResource);
+
+ let location = getRDFProperty(ds, source, "installLocation");
+ if (location) {
+ if (!(location in migrateData))
+ migrateData[location] = {};
+ let id = source.ValueUTF8.substring(PREFIX_ITEM_URI.length);
+ migrateData[location][id] = {
+ version: getRDFProperty(ds, source, "version"),
+ userDisabled: false,
+ targetApplications: []
+ }
+
+ let disabled = getRDFProperty(ds, source, "userDisabled");
+ if (disabled == "true" || disabled == "needs-disable")
+ migrateData[location][id].userDisabled = true;
+
+ let targetApps = ds.GetTargets(source, EM_R("targetApplication"),
+ true);
+ while (targetApps.hasMoreElements()) {
+ let targetApp = targetApps.getNext()
+ .QueryInterface(Ci.nsIRDFResource);
+ let appInfo = {
+ id: getRDFProperty(ds, targetApp, "id")
+ };
+
+ let minVersion = getRDFProperty(ds, targetApp, "updatedMinVersion");
+ if (minVersion) {
+ appInfo.minVersion = minVersion;
+ appInfo.maxVersion = getRDFProperty(ds, targetApp, "updatedMaxVersion");
+ }
+ else {
+ appInfo.minVersion = getRDFProperty(ds, targetApp, "minVersion");
+ appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion");
+ }
+ migrateData[location][id].targetApplications.push(appInfo);
+ }
+ }
+ }
+ }
+ catch (e) {
+ logger.warn("Error reading " + FILE_OLD_DATABASE, e);
+ migrateData = null;
+ }
+
+ return migrateData;
+ },
+
+ /**
+ * Retrieves migration data from a database that has an older or newer schema.
+ *
+ * @return an object holding information about what add-ons were previously
+ * userDisabled and any updated compatibility information
+ */
+ getMigrateDataFromDatabase: function XPIDB_getMigrateDataFromDatabase(aConnection) {
+ let migrateData = {};
+
+ // Attempt to migrate data from a different (even future!) version of the
+ // database
+ try {
+ var stmt = aConnection.createStatement("PRAGMA table_info(addon)");
+
+ const REQUIRED = ["internal_id", "id", "location", "userDisabled",
+ "installDate", "version"];
+
+ let reqCount = 0;
+ let props = [];
+ for (let row in resultRows(stmt)) {
+ if (REQUIRED.indexOf(row.name) != -1) {
+ reqCount++;
+ props.push(row.name);
+ }
+ else if (DB_METADATA.indexOf(row.name) != -1) {
+ props.push(row.name);
+ }
+ else if (DB_BOOL_METADATA.indexOf(row.name) != -1) {
+ props.push(row.name);
+ }
+ }
+
+ if (reqCount < REQUIRED.length) {
+ logger.error("Unable to read anything useful from the database");
+ return null;
+ }
+ stmt.finalize();
+
+ stmt = aConnection.createStatement("SELECT " + props.join(",") + " FROM addon");
+ for (let row in resultRows(stmt)) {
+ if (!(row.location in migrateData))
+ migrateData[row.location] = {};
+ let addonData = {
+ targetApplications: []
+ }
+ migrateData[row.location][row.id] = addonData;
+
+ props.forEach(function(aProp) {
+ if (aProp == "isForeignInstall")
+ addonData.foreignInstall = (row[aProp] == 1);
+ if (DB_BOOL_METADATA.indexOf(aProp) != -1)
+ addonData[aProp] = row[aProp] == 1;
+ else
+ addonData[aProp] = row[aProp];
+ })
+ }
+
+ var taStmt = aConnection.createStatement("SELECT id, minVersion, " +
+ "maxVersion FROM " +
+ "targetApplication WHERE " +
+ "addon_internal_id=:internal_id");
+
+ for (let location in migrateData) {
+ for (let id in migrateData[location]) {
+ taStmt.params.internal_id = migrateData[location][id].internal_id;
+ delete migrateData[location][id].internal_id;
+ for (let row in resultRows(taStmt)) {
+ migrateData[location][id].targetApplications.push({
+ id: row.id,
+ minVersion: row.minVersion,
+ maxVersion: row.maxVersion
+ });
+ }
+ }
+ }
+ }
+ catch (e) {
+ // An error here means the schema is too different to read
+ logger.error("Error migrating data", e);
+ return null;
+ }
+ finally {
+ if (taStmt)
+ taStmt.finalize();
+ if (stmt)
+ stmt.finalize();
+ }
+
+ return migrateData;
+ },
+
+ /**
+ * Shuts down the database connection and releases all cached objects.
+ * Return: Promise{integer} resolves / rejects with the result of the DB
+ * flush after the database is flushed and
+ * all cleanup is done
+ */
+ shutdown: function XPIDB_shutdown() {
+ logger.debug("shutdown");
+ if (this.initialized) {
+ // If our last database I/O had an error, try one last time to save.
+ if (this.lastError)
+ this.saveChanges();
+
+ this.initialized = false;
+
+ if (this._deferredSave) {
+ AddonManagerPrivate.recordSimpleMeasure(
+ "XPIDB_saves_total", this._deferredSave.totalSaves);
+ AddonManagerPrivate.recordSimpleMeasure(
+ "XPIDB_saves_overlapped", this._deferredSave.overlappedSaves);
+ AddonManagerPrivate.recordSimpleMeasure(
+ "XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0);
+ }
+
+ // Return a promise that any pending writes of the DB are complete and we
+ // are finished cleaning up
+ let flushPromise = this.flush();
+ flushPromise.then(null, error => {
+ logger.error("Flush of XPI database failed", error);
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
+ // If our last attempt to read or write the DB failed, force a new
+ // extensions.ini to be written to disk on the next startup
+ Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
+ })
+ .then(count => {
+ // Clear out the cached addons data loaded from JSON
+ delete this.addonDB;
+ delete this._dbPromise;
+ // same for the deferred save
+ delete this._deferredSave;
+ // re-enable the schema version setter
+ delete this._schemaVersionSet;
+ });
+ return flushPromise;
+ }
+ return Promise.resolve(0);
+ },
+
+ /**
+ * Asynchronously list all addons that match the filter function
+ * @param aFilter
+ * Function that takes an addon instance and returns
+ * true if that addon should be included in the selected array
+ * @param aCallback
+ * Called back with an array of addons matching aFilter
+ * or an empty array if none match
+ */
+ getAddonList: function(aFilter, aCallback) {
+ this.asyncLoadDB().then(
+ addonDB => {
+ let addonList = _filterDB(addonDB, aFilter);
+ asyncMap(addonList, getRepositoryAddon, makeSafe(aCallback));
+ })
+ .then(null,
+ error => {
+ logger.error("getAddonList failed", error);
+ makeSafe(aCallback)([]);
+ });
+ },
+
+ /**
+ * (Possibly asynchronously) get the first addon that matches the filter function
+ * @param aFilter
+ * Function that takes an addon instance and returns
+ * true if that addon should be selected
+ * @param aCallback
+ * Called back with the addon, or null if no matching addon is found
+ */
+ getAddon: function(aFilter, aCallback) {
+ return this.asyncLoadDB().then(
+ addonDB => {
+ getRepositoryAddon(_findAddon(addonDB, aFilter), makeSafe(aCallback));
+ })
+ .then(null,
+ error => {
+ logger.error("getAddon failed", e);
+ makeSafe(aCallback)(null);
+ });
+ },
+
+ /**
+ * Asynchronously gets an add-on with a particular ID in a particular
+ * install location.
+ *
+ * @param aId
+ * The ID of the add-on to retrieve
+ * @param aLocation
+ * The name of the install location
+ * @param aCallback
+ * A callback to pass the DBAddonInternal to
+ */
+ getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) {
+ this.asyncLoadDB().then(
+ addonDB => getRepositoryAddon(addonDB.get(aLocation + ":" + aId),
+ makeSafe(aCallback)));
+ },
+
+ /**
+ * Asynchronously gets the add-on with the specified ID that is visible.
+ *
+ * @param aId
+ * The ID of the add-on to retrieve
+ * @param aCallback
+ * A callback to pass the DBAddonInternal to
+ */
+ getVisibleAddonForID: function XPIDB_getVisibleAddonForID(aId, aCallback) {
+ this.getAddon(aAddon => ((aAddon.id == aId) && aAddon.visible),
+ aCallback);
+ },
+
+ /**
+ * Asynchronously gets the visible add-ons, optionally restricting by type.
+ *
+ * @param aTypes
+ * An array of types to include or null to include all types
+ * @param aCallback
+ * A callback to pass the array of DBAddonInternals to
+ */
+ getVisibleAddons: function XPIDB_getVisibleAddons(aTypes, aCallback) {
+ this.getAddonList(aAddon => (aAddon.visible &&
+ (!aTypes || (aTypes.length == 0) ||
+ (aTypes.indexOf(aAddon.type) > -1))),
+ aCallback);
+ },
+
+ /**
+ * Synchronously gets all add-ons of a particular type.
+ *
+ * @param aType
+ * The type of add-on to retrieve
+ * @return an array of DBAddonInternals
+ */
+ getAddonsByType: function XPIDB_getAddonsByType(aType) {
+ if (!this.addonDB) {
+ // jank-tastic! Must synchronously load DB if the theme switches from
+ // an XPI theme to a lightweight theme before the DB has loaded,
+ // because we're called from sync XPIProvider.addonChanged
+ logger.warn("Synchronous load of XPI database due to getAddonsByType(" + aType + ")");
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_byType", XPIProvider.runPhase);
+ this.syncLoadDB(true);
+ }
+ return _filterDB(this.addonDB, aAddon => (aAddon.type == aType));
+ },
+
+ /**
+ * Synchronously gets an add-on with a particular internalName.
+ *
+ * @param aInternalName
+ * The internalName of the add-on to retrieve
+ * @return a DBAddonInternal
+ */
+ getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) {
+ if (!this.addonDB) {
+ // This may be called when the DB hasn't otherwise been loaded
+ logger.warn("Synchronous load of XPI database due to getVisibleAddonForInternalName");
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_forInternalName",
+ XPIProvider.runPhase);
+ this.syncLoadDB(true);
+ }
+
+ return _findAddon(this.addonDB,
+ aAddon => aAddon.visible &&
+ (aAddon.internalName == aInternalName));
+ },
+
+ /**
+ * Asynchronously gets all add-ons with pending operations.
+ *
+ * @param aTypes
+ * The types of add-ons to retrieve or null to get all types
+ * @param aCallback
+ * A callback to pass the array of DBAddonInternal to
+ */
+ getVisibleAddonsWithPendingOperations:
+ function XPIDB_getVisibleAddonsWithPendingOperations(aTypes, aCallback) {
+
+ this.getAddonList(
+ aAddon => (aAddon.visible &&
+ (aAddon.pendingUninstall ||
+ // Logic here is tricky. If we're active but disabled,
+ // we're pending disable; !active && !disabled, we're pending enable
+ (aAddon.active == aAddon.disabled)) &&
+ (!aTypes || (aTypes.length == 0) || (aTypes.indexOf(aAddon.type) > -1))),
+ aCallback);
+ },
+
+ /**
+ * Asynchronously get an add-on by its Sync GUID.
+ *
+ * @param aGUID
+ * Sync GUID of add-on to fetch
+ * @param aCallback
+ * A callback to pass the DBAddonInternal record to. Receives null
+ * if no add-on with that GUID is found.
+ *
+ */
+ getAddonBySyncGUID: function XPIDB_getAddonBySyncGUID(aGUID, aCallback) {
+ this.getAddon(aAddon => aAddon.syncGUID == aGUID,
+ aCallback);
+ },
+
+ /**
+ * Synchronously gets all add-ons in the database.
+ * This is only called from the preference observer for the default
+ * compatibility version preference, so we can return an empty list if
+ * we haven't loaded the database yet.
+ *
+ * @return an array of DBAddonInternals
+ */
+ getAddons: function XPIDB_getAddons() {
+ if (!this.addonDB) {
+ return [];
+ }
+ return _filterDB(this.addonDB, aAddon => true);
+ },
+
+ /**
+ * Synchronously adds an AddonInternal's metadata to the database.
+ *
+ * @param aAddon
+ * AddonInternal to add
+ * @param aDescriptor
+ * The file descriptor of the add-on
+ * @return The DBAddonInternal that was added to the database
+ */
+ addAddonMetadata: function XPIDB_addAddonMetadata(aAddon, aDescriptor) {
+ if (!this.addonDB) {
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_addMetadata",
+ XPIProvider.runPhase);
+ this.syncLoadDB(false);
+ }
+
+ let newAddon = new DBAddonInternal(aAddon);
+ newAddon.descriptor = aDescriptor;
+ this.addonDB.set(newAddon._key, newAddon);
+ if (newAddon.visible) {
+ this.makeAddonVisible(newAddon);
+ }
+
+ this.saveChanges();
+ return newAddon;
+ },
+
+ /**
+ * Synchronously updates an add-on's metadata in the database. Currently just
+ * removes and recreates.
+ *
+ * @param aOldAddon
+ * The DBAddonInternal to be replaced
+ * @param aNewAddon
+ * The new AddonInternal to add
+ * @param aDescriptor
+ * The file descriptor of the add-on
+ * @return The DBAddonInternal that was added to the database
+ */
+ updateAddonMetadata: function XPIDB_updateAddonMetadata(aOldAddon, aNewAddon,
+ aDescriptor) {
+ this.removeAddonMetadata(aOldAddon);
+ aNewAddon.syncGUID = aOldAddon.syncGUID;
+ aNewAddon.installDate = aOldAddon.installDate;
+ aNewAddon.applyBackgroundUpdates = aOldAddon.applyBackgroundUpdates;
+ aNewAddon.foreignInstall = aOldAddon.foreignInstall;
+ aNewAddon.active = (aNewAddon.visible && !aNewAddon.disabled && !aNewAddon.pendingUninstall);
+
+ // addAddonMetadata does a saveChanges()
+ return this.addAddonMetadata(aNewAddon, aDescriptor);
+ },
+
+ /**
+ * Synchronously removes an add-on from the database.
+ *
+ * @param aAddon
+ * The DBAddonInternal being removed
+ */
+ removeAddonMetadata: function XPIDB_removeAddonMetadata(aAddon) {
+ this.addonDB.delete(aAddon._key);
+ this.saveChanges();
+ },
+
+ /**
+ * Synchronously marks a DBAddonInternal as visible marking all other
+ * instances with the same ID as not visible.
+ *
+ * @param aAddon
+ * The DBAddonInternal to make visible
+ */
+ makeAddonVisible: function XPIDB_makeAddonVisible(aAddon) {
+ logger.debug("Make addon " + aAddon._key + " visible");
+ for (let [, otherAddon] of this.addonDB) {
+ if ((otherAddon.id == aAddon.id) && (otherAddon._key != aAddon._key)) {
+ logger.debug("Hide addon " + otherAddon._key);
+ otherAddon.visible = false;
+ }
+ }
+ aAddon.visible = true;
+ this.saveChanges();
+ },
+
+ /**
+ * Synchronously sets properties for an add-on.
+ *
+ * @param aAddon
+ * The DBAddonInternal being updated
+ * @param aProperties
+ * A dictionary of properties to set
+ */
+ setAddonProperties: function XPIDB_setAddonProperties(aAddon, aProperties) {
+ for (let key in aProperties) {
+ aAddon[key] = aProperties[key];
+ }
+ this.saveChanges();
+ },
+
+ /**
+ * Synchronously sets the Sync GUID for an add-on.
+ * Only called when the database is already loaded.
+ *
+ * @param aAddon
+ * The DBAddonInternal being updated
+ * @param aGUID
+ * GUID string to set the value to
+ * @throws if another addon already has the specified GUID
+ */
+ setAddonSyncGUID: function XPIDB_setAddonSyncGUID(aAddon, aGUID) {
+ // Need to make sure no other addon has this GUID
+ function excludeSyncGUID(otherAddon) {
+ return (otherAddon._key != aAddon._key) && (otherAddon.syncGUID == aGUID);
+ }
+ let otherAddon = _findAddon(this.addonDB, excludeSyncGUID);
+ if (otherAddon) {
+ throw new Error("Addon sync GUID conflict for addon " + aAddon._key +
+ ": " + otherAddon._key + " already has GUID " + aGUID);
+ }
+ aAddon.syncGUID = aGUID;
+ this.saveChanges();
+ },
+
+ /**
+ * Synchronously updates an add-on's active flag in the database.
+ *
+ * @param aAddon
+ * The DBAddonInternal to update
+ */
+ updateAddonActive: function XPIDB_updateAddonActive(aAddon, aActive) {
+ logger.debug("Updating active state for add-on " + aAddon.id + " to " + aActive);
+
+ aAddon.active = aActive;
+ this.saveChanges();
+ },
+
+ /**
+ * Synchronously calculates and updates all the active flags in the database.
+ */
+ updateActiveAddons: function XPIDB_updateActiveAddons() {
+ if (!this.addonDB) {
+ logger.warn("updateActiveAddons called when DB isn't loaded");
+ // force the DB to load
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_updateActive",
+ XPIProvider.runPhase);
+ this.syncLoadDB(true);
+ }
+ logger.debug("Updating add-on states");
+ for (let [, addon] of this.addonDB) {
+ let newActive = (addon.visible && !addon.disabled && !addon.pendingUninstall);
+ if (newActive != addon.active) {
+ addon.active = newActive;
+ this.saveChanges();
+ }
+ }
+ },
+
+ /**
+ * Writes out the XPI add-ons list for the platform to read.
+ * @return true if the file was successfully updated, false otherwise
+ */
+ writeAddonsList: function XPIDB_writeAddonsList() {
+ if (!this.addonDB) {
+ // force the DB to load
+ AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_writeList",
+ XPIProvider.runPhase);
+ this.syncLoadDB(true);
+ }
+ Services.appinfo.invalidateCachesOnRestart();
+
+ let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
+ true);
+ let enabledAddons = [];
+ let text = "[ExtensionDirs]\r\n";
+ let count = 0;
+ let fullCount = 0;
+
+ let activeAddons = _filterDB(
+ this.addonDB,
+ aAddon => aAddon.active && !aAddon.bootstrap && (aAddon.type != "theme"));
+
+ for (let row of activeAddons) {
+ text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
+ enabledAddons.push(encodeURIComponent(row.id) + ":" +
+ encodeURIComponent(row.version));
+ }
+ fullCount += count;
+
+ // The selected skin may come from an inactive theme (the default theme
+ // when a lightweight theme is applied for example)
+ text += "\r\n[ThemeDirs]\r\n";
+
+ let dssEnabled = false;
+ try {
+ dssEnabled = Services.prefs.getBoolPref(PREF_EM_DSS_ENABLED);
+ } catch (e) {}
+
+ let themes = [];
+ if (dssEnabled) {
+ themes = _filterDB(this.addonDB, aAddon => aAddon.type == "theme");
+ }
+ else {
+ let activeTheme = _findAddon(
+ this.addonDB,
+ aAddon => (aAddon.type == "theme") &&
+ (aAddon.internalName == XPIProvider.selectedSkin));
+ if (activeTheme) {
+ themes.push(activeTheme);
+ }
+ }
+
+ if (themes.length > 0) {
+ count = 0;
+ for (let row of themes) {
+ text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
+ enabledAddons.push(encodeURIComponent(row.id) + ":" +
+ encodeURIComponent(row.version));
+ }
+ fullCount += count;
+ }
+
+ text += "\r\n[MultiprocessIncompatibleExtensions]\r\n";
+
+ count = 0;
+ for (let row of activeAddons) {
+ if (!row.multiprocessCompatible) {
+ text += "Extension" + (count++) + "=" + row.id + "\r\n";
+ }
+ }
+
+ if (fullCount > 0) {
+ logger.debug("Writing add-ons list");
+
+ try {
+ let addonsListTmp = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST + ".tmp"],
+ true);
+ var fos = FileUtils.openFileOutputStream(addonsListTmp);
+ fos.write(text, text.length);
+ fos.close();
+ addonsListTmp.moveTo(addonsListTmp.parent, FILE_XPI_ADDONS_LIST);
+
+ Services.prefs.setCharPref(PREF_EM_ENABLED_ADDONS, enabledAddons.join(","));
+ }
+ catch (e) {
+ logger.error("Failed to write add-ons list to profile directory", e);
+ return false;
+ }
+ }
+ else {
+ if (addonsList.exists()) {
+ logger.debug("Deleting add-ons list");
+ try {
+ addonsList.remove(false);
+ }
+ catch (e) {
+ logger.error("Failed to remove " + addonsList.path, e);
+ return false;
+ }
+ }
+
+ Services.prefs.clearUserPref(PREF_EM_ENABLED_ADDONS);
+ }
+ return true;
+ }
+};
diff --git a/toolkit/mozapps/extensions/internal/moz.build b/toolkit/mozapps/extensions/internal/moz.build
new file mode 100644
index 000000000..efcd2af21
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/moz.build
@@ -0,0 +1,40 @@
+# -*- 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/.
+
+EXTRA_JS_MODULES.addons += [
+ '../../webextensions/internal/ProductAddonChecker.jsm',
+ 'AddonLogging.jsm',
+ 'AddonRepository_SQLiteMigrator.jsm',
+ 'Content.js',
+ 'GMPProvider.jsm',
+ 'LightweightThemeImageOptimizer.jsm',
+ 'SpellCheckDictionaryBootstrap.js',
+]
+
+# Don't ship unused providers on Android
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
+ EXTRA_JS_MODULES.addons += [
+ 'PluginProvider.jsm',
+ ]
+
+EXTRA_PP_JS_MODULES.addons += [
+ 'AddonRepository.jsm',
+ 'AddonUpdateChecker.jsm',
+ 'XPIProvider.jsm',
+ 'XPIProviderUtils.js',
+]
+
+# This is used in multiple places, so is defined here to avoid it getting
+# out of sync.
+DEFINES['MOZ_EXTENSIONS_DB_SCHEMA'] = 16
+
+# Additional debugging info is exposed in debug builds
+if CONFIG['MOZ_EM_DEBUG']:
+ DEFINES['MOZ_EM_DEBUG'] = 1
+
+# Apperently this needs to be defined because it isn't picked up automagically any more
+if CONFIG['MOZ_PHOENIX_EXTENSIONS']:
+ DEFINES['MOZ_PHOENIX_EXTENSIONS'] = 1 \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/jar.mn b/toolkit/mozapps/extensions/jar.mn
new file mode 100644
index 000000000..e95d93ca0
--- /dev/null
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -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/.
+
+toolkit.jar:
+% content mozapps %content/mozapps/
+* content/mozapps/extensions/extensions.xul (content/extensions.xul)
+ content/mozapps/extensions/extensions.css (content/extensions.css)
+* content/mozapps/extensions/extensions.js (content/extensions.js)
+* content/mozapps/extensions/extensions.xml (content/extensions.xml)
+ content/mozapps/extensions/updateinfo.xsl (content/updateinfo.xsl)
+ content/mozapps/extensions/about.xul (content/about.xul)
+ content/mozapps/extensions/about.js (content/about.js)
+ content/mozapps/extensions/list.xul (content/list.xul)
+ content/mozapps/extensions/list.js (content/list.js)
+ content/mozapps/extensions/blocklist.xul (content/blocklist.xul)
+ content/mozapps/extensions/blocklist.js (content/blocklist.js)
+ content/mozapps/extensions/blocklist.css (content/blocklist.css)
+ content/mozapps/extensions/blocklist.xml (content/blocklist.xml)
+ content/mozapps/extensions/selectAddons.xul (content/selectAddons.xul)
+ content/mozapps/extensions/selectAddons.xml (content/selectAddons.xml)
+ content/mozapps/extensions/selectAddons.js (content/selectAddons.js)
+ content/mozapps/extensions/selectAddons.css (content/selectAddons.css)
+* content/mozapps/extensions/update.xul (content/update.xul)
+ content/mozapps/extensions/update.js (content/update.js)
+ content/mozapps/extensions/eula.xul (content/eula.xul)
+ content/mozapps/extensions/eula.js (content/eula.js)
+ content/mozapps/extensions/newaddon.xul (content/newaddon.xul)
+ content/mozapps/extensions/newaddon.js (content/newaddon.js)
+ content/mozapps/extensions/setting.xml (content/setting.xml)
+ content/mozapps/extensions/pluginPrefs.xul (content/pluginPrefs.xul)
+ content/mozapps/extensions/gmpPrefs.xul (content/gmpPrefs.xul)
+ content/mozapps/extensions/OpenH264-license.txt (content/OpenH264-license.txt)
+ content/mozapps/xpinstall/xpinstallConfirm.xul (content/xpinstallConfirm.xul)
+ content/mozapps/xpinstall/xpinstallConfirm.js (content/xpinstallConfirm.js)
+ content/mozapps/xpinstall/xpinstallConfirm.css (content/xpinstallConfirm.css)
+ content/mozapps/xpinstall/xpinstallItem.xml (content/xpinstallItem.xml)
diff --git a/toolkit/mozapps/extensions/moz.build b/toolkit/mozapps/extensions/moz.build
new file mode 100644
index 000000000..19f449210
--- /dev/null
+++ b/toolkit/mozapps/extensions/moz.build
@@ -0,0 +1,55 @@
+# -*- 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/.
+
+DIRS += ['internal']
+TEST_DIRS += ['test']
+
+XPIDL_SOURCES += [
+ 'amIAddonManager.idl',
+ 'amIAddonPathService.idl',
+ 'amIWebInstaller.idl',
+ 'amIWebInstallListener.idl',
+]
+
+XPIDL_MODULE = 'extensions'
+
+EXTRA_COMPONENTS += [
+ 'addonManager.js',
+ 'amContentHandler.js',
+ 'amInstallTrigger.js',
+ 'amWebInstallListener.js',
+]
+
+EXTRA_PP_COMPONENTS += [
+ 'extensions.manifest',
+ 'nsBlocklistService.js',
+]
+
+EXTRA_JS_MODULES += [
+ 'ChromeManifestParser.jsm',
+ 'DeferredSave.jsm',
+ 'LightweightThemeManager.jsm',
+]
+
+EXTRA_PP_JS_MODULES += [
+ 'AddonManager.jsm'
+]
+
+# Additional debugging info is exposed in debug builds
+if CONFIG['MOZ_EM_DEBUG']:
+ DEFINES['MOZ_EM_DEBUG'] = 1
+
+JAR_MANIFESTS += ['jar.mn']
+
+EXPORTS.mozilla += [
+ 'AddonPathService.h',
+]
+
+UNIFIED_SOURCES += [
+ 'AddonPathService.cpp'
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/toolkit/mozapps/extensions/nsBlocklistService.js b/toolkit/mozapps/extensions/nsBlocklistService.js
new file mode 100644
index 000000000..936c9d1b5
--- /dev/null
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -0,0 +1,1478 @@
+/* -*- 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 strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+try {
+ // AddonManager.jsm doesn't allow itself to be imported in the child
+ // process. We're used in the child process (for now), so guard against
+ // this.
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+} catch (e) {
+}
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+ "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+
+const TOOLKIT_ID = "toolkit@mozilla.org"
+const KEY_PROFILEDIR = "ProfD";
+const KEY_APPDIR = "XCurProcD";
+const FILE_BLOCKLIST = "blocklist.xml";
+const PREF_BLOCKLIST_LASTUPDATETIME = "app.update.lastUpdateTime.blocklist-background-update-timer";
+const PREF_BLOCKLIST_URL = "extensions.blocklist.url";
+const PREF_BLOCKLIST_ITEM_URL = "extensions.blocklist.itemURL";
+const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
+const PREF_BLOCKLIST_INTERVAL = "extensions.blocklist.interval";
+const PREF_BLOCKLIST_LEVEL = "extensions.blocklist.level";
+const PREF_BLOCKLIST_PINGCOUNTTOTAL = "extensions.blocklist.pingCountTotal";
+const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
+const PREF_BLOCKLIST_SUPPRESSUI = "extensions.blocklist.suppressUI";
+const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
+const PREF_GENERAL_USERAGENT_LOCALE = "general.useragent.locale";
+const PREF_APP_DISTRIBUTION = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
+const PREF_EM_LOGGING_ENABLED = "extensions.logging.enabled";
+const XMLURI_BLOCKLIST = "http://www.mozilla.org/2006/addons-blocklist";
+const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
+const UNKNOWN_XPCOM_ABI = "unknownABI";
+const URI_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul"
+const DEFAULT_SEVERITY = 3;
+const DEFAULT_LEVEL = 2;
+const MAX_BLOCK_LEVEL = 3;
+const SEVERITY_OUTDATED = 0;
+const VULNERABILITYSTATUS_NONE = 0;
+const VULNERABILITYSTATUS_UPDATE_AVAILABLE = 1;
+const VULNERABILITYSTATUS_NO_UPDATE = 2;
+
+const EXTENSION_BLOCK_FILTERS = ["id", "name", "creator", "homepageURL", "updateURL"];
+
+var gLoggingEnabled = null;
+var gBlocklistEnabled = true;
+var gBlocklistLevel = DEFAULT_LEVEL;
+
+XPCOMUtils.defineLazyServiceGetter(this, "gConsole",
+ "@mozilla.org/consoleservice;1",
+ "nsIConsoleService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gVersionChecker",
+ "@mozilla.org/xpcom/version-comparator;1",
+ "nsIVersionComparator");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gCertBlocklistService",
+ "@mozilla.org/security/certblocklist;1",
+ "nsICertBlocklist");
+
+XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() {
+ return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
+ QueryInterface(Ci.nsIPrefBranch);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gApp", function bls_gApp() {
+ return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
+ QueryInterface(Ci.nsIXULRuntime);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gABI", function bls_gABI() {
+ let abi = null;
+ try {
+ abi = gApp.XPCOMABI;
+ }
+ catch (e) {
+ LOG("BlockList Global gABI: XPCOM ABI unknown.");
+ }
+#ifdef XP_MACOSX
+ // Mac universal build should report a different ABI than either macppc
+ // or mactel.
+ let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+ getService(Ci.nsIMacUtils);
+
+ if (macutils.isUniversalBinary)
+ abi += "-u-" + macutils.architecturesInBinary;
+#endif
+ return abi;
+});
+
+XPCOMUtils.defineLazyGetter(this, "gOSVersion", function bls_gOSVersion() {
+ let osVersion;
+ let sysInfo = Cc["@mozilla.org/system-info;1"].
+ getService(Ci.nsIPropertyBag2);
+ try {
+ osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
+ }
+ catch (e) {
+ LOG("BlockList Global gOSVersion: OS Version unknown.");
+ }
+
+ if (osVersion) {
+ try {
+ osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+ }
+ catch (e) {
+ // Not all platforms have a secondary widget library, so an error is nothing to worry about.
+ }
+ osVersion = encodeURIComponent(osVersion);
+ }
+ return osVersion;
+});
+
+// shared code for suppressing bad cert dialogs
+XPCOMUtils.defineLazyGetter(this, "gCertUtils", function bls_gCertUtils() {
+ let temp = { };
+ Components.utils.import("resource://gre/modules/CertUtils.jsm", temp);
+ return temp;
+});
+
+/**
+ * Logs a string to the error console.
+ * @param string
+ * The string to write to the error console..
+ */
+function LOG(string) {
+ if (gLoggingEnabled) {
+ dump("*** " + string + "\n");
+ gConsole.logStringMessage(string);
+ }
+}
+
+/**
+ * Gets a preference value, handling the case where there is no default.
+ * @param func
+ * The name of the preference function to call, on nsIPrefBranch
+ * @param preference
+ * The name of the preference
+ * @param defaultValue
+ * The default value to return in the event the preference has
+ * no setting
+ * @returns The value of the preference, or undefined if there was no
+ * user or default value.
+ */
+function getPref(func, preference, defaultValue) {
+ try {
+ return gPref[func](preference);
+ }
+ catch (e) {
+ }
+ return defaultValue;
+}
+
+/**
+ * Constructs a URI to a spec.
+ * @param spec
+ * The spec to construct a URI to
+ * @returns The nsIURI constructed.
+ */
+function newURI(spec) {
+ var ioServ = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+ return ioServ.newURI(spec, null, null);
+}
+
+// Restarts the application checking in with observers first
+function restartApp() {
+ // Notify all windows that an application quit has been requested.
+ var os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
+ createInstance(Ci.nsISupportsPRBool);
+ os.notifyObservers(cancelQuit, "quit-application-requested", null);
+
+ // Something aborted the quit process.
+ if (cancelQuit.data)
+ return;
+
+ var as = Cc["@mozilla.org/toolkit/app-startup;1"].
+ getService(Ci.nsIAppStartup);
+ as.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
+}
+
+/**
+ * Checks whether this blocklist element is valid for the current OS and ABI.
+ * If the element has an "os" attribute then the current OS must appear in
+ * its comma separated list for the element to be valid. Similarly for the
+ * xpcomabi attribute.
+ */
+function matchesOSABI(blocklistElement) {
+ if (blocklistElement.hasAttribute("os")) {
+ var choices = blocklistElement.getAttribute("os").split(",");
+ if (choices.length > 0 && choices.indexOf(gApp.OS) < 0)
+ return false;
+ }
+
+ if (blocklistElement.hasAttribute("xpcomabi")) {
+ choices = blocklistElement.getAttribute("xpcomabi").split(",");
+ if (choices.length > 0 && choices.indexOf(gApp.XPCOMABI) < 0)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Gets the current value of the locale. It's possible for this preference to
+ * be localized, so we have to do a little extra work here. Similar code
+ * exists in nsHttpHandler.cpp when building the UA string.
+ */
+function getLocale() {
+ try {
+ // Get the default branch
+ var defaultPrefs = gPref.getDefaultBranch(null);
+ return defaultPrefs.getComplexValue(PREF_GENERAL_USERAGENT_LOCALE,
+ Ci.nsIPrefLocalizedString).data;
+ } catch (e) {}
+
+ return gPref.getCharPref(PREF_GENERAL_USERAGENT_LOCALE);
+}
+
+/* Get the distribution pref values, from defaults only */
+function getDistributionPrefValue(aPrefName) {
+ var prefValue = "default";
+
+ var defaults = gPref.getDefaultBranch(null);
+ try {
+ prefValue = defaults.getCharPref(aPrefName);
+ } catch (e) {
+ // use default when pref not found
+ }
+
+ return prefValue;
+}
+
+/**
+ * Parse a string representation of a regular expression. Needed because we
+ * use the /pattern/flags form (because it's detectable), which is only
+ * supported as a literal in JS.
+ *
+ * @param aStr
+ * String representation of regexp
+ * @return RegExp instance
+ */
+function parseRegExp(aStr) {
+ let lastSlash = aStr.lastIndexOf("/");
+ let pattern = aStr.slice(1, lastSlash);
+ let flags = aStr.slice(lastSlash + 1);
+ return new RegExp(pattern, flags);
+}
+
+/**
+ * Helper function to test if the blockEntry matches with the plugin.
+ *
+ * @param blockEntry
+ * The plugin blocklist entries to compare against.
+ * @param plugin
+ * The nsIPluginTag to get the blocklist state for.
+ * @returns True if the blockEntry matches the plugin, false otherwise.
+ */
+function matchesAllPluginNames(blockEntry, plugin) {
+ for (let name in blockEntry.matches) {
+ if (!(name in plugin) ||
+ typeof(plugin[name]) != "string" ||
+ !blockEntry.matches[name].test(plugin[name])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Manages the Blocklist. The Blocklist is a representation of the contents of
+ * blocklist.xml and allows us to remotely disable / re-enable blocklisted
+ * items managed by the Extension Manager with an item's appDisabled property.
+ * It also blocklists plugins with data from blocklist.xml.
+ */
+
+function Blocklist() {
+ Services.obs.addObserver(this, "xpcom-shutdown", false);
+ Services.obs.addObserver(this, "sessionstore-windows-restored", false);
+ gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
+ gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
+ gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
+ MAX_BLOCK_LEVEL);
+ gPref.addObserver("extensions.blocklist.", this, false);
+ gPref.addObserver(PREF_EM_LOGGING_ENABLED, this, false);
+ this.wrappedJSObject = this;
+}
+
+Blocklist.prototype = {
+ /**
+ * Extension ID -> array of Version Ranges
+ * Each value in the version range array is a JS Object that has the
+ * following properties:
+ * "minVersion" The minimum version in a version range (default = 0)
+ * "maxVersion" The maximum version in a version range (default = *)
+ * "targetApps" Application ID -> array of Version Ranges
+ * (default = current application ID)
+ * Each value in the version range array is a JS Object that
+ * has the following properties:
+ * "minVersion" The minimum version in a version range
+ * (default = 0)
+ * "maxVersion" The maximum version in a version range
+ * (default = *)
+ */
+ _addonEntries: null,
+ _pluginEntries: null,
+
+ observe: function Blocklist_observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "xpcom-shutdown":
+ Services.obs.removeObserver(this, "xpcom-shutdown");
+ gPref.removeObserver("extensions.blocklist.", this);
+ gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this);
+ break;
+ case "nsPref:changed":
+ switch (aData) {
+ case PREF_EM_LOGGING_ENABLED:
+ gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
+ break;
+ case PREF_BLOCKLIST_ENABLED:
+ gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
+ this._loadBlocklist();
+ this._blocklistUpdated(null, null);
+ break;
+ case PREF_BLOCKLIST_LEVEL:
+ gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
+ MAX_BLOCK_LEVEL);
+ this._blocklistUpdated(null, null);
+ break;
+ }
+ break;
+ case "sessionstore-windows-restored":
+ Services.obs.removeObserver(this, "sessionstore-windows-restored");
+ this._preloadBlocklist();
+ break;
+ }
+ },
+
+ /* See nsIBlocklistService */
+ isAddonBlocklisted: function Blocklist_isAddonBlocklisted(addon, appVersion, toolkitVersion) {
+ return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) ==
+ Ci.nsIBlocklistService.STATE_BLOCKED;
+ },
+
+ /* See nsIBlocklistService */
+ getAddonBlocklistState: function Blocklist_getAddonBlocklistState(addon, appVersion, toolkitVersion) {
+ if (!this._isBlocklistLoaded())
+ this._loadBlocklist();
+ return this._getAddonBlocklistState(addon, this._addonEntries,
+ appVersion, toolkitVersion);
+ },
+
+ /**
+ * Private version of getAddonBlocklistState that allows the caller to pass in
+ * the add-on blocklist entries to compare against.
+ *
+ * @param id
+ * The ID of the item to get the blocklist state for.
+ * @param version
+ * The version of the item to get the blocklist state for.
+ * @param addonEntries
+ * The add-on blocklist entries to compare against.
+ * @param appVersion
+ * The application version to compare to, will use the current
+ * version if null.
+ * @param toolkitVersion
+ * The toolkit version to compare to, will use the current version if
+ * null.
+ * @returns The blocklist state for the item, one of the STATE constants as
+ * defined in nsIBlocklistService.
+ */
+ _getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(addon,
+ addonEntries, appVersion, toolkitVersion) {
+ if (!gBlocklistEnabled)
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+
+ if (!appVersion)
+ appVersion = gApp.version;
+ if (!toolkitVersion)
+ toolkitVersion = gApp.platformVersion;
+
+ var blItem = this._findMatchingAddonEntry(addonEntries, addon);
+ if (!blItem)
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+
+ for (let currentblItem of blItem.versions) {
+ if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion))
+ return currentblItem.severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED :
+ Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
+ }
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ },
+
+ /**
+ * Returns the set of prefs of the add-on stored in the blocklist file
+ * (probably to revert them on disabling).
+ * @param addon
+ * The add-on whose to-be-reset prefs are to be found.
+ */
+ _getAddonPrefs: function Blocklist_getAddonPrefs(addon) {
+ let entry = this._findMatchingAddonEntry(this._addonEntries, addon);
+ return entry.prefs.slice(0);
+ },
+
+ _findMatchingAddonEntry: function Blocklist_findMatchingAddonEntry(aAddonEntries,
+ aAddon) {
+ if (!aAddon)
+ return null;
+ // Returns true if the params object passes the constraints set by entry.
+ // (For every non-null property in entry, the same key must exist in
+ // params and value must be the same)
+ function checkEntry(entry, params) {
+ for (let [key, value] of entry) {
+ if (value === null || value === undefined)
+ continue;
+ if (params[key]) {
+ if (value instanceof RegExp) {
+ if (!value.test(params[key])) {
+ return false;
+ }
+ } else if (value !== params[key]) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ let params = {};
+ for (let filter of EXTENSION_BLOCK_FILTERS) {
+ params[filter] = aAddon[filter];
+ }
+ if (params.creator)
+ params.creator = params.creator.name;
+ for (let entry of aAddonEntries) {
+ if (checkEntry(entry.attributes, params)) {
+ return entry;
+ }
+ }
+ return null;
+ },
+
+ /* See nsIBlocklistService */
+ getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(addon, appVersion, toolkitVersion) {
+ if (!gBlocklistEnabled)
+ return "";
+
+ if (!this._isBlocklistLoaded())
+ this._loadBlocklist();
+
+ let blItem = this._findMatchingAddonEntry(this._addonEntries, addon);
+ if (!blItem || !blItem.blockID)
+ return null;
+
+ return this._createBlocklistURL(blItem.blockID);
+ },
+
+ _createBlocklistURL: function Blocklist_createBlocklistURL(id) {
+ let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
+ url = url.replace(/%blockID%/g, id);
+
+ return url;
+ },
+
+ notify: function Blocklist_notify(aTimer) {
+ if (!gBlocklistEnabled)
+ return;
+
+ try {
+ var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
+ }
+ catch (e) {
+ LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
+ " is missing!");
+ return;
+ }
+
+ var pingCountVersion = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTVERSION, 0);
+ var pingCountTotal = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTTOTAL, 1);
+ var daysSinceLastPing = 0;
+ if (pingCountVersion == 0) {
+ daysSinceLastPing = "new";
+ }
+ else {
+ // Seconds in one day is used because nsIUpdateTimerManager stores the
+ // last update time in seconds.
+ let secondsInDay = 60 * 60 * 24;
+ let lastUpdateTime = getPref("getIntPref", PREF_BLOCKLIST_LASTUPDATETIME, 0);
+ if (lastUpdateTime == 0) {
+ daysSinceLastPing = "invalid";
+ }
+ else {
+ let now = Math.round(Date.now() / 1000);
+ daysSinceLastPing = Math.floor((now - lastUpdateTime) / secondsInDay);
+ }
+
+ if (daysSinceLastPing == 0 || daysSinceLastPing == "invalid") {
+ pingCountVersion = pingCountTotal = "invalid";
+ }
+ }
+
+ if (pingCountVersion < 1)
+ pingCountVersion = 1;
+ if (pingCountTotal < 1)
+ pingCountTotal = 1;
+
+ dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
+ dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
+ dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
+ dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
+ dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
+ dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
+ dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
+ dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
+ dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get());
+ dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
+ dsURI = dsURI.replace(/%DISTRIBUTION%/g,
+ getDistributionPrefValue(PREF_APP_DISTRIBUTION));
+ dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
+ getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
+ dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion);
+ dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal);
+ dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
+ dsURI = dsURI.replace(/\+/g, "%2B");
+
+ // Under normal operations it will take around 5,883,516 years before the
+ // preferences used to store pingCountVersion and pingCountTotal will rollover
+ // so this code doesn't bother trying to do the "right thing" here.
+ if (pingCountVersion != "invalid") {
+ pingCountVersion++;
+ if (pingCountVersion > 2147483647) {
+ // Rollover to -1 if the value is greater than what is support by an
+ // integer preference. The -1 indicates that the counter has been reset.
+ pingCountVersion = -1;
+ }
+ gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, pingCountVersion);
+ }
+
+ if (pingCountTotal != "invalid") {
+ pingCountTotal++;
+ if (pingCountTotal > 2147483647) {
+ // Rollover to 1 if the value is greater than what is support by an
+ // integer preference.
+ pingCountTotal = -1;
+ }
+ gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTTOTAL, pingCountTotal);
+ }
+
+ // Verify that the URI is valid
+ try {
+ var uri = newURI(dsURI);
+ }
+ catch (e) {
+ LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
+ "for: " + dsURI + ", error: " + e);
+ return;
+ }
+
+ LOG("Blocklist::notify: Requesting " + uri.spec);
+ var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+ createInstance(Ci.nsIXMLHttpRequest);
+ request.open("GET", uri.spec, true);
+ request.channel.notificationCallbacks = new gCertUtils.BadCertHandler();
+ request.overrideMimeType("text/xml");
+ request.setRequestHeader("Cache-Control", "no-cache");
+ request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
+
+ var self = this;
+ request.addEventListener("error", function errorEventListener(event) {
+ self.onXMLError(event); }, false);
+ request.addEventListener("load", function loadEventListener(event) {
+ self.onXMLLoad(event); }, false);
+ request.send(null);
+
+ // When the blocklist loads we need to compare it to the current copy so
+ // make sure we have loaded it.
+ if (!this._isBlocklistLoaded())
+ this._loadBlocklist();
+ },
+
+ onXMLLoad: Task.async(function* (aEvent) {
+ let request = aEvent.target;
+ try {
+ gCertUtils.checkCert(request.channel);
+ }
+ catch (e) {
+ LOG("Blocklist::onXMLLoad: " + e);
+ return;
+ }
+ let responseXML = request.responseXML;
+ if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
+ (request.status != 200 && request.status != 0)) {
+ LOG("Blocklist::onXMLLoad: there was an error during load");
+ return;
+ }
+
+ var oldAddonEntries = this._addonEntries;
+ var oldPluginEntries = this._pluginEntries;
+ this._addonEntries = [];
+ this._pluginEntries = [];
+
+ this._loadBlocklistFromString(request.responseText);
+ this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
+
+ try {
+ let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
+ yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
+ } catch (e) {
+ LOG("Blocklist::onXMLLoad: " + e);
+ }
+ }),
+
+ onXMLError: function Blocklist_onXMLError(aEvent) {
+ try {
+ var request = aEvent.target;
+ // the following may throw (e.g. a local file or timeout)
+ var status = request.status;
+ }
+ catch (e) {
+ request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
+ status = request.status;
+ }
+ var statusText = "nsIXMLHttpRequest channel unavailable";
+ // When status is 0 we don't have a valid channel.
+ if (status != 0) {
+ try {
+ statusText = request.statusText;
+ } catch (e) {
+ }
+ }
+ LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
+ statusText);
+ },
+
+ /**
+ * Finds the newest blocklist file from the application and the profile and
+ * load it or does nothing if neither exist.
+ */
+ _loadBlocklist: function Blocklist_loadBlocklist() {
+ this._addonEntries = [];
+ this._pluginEntries = [];
+ var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+ if (profFile.exists()) {
+ this._loadBlocklistFromFile(profFile);
+ return;
+ }
+ var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+ if (appFile.exists()) {
+ this._loadBlocklistFromFile(appFile);
+ return;
+ }
+ LOG("Blocklist::_loadBlocklist: no XML File found");
+ },
+
+ /**
+# The blocklist XML file looks something like this:
+#
+# <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+# <emItems>
+# <emItem id="item_1@domain" blockID="i1">
+# <prefs>
+# <pref>accessibility.accesskeycausesactivation</pref>
+# <pref>accessibility.blockautorefresh</pref>
+# </prefs>
+# <versionRange minVersion="1.0" maxVersion="2.0.*">
+# <targetApplication id="{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}">
+# <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+# <versionRange minVersion="1.7" maxVersion="1.7.*"/>
+# </targetApplication>
+# <targetApplication id="toolkit@mozilla.org">
+# <versionRange minVersion="1.9" maxVersion="1.9.*"/>
+# </targetApplication>
+# </versionRange>
+# <versionRange minVersion="3.0" maxVersion="3.0.*">
+# <targetApplication id="{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}">
+# <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+# </targetApplication>
+# <targetApplication id="toolkit@mozilla.org">
+# <versionRange minVersion="1.9" maxVersion="1.9.*"/>
+# </targetApplication>
+# </versionRange>
+# </emItem>
+# <emItem id="item_2@domain" blockID="i2">
+# <versionRange minVersion="3.1" maxVersion="4.*"/>
+# </emItem>
+# <emItem id="item_3@domain">
+# <versionRange>
+# <targetApplication id="{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}">
+# <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+# </targetApplication>
+# </versionRange>
+# </emItem>
+# <emItem id="item_4@domain" blockID="i3">
+# <versionRange>
+# <targetApplication>
+# <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+# </targetApplication>
+# </versionRange>
+# <emItem id="/@badperson\.com$/"/>
+# </emItems>
+# <pluginItems>
+# <pluginItem blockID="i4">
+# <!-- All match tags must match a plugin to blocklist a plugin -->
+# <match name="name" exp="some plugin"/>
+# <match name="description" exp="1[.]2[.]3"/>
+# </pluginItem>
+# </pluginItems>
+# <certItems>
+# <!-- issuerName is the DER issuer name data base64 encoded... -->
+# <certItem issuerName="MA0xCzAJBgNVBAMMAmNh">
+# <!-- ... as is the serial number DER data -->
+# <serialNumber>AkHVNA==</serialNumber>
+# </certItem>
+# </certItems>
+# </blocklist>
+ */
+
+ _loadBlocklistFromFile: function Blocklist_loadBlocklistFromFile(file) {
+ if (!gBlocklistEnabled) {
+ LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
+ return;
+ }
+
+ let telemetry = Services.telemetry;
+
+ if (this._isBlocklistPreloaded()) {
+ telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(false);
+ this._loadBlocklistFromString(this._preloadedBlocklistContent);
+ delete this._preloadedBlocklistContent;
+ return;
+ }
+
+ if (!file.exists()) {
+ LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist " + file.path);
+ return;
+ }
+
+ telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(true);
+
+ let text = "";
+ let fstream = null;
+ let cstream = null;
+
+ try {
+ fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
+ .createInstance(Components.interfaces.nsIConverterInputStream);
+
+ fstream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ let str = {};
+ let read = 0;
+
+ do {
+ read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
+ text += str.value;
+ } while (read != 0);
+ } catch (e) {
+ LOG("Blocklist::_loadBlocklistFromFile: Failed to load XML file " + e);
+ } finally {
+ if (cstream)
+ cstream.close();
+ if (fstream)
+ fstream.close();
+ }
+
+ if (text)
+ this._loadBlocklistFromString(text);
+ },
+
+ _isBlocklistLoaded: function() {
+ return this._addonEntries != null && this._pluginEntries != null;
+ },
+
+ _isBlocklistPreloaded: function() {
+ return this._preloadedBlocklistContent != null;
+ },
+
+ /* Used for testing */
+ _clear: function() {
+ this._addonEntries = null;
+ this._pluginEntries = null;
+ this._preloadedBlocklistContent = null;
+ },
+
+ _preloadBlocklist: Task.async(function*() {
+ let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
+ try {
+ yield this._preloadBlocklistFile(profPath);
+ return;
+ } catch (e) {
+ LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e)
+ }
+
+ var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+ try{
+ yield this._preloadBlocklistFile(appFile.path);
+ return;
+ } catch (e) {
+ LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e)
+ }
+
+ LOG("Blocklist::_preloadBlocklist: no XML File found");
+ }),
+
+ _preloadBlocklistFile: Task.async(function* (path){
+ if (this._addonEntries) {
+ // The file has been already loaded.
+ return;
+ }
+
+ if (!gBlocklistEnabled) {
+ LOG("Blocklist::_preloadBlocklistFile: blocklist is disabled");
+ return;
+ }
+
+ let text = yield OS.File.read(path, { encoding: "utf-8" });
+
+ if (!this._addonEntries) {
+ // Store the content only if a sync load has not been performed in the meantime.
+ this._preloadedBlocklistContent = text;
+ }
+ }),
+
+ _loadBlocklistFromString : function Blocklist_loadBlocklistFromString(text) {
+ try {
+ var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
+ createInstance(Ci.nsIDOMParser);
+ var doc = parser.parseFromString(text, "text/xml");
+ if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
+ LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
+ "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
+ "Received: " + doc.documentElement.namespaceURI);
+ return;
+ }
+
+ var childNodes = doc.documentElement.childNodes;
+ for (let element of childNodes) {
+ if (!(element instanceof Ci.nsIDOMElement))
+ continue;
+ switch (element.localName) {
+ case "emItems":
+ this._addonEntries = this._processItemNodes(element.childNodes, "em",
+ this._handleEmItemNode);
+ break;
+ case "pluginItems":
+ this._pluginEntries = this._processItemNodes(element.childNodes, "plugin",
+ this._handlePluginItemNode);
+ break;
+ case "certItems":
+ this._processItemNodes(element.childNodes, "cert",
+ this._handleCertItemNode.bind(this));
+ break;
+ default:
+ Services.obs.notifyObservers(element,
+ "blocklist-data-" + element.localName,
+ null);
+ }
+ }
+ gCertBlocklistService.saveEntries();
+ }
+ catch (e) {
+ LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
+ return;
+ }
+ },
+
+ _processItemNodes: function Blocklist_processItemNodes(itemNodes, prefix, handler) {
+ var result = [];
+ var itemName = prefix + "Item";
+ for (var i = 0; i < itemNodes.length; ++i) {
+ var blocklistElement = itemNodes.item(i);
+ if (!(blocklistElement instanceof Ci.nsIDOMElement) ||
+ blocklistElement.localName != itemName)
+ continue;
+
+ handler(blocklistElement, result);
+ }
+ return result;
+ },
+
+ _handleCertItemNode: function Blocklist_handleCertItemNode(blocklistElement,
+ result) {
+ let issuer = blocklistElement.getAttribute("issuerName");
+ for (let snElement of blocklistElement.children) {
+ try {
+ gCertBlocklistService.addRevokedCert(issuer, snElement.textContent);
+ } catch (e) {
+ // we want to keep trying other elements since missing all items
+ // is worse than missing one
+ LOG("Blocklist::_handleCertItemNode: Error adding revoked cert " + e);
+ }
+ }
+ },
+
+ _handleEmItemNode: function Blocklist_handleEmItemNode(blocklistElement, result) {
+ if (!matchesOSABI(blocklistElement))
+ return;
+
+ let blockEntry = {
+ versions: [],
+ prefs: [],
+ blockID: null,
+ attributes: new Map()
+ // Atleast one of EXTENSION_BLOCK_FILTERS must get added to attributes
+ };
+
+ // Any filter starting with '/' is interpreted as a regex. So if an attribute
+ // starts with a '/' it must be checked via a regex.
+ function regExpCheck(attr) {
+ return attr.startsWith("/") ? parseRegExp(attr) : attr;
+ }
+
+ for (let filter of EXTENSION_BLOCK_FILTERS) {
+ let attr = blocklistElement.getAttribute(filter);
+ if (attr)
+ blockEntry.attributes.set(filter, regExpCheck(attr));
+ }
+
+ var childNodes = blocklistElement.childNodes;
+
+ for (let x = 0; x < childNodes.length; x++) {
+ var childElement = childNodes.item(x);
+ if (!(childElement instanceof Ci.nsIDOMElement))
+ continue;
+ if (childElement.localName === "prefs") {
+ let prefElements = childElement.childNodes;
+ for (let i = 0; i < prefElements.length; i++) {
+ let prefElement = prefElements.item(i);
+ if (!(prefElement instanceof Ci.nsIDOMElement) ||
+ prefElement.localName !== "pref")
+ continue;
+ blockEntry.prefs.push(prefElement.textContent);
+ }
+ }
+ else if (childElement.localName === "versionRange")
+ blockEntry.versions.push(new BlocklistItemData(childElement));
+ }
+ // if only the extension ID is specified block all versions of the
+ // extension for the current application.
+ if (blockEntry.versions.length == 0)
+ blockEntry.versions.push(new BlocklistItemData(null));
+
+ blockEntry.blockID = blocklistElement.getAttribute("blockID");
+
+ result.push(blockEntry);
+ },
+
+ _handlePluginItemNode: function Blocklist_handlePluginItemNode(blocklistElement, result) {
+ if (!matchesOSABI(blocklistElement))
+ return;
+
+ var matchNodes = blocklistElement.childNodes;
+ var blockEntry = {
+ matches: {},
+ versions: [],
+ blockID: null,
+ infoURL: null,
+ };
+ var hasMatch = false;
+ for (var x = 0; x < matchNodes.length; ++x) {
+ var matchElement = matchNodes.item(x);
+ if (!(matchElement instanceof Ci.nsIDOMElement))
+ continue;
+ if (matchElement.localName == "match") {
+ var name = matchElement.getAttribute("name");
+ var exp = matchElement.getAttribute("exp");
+ try {
+ blockEntry.matches[name] = new RegExp(exp, "m");
+ hasMatch = true;
+ } catch (e) {
+ // Ignore invalid regular expressions
+ }
+ }
+ if (matchElement.localName == "versionRange") {
+ blockEntry.versions.push(new BlocklistItemData(matchElement));
+ }
+ else if (matchElement.localName == "infoURL") {
+ blockEntry.infoURL = matchElement.textContent;
+ }
+ }
+ // Plugin entries require *something* to match to an actual plugin
+ if (!hasMatch)
+ return;
+ // Add a default versionRange if there wasn't one specified
+ if (blockEntry.versions.length == 0)
+ blockEntry.versions.push(new BlocklistItemData(null));
+
+ blockEntry.blockID = blocklistElement.getAttribute("blockID");
+
+ result.push(blockEntry);
+ },
+
+ /* See nsIBlocklistService */
+ getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin,
+ appVersion, toolkitVersion) {
+ if (!this._isBlocklistLoaded())
+ this._loadBlocklist();
+ return this._getPluginBlocklistState(plugin, this._pluginEntries,
+ appVersion, toolkitVersion);
+ },
+
+ /**
+ * Private version of getPluginBlocklistState that allows the caller to pass in
+ * the plugin blocklist entries.
+ *
+ * @param plugin
+ * The nsIPluginTag to get the blocklist state for.
+ * @param pluginEntries
+ * The plugin blocklist entries to compare against.
+ * @param appVersion
+ * The application version to compare to, will use the current
+ * version if null.
+ * @param toolkitVersion
+ * The toolkit version to compare to, will use the current version if
+ * null.
+ * @returns The blocklist state for the item, one of the STATE constants as
+ * defined in nsIBlocklistService.
+ */
+ _getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin,
+ pluginEntries, appVersion, toolkitVersion) {
+ if (!gBlocklistEnabled)
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+
+ if (!appVersion)
+ appVersion = gApp.version;
+ if (!toolkitVersion)
+ toolkitVersion = gApp.platformVersion;
+
+ for each (var blockEntry in pluginEntries) {
+ var matchFailed = false;
+ for (var name in blockEntry.matches) {
+ if (!(name in plugin) ||
+ typeof(plugin[name]) != "string" ||
+ !blockEntry.matches[name].test(plugin[name])) {
+ matchFailed = true;
+ break;
+ }
+ }
+
+ if (matchFailed)
+ continue;
+
+ for (let blockEntryVersion of blockEntry.versions) {
+ if (blockEntryVersion.includesItem(plugin.version, appVersion,
+ toolkitVersion)) {
+ if (blockEntryVersion.severity >= gBlocklistLevel)
+ return Ci.nsIBlocklistService.STATE_BLOCKED;
+ if (blockEntryVersion.severity == SEVERITY_OUTDATED) {
+ let vulnerabilityStatus = blockEntryVersion.vulnerabilityStatus;
+ if (vulnerabilityStatus == VULNERABILITYSTATUS_UPDATE_AVAILABLE)
+ return Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE;
+ if (vulnerabilityStatus == VULNERABILITYSTATUS_NO_UPDATE)
+ return Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE;
+ return Ci.nsIBlocklistService.STATE_OUTDATED;
+ }
+ return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
+ }
+ }
+ }
+
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ },
+
+ /**
+ * Get the matching blocklist entry for the passed plugin, if
+ * available.
+ * @param plugin The plugin to find the block entry for.
+ * @returns The block entry which matches the passed plugin, null
+ * otherwise.
+ */
+ _getPluginBlockEntry: function (plugin) {
+ if (!gBlocklistEnabled)
+ return null;
+
+ if (!this._isBlocklistLoaded())
+ this._loadBlocklist();
+
+ for each (let blockEntry in this._pluginEntries) {
+ if (matchesAllPluginNames(blockEntry, plugin)) {
+ return blockEntry;
+ }
+ }
+
+ return null;
+ },
+
+ /* See nsIBlocklistService */
+ getPluginBlocklistURL: function Blocklist_getPluginBlocklistURL(plugin) {
+ let blockEntry = this._getPluginBlockEntry(plugin);
+ if (!blockEntry || !blockEntry.blockID) {
+ return null;
+ }
+
+ return this._createBlocklistURL(blockEntry.blockID);
+ },
+
+ /* See nsIBlocklistService */
+ getPluginInfoURL: function (plugin) {
+ let blockEntry = this._getPluginBlockEntry(plugin);
+ if (!blockEntry || !blockEntry.blockID) {
+ return null;
+ }
+
+ return blockEntry.infoURL;
+ },
+
+ _blocklistUpdated: function Blocklist_blocklistUpdated(oldAddonEntries, oldPluginEntries) {
+ var addonList = [];
+
+ // A helper function that reverts the prefs passed to default values.
+ function resetPrefs(prefs) {
+ for (let pref of prefs)
+ gPref.clearUserPref(pref);
+ }
+ var self = this;
+ const types = ["extension", "theme", "locale", "dictionary", "service"];
+ AddonManager.getAddonsByTypes(types, function blocklistUpdated_getAddonsByTypes(addons) {
+
+ for (let addon of addons) {
+ let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
+ if (oldAddonEntries)
+ oldState = self._getAddonBlocklistState(addon, oldAddonEntries);
+ let state = self.getAddonBlocklistState(addon);
+
+ LOG("Blocklist state for " + addon.id + " changed from " +
+ oldState + " to " + state);
+
+ // We don't want to re-warn about add-ons
+ if (state == oldState)
+ continue;
+
+ if (state === Ci.nsIBlocklistService.STATE_BLOCKED) {
+ // It's a hard block. We must reset certain preferences.
+ let prefs = self._getAddonPrefs(addon);
+ resetPrefs(prefs);
+ }
+
+ // Ensure that softDisabled is false if the add-on is not soft blocked
+ if (state != Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
+ addon.softDisabled = false;
+
+ // Don't warn about add-ons becoming unblocked.
+ if (state == Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
+ continue;
+
+ // If an add-on has dropped from hard to soft blocked just mark it as
+ // soft disabled and don't warn about it.
+ if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED &&
+ oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ addon.softDisabled = true;
+ continue;
+ }
+
+ // If the add-on is already disabled for some reason then don't warn
+ // about it
+ if (!addon.isActive)
+ continue;
+
+ addonList.push({
+ name: addon.name,
+ version: addon.version,
+ icon: addon.iconURL,
+ disable: false,
+ blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
+ item: addon,
+ url: self.getAddonBlocklistURL(addon),
+ });
+ }
+
+ AddonManagerPrivate.updateAddonAppDisabledStates();
+
+ var phs = Cc["@mozilla.org/plugin/host;1"].
+ getService(Ci.nsIPluginHost);
+ var plugins = phs.getPluginTags();
+
+ for (let plugin of plugins) {
+ let oldState = -1;
+ if (oldPluginEntries)
+ oldState = self._getPluginBlocklistState(plugin, oldPluginEntries);
+ let state = self.getPluginBlocklistState(plugin);
+ LOG("Blocklist state for " + plugin.name + " changed from " +
+ oldState + " to " + state);
+ // We don't want to re-warn about items
+ if (state == oldState)
+ continue;
+
+ if (oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
+ plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ }
+ else if (!plugin.disabled && state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
+ if (state == Ci.nsIBlocklistService.STATE_OUTDATED) {
+ gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true);
+ }
+ else if (state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE &&
+ state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
+ addonList.push({
+ name: plugin.name,
+ version: plugin.version,
+ icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
+ disable: false,
+ blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
+ item: plugin,
+ url: self.getPluginBlocklistURL(plugin),
+ });
+ }
+ }
+ }
+
+ if (addonList.length == 0) {
+ Services.obs.notifyObservers(self, "blocklist-updated", "");
+ return;
+ }
+
+ if ("@mozilla.org/addons/blocklist-prompt;1" in Cc) {
+ try {
+ let blockedPrompter = Cc["@mozilla.org/addons/blocklist-prompt;1"]
+ .getService(Ci.nsIBlocklistPrompt);
+ blockedPrompter.prompt(addonList);
+ } catch (e) {
+ LOG(e);
+ }
+ Services.obs.notifyObservers(self, "blocklist-updated", "");
+ return;
+ }
+
+ var args = {
+ restart: false,
+ list: addonList
+ };
+ // This lets the dialog get the raw js object
+ args.wrappedJSObject = args;
+
+ /*
+ Some tests run without UI, so the async code listens to a message
+ that can be sent programatically
+ */
+ let applyBlocklistChanges = function blocklistUpdated_applyBlocklistChanges() {
+ for (let addon of addonList) {
+ if (!addon.disable)
+ continue;
+
+ if (addon.item instanceof Ci.nsIPluginTag)
+ addon.item.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ else {
+ // This add-on is softblocked.
+ addon.item.softDisabled = true;
+ // We must revert certain prefs.
+ let prefs = self._getAddonPrefs(addon.item);
+ resetPrefs(prefs);
+ }
+ }
+
+ if (args.restart)
+ restartApp();
+
+ Services.obs.notifyObservers(self, "blocklist-updated", "");
+ Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed");
+ }
+
+ Services.obs.addObserver(applyBlocklistChanges, "addon-blocklist-closed", false);
+
+ if (getPref("getBoolPref", PREF_BLOCKLIST_SUPPRESSUI, false)) {
+ applyBlocklistChanges();
+ return;
+ }
+
+ function blocklistUnloadHandler(event) {
+ if (event.target.location == URI_BLOCKLIST_DIALOG) {
+ applyBlocklistChanges();
+ blocklistWindow.removeEventListener("unload", blocklistUnloadHandler);
+ }
+ }
+
+ let blocklistWindow = Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
+ "chrome,centerscreen,dialog,titlebar", args);
+ if (blocklistWindow)
+ blocklistWindow.addEventListener("unload", blocklistUnloadHandler, false);
+ });
+ },
+
+ classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIBlocklistService,
+ Ci.nsITimerCallback]),
+};
+
+/**
+ * Helper for constructing a blocklist.
+ */
+function BlocklistItemData(versionRangeElement) {
+ var versionRange = this.getBlocklistVersionRange(versionRangeElement);
+ this.minVersion = versionRange.minVersion;
+ this.maxVersion = versionRange.maxVersion;
+ if (versionRangeElement && versionRangeElement.hasAttribute("severity"))
+ this.severity = versionRangeElement.getAttribute("severity");
+ else
+ this.severity = DEFAULT_SEVERITY;
+ if (versionRangeElement && versionRangeElement.hasAttribute("vulnerabilitystatus")) {
+ this.vulnerabilityStatus = versionRangeElement.getAttribute("vulnerabilitystatus");
+ } else {
+ this.vulnerabilityStatus = VULNERABILITYSTATUS_NONE;
+ }
+ this.targetApps = { };
+ var found = false;
+
+ if (versionRangeElement) {
+ for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
+ var targetAppElement = versionRangeElement.childNodes.item(i);
+ if (!(targetAppElement instanceof Ci.nsIDOMElement) ||
+ targetAppElement.localName != "targetApplication")
+ continue;
+ found = true;
+ // default to the current application if id is not provided.
+ var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
+ this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
+ }
+ }
+ // Default to all versions of the current application when no targetApplication
+ // elements were found
+ if (!found)
+ this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
+}
+
+BlocklistItemData.prototype = {
+ /**
+ * Tests if a version of an item is included in the version range and target
+ * application information represented by this BlocklistItemData using the
+ * provided application and toolkit versions.
+ * @param version
+ * The version of the item being tested.
+ * @param appVersion
+ * The application version to test with.
+ * @param toolkitVersion
+ * The toolkit version to test with.
+ * @returns True if the version range covers the item version and application
+ * or toolkit version.
+ */
+ includesItem: function BlocklistItemData_includesItem(version, appVersion, toolkitVersion) {
+ // Some platforms have no version for plugins, these don't match if there
+ // was a min/maxVersion provided
+ if (!version && (this.minVersion || this.maxVersion))
+ return false;
+
+ // Check if the item version matches
+ if (!this.matchesRange(version, this.minVersion, this.maxVersion))
+ return false;
+
+ // Check if the application version matches
+ if (this.matchesTargetRange(gApp.ID, appVersion))
+ return true;
+
+ // Check if the toolkit version matches
+ return this.matchesTargetRange(TOOLKIT_ID, toolkitVersion);
+ },
+
+ /**
+ * Checks if a version is higher than or equal to the minVersion (if provided)
+ * and lower than or equal to the maxVersion (if provided).
+ * @param version
+ * The version to test.
+ * @param minVersion
+ * The minimum version. If null it is assumed that version is always
+ * larger.
+ * @param maxVersion
+ * The maximum version. If null it is assumed that version is always
+ * smaller.
+ */
+ matchesRange: function BlocklistItemData_matchesRange(version, minVersion, maxVersion) {
+ if (minVersion && gVersionChecker.compare(version, minVersion) < 0)
+ return false;
+ if (maxVersion && gVersionChecker.compare(version, maxVersion) > 0)
+ return false;
+ return true;
+ },
+
+ /**
+ * Tests if there is a matching range for the given target application id and
+ * version.
+ * @param appID
+ * The application ID to test for, may be for an application or toolkit
+ * @param appVersion
+ * The version of the application to test for.
+ * @returns True if this version range covers the application version given.
+ */
+ matchesTargetRange: function BlocklistItemData_matchesTargetRange(appID, appVersion) {
+ var blTargetApp = this.targetApps[appID];
+ if (!blTargetApp)
+ return false;
+
+ for (let app of blTargetApp) {
+ if (this.matchesRange(appVersion, app.minVersion, app.maxVersion))
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Retrieves a version range (e.g. minVersion and maxVersion) for a
+ * blocklist item's targetApplication element.
+ * @param targetAppElement
+ * A targetApplication blocklist element.
+ * @returns An array of JS objects with the following properties:
+ * "minVersion" The minimum version in a version range (default = null).
+ * "maxVersion" The maximum version in a version range (default = null).
+ */
+ getBlocklistAppVersions: function BlocklistItemData_getBlocklistAppVersions(targetAppElement) {
+ var appVersions = [ ];
+
+ if (targetAppElement) {
+ for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
+ var versionRangeElement = targetAppElement.childNodes.item(i);
+ if (!(versionRangeElement instanceof Ci.nsIDOMElement) ||
+ versionRangeElement.localName != "versionRange")
+ continue;
+ appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
+ }
+ }
+ // return minVersion = null and maxVersion = null if no specific versionRange
+ // elements were found
+ if (appVersions.length == 0)
+ appVersions.push(this.getBlocklistVersionRange(null));
+ return appVersions;
+ },
+
+ /**
+ * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
+ * versionRange element.
+ * @param versionRangeElement
+ * The versionRange blocklist element.
+ * @returns A JS object with the following properties:
+ * "minVersion" The minimum version in a version range (default = null).
+ * "maxVersion" The maximum version in a version range (default = null).
+ */
+ getBlocklistVersionRange: function BlocklistItemData_getBlocklistVersionRange(versionRangeElement) {
+ var minVersion = null;
+ var maxVersion = null;
+ if (!versionRangeElement)
+ return { minVersion: minVersion, maxVersion: maxVersion };
+
+ if (versionRangeElement.hasAttribute("minVersion"))
+ minVersion = versionRangeElement.getAttribute("minVersion");
+ if (versionRangeElement.hasAttribute("maxVersion"))
+ maxVersion = versionRangeElement.getAttribute("maxVersion");
+
+ return { minVersion: minVersion, maxVersion: maxVersion };
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Blocklist]);
diff --git a/toolkit/mozapps/extensions/test/AddonManagerTesting.jsm b/toolkit/mozapps/extensions/test/AddonManagerTesting.jsm
new file mode 100644
index 000000000..52c954b1a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/AddonManagerTesting.jsm
@@ -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/. */
+
+// This file is a test-only JSM containing utility methods for
+// interacting with the add-ons manager.
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+ "AddonTestUtils",
+];
+
+const {utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
+ "resource://gre/modules/AddonManager.jsm");
+
+this.AddonTestUtils = {
+ /**
+ * Uninstall an add-on that is specified by its ID.
+ *
+ * The returned promise resolves on successful uninstall and rejects
+ * if the add-on is not unknown.
+ *
+ * @return Promise<restartRequired>
+ */
+ uninstallAddonByID: function (id) {
+ let deferred = Promise.defer();
+
+ AddonManager.getAddonByID(id, (addon) => {
+ if (!addon) {
+ deferred.reject(new Error("Add-on is not known: " + id));
+ return;
+ }
+
+ let listener = {
+ onUninstalling: function (addon, needsRestart) {
+ if (addon.id != id) {
+ return;
+ }
+
+ if (needsRestart) {
+ AddonManager.removeAddonListener(listener);
+ deferred.resolve(true);
+ }
+ },
+
+ onUninstalled: function (addon) {
+ if (addon.id != id) {
+ return;
+ }
+
+ AddonManager.removeAddonListener(listener);
+ deferred.resolve(false);
+ },
+
+ onOperationCancelled: function (addon) {
+ if (addon.id != id) {
+ return;
+ }
+
+ AddonManager.removeAddonListener(listener);
+ deferred.reject(new Error("Uninstall cancelled."));
+ },
+ };
+
+ AddonManager.addAddonListener(listener);
+ addon.uninstall();
+ });
+
+ return deferred.promise;
+ },
+
+ /**
+ * Install an XPI add-on from a URL.
+ *
+ * @return Promise<addon>
+ */
+ installXPIFromURL: function (url, hash, name, iconURL, version) {
+ let deferred = Promise.defer();
+
+ AddonManager.getInstallForURL(url, (install) => {
+ let fail = () => { deferred.reject(new Error("Add-on install failed.")) };
+
+ let listener = {
+ onDownloadCancelled: fail,
+ onDownloadFailed: fail,
+ onInstallCancelled: fail,
+ onInstallFailed: fail,
+ onInstallEnded: function (install, addon) {
+ deferred.resolve(addon);
+ },
+ };
+
+ install.addListener(listener);
+ install.install();
+ }, "application/x-xpinstall", hash, name, iconURL, version);
+
+ return deferred.promise;
+ },
+};
diff --git a/toolkit/mozapps/extensions/test/Makefile.in b/toolkit/mozapps/extensions/test/Makefile.in
new file mode 100644
index 000000000..6c667ecab
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/Makefile.in
@@ -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/.
+
+ADDONSRC = $(srcdir)/addons
+TESTROOT = $(CURDIR)/$(DEPTH)/_tests/xpcshell/$(relativesrcdir)
+TESTXPI = $(TESTROOT)/xpcshell/addons
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+ rm -rf $(TESTXPI)
+ $(NSINSTALL) -D $(TESTXPI)
+ if [ -d $(ADDONSRC) ]; then \
+ $(EXIT_ON_ERROR) \
+ for dir in $(ADDONSRC)/*; do \
+ base=`basename $$dir` ; \
+ (cd $$dir && zip -qr $(TESTXPI)/$$base.xpi *) \
+ done \
+ fi
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_hard1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_hard1_1/install.rdf
new file mode 100644
index 000000000..7b1b02a17
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_hard1_1/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>hardblock@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Hardblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_hard1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_hard1_2/install.rdf
new file mode 100644
index 000000000..ae364637e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_hard1_2/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>hardblock@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>Hardblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_hard1_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_hard1_3/install.rdf
new file mode 100644
index 000000000..568c41a43
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_hard1_3/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>hardblock@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>Hardblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_1/install.rdf
new file mode 100644
index 000000000..1281ab53f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_1/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>regexpblock@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>RegExp-blocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_2/install.rdf
new file mode 100644
index 000000000..8b6dd09f5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_2/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>regexpblock@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>RegExp-blocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_3/install.rdf
new file mode 100644
index 000000000..fade395f9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_regexp1_3/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>regexpblock@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>RegExp-blocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft1_1/install.rdf
new file mode 100644
index 000000000..4a18f64e0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft1_1/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft1_2/install.rdf
new file mode 100644
index 000000000..8a2519222
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft1_2/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft1_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft1_3/install.rdf
new file mode 100644
index 000000000..2c55e5ff7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft1_3/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock1@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft2_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft2_1/install.rdf
new file mode 100644
index 000000000..eebac4b21
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft2_1/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft2_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft2_2/install.rdf
new file mode 100644
index 000000000..f37741d04
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft2_2/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock2@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft2_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft2_3/install.rdf
new file mode 100644
index 000000000..e15f99264
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft2_3/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock2@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft3_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft3_1/install.rdf
new file mode 100644
index 000000000..f4b70a24b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft3_1/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft3_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft3_2/install.rdf
new file mode 100644
index 000000000..987204fa6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft3_2/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock3@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft3_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft3_3/install.rdf
new file mode 100644
index 000000000..19ab4b9fe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft3_3/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock3@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft4_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft4_1/install.rdf
new file mode 100644
index 000000000..a3cd06f5f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft4_1/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock4@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft4_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft4_2/install.rdf
new file mode 100644
index 000000000..eeff9fb79
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft4_2/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock4@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft4_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft4_3/install.rdf
new file mode 100644
index 000000000..1d2650603
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft4_3/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>softblock4@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft5_1/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft5_1/install.rdf
new file mode 100644
index 000000000..85d7108d6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft5_1/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<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>softblock5@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:internalName>test/1.0</em:internalName>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft5_2/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft5_2/install.rdf
new file mode 100644
index 000000000..394fd909e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft5_2/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<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>softblock5@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:internalName>test/1.0</em:internalName>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/blocklist_soft5_3/install.rdf b/toolkit/mozapps/extensions/test/addons/blocklist_soft5_3/install.rdf
new file mode 100644
index 000000000..2a1fec25a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/blocklist_soft5_3/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<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>softblock5@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:name>Softblocked add-on</em:name>
+ <em:internalName>test/1.0</em:internalName>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js b/toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js
new file mode 100644
index 000000000..f8a62bcaa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js
@@ -0,0 +1,29 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+let seenGlobals = new Set();
+let scope = this;
+function checkGlobal(name, type) {
+ if (scope[name] && typeof(scope[name]) == type)
+ seenGlobals.add(name);
+}
+
+let wrapped = {};
+Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals", null);
+for (let [name, type] of wrapped.expectedGlobals) {
+ checkGlobal(name, type);
+}
+
+function install(data, reason) {
+}
+
+function startup(data, reason) {
+ Services.obs.notifyObservers({
+ wrappedJSObject: seenGlobals
+ }, "bootstrap-seen-globals", null);
+}
+
+function shutdown(data, reason) {
+}
+
+function uninstall(data, reason) {
+}
diff --git a/toolkit/mozapps/extensions/test/addons/bootstrap_globals/install.rdf b/toolkit/mozapps/extensions/test/addons/bootstrap_globals/install.rdf
new file mode 100644
index 000000000..f11a626fd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/bootstrap_globals/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>bootstrap_globals@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap Globals</em:name>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/min1max1/install.rdf b/toolkit/mozapps/extensions/test/addons/min1max1/install.rdf
new file mode 100644
index 000000000..3a0ace227
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/min1max1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>min1max1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test minVersion 1 maxVersion 1</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/min1max2/install.rdf b/toolkit/mozapps/extensions/test/addons/min1max2/install.rdf
new file mode 100644
index 000000000..0184f1963
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/min1max2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>min1max2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test minVersion 1 maxVersion 2</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/min1max3/install.rdf b/toolkit/mozapps/extensions/test/addons/min1max3/install.rdf
new file mode 100644
index 000000000..dbb1b7318
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/min1max3/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>min1max3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test minVersion 1 maxVersion 3</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/min1max3b/install.rdf b/toolkit/mozapps/extensions/test/addons/min1max3b/install.rdf
new file mode 100644
index 000000000..f50c65c6a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/min1max3b/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>min1max3b@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Another Test minVersion 1 maxVersion 3</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/override1x2-1x3/install.rdf b/toolkit/mozapps/extensions/test/addons/override1x2-1x3/install.rdf
new file mode 100644
index 000000000..92cf3ec96
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/override1x2-1x3/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>override1x2-1x3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:updateURL>http://localhost:4444/data/test_bug542391.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test override compat from 1..2 to 1..3</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_AddonRepository_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_1/install.rdf
new file mode 100644
index 000000000..82cfd0472
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_1/install.rdf
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<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>test_AddonRepository_1@tests.mozilla.org</em:id>
+ <em:version>1.1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>XPI Add-on 1</em:name>
+ <em:description>XPI Add-on 1 - Description</em:description>
+ <em:creator>XPI Add-on 1 - Creator</em:creator>
+ <em:developer>XPI Add-on 1 - First Developer</em:developer>
+ <em:developer>XPI Add-on 1 - Second Developer</em:developer>
+ <em:translator>XPI Add-on 1 - First Translator</em:translator>
+ <em:translator>XPI Add-on 1 - Second Translator</em:translator>
+ <em:contributor>XPI Add-on 1 - First Contributor</em:contributor>
+ <em:contributor>XPI Add-on 1 - Second Contributor</em:contributor>
+ <em:homepageURL>http://localhost/xpi/1/homepage.html</em:homepageURL>
+ <em:optionsURL>http://localhost/xpi/1/options.html</em:optionsURL>
+ <em:aboutURL>http://localhost/xpi/1/about.html</em:aboutURL>
+ <em:iconURL>http://localhost/xpi/1/icon.png</em:iconURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_AddonRepository_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_2/install.rdf
new file mode 100644
index 000000000..80776e6c3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>test_AddonRepository_2@tests.mozilla.org</em:id>
+ <em:type>4</em:type>
+ <em:internalName>test2/1.0</em:internalName>
+ <em:version>1.2</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>XPI Add-on 2</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/icon.png b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/icon.png
new file mode 100644
index 000000000..41409edfe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/icon.png
@@ -0,0 +1 @@
+Fake icon image
diff --git a/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/install.rdf
new file mode 100644
index 000000000..bade9c069
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>test_AddonRepository_3@tests.mozilla.org</em:id>
+ <em:type>4</em:type>
+ <em:internalName>test3/1.0</em:internalName>
+ <em:version>1.3</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>XPI Add-on 3</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/preview.png b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/preview.png
new file mode 100644
index 000000000..321ce47cf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_AddonRepository_3/preview.png
@@ -0,0 +1 @@
+Fake preview image
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js
new file mode 100644
index 000000000..eba6762c8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js
@@ -0,0 +1,32 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Test steps chain from pref observers on *_reason,
+// so always set that last
+function install(data, reason) {
+ Components.utils.import(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.installed_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", data.oldVersion);
+ Components.utils.unload(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Components.utils.reportError("bootstrap startup");
+ Components.utils.import(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.active_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", data.oldVersion);
+ Components.utils.unload(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf
new file mode 100644
index 000000000..f02a3869c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<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>bootstrap1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+ <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+ <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/version.jsm b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/version.jsm
new file mode 100644
index 000000000..7fe60e632
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/version.jsm
@@ -0,0 +1,3 @@
+this.EXPORTED_SYMBOLS = ["VERSION"];
+
+this.VERSION = 1;
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/bootstrap.js
new file mode 100644
index 000000000..8839bfb7d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/bootstrap.js
@@ -0,0 +1,31 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Test steps chain from pref observers on *_reason,
+// so always set that last
+function install(data, reason) {
+ Components.utils.import(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.installed_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", data.oldVersion);
+ Components.utils.unload(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Components.utils.import(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.active_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", data.oldVersion);
+ Components.utils.unload(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/install.rdf
new file mode 100644
index 000000000..480f03fd1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bootstrap1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/version.jsm b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/version.jsm
new file mode 100644
index 000000000..532741e12
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/version.jsm
@@ -0,0 +1,3 @@
+this.EXPORTED_SYMBOLS = ["VERSION"];
+
+this.VERSION = 2;
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js
new file mode 100644
index 000000000..8839bfb7d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js
@@ -0,0 +1,31 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Test steps chain from pref observers on *_reason,
+// so always set that last
+function install(data, reason) {
+ Components.utils.import(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.installed_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", data.oldVersion);
+ Components.utils.unload(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Components.utils.import(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.active_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", data.oldVersion);
+ Components.utils.unload(data.resourceURI.spec + "version.jsm");
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/install.rdf
new file mode 100644
index 000000000..e9385cbb3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bootstrap1@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>undefined</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/version.jsm b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/version.jsm
new file mode 100644
index 000000000..1b813faaf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/version.jsm
@@ -0,0 +1,3 @@
+this.EXPORTED_SYMBOLS = ["VERSION"];
+
+this.VERSION = 3;
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_4/install.rdf
new file mode 100644
index 000000000..2b88e0ad0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_4/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>bootstrap1@tests.mozilla.org</em:id>
+ <em:version>4.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js
new file mode 100644
index 000000000..476edfeee
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js
@@ -0,0 +1,17 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest2.installed_version", 1);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest2.active_version", 1);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest2.active_version", 0);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest2.installed_version", 0);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf
new file mode 100644
index 000000000..e0e8ca978
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<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>bootstrap2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 2</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+ <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+ <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_2/install.rdf
new file mode 100644
index 000000000..791a6263f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_2/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-2@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <!-- XPCShell -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Toolkit -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716_2.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_a_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_a_1/install.rdf
new file mode 100644
index 000000000..36d15b8aa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_a_1/install.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<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>bug299716-a@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test A</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_a_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_a_2/install.rdf
new file mode 100644
index 000000000..3521a503c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_a_2/install.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<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>bug299716-a@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test A</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_b_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_b_1/install.rdf
new file mode 100644
index 000000000..d92a4ec41
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_b_1/install.rdf
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<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>bug299716-b@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test B</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_b_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_b_2/install.rdf
new file mode 100644
index 000000000..c3ad76b84
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_b_2/install.rdf
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<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>bug299716-b@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test B</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_c_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_c_1/install.rdf
new file mode 100644
index 000000000..a937b6e76
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_c_1/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-c@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <!-- XPCShell -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Toolkit -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test C</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_c_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_c_2/install.rdf
new file mode 100644
index 000000000..8afca3ff9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_c_2/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-c@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <!-- XPCShell -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Toolkit -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test C</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_d_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_d_1/install.rdf
new file mode 100644
index 000000000..4c0dcc2ef
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_d_1/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-d@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <!-- XPCShell -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Toolkit, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test D</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_d_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_d_2/install.rdf
new file mode 100644
index 000000000..2b113809a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_d_2/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-d@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <!-- XPCShell -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Toolkit, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test D</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_e_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_e_1/install.rdf
new file mode 100644
index 000000000..03eb7180e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_e_1/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-e@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <!-- Toolkit -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- XPCShell, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test E</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_e_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_e_2/install.rdf
new file mode 100644
index 000000000..3ed7cd932
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_e_2/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-e@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <!-- Toolkit -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- XPCShell, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test E</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_f_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_f_1/install.rdf
new file mode 100644
index 000000000..cacf824c1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_f_1/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-f@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <!-- Toolkit, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- XPCShell, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test F</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_f_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_f_2/install.rdf
new file mode 100644
index 000000000..09954ec36
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_f_2/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug299716-f@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <!-- Toolkit, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- XPCShell, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test F</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_g_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_g_1/install.rdf
new file mode 100644
index 000000000..5e4a6f6a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_g_1/install.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<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>bug299716-g@tests.mozilla.org</em:id>
+ <em:version>0.1</em:version>
+
+ <!-- Toolkit, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test G</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug299716_g_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug299716_g_2/install.rdf
new file mode 100644
index 000000000..913233cec
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug299716_g_2/install.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<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>bug299716-g@tests.mozilla.org</em:id>
+ <em:version>0.2</em:version>
+
+ <!-- Toolkit, invalid -->
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 299716 test G</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug299716.rdf</em:updateURL>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_1/install.rdf
new file mode 100644
index 000000000..fd0dd50b7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_1/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_1@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 1</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121_1.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_2/install.rdf
new file mode 100644
index 000000000..607b68357
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_2/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_2@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 2</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_3/install.rdf
new file mode 100644
index 000000000..3a4c7eafc
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_3/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_3@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 5</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_4/install.rdf
new file mode 100644
index 000000000..8557df116
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_4/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_4@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 4</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121_4.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_5/install.rdf
new file mode 100644
index 000000000..343a9d44c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_5/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_5@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 5</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121_5.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_6/install.rdf
new file mode 100644
index 000000000..5a724cc99
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_6/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_6@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 6</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_7/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_7/install.rdf
new file mode 100644
index 000000000..70fe81168
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_7/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_7@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 7</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_8/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_8/install.rdf
new file mode 100644
index 000000000..2aface3b4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_8/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_8@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 8</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121_8.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug324121_9/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug324121_9/install.rdf
new file mode 100644
index 000000000..7804e833c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug324121_9/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<!-- Compatible to install -->
+
+<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>bug324121_9@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 324121 Test 9</em:name>
+ <em:updateURL>http://localhost:4444/data/test_bug324121_9.rdf</em:updateURL>
+
+ </Description>
+</RDF>
+
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug335238_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug335238_1/install.rdf
new file mode 100644
index 000000000..c60b5711b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug335238_1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug335238_1@tests.mozilla.org</em:id>
+ <em:version>1.3.4</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 335238</em:name>
+ <em:updateURL>http://localhost:4444/0?id=%ITEM_ID%&amp;version=%ITEM_VERSION%&amp;maxAppVersion=%ITEM_MAXAPPVERSION%&amp;status=%ITEM_STATUS%&amp;appId=%APP_ID%&amp;appVersion=%APP_VERSION%&amp;appOs=%APP_OS%&amp;appAbi=%APP_ABI%&amp;locale=%APP_LOCALE%&amp;reqVersion=%REQ_VERSION%</em:updateURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug335238_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug335238_2/install.rdf
new file mode 100644
index 000000000..23faf5a34
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug335238_2/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug335238_2@tests.mozilla.org</em:id>
+ <em:version>28at</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>7</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:requires>
+ <Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>72</em:maxVersion>
+ </Description>
+ </em:requires>
+
+ <em:name>Bug 335238</em:name>
+ <em:updateURL>http://localhost:4444/1?id=%ITEM_ID%&amp;version=%ITEM_VERSION%&amp;maxAppVersion=%ITEM_MAXAPPVERSION%&amp;status=%ITEM_STATUS%&amp;appId=%APP_ID%&amp;appVersion=%APP_VERSION%&amp;appOs=%APP_OS%&amp;appAbi=%APP_ABI%&amp;locale=%APP_LOCALE%&amp;reqVersion=%REQ_VERSION%</em:updateURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug335238_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug335238_3/install.rdf
new file mode 100644
index 000000000..d44448208
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug335238_3/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug335238_3@tests.mozilla.org</em:id>
+ <em:version>58</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:requires>
+ <Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>72</em:maxVersion>
+ </Description>
+ </em:requires>
+
+ <em:name>Bug 335238</em:name>
+ <em:updateURL>http://localhost:4444/2?id=%ITEM_ID%&amp;version=%ITEM_VERSION%&amp;maxAppVersion=%ITEM_MAXAPPVERSION%&amp;status=%ITEM_STATUS%&amp;appId=%APP_ID%&amp;appVersion=%APP_VERSION%&amp;appOs=%APP_OS%&amp;appAbi=%APP_ABI%&amp;locale=%APP_LOCALE%&amp;reqVersion=%REQ_VERSION%</em:updateURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug335238_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug335238_4/install.rdf
new file mode 100644
index 000000000..6ec052d36
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug335238_4/install.rdf
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<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>bug335238_4@tests.mozilla.org</em:id>
+ <em:version>4</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2+</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:requires>
+ <Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>72</em:maxVersion>
+ </Description>
+ </em:requires>
+
+ <em:name>Bug 335238</em:name>
+ <em:updateURL>http://localhost:4444/3?id=%ITEM_ID%&amp;version=%ITEM_VERSION%&amp;maxAppVersion=%ITEM_MAXAPPVERSION%&amp;status=%ITEM_STATUS%&amp;appId=%APP_ID%&amp;appVersion=%APP_VERSION%&amp;appOs=%APP_OS%&amp;appAbi=%APP_ABI%&amp;locale=%APP_LOCALE%&amp;reqVersion=%REQ_VERSION%</em:updateURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug371495/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug371495/install.rdf
new file mode 100644
index 000000000..c60caf594
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug371495/install.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+
+<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>bug371495@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test theme</em:name>
+ <em:type>4</em:type>
+ <em:internalName>test/1.0</em:internalName>
+ <em:optionsURL>chrome://foo/content/bar.xul</em:optionsURL>
+ <em:aboutURL>chrome://foo/content/bar.xul</em:aboutURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug394300_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug394300_1/install.rdf
new file mode 100644
index 000000000..2e5ace760
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug394300_1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug394300_1@tests.mozilla.org</em:id>
+ <em:version>5</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 394300 Test 1</em:name>
+ <em:updateURL>http://localhost:4444/test_bug394300.rdf</em:updateURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug394300_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug394300_2/install.rdf
new file mode 100644
index 000000000..ae54424d1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug394300_2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug394300_2@tests.mozilla.org</em:id>
+ <em:version>5</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>Bug 394300 Test 2</em:name>
+ <em:updateURL>http://localhost:4444/test_bug394300.rdf</em:updateURL>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug397778/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug397778/install.rdf
new file mode 100644
index 000000000..cfcfd406f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug397778/install.rdf
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+
+<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>bug397778@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:localized>
+ <Description em:locale="fr">
+ <em:name>fr Name</em:name>
+ <em:description>fr Description</em:description>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="de-DE">
+ <em:name>de-DE Name</em:name>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="ES-es">
+ <em:name>es-ES Name</em:name>
+ <em:description>es-ES Description</em:description>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="zh-TW">
+ <em:name>zh-TW Name</em:name>
+ <em:description>zh-TW Description</em:description>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="zh-CN">
+ <em:name>zh-CN Name</em:name>
+ <em:description>zh-CN Description</em:description>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="en-GB">
+ <em:name>en-GB Name</em:name>
+ <em:description>en-GB Description</em:description>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="en">
+ <em:name>en Name</em:name>
+ <em:description>en Description</em:description>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="en-CA">
+ <em:name>en-CA Name</em:name>
+ <em:description>en-CA Description</em:description>
+ </Description>
+ </em:localized>
+
+ <!-- Front End MetaData -->
+ <em:name>Fallback Name</em:name>
+ <em:description>Fallback Description</em:description>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug425657/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug425657/install.rdf
new file mode 100644
index 000000000..e4e1b339b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug425657/install.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug425657@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Deutsches Wörterbuch</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug470377_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug470377_1/install.rdf
new file mode 100644
index 000000000..5397e8a87
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug470377_1/install.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_1@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug470377_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug470377_2/install.rdf
new file mode 100644
index 000000000..b1dde7f7a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug470377_2/install.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_2@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug470377_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug470377_3/install.rdf
new file mode 100644
index 000000000..ae483434a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug470377_3/install.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_3@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug470377_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug470377_4/install.rdf
new file mode 100644
index 000000000..97abacc5e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug470377_4/install.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_4@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug470377_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug470377_5/install.rdf
new file mode 100644
index 000000000..bff1104a7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug470377_5/install.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_5@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug521905/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug521905/install.rdf
new file mode 100644
index 000000000..444bdc556
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug521905/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug521905@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Bug 521905</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug567173/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug567173/install.rdf
new file mode 100644
index 000000000..f97bd1302
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug567173/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug567173</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bug 567173</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug567184/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bug567184/bootstrap.js
new file mode 100644
index 000000000..09c083532
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug567184/bootstrap.js
@@ -0,0 +1,7 @@
+function install(data, reason) { }
+
+function startup(data, reason) { }
+
+function shutdown(data, reason) { }
+
+function uninstall(data, reason) {}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug567184/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug567184/install.rdf
new file mode 100644
index 000000000..1e13ceb87
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug567184/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bug567184@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Bug 567184 Test</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>undefined</em:minVersion>
+ <em:maxVersion>undefined</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug587088_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug587088_1/install.rdf
new file mode 100644
index 000000000..83220ce06
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug587088_1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Bug 587088 Test</em:name>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile b/toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile
new file mode 100644
index 000000000..d2277257f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile
@@ -0,0 +1 @@
+Contents of add-on version 1
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile1 b/toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile1
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug587088_1/testfile1
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug587088_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug587088_2/install.rdf
new file mode 100644
index 000000000..ba23ab802
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug587088_2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Bug 587088 Test</em:name>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile b/toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile
new file mode 100644
index 000000000..07afddfa7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile
@@ -0,0 +1 @@
+Contents of add-on version 2
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile2 b/toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile2
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug587088_2/testfile2
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug594058/directory/file1 b/toolkit/mozapps/extensions/test/addons/test_bug594058/directory/file1
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug594058/directory/file1
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug594058/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug594058/install.rdf
new file mode 100644
index 000000000..682831949
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug594058/install.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<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>bug594058@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>bug 594058</em:name>
+ <em:description>stat-based invalidation</em:description>
+ <em:unpack>true</em:unpack>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug595573/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug595573/install.rdf
new file mode 100644
index 000000000..36c03fd00
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug595573/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is compatible with the XPCShell test suite -->
+<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>{2f69dacd-03df-4150-a9f1-e8a7b2748829}</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug655254/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug655254/install.rdf
new file mode 100644
index 000000000..a3fa0d707
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug655254/install.rdf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Test 1</em:name>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug655254_2/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bug655254_2/bootstrap.js
new file mode 100644
index 000000000..b79648e89
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug655254_2/bootstrap.js
@@ -0,0 +1,9 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 1);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug655254_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug655254_2/install.rdf
new file mode 100644
index 000000000..71827885f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug655254_2/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:name>Test 2</em:name>
+ <em:bootstrap>true</em:bootstrap>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug659772/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug659772/install.rdf
new file mode 100644
index 000000000..3b34c63d3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug659772/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is compatible with the XPCShell test suite -->
+<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>addon3@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug675371/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_bug675371/chrome.manifest
new file mode 100644
index 000000000..17d5c99ec
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug675371/chrome.manifest
@@ -0,0 +1 @@
+content bug675371 .
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug675371/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug675371/install.rdf
new file mode 100644
index 000000000..ca2881e5a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug675371/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bug675371@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Bug 675371 Test</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug675371/test.js b/toolkit/mozapps/extensions/test/addons/test_bug675371/test.js
new file mode 100644
index 000000000..ae74c174d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug675371/test.js
@@ -0,0 +1 @@
+active = true;
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug740612_1/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bug740612_1/bootstrap.js
new file mode 100644
index 000000000..6703e7f7d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug740612_1/bootstrap.js
@@ -0,0 +1 @@
+const APP_STARTUP = 1;
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug740612_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug740612_1/install.rdf
new file mode 100644
index 000000000..b2316273f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug740612_1/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bug740612_1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug740612_2/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bug740612_2/bootstrap.js
new file mode 100644
index 000000000..2ad481453
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug740612_2/bootstrap.js
@@ -0,0 +1,23 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const VERSION = "1.0";
+
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug740612_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug740612_2/install.rdf
new file mode 100644
index 000000000..ff4d613ef
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug740612_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bug740612_2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 2</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_bug757663/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bug757663/install.rdf
new file mode 100644
index 000000000..be8d85b1b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug757663/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>bug757663@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_cacheflush1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_cacheflush1/install.rdf
new file mode 100644
index 000000000..5e64b65c1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_cacheflush1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>File Pointer Test</em:name>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_cacheflush2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_cacheflush2/install.rdf
new file mode 100644
index 000000000..7728002ea
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_cacheflush2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>File Pointer Test</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/chrome.manifest
new file mode 100644
index 000000000..4d63b6b06
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/chrome.manifest
@@ -0,0 +1,6 @@
+content test-addon-1 chrome/content
+# comment!
+ locale test-addon-1 en-US locale/en-US
+ # commentaire!
+ locale test-addon-1 fr-FR locale/fr-FR
+overlay chrome://browser/content/browser.xul chrome://test-addon-1/content/overlay.xul
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/install.rdf
new file mode 100644
index 000000000..486be8670
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/chrome.manifest
new file mode 100644
index 000000000..3b0195077
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/chrome.manifest
@@ -0,0 +1,7 @@
+content test-addon-1 chrome/content
+
+ locale test-addon-1 en-US locale/en-US
+ locale test-addon-1 fr-FR locale/fr-FR
+overlay chrome://browser/content/browser.xul chrome://test-addon-1/content/overlay.xul
+binary-component components/something.so
+manifest thisdoesntexist.manifest
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/install.rdf
new file mode 100644
index 000000000..9a9ee4823
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 2</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>true</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/chrome.manifest
new file mode 100644
index 000000000..73190ed8f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/chrome.manifest
@@ -0,0 +1,9 @@
+content test-addon-1 chrome/content
+
+ locale test-addon-1 en-US locale/en-US
+ locale test-addon-1 fr-FR locale/fr-FR
+overlay chrome://browser/content/browser.xul chrome://test-addon-1/content/overlay.xul
+
+ binary-component components/something.so
+
+ manifest jar:inner.jar!/nested.manifest
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/inner.jar b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/inner.jar
new file mode 100644
index 000000000..b4a40052f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/inner.jar
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/install.rdf
new file mode 100644
index 000000000..3a4a709e0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 3</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>true</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/chrome.manifest
new file mode 100644
index 000000000..60d4f01f0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/chrome.manifest
@@ -0,0 +1,6 @@
+content test-addon-1 chrome/content
+
+ locale test-addon-1 en-US locale/en-US
+ locale test-addon-1 fr-FR locale/fr-FR
+overlay chrome://browser/content/browser.xul chrome://test-addon-1/content/overlay.xul
+ manifest components/components.manifest
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/components.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/components.manifest
new file mode 100644
index 000000000..1e0aea440
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/components.manifest
@@ -0,0 +1,2 @@
+binary-component mycomponent.dll
+manifest other/something.manifest
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/other/something.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/other/something.manifest
new file mode 100644
index 000000000..73d58dd66
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/other/something.manifest
@@ -0,0 +1 @@
+binary-component thermalnuclearwar.dll
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/install.rdf
new file mode 100644
index 000000000..463e3f27e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon4@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 4</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>true</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/chrome.manifest
new file mode 100644
index 000000000..b0aa32adc
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/chrome.manifest
@@ -0,0 +1,7 @@
+content test-addon-1 chrome/content
+
+ locale test-addon-1 en-US locale/en-US
+ locale test-addon-1 fr-FR locale/fr-FR
+overlay chrome://browser/content/browser.xul chrome://test-addon-1/content/overlay.xul
+
+ binary-component components/something.so
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/install.rdf
new file mode 100644
index 000000000..7836bced8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon5@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 5</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>false</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/chrome.manifest
new file mode 100644
index 000000000..4ebb75c30
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/chrome.manifest
@@ -0,0 +1 @@
+resource test-addon-1 .
diff --git a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/install.rdf
new file mode 100644
index 000000000..5d94de0ea
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon6@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 6</em:name>
+ <em:description>Test Description</em:description>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_data_directory/install.rdf b/toolkit/mozapps/extensions/test/addons/test_data_directory/install.rdf
new file mode 100644
index 000000000..aebfe3b68
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_data_directory/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>datadirectory1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Data Directory 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf
new file mode 100644
index 000000000..e1f2b5173
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+
+<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>test_db_sanity_1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+ <em:creator>Keyboard Cat</em:creator>
+ <em:homepageURL>http://mozilla.org/</em:homepageURL>
+
+ <em:contributor>Keyboard Cat 2</em:contributor>
+ <em:translator>Keyboard Cat 3</em:translator>
+
+ <em:localized>
+ <Description>
+ <em:locale>en-1</em:locale>
+ <em:name>Test 1 (en-1)</em:name>
+ <em:description>Test Description (en-1)</em:description>
+ <em:creator>Keyboard Cat (en-1)</em:creator>
+ <em:homepageURL>http://mozilla.org/en-1/</em:homepageURL>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description>
+ <em:locale>en-2</em:locale>
+ <em:name>Test 1 (en-2)</em:name>
+ <em:description>Test Description (en-2)</em:description>
+ <em:creator>Keyboard Cat (en-2)</em:creator>
+ <em:homepageURL>http://mozilla.org/en-2/</em:homepageURL>
+ </Description>
+ </em:localized>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>keyboard-cats-awesome-browser@keyboard.cat</em:id>
+ <em:minVersion>3.1415</em:minVersion>
+ <em:maxVersion>3.1415</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:targetPlatform>XPCShell_noarch-spidermonkey</em:targetPlatform>
+ <em:targetPlatform>WINNT_x86</em:targetPlatform>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf
new file mode 100644
index 000000000..da9b067ab
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+
+<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>test_db_sanity_1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:name>Test 1</em:name>
+ <em:description>Test Description!!!</em:description>
+ <em:creator>Keyboard Cat</em:creator>
+ <em:homepageURL>http://mozilla.org/</em:homepageURL>
+
+ <em:contributor>Keyboard Cat 2</em:contributor>
+ <em:translator>Keyboard Cat 3</em:translator>
+ <em:translator>Keyboard Cat 4</em:translator>
+
+ <em:localized>
+ <Description>
+ <em:locale>en-1</em:locale>
+ <em:name>Test 1 (en-1)</em:name>
+ <em:description>Test Description (en-1)</em:description>
+ <em:creator>Keyboard Cat (en-1)</em:creator>
+ <em:homepageURL>http://mozilla.org/en-1/</em:homepageURL>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description>
+ <em:locale>en-3</em:locale>
+ <em:name>Test 1 (en-3)</em:name>
+ <em:description>Test Description (en-3)</em:description>
+ <em:creator>Keyboard Cat (en-3)</em:creator>
+ <em:homepageURL>http://mozilla.org/en-3/</em:homepageURL>
+ </Description>
+ </em:localized>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>keyboard-cats-awesome-browser-3000@keyboard.cat</em:id>
+ <em:minVersion>3.1415</em:minVersion>
+ <em:maxVersion>3.1415</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:targetPlatform>XPCShell_noarch-spidermonkey</em:targetPlatform>
+ <em:targetPlatform>WINNT_i386</em:targetPlatform>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary/dictionaries/ab-CD.dic b/toolkit/mozapps/extensions/test/addons/test_dictionary/dictionaries/ab-CD.dic
new file mode 100644
index 000000000..3feac546d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary/dictionaries/ab-CD.dic
@@ -0,0 +1,2 @@
+1
+test1
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary/install.rdf b/toolkit/mozapps/extensions/test/addons/test_dictionary/install.rdf
new file mode 100644
index 000000000..9e66ab237
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<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>ab-CD@dictionaries.addons.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:type>64</em:type>
+ <em:unpack>true</em:unpack>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Dictionary</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary_2/dictionaries/ab-CD.dic b/toolkit/mozapps/extensions/test/addons/test_dictionary_2/dictionaries/ab-CD.dic
new file mode 100644
index 000000000..b35b9c1a6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary_2/dictionaries/ab-CD.dic
@@ -0,0 +1,2 @@
+1
+test2
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_dictionary_2/install.rdf
new file mode 100644
index 000000000..a74a114fd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>ab-CD@dictionaries.addons.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:unpack>true</em:unpack>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Dictionary</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_dictionary_3/install.rdf
new file mode 100644
index 000000000..c056e87ff
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary_3/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<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>ab-CD@dictionaries.addons.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:type>64</em:type>
+ <em:unpack>true</em:unpack>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Dictionary</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_dictionary_4/install.rdf
new file mode 100644
index 000000000..7470284ba
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary_4/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>ef@dictionaries.addons.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:unpack>true</em:unpack>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Dictionary ef</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_dictionary_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_dictionary_5/install.rdf
new file mode 100644
index 000000000..11eba90d7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_dictionary_5/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<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>gh@dictionaries.addons.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:type>64</em:type>
+ <em:unpack>true</em:unpack>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Dictionary gh</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_distribution1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_distribution1_2/install.rdf
new file mode 100644
index 000000000..8bd5966c9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_distribution1_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Distributed add-ons test</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_experiment1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_experiment1/install.rdf
new file mode 100644
index 000000000..414a36b30
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_experiment1/install.rdf
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+
+<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>experiment1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:type>128</em:type>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Experiment 1</em:name>
+ <em:description>Test Description</em:description>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_filepointer/install.rdf b/toolkit/mozapps/extensions/test/addons/test_filepointer/install.rdf
new file mode 100644
index 000000000..5e64b65c1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_filepointer/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>File Pointer Test</em:name>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_getresource/icon.png b/toolkit/mozapps/extensions/test/addons/test_getresource/icon.png
new file mode 100644
index 000000000..40765b0e2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_getresource/icon.png
@@ -0,0 +1 @@
+Dummy icon file \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/addons/test_getresource/install.rdf b/toolkit/mozapps/extensions/test/addons/test_getresource/install.rdf
new file mode 100644
index 000000000..8d2740dbb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_getresource/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+ <em:bootstrap>true</em:bootstrap>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_getresource/subdir/subfile.txt b/toolkit/mozapps/extensions/test/addons/test_getresource/subdir/subfile.txt
new file mode 100644
index 000000000..a28d18162
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_getresource/subdir/subfile.txt
@@ -0,0 +1 @@
+Dummy file in subdirectory \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/addons/test_install1/icon.png b/toolkit/mozapps/extensions/test/addons/test_install1/icon.png
new file mode 100644
index 000000000..41409edfe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install1/icon.png
@@ -0,0 +1 @@
+Fake icon image
diff --git a/toolkit/mozapps/extensions/test/addons/test_install1/icon64.png b/toolkit/mozapps/extensions/test/addons/test_install1/icon64.png
new file mode 100644
index 000000000..41409edfe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install1/icon64.png
@@ -0,0 +1 @@
+Fake icon image
diff --git a/toolkit/mozapps/extensions/test/addons/test_install1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install1/install.rdf
new file mode 100644
index 000000000..efe3f18ae
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install1/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is compatible with the XPCShell test suite -->
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_install2_1/icon.png b/toolkit/mozapps/extensions/test/addons/test_install2_1/icon.png
new file mode 100644
index 000000000..41409edfe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install2_1/icon.png
@@ -0,0 +1 @@
+Fake icon image
diff --git a/toolkit/mozapps/extensions/test/addons/test_install2_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install2_1/install.rdf
new file mode 100644
index 000000000..116eb7069
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install2_1/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is compatible with the XPCShell test suite -->
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Real Test 2</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_install2_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install2_2/install.rdf
new file mode 100644
index 000000000..7197ea1fb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install2_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is compatible with the XPCShell test suite -->
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Real Test 3</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_install3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install3/install.rdf
new file mode 100644
index 000000000..8e72017ad
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install3/install.rdf
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is incompatible with the XPCShell test suite until
+ a compatibility update check is performed -->
+<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>addon3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Real Test 4</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:updateURL>http://localhost:4444/data/test_install.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/addon4.xpi b/toolkit/mozapps/extensions/test/addons/test_install4/addon4.xpi
new file mode 100644
index 000000000..e57a4f5b6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/addon4.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/addon5.jar b/toolkit/mozapps/extensions/test/addons/test_install4/addon5.jar
new file mode 100644
index 000000000..93fbfbe6e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/addon5.jar
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/addon6.xpi b/toolkit/mozapps/extensions/test/addons/test_install4/addon6.xpi
new file mode 100644
index 000000000..3613dab04
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/addon6.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/addon7.jar b/toolkit/mozapps/extensions/test/addons/test_install4/addon7.jar
new file mode 100644
index 000000000..1af178887
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/addon7.jar
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/badaddon.jar b/toolkit/mozapps/extensions/test/addons/test_install4/badaddon.jar
new file mode 100644
index 000000000..33695b99f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/badaddon.jar
@@ -0,0 +1 @@
+This is corrupt
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/badaddon.xpi b/toolkit/mozapps/extensions/test/addons/test_install4/badaddon.xpi
new file mode 100644
index 000000000..33695b99f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/badaddon.xpi
@@ -0,0 +1 @@
+This is corrupt
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/icon.png b/toolkit/mozapps/extensions/test/addons/test_install4/icon.png
new file mode 100644
index 000000000..57f2c2eb6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/icon.png
@@ -0,0 +1 @@
+This is ignored
diff --git a/toolkit/mozapps/extensions/test/addons/test_install4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install4/install.rdf
new file mode 100644
index 000000000..5e99ae29a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install4/install.rdf
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+
+<!-- A multi-package XPI -->
+<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:type>32</em:type>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_install5/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_install5/chrome.manifest
new file mode 100644
index 000000000..703adf2a7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install5/chrome.manifest
@@ -0,0 +1 @@
+binary-component components/mycomponent.so
diff --git a/toolkit/mozapps/extensions/test/addons/test_install5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install5/install.rdf
new file mode 100644
index 000000000..1f96e4b49
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install5/install.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+
+<!-- An extension that is incompatible with the XPCShell test suite and
+ has binary components, so won't be compatible-by-default. -->
+<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>addon5@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Real Test 5</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>true</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_install6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_install6/install.rdf
new file mode 100644
index 000000000..b1f97c1fd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_install6/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<!-- An extension that has a compatibility override making it incompatible. -->
+<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>addon6@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon Test 6</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_jetpack/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_jetpack/bootstrap.js
new file mode 100644
index 000000000..2449baeb8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_jetpack/bootstrap.js
@@ -0,0 +1,17 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason) {
+ Services.prefs.setIntPref("jetpacktest.installed_version", 1);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("jetpacktest.active_version", 1);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("jetpacktest.active_version", 0);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("jetpacktest.installed_version", 0);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_jetpack/harness-options.json b/toolkit/mozapps/extensions/test/addons/test_jetpack/harness-options.json
new file mode 100644
index 000000000..9e26dfeeb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_jetpack/harness-options.json
@@ -0,0 +1 @@
+{} \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/addons/test_jetpack/install.rdf b/toolkit/mozapps/extensions/test/addons/test_jetpack/install.rdf
new file mode 100644
index 000000000..e88794a60
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_jetpack/install.rdf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<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>jetpack@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test jetpack</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+ <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+ <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_langpack/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_langpack/chrome.manifest
new file mode 100644
index 000000000..16fe819a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_langpack/chrome.manifest
@@ -0,0 +1 @@
+locale test-langpack x-testing locale/x-testing
diff --git a/toolkit/mozapps/extensions/test/addons/test_langpack/install.rdf b/toolkit/mozapps/extensions/test/addons/test_langpack/install.rdf
new file mode 100644
index 000000000..056f6dff5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_langpack/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>langpack-x-testing@tests.mozilla.org</em:id>
+ <em:type>8</em:type>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Language Pack x-testing</em:name>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_locale/install.rdf b/toolkit/mozapps/extensions/test/addons/test_locale/install.rdf
new file mode 100644
index 000000000..d8d027b93
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_locale/install.rdf
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:localized>
+ <Description em:locale="fr-FR">
+ <em:locale/> <!-- Should be ignored and not fail -->
+ <em:name>fr-FR Name</em:name>
+ <em:description>fr-FR Description</em:description>
+ <em:contributor>Fr Contributor 1</em:contributor>
+ <em:contributor>Fr Contributor 2</em:contributor>
+ <em:contributor>Fr Contributor 3</em:contributor>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="de-DE">
+ <em:name>de-DE Name</em:name>
+ </Description>
+ </em:localized>
+
+ <em:localized>
+ <Description em:locale="es-ES">
+ <em:name>es-ES Name</em:name>
+ <em:description>es-ES Description</em:description>
+ </Description>
+ </em:localized>
+
+ <!-- Subsequent definitions for the same locale should be ignored -->
+ <em:localized>
+ <Description em:locale="fr-FR">
+ <em:name>Repeated locale</em:name>
+ </Description>
+ </em:localized>
+
+ <!-- Properties with no listed locale should be ignored -->
+ <em:localized>
+ <Description>
+ <em:name>Missing locale</em:name>
+ </Description>
+ </em:localized>
+
+ <!-- Front End MetaData -->
+ <em:name>Fallback Name</em:name>
+ <em:description>Fallback Description</em:description>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf
new file mode 100644
index 000000000..09655c2a6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon5@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 5</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf
new file mode 100644
index 000000000..75f110d2a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon6@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 6</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate4_6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_migrate4_6/install.rdf
new file mode 100644
index 000000000..5924982f7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate4_6/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon6@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 6</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate4_7/install.rdf b/toolkit/mozapps/extensions/test/addons/test_migrate4_7/install.rdf
new file mode 100644
index 000000000..072751cf2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate4_7/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon7@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 7</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_migrate6/install.rdf
new file mode 100644
index 000000000..ff8280ae3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate6/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon6@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 6</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate7/install.rdf b/toolkit/mozapps/extensions/test/addons/test_migrate7/install.rdf
new file mode 100644
index 000000000..fd1df0e08
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate7/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon7@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 7</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>true</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate8/chrome.manifest b/toolkit/mozapps/extensions/test/addons/test_migrate8/chrome.manifest
new file mode 100644
index 000000000..8570bae82
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate8/chrome.manifest
@@ -0,0 +1,6 @@
+content test-addon-1 chrome/content
+
+ locale test-addon-1 en-US locale/en-US
+ locale test-addon-1 fr-FR locale/fr-FR
+overlay chrome://browser/content/browser.xul chrome://test-addon-1/content/overlay.xul
+binary-component components/something.so
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate8/install.rdf b/toolkit/mozapps/extensions/test/addons/test_migrate8/install.rdf
new file mode 100644
index 000000000..61ed24763
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate8/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon8@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 8</em:name>
+ <em:description>Test Description</em:description>
+ <em:unpack>true</em:unpack>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_migrate9/install.rdf b/toolkit/mozapps/extensions/test/addons/test_migrate9/install.rdf
new file mode 100644
index 000000000..116dd0176
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate9/install.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+
+<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>addon9@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:internalName>theme1/1.0</em:internalName>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Theme 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:skinnable>true</em:skinnable>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_theme/install.rdf b/toolkit/mozapps/extensions/test/addons/test_theme/install.rdf
new file mode 100644
index 000000000..e1a37d0a4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_theme/install.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+
+<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>theme1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:internalName>theme1/1.0</em:internalName>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Theme 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:skinnable>true</em:skinnable>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_theme/preview.png b/toolkit/mozapps/extensions/test/addons/test_theme/preview.png
new file mode 100644
index 000000000..321ce47cf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_theme/preview.png
@@ -0,0 +1 @@
+Fake preview image
diff --git a/toolkit/mozapps/extensions/test/addons/test_undoincompatible/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_undoincompatible/bootstrap.js
new file mode 100644
index 000000000..1666f2972
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_undoincompatible/bootstrap.js
@@ -0,0 +1 @@
+Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
diff --git a/toolkit/mozapps/extensions/test/addons/test_undoincompatible/install.rdf b/toolkit/mozapps/extensions/test/addons/test_undoincompatible/install.rdf
new file mode 100644
index 000000000..b038ebc51
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_undoincompatible/install.rdf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<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>incompatible@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Incompatible Addon</em:name>
+ <em:description>I am incompatible</em:description>
+
+ <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+ <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+ <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_undouninstall1/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_undouninstall1/bootstrap.js
new file mode 100644
index 000000000..1666f2972
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_undouninstall1/bootstrap.js
@@ -0,0 +1 @@
+Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
diff --git a/toolkit/mozapps/extensions/test/addons/test_undouninstall1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_undouninstall1/install.rdf
new file mode 100644
index 000000000..4178fe929
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_undouninstall1/install.rdf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<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>undouninstall1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+ <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+ <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_update/install.rdf b/toolkit/mozapps/extensions/test/addons/test_update/install.rdf
new file mode 100644
index 000000000..801a35a8f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_update/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_update12/install.rdf b/toolkit/mozapps/extensions/test/addons/test_update12/install.rdf
new file mode 100644
index 000000000..3589cb55c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_update12/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon12@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 12</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_update8/install.rdf b/toolkit/mozapps/extensions/test/addons/test_update8/install.rdf
new file mode 100644
index 000000000..43e31af42
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_update8/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon8@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 8</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_updateid2_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_updateid2_2/install.rdf
new file mode 100644
index 000000000..5982b9868
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_updateid2_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:updateURL>http://localhost:4444/data/test_updateid.rdf</em:updateURL>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 2</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_updateid2_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_updateid2_5/install.rdf
new file mode 100644
index 000000000..e923a5289
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_updateid2_5/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>5.0</em:version>
+ <em:updateURL>http://localhost:4444/data/test_updateid.rdf</em:updateURL>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 2</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_updateid3_3/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_updateid3_3/bootstrap.js
new file mode 100644
index 000000000..c28d75925
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_updateid3_3/bootstrap.js
@@ -0,0 +1,21 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 3);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 3);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_updateid3_3/install.rdf b/toolkit/mozapps/extensions/test/addons/test_updateid3_3/install.rdf
new file mode 100644
index 000000000..ffed064cf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_updateid3_3/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<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>addon3@tests.mozilla.org</em:id>
+ <em:version>3.0</em:version>
+ <em:updateURL>http://localhost:4444/data/test_updateid.rdf</em:updateURL>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 3</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_updateid4_4/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_updateid4_4/bootstrap.js
new file mode 100644
index 000000000..6b1753cb2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_updateid4_4/bootstrap.js
@@ -0,0 +1,21 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 4);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 4);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/addons/test_updateid4_4/install.rdf b/toolkit/mozapps/extensions/test/addons/test_updateid4_4/install.rdf
new file mode 100644
index 000000000..b354ac5c1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_updateid4_4/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<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>addon4@tests.mozilla.org</em:id>
+ <em:version>4.0</em:version>
+ <em:updateURL>http://localhost:4444/data/test_updateid.rdf</em:updateURL>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test 4</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_1/install.rdf b/toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_1/install.rdf
new file mode 100644
index 000000000..76e662977
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>upgradeable1x2-3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test min 1 max 2 upgrade to 3</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_2/install.rdf b/toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_2/install.rdf
new file mode 100644
index 000000000..e57672c42
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/upgradeable1x2-3_2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>upgradeable1x2-3@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test min 1 max 2 upgrade to 3</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/Makefile.in b/toolkit/mozapps/extensions/test/browser/Makefile.in
new file mode 100644
index 000000000..6bd692d9b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/Makefile.in
@@ -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/.
+
+ADDONSRC = $(srcdir)/addons
+TESTXPI = $(CURDIR)/$(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)/addons
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+ rm -rf $(TESTXPI)
+ $(NSINSTALL) -D $(TESTXPI)
+ if [ -d $(ADDONSRC) ]; then \
+ $(EXIT_ON_ERROR) \
+ for dir in $(ADDONSRC)/*; do \
+ base=`basename $$dir` ; \
+ (cd $$dir && zip -q $(TESTXPI)/$$base.xpi *) \
+ done \
+ fi
diff --git a/toolkit/mozapps/extensions/test/browser/addon_about.xul b/toolkit/mozapps/extensions/test/browser/addon_about.xul
new file mode 100644
index 000000000..c2b8b935e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addon_about.xul
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="addon-test-about-window">
+ <label value="Oh hai!"/>
+</window>
diff --git a/toolkit/mozapps/extensions/test/browser/addon_prefs.xul b/toolkit/mozapps/extensions/test/browser/addon_prefs.xul
new file mode 100644
index 000000000..85cfe6b2d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addon_prefs.xul
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="addon-test-pref-window">
+ <label value="Oh hai!"/>
+</window>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_1/install.rdf
new file mode 100644
index 000000000..5c164ec07
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon1</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_10/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_10/install.rdf
new file mode 100644
index 000000000..95b6488dd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_10/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon10@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon10</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_2/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_2/install.rdf
new file mode 100644
index 000000000..d02cefac2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon2</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_3/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_3/install.rdf
new file mode 100644
index 000000000..23b4813c6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_3/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon3@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon3</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_4/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_4/install.rdf
new file mode 100644
index 000000000..0150fc3c0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_4/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon4@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon4</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_5/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_5/install.rdf
new file mode 100644
index 000000000..dfcbf0384
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_5/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon5@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon5</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_6/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_6/install.rdf
new file mode 100644
index 000000000..8e1027923
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_6/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon6@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon6</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_7/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_7/install.rdf
new file mode 100644
index 000000000..023f9f05f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_7/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon7@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon7</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_8_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_8_1/install.rdf
new file mode 100644
index 000000000..57855e094
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_8_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon8@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon8</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_9_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_9_1/install.rdf
new file mode 100644
index 000000000..e1d6554f3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug557956_9_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon9@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon9</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_1/install.rdf
new file mode 100644
index 000000000..f5780d5d3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug567127_1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>browser_bug567127 #1</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_2/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_2/install.rdf
new file mode 100644
index 000000000..84c542cf9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug567127_2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>bug567127_2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>browser_bug567127 #2</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_1/install.rdf
new file mode 100644
index 000000000..726ffee8b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Bootstrap upgrade test</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_2/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_2/install.rdf
new file mode 100644
index 000000000..16e4fd0cd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_bug596336_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Bootstrap upgrade test</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop1/install.rdf
new file mode 100644
index 000000000..0a845ed31
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop1/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>dragdrop1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Drag Drop test 1</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop2/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop2/install.rdf
new file mode 100644
index 000000000..03072fdf4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_dragdrop2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>dragdrop2@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Drag Drop test 2</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_experiment1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_experiment1/install.rdf
new file mode 100644
index 000000000..92f20a4ef
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_experiment1/install.rdf
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+
+<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>test-experiment1@experiments.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:type>128</em:type>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Experiment 1</em:name>
+ <em:description>Test Description</em:description>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/bootstrap.js b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/bootstrap.js
new file mode 100644
index 000000000..7871af738
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/bootstrap.js
@@ -0,0 +1,8 @@
+function install (params, aReason) {
+}
+function uninstall (params, aReason) {
+}
+function startup (params, aReason) {
+}
+function shutdown (params, aReason) {
+}
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/install.rdf
new file mode 100644
index 000000000..2bf7c6e2e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<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>inlinesettings1@tests.mozilla.org</em:id>
+ <em:name>Inline Settings (Bootstrap)</em:name>
+ <em:version>1</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul
new file mode 100644
index 000000000..c271c35da
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul
@@ -0,0 +1,20 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting pref="extensions.inlinesettings1.bool" type="bool" title="Bool" checkboxlabel="Check box label"/>
+ <setting pref="extensions.inlinesettings1.boolint" type="boolint" on="1" off="2" title="BoolInt"/>
+ <setting pref="extensions.inlinesettings1.integer" type="integer" title="Integer"/>
+ <setting pref="extensions.inlinesettings1.string" type="string" title="String"/>
+ <setting type="control" title="Menulist">
+ <menulist sizetopopup="always" oncommand="window._testValue = this.value;">
+ <menupopup>
+ <menuitem label="Alpha" value="1" />
+ <menuitem label="Bravo" value="2" />
+ <menuitem label="Charlie" value="3" />
+ </menupopup>
+ </menulist>
+ </setting>
+ <setting pref="extensions.inlinesettings1.color" type="color" title="Color"/>
+ <setting pref="extensions.inlinesettings1.file" type="file" title="File"/>
+ <setting pref="extensions.inlinesettings1.directory" type="directory" title="Directory"/>
+ <setting pref="extensions.inlinesettings1.integer-size" type="integer" title="Integer with size" size="1" />
+</vbox>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/binding.xml b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/binding.xml
new file mode 100644
index 000000000..6ac72a03c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/binding.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<bindings 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="custom"
+ extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
+ <content>
+ <xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label anonid="label" class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
+ </xul:hbox>
+ <xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
+ </xul:vbox>
+ <xul:hbox class="preferences-alignment">
+ <xul:label anonid="input" value="Woah!"/>
+ </xul:hbox>
+ </content>
+ </binding>
+</bindings>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/bootstrap.js b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/bootstrap.js
new file mode 100644
index 000000000..7871af738
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/bootstrap.js
@@ -0,0 +1,8 @@
+function install (params, aReason) {
+}
+function uninstall (params, aReason) {
+}
+function startup (params, aReason) {
+}
+function shutdown (params, aReason) {
+}
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/chrome.manifest b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/chrome.manifest
new file mode 100644
index 000000000..f7132fc46
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/chrome.manifest
@@ -0,0 +1,2 @@
+content inlinesettings ./
+locale inlinesettings en-US ./
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/install.rdf
new file mode 100644
index 000000000..2bf7c6e2e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<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>inlinesettings1@tests.mozilla.org</em:id>
+ <em:name>Inline Settings (Bootstrap)</em:name>
+ <em:version>1</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/options.xul b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/options.xul
new file mode 100644
index 000000000..148fb9856
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/options.xul
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE vbox SYSTEM "chrome://inlinesettings/locale/string.dtd">
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting type="custom" title="&custom.title;" style="background-color: blue; display: -moz-grid-line; -moz-binding: url('chrome://inlinesettings/content/binding.xml#custom');"/>
+</vbox>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/string.dtd b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/string.dtd
new file mode 100644
index 000000000..0b2dcc8fe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/string.dtd
@@ -0,0 +1 @@
+<!ENTITY custom.title "Custom">
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/bootstrap.js b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/bootstrap.js
new file mode 100644
index 000000000..7871af738
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/bootstrap.js
@@ -0,0 +1,8 @@
+function install (params, aReason) {
+}
+function uninstall (params, aReason) {
+}
+function startup (params, aReason) {
+}
+function shutdown (params, aReason) {
+}
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/install.rdf
new file mode 100644
index 000000000..e3a054890
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/install.rdf
@@ -0,0 +1,20 @@
+<?xml version="1.0" ?>
+<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>inlinesettings1@tests.mozilla.org</em:id>
+ <em:name>Inline Settings (Bootstrap)</em:name>
+ <em:version>1</em:version>
+ <em:bootstrap>true</em:bootstrap>
+ <em:optionsType>4</em:optionsType>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/options.xul b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/options.xul
new file mode 100644
index 000000000..095d3bcef
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/options.xul
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting pref="extensions.inlinesettings1.bool" type="bool" title="Bool" checkboxlabel="Check box label"/>
+ <setting pref="extensions.inlinesettings1.boolint" type="boolint" on="1" off="2" title="BoolInt"/>
+ <setting pref="extensions.inlinesettings1.integer" type="integer" title="Integer"/>
+ <setting pref="extensions.inlinesettings1.string" type="string" title="String"/>
+ <setting type="control" title="Menulist">
+ <menulist sizetopopup="always" oncommand="window._testValue = this.value;">
+ <menupopup>
+ <menuitem label="Alpha" value="1" />
+ <menuitem label="Bravo" value="2" />
+ <menuitem label="Charlie" value="3" />
+ </menupopup>
+ </menulist>
+ </setting>
+ <setting pref="extensions.inlinesettings1.color" type="color" title="Color"/>
+ <setting pref="extensions.inlinesettings1.file" type="file" title="File"/>
+ <setting pref="extensions.inlinesettings1.directory" type="directory" title="Directory"/>
+</vbox>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_install1_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_install1_1/install.rdf
new file mode 100644
index 000000000..ba71f4c95
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_install1_1/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+ <em:updateURL>http://example.com/browser/toolkit/mozapps/extensions/test/browser/browser_install.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Install Tests</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2/install.rdf
new file mode 100644
index 000000000..4497c31e2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Install Tests</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_installssl/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_installssl/install.rdf
new file mode 100644
index 000000000..0906bbe91
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_installssl/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<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>sslinstall@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>SSL Install Tests</em:name>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_searching/bootstrap.js b/toolkit/mozapps/extensions/test/browser/addons/browser_searching/bootstrap.js
new file mode 100644
index 000000000..7b86e419a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_searching/bootstrap.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function install(data, reason) {}
+function startup(data, reason) {}
+function shutdown(data, reason) {}
+function uninstall(data, reason) {}
+
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_searching/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_searching/install.rdf
new file mode 100644
index 000000000..f26f9ad80
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_searching/install.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<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>remote1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:type>2</em:type>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>PASS - b - installed</em:name>
+ <em:description>Test sumary - SEARCH SEARCH</em:description>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_select_compatoverrides_1/install.rdf b/toolkit/mozapps/extensions/test/browser/addons/browser_select_compatoverrides_1/install.rdf
new file mode 100644
index 000000000..47a0e373a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_select_compatoverrides_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0.1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Addon1</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/blockNoPlugins.xml b/toolkit/mozapps/extensions/test/browser/blockNoPlugins.xml
new file mode 100644
index 000000000..e4e191b37
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/blockNoPlugins.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310001">
+ <emItems>
+ </emItems>
+ <pluginItems>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/browser/blockPluginHard.xml b/toolkit/mozapps/extensions/test/browser/blockPluginHard.xml
new file mode 100644
index 000000000..24eb5bc6f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/blockPluginHard.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310000">
+ <emItems>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="p9999">
+ <match name="filename" exp="libnptest\.so|nptest\.dll|Test\.plugin" />
+ <versionRange severity="2"></versionRange>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/browser/browser-common.ini b/toolkit/mozapps/extensions/test/browser/browser-common.ini
new file mode 100644
index 000000000..eaab29f75
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser-common.ini
@@ -0,0 +1,78 @@
+[DEFAULT]
+support-files =
+ head.js
+
+[browser_about.js]
+skip-if = os == 'linux' || os == 'win' # bug 632290
+[browser_bug523784.js]
+[browser_bug557943.js]
+[browser_bug562797.js]
+skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+[browser_bug562854.js]
+[browser_bug562890.js]
+[browser_bug562899.js]
+skip-if = buildapp == 'mulet'
+[browser_bug562992.js]
+[browser_bug567127.js]
+[browser_bug567137.js]
+[browser_bug570760.js]
+skip-if = e10s # Bug ?????? - EventUtils.synthesizeKey not e10s friendly
+[browser_bug572561.js]
+[browser_bug577990.js]
+[browser_bug580298.js]
+[browser_bug581076.js]
+[browser_bug586574.js]
+[browser_bug587970.js]
+[browser_bug591465.js]
+[browser_bug591663.js]
+[browser_bug593535.js]
+skip-if = true # Bug 1093190 - Disabled due to leak
+[browser_bug596336.js]
+[browser_bug608316.js]
+[browser_bug610764.js]
+[browser_bug618502.js]
+[browser_bug679604.js]
+[browser_bug714593.js]
+[browser_bug590347.js]
+[browser_debug_button.js]
+[browser_details.js]
+[browser_discovery.js]
+skip-if = e10s # Bug ?????? - test times out on try on all platforms, but works locally for markh!
+[browser_dragdrop.js]
+skip-if = buildapp == 'mulet'
+[browser_experiments.js]
+skip-if = e10s
+[browser_list.js]
+[browser_metadataTimeout.js]
+[browser_searching.js]
+[browser_sorting.js]
+[browser_sorting_plugins.js]
+[browser_plugin_enabled_state_locked.js]
+skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #1760 about:blank]
+[browser_uninstalling.js]
+skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #1760 about:blank]
+[browser_install.js]
+[browser_recentupdates.js]
+[browser_manualupdates.js]
+[browser_globalwarnings.js]
+[browser_globalinformations.js]
+skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+[browser_eula.js]
+skip-if = buildapp == 'mulet'
+[browser_updateid.js]
+skip-if = e10s # Bug ?????? - window leak reported at end of test run.
+[browser_purchase.js]
+skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+[browser_openDialog.js]
+skip-if = e10s
+[browser_types.js]
+skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #1760 about:blank]
+[browser_inlinesettings.js]
+[browser_inlinesettings_custom.js]
+[browser_inlinesettings_info.js]
+[browser_tabsettings.js]
+skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #1760 about:blank]
+[browser_pluginprefs.js]
+skip-if = buildapp == 'mulet'
+[browser_CTP_plugins.js]
+skip-if = buildapp == 'mulet' || e10s # Bug 899347 - no e10s click-to-play support
diff --git a/toolkit/mozapps/extensions/test/browser/browser-window.ini b/toolkit/mozapps/extensions/test/browser/browser-window.ini
new file mode 100644
index 000000000..95494eb3e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser-window.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+install-to-subdir = test-window
+
+[include:browser-common.ini]
diff --git a/toolkit/mozapps/extensions/test/browser/browser.ini b/toolkit/mozapps/extensions/test/browser/browser.ini
new file mode 100644
index 000000000..dc6f86bd6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -0,0 +1,53 @@
+[DEFAULT]
+skip-if = buildapp == 'mulet'
+support-files =
+ addon_about.xul
+ addon_prefs.xul
+ cancelCompatCheck.sjs
+ discovery.html
+ discovery_frame.html
+ discovery_install.html
+ more_options.xul
+ options.xul
+ plugin_test.html
+ redirect.sjs
+ releaseNotes.xhtml
+ blockNoPlugins.xml
+ blockPluginHard.xml
+ browser_bug557956.rdf
+ browser_bug557956_8_2.xpi
+ browser_bug557956_9_2.xpi
+ browser_bug557956.xml
+ browser_bug591465.xml
+ browser_bug593535.xml
+ browser_searching.xml
+ browser_searching_empty.xml
+ browser_select_compatoverrides.xml
+ browser_updatessl.rdf
+ browser_updatessl.rdf^headers^
+ browser_install.rdf
+ browser_install.rdf^headers^
+ browser_install.xml
+ browser_install1_3.xpi
+ browser_eula.xml
+ browser_purchase.xml
+
+[browser_addonrepository_performance.js]
+[browser_bug557956.js]
+skip-if = e10s
+[browser_bug616841.js]
+[browser_cancelCompatCheck.js]
+[browser_checkAddonCompatibility.js]
+[browser_gmpProvider.js]
+[browser_installssl.js]
+[browser_newaddon.js]
+skip-if = e10s
+[browser_select_compatoverrides.js]
+[browser_select_confirm.js]
+[browser_select_selection.js]
+[browser_select_update.js]
+[browser_updatessl.js]
+[browser_task_next_test.js]
+[browser_discovery_install.js]
+
+[include:browser-common.ini]
diff --git a/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js b/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js
new file mode 100644
index 000000000..1b119ca5b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js
@@ -0,0 +1,234 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 gHttpTestRoot = "http://127.0.0.1:8888/" + RELATIVE_DIR + "/";
+let gManagerWindow;
+let gTestPluginId;
+let gPluginBrowser;
+
+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", false);
+ blocklistNotifier.notify(null);
+}
+
+var _originalBlocklistURL = null;
+function setAndUpdateBlocklist(aURL, aCallback) {
+ if (!_originalBlocklistURL) {
+ _originalBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
+ }
+ Services.prefs.setCharPref("extensions.blocklist.url", aURL);
+ updateBlocklist(aCallback);
+}
+
+function resetBlocklist(aCallback) {
+ Services.prefs.setCharPref("extensions.blocklist.url", _originalBlocklistURL);
+}
+
+function test() {
+ waitForExplicitFinish();
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+ Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
+ let pluginTag = getTestPluginTag();
+ pluginTag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
+ open_manager("addons://list/plugin", part1);
+}
+
+function part1(aWindow) {
+ gManagerWindow = aWindow;
+ AddonManager.getAddonsByTypes(["plugin"], part2);
+}
+
+function part2(aPlugins) {
+ for (let plugin of aPlugins) {
+ if (plugin.name == "Test Plug-in") {
+ gTestPluginId = plugin.id;
+ break;
+ }
+ }
+ ok(gTestPluginId, "part2: Test Plug-in should exist");
+ AddonManager.getAddonByID(gTestPluginId, part3);
+}
+
+function part3(aTestPlugin) {
+ let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
+ pluginEl.parentNode.ensureElementIsVisible(pluginEl);
+ let enableButton = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "enable-btn");
+ is_element_hidden(enableButton, "part3: enable button should not be visible");
+ let disableButton = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "enable-btn");
+ is_element_hidden(disableButton, "part3: disable button should not be visible");
+ let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
+ is_element_visible(menu, "part3: state menu should be visible");
+ let askToActivateItem = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "ask-to-activate-menuitem");
+ is(menu.selectedItem, askToActivateItem, "part3: state menu should have 'Ask To Activate' selected");
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gPluginBrowser = gBrowser.selectedBrowser;
+ gPluginBrowser.addEventListener("PluginBindingAttached", part4, true, true);
+ gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
+}
+
+function part4() {
+ let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser);
+ waitForCondition(condition, () => {
+ gPluginBrowser.removeEventListener("PluginBindingAttached", part4);
+ gBrowser.removeCurrentTab();
+
+ let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
+ let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
+ let alwaysActivateItem = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "always-activate-menuitem");
+ menu.selectedItem = alwaysActivateItem;
+ alwaysActivateItem.doCommand();
+ gBrowser.selectedTab = gBrowser.addTab();
+ gPluginBrowser = gBrowser.selectedBrowser;
+ gPluginBrowser.addEventListener("load", part5, true);
+ gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
+ }, "part4: should have a click-to-play notification");
+}
+
+function part5() {
+ let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
+ ok(testPlugin, "part5: should have a plugin element in the page");
+ let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+ let condition = function() objLoadingContent.activated;
+ waitForCondition(condition, part6, "part5: waited too long for plugin to activate");
+}
+
+function part6() {
+ let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
+ ok(testPlugin, "part6: should have a plugin element in the page");
+ let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+ ok(objLoadingContent.activated, "part6: plugin should be activated");
+ gPluginBrowser.removeEventListener("load", part5);
+ gBrowser.removeCurrentTab();
+
+ let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
+ let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
+ let neverActivateItem = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "never-activate-menuitem");
+ menu.selectedItem = neverActivateItem;
+ neverActivateItem.doCommand();
+ gBrowser.selectedTab = gBrowser.addTab();
+ gPluginBrowser = gBrowser.selectedBrowser;
+ gPluginBrowser.addEventListener("PluginBindingAttached", part7, true, true);
+ gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
+}
+
+function part7() {
+ let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser);
+ waitForCondition(condition, () => {
+ let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
+ ok(testPlugin, "part7: should have a plugin element in the page");
+ let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+ ok(!objLoadingContent.activated, "part7: plugin should not be activated");
+
+ gPluginBrowser.removeEventListener("PluginBindingAttached", part7);
+ gBrowser.removeCurrentTab();
+
+ let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
+ let details = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "details-btn");
+ is_element_visible(details, "part7: details link should be visible");
+ EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
+ wait_for_view_load(gManagerWindow, part8);
+ }, "part7: disabled plugins still show a notification");
+}
+
+function part8() {
+ let enableButton = gManagerWindow.document.getElementById("detail-enable-btn");
+ is_element_hidden(enableButton, "part8: detail enable button should be hidden");
+ let disableButton = gManagerWindow.document.getElementById("detail-disable-btn");
+ is_element_hidden(disableButton, "part8: detail disable button should be hidden");
+ let menu = gManagerWindow.document.getElementById("detail-state-menulist");
+ is_element_visible(menu, "part8: detail state menu should be visible");
+ let neverActivateItem = gManagerWindow.document.getElementById("detail-never-activate-menuitem");
+ is(menu.selectedItem, neverActivateItem, "part8: state menu should have 'Never Activate' selected");
+
+ let alwaysActivateItem = gManagerWindow.document.getElementById("detail-always-activate-menuitem");
+ menu.selectedItem = alwaysActivateItem;
+ alwaysActivateItem.doCommand();
+ gBrowser.selectedTab = gBrowser.addTab();
+ gPluginBrowser = gBrowser.selectedBrowser;
+ gPluginBrowser.addEventListener("load", part9, true);
+ gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
+}
+
+function part9() {
+ let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
+ ok(testPlugin, "part9: should have a plugin element in the page");
+ let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+ let condition = function() objLoadingContent.activated;
+ waitForCondition(condition, part10, "part9: waited too long for plugin to activate");
+}
+
+function part10() {
+ let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
+ ok(testPlugin, "part10: should have a plugin element in the page");
+ let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+ ok(objLoadingContent.activated, "part10: plugin should be activated");
+ gPluginBrowser.removeEventListener("load", part9);
+ gBrowser.removeCurrentTab();
+
+ let menu = gManagerWindow.document.getElementById("detail-state-menulist");
+ let askToActivateItem = gManagerWindow.document.getElementById("detail-ask-to-activate-menuitem");
+ menu.selectedItem = askToActivateItem;
+ askToActivateItem.doCommand();
+ gBrowser.selectedTab = gBrowser.addTab();
+ gPluginBrowser = gBrowser.selectedBrowser;
+ gPluginBrowser.addEventListener("PluginBindingAttached", part11, true, true);
+ gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
+}
+
+function part11() {
+ let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser);
+ waitForCondition(condition, () => {
+ gPluginBrowser.removeEventListener("PluginBindingAttached", part11);
+ gBrowser.removeCurrentTab();
+
+ let pluginTag = getTestPluginTag();
+
+ // causes appDisabled to be set
+ setAndUpdateBlocklist(gHttpTestRoot + "blockPluginHard.xml",
+ function() {
+ close_manager(gManagerWindow, function() {
+ open_manager("addons://list/plugin", part12);
+ });
+ });
+ }, "part11: should have a click-to-play notification");
+}
+
+function part12(aWindow) {
+ gManagerWindow = aWindow;
+ let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
+ pluginEl.parentNode.ensureElementIsVisible(pluginEl);
+ let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
+ is(menu.disabled, true, "part12: state menu should be disabled");
+
+ let details = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
+ wait_for_view_load(gManagerWindow, part13);
+}
+
+function part13() {
+ let menu = gManagerWindow.document.getElementById("detail-state-menulist");
+ is(menu.disabled, true, "part13: detail state menu should be disabled");
+
+ setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", function() {
+ run_next_test();
+ });
+}
+
+function end_test() {
+ Services.prefs.clearUserPref("plugins.click_to_play");
+ Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
+ let pluginTag = getTestPluginTag();
+ pluginTag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ resetBlocklist();
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_about.js b/toolkit/mozapps/extensions/test/browser/browser_about.js
new file mode 100644
index 000000000..f781cf146
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_about.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests the default and custom "about" dialogs of add-ons.
+ *
+ * Test for bug 610661 <https://bugzilla.mozilla.org/show_bug.cgi?id=610661>:
+ * Addon object not passed to custom about dialogs.
+ */
+
+var gManagerWindow;
+
+const URI_ABOUT_DEFAULT = "chrome://mozapps/content/extensions/about.xul";
+const URI_ABOUT_CUSTOM = CHROMEROOT + "addon_about.xul";
+
+function test() {
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+ var gProvider = new MockProvider();
+ gProvider.createAddons([{
+ id: "test1@tests.mozilla.org",
+ name: "Test add-on 1",
+ description: "foo"
+ },
+ {
+ id: "test2@tests.mozilla.org",
+ name: "Test add-on 2",
+ description: "bar",
+ aboutURL: URI_ABOUT_CUSTOM
+ }]);
+
+ open_manager("addons://list/extension", function(aManager) {
+ gManagerWindow = aManager;
+
+ test_about_window("Test add-on 1", URI_ABOUT_DEFAULT, function() {
+ test_about_window("Test add-on 2", URI_ABOUT_CUSTOM, function() {
+ close_manager(gManagerWindow, finish);
+ });
+ });
+ });
+}
+
+function test_about_window(aAddonItemName, aExpectedAboutUri, aCallback) {
+ var addonList = gManagerWindow.document.getElementById("addon-list");
+ for (var addonItem of addonList.childNodes) {
+ if (addonItem.hasAttribute("name") &&
+ addonItem.getAttribute("name") === aAddonItemName)
+ break;
+ }
+
+ info("Waiting for about dialog");
+ Services.ww.registerNotification(function TEST_ww_observer(aSubject, aTopic,
+ aData) {
+ if (aTopic == "domwindowclosed") {
+ Services.ww.unregisterNotification(TEST_ww_observer);
+
+ info("About dialog closed, waiting for focus on browser window");
+ waitForFocus(() => executeSoon(aCallback));
+ } else if (aTopic == "domwindowopened") {
+ info("About dialog opened, waiting for focus");
+
+ let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+ waitForFocus(function() {
+ info("Saw about dialog");
+
+ is(win.location,
+ aExpectedAboutUri,
+ "The correct add-on about window should have opened");
+
+ is(win.arguments && win.arguments[0] && win.arguments[0].name,
+ aAddonItemName,
+ "window.arguments[0] should refer to the add-on object");
+
+ executeSoon(() => win.close());
+ }, win);
+ }
+ });
+
+ gManagerWindow.gViewController.doCommand("cmd_showItemAbout",
+ addonItem.mAddon);
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_addonrepository_performance.js b/toolkit/mozapps/extensions/test/browser/browser_addonrepository_performance.js
new file mode 100644
index 000000000..0ba3127cd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_addonrepository_performance.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the metadata request includes startup time measurements
+
+let tmp = {};
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm", tmp);
+let AddonRepository = tmp.AddonRepository;
+
+var gTelemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
+var gManagerWindow;
+var gProvider;
+
+function parseParams(aQuery) {
+ let params = {};
+
+ for (let param of aQuery.split("&")) {
+ let [key, value] = param.split("=");
+ params[key] = value;
+ }
+
+ return params;
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ var gSeenRequest = false;
+
+ gProvider = new MockProvider();
+ gProvider.createAddons([{
+ id: "test1@tests.mozilla.org",
+ name: "Test add-on"
+ }]);
+
+ function observe(aSubject, aTopic, aData) {
+ aSubject.QueryInterface(Ci.nsIChannel);
+ let url = aSubject.URI.QueryInterface(Ci.nsIURL);
+ if (url.filePath != "/extensions-dummy/metadata") {
+ return;
+ }
+ info(url.query);
+
+ // Check if we encountered telemetry errors and turn the tests for which
+ // we don't have valid data into known failures.
+ let snapshot = gTelemetry.getHistogramById("STARTUP_MEASUREMENT_ERRORS")
+ .snapshot();
+
+ let tProcessValid = (snapshot.counts[0] == 0);
+ let tMainValid = tProcessValid && (snapshot.counts[2] == 0);
+ let tFirstPaintValid = tProcessValid && (snapshot.counts[5] == 0);
+ let tSessionRestoredValid = tProcessValid && (snapshot.counts[6] == 0);
+
+ let params = parseParams(url.query);
+
+ is(params.appOS, Services.appinfo.OS, "OS should be correct");
+ is(params.appVersion, Services.appinfo.version, "Version should be correct");
+
+ if (tMainValid) {
+ ok(params.tMain >= 0, "Should be a sensible tMain");
+ } else {
+ todo(false, "An error occurred while recording the startup timestamps, skipping this test");
+ }
+
+ if (tFirstPaintValid) {
+ ok(params.tFirstPaint >= 0, "Should be a sensible tFirstPaint");
+ } else {
+ todo(false, "An error occurred while recording the startup timestamps, skipping this test");
+ }
+
+ if (tSessionRestoredValid) {
+ ok(params.tSessionRestored >= 0, "Should be a sensible tSessionRestored");
+ } else {
+ todo(false, "An error occurred while recording the startup timestamps, skipping this test");
+ }
+
+ gSeenRequest = true;
+ }
+
+ const PREF = "extensions.getAddons.getWithPerformance.url";
+
+ // Watch HTTP requests
+ Services.obs.addObserver(observe, "http-on-modify-request", false);
+ Services.prefs.setCharPref(PREF,
+ "http://127.0.0.1:8888/extensions-dummy/metadata?appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
+
+ registerCleanupFunction(function() {
+ Services.obs.removeObserver(observe, "http-on-modify-request");
+ });
+
+ AddonRepository._beginGetAddons(["test1@tests.mozilla.org"], {
+ searchFailed: function() {
+ ok(gSeenRequest, "Should have seen metadata request");
+ finish();
+ }
+ }, true);
+}
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug523784.js b/toolkit/mozapps/extensions/test/browser/browser_bug523784.js
new file mode 100644
index 000000000..c467e5cc0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug523784.js
@@ -0,0 +1,120 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const URI_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+// This tests that the blocklist dialog still affects soft-blocked add-ons
+// if the user clicks the "Restart Later" button. It also ensures that the
+// "Cancel" button is correctly renamed (to "Restart Later").
+let args = {
+ restart: false,
+ list: [{
+ name: "Bug 523784 softblocked addon",
+ version: "1",
+ icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
+ disable: false,
+ blocked: false,
+ url: 'http://example.com/bug523784_1',
+ }],
+};
+
+function test() {
+ waitForExplicitFinish();
+
+ let windowObserver = function(aSubject, aTopic, aData) {
+ if (aTopic != "domwindowopened")
+ return;
+
+ Services.ww.unregisterNotification(windowObserver);
+
+ let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ win.addEventListener("load", function() {
+ win.removeEventListener("load", arguments.callee, false);
+
+ executeSoon(function() bug523784_test1(win));
+ }, false);
+ };
+ Services.ww.registerNotification(windowObserver);
+
+ args.wrappedJSObject = args;
+ Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
+ "chrome,centerscreen,dialog,titlebar", args);
+}
+
+function bug523784_test1(win) {
+ let bundle = Services.strings.
+ createBundle("chrome://mozapps/locale/update/updates.properties");
+ let cancelButton = win.document.documentElement.getButton("cancel");
+ let moreInfoLink = win.document.getElementById("moreInfo");
+
+ is(cancelButton.getAttribute("label"),
+ bundle.GetStringFromName("restartLaterButton"),
+ "Text should be changed on Cancel button");
+ is(cancelButton.getAttribute("accesskey"),
+ bundle.GetStringFromName("restartLaterButton.accesskey"),
+ "Accesskey should also be changed on Cancel button");
+ is(moreInfoLink.getAttribute("href"),
+ 'http://example.com/bug523784_1',
+ "More Info link should link to a detailed blocklist page.");
+ let windowObserver = function(aSubject, aTopic, aData) {
+ if (aTopic != "domwindowclosed")
+ return;
+
+ Services.ww.unregisterNotification(windowObserver);
+
+ ok(args.list[0].disable, "Should be blocking add-on");
+ ok(!args.restart, "Should not restart browser immediately");
+
+ executeSoon(bug523784_test2);
+ };
+ Services.ww.registerNotification(windowObserver);
+
+ cancelButton.doCommand();
+}
+
+function bug523784_test2(win) {
+ let windowObserver = function(aSubject, aTopic, aData) {
+ if (aTopic != "domwindowopened")
+ return;
+
+ Services.ww.unregisterNotification(windowObserver);
+ let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ win.addEventListener("load", function() {
+ win.removeEventListener("load", arguments.callee, false);
+
+ executeSoon(function(){
+ let moreInfoLink = win.document.getElementById("moreInfo");
+ let cancelButton = win.document.documentElement.getButton("cancel");
+ is(moreInfoLink.getAttribute("href"),
+ Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL"),
+ "More Info link should link to the general blocklist page.");
+ cancelButton.doCommand();
+ executeSoon(finish);
+ })
+ }, false);
+ };
+ Services.ww.registerNotification(windowObserver);
+
+ // Add 2 more addons to the blocked list to check that the more info link
+ // points to the general blocked list page.
+ args.list.push({
+ name: "Bug 523784 softblocked addon 2",
+ version: "2",
+ icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
+ disable: false,
+ blocked: false,
+ url: 'http://example.com/bug523784_2'
+ });
+ args.list.push({
+ name: "Bug 523784 softblocked addon 3",
+ version: "4",
+ icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
+ disable: false,
+ blocked: false,
+ url: 'http://example.com/bug523784_3'
+ });
+
+ args.wrappedJSObject = args;
+ Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
+ "chrome,centerscreen,dialog,titlebar", args);
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557943.js b/toolkit/mozapps/extensions/test/browser/browser_bug557943.js
new file mode 100644
index 000000000..94a8b6f49
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557943.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 557943 - Searching for addons can result in wrong results
+
+var gManagerWindow;
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Microsoft .NET Framework Assistant",
+ description: "",
+ version: "6.66"
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "AwesomeNet Addon",
+ description: ""
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "Dictionnaire MySpell en Francais (réforme 1990)",
+ description: ""
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+
+function perform_search(aQuery, aCallback) {
+ waitForFocus(function() {
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = aQuery;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ var list = gManagerWindow.document.getElementById("search-list");
+ var rows = list.getElementsByTagName("richlistitem");
+ aCallback(rows);
+ });
+ }, gManagerWindow);
+}
+
+
+add_test(function() {
+ perform_search(".net", function(aRows) {
+ is(aRows.length, 1, "Should only get one result");
+ is(aRows[0].mAddon.id, "addon1@tests.mozilla.org", "Should get expected addon as only result");
+ run_next_test();
+ });
+});
+
+add_test(function() {
+ perform_search("réf", function(aRows) {
+ is(aRows.length, 1, "Should only get one result");
+ is(aRows[0].mAddon.id, "addon3@tests.mozilla.org", "Should get expected addon as only result");
+ run_next_test();
+ });
+});
+
+add_test(function() {
+ perform_search("javascript:void()", function(aRows) {
+ is(aRows.length, 0, "Should not get any results");
+ run_next_test();
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
new file mode 100644
index 000000000..919564b45
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
@@ -0,0 +1,518 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test the compatibility dialog that displays during startup when the browser
+// version changes.
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_MIN_PLATFORM_COMPAT = "extensions.minCompatiblePlatformVersion";
+
+Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+// avoid the 'leaked window property' check
+let scope = {};
+Components.utils.import("resource://gre/modules/TelemetrySession.jsm", scope);
+let TelemetrySession = scope.TelemetrySession;
+
+/**
+ * Test add-ons:
+ *
+ * Addon minVersion maxVersion Notes
+ * addon1 0 *
+ * addon2 0 0
+ * addon3 0 0
+ * addon4 1 *
+ * addon5 0 0 Made compatible by update check
+ * addon6 0 0 Made compatible by update check
+ * addon7 0 0 Has a broken update available
+ * addon8 0 0 Has an update available
+ * addon9 0 0 Has an update available
+ */
+
+function test() {
+ requestLongerTimeout(2);
+ waitForExplicitFinish();
+
+ run_next_test();
+}
+
+function end_test() {
+ // Test generates a lot of available installs so just cancel them all
+ AddonManager.getAllInstalls(function(aInstalls) {
+ for (let install of aInstalls)
+ install.cancel();
+
+ finish();
+ });
+}
+
+function install_test_addons(aCallback) {
+ var installs = [];
+
+ // Use a blank update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+
+ let names = ["browser_bug557956_1",
+ "browser_bug557956_2",
+ "browser_bug557956_3",
+ "browser_bug557956_4",
+ "browser_bug557956_5",
+ "browser_bug557956_6",
+ "browser_bug557956_7",
+ "browser_bug557956_8_1",
+ "browser_bug557956_9_1",
+ "browser_bug557956_10"];
+ for (let name of names) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/" + name + ".xpi", function(aInstall) {
+ installs.push(aInstall);
+ }, "application/x-xpinstall");
+ }
+
+ var listener = {
+ installCount: 0,
+
+ onInstallEnded: function() {
+ this.installCount++;
+ if (this.installCount == installs.length) {
+ // Switch to the test update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf");
+
+ executeSoon(aCallback);
+ }
+ }
+ };
+
+ for (let install of installs) {
+ install.addListener(listener);
+ install.install();
+ }
+}
+
+function uninstall_test_addons(aCallback) {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org"],
+ function(aAddons) {
+ for (let addon of aAddons) {
+ if (addon)
+ addon.uninstall();
+ }
+ aCallback();
+ });
+}
+
+// Open the compatibility dialog, with the list of addon IDs
+// that were disabled by this "update"
+function open_compatibility_window(aDisabledAddons, aCallback) {
+ // This will reset the longer timeout multiplier to 2 which will give each
+ // test that calls open_compatibility_window a minimum of 60 seconds to
+ // complete.
+ requestLongerTimeout(2);
+
+ var variant = Cc["@mozilla.org/variant;1"].
+ createInstance(Ci.nsIWritableVariant);
+ variant.setFromVariant(aDisabledAddons);
+
+ // Cannot be modal as we want to interact with it, shouldn't cause problems
+ // with testing though.
+ var features = "chrome,centerscreen,dialog,titlebar";
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ var win = ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
+
+ win.addEventListener("load", function() {
+ win.removeEventListener("load", arguments.callee, false);
+
+ info("Compatibility dialog opened");
+
+ function page_shown(aEvent) {
+ if (aEvent.target.pageid)
+ info("Page " + aEvent.target.pageid + " shown");
+ }
+
+ win.addEventListener("pageshow", page_shown, false);
+ win.addEventListener("unload", function() {
+ win.removeEventListener("unload", arguments.callee, false);
+ win.removeEventListener("pageshow", page_shown, false);
+ info("Compatibility dialog closed");
+ }, false);
+
+ aCallback(win);
+ }, false);
+}
+
+function wait_for_window_close(aWindow, aCallback) {
+ aWindow.addEventListener("unload", function() {
+ aWindow.removeEventListener("unload", arguments.callee, false);
+ aCallback();
+ }, false);
+}
+
+function wait_for_page(aWindow, aPageId, aCallback) {
+ var page = aWindow.document.getElementById(aPageId);
+ page.addEventListener("pageshow", function() {
+ page.removeEventListener("pageshow", arguments.callee, false);
+ executeSoon(function() {
+ aCallback(aWindow);
+ });
+ }, false);
+}
+
+function get_list_names(aList) {
+ var items = [];
+ for (let listItem of aList.childNodes)
+ items.push(listItem.label);
+ items.sort();
+ return items;
+}
+
+function check_telemetry({disabled, metaenabled, metadisabled, upgraded, failed, declined}) {
+ let ping = TelemetrySession.getPayload();
+ // info(JSON.stringify(ping));
+ let am = ping.simpleMeasurements.addonManager;
+ if (disabled !== undefined)
+ is(am.appUpdate_disabled, disabled, disabled + " add-ons disabled by version change");
+ if (metaenabled !== undefined)
+ is(am.appUpdate_metadata_enabled, metaenabled, metaenabled + " add-ons enabled by metadata");
+ if (metadisabled !== undefined)
+ is(am.appUpdate_metadata_disabled, metadisabled, metadisabled + " add-ons disabled by metadata");
+ if (upgraded !== undefined)
+ is(am.appUpdate_upgraded, upgraded, upgraded + " add-ons upgraded");
+ if (failed !== undefined)
+ is(am.appUpdate_upgradeFailed, failed, failed + " upgrades failed");
+ if (declined !== undefined)
+ is(am.appUpdate_upgradeDeclined, declined, declined + " upgrades declined");
+}
+
+add_test(function test_setup() {
+ TelemetrySession.setup().then(run_next_test);
+});
+
+// Tests that the right add-ons show up in the mismatch dialog and updates can
+// be installed
+add_test(function basic_mismatch() {
+ install_test_addons(function() {
+ // These add-ons become disabled
+ var disabledAddonIds = [
+ "addon3@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org"
+ ];
+
+ AddonManager.getAddonsByIDs(["addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a5, a6]) {
+ // Check starting (pre-update) conditions
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(!a6.isCompatible, "addon6 should not be compatible");
+
+ open_compatibility_window(disabledAddonIds, function(aWindow) {
+ var doc = aWindow.document;
+ wait_for_page(aWindow, "mismatch", function(aWindow) {
+ var items = get_list_names(doc.getElementById("mismatch.incompatible"));
+ // Check that compatibility updates from individual add-on update checks were applied.
+ is(items.length, 4, "Should have seen 4 still incompatible items");
+ is(items[0], "Addon3 1.0", "Should have seen addon3 still incompatible");
+ is(items[1], "Addon7 1.0", "Should have seen addon7 still incompatible");
+ is(items[2], "Addon8 1.0", "Should have seen addon8 still incompatible");
+ is(items[3], "Addon9 1.0", "Should have seen addon9 still incompatible");
+
+ // If it wasn't disabled by this run, we don't try to enable it
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(a6.isCompatible, "addon6 should be compatible");
+
+ var button = doc.documentElement.getButton("next");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_page(aWindow, "found", function(aWindow) {
+ ok(doc.getElementById("xpinstallDisabledAlert").hidden,
+ "Install should be allowed");
+
+ var list = doc.getElementById("found.updates");
+ var items = get_list_names(list);
+ is(items.length, 3, "Should have seen 3 updates available");
+ is(items[0], "Addon7 2.0", "Should have seen update for addon7");
+ is(items[1], "Addon8 2.0", "Should have seen update for addon8");
+ is(items[2], "Addon9 2.0", "Should have seen update for addon9");
+
+ ok(!doc.documentElement.getButton("next").disabled,
+ "Next button should be enabled");
+
+ // Uncheck all
+ for (let listItem of list.childNodes)
+ EventUtils.synthesizeMouse(listItem, 2, 2, { }, aWindow);
+
+ ok(doc.documentElement.getButton("next").disabled,
+ "Next button should not be enabled");
+
+ // Check the ones we want to install
+ for (let listItem of list.childNodes) {
+ if (listItem.label != "Addon7 2.0")
+ EventUtils.synthesizeMouse(listItem, 2, 2, { }, aWindow);
+ }
+
+ var button = doc.documentElement.getButton("next");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_page(aWindow, "finished", function(aWindow) {
+ var button = doc.documentElement.getButton("finish");
+ ok(!button.hidden, "Finish button should not be hidden");
+ ok(!button.disabled, "Finish button should not be disabled");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_window_close(aWindow, function() {
+ AddonManager.getAddonsByIDs(["addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org"],
+ function([a8, a9]) {
+ is(a8.version, "2.0", "addon8 should have updated");
+ is(a9.version, "2.0", "addon9 should have updated");
+
+ check_telemetry({disabled: 5, metaenabled: 1, metadisabled: 0,
+ upgraded: 2, failed: 0, declined: 1});
+
+ uninstall_test_addons(run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that the install failures show the install failed page and disabling
+// xpinstall shows the right UI.
+add_test(function failure_page() {
+ install_test_addons(function() {
+ // These add-ons become disabled
+ var disabledAddonIds = [
+ "addon3@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org"
+ ];
+
+ Services.prefs.setBoolPref("xpinstall.enabled", false);
+
+ open_compatibility_window(disabledAddonIds, function(aWindow) {
+ var doc = aWindow.document;
+ wait_for_page(aWindow, "mismatch", function(aWindow) {
+ var items = get_list_names(doc.getElementById("mismatch.incompatible"));
+ is(items.length, 4, "Should have seen 4 still incompatible items");
+ is(items[0], "Addon3 1.0", "Should have seen addon3 still incompatible");
+ is(items[1], "Addon7 1.0", "Should have seen addon7 still incompatible");
+ is(items[2], "Addon8 1.0", "Should have seen addon8 still incompatible");
+ is(items[3], "Addon9 1.0", "Should have seen addon9 still incompatible");
+
+ // Check that compatibility updates were applied.
+ AddonManager.getAddonsByIDs(["addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a5, a6]) {
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(a6.isCompatible, "addon6 should be compatible");
+
+ var button = doc.documentElement.getButton("next");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_page(aWindow, "found", function(aWindow) {
+ ok(!doc.getElementById("xpinstallDisabledAlert").hidden,
+ "Install should not be allowed");
+
+ ok(doc.documentElement.getButton("next").disabled,
+ "Next button should be disabled");
+
+ var checkbox = doc.getElementById("enableXPInstall");
+ EventUtils.synthesizeMouse(checkbox, 2, 2, { }, aWindow);
+
+ ok(!doc.documentElement.getButton("next").disabled,
+ "Next button should be enabled");
+
+ var list = doc.getElementById("found.updates");
+ var items = get_list_names(list);
+ is(items.length, 3, "Should have seen 3 updates available");
+ is(items[0], "Addon7 2.0", "Should have seen update for addon7");
+ is(items[1], "Addon8 2.0", "Should have seen update for addon8");
+ is(items[2], "Addon9 2.0", "Should have seen update for addon9");
+
+ // Unheck the ones we don't want to install
+ for (let listItem of list.childNodes) {
+ if (listItem.label != "Addon7 2.0")
+ EventUtils.synthesizeMouse(listItem, 2, 2, { }, aWindow);
+ }
+
+ var button = doc.documentElement.getButton("next");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_page(aWindow, "installerrors", function(aWindow) {
+ var button = doc.documentElement.getButton("finish");
+ ok(!button.hidden, "Finish button should not be hidden");
+ ok(!button.disabled, "Finish button should not be disabled");
+
+ wait_for_window_close(aWindow, function() {
+ uninstall_test_addons(run_next_test);
+ });
+
+ check_telemetry({disabled: 5, metaenabled: 1, metadisabled: 0,
+ upgraded: 0, failed: 1, declined: 2});
+
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that no add-ons show up in the mismatch dialog when they are all disabled
+add_test(function all_disabled() {
+ install_test_addons(function() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org"],
+ function(aAddons) {
+ for (let addon of aAddons)
+ addon.userDisabled = true;
+
+ open_compatibility_window([], function(aWindow) {
+ // Should close immediately on its own
+ wait_for_window_close(aWindow, function() {
+ uninstall_test_addons(run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Tests that the right UI shows for when no updates are available
+add_test(function no_updates() {
+ install_test_addons(function() {
+ AddonManager.getAddonsByIDs(["addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org"],
+ function(aAddons) {
+ for (let addon of aAddons)
+ addon.uninstall();
+
+ // These add-ons were disabled by the upgrade
+ var inactiveAddonIds = [
+ "addon3@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"
+ ];
+
+ open_compatibility_window(inactiveAddonIds, function(aWindow) {
+ var doc = aWindow.document;
+ wait_for_page(aWindow, "mismatch", function(aWindow) {
+ var items = get_list_names(doc.getElementById("mismatch.incompatible"));
+ is(items.length, 1, "Should have seen 1 still incompatible items");
+ is(items[0], "Addon3 1.0", "Should have seen addon3 still incompatible");
+
+ var button = doc.documentElement.getButton("next");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_page(aWindow, "noupdates", function(aWindow) {
+ var button = doc.documentElement.getButton("finish");
+ ok(!button.hidden, "Finish button should not be hidden");
+ ok(!button.disabled, "Finish button should not be disabled");
+
+ wait_for_window_close(aWindow, function() {
+ uninstall_test_addons(run_next_test);
+ });
+
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that compatibility overrides are retrieved and affect addon
+// compatibility.
+add_test(function overrides_retrieved() {
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, false);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+ is(AddonManager.strictCompatibility, false, "Strict compatibility should be disabled");
+
+ // Use a blank update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+
+ install_test_addons(function() {
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org"],
+ function(aAddons) {
+
+ for (let addon of aAddons) {
+ if (addon.id == "addon10@tests.mozilla.org")
+ is(addon.isCompatible, true, "Addon10 should be compatible before compat overrides are refreshed");
+ else
+ addon.uninstall();
+ }
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, TESTROOT + "browser_bug557956.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ open_compatibility_window([], function(aWindow) {
+ var doc = aWindow.document;
+ wait_for_page(aWindow, "mismatch", function(aWindow) {
+ var items = get_list_names(doc.getElementById("mismatch.incompatible"));
+ is(items.length, 1, "Should have seen 1 incompatible item");
+ is(items[0], "Addon10 1.0", "Should have seen addon10 as incompatible");
+
+ var button = doc.documentElement.getButton("next");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+
+ wait_for_page(aWindow, "noupdates", function(aWindow) {
+ var button = doc.documentElement.getButton("finish");
+ ok(!button.hidden, "Finish button should not be hidden");
+ ok(!button.disabled, "Finish button should not be disabled");
+
+ wait_for_window_close(aWindow, function() {
+ uninstall_test_addons(run_next_test);
+ });
+
+ check_telemetry({disabled: 0, metaenabled: 0, metadisabled: 1,
+ upgraded: 0, failed: 0, declined: 0});
+
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+ });
+ });
+ });
+ });
+ });
+});
+
+add_test(function test_shutdown() {
+ TelemetrySession.shutdown().then(run_next_test);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557956.rdf b/toolkit/mozapps/extensions/test/browser/browser_bug557956.rdf
new file mode 100644
index 000000000..eebf6c0f9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.rdf
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon6@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon7@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://example.com/browser/toolkit/mozapps/extensions/test/browser/browser_bug557956_7_2.xpi</em:updateLink>
+ <em:updateHash>sha1:18674cf7ad76664e0ead6280a43cc0c681180505</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon8@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://example.com/browser/toolkit/mozapps/extensions/test/browser/browser_bug557956_8_2.xpi</em:updateLink>
+ <em:updateHash>sha1:d6240607c4f202226fa291d9b7e537ff2a309616</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon9@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://example.com/browser/toolkit/mozapps/extensions/test/browser/browser_bug557956_9_2.xpi</em:updateLink>
+ <em:updateHash>sha1:b25d1ee94acc734a4a039d31c1620051bbbd5633</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557956.xml b/toolkit/mozapps/extensions/test/browser/browser_bug557956.xml
new file mode 100644
index 000000000..6184e5214
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <addon_compatibility hosted="false">
+ <guid>addon10@tests.mozilla.org</guid>
+ <name>Addon10</name>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>1.0</min_version>
+ <max_version>2.0</max_version>
+ <compatible_applications>
+ <application>
+ <min_version>0.1</min_version>
+ <max_version>999.0</max_version>
+ <appID>toolkit@mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557956_8_2.xpi b/toolkit/mozapps/extensions/test/browser/browser_bug557956_8_2.xpi
new file mode 100644
index 000000000..9476a8907
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956_8_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557956_9_2.xpi b/toolkit/mozapps/extensions/test/browser/browser_bug557956_9_2.xpi
new file mode 100644
index 000000000..df7673672
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956_9_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562797.js b/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
new file mode 100644
index 000000000..6560e9a2c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
@@ -0,0 +1,965 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests that history navigation works for the add-ons manager.
+ */
+
+const MAIN_URL = "https://example.com/" + RELATIVE_DIR + "discovery.html";
+const SECOND_URL = "https://example.com/" + RELATIVE_DIR + "releaseNotes.xhtml";
+
+var gLoadCompleteCallback = null;
+
+var gProgressListener = {
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+ // Only care about the network stop status events
+ if (!(aStateFlags & (Ci.nsIWebProgressListener.STATE_IS_NETWORK)) ||
+ !(aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP)))
+ return;
+
+ if (gLoadCompleteCallback)
+ executeSoon(gLoadCompleteCallback);
+ gLoadCompleteCallback = null;
+ },
+
+ onLocationChange: function() { },
+ onSecurityChange: function() { },
+ onProgressChange: function() { },
+ onStatusChange: function() { },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference]),
+};
+
+function waitForLoad(aManager, aCallback) {
+ var browser = aManager.document.getElementById("discover-browser");
+ browser.addProgressListener(gProgressListener);
+
+ gLoadCompleteCallback = function() {
+ browser.removeProgressListener(gProgressListener);
+ aCallback();
+ };
+}
+
+function clickLink(aManager, aId, aCallback) {
+ waitForLoad(aManager, aCallback);
+
+ var browser = aManager.document.getElementById("discover-browser");
+
+ var link = browser.contentDocument.getElementById(aId);
+ EventUtils.sendMouseEvent({type: "click"}, link);
+}
+
+function test() {
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+ Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL);
+
+ var gProvider = new MockProvider();
+ gProvider.createAddons([{
+ id: "test1@tests.mozilla.org",
+ name: "Test add-on 1",
+ description: "foo"
+ },
+ {
+ id: "test2@tests.mozilla.org",
+ name: "Test add-on 2",
+ description: "bar"
+ },
+ {
+ id: "test3@tests.mozilla.org",
+ name: "Test add-on 3",
+ type: "theme",
+ description: "bar"
+ }]);
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+function go_back(aManager) {
+ if (gUseInContentUI) {
+ gBrowser.goBack();
+ } else {
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("back-btn"),
+ { }, aManager);
+ }
+}
+
+function go_back_backspace(aManager) {
+ EventUtils.synthesizeKey("VK_BACK_SPACE",{});
+}
+
+function go_forward_backspace(aManager) {
+ EventUtils.synthesizeKey("VK_BACK_SPACE",{shiftKey: true});
+}
+
+function go_forward(aManager) {
+ if (gUseInContentUI) {
+ gBrowser.goForward();
+ } else {
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("forward-btn"),
+ { }, aManager);
+ }
+}
+
+function check_state(aManager, canGoBack, canGoForward) {
+ var doc = aManager.document;
+
+ if (gUseInContentUI) {
+ is(gBrowser.canGoBack, canGoBack, "canGoBack should be correct");
+ is(gBrowser.canGoForward, canGoForward, "canGoForward should be correct");
+ }
+
+ if (!is_hidden(doc.getElementById("back-btn"))) {
+ is(!doc.getElementById("back-btn").disabled, canGoBack, "Back button should have the right state");
+ is(!doc.getElementById("forward-btn").disabled, canGoForward, "Forward button should have the right state");
+ }
+}
+
+function is_in_list(aManager, view, canGoBack, canGoForward) {
+ var doc = aManager.document;
+
+ is(doc.getElementById("categories").selectedItem.value, view, "Should be on the right category");
+ is(doc.getElementById("view-port").selectedPanel.id, "list-view", "Should be on the right view");
+
+ check_state(aManager, canGoBack, canGoForward);
+}
+
+function is_in_search(aManager, query, canGoBack, canGoForward) {
+ var doc = aManager.document;
+
+ is(doc.getElementById("categories").selectedItem.value, "addons://search/", "Should be on the right category");
+ is(doc.getElementById("view-port").selectedPanel.id, "search-view", "Should be on the right view");
+ is(doc.getElementById("header-search").value, query, "Should have used the right query");
+
+ check_state(aManager, canGoBack, canGoForward);
+}
+
+function is_in_detail(aManager, view, canGoBack, canGoForward) {
+ var doc = aManager.document;
+
+ is(doc.getElementById("categories").selectedItem.value, view, "Should be on the right category");
+ is(doc.getElementById("view-port").selectedPanel.id, "detail-view", "Should be on the right view");
+
+ check_state(aManager, canGoBack, canGoForward);
+}
+
+function is_in_discovery(aManager, url, canGoBack, canGoForward) {
+ var browser = aManager.document.getElementById("discover-browser");
+
+ is(aManager.document.getElementById("discover-view").selectedPanel, browser,
+ "Browser should be visible");
+
+ var spec = browser.currentURI.spec;
+ var pos = spec.indexOf("#");
+ if (pos != -1)
+ spec = spec.substring(0, pos);
+
+ is(spec, url, "Should have loaded the right url");
+
+ check_state(aManager, canGoBack, canGoForward);
+}
+
+function double_click_addon_element(aManager, aId) {
+ var addon = get_addon_element(aManager, aId);
+ addon.parentNode.ensureElementIsVisible(addon);
+ EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 1 }, aManager);
+ EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 2 }, aManager);
+}
+
+// Tests simple forward and back navigation and that the right heading and
+// category is selected
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ go_forward(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 4");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 5");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ double_click_addon_element(aManager, "test1@tests.mozilla.org");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 6");
+ is_in_detail(aManager, "addons://list/extension", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 7");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that browsing to the add-ons manager from a website and going back works
+// Only relevant for in-content UI
+add_test(function() {
+ if (!gUseInContentUI) {
+ run_next_test();
+ return;
+ }
+
+ info("Part 1");
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI("http://example.com/");
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "http://example.com/")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+ //Must let the load complete for it to go into the session history
+ executeSoon(function() {
+ info("Part 2");
+ ok(!gBrowser.canGoBack, "Should not be able to go back");
+ ok(!gBrowser.canGoForward, "Should not be able to go forward");
+
+ gBrowser.loadURI("about:addons");
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "about:addons")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, true);
+
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/extension", true, false);
+
+ executeSoon(() => go_back(aManager));
+ gBrowser.addEventListener("pageshow", function() {
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+ info("Part 4");
+ executeSoon(() => executeSoon(function () {
+ is(gBrowser.currentURI.spec, "http://example.com/", "Should be showing the webpage");
+ ok(!gBrowser.canGoBack, "Should not be able to go back");
+ ok(gBrowser.canGoForward, "Should be able to go forward");
+
+ go_forward(aManager);
+ gBrowser.addEventListener("pageshow", function() {
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 5");
+ is_in_list(aManager, "addons://list/extension", true, false);
+
+ close_manager(aManager, run_next_test);
+ });
+ }, false);
+ }));
+ }, false);
+ });
+ }, true);
+ });
+ }, false);
+});
+
+// Tests simple forward and back navigation and that the right heading and
+// category is selected -- Keyboard navigation [Bug 565359]
+// Only add the test if the backspace key navigates back and addon-manager
+// loaded in a tab
+add_test(function() {
+
+ if (!gUseInContentUI || (Services.prefs.getIntPref("browser.backspace_action") != 0)) {
+ run_next_test();
+ return;
+ }
+
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back_backspace(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ go_forward_backspace(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 4");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back_backspace(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 5");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ double_click_addon_element(aManager, "test1@tests.mozilla.org");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 6");
+ is_in_detail(aManager, "addons://list/extension", true, false);
+
+ go_back_backspace(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 7");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+
+// Tests that opening a custom first view only stores a single history entry
+add_test(function() {
+ open_manager("addons://list/plugin", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/plugin", false, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-extension"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/extension", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/plugin", false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+});
+
+
+// Tests that opening a view while the manager is already open adds a new
+// history entry
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ aManager.loadView("addons://list/plugin");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ go_forward(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 4");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Tests than navigating to a website and then going back returns to the
+// previous view
+// Only relevant for in-content UI
+add_test(function() {
+ if (!gUseInContentUI) {
+ run_next_test();
+ return;
+ }
+
+ open_manager("addons://list/plugin", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/plugin", false, false);
+
+ gBrowser.loadURI("http://example.com/");
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "http://example.com/")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+ info("Part 2");
+
+ executeSoon(function() {
+ ok(gBrowser.canGoBack, "Should be able to go back");
+ ok(!gBrowser.canGoForward, "Should not be able to go forward");
+
+ go_back(aManager);
+
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "about:addons")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/plugin", false, true);
+
+ executeSoon(() => go_forward(aManager));
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "http://example.com/")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+ info("Part 4");
+
+ executeSoon(function() {
+ ok(gBrowser.canGoBack, "Should be able to go back");
+ ok(!gBrowser.canGoForward, "Should not be able to go forward");
+
+ go_back(aManager);
+
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "about:addons")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 5");
+ is_in_list(aManager, "addons://list/plugin", false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ }, false);
+ });
+ }, false);
+ });
+ }, false);
+ });
+ }, false);
+ });
+});
+
+// Tests that going back to search results works
+add_test(function() {
+ // Before we open the add-ons manager, we should make sure that no filter
+ // has been set. If one is set, we remove it.
+ // This is for the check below, from bug 611459.
+ let store = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
+ store.removeValue("about:addons", "search-filter-radiogroup", "value");
+
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ var search = aManager.document.getElementById("header-search");
+ search.focus();
+ search.value = "bar";
+ EventUtils.synthesizeKey("VK_RETURN", {}, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ // Remote search is meant to be checked by default (bug 611459), so we
+ // confirm that and then switch to a local search.
+ var localFilter = aManager.document.getElementById("search-filter-local");
+ var remoteFilter = aManager.document.getElementById("search-filter-remote");
+
+ is(remoteFilter.selected, true, "Remote filter should be set by default");
+
+ var list = aManager.document.getElementById("search-list");
+ list.ensureElementIsVisible(localFilter);
+ EventUtils.synthesizeMouseAtCenter(localFilter, { }, aManager);
+
+ is(localFilter.selected, true, "Should have changed to local filter");
+
+ // Now we continue with the normal test.
+
+ info("Part 2");
+ is_in_search(aManager, "bar", true, false);
+ check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
+
+ double_click_addon_element(aManager, "test2@tests.mozilla.org");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 3");
+ is_in_detail(aManager, "addons://search/", true, false);
+
+ go_back(aManager);
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 4");
+ is_in_search(aManager, "bar", true, true);
+ check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
+
+ go_forward(aManager);
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 5");
+ is_in_detail(aManager, "addons://search/", true, false);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that going back from a webpage to a detail view loaded from a search
+// result works
+// Only relevant for in-content UI
+add_test(function() {
+ if (!gUseInContentUI) {
+ run_next_test();
+ return;
+ }
+
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ var search = aManager.document.getElementById("header-search");
+ search.focus();
+ search.value = "bar";
+ EventUtils.synthesizeKey("VK_RETURN", {});
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_search(aManager, "bar", true, false);
+ check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
+
+ double_click_addon_element(aManager, "test2@tests.mozilla.org");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 3");
+ is_in_detail(aManager, "addons://search/", true, false);
+
+ gBrowser.loadURI("http://example.com/");
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "http://example.com/")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+ info("Part 4");
+ executeSoon(function() {
+ ok(gBrowser.canGoBack, "Should be able to go back");
+ ok(!gBrowser.canGoForward, "Should not be able to go forward");
+
+ go_back(aManager);
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "about:addons")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 5");
+ is_in_detail(aManager, "addons://search/", true, true);
+
+ go_back(aManager);
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 6");
+ is_in_search(aManager, "bar", true, true);
+ check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ }, false);
+ });
+ }, false);
+ });
+ });
+ });
+});
+
+// Tests that refreshing a list view does not affect the history
+// Only relevant for in-content UI
+add_test(function() {
+ if (!gUseInContentUI) {
+ run_next_test();
+ return;
+ }
+
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ gBrowser.reload();
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "about:addons")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 3");
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back(aManager);
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 4");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ }, false);
+ });
+ });
+});
+
+// Tests that refreshing a detail view does not affect the history
+// Only relevant for in-content UI
+add_test(function() {
+ if (!gUseInContentUI) {
+ run_next_test();
+ return;
+ }
+
+ open_manager(null, function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ double_click_addon_element(aManager, "test1@tests.mozilla.org");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_detail(aManager, "addons://list/extension", true, false);
+
+ gBrowser.reload();
+ gBrowser.addEventListener("pageshow", function(event) {
+ if (event.target.location != "about:addons")
+ return;
+ gBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+ wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
+ info("Part 3");
+ is_in_detail(aManager, "addons://list/extension", true, false);
+
+ go_back(aManager);
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 4");
+ is_in_list(aManager, "addons://list/extension", false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ }, false);
+ });
+ });
+});
+
+// Tests that removing an extension from the detail view goes back and doesn't
+// allow you to go forward again.
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension", false, false);
+
+ double_click_addon_element(aManager, "test1@tests.mozilla.org");
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_detail(aManager, "addons://list/extension", true, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("detail-uninstall-btn"),
+ { }, aManager);
+
+ wait_for_view_load(aManager, function() {
+ if (gUseInContentUI) {
+ // TODO until bug 590661 is fixed the back button will be enabled
+ // when displaying in content
+ is_in_list(aManager, "addons://list/extension", true, false);
+ } else {
+ is_in_list(aManager, "addons://list/extension", false, false);
+ }
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+});
+
+// Tests that the back and forward buttons only show up for windowed mode
+add_test(function() {
+ open_manager(null, function(aManager) {
+ var doc = aManager.document;
+
+ if (gUseInContentUI) {
+ var btn = document.getElementById("back-button");
+ if (!btn || is_hidden(btn)) {
+ is_element_visible(doc.getElementById("back-btn"), "Back button should not be hidden");
+ is_element_visible(doc.getElementById("forward-btn"), "Forward button should not be hidden");
+ } else {
+ is_element_hidden(doc.getElementById("back-btn"), "Back button should be hidden");
+ is_element_hidden(doc.getElementById("forward-btn"), "Forward button should be hidden");
+ }
+ } else {
+ is_element_visible(doc.getElementById("back-btn"), "Back button should not be hidden");
+ is_element_visible(doc.getElementById("forward-btn"), "Forward button should not be hidden");
+ }
+
+ close_manager(aManager, run_next_test);
+ });
+});
+
+// Tests that opening the manager opens the last view
+add_test(function() {
+ open_manager("addons://list/plugin", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/plugin", false, false);
+
+ close_manager(aManager, function() {
+ open_manager(null, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/plugin", false, false);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+});
+
+// Tests that navigating the discovery page works when that was the first view
+add_test(function() {
+ open_manager("addons://discover/", function(aManager) {
+ info("1");
+ is_in_discovery(aManager, MAIN_URL, false, false);
+
+ clickLink(aManager, "link-good", function() {
+ info("2");
+ is_in_discovery(aManager, SECOND_URL, true, false);
+
+ waitForLoad(aManager, function() {
+ info("3");
+ is_in_discovery(aManager, MAIN_URL, false, true);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, SECOND_URL, true, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_discovery(aManager, SECOND_URL, true, true);
+
+ go_back(aManager);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, MAIN_URL, false, true);
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ });
+ });
+
+ go_forward(aManager);
+ });
+
+ go_back(aManager);
+ });
+ });
+});
+
+// Tests that navigating the discovery page works when that was the second view
+add_test(function() {
+ open_manager("addons://list/plugin", function(aManager) {
+ is_in_list(aManager, "addons://list/plugin", false, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-discover"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_discovery(aManager, MAIN_URL, true, false);
+
+ clickLink(aManager, "link-good", function() {
+ is_in_discovery(aManager, SECOND_URL, true, false);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, MAIN_URL, true, true);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, SECOND_URL, true, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_list(aManager, "addons://list/plugin", true, false);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_discovery(aManager, SECOND_URL, true, true);
+
+ go_back(aManager);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, MAIN_URL, true, true);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_list(aManager, "addons://list/plugin", false, true);
+
+ go_forward(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_discovery(aManager, MAIN_URL, true, true);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, SECOND_URL, true, true);
+
+ close_manager(aManager, run_next_test);
+ });
+
+ go_forward(aManager);
+ });
+ });
+ });
+ });
+ });
+ });
+
+ go_forward(aManager);
+ });
+
+ go_back(aManager);
+ });
+ });
+ });
+});
+
+// Tests that when displaying in-content and opened in the background the back
+// and forward buttons still appear when switching tabs
+add_test(function() {
+ if (!gUseInContentUI) {
+ run_next_test();
+ return;
+ }
+
+ var tab = gBrowser.addTab("about:addons");
+ var browser = gBrowser.getBrowserForTab(tab);
+
+ browser.addEventListener("pageshow", function(event) {
+ if (event.target.location.href != "about:addons")
+ return;
+ browser.removeEventListener("pageshow", arguments.callee, true);
+
+ wait_for_manager_load(browser.contentWindow.wrappedJSObject, function() {
+ wait_for_view_load(browser.contentWindow.wrappedJSObject, function(aManager) {
+ gBrowser.selectedTab = tab;
+
+ var doc = aManager.document;
+ var btn = document.getElementById("back-button");
+ if (!btn || is_hidden(btn)) {
+ is_element_visible(doc.getElementById("back-btn"), "Back button should not be hidden");
+ is_element_visible(doc.getElementById("forward-btn"), "Forward button should not be hidden");
+ } else {
+ is_element_hidden(doc.getElementById("back-btn"), "Back button should be hidden");
+ is_element_hidden(doc.getElementById("forward-btn"), "Forward button should be hidden");
+ }
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+ }, true);
+});
+
+// Tests that refreshing the disicovery pane integrates properly with history
+add_test(function() {
+ open_manager("addons://list/plugin", function(aManager) {
+ is_in_list(aManager, "addons://list/plugin", false, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-discover"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_discovery(aManager, MAIN_URL, true, false);
+
+ clickLink(aManager, "link-good", function() {
+ is_in_discovery(aManager, SECOND_URL, true, false);
+
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-discover"), { }, aManager);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, MAIN_URL, true, false);
+
+ go_back(aManager);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, SECOND_URL, true, true);
+
+ go_back(aManager);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, MAIN_URL, true, true);
+
+ go_back(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_list(aManager, "addons://list/plugin", false, true);
+
+ go_forward(aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ is_in_discovery(aManager, MAIN_URL, true, true);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, SECOND_URL, true, true);
+
+ waitForLoad(aManager, function() {
+ is_in_discovery(aManager, MAIN_URL, true, false);
+
+ close_manager(aManager, run_next_test);
+ });
+ go_forward(aManager);
+ });
+
+ go_forward(aManager);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562854.js b/toolkit/mozapps/extensions/test/browser/browser_bug562854.js
new file mode 100644
index 000000000..4c06cebb7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562854.js
@@ -0,0 +1,129 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests that double-click does not go to detail view if the target is a link or button.
+ */
+
+function test() {
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+ var gProvider = new MockProvider();
+ gProvider.createAddons([{
+ id: "test1@tests.mozilla.org",
+ name: "Test add-on 1",
+ description: "foo",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }]);
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+function is_in_list(aManager, view) {
+ var doc = aManager.document;
+
+ is(doc.getElementById("categories").selectedItem.value, view, "Should be on the right category");
+ is(doc.getElementById("view-port").selectedPanel.id, "list-view", "Should be on the right view");
+}
+
+function is_in_detail(aManager, view) {
+ var doc = aManager.document;
+
+ is(doc.getElementById("categories").selectedItem.value, view, "Should be on the right category");
+ is(doc.getElementById("view-port").selectedPanel.id, "detail-view", "Should be on the right view");
+}
+
+// Check that double-click does something.
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension");
+
+ var addon = get_addon_element(aManager, "test1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+ EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 1 }, aManager);
+ EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 2 }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_detail(aManager, "addons://list/extension");
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+});
+
+// Check that double-click does nothing when over the disable button.
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension");
+
+ var addon = get_addon_element(aManager, "test1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+ EventUtils.synthesizeMouseAtCenter(
+ aManager.document.getAnonymousElementByAttribute(addon, "anonid", "disable-btn"),
+ { clickCount: 1 },
+ aManager
+ );
+ // The disable button is replaced by the enable button when clicked on.
+ EventUtils.synthesizeMouseAtCenter(
+ aManager.document.getAnonymousElementByAttribute(addon, "anonid", "enable-btn"),
+ { clickCount: 2 },
+ aManager
+ );
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/extension");
+
+ close_manager(aManager, run_next_test);
+ });
+ });
+});
+
+// Check that double-click does nothing when over the undo button.
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Part 1");
+ is_in_list(aManager, "addons://list/extension");
+
+ var addon = get_addon_element(aManager, "test1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+ EventUtils.synthesizeMouseAtCenter(
+ aManager.document.getAnonymousElementByAttribute(addon, "anonid", "remove-btn"),
+ { clickCount: 1 },
+ aManager
+ );
+
+ // The undo button is removed when clicked on.
+ // We need to wait for the UI to catch up.
+ setTimeout(function() {
+ var target = aManager.document.getAnonymousElementByAttribute(addon, "anonid", "undo-btn");
+ var rect = target.getBoundingClientRect();
+ var addonRect = addon.getBoundingClientRect();
+
+ EventUtils.synthesizeMouse(target, rect.width / 2, rect.height / 2, { clickCount: 1 }, aManager);
+ EventUtils.synthesizeMouse(addon,
+ rect.left - addonRect.left + rect.width / 2,
+ rect.top - addonRect.top + rect.height / 2,
+ { clickCount: 2 },
+ aManager
+ );
+
+ wait_for_view_load(aManager, function(aManager) {
+ info("Part 2");
+ is_in_list(aManager, "addons://list/extension");
+
+ close_manager(aManager, run_next_test);
+ });
+ }, 0);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562890.js b/toolkit/mozapps/extensions/test/browser/browser_bug562890.js
new file mode 100644
index 000000000..375cb9ef0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562890.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests the Preferences button for addons in list view
+ */
+
+function test() {
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+ var addonPrefsURI = CHROMEROOT + "addon_prefs.xul";
+
+ var gProvider = new MockProvider();
+ gProvider.createAddons([{
+ id: "test1@tests.mozilla.org",
+ name: "Test add-on 1",
+ description: "foo"
+ },
+ {
+ id: "test2@tests.mozilla.org",
+ name: "Test add-on 2",
+ description: "bar",
+ optionsURL: addonPrefsURI
+ }]);
+
+ open_manager("addons://list/extension", function(aManager) {
+ var addonList = aManager.document.getElementById("addon-list");
+ for (var addonItem of addonList.childNodes) {
+ if (addonItem.hasAttribute("name") &&
+ addonItem.getAttribute("name") == "Test add-on 1")
+ break;
+ }
+ var prefsBtn = aManager.document.getAnonymousElementByAttribute(addonItem,
+ "anonid",
+ "preferences-btn");
+ is(prefsBtn.hidden, true, "Prefs button should be hidden for addon with no optionsURL set")
+
+ for (addonItem of addonList.childNodes) {
+ if (addonItem.hasAttribute("name") &&
+ addonItem.getAttribute("name") == "Test add-on 2")
+ break;
+ }
+ prefsBtn = aManager.document.getAnonymousElementByAttribute(addonItem,
+ "anonid",
+ "preferences-btn");
+ is(prefsBtn.hidden, false, "Prefs button should be shown for addon with a optionsURL set")
+
+ Services.ww.registerNotification(function TEST_ww_observer(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowclosed") {
+ Services.ww.unregisterNotification(TEST_ww_observer);
+ // Give the preference window a chance to finish closing before closing
+ // the add-ons manager.
+ executeSoon(function() {
+ close_manager(aManager, finish);
+ });
+ } else if (aTopic == "domwindowopened") {
+ let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+ win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+ win.addEventListener("load", function TEST_ww_onLoad() {
+ if (win.location != addonPrefsURI)
+ return;
+
+ win.removeEventListener("load", TEST_ww_onLoad, false);
+ is(win.location, addonPrefsURI,
+ "The correct addon pref window should have opened");
+ win.close();
+ }, false);
+ }
+ });
+
+ addonList.ensureElementIsVisible(addonItem);
+ EventUtils.synthesizeMouseAtCenter(prefsBtn, { }, aManager);
+ });
+
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562899.js b/toolkit/mozapps/extensions/test/browser/browser_bug562899.js
new file mode 100644
index 000000000..8bfabf004
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562899.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Simulates quickly switching between different list views to verify that only
+// the last selected is displayed
+
+let tempScope = {};
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
+let LightweightThemeManager = tempScope.LightweightThemeManager;
+
+const xpi = "browser/toolkit/mozapps/extensions/test/browser/browser_installssl.xpi";
+
+var gManagerWindow;
+var gCategoryUtilities;
+
+function test() {
+ waitForExplicitFinish();
+
+ // Add a lightweight theme so at least one theme exists
+ LightweightThemeManager.currentTheme = {
+ id: "test",
+ name: "Test lightweight theme",
+ headerURL: "http://example.com/header.png"
+ };
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ LightweightThemeManager.forgetUsedTheme("test");
+ finish();
+ });
+}
+
+// Tests that loading a second view before the first has not finished loading
+// does not merge the results
+add_test(function() {
+ var themeCount = null;
+ var pluginCount = null;
+ var themeItem = gCategoryUtilities.get("theme");
+ var pluginItem = gCategoryUtilities.get("plugin");
+ var list = gManagerWindow.document.getElementById("addon-list");
+
+ gCategoryUtilities.open(themeItem, function() {
+ themeCount = list.childNodes.length;
+ ok(themeCount > 0, "Test is useless if there are no themes");
+
+ gCategoryUtilities.open(pluginItem, function() {
+ pluginCount = list.childNodes.length;
+ ok(pluginCount > 0, "Test is useless if there are no plugins");
+
+ gCategoryUtilities.open(themeItem);
+
+ gCategoryUtilities.open(pluginItem, function() {
+ is(list.childNodes.length, pluginCount, "Should only see the plugins");
+
+ var item = list.firstChild;
+ while (item) {
+ is(item.getAttribute("type"), "plugin", "All items should be plugins");
+ item = item.nextSibling;
+ }
+
+ // Tests that switching to, from, to the same pane in quick succession
+ // still only shows the right number of results
+
+ gCategoryUtilities.open(themeItem);
+ gCategoryUtilities.open(pluginItem);
+ gCategoryUtilities.open(themeItem, function() {
+ is(list.childNodes.length, themeCount, "Should only see the theme");
+
+ var item = list.firstChild;
+ while (item) {
+ is(item.getAttribute("type"), "theme", "All items should be theme");
+ item = item.nextSibling;
+ }
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562992.js b/toolkit/mozapps/extensions/test/browser/browser_bug562992.js
new file mode 100644
index 000000000..1cd4d90cd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562992.js
@@ -0,0 +1,70 @@
+/* -*- 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/. */
+
+/**
+ * This test ensures that when the extension manager UI is open and a
+ * restartless extension is installed from the web, its correct name appears
+ * when the download and installation complete. See bug 562992.
+ */
+
+var gManagerWindow;
+var gProvider;
+var gInstall;
+
+const EXTENSION_NAME = "Wunderbar";
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ open_manager("addons://list/extension", function (aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function () {
+ finish();
+ });
+}
+
+// Create a MockInstall with a MockAddon payload and add it to the provider,
+// causing the onNewInstall event to fire, which in turn will cause a new
+// "installing" item to appear in the list of extensions.
+add_test(function () {
+ let addon = new MockAddon(undefined, EXTENSION_NAME, "extension", true);
+ gInstall = new MockInstall(undefined, undefined, addon);
+ gInstall.addTestListener({
+ onNewInstall: run_next_test
+ });
+ gProvider.addInstall(gInstall);
+});
+
+// Finish the install, which will cause the "installing" item to be converted
+// to an "installed" item, which should have the correct add-on name.
+add_test(function () {
+ gInstall.addTestListener({
+ onInstallEnded: function () {
+ let list = gManagerWindow.document.getElementById("addon-list");
+
+ // To help prevent future breakage, don't assume the item is the only one
+ // in the list, or that it's first in the list. Find it by name.
+ for (let i = 0; i < list.itemCount; i++) {
+ let item = list.getItemAtIndex(i);
+ if (item.getAttribute("name") === EXTENSION_NAME) {
+ ok(true, "Item with correct name found");
+ run_next_test();
+ return;
+ }
+ }
+ ok(false, "Item with correct name was not found");
+ run_next_test();
+ }
+ });
+ gInstall.install();
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
new file mode 100644
index 000000000..5c5dce069
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
@@ -0,0 +1,137 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests bug 567127 - Add install button to the add-ons manager
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+var gManagerWindow;
+var gSawInstallNotification = false;
+
+// This listens for the next opened window and checks it is of the right url.
+// opencallback is called when the new window is fully loaded
+// closecallback is called when the window is closed
+function WindowOpenListener(url, opencallback, closecallback) {
+ this.url = url;
+ this.opencallback = opencallback;
+ this.closecallback = closecallback;
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+ wm.addListener(this);
+}
+
+WindowOpenListener.prototype = {
+ url: null,
+ opencallback: null,
+ closecallback: null,
+ window: null,
+ domwindow: null,
+
+ handleEvent: function(event) {
+ is(this.domwindow.document.location.href, this.url, "Should have opened the correct window");
+
+ this.domwindow.removeEventListener("load", this, false);
+ // Allow any other load handlers to execute
+ var self = this;
+ executeSoon(function() { self.opencallback(self.domwindow); } );
+ },
+
+ onWindowTitleChange: function(window, title) {
+ },
+
+ onOpenWindow: function(window) {
+ if (this.window)
+ return;
+
+ this.window = window;
+ this.domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindow);
+ this.domwindow.addEventListener("load", this, false);
+ },
+
+ onCloseWindow: function(window) {
+ if (this.window != window)
+ return;
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+ wm.removeListener(this);
+ this.opencallback = null;
+ this.window = null;
+ this.domwindow = null;
+
+ // Let the window close complete
+ executeSoon(this.closecallback);
+ this.closecallback = null;
+ }
+};
+
+
+var gInstallNotificationObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
+ is(installInfo.installs.length, 2, "Should be installing 2 files.")
+ if (gTestInWindow)
+ is(installInfo.browser, null, "Notification should have a null browser");
+ else
+ isnot(installInfo.browser, null, "Notification should have non-null browser");
+ gSawInstallNotification = true;
+ Services.obs.removeObserver(this, "addon-install-started");
+ }
+};
+
+
+function test_confirmation(aWindow, aExpectedURLs) {
+ var list = aWindow.document.getElementById("itemList");
+ is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
+
+ for (let url of aExpectedURLs) {
+ let found = false;
+ for (let node of list.children) {
+ if (node.url == url) {
+ found = true;
+ break;
+ }
+ }
+ ok(found, "Should have seen " + url + " in the list");
+ }
+
+ aWindow.document.documentElement.cancelDialog();
+}
+
+add_task(function* test_install_from_file() {
+ gManagerWindow = yield open_manager("addons://list/extension");
+
+ var filePaths = [
+ get_addon_file_url("browser_bug567127_1.xpi"),
+ get_addon_file_url("browser_bug567127_2.xpi")
+ ];
+ MockFilePicker.returnFiles = filePaths.map(function(aPath) aPath.file);
+
+ Services.obs.addObserver(gInstallNotificationObserver,
+ "addon-install-started", false);
+
+ // Set handler that executes the core test after the window opens,
+ // and resolves the promise when the window closes
+ let pInstallURIClosed = new Promise((resolve, reject) => {
+ new WindowOpenListener(INSTALL_URI, function(aWindow) {
+ try {
+ test_confirmation(aWindow, filePaths.map(function(aPath) aPath.spec));
+ } catch(e) {
+ reject(e);
+ }
+ }, resolve);
+ });
+
+ gManagerWindow.gViewController.doCommand("cmd_installFromFile");
+
+ yield pInstallURIClosed;
+
+ is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+
+ MockFilePicker.cleanup();
+ yield close_manager(gManagerWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug567137.js b/toolkit/mozapps/extensions/test/browser/browser_bug567137.js
new file mode 100644
index 000000000..bb9b9a894
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567137.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that the selected category is persisted across loads of the manager
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager(null, function(aWindow) {
+ let utils = new CategoryUtilities(aWindow);
+
+ // Open the plugins category
+ utils.openType("plugin", function() {
+
+ // Re-open the manager
+ close_manager(aWindow, function() {
+ open_manager(null, function(aWindow) {
+ utils = new CategoryUtilities(aWindow);
+
+ is(utils.selectedCategory, "plugin", "Should have shown the plugins category");
+
+ // Open the extensions category
+ utils.openType("extension", function() {
+
+ // Re-open the manager
+ close_manager(aWindow, function() {
+ open_manager(null, function(aWindow) {
+ utils = new CategoryUtilities(aWindow);
+
+ is(utils.selectedCategory, "extension", "Should have shown the extensions category");
+ close_manager(aWindow, finish);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug570760.js b/toolkit/mozapps/extensions/test/browser/browser_bug570760.js
new file mode 100644
index 000000000..765665e8c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug570760.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("");
+
+// Bug 570760 - Make ctrl-f and / focus the search box in the add-ons manager
+
+var gManagerWindow;
+var focusCount = 0;
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ function focusHandler() {
+ searchBox.blur();
+ focusCount++;
+ }
+ searchBox.addEventListener("focus", focusHandler);
+ f_key_test();
+ slash_key_test();
+ searchBox.removeEventListener("focus", focusHandler);
+ end_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+function f_key_test() {
+ EventUtils.synthesizeKey("f", { accelKey: true }, gManagerWindow);
+ is(focusCount, 1, "Search box should have been focused due to the f key");
+}
+
+function slash_key_test() {
+ EventUtils.synthesizeKey("/", { }, gManagerWindow);
+ is(focusCount, 2, "Search box should have been focused due to the / key");
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug572561.js b/toolkit/mozapps/extensions/test/browser/browser_bug572561.js
new file mode 100644
index 000000000..69102060e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug572561.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the locale category is shown if there are no locale packs
+// installed but some are pending install
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+var gInstallProperties = [{
+ name: "Locale Category Test",
+ type: "locale"
+}];
+var gInstall;
+var gExpectedCancel = false;
+var gTestInstallListener = {
+ onInstallStarted: function(aInstall) {
+ check_hidden(false);
+ },
+
+ onInstallEnded: function(aInstall) {
+ check_hidden(false);
+ run_next_test();
+ },
+
+ onInstallCancelled: function(aInstall) {
+ ok(gExpectedCancel, "Should expect install cancel");
+ check_hidden(false);
+ run_next_test();
+ },
+
+ onInstallFailed: function(aInstall) {
+ ok(false, "Did not expect onInstallFailed");
+ run_next_test();
+ }
+};
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+function check_hidden(aExpectedHidden) {
+ var hidden = !gCategoryUtilities.isTypeVisible("locale");
+ is(hidden, aExpectedHidden, "Should have correct hidden state");
+}
+
+// Tests that a non-active install does not make the locale category show
+add_test(function() {
+ check_hidden(true);
+ gInstall = gProvider.createInstalls(gInstallProperties)[0];
+ gInstall.addTestListener(gTestInstallListener);
+ check_hidden(true);
+ run_next_test();
+});
+
+// Test that restarting the add-on manager with a non-active install
+// does not cause the locale category to show
+add_test(function() {
+ restart_manager(gManagerWindow, null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ check_hidden(true);
+ run_next_test();
+ });
+});
+
+// Test that installing the install shows the locale category
+add_test(function() {
+ gInstall.install();
+});
+
+// Test that restarting the add-on manager does not cause the locale category
+// to become hidden
+add_test(function() {
+ restart_manager(gManagerWindow, null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ check_hidden(false);
+
+ gExpectedCancel = true;
+ gInstall.cancel();
+ });
+});
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug573062.js b/toolkit/mozapps/extensions/test/browser/browser_bug573062.js
new file mode 100644
index 000000000..c61131f03
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug573062.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+ waitForExplicitFinish();
+
+ var gProvider = new MockProvider();
+ let perms = AddonManager.PERM_CAN_UNINSTALL |
+ AddonManager.PERM_CAN_ENABLE | AddonManager.PERM_CAN_DISABLE;
+
+ gProvider.createAddons([{
+ id: "restart-enable-disable@tests.mozilla.org",
+ name: "restart-enable-disable",
+ description: "foo",
+ permissions: perms,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_ENABLE |
+ AddonManager.OP_NEEDS_RESTART_DISABLE
+ },
+ {
+ id: "restart-uninstall@tests.mozilla.org",
+ name: "restart-uninstall",
+ description: "foo",
+ permissions: perms,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_UNINSTALL
+ },
+ {
+ id: "no-restart-required@tests.mozilla.org",
+ name: "no-restart-required",
+ description: "bar",
+ permissions: perms,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ let addonList = aWindow.document.getElementById("addon-list");
+ let ed_r_Item, un_r_Item, no_r_Item;
+ for (let addonItem of addonList.childNodes) {
+ let name = addonItem.getAttribute("name");
+ switch (name) {
+ case "restart-enable-disable":
+ ed_r_Item = addonItem;
+ break;
+ case "restart-uninstall":
+ un_r_Item = addonItem;
+ break;
+ case "no-restart-required":
+ no_r_Item = addonItem;
+ break;
+ }
+ }
+
+ // Check the buttons in the list view.
+ function checkTooltips(aItem, aEnable, aDisable, aRemove) {
+ ok(aItem._enableBtn.getAttribute("tooltiptext") == aEnable);
+ ok(aItem._disableBtn.getAttribute("tooltiptext") == aDisable);
+ ok(aItem._removeBtn.getAttribute("tooltiptext") == aRemove);
+ }
+
+ let strs = aWindow.gStrings.ext;
+ addonList.selectedItem = ed_r_Item;
+ let ed_args = [ed_r_Item,
+ strs.GetStringFromName("enableAddonRestartRequiredTooltip"),
+ strs.GetStringFromName("disableAddonRestartRequiredTooltip"),
+ strs.GetStringFromName("uninstallAddonTooltip")];
+ checkTooltips.apply(null, ed_args);
+
+ addonList.selectedItem = un_r_Item;
+ let un_args = [un_r_Item,
+ strs.GetStringFromName("enableAddonTooltip"),
+ strs.GetStringFromName("disableAddonTooltip"),
+ strs.GetStringFromName("uninstallAddonRestartRequiredTooltip")];
+ checkTooltips.apply(null, un_args);
+
+ addonList.selectedItem = no_r_Item;
+ let no_args = [no_r_Item,
+ strs.GetStringFromName("enableAddonTooltip"),
+ strs.GetStringFromName("disableAddonTooltip"),
+ strs.GetStringFromName("uninstallAddonTooltip")];
+ checkTooltips.apply(null, no_args)
+
+ // Check the buttons in the details view.
+ function checkTooltips2(aItem, aEnable, aDisable, aRemove) {
+ let detailEnable = aWindow.document.getElementById("detail-enable-btn");
+ let detailDisable = aWindow.document.getElementById("detail-disable-btn");
+ let detailUninstall = aWindow.document.getElementById("detail-uninstall-btn");
+ ok(detailEnable.getAttribute("tooltiptext") == aEnable);
+ ok(detailDisable.getAttribute("tooltiptext") == aDisable);
+ ok(detailUninstall.getAttribute("tooltiptext") == aRemove);
+ }
+
+ function showInDetailView(aAddonId) {
+ aWindow.gViewController.loadView("addons://detail/" +
+ aWindow.encodeURIComponent(aAddonId));
+ }
+
+ // enable-disable:
+ showInDetailView("restart-enable-disable@tests.mozilla.org");
+ wait_for_view_load(aWindow, function() {
+ checkTooltips2.apply(null, ed_args);
+ // uninstall:
+ showInDetailView("restart-uninstall@tests.mozilla.org");
+ wait_for_view_load(aWindow, function() {
+ checkTooltips2.apply(null, un_args);
+ // no restart:
+ showInDetailView("no-restart-required@tests.mozilla.org");
+ wait_for_view_load(aWindow, function() {
+ checkTooltips2.apply(null, no_args);
+ aWindow.close();
+ finish();
+ });
+ });
+ });
+
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug577990.js b/toolkit/mozapps/extensions/test/browser/browser_bug577990.js
new file mode 100644
index 000000000..2c3c7ba5a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug577990.js
@@ -0,0 +1,132 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the visible delay in showing the "Language" category occurs
+// very minimally
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+var gInstall;
+var gInstallProperties = [{
+ name: "Locale Category Test",
+ type: "locale"
+}];
+
+function test() {
+ try {
+ if (Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).D2DEnabled) {
+ requestLongerTimeout(2);
+ }
+ } catch(e) {}
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+function install_locale(aCallback) {
+ gInstall = gProvider.createInstalls(gInstallProperties)[0];
+ gInstall.addTestListener({
+ onInstallEnded: function(aInstall) {
+ gInstall.removeTestListener(this);
+ executeSoon(aCallback);
+ }
+ });
+ gInstall.install();
+}
+
+function check_hidden(aExpectedHidden) {
+ var hidden = !gCategoryUtilities.isTypeVisible("locale");
+ is(hidden, !!aExpectedHidden, "Should have correct hidden state");
+}
+
+function run_open_test(aTestSetup, aLoadHidden, aInitializedHidden, aSelected) {
+ function loadCallback(aManagerWindow) {
+ gManagerWindow = aManagerWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ check_hidden(aLoadHidden);
+ }
+
+ function run() {
+ open_manager(null, function() {
+ check_hidden(aInitializedHidden);
+ var selected = (gCategoryUtilities.selectedCategory == "locale");
+ is(selected, !!aSelected, "Should have correct selected state");
+
+ run_next_test();
+ }, loadCallback);
+ }
+
+ close_manager(gManagerWindow, function() {
+ // Allow for asynchronous functions to run before the manager opens
+ aTestSetup ? aTestSetup(run) : run();
+ });
+}
+
+
+// Tests that the locale category is hidden when there are no locales installed
+add_test(function() {
+ run_open_test(null, true, true);
+});
+
+// Tests that installing a locale while the Add-on Manager is open shows the
+// locale category
+add_test(function() {
+ check_hidden(true);
+ install_locale(function() {
+ check_hidden(false);
+ run_next_test();
+ });
+});
+
+// Tests that the locale category is shown with no delay when restarting
+// Add-on Manager
+add_test(function() {
+ run_open_test(null, false, false);
+});
+
+// Tests that cancelling the locale install and restarting the Add-on Manager
+// causes the locale category to be hidden with an acceptable delay
+add_test(function() {
+ gInstall.cancel();
+ run_open_test(null, false, true)
+});
+
+// Tests that the locale category is hidden with no delay when restarting
+// Add-on Manager
+add_test(function() {
+ run_open_test(null, true, true);
+});
+
+// Tests that installing a locale when the Add-on Manager is closed, and then
+// opening the Add-on Manager causes the locale category to be shown with an
+// acceptable delay
+add_test(function() {
+ run_open_test(install_locale, true, false);
+});
+
+// Tests that selection of the locale category persists
+add_test(function() {
+ gCategoryUtilities.openType("locale", function() {
+ run_open_test(null, false, false, true);
+ });
+});
+
+// Tests that cancelling the locale install and restarting the Add-on Manager
+// causes the locale category to be hidden and not selected
+add_test(function() {
+ gInstall.cancel();
+ run_open_test(null, false, true);
+});
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug580298.js b/toolkit/mozapps/extensions/test/browser/browser_bug580298.js
new file mode 100644
index 000000000..8456cc433
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug580298.js
@@ -0,0 +1,111 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that certain types of addons do not have their version number
+// displayed. This currently only includes lightweight themes.
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "extension@tests.mozilla.org",
+ name: "Extension 1",
+ type: "extension",
+ version: "123"
+ }, {
+ id: "theme@tests.mozilla.org",
+ name: "Theme 2",
+ type: "theme",
+ version: "456"
+ }, {
+ id: "lwtheme@personas.mozilla.org",
+ name: "Persona 3",
+ type: "theme",
+ version: "789"
+ }]);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+function get(aId) {
+ return gManagerWindow.document.getElementById(aId);
+}
+
+function get_node(parent, anonid) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
+}
+
+function open_details(aList, aItem, aCallback) {
+ aList.ensureElementIsVisible(aItem);
+ EventUtils.synthesizeMouseAtCenter(aItem, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(aItem, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, aCallback);
+}
+
+function check_addon_has_version(aList, aName, aVersion) {
+ for (let i = 0; i < aList.itemCount; i++) {
+ let item = aList.getItemAtIndex(i);
+ if (get_node(item, "name").value === aName) {
+ ok(true, "Item with correct name found");
+ is(get_node(item, "version").value, aVersion, "Item has correct version");
+ return item;
+ }
+ }
+ ok(false, "Item with correct name was not found");
+ return null;
+}
+
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ info("Extension");
+ let list = gManagerWindow.document.getElementById("addon-list");
+ let item = check_addon_has_version(list, "Extension 1", "123");
+ open_details(list, item, function() {
+ is_element_visible(get("detail-version"), "Details view has version visible");
+ is(get("detail-version").value, "123", "Details view has correct version");
+ run_next_test();
+ });
+ });
+});
+
+add_test(function() {
+ gCategoryUtilities.openType("theme", function() {
+ info("Normal theme");
+ let list = gManagerWindow.document.getElementById("addon-list");
+ let item = check_addon_has_version(list, "Theme 2", "456");
+ open_details(list, item, function() {
+ is_element_visible(get("detail-version"), "Details view has version visible");
+ is(get("detail-version").value, "456", "Details view has correct version");
+ run_next_test();
+ });
+ });
+});
+
+add_test(function() {
+ gCategoryUtilities.openType("theme", function() {
+ info("Lightweight theme");
+ let list = gManagerWindow.document.getElementById("addon-list");
+ // See that the version isn't displayed
+ let item = check_addon_has_version(list, "Persona 3", "");
+ open_details(list, item, function() {
+ is_element_hidden(get("detail-version"), "Details view has version hidden");
+ // If the version element is hidden then we don't care about its value
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug581076.js b/toolkit/mozapps/extensions/test/browser/browser_bug581076.js
new file mode 100644
index 000000000..4c25c409d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug581076.js
@@ -0,0 +1,128 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 581076 - No "See all results" link present when searching for add-ons and not all are displayed (extensions.getAddons.maxResults)
+
+const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const PREF_GETADDONS_MAXRESULTS = "extensions.getAddons.maxResults";
+const SEARCH_URL = TESTROOT + "browser_searching.xml";
+const SEARCH_EXPECTED_TOTAL = 100;
+const SEARCH_QUERY = "search";
+
+var gManagerWindow;
+
+
+function test() {
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, SEARCH_URL);
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+
+ waitForExplicitFinish();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ // Test generates a lot of available installs so just cancel them all
+ AddonManager.getAllInstalls(function(aInstalls) {
+ for (let install of aInstalls)
+ install.cancel();
+
+ close_manager(gManagerWindow, finish);
+ });
+}
+
+function search(aRemoteSearch, aCallback) {
+ waitForFocus(function() {
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = SEARCH_QUERY;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ if (aRemoteSearch)
+ var filter = gManagerWindow.document.getElementById("search-filter-remote");
+ else
+ var filter = gManagerWindow.document.getElementById("search-filter-local");
+ EventUtils.synthesizeMouseAtCenter(filter, { }, gManagerWindow);
+
+ executeSoon(aCallback);
+ });
+ }, gManagerWindow);
+}
+
+function check_allresultslink(aShouldShow) {
+ var list = gManagerWindow.document.getElementById("search-list");
+ var link = gManagerWindow.document.getElementById("search-allresults-link");
+ is(link.parentNode, list.lastChild, "Footer should be at the end of the richlistbox");
+ if (aShouldShow) {
+ is_element_visible(link, "All Results link should be visible");
+ is(link.value, "See all " + SEARCH_EXPECTED_TOTAL + " results", "All Results link should show the correct message");
+ var scope = {};
+ Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm", scope);
+ is(link.href, scope.AddonRepository.getSearchURL(SEARCH_QUERY), "All Results link should have the correct href");
+ } else {
+ is_element_hidden(link, "All Results link should be hidden");
+ }
+}
+
+add_test(function() {
+ info("Searching locally");
+ search(false, function() {
+ check_allresultslink(false);
+ restart_manager(gManagerWindow, null, function(aManager) {
+ gManagerWindow = aManager;
+ run_next_test();
+ });
+ });
+});
+
+add_test(function() {
+ info("Searching remotely - more results than cap");
+ Services.prefs.setIntPref(PREF_GETADDONS_MAXRESULTS, 3);
+ search(true, function() {
+ check_allresultslink(true);
+ restart_manager(gManagerWindow, null, function(aManager) {
+ gManagerWindow = aManager;
+ run_next_test();
+ });
+ });
+});
+
+add_test(function() {
+ info("Searching remotely - less results than cap");
+ Services.prefs.setIntPref(PREF_GETADDONS_MAXRESULTS, 200);
+ search(true, function() {
+ check_allresultslink(false);
+ restart_manager(gManagerWindow, null, function(aManager) {
+ gManagerWindow = aManager;
+ run_next_test();
+ });
+ });
+});
+
+add_test(function() {
+ info("Searching remotely - more results than cap");
+ Services.prefs.setIntPref(PREF_GETADDONS_MAXRESULTS, 3);
+ search(true, function() {
+ check_allresultslink(true);
+ run_next_test();
+ });
+});
+
+add_test(function() {
+ info("Switching views");
+ gManagerWindow.loadView("addons://list/extension");
+ wait_for_view_load(gManagerWindow, function() {
+ info("Re-loading previous search");
+ search(true, function() {
+ check_allresultslink(true);
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug586574.js b/toolkit/mozapps/extensions/test/browser/browser_bug586574.js
new file mode 100644
index 000000000..fb5ebf01b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug586574.js
@@ -0,0 +1,286 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 586574 - Provide way to set a default for automatic updates
+// Bug 710064 - Make the "Update Add-ons Automatically" checkbox state
+// also depend on the extensions.update.enabled pref
+
+// TEST_PATH=toolkit/mozapps/extensions/test/browser/browser_bug586574.js make -C obj-ff mochitest-browser-chrome
+
+const PREF_UPDATE_ENABLED = "extensions.update.enabled";
+const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault";
+
+var gManagerWindow;
+var gProvider;
+
+var gUtilsBtn;
+var gUtilsMenu;
+var gDropdownMenu;
+var gSetDefault;
+var gResetToAutomatic;
+var gResetToManual;
+
+// Make sure we don't accidentally start a background update while the prefs
+// are enabled.
+disableBackgroundUpdateTimer();
+registerCleanupFunction(() => {
+ enableBackgroundUpdateTimer();
+});
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "addon 1",
+ version: "1.0",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+
+ gUtilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
+ gUtilsMenu = gManagerWindow.document.getElementById("utils-menu");
+
+ run_next_test();
+ });
+}
+
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+
+function wait_for_popup(aCallback) {
+ if (gUtilsMenu.state == "open") {
+ aCallback();
+ return;
+ }
+
+ gUtilsMenu.addEventListener("popupshown", function() {
+ gUtilsMenu.removeEventListener("popupshown", arguments.callee, false);
+ info("Utilities menu shown");
+ aCallback();
+ }, false);
+}
+
+function wait_for_hide(aCallback) {
+ if (gUtilsMenu.state == "closed") {
+ aCallback();
+ return;
+ }
+
+ gUtilsMenu.addEventListener("popuphidden", function() {
+ gUtilsMenu.removeEventListener("popuphidden", arguments.callee, false);
+ info("Utilities menu hidden");
+ aCallback();
+ }, false);
+}
+
+add_test(function() {
+ gSetDefault = gManagerWindow.document.getElementById("utils-autoUpdateDefault");
+ gResetToAutomatic = gManagerWindow.document.getElementById("utils-resetAddonUpdatesToAutomatic");
+ gResetToManual = gManagerWindow.document.getElementById("utils-resetAddonUpdatesToManual");
+
+ info("Ensuring default prefs are set to true");
+ Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, true);
+
+ wait_for_popup(function() {
+ is(gSetDefault.getAttribute("checked"), "true",
+ "#1 Set Default menuitem should be checked");
+ is_element_visible(gResetToAutomatic,
+ "#1 Reset to Automatic menuitem should be visible");
+ is_element_hidden(gResetToManual,
+ "#1 Reset to Manual menuitem should be hidden");
+
+ var listener = {
+ onPropertyChanged: function(aAddon, aProperties) {
+ AddonManager.removeAddonListener(listener);
+ is(aAddon.id, gProvider.addons[0].id,
+ "Should get onPropertyChanged event for correct addon");
+ ok(!("applyBackgroundUpdates" in aProperties),
+ "Should have gotten applyBackgroundUpdates in properties array");
+ is(aAddon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT,
+ "Addon.applyBackgroundUpdates should have been reset to default");
+
+ info("Setting Addon.applyBackgroundUpdates back to disabled");
+ aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ wait_for_hide(run_next_test);
+ }
+ };
+ AddonManager.addAddonListener(listener);
+
+ info("Clicking Reset to Automatic menuitem");
+ EventUtils.synthesizeMouseAtCenter(gResetToAutomatic, { }, gManagerWindow);
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ info("Disabling extensions.update.enabled");
+ Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, false);
+
+ wait_for_popup(function() {
+ isnot(gSetDefault.getAttribute("checked"), "true",
+ "#2 Set Default menuitem should not be checked");
+ is_element_visible(gResetToAutomatic,
+ "#2 Reset to Automatic menuitem should be visible");
+ is_element_hidden(gResetToManual,
+ "#2 Reset to Manual menuitem should be hidden");
+
+ wait_for_hide(run_next_test);
+
+ info("Clicking Set Default menuitem to reenable");
+ EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ ok(Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
+ "extensions.update.enabled should be true after the previous test");
+ ok(Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
+ "extensions.update.autoUpdateDefault should be true after the previous test");
+
+ info("Disabling both extensions.update.enabled and extensions.update.autoUpdateDefault");
+ Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, false);
+ Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, false);
+
+ wait_for_popup(function() {
+ isnot(gSetDefault.getAttribute("checked"), "true",
+ "#3 Set Default menuitem should not be checked");
+ is_element_hidden(gResetToAutomatic,
+ "#3 Reset to automatic menuitem should be hidden");
+ is_element_visible(gResetToManual,
+ "#3 Reset to manual menuitem should be visible");
+
+ wait_for_hide(run_next_test);
+
+ info("Clicking Set Default menuitem to reenable");
+ EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ ok(Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
+ "extensions.update.enabled should be true after the previous test");
+ ok(Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
+ "extensions.update.autoUpdateDefault should be true after the previous test");
+
+ info("clicking the button to disable extensions.update.autoUpdateDefault");
+ wait_for_popup(function() {
+ is(gSetDefault.getAttribute("checked"), "true",
+ "#4 Set Default menuitem should be checked");
+ is_element_visible(gResetToAutomatic,
+ "#4 Reset to Automatic menuitem should be visible");
+ is_element_hidden(gResetToManual,
+ "#4 Reset to Manual menuitem should be hidden");
+
+ wait_for_hide(run_next_test);
+
+ info("Clicking Set Default menuitem to disable");
+ EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ ok(Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
+ "extensions.update.enabled should be true after the previous test");
+ is(Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT), false,
+ "extensions.update.autoUpdateDefault should be false after the previous test");
+
+ wait_for_popup(function() {
+ isnot(gSetDefault.getAttribute("checked"), "true",
+ "#5 Set Default menuitem should not be checked");
+ is_element_hidden(gResetToAutomatic,
+ "#5 Reset to automatic menuitem should be hidden");
+ is_element_visible(gResetToManual,
+ "#5 Reset to manual menuitem should be visible");
+
+ var listener = {
+ onPropertyChanged: function(aAddon, aProperties) {
+ AddonManager.removeAddonListener(listener);
+ is(aAddon.id, gProvider.addons[0].id,
+ "Should get onPropertyChanged event for correct addon");
+ ok(!("applyBackgroundUpdates" in aProperties),
+ "Should have gotten applyBackgroundUpdates in properties array");
+ is(aAddon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT,
+ "Addon.applyBackgroundUpdates should have been reset to default");
+
+ info("Setting Addon.applyBackgroundUpdates back to disabled");
+ aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ wait_for_hide(run_next_test);
+ }
+ };
+ AddonManager.addAddonListener(listener);
+
+ info("Clicking Reset to Manual menuitem");
+ EventUtils.synthesizeMouseAtCenter(gResetToManual, { }, gManagerWindow);
+
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ wait_for_popup(function() {
+ isnot(gSetDefault.getAttribute("checked"), "true",
+ "#6 Set Default menuitem should not be checked");
+ is_element_hidden(gResetToAutomatic,
+ "#6 Reset to automatic menuitem should be hidden");
+ is_element_visible(gResetToManual,
+ "#6 Reset to manual menuitem should be visible");
+
+ wait_for_hide(run_next_test);
+
+ info("Clicking Set Default menuitem");
+ EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ wait_for_popup(function() {
+ is(gSetDefault.getAttribute("checked"), "true",
+ "#7 Set Default menuitem should be checked");
+ is_element_visible(gResetToAutomatic,
+ "#7 Reset to Automatic menuitem should be visible");
+ is_element_hidden(gResetToManual,
+ "#7 Reset to Manual menuitem should be hidden");
+
+ wait_for_hide(run_next_test);
+
+ gUtilsMenu.hidePopup();
+ });
+
+ info("Opening utilities menu");
+ EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
+});
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug587970.js b/toolkit/mozapps/extensions/test/browser/browser_bug587970.js
new file mode 100644
index 000000000..0ff6d1b59
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug587970.js
@@ -0,0 +1,180 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 587970 - Provide ability "Update all now" within 'Available Updates' screen
+
+var gManagerWindow;
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "addon 1",
+ version: "1.0",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "addon 2",
+ version: "2.0",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "addon 3",
+ version: "3.0",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+ }]);
+
+
+ open_manager("addons://updates/available", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+
+add_test(function() {
+ var list = gManagerWindow.document.getElementById("updates-list");
+ is(list.childNodes.length, 0, "Available updates list should be empty");
+
+ var emptyNotice = gManagerWindow.document.getElementById("empty-availableUpdates-msg");
+ is_element_visible(emptyNotice, "Empty notice should be visible");
+
+ var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
+ is_element_hidden(updateSelected, "Update Selected button should be hidden");
+
+ info("Adding updates");
+ gProvider.createInstalls([{
+ name: "addon 1",
+ version: "1.1",
+ existingAddon: gProvider.addons[0]
+ }, {
+ name: "addon 2",
+ version: "2.1",
+ existingAddon: gProvider.addons[1]
+ }, {
+ name: "addon 3",
+ version: "3.1",
+ existingAddon: gProvider.addons[2]
+ }]);
+
+ function wait_for_refresh() {
+ if (list.childNodes.length == 3 &&
+ list.childNodes[0].mManualUpdate &&
+ list.childNodes[1].mManualUpdate &&
+ list.childNodes[2].mManualUpdate) {
+ run_next_test();
+ } else {
+ info("Waiting for pane to refresh");
+ setTimeout(wait_for_refresh, 10);
+ }
+ }
+ info("Waiting for pane to refresh");
+ setTimeout(wait_for_refresh, 10);
+});
+
+
+add_test(function() {
+ var list = gManagerWindow.document.getElementById("updates-list");
+ is(list.childNodes.length, 3, "Available updates list should have 2 items");
+
+ var item1 = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ isnot(item1, null, "Item for addon1@tests.mozilla.org should be in list");
+ var item2 = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
+ isnot(item2, null, "Item for addon2@tests.mozilla.org should be in list");
+ var item3 = get_addon_element(gManagerWindow, "addon3@tests.mozilla.org");
+ isnot(item3, null, "Item for addon3@tests.mozilla.org should be in list");
+
+ var emptyNotice = gManagerWindow.document.getElementById("empty-availableUpdates-msg");
+ is_element_hidden(emptyNotice, "Empty notice should be hidden");
+
+ var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
+ is_element_visible(updateSelected, "Update Selected button should be visible");
+ is(updateSelected.disabled, false, "Update Selected button should be enabled by default");
+
+ is(item1._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon1");
+ is(item2._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon2");
+ is(item3._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon3");
+
+ info("Unchecking Include Update checkbox for addon1");
+ EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, { }, gManagerWindow);
+ is(item1._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon1");
+ is(updateSelected.disabled, false, "Update Selected button should still be enabled");
+
+ info("Unchecking Include Update checkbox for addon2");
+ EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, { }, gManagerWindow);
+ is(item2._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon2");
+ is(updateSelected.disabled, false, "Update Selected button should still be enabled");
+
+ info("Unchecking Include Update checkbox for addon3");
+ EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, { }, gManagerWindow);
+ is(item3._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon3");
+ is(updateSelected.disabled, true, "Update Selected button should now be disabled");
+
+ info("Checking Include Update checkbox for addon2");
+ EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, { }, gManagerWindow);
+ is(item2._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon2");
+ is(updateSelected.disabled, false, "Update Selected button should now be enabled");
+
+ info("Checking Include Update checkbox for addon3");
+ EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, { }, gManagerWindow);
+ is(item3._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon3");
+ is(updateSelected.disabled, false, "Update Selected button should now be enabled");
+
+ var installCount = 0;
+ var listener = {
+ onDownloadStarted: function(aInstall) {
+ isnot(aInstall.existingAddon.id, "addon1@tests.mozilla.org", "Should not have seen a download start for addon1");
+ },
+
+ onInstallEnded: function(aInstall) {
+ if (++installCount < 2)
+ return;
+
+ gProvider.installs[0].removeTestListener(listener);
+ gProvider.installs[1].removeTestListener(listener);
+ gProvider.installs[2].removeTestListener(listener);
+
+ // Installs are started synchronously so by the time an executeSoon is
+ // executed all installs that are going to start will have started
+ executeSoon(function() {
+ is(gProvider.installs[0].state, AddonManager.STATE_AVAILABLE, "addon1 should not have been upgraded");
+ is(gProvider.installs[1].state, AddonManager.STATE_INSTALLED, "addon2 should have been upgraded");
+ is(gProvider.installs[2].state, AddonManager.STATE_INSTALLED, "addon3 should have been upgraded");
+
+ run_next_test();
+ });
+ }
+ }
+ gProvider.installs[0].addTestListener(listener);
+ gProvider.installs[1].addTestListener(listener);
+ gProvider.installs[2].addTestListener(listener);
+ info("Clicking Update Selected button");
+ EventUtils.synthesizeMouseAtCenter(updateSelected, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
+ is(updateSelected.disabled, true, "Update Selected button should be disabled");
+
+ var item1 = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ isnot(item1, null, "Item for addon1@tests.mozilla.org should be in list");
+ is(item1._includeUpdate.checked, false, "Include Update checkbox should not have changed");
+
+ info("Checking Include Update checkbox for addon1");
+ EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, { }, gManagerWindow);
+ is(item1._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon1");
+ is(updateSelected.disabled, false, "Update Selected button should now not be disabled");
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug590347.js b/toolkit/mozapps/extensions/test/browser/browser_bug590347.js
new file mode 100644
index 000000000..8fe9c715e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug590347.js
@@ -0,0 +1,120 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 590347
+// Tests if softblock notifications are exposed in preference to incompatible
+// notifications when compatibility checking is disabled
+
+var gProvider;
+var gManagerWindow;
+var gCategoryUtilities;
+
+var gApp = document.getElementById("bundle_brand").getString("brandShortName");
+var gVersion = Services.appinfo.version;
+
+// Opens the details view of an add-on
+function open_details(aId, aType, aCallback) {
+ requestLongerTimeout(2);
+
+ gCategoryUtilities.openType(aType, function() {
+ var list = gManagerWindow.document.getElementById("addon-list");
+ var item = list.firstChild;
+ while (item) {
+ if ("mAddon" in item && item.mAddon.id == aId) {
+ list.ensureElementIsVisible(item);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, aCallback);
+ return;
+ }
+ item = item.nextSibling;
+ }
+ ok(false, "Should have found the add-on in the list");
+ });
+}
+
+function get_list_view_warning_node() {
+ let item = gManagerWindow.document.getElementById("addon-list").firstChild;
+ let found = false;
+ while (item) {
+ if (item.mAddon.name == "Test add-on") {
+ found = true;
+ break;
+ }
+ item = item.nextSibling;
+ }
+ ok(found, "Test add-on node should have been found.");
+ return item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
+}
+
+function get_detail_view_warning_node(aManagerWindow) {
+ if(aManagerWindow)
+ return aManagerWindow.document.getElementById("detail-warning");
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test add-on",
+ description: "A test add-on",
+ isCompatible: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
+ }]);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+// Check with compatibility checking enabled
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, true);
+ let warning_node = get_list_view_warning_node();
+ is_element_visible(warning_node, "Warning message should be visible");
+ is(warning_node.textContent, "Test add-on is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
+ run_next_test();
+ });
+});
+
+add_test(function() {
+ open_details("addon1@tests.mozilla.org", "extension", function() {
+ let warning_node = get_detail_view_warning_node(gManagerWindow);
+ is_element_visible(warning_node, "Warning message should be visible");
+ is(warning_node.textContent, "Test add-on is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
+ Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, false);
+ run_next_test();
+ });
+});
+
+// Check with compatibility checking disabled
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ let warning_node = get_list_view_warning_node();
+ is_element_visible(warning_node, "Warning message should be visible");
+ is(warning_node.textContent, "Test add-on is known to cause security or stability issues.", "Warning message should be correct");
+ run_next_test();
+ });
+});
+
+add_test(function() {
+ open_details("addon1@tests.mozilla.org", "extension", function() {
+ let warning_node = get_detail_view_warning_node(gManagerWindow);
+ is_element_visible(warning_node, "Warning message should be visible");
+ is(warning_node.textContent, "Test add-on is known to cause security or stability issues.", "Warning message should be correct");
+ run_next_test();
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug591465.js b/toolkit/mozapps/extensions/test/browser/browser_bug591465.js
new file mode 100644
index 000000000..2380deee1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug591465.js
@@ -0,0 +1,512 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 591465 - Context menu of add-ons miss context related state change entries
+
+
+let tempScope = {};
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
+let LightweightThemeManager = tempScope.LightweightThemeManager;
+
+
+const PREF_GETADDONS_MAXRESULTS = "extensions.getAddons.maxResults";
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const SEARCH_URL = TESTROOT + "browser_bug591465.xml";
+const SEARCH_QUERY = "SEARCH";
+
+var gManagerWindow;
+var gProvider;
+var gContextMenu;
+var gLWTheme = {
+ id: "4",
+ version: "1",
+ name: "Bling",
+ description: "SO MUCH BLING!",
+ author: "Pixel Pusher",
+ homepageURL: "http://mochi.test:8888/data/index.html",
+ headerURL: "http://mochi.test:8888/data/header.png",
+ footerURL: "http://mochi.test:8888/data/footer.png",
+ previewURL: "http://mochi.test:8888/data/preview.png",
+ iconURL: "http://mochi.test:8888/data/icon.png"
+ };
+
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "addon 1",
+ version: "1.0"
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "addon 2",
+ version: "1.0",
+ _userDisabled: true
+ }, {
+ id: "theme1@tests.mozilla.org",
+ name: "theme 1",
+ version: "1.0",
+ type: "theme"
+ }, {
+ id: "theme2@tests.mozilla.org",
+ name: "theme 2",
+ version: "1.0",
+ type: "theme",
+ _userDisabled: true
+ }, {
+ id: "theme3@tests.mozilla.org",
+ name: "theme 3",
+ version: "1.0",
+ type: "theme",
+ permissions: 0
+ }]);
+
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gContextMenu = aWindow.document.getElementById("addonitem-popup");
+ run_next_test();
+ });
+}
+
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+
+function check_contextmenu(aIsTheme, aIsEnabled, aIsRemote, aIsDetails, aIsSingleItemCase) {
+ if (aIsTheme || aIsEnabled || aIsRemote)
+ is_element_hidden(gManagerWindow.document.getElementById("menuitem_enableItem"),
+ "'Enable' should be hidden");
+ else
+ is_element_visible(gManagerWindow.document.getElementById("menuitem_enableItem"),
+ "'Enable' should be visible");
+
+ if (aIsTheme || !aIsEnabled || aIsRemote)
+ is_element_hidden(gManagerWindow.document.getElementById("menuitem_disableItem"),
+ "'Disable' should be hidden");
+ else
+ is_element_visible(gManagerWindow.document.getElementById("menuitem_disableItem"),
+ "'Disable' should be visible");
+
+ if (!aIsTheme || aIsEnabled || aIsRemote || aIsSingleItemCase)
+ is_element_hidden(gManagerWindow.document.getElementById("menuitem_enableTheme"),
+ "'Wear Theme' should be hidden");
+ else
+ is_element_visible(gManagerWindow.document.getElementById("menuitem_enableTheme"),
+ "'Wear Theme' should be visible");
+
+ if (!aIsTheme || !aIsEnabled || aIsRemote || aIsSingleItemCase)
+ is_element_hidden(gManagerWindow.document.getElementById("menuitem_disableTheme"),
+ "'Stop Wearing Theme' should be hidden");
+ else
+ is_element_visible(gManagerWindow.document.getElementById("menuitem_disableTheme"),
+ "'Stop Wearing Theme' should be visible");
+
+ if (aIsRemote)
+ is_element_visible(gManagerWindow.document.getElementById("menuitem_installItem"),
+ "'Install' should be visible");
+ else
+ is_element_hidden(gManagerWindow.document.getElementById("menuitem_installItem"),
+ "'Install' should be hidden");
+
+ if (aIsDetails)
+ is_element_hidden(gManagerWindow.document.getElementById("menuitem_showDetails"),
+ "'Show More Information' should be hidden in details view");
+ else
+ is_element_visible(gManagerWindow.document.getElementById("menuitem_showDetails"),
+ "'Show More Information' should be visible in list view");
+
+ if (aIsSingleItemCase)
+ is_element_hidden(gManagerWindow.document.getElementById("addonitem-menuseparator"),
+ "Menu separator should be hidden with only one menu item");
+ else
+ is_element_visible(gManagerWindow.document.getElementById("addonitem-menuseparator"),
+ "Menu separator should be visible with multiple menu items");
+
+}
+
+
+add_test(function() {
+ var el = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ isnot(el, null, "Should have found addon element");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, true, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on enabled extension item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+add_test(function() {
+ var el = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ isnot(el, null, "Should have found addon element");
+ el.mAddon.userDisabled = true;
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, false, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on newly disabled extension item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+add_test(function() {
+ var el = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ isnot(el, null, "Should have found addon element");
+ el.mAddon.userDisabled = false;
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, true, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on newly enabled extension item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+add_test(function() {
+ var el = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, false, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on disabled extension item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+
+add_test(function() {
+ gManagerWindow.loadView("addons://list/theme");
+ wait_for_view_load(gManagerWindow, function() {
+ var el = get_addon_element(gManagerWindow, "theme1@tests.mozilla.org");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, true, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on enabled theme item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+
+add_test(function() {
+ var el = get_addon_element(gManagerWindow, "theme2@tests.mozilla.org");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, false, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on disabled theme item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+
+add_test(function() {
+ LightweightThemeManager.currentTheme = gLWTheme;
+
+ var el = get_addon_element(gManagerWindow, "4@personas.mozilla.org");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, true, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on enabled LW theme item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+
+add_test(function() {
+ LightweightThemeManager.currentTheme = null;
+
+ var el = get_addon_element(gManagerWindow, "4@personas.mozilla.org");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, false, false, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on disabled LW theme item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+});
+
+
+add_test(function() {
+ LightweightThemeManager.currentTheme = gLWTheme;
+
+ gManagerWindow.loadView("addons://detail/4@personas.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, true, false, true, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on enabled LW theme, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+
+add_test(function() {
+ LightweightThemeManager.currentTheme = null;
+
+ gManagerWindow.loadView("addons://detail/4@personas.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, false, false, true, false);
+
+ gContextMenu.hidePopup();
+
+ AddonManager.getAddonByID("4@personas.mozilla.org", function(aAddon) {
+ aAddon.uninstall();
+ run_next_test();
+ });
+ }, false);
+
+ info("Opening context menu on disabled LW theme, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+
+add_test(function() {
+ gManagerWindow.loadView("addons://detail/addon1@tests.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, true, false, true, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on enabled extension, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+
+add_test(function() {
+ gManagerWindow.loadView("addons://detail/addon2@tests.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, false, false, true, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on disabled extension, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+
+add_test(function() {
+ gManagerWindow.loadView("addons://detail/theme1@tests.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, true, false, true, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on enabled theme, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+
+add_test(function() {
+ gManagerWindow.loadView("addons://detail/theme2@tests.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, false, false, true, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on disabled theme, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+add_test(function() {
+ gManagerWindow.loadView("addons://detail/theme3@tests.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(true, true, false, true, true);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu with single menu item on enabled theme, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
+
+add_test(function() {
+ info("Searching for remote addons");
+
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, SEARCH_URL);
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = SEARCH_QUERY;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ var filter = gManagerWindow.document.getElementById("search-filter-remote");
+ EventUtils.synthesizeMouseAtCenter(filter, { }, gManagerWindow);
+ executeSoon(function() {
+
+ var el = get_addon_element(gManagerWindow, "remote1@tests.mozilla.org");
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, false, true, false, false);
+
+ gContextMenu.hidePopup();
+ run_next_test();
+ }, false);
+
+ info("Opening context menu on remote extension item");
+ el.parentNode.ensureElementIsVisible(el);
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+
+ });
+ });
+});
+
+
+add_test(function() {
+ gManagerWindow.loadView("addons://detail/remote1@tests.mozilla.org");
+ wait_for_view_load(gManagerWindow, function() {
+
+ gContextMenu.addEventListener("popupshown", function() {
+ gContextMenu.removeEventListener("popupshown", arguments.callee, false);
+
+ check_contextmenu(false, false, true, true, false);
+
+ gContextMenu.hidePopup();
+
+ // Delete the created install
+ AddonManager.getAllInstalls(function(aInstalls) {
+ is(aInstalls.length, 1, "Should be one available install");
+ aInstalls[0].cancel();
+
+ run_next_test();
+ });
+ }, false);
+
+ info("Opening context menu on remote extension, in detail view");
+ var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug591465.xml b/toolkit/mozapps/extensions/test/browser/browser_bug591465.xml
new file mode 100644
index 000000000..bd648cf0f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug591465.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>MAGICAL SEARCH RESULT</name>
+ <type id='1'>Extension</type>
+ <guid>remote1@tests.mozilla.org</guid>
+ <version>3.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary - SEARCH SEARCH</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="2">http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_searching.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug591663.js b/toolkit/mozapps/extensions/test/browser/browser_bug591663.js
new file mode 100644
index 000000000..0736aa391
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug591663.js
@@ -0,0 +1,161 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that the empty notice in the list view disappears as it should
+
+// Don't use a standard list view (e.g. "extension") to ensure that the list is
+// initially empty. Don't need to worry about the list of categories displayed
+// since only the list view itself is tested.
+let VIEW_ID = "addons://list/mock-addon";
+
+let LIST_ID = "addon-list";
+let EMPTY_ID = "addon-list-empty";
+
+let gManagerWindow;
+let gProvider;
+let gItem;
+
+let gInstallProperties = {
+ name: "Bug 591663 Mock Install",
+ type: "mock-addon"
+};
+let gAddonProperties = {
+ id: "test1@tests.mozilla.org",
+ name: "Bug 591663 Mock Add-on",
+ type: "mock-addon"
+};
+let gExtensionProperties = {
+ name: "Bug 591663 Extension Install",
+ type: "extension"
+};
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider(true, [{
+ id: "mock-addon",
+ name: "Mock Add-ons",
+ uiPriority: 4500,
+ flags: AddonManager.TYPE_UI_VIEW_LIST
+ }]);
+
+ open_manager(VIEW_ID, function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+/**
+ * Check that the list view is as expected
+ *
+ * @param aItem
+ * The expected item in the list, or null if list should be empty
+ */
+function check_list(aItem) {
+ // Check state of the empty notice
+ let emptyNotice = gManagerWindow.document.getElementById(EMPTY_ID);
+ ok(emptyNotice != null, "Should have found the empty notice");
+ is(!emptyNotice.hidden, (aItem == null), "Empty notice should be showing if list empty");
+
+ // Check the children of the list
+ let list = gManagerWindow.document.getElementById(LIST_ID);
+ is(list.itemCount, aItem ? 1 : 0, "Should get expected number of items in list");
+ if (aItem != null) {
+ let itemName = list.firstChild.getAttribute("name");
+ is(itemName, aItem.name, "List item should have correct name");
+ }
+}
+
+
+// Test that the empty notice is showing and no items are showing in list
+add_test(function() {
+ check_list(null);
+ run_next_test();
+});
+
+// Test that a new, non-active, install does not affect the list view
+add_test(function() {
+ gItem = gProvider.createInstalls([gInstallProperties])[0];
+ check_list(null);
+ run_next_test();
+});
+
+// Test that onInstallStarted properly hides empty notice and adds install to list
+add_test(function() {
+ gItem.addTestListener({
+ onDownloadStarted: function() {
+ // Install type unknown until download complete
+ check_list(null);
+ },
+ onInstallStarted: function() {
+ check_list(gItem);
+ },
+ onInstallEnded: function() {
+ check_list(gItem);
+ run_next_test();
+ }
+ });
+
+ gItem.install();
+});
+
+// Test that restarting the manager does not change list
+add_test(function() {
+ restart_manager(gManagerWindow, VIEW_ID, function(aManagerWindow) {
+ gManagerWindow = aManagerWindow;
+ check_list(gItem);
+ run_next_test();
+ });
+});
+
+// Test that onInstallCancelled removes install and shows empty notice
+add_test(function() {
+ gItem.cancel();
+ gItem = null;
+ check_list(null);
+ run_next_test();
+});
+
+// Test that add-ons of a different type do not show up in the list view
+add_test(function() {
+ let extension = gProvider.createInstalls([gExtensionProperties])[0];
+ check_list(null);
+
+ extension.addTestListener({
+ onDownloadStarted: function() {
+ check_list(null);
+ },
+ onInstallStarted: function() {
+ check_list(null);
+ },
+ onInstallEnded: function() {
+ check_list(null);
+ extension.cancel();
+ run_next_test();
+ }
+ });
+
+ extension.install();
+});
+
+// Test that onExternalInstall properly hides empty notice and adds install to list
+add_test(function() {
+ gItem = gProvider.createAddons([gAddonProperties])[0];
+ check_list(gItem);
+ run_next_test();
+});
+
+// Test that restarting the manager does not change list
+add_test(function() {
+ restart_manager(gManagerWindow, VIEW_ID, function(aManagerWindow) {
+ gManagerWindow = aManagerWindow;
+ check_list(gItem);
+ run_next_test();
+ });
+});
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug593535.js b/toolkit/mozapps/extensions/test/browser/browser_bug593535.js
new file mode 100644
index 000000000..a78ef9a23
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug593535.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 593535 - Failure to download extension causes about:addons to list the
+// addon with no way to restart the download
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const SEARCH_URL = TESTROOT + "browser_bug593535.xml";
+const QUERY = "NOTFOUND";
+
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ AddonManager.getAllInstalls(function(aInstallsList) {
+ for (var install of aInstallsList) {
+ var sourceURI = install.sourceURI.spec;
+ if (sourceURI.match(/^http:\/\/example\.com\/(.+)\.xpi$/) != null)
+ install.cancel();
+ }
+
+ finish();
+ });
+ });
+}
+
+function search(aQuery, aCallback) {
+ // Point search to the correct xml test file
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, SEARCH_URL);
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = aQuery;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ var remoteFilter = gManagerWindow.document.getElementById("search-filter-remote");
+ EventUtils.synthesizeMouseAtCenter(remoteFilter, { }, gManagerWindow);
+
+ aCallback();
+ });
+}
+
+function get_addon_item(aName) {
+ var id = aName + "@tests.mozilla.org";
+ var list = gManagerWindow.document.getElementById("search-list");
+ var rows = list.getElementsByTagName("richlistitem");
+ for (let row of rows) {
+ if (row.mAddon && row.mAddon.id == id)
+ return row;
+ }
+
+ return null;
+}
+
+function get_install_button(aItem) {
+ isnot(aItem, null, "Item should not be null when checking state of install button");
+ var installStatus = getAnonymousElementByAttribute(aItem, "anonid", "install-status");
+ return getAnonymousElementByAttribute(installStatus, "anonid", "install-remote-btn");
+}
+
+
+function getAnonymousElementByAttribute(aElement, aName, aValue) {
+ return gManagerWindow.document.getAnonymousElementByAttribute(aElement,
+ aName,
+ aValue);
+}
+
+
+
+// Tests that a failed install for a remote add-on will ask to retry the install
+add_test(function() {
+ var remoteItem;
+
+ var listener = {
+ onDownloadFailed: function(aInstall) {
+ aInstall.removeListener(this);
+ ok(true, "Install failed as expected");
+
+ executeSoon(function() {
+ is(remoteItem.getAttribute("notification"), "warning", "Item should have notification attribute set to 'warning'");
+ is_element_visible(remoteItem._warning, "Warning text should be visible");
+ is(remoteItem._warning.textContent, "There was an error downloading NOTFOUND.", "Warning should show correct message");
+ is_element_visible(remoteItem._warningLink, "Retry button should be visible");
+ run_next_test();
+ });
+ },
+
+ onInstallEnded: function() {
+ ok(false, "Install should have failed");
+ }
+ }
+
+ search(QUERY, function() {
+ var list = gManagerWindow.document.getElementById("search-list");
+ remoteItem = get_addon_item("notfound1");
+ list.ensureElementIsVisible(remoteItem);
+
+ remoteItem.mAddon.install.addListener(listener);
+
+ var installBtn = get_install_button(remoteItem);
+ EventUtils.synthesizeMouseAtCenter(installBtn, { }, gManagerWindow);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug593535.xml b/toolkit/mozapps/extensions/test/browser/browser_bug593535.xml
new file mode 100644
index 000000000..847c2854d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug593535.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>NOTFOUND</name>
+ <type id='1'>Extension</type>
+ <guid>notfound1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Install file not found - NOTFOUND</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/file_not_found.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug596336.js b/toolkit/mozapps/extensions/test/browser/browser_bug596336.js
new file mode 100644
index 000000000..935820613
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug596336.js
@@ -0,0 +1,180 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that upgrading bootstrapped add-ons behaves correctly while the
+// manager is open
+
+var gManagerWindow;
+var gCategoryUtilities;
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+function get_list_item_count() {
+ return get_test_items_in_list(gManagerWindow).length;
+}
+
+function get_node(parent, anonid) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
+}
+
+function get_class_node(parent, cls) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "class", cls);
+}
+
+function install_addon(aXpi, aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/" + aXpi + ".xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall) {
+ executeSoon(aCallback);
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_addon(aAddon, version) {
+ is(get_list_item_count(), 1, "Should be one item in the list");
+ is(aAddon.version, version, "Add-on should have the right version");
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should see the add-on in the list");
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(get_node(item, "version").value, version, "Version should be correct");
+
+ if (aAddon.userDisabled)
+ is_element_visible(get_class_node(item, "disabled-postfix"), "Disabled postfix should be hidden");
+ else
+ is_element_hidden(get_class_node(item, "disabled-postfix"), "Disabled postfix should be hidden");
+}
+
+// Install version 1 then upgrade to version 2 with the manager open
+add_test(function() {
+ install_addon("browser_bug596336_1", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ check_addon(aAddon, "1.0");
+ ok(!aAddon.userDisabled, "Add-on should not be disabled");
+
+ install_addon("browser_bug596336_2", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ check_addon(aAddon, "2.0");
+ ok(!aAddon.userDisabled, "Add-on should not be disabled");
+
+ aAddon.uninstall();
+
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Install version 1 mark it as disabled then upgrade to version 2 with the
+// manager open
+add_test(function() {
+ install_addon("browser_bug596336_1", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ aAddon.userDisabled = true;
+ check_addon(aAddon, "1.0");
+ ok(aAddon.userDisabled, "Add-on should be disabled");
+
+ install_addon("browser_bug596336_2", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ check_addon(aAddon, "2.0");
+ ok(aAddon.userDisabled, "Add-on should be disabled");
+
+ aAddon.uninstall();
+
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Install version 1 click the remove button and then upgrade to version 2 with
+// the manager open
+add_test(function() {
+ install_addon("browser_bug596336_1", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ check_addon(aAddon, "1.0");
+ ok(!aAddon.userDisabled, "Add-on should not be disabled");
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ EventUtils.synthesizeMouseAtCenter(get_node(item, "remove-btn"), { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+ is_element_visible(get_class_node(item, "pending"), "Pending message should be visible");
+
+ install_addon("browser_bug596336_2", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ check_addon(aAddon, "2.0");
+ ok(!aAddon.userDisabled, "Add-on should not be disabled");
+
+ aAddon.uninstall();
+
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Install version 1, disable it, click the remove button and then upgrade to
+// version 2 with the manager open
+add_test(function() {
+ install_addon("browser_bug596336_1", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ aAddon.userDisabled = true;
+ check_addon(aAddon, "1.0");
+ ok(aAddon.userDisabled, "Add-on should be disabled");
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ EventUtils.synthesizeMouseAtCenter(get_node(item, "remove-btn"), { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+ is_element_visible(get_class_node(item, "pending"), "Pending message should be visible");
+
+ install_addon("browser_bug596336_2", function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ check_addon(aAddon, "2.0");
+ ok(aAddon.userDisabled, "Add-on should be disabled");
+
+ aAddon.uninstall();
+
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug608316.js b/toolkit/mozapps/extensions/test/browser/browser_bug608316.js
new file mode 100644
index 000000000..39986c23b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug608316.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 608316 - Test that cancelling an uninstall during the onUninstalling
+// event doesn't confuse the UI
+
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "addon 1",
+ version: "1.0"
+ }]);
+
+ run_next_test();
+}
+
+
+function end_test() {
+ finish();
+}
+
+
+add_test(function() {
+ var sawUninstall = false;
+ var listener = {
+ onUninstalling: function(aAddon, aRestartRequired) {
+ if (aAddon.id != "addon1@tests.mozilla.org")
+ return;
+ sawUninstall = true;
+ aAddon.cancelUninstall();
+ }
+ }
+
+ // Important to add this before opening the UI so it gets its events first
+ AddonManager.addAddonListener(listener);
+ registerCleanupFunction(function() {
+ AddonManager.removeAddonListener(listener);
+ });
+
+ open_manager("addons://list/extension", function(aManager) {
+ var addon = get_addon_element(aManager, "addon1@tests.mozilla.org");
+ isnot(addon, null, "Should see the add-on in the list");
+
+ var removeBtn = aManager.document.getAnonymousElementByAttribute(addon, "anonid", "remove-btn");
+ EventUtils.synthesizeMouseAtCenter(removeBtn, { }, aManager);
+
+ ok(sawUninstall, "Should have seen the uninstall event");
+ sawUninstall = false;
+
+ is(addon.getAttribute("pending"), "", "Add-on should not be uninstalling");
+
+ close_manager(aManager, function() {
+ ok(!sawUninstall, "Should not have seen another uninstall event");
+
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug610764.js b/toolkit/mozapps/extensions/test/browser/browser_bug610764.js
new file mode 100644
index 000000000..58de88130
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug610764.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the discovery view is the default
+
+var gCategoryUtilities;
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager(null, function(aWindow) {
+ waitForFocus(function() {
+ // The last view is cached except when it is the search view so switch to
+ // that and reopen to ensure we see the default view
+ var searchBox = aWindow.document.getElementById("header-search");
+ searchBox.value = "bar";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, aWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, aWindow);
+
+ wait_for_view_load(aWindow, function() {
+ close_manager(aWindow, function() {
+ open_manager(null, function(aWindow) {
+ gCategoryUtilities = new CategoryUtilities(aWindow);
+ is(gCategoryUtilities.selectedCategory, "discover", "Should show the discovery pane by default");
+
+ close_manager(aWindow, finish);
+ });
+ });
+ });
+ }, aWindow);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug616841.js b/toolkit/mozapps/extensions/test/browser/browser_bug616841.js
new file mode 100644
index 000000000..3cf6f5346
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug616841.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test_string_compare() {
+ ok("C".localeCompare("D") < 0, "C < D");
+ ok("D".localeCompare("C") > 0, "D > C");
+ ok("\u010C".localeCompare("D") < 0, "\u010C < D");
+ ok("D".localeCompare("\u010C") > 0, "D > \u010C");
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ test_string_compare();
+
+ AddonManager.getAddonByID("foo", function(aAddon) {
+ test_string_compare();
+ finish();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug618502.js b/toolkit/mozapps/extensions/test/browser/browser_bug618502.js
new file mode 100644
index 000000000..36ba8fb69
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug618502.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 608316 - Test that opening the manager to an add-on that doesn't exist
+// just loads the default view
+
+var gCategoryUtilities;
+
+function test() {
+ waitForExplicitFinish();
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+add_test(function() {
+ open_manager("addons://detail/foo", function(aManager) {
+ gCategoryUtilities = new CategoryUtilities(aManager);
+ is(gCategoryUtilities.selectedCategory, "discover", "Should fall back to the discovery pane");
+
+ close_manager(aManager, run_next_test);
+ });
+});
+
+// Also test that opening directly to an add-on that does exist doesn't break
+// and selects the right category
+add_test(function() {
+ new MockProvider().createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "addon 1",
+ version: "1.0"
+ }]);
+
+ open_manager("addons://detail/addon1@tests.mozilla.org", function(aManager) {
+ gCategoryUtilities = new CategoryUtilities(aManager);
+ is(gCategoryUtilities.selectedCategory, "extension", "Should have selected the right category");
+
+ close_manager(aManager, run_next_test);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug679604.js b/toolkit/mozapps/extensions/test/browser/browser_bug679604.js
new file mode 100644
index 000000000..e1ec605c2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug679604.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 679604 - Test that a XUL persisted category from an older version of
+// Firefox doesn't break the add-ons manager when that category doesn't exist
+
+var gManagerWindow;
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager(null, function(aWindow) {
+ var categories = aWindow.document.getElementById("categories");
+ categories.setAttribute("last-selected", "foo");
+ aWindow.document.persist("categories", "last-selected");
+
+ close_manager(aWindow, function() {
+ Services.prefs.clearUserPref(PREF_UI_LASTCATEGORY);
+
+ open_manager(null, function(aWindow) {
+ is(new CategoryUtilities(aWindow).selectedCategory, "discover",
+ "Should have loaded the right view");
+
+ close_manager(aWindow, finish);
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug714593.js b/toolkit/mozapps/extensions/test/browser/browser_bug714593.js
new file mode 100644
index 000000000..b9a7faa5e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug714593.js
@@ -0,0 +1,140 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that installed addons in the search view load inline prefs properly
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const NO_MATCH_URL = TESTROOT + "browser_searching_empty.xml";
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+function test() {
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "inlinesettings2@tests.mozilla.org",
+ name: "Inline Settings (Regular)",
+ version: "1",
+ optionsURL: CHROMEROOT + "options.xul",
+ optionsType: AddonManager.OPTIONS_TYPE_INLINE
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+/*
+ * Checks whether or not the Add-ons Manager is currently searching
+ *
+ * @param aExpectedSearching
+ * The expected isSearching state
+ */
+function check_is_searching(aExpectedSearching) {
+ var loading = gManagerWindow.document.getElementById("search-loading");
+ is(!is_hidden(loading), aExpectedSearching,
+ "Search throbber should be showing iff currently searching");
+}
+
+/*
+ * Completes a search
+ *
+ * @param aQuery
+ * The query to search for
+ * @param aFinishImmediately
+ * Boolean representing whether or not the search is expected to
+ * finish immediately
+ * @param aCallback
+ * The callback to call when the search is done
+ * @param aCategoryType
+ * The expected selected category after the search is done.
+ * Optional and defaults to "search"
+ */
+function search(aQuery, aFinishImmediately, aCallback, aCategoryType) {
+ // Point search to the correct xml test file
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, NO_MATCH_URL);
+
+ aCategoryType = aCategoryType ? aCategoryType : "search";
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = aQuery;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ var finishImmediately = true;
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, aCategoryType, "Expected category view should be selected");
+ is(gCategoryUtilities.isTypeVisible("search"), aCategoryType == "search",
+ "Search category should only be visible if it is the current view");
+ is(finishImmediately, aFinishImmediately, "Search should finish immediately only if expected");
+
+ aCallback();
+ });
+
+ finishImmediately = false
+ if (!aFinishImmediately)
+ check_is_searching(true);
+}
+
+/*
+ * Get item for a specific add-on by name
+ *
+ * @param aName
+ * The name of the add-on to search for
+ * @return Row of add-on if found, null otherwise
+ */
+function get_addon_item(aName) {
+ var id = aName + "@tests.mozilla.org";
+ var list = gManagerWindow.document.getElementById("search-list");
+ var rows = list.getElementsByTagName("richlistitem");
+ for (let row of rows) {
+ if (row.mAddon && row.mAddon.id == id)
+ return row;
+ }
+
+ return null;
+}
+
+add_test(function() {
+ search("settings", false, function() {
+ var localFilter = gManagerWindow.document.getElementById("search-filter-local");
+ EventUtils.synthesizeMouseAtCenter(localFilter, { }, gManagerWindow);
+
+ var item = get_addon_item("inlinesettings2");
+ // Force the XBL binding to apply.
+ item.clientTop;
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gManagerWindow.gViewController.currentViewObj, gManagerWindow.gDetailView, "View should have changed to detail");
+
+ var searchCategory = gManagerWindow.document.getElementById("category-search");
+ EventUtils.synthesizeMouseAtCenter(searchCategory, { }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gManagerWindow.gViewController.currentViewObj, gManagerWindow.gSearchView, "View should have changed back to search");
+
+ // Reset filter to remote to avoid breaking later tests.
+ var remoteFilter = gManagerWindow.document.getElementById("search-filter-remote");
+ EventUtils.synthesizeMouseAtCenter(remoteFilter, { }, gManagerWindow);
+ run_next_test();
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js b/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js
new file mode 100644
index 000000000..1799adcdd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js
@@ -0,0 +1,462 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that we can cancel the add-on compatibility check while it is
+// in progress (bug 772484).
+// Test framework copied from browser_bug557956.js
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_MIN_PLATFORM_COMPAT = "extensions.minCompatiblePlatformVersion";
+const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
+
+let repo = {};
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm", repo);
+Components.utils.import("resource://gre/modules/Promise.jsm", this);
+
+/**
+ * Test add-ons:
+ *
+ * Addon minVersion maxVersion Notes
+ * addon1 0 *
+ * addon2 0 0
+ * addon3 0 0
+ * addon4 1 *
+ * addon5 0 0 Made compatible by update check
+ * addon6 0 0 Made compatible by update check
+ * addon7 0 0 Has a broken update available
+ * addon8 0 0 Has an update available
+ * addon9 0 0 Has an update available
+ * addon10 0 0 Made incompatible by override check
+ */
+
+// describe the addons
+let ao1 = { file: "browser_bug557956_1", id: "addon1@tests.mozilla.org"};
+let ao2 = { file: "browser_bug557956_2", id: "addon2@tests.mozilla.org"};
+let ao3 = { file: "browser_bug557956_3", id: "addon3@tests.mozilla.org"};
+let ao4 = { file: "browser_bug557956_4", id: "addon4@tests.mozilla.org"};
+let ao5 = { file: "browser_bug557956_5", id: "addon5@tests.mozilla.org"};
+let ao6 = { file: "browser_bug557956_6", id: "addon6@tests.mozilla.org"};
+let ao7 = { file: "browser_bug557956_7", id: "addon7@tests.mozilla.org"};
+let ao8 = { file: "browser_bug557956_8_1", id: "addon8@tests.mozilla.org"};
+let ao9 = { file: "browser_bug557956_9_1", id: "addon9@tests.mozilla.org"};
+let ao10 = { file: "browser_bug557956_10", id: "addon10@tests.mozilla.org"};
+
+// Return a promise that resolves after the specified delay in MS
+function delayMS(aDelay) {
+ let deferred = Promise.defer();
+ setTimeout(deferred.resolve, aDelay);
+ return deferred.promise;
+}
+
+// Return a promise that resolves when the specified observer topic is notified
+function promise_observer(aTopic) {
+ let deferred = Promise.defer();
+ Services.obs.addObserver(function observe(aSubject, aObsTopic, aData) {
+ Services.obs.removeObserver(arguments.callee, aObsTopic);
+ deferred.resolve([aSubject, aData]);
+ }, aTopic, false);
+ return deferred.promise;
+}
+
+// Install a set of addons using a bogus update URL so that we can force
+// the compatibility update to happen later
+// @param aUpdateURL The real update URL to use after the add-ons are installed
+function promise_install_test_addons(aAddonList, aUpdateURL) {
+ info("Starting add-on installs");
+ var installs = [];
+ let deferred = Promise.defer();
+
+ // Use a blank update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+
+ for (let addon of aAddonList) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/" + addon.file + ".xpi", function(aInstall) {
+ installs.push(aInstall);
+ }, "application/x-xpinstall");
+ }
+
+ var listener = {
+ installCount: 0,
+
+ onInstallEnded: function() {
+ this.installCount++;
+ if (this.installCount == installs.length) {
+ info("Done add-on installs");
+ // Switch to the test update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, aUpdateURL);
+ deferred.resolve();
+ }
+ }
+ };
+
+ for (let install of installs) {
+ install.addListener(listener);
+ install.install();
+ }
+
+ return deferred.promise;
+}
+
+function promise_addons_by_ids(aAddonIDs) {
+ info("promise_addons_by_ids " + aAddonIDs.toSource());
+ let deferred = Promise.defer();
+ AddonManager.getAddonsByIDs(aAddonIDs, deferred.resolve);
+ return deferred.promise;
+}
+
+function* promise_uninstall_test_addons() {
+ info("Starting add-on uninstalls");
+ let addons = yield promise_addons_by_ids([ao1.id, ao2.id, ao3.id, ao4.id, ao5.id,
+ ao6.id, ao7.id, ao8.id, ao9.id, ao10.id]);
+ let deferred = Promise.defer();
+ let uninstallCount = addons.length;
+ let listener = {
+ onUninstalled: function(aAddon) {
+ if (aAddon) {
+ info("Finished uninstalling " + aAddon.id);
+ }
+ if (--uninstallCount == 0) {
+ info("Done add-on uninstalls");
+ AddonManager.removeAddonListener(listener);
+ deferred.resolve();
+ }
+ }};
+ AddonManager.addAddonListener(listener);
+ for (let addon of addons) {
+ if (addon)
+ addon.uninstall();
+ else
+ listener.onUninstalled(null);
+ }
+ yield deferred.promise;
+}
+
+// Returns promise{window}, resolves with a handle to the compatibility
+// check window
+function promise_open_compatibility_window(aInactiveAddonIds) {
+ let deferred = Promise.defer();
+ // This will reset the longer timeout multiplier to 2 which will give each
+ // test that calls open_compatibility_window a minimum of 60 seconds to
+ // complete.
+ requestLongerTimeout(2);
+
+ var variant = Cc["@mozilla.org/variant;1"].
+ createInstance(Ci.nsIWritableVariant);
+ variant.setFromVariant(aInactiveAddonIds);
+
+ // Cannot be modal as we want to interract with it, shouldn't cause problems
+ // with testing though.
+ var features = "chrome,centerscreen,dialog,titlebar";
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ var win = ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
+
+ win.addEventListener("load", function() {
+ function page_shown(aEvent) {
+ if (aEvent.target.pageid)
+ info("Page " + aEvent.target.pageid + " shown");
+ }
+
+ win.removeEventListener("load", arguments.callee, false);
+
+ info("Compatibility dialog opened");
+
+ win.addEventListener("pageshow", page_shown, false);
+ win.addEventListener("unload", function() {
+ win.removeEventListener("unload", arguments.callee, false);
+ win.removeEventListener("pageshow", page_shown, false);
+ dump("Compatibility dialog closed\n");
+ }, false);
+
+ deferred.resolve(win);
+ }, false);
+ return deferred.promise;
+}
+
+function promise_window_close(aWindow) {
+ let deferred = Promise.defer();
+ aWindow.addEventListener("unload", function() {
+ aWindow.removeEventListener("unload", arguments.callee, false);
+ deferred.resolve(aWindow);
+ }, false);
+ return deferred.promise;
+}
+
+function promise_page(aWindow, aPageId) {
+ let deferred = Promise.defer();
+ var page = aWindow.document.getElementById(aPageId);
+ if (aWindow.document.getElementById("updateWizard").currentPage === page) {
+ deferred.resolve(aWindow);
+ } else {
+ page.addEventListener("pageshow", function() {
+ page.removeEventListener("pageshow", arguments.callee, false);
+ executeSoon(function() {
+ deferred.resolve(aWindow);
+ });
+ }, false);
+ }
+ return deferred.promise;
+}
+
+function get_list_names(aList) {
+ var items = [];
+ for (let listItem of aList.childNodes)
+ items.push(listItem.label);
+ items.sort();
+ return items;
+}
+
+// These add-ons became inactive during the upgrade
+let inactiveAddonIds = [
+ ao5.id,
+ ao6.id,
+ ao7.id,
+ ao8.id,
+ ao9.id
+];
+
+// Make sure the addons in the list are not installed
+function* check_addons_uninstalled(aAddonList) {
+ let foundList = yield promise_addons_by_ids([addon.id for (addon of aAddonList)]);
+ for (let i = 0; i < aAddonList.length; i++) {
+ ok(!foundList[i], "Addon " + aAddonList[i].id + " is not installed");
+ }
+ info("Add-on uninstall check complete");
+ yield true;
+}
+
+// Test what happens when the user cancels during AddonRepository.repopulateCache()
+// Add-ons that have updates available should not update if they were disabled before
+// For this test, addon8 became disabled during update and addon9 was previously disabled,
+// so addon8 should update and addon9 should not
+add_task(function cancel_during_repopulate() {
+ let a5, a8, a9, a10;
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+
+ let installsDone = promise_observer("TEST:all-updates-done");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // Set up our test addons so that the server-side JS has a 500ms delay to make
+ // sure we cancel the dialog before we get the data we want to refill our
+ // AddonRepository cache
+ let addonList = [ao5, ao8, ao9, ao10];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs?500");
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, TESTROOT + "browser_bug557956.xml");
+
+ [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(!a8.isCompatible, "addon8 should not be compatible");
+ ok(!a9.isCompatible, "addon9 should not be compatible");
+
+ let compatWindow = yield promise_open_compatibility_window([ao5.id, ao8.id]);
+ var doc = compatWindow.document;
+ yield promise_page(compatWindow, "versioninfo");
+
+ // Brief delay to let the update window finish requesting all add-ons and start
+ // reloading the addon repository
+ yield delayMS(50);
+
+ info("Cancel the compatibility check dialog");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ info("Waiting for installs to complete");
+ yield installsDone;
+ ok(!repo.AddonRepository.isSearching, "Background installs are done");
+
+ // There should be no active updates
+ let getInstalls = Promise.defer();
+ AddonManager.getAllInstalls(getInstalls.resolve);
+ let installs = yield getInstalls.promise;
+ is (installs.length, 0, "There should be no active installs after background installs are done");
+
+ // addon8 should have updated in the background,
+ // addon9 was listed as previously disabled so it should not have updated
+ [a5, a8, a9, a10] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id, ao10.id]);
+ ok(a5.isCompatible, "addon5 should be compatible");
+ ok(a8.isCompatible, "addon8 should have been upgraded");
+ ok(!a9.isCompatible, "addon9 should not have been upgraded");
+ ok(!a10.isCompatible, "addon10 should not be compatible");
+
+ info("Updates done");
+ yield promise_uninstall_test_addons();
+ info("done uninstalling add-ons");
+});
+
+// User cancels after repopulateCache, while we're waiting for the addon.findUpdates()
+// calls in gVersionInfoPage_onPageShow() to complete
+// For this test, both addon8 and addon9 were disabled by this update, but addon8
+// is set to not auto-update, so only addon9 should update in the background
+add_task(function cancel_during_findUpdates() {
+ let a5, a8, a9;
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ // Clear the AddonRepository-last-updated preference to ensure that it reloads
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+ let observeUpdateDone = promise_observer("TEST:addon-repository-data-updated");
+ let installsDone = promise_observer("TEST:all-updates-done");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // No delay on the .sjs this time because we want the cache to repopulate
+ let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs");
+
+ [a8] = yield promise_addons_by_ids([ao8.id]);
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
+ var doc = compatWindow.document;
+ yield promise_page(compatWindow, "versioninfo");
+
+ info("Waiting for repository-data-updated");
+ yield observeUpdateDone;
+
+ // Quick wait to make sure the findUpdates calls get queued
+ yield delayMS(5);
+
+ info("Cancel the compatibility check dialog");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ info("Waiting for installs to complete 2");
+ yield installsDone;
+ ok(!repo.AddonRepository.isSearching, "Background installs are done 2");
+
+ // addon8 should have updated in the background,
+ // addon9 was listed as previously disabled so it should not have updated
+ [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
+ ok(a5.isCompatible, "addon5 should be compatible");
+ ok(!a8.isCompatible, "addon8 should not have been upgraded");
+ ok(a9.isCompatible, "addon9 should have been upgraded");
+
+ let getInstalls = Promise.defer();
+ AddonManager.getAllInstalls(getInstalls.resolve);
+ let installs = yield getInstalls.promise;
+ is (installs.length, 0, "There should be no active installs after the dialog is cancelled 2");
+
+ info("findUpdates done");
+ yield promise_uninstall_test_addons();
+});
+
+// Cancelling during the 'mismatch' screen allows add-ons that can auto-update
+// to continue updating in the background and cancels any other updates
+// Same conditions as the previous test - addon8 and addon9 have updates available,
+// addon8 is set to not auto-update so only addon9 should become compatible
+add_task(function cancel_mismatch() {
+ let a3, a5, a7, a8, a9;
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ // Clear the AddonRepository-last-updated preference to ensure that it reloads
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+ let installsDone = promise_observer("TEST:all-updates-done");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // No delay on the .sjs this time because we want the cache to repopulate
+ let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs");
+
+ [a8] = yield promise_addons_by_ids([ao8.id]);
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ // Check that the addons start out not compatible.
+ [a3, a7, a8, a9] = yield promise_addons_by_ids([ao3.id, ao7.id, ao8.id, ao9.id]);
+ ok(!a3.isCompatible, "addon3 should not be compatible");
+ ok(!a7.isCompatible, "addon7 should not be compatible");
+ ok(!a8.isCompatible, "addon8 should not be compatible");
+ ok(!a9.isCompatible, "addon9 should not be compatible");
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
+ var doc = compatWindow.document;
+ info("Wait for mismatch page");
+ yield promise_page(compatWindow, "mismatch");
+ info("Click the Don't Check button");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ yield promise_window_close(compatWindow);
+ info("Waiting for installs to complete in cancel_mismatch");
+ yield installsDone;
+
+ // addon8 should not have updated in the background,
+ // addon9 was listed as previously disabled so it should not have updated
+ [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
+ ok(a5.isCompatible, "addon5 should be compatible");
+ ok(!a8.isCompatible, "addon8 should not have been upgraded");
+ ok(a9.isCompatible, "addon9 should have been upgraded");
+
+ // Make sure there are no pending addon installs
+ let pInstalls = Promise.defer();
+ AddonManager.getAllInstalls(pInstalls.resolve);
+ let installs = yield pInstalls.promise;
+ ok(installs.length == 0, "No remaining add-on installs (" + installs.toSource() + ")");
+
+ yield promise_uninstall_test_addons();
+ yield check_addons_uninstalled(addonList);
+});
+
+// Cancelling during the 'mismatch' screen with only add-ons that have
+// no updates available
+add_task(function cancel_mismatch_no_updates() {
+ let a3, a5, a6
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // No delay on the .sjs this time because we want the cache to repopulate
+ let addonList = [ao3, ao5, ao6];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs");
+
+ // Check that the addons start out not compatible.
+ [a3, a5, a6] = yield promise_addons_by_ids([ao3.id, ao5.id, ao6.id]);
+ ok(!a3.isCompatible, "addon3 should not be compatible");
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(!a6.isCompatible, "addon6 should not be compatible");
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ let compatWindow = yield promise_open_compatibility_window([ao3.id, ao5.id, ao6.id]);
+ var doc = compatWindow.document;
+ info("Wait for mismatch page");
+ yield promise_page(compatWindow, "mismatch");
+ info("Click the Don't Check button");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ yield promise_window_close(compatWindow);
+
+ [a3, a5, a6] = yield promise_addons_by_ids([ao3.id, ao5.id, ao6.id]);
+ ok(!a3.isCompatible, "addon3 should not be compatible");
+ ok(a5.isCompatible, "addon5 should have become compatible");
+ ok(a6.isCompatible, "addon6 should have become compatible");
+
+ // Make sure there are no pending addon installs
+ let pInstalls = Promise.defer();
+ AddonManager.getAllInstalls(pInstalls.resolve);
+ let installs = yield pInstalls.promise;
+ ok(installs.length == 0, "No remaining add-on installs (" + installs.toSource() + ")");
+
+ yield promise_uninstall_test_addons();
+ yield check_addons_uninstalled(addonList);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_checkAddonCompatibility.js b/toolkit/mozapps/extensions/test/browser/browser_checkAddonCompatibility.js
new file mode 100644
index 000000000..6c42e0126
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_checkAddonCompatibility.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that all bundled add-ons are compatible.
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ ok(AddonManager.strictCompatibility, "Strict compatibility should be enabled");
+
+ AddonManager.getAllAddons(function gAACallback(aAddons) {
+ // Sort add-ons (by type and name) to improve output.
+ aAddons.sort(function compareTypeName(a, b) {
+ return a.type.localeCompare(b.type) || a.name.localeCompare(b.name);
+ });
+
+ let allCompatible = true;
+ for (let a of aAddons) {
+ // Ignore plugins.
+ if (a.type == "plugin")
+ continue;
+
+ ok(a.isCompatible, a.type + " " + a.name + " " + a.version + " should be compatible");
+ allCompatible = allCompatible && a.isCompatible;
+ }
+ // Add a reminder.
+ if (!allCompatible)
+ ok(false, "As this test failed, test browser_bug557956.js should have failed, too.");
+
+ finish();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_debug_button.js b/toolkit/mozapps/extensions/test/browser/browser_debug_button.js
new file mode 100644
index 000000000..3f371e906
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_debug_button.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests debug button for addons in list view
+ */
+
+let { Promise } = Components.utils.import("resource://gre/modules/Promise.jsm", {});
+let { Task } = Components.utils.import("resource://gre/modules/Task.jsm", {});
+
+const getDebugButton = node =>
+ node.ownerDocument.getAnonymousElementByAttribute(node, "anonid", "debug-btn");
+const addonDebuggingEnabled = bool =>
+ Services.prefs.setBoolPref("devtools.chrome.enabled", !!bool);
+const remoteDebuggingEnabled = bool =>
+ Services.prefs.setBoolPref("devtools.debugger.remote-enabled", !!bool);
+
+function test() {
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+
+ var gProvider = new MockProvider();
+ gProvider.createAddons([{
+ id: "non-debuggable@tests.mozilla.org",
+ name: "No debug",
+ description: "foo"
+ },
+ {
+ id: "debuggable@tests.mozilla.org",
+ name: "Debuggable",
+ description: "bar",
+ isDebuggable: true
+ }]);
+
+ Task.spawn(function* () {
+ addonDebuggingEnabled(false);
+ remoteDebuggingEnabled(false);
+
+ yield testDOM((nondebug, debuggable) => {
+ is(nondebug.disabled, true,
+ "addon:disabled::remote:disabled button is disabled for legacy addons");
+ is(nondebug.hidden, true,
+ "addon:disabled::remote:disabled button is hidden for legacy addons");
+ is(debuggable.disabled, true,
+ "addon:disabled::remote:disabled button is disabled for debuggable addons");
+ is(debuggable.hidden, true,
+ "addon:disabled::remote:disabled button is hidden for debuggable addons");
+ });
+
+ addonDebuggingEnabled(true);
+ remoteDebuggingEnabled(false);
+
+ yield testDOM((nondebug, debuggable) => {
+ is(nondebug.disabled, true,
+ "addon:enabled::remote:disabled button is disabled for legacy addons");
+ is(nondebug.disabled, true,
+ "addon:enabled::remote:disabled button is hidden for legacy addons");
+ is(debuggable.disabled, true,
+ "addon:enabled::remote:disabled button is disabled for debuggable addons");
+ is(debuggable.disabled, true,
+ "addon:enabled::remote:disabled button is hidden for debuggable addons");
+ });
+
+ addonDebuggingEnabled(false);
+ remoteDebuggingEnabled(true);
+
+ yield testDOM((nondebug, debuggable) => {
+ is(nondebug.disabled, true,
+ "addon:disabled::remote:enabled button is disabled for legacy addons");
+ is(nondebug.disabled, true,
+ "addon:disabled::remote:enabled button is hidden for legacy addons");
+ is(debuggable.disabled, true,
+ "addon:disabled::remote:enabled button is disabled for debuggable addons");
+ is(debuggable.disabled, true,
+ "addon:disabled::remote:enabled button is hidden for debuggable addons");
+ });
+
+ addonDebuggingEnabled(true);
+ remoteDebuggingEnabled(true);
+
+ yield testDOM((nondebug, debuggable) => {
+ is(nondebug.disabled, true,
+ "addon:enabled::remote:enabled button is disabled for legacy addons");
+ is(nondebug.disabled, true,
+ "addon:enabled::remote:enabled button is hidden for legacy addons");
+ is(debuggable.disabled, false,
+ "addon:enabled::remote:enabled button is enabled for debuggable addons");
+ is(debuggable.hidden, false,
+ "addon:enabled::remote:enabled button is visible for debuggable addons");
+ });
+
+ finish();
+ });
+
+ function testDOM (testCallback) {
+ let deferred = Promise.defer();
+ open_manager("addons://list/extension", function(aManager) {
+ const {document} = aManager;
+ const addonList = document.getElementById("addon-list");
+ const nondebug = addonList.querySelector("[name='No debug']");
+ const debuggable = addonList.querySelector("[name='Debuggable']");
+
+ testCallback.apply(null, [nondebug, debuggable].map(getDebugButton));
+
+ close_manager(aManager, deferred.resolve);
+ });
+ return deferred.promise;
+ }
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_details.js b/toolkit/mozapps/extensions/test/browser/browser_details.js
new file mode 100644
index 000000000..7394c87da
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_details.js
@@ -0,0 +1,764 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests various aspects of the details view
+
+const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault"
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const SEARCH_URL = TESTROOT + "browser_details.xml";
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+var gApp = document.getElementById("bundle_brand").getString("brandShortName");
+var gVersion = Services.appinfo.version;
+var gBlocklistURL = Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL");
+var gPluginURL = Services.urlFormatter.formatURLPref("plugins.update.url");
+var gDate = new Date(2010, 7, 1);
+
+function open_details(aId, aType, aCallback) {
+ requestLongerTimeout(2);
+
+ gCategoryUtilities.openType(aType, function() {
+ var list = gManagerWindow.document.getElementById("addon-list");
+ var item = list.firstChild;
+ while (item) {
+ if ("mAddon" in item && item.mAddon.id == aId) {
+ list.ensureElementIsVisible(item);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, aCallback);
+ return;
+ }
+ item = item.nextSibling;
+ }
+ ok(false, "Should have found the add-on in the list");
+ });
+}
+
+function get(aId) {
+ return gManagerWindow.document.getElementById(aId);
+}
+
+function test() {
+ requestLongerTimeout(2);
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, SEARCH_URL);
+
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test add-on 1",
+ version: "2.1",
+ description: "Short description",
+ fullDescription: "Longer description",
+ type: "extension",
+ iconURL: "chrome://foo/skin/icon.png",
+ icon64URL: "chrome://foo/skin/icon64.png",
+ contributionURL: "http://foo.com",
+ contributionAmount: "$0.99",
+ sourceURI: Services.io.newURI("http://example.com/foo", null, null),
+ averageRating: 4,
+ reviewCount: 5,
+ reviewURL: "http://example.com/reviews",
+ homepageURL: "http://example.com/addon1",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "Test add-on 2",
+ version: "2.2",
+ description: "Short description",
+ creator: { name: "Mozilla", url: null },
+ type: "extension",
+ iconURL: "chrome://foo/skin/icon.png",
+ contributionURL: "http://foo.com",
+ contributionAmount: null,
+ updateDate: gDate,
+ permissions: 0,
+ screenshots: [{
+ url: "chrome://branding/content/about.png",
+ width: 200,
+ height: 150
+ }],
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "Test add-on 3",
+ description: "Short description",
+ creator: { name: "Mozilla", url: "http://www.mozilla.org" },
+ type: "extension",
+ sourceURI: Services.io.newURI("http://example.com/foo", null, null),
+ updateDate: gDate,
+ reviewCount: 1,
+ reviewURL: "http://example.com/reviews",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
+ isActive: false,
+ isCompatible: false,
+ appDisabled: true,
+ permissions: AddonManager.PERM_CAN_ENABLE |
+ AddonManager.PERM_CAN_DISABLE |
+ AddonManager.PERM_CAN_UPGRADE,
+ screenshots: [{
+ url: "http://example.com/screenshot",
+ width: 400,
+ height: 300,
+ thumbnailURL: "chrome://branding/content/icon64.png",
+ thumbnailWidth: 160,
+ thumbnailHeight: 120
+ }],
+ }, {
+ id: "addon4@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon4@tests.mozilla.org",
+ name: "Test add-on 4",
+ _userDisabled: true,
+ isActive: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED
+ }, {
+ id: "addon5@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon5@tests.mozilla.org",
+ name: "Test add-on 5",
+ isActive: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
+ appDisabled: true
+ }, {
+ id: "addon6@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon6@tests.mozilla.org",
+ name: "Test add-on 6",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon7@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon7@tests.mozilla.org",
+ name: "Test add-on 7",
+ _userDisabled: true,
+ isActive: false
+ }, {
+ id: "addon8@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon8@tests.mozilla.org",
+ name: "Test add-on 8",
+ blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED
+ }]);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+// Opens and tests the details view for add-on 1
+add_test(function() {
+ open_details("addon1@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 1", "Name should be correct");
+ is_element_visible(get("detail-version"), "Version should not be hidden");
+ is(get("detail-version").value, "2.1", "Version should be correct");
+ is(get("detail-icon").src, "chrome://foo/skin/icon64.png", "Icon should be correct");
+ is_element_hidden(get("detail-creator"), "Creator should be hidden");
+ is_element_hidden(get("detail-screenshot"), "Screenshot should be hidden");
+ is(get("detail-screenshot").width, "", "Screenshot dimensions should not be set");
+ is(get("detail-screenshot").height, "", "Screenshot dimensions should not be set");
+ is(get("detail-desc").textContent, "Short description", "Description should be correct");
+ is(get("detail-fulldesc").textContent, "Longer description", "Full description should be correct");
+
+ is_element_visible(get("detail-contributions"), "Contributions section should be visible");
+ is_element_visible(get("detail-contrib-suggested"), "Contributions amount should be visible");
+ ok(get("detail-contrib-suggested").value, "$0.99");
+
+ is_element_visible(get("detail-updates-row"), "Updates should not be hidden");
+ is_element_hidden(get("detail-dateUpdated"), "Update date should be hidden");
+
+ is_element_visible(get("detail-rating-row"), "Rating row should not be hidden");
+ is_element_visible(get("detail-rating"), "Rating should not be hidden");
+ is(get("detail-rating").averageRating, 4, "Rating should be correct");
+ is_element_visible(get("detail-reviews"), "Reviews should not be hidden");
+ is(get("detail-reviews").href, "http://example.com/reviews", "Review URL should be correct");
+ is(get("detail-reviews").value, "5 reviews", "Review text should be correct");
+
+ is_element_visible(get("detail-homepage-row"), "Homepage should be visible");
+ ok(get("detail-homepage").href, "http://example.com/addon1");
+ is_element_hidden(get("detail-repository-row"), "Repository profile should not be visible");
+
+ is_element_hidden(get("detail-size"), "Size should be hidden");
+
+ is_element_hidden(get("detail-downloads"), "Downloads should be hidden");
+
+ is_element_visible(get("detail-autoUpdate"), "Updates should not be hidden");
+ ok(get("detail-autoUpdate").childNodes[1].selected, "Updates ahould be automatic");
+ is_element_hidden(get("detail-findUpdates-btn"), "Check for updates should be hidden");
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").lastChild, {}, gManagerWindow);
+ ok(get("detail-autoUpdate").lastChild.selected, "Updates should be manual");
+ is_element_visible(get("detail-findUpdates-btn"), "Check for updates should be visible");
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").firstChild, {}, gManagerWindow);
+ ok(get("detail-autoUpdate").firstChild.selected, "Updates should be automatic");
+ is_element_hidden(get("detail-findUpdates-btn"), "Check for updates should be hidden");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ // Disable it
+ EventUtils.synthesizeMouseAtCenter(get("detail-disable-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 1 will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Reopen it
+ open_details("addon1@tests.mozilla.org", "extension", function() {
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 1 will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Undo disabling
+ EventUtils.synthesizeMouseAtCenter(get("detail-undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+});
+
+// Opens and tests the details view for add-on 2
+add_test(function() {
+ open_details("addon2@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 2", "Name should be correct");
+ is_element_visible(get("detail-version"), "Version should not be hidden");
+ is(get("detail-version").value, "2.2", "Version should be correct");
+ is(get("detail-icon").src, "chrome://foo/skin/icon.png", "Icon should be correct");
+
+ is_element_visible(get("detail-creator"), "Creator should not be hidden");
+ is_element_visible(get("detail-creator")._creatorName, "Creator name should not be hidden");
+ is(get("detail-creator")._creatorName.value, "Mozilla", "Creator should be correct");
+ is_element_hidden(get("detail-creator")._creatorLink, "Creator link should be hidden");
+
+ is_element_visible(get("detail-screenshot"), "Screenshot should be visible");
+ is(get("detail-screenshot").src, "chrome://branding/content/about.png", "Should be showing the full sized screenshot");
+ is(get("detail-screenshot").width, 200, "Screenshot dimensions should be set");
+ is(get("detail-screenshot").height, 150, "Screenshot dimensions should be set");
+ is(get("detail-screenshot").hasAttribute("loading"), true, "Screenshot should have loading attribute");
+ is(get("detail-desc").textContent, "Short description", "Description should be correct");
+ is_element_hidden(get("detail-fulldesc"), "Full description should be hidden");
+
+ is_element_visible(get("detail-contributions"), "Contributions section should be visible");
+ is_element_hidden(get("detail-contrib-suggested"), "Contributions amount should be hidden");
+
+ is_element_visible(get("detail-dateUpdated"), "Update date should not be hidden");
+ is(get("detail-dateUpdated").value, formatDate(gDate), "Update date should be correct");
+
+ is_element_hidden(get("detail-rating-row"), "Rating should be hidden");
+
+ is_element_hidden(get("detail-homepage-row"), "Homepage should not be visible");
+ is_element_hidden(get("detail-repository-row"), "Repository profile should not be visible");
+
+ is_element_hidden(get("detail-size"), "Size should be hidden");
+
+ is_element_hidden(get("detail-downloads"), "Downloads should be hidden");
+
+ is_element_hidden(get("detail-updates-row"), "Updates should be hidden");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_hidden(get("detail-uninstall-btn"), "Remove button should be hidden");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ get("detail-screenshot").addEventListener("load", function() {
+ this.removeEventListener("load", arguments.callee, false);
+ is(this.hasAttribute("loading"), false, "Screenshot should not have loading attribute");
+ run_next_test();
+ }, false);
+ });
+});
+
+// Opens and tests the details view for add-on 3
+add_test(function() {
+ open_details("addon3@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 3", "Name should be correct");
+ is_element_hidden(get("detail-version"), "Version should be hidden");
+ is(get("detail-icon").src, "", "Icon should be correct");
+
+ is_element_visible(get("detail-creator"), "Creator should not be hidden");
+ is_element_hidden(get("detail-creator")._creatorName, "Creator name should be hidden");
+ is_element_visible(get("detail-creator")._creatorLink, "Creator link should not be hidden");
+ is(get("detail-creator")._creatorLink.value, "Mozilla", "Creator link should be correct");
+ is(get("detail-creator")._creatorLink.href, "http://www.mozilla.org", "Creator link href should be correct");
+
+ is_element_visible(get("detail-screenshot"), "Screenshot should be visible");
+ is(get("detail-screenshot").src, "chrome://branding/content/icon64.png", "Should be showing the thumbnail");
+ is(get("detail-screenshot").width, 160, "Screenshot dimensions should be set");
+ is(get("detail-screenshot").height, 120, "Screenshot dimensions should be set");
+ is(get("detail-screenshot").hasAttribute("loading"), true, "Screenshot should have loading attribute");
+
+ is_element_hidden(get("detail-contributions"), "Contributions section should be hidden");
+
+ is_element_visible(get("detail-updates-row"), "Updates should not be hidden");
+ is_element_visible(get("detail-dateUpdated"), "Update date should not be hidden");
+ is(get("detail-dateUpdated").value, formatDate(gDate), "Update date should be correct");
+
+ is_element_visible(get("detail-rating-row"), "Rating row should not be hidden");
+ is_element_hidden(get("detail-rating"), "Rating should be hidden");
+ is_element_visible(get("detail-reviews"), "Reviews should not be hidden");
+ is(get("detail-reviews").href, "http://example.com/reviews", "Review URL should be correct");
+ is(get("detail-reviews").value, "1 review", "Review text should be correct");
+
+ is_element_hidden(get("detail-size"), "Size should be hidden");
+
+ is_element_hidden(get("detail-downloads"), "Downloads should be hidden");
+
+ is_element_visible(get("detail-autoUpdate"), "Updates should not be hidden");
+ ok(get("detail-autoUpdate").lastChild.selected, "Updates should be manual");
+ is_element_visible(get("detail-findUpdates-btn"), "Check for updates should be visible");
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").childNodes[1], {}, gManagerWindow);
+ ok(get("detail-autoUpdate").childNodes[1].selected, "Updates should be automatic");
+ is_element_hidden(get("detail-findUpdates-btn"), "Check for updates should be hidden");
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").lastChild, {}, gManagerWindow);
+ ok(get("detail-autoUpdate").lastChild.selected, "Updates should be manual");
+ is_element_visible(get("detail-findUpdates-btn"), "Check for updates should be visible");
+
+ info("Setting " + PREF_AUTOUPDATE_DEFAULT + " to true");
+ Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, true);
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").firstChild, {}, gManagerWindow);
+ ok(get("detail-autoUpdate").firstChild.selected, "Updates should be default");
+ is_element_hidden(get("detail-findUpdates-btn"), "Check for updates should be hidden");
+
+ info("Setting " + PREF_AUTOUPDATE_DEFAULT + " to false");
+ Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, false);
+ ok(get("detail-autoUpdate").firstChild.selected, "Updates should be default");
+ is_element_visible(get("detail-findUpdates-btn"), "Check for updates should be visible");
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").childNodes[1], {}, gManagerWindow);
+ ok(get("detail-autoUpdate").childNodes[1].selected, "Updates should be automatic");
+ is_element_hidden(get("detail-findUpdates-btn"), "Check for updates should be hidden");
+ EventUtils.synthesizeMouseAtCenter(get("detail-autoUpdate").firstChild, {}, gManagerWindow);
+ ok(get("detail-autoUpdate").firstChild.selected, "Updates should be default");
+ is_element_visible(get("detail-findUpdates-btn"), "Check for updates should be visible");
+ Services.prefs.clearUserPref(PREF_AUTOUPDATE_DEFAULT);
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_hidden(get("detail-uninstall-btn"), "Remove button should be hidden");
+
+ is_element_visible(get("detail-warning"), "Warning message should be visible");
+ is(get("detail-warning").textContent, "Test add-on 3 is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ get("detail-screenshot").addEventListener("load", function() {
+ this.removeEventListener("load", arguments.callee, false);
+ is(this.hasAttribute("loading"), false, "Screenshot should not have loading attribute");
+ run_next_test();
+ }, false);
+ });
+});
+
+// Opens and tests the details view for add-on 4
+add_test(function() {
+ open_details("addon4@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 4", "Name should be correct");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_visible(get("detail-warning"), "Warning message should be visible");
+ is(get("detail-warning").textContent, "Test add-on 4 is known to cause security or stability issues.", "Warning message should be correct");
+ is_element_visible(get("detail-warning-link"), "Warning link should be visible");
+ is(get("detail-warning-link").value, "More Information", "Warning link text should be correct");
+ is(get("detail-warning-link").href, "http://example.com/addon4@tests.mozilla.org", "Warning link should be correct");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ // Enable it
+ EventUtils.synthesizeMouseAtCenter(get("detail-enable-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 4 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Reopen it
+ open_details("addon4@tests.mozilla.org", "extension", function() {
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 4 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Undo enabling
+ EventUtils.synthesizeMouseAtCenter(get("detail-undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_visible(get("detail-warning"), "Warning message should be visible");
+ is(get("detail-warning").textContent, "Test add-on 4 is known to cause security or stability issues.", "Warning message should be correct");
+ is_element_visible(get("detail-warning-link"), "Warning link should be visible");
+ is(get("detail-warning-link").value, "More Information", "Warning link text should be correct");
+ is(get("detail-warning-link").href, "http://example.com/addon4@tests.mozilla.org", "Warning link should be correct");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+});
+
+// Opens and tests the details view for add-on 5
+add_test(function() {
+ open_details("addon5@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 5", "Name should be correct");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_visible(get("detail-error"), "Error message should be visible");
+ is(get("detail-error").textContent, "Test add-on 5 has been disabled due to security or stability issues.", "Error message should be correct");
+ is_element_visible(get("detail-error-link"), "Error link should be visible");
+ is(get("detail-error-link").value, "More Information", "Error link text should be correct");
+ is(get("detail-error-link").href, "http://example.com/addon5@tests.mozilla.org", "Error link should be correct");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+});
+
+// Opens and tests the details view for add-on 6
+add_test(function() {
+ open_details("addon6@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 6", "Name should be correct");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ // Disable it
+ EventUtils.synthesizeMouseAtCenter(get("detail-disable-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ // Reopen it
+ open_details("addon6@tests.mozilla.org", "extension", function() {
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be visible");
+
+ // Enable it
+ EventUtils.synthesizeMouseAtCenter(get("detail-enable-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+});
+
+// Opens and tests the details view for add-on 7
+add_test(function() {
+ open_details("addon7@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 7", "Name should be correct");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ // Enable it
+ EventUtils.synthesizeMouseAtCenter(get("detail-enable-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 7 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Reopen it
+ open_details("addon7@tests.mozilla.org", "extension", function() {
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 7 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Undo enabling
+ EventUtils.synthesizeMouseAtCenter(get("detail-undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+});
+
+// Opens and tests the details view for add-on 8
+add_test(function() {
+ open_details("addon8@tests.mozilla.org", "extension", function() {
+ is(get("detail-name").textContent, "Test add-on 8", "Name should be correct");
+
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_visible(get("detail-warning"), "Warning message should be visible");
+ is(get("detail-warning").textContent, "An important update is available for Test add-on 8.", "Warning message should be correct");
+ is_element_visible(get("detail-warning-link"), "Warning link should be visible");
+ is(get("detail-warning-link").value, "Update Now", "Warning link text should be correct");
+ is(get("detail-warning-link").href, gPluginURL, "Warning link should be correct");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ // Disable it
+ EventUtils.synthesizeMouseAtCenter(get("detail-disable-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 8 will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Reopen it
+ open_details("addon8@tests.mozilla.org", "extension", function() {
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
+ is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_visible(get("detail-pending"), "Pending message should be visible");
+ is(get("detail-pending").textContent, "Test add-on 8 will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ // Undo disabling
+ EventUtils.synthesizeMouseAtCenter(get("detail-undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_visible(get("detail-warning"), "Warning message should be visible");
+ is(get("detail-warning").textContent, "An important update is available for Test add-on 8.", "Warning message should be correct");
+ is_element_visible(get("detail-warning-link"), "Warning link should be visible");
+ is(get("detail-warning-link").value, "Update Now", "Warning link text should be correct");
+ is(get("detail-warning-link").href, gPluginURL, "Warning link should be correct");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-error-link"), "Error link should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that upgrades with onExternalInstall apply immediately
+add_test(function() {
+ open_details("addon1@tests.mozilla.org", "extension", function() {
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test add-on replacement",
+ version: "2.5",
+ description: "Short description replacement",
+ fullDescription: "Longer description replacement",
+ type: "extension",
+ iconURL: "chrome://foo/skin/icon.png",
+ icon64URL: "chrome://foo/skin/icon264.png",
+ sourceURI: Services.io.newURI("http://example.com/foo", null, null),
+ averageRating: 2,
+ optionsURL: "chrome://foo/content/options.xul",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }]);
+
+ is(get("detail-name").textContent, "Test add-on replacement", "Name should be correct");
+ is_element_visible(get("detail-version"), "Version should not be hidden");
+ is(get("detail-version").value, "2.5", "Version should be correct");
+ is(get("detail-icon").src, "chrome://foo/skin/icon264.png", "Icon should be correct");
+ is_element_hidden(get("detail-creator"), "Creator should be hidden");
+ is_element_hidden(get("detail-screenshot"), "Screenshot should be hidden");
+ is(get("detail-desc").textContent, "Short description replacement", "Description should be correct");
+ is(get("detail-fulldesc").textContent, "Longer description replacement", "Full description should be correct");
+
+ is_element_hidden(get("detail-contributions"), "Contributions section should be hidden");
+
+ is_element_hidden(get("detail-dateUpdated"), "Update date should be hidden");
+
+ is_element_visible(get("detail-rating-row"), "Rating row should not be hidden");
+ is_element_visible(get("detail-rating"), "Rating should not be hidden");
+ is(get("detail-rating").averageRating, 2, "Rating should be correct");
+ is_element_hidden(get("detail-reviews"), "Reviews should be hidden");
+
+ is_element_hidden(get("detail-homepage-row"), "Homepage should be hidden");
+
+ is_element_hidden(get("detail-size"), "Size should be hidden");
+
+ is_element_hidden(get("detail-downloads"), "Downloads should be hidden");
+
+ is_element_visible(get("detail-prefs-btn"), "Preferences button should be visible");
+ is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
+ is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
+ is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
+
+ is_element_hidden(get("detail-warning"), "Warning message should be hidden");
+ is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
+ is_element_hidden(get("detail-error"), "Error message should be hidden");
+ is_element_hidden(get("detail-pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+});
+
+// Check that onPropertyChanges for appDisabled updates the UI
+add_test(function() {
+ info("Checking that onPropertyChanges for appDisabled updates the UI");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ aAddon.userDisabled = true;
+ aAddon.isCompatible = true;
+ aAddon.appDisabled = false;
+
+ open_details("addon1@tests.mozilla.org", "extension", function() {
+ is(get("detail-view").getAttribute("active"), "false", "Addon should not be marked as active");
+ is_element_hidden(get("detail-warning"), "Warning message should not be visible");
+
+ info("Making addon incompatible and appDisabled");
+ aAddon.isCompatible = false;
+ aAddon.appDisabled = true;
+
+ is(get("detail-view").getAttribute("active"), "false", "Addon should not be marked as active");
+ is_element_visible(get("detail-warning"), "Warning message should be visible");
+ is(get("detail-warning").textContent, "Test add-on replacement is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
+
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_discovery.js b/toolkit/mozapps/extensions/test/browser/browser_discovery.js
new file mode 100644
index 000000000..708ba311b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_discovery.js
@@ -0,0 +1,637 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the discovery view loads properly
+
+const MAIN_URL = "https://example.com/" + RELATIVE_DIR + "discovery.html";
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+var gLoadCompleteCallback = null;
+
+var gProgressListener = {
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+ // Only care about the network stop status events
+ if (!(aStateFlags & (Ci.nsIWebProgressListener.STATE_IS_NETWORK)) ||
+ !(aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP)))
+ return;
+
+ if (gLoadCompleteCallback)
+ executeSoon(gLoadCompleteCallback);
+ gLoadCompleteCallback = null;
+ },
+
+ onLocationChange: function() { },
+ onSecurityChange: function() { },
+ onProgressChange: function() { },
+ onStatusChange: function() { },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference]),
+};
+
+function test() {
+ // Switch to a known url
+ Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL);
+ // Temporarily enable caching
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test add-on 1",
+ type: "extension",
+ version: "2.2",
+ isCompatible: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
+ userDisabled: false
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "Test add-on 2",
+ type: "plugin",
+ version: "3.1.5",
+ isCompatible: true,
+ blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
+ userDisabled: false
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "Test add-on 3",
+ type: "theme",
+ version: "1.2b1",
+ isCompatible: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
+ userDisabled: true
+ }]);
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+function getURL(aBrowser) {
+ if (gManagerWindow.document.getElementById("discover-view").selectedPanel !=
+ aBrowser)
+ return null;
+
+ var url = aBrowser.currentURI.spec;
+ var pos = url.indexOf("#");
+ if (pos != -1)
+ return url.substring(0, pos);
+ return url;
+}
+
+function getHash(aBrowser) {
+ if (gManagerWindow.document.getElementById("discover-view").selectedPanel !=
+ aBrowser)
+ return null;
+
+ var url = aBrowser.currentURI.spec;
+ var pos = url.indexOf("#");
+ if (pos != -1)
+ return decodeURIComponent(url.substring(pos + 1));
+ return null;
+}
+
+function testHash(aBrowser, aTestAddonVisible, aCallback) {
+ var hash = getHash(aBrowser);
+ isnot(hash, null, "There should be a hash");
+ try {
+ var data = JSON.parse(hash);
+ }
+ catch (e) {
+ ok(false, "Hash should have been valid JSON: " + e);
+ aCallback();
+ return;
+ }
+ is(typeof data, "object", "Hash should be a JS object");
+
+ // Ensure that at least the test add-ons are present
+ if (aTestAddonVisible[0])
+ ok("addon1@tests.mozilla.org" in data, "Test add-on 1 should be listed");
+ else
+ ok(!("addon1@tests.mozilla.org" in data), "Test add-on 1 should not be listed");
+ if (aTestAddonVisible[1])
+ ok("addon2@tests.mozilla.org" in data, "Test add-on 2 should be listed");
+ else
+ ok(!("addon2@tests.mozilla.org" in data), "Test add-on 2 should not be listed");
+ if (aTestAddonVisible[2])
+ ok("addon3@tests.mozilla.org" in data, "Test add-on 3 should be listed");
+ else
+ ok(!("addon3@tests.mozilla.org" in data), "Test add-on 3 should not be listed");
+
+ // Test against all the add-ons the manager knows about since plugins and
+ // app extensions may exist
+ AddonManager.getAllAddons(function(aAddons) {
+ for (let addon of aAddons) {
+ if (!(addon.id in data)) {
+ // Test add-ons will have shown an error if necessary above
+ if (addon.id.substring(6) != "@tests.mozilla.org")
+ ok(false, "Add-on " + addon.id + " was not included in the data");
+ continue;
+ }
+
+ info("Testing data for add-on " + addon.id);
+ var addonData = data[addon.id];
+ is(addonData.name, addon.name, "Name should be correct");
+ is(addonData.version, addon.version, "Version should be correct");
+ is(addonData.type, addon.type, "Type should be correct");
+ is(addonData.userDisabled, addon.userDisabled, "userDisabled should be correct");
+ is(addonData.isBlocklisted, addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED, "blocklisted should be correct");
+ is(addonData.isCompatible, addon.isCompatible, "isCompatible should be correct");
+ }
+ aCallback();
+ });
+}
+
+function isLoading() {
+ var loading = gManagerWindow.document.getElementById("discover-view").selectedPanel ==
+ gManagerWindow.document.getElementById("discover-loading");
+ if (loading) {
+ is_element_visible(gManagerWindow.document.querySelector("#discover-loading .loading"),
+ "Loading message should be visible when its panel is the selected panel");
+ }
+ return loading;
+}
+
+function isError() {
+ return gManagerWindow.document.getElementById("discover-view").selectedPanel ==
+ gManagerWindow.document.getElementById("discover-error");
+}
+
+function clickLink(aId, aCallback) {
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ browser.addProgressListener(gProgressListener);
+
+ gLoadCompleteCallback = function() {
+ browser.removeProgressListener(gProgressListener);
+ aCallback();
+ };
+
+ var link = browser.contentDocument.getElementById(aId);
+ EventUtils.sendMouseEvent({type: "click"}, link);
+
+ executeSoon(function() {
+ ok(isLoading(), "Clicking a link should show the loading pane");
+ });
+}
+
+// Tests that switching to the discovery view displays the right url
+add_test(function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ gCategoryUtilities.openType("discover", function() {
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ testHash(browser, [true, true, true], function() {
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+
+ ok(isLoading(), "Should be loading at first");
+ });
+});
+
+// Tests that loading the add-ons manager with the discovery view as the last
+// selected view displays the right url
+add_test(function() {
+ // Hide one of the test add-ons
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", false);
+ Services.prefs.setBoolPref("extensions.addon3@tests.mozilla.org.getAddons.cache.enabled", true);
+
+ open_manager(null, function(aWindow) {
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ testHash(browser, [true, false, true], function() {
+ close_manager(gManagerWindow, run_next_test);
+ });
+ }, function(aWindow) {
+ gManagerWindow = aWindow;
+ ok(isLoading(), "Should be loading at first");
+ });
+});
+
+// Tests that loading the add-ons manager with the discovery view as the initial
+// view displays the right url
+add_test(function() {
+ Services.prefs.clearUserPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled");
+ Services.prefs.setBoolPref("extensions.addon3@tests.mozilla.org.getAddons.cache.enabled", false);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ gCategoryUtilities.openType("extension", function() {
+ close_manager(gManagerWindow, function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ testHash(browser, [true, true, false], function() {
+ Services.prefs.clearUserPref("extensions.addon3@tests.mozilla.org.getAddons.cache.enabled");
+ close_manager(gManagerWindow, run_next_test);
+ });
+ }, function(aWindow) {
+ gManagerWindow = aWindow;
+ ok(isLoading(), "Should be loading at first");
+ });
+ });
+ });
+ });
+});
+
+// Tests that switching to the discovery view displays the right url
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ gCategoryUtilities.openType("discover", function() {
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ is(getHash(browser), null, "Hash should not have been passed");
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+});
+
+// Tests that loading the add-ons manager with the discovery view as the last
+// selected view displays the right url
+add_test(function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ is(getHash(browser), null, "Hash should not have been passed");
+ close_manager(gManagerWindow, run_next_test);
+ });
+});
+
+// Tests that loading the add-ons manager with the discovery view as the initial
+// view displays the right url
+add_test(function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ gCategoryUtilities.openType("extension", function() {
+ close_manager(gManagerWindow, function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ is(getHash(browser), null, "Hash should not have been passed");
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Tests that navigating to an insecure page fails
+add_test(function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ clickLink("link-http", function() {
+ ok(isError(), "Should have shown the error page");
+
+ gCategoryUtilities.openType("extension", function() {
+ gCategoryUtilities.openType("discover", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ ok(isLoading(), "Should start loading again");
+ });
+ });
+ });
+});
+
+// Tests that navigating to a different domain fails
+add_test(function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ clickLink("link-domain", function() {
+ ok(isError(), "Should have shown the error page");
+
+ gCategoryUtilities.openType("extension", function() {
+ gCategoryUtilities.openType("discover", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ ok(isLoading(), "Should start loading again");
+ });
+ });
+ });
+});
+
+// Tests that navigating to a missing page fails
+add_test(function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ clickLink("link-bad", function() {
+ ok(isError(), "Should have shown the error page");
+
+ gCategoryUtilities.openType("extension", function() {
+ gCategoryUtilities.openType("discover", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ ok(isLoading(), "Should start loading again");
+ });
+ });
+ });
+});
+
+// Tests that navigating to a page on the same domain works
+add_test(function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ clickLink("link-good", function() {
+ is(getURL(browser), "https://example.com/" + RELATIVE_DIR + "releaseNotes.xhtml", "Should have loaded the right url");
+
+ gCategoryUtilities.openType("extension", function() {
+ gCategoryUtilities.openType("discover", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Tests repeated navigation to the same page followed by a navigation to a
+// different domain
+add_test(function() {
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ var count = 10;
+ function clickAgain(aCallback) {
+ if (count-- == 0)
+ aCallback();
+ else
+ clickLink("link-normal", clickAgain.bind(null, aCallback));
+ }
+
+ clickAgain(function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ clickLink("link-domain", function() {
+ ok(isError(), "Should have shown the error page");
+
+ gCategoryUtilities.openType("extension", function() {
+ gCategoryUtilities.openType("discover", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ ok(isLoading(), "Should start loading again");
+ });
+ });
+ });
+ });
+});
+
+// Loading an insecure main page should work if that is what the prefs say, should
+// also be able to navigate to a https page and back again
+add_test(function() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, TESTROOT + "discovery.html");
+
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), TESTROOT + "discovery.html", "Should have loaded the right url");
+
+ clickLink("link-normal", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ clickLink("link-http", function() {
+ is(getURL(browser), TESTROOT + "discovery.html", "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+});
+
+// Stopping the initial load should display the error page and then correctly
+// reload when switching away and back again
+add_test(function() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+
+ EventUtils.synthesizeMouse(gCategoryUtilities.get("discover"), 2, 2, { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ ok(isError(), "Should have shown the error page");
+
+ gCategoryUtilities.openType("extension", function() {
+ EventUtils.synthesizeMouse(gCategoryUtilities.get("discover"), 2, 2, { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ ok(isError(), "Should have shown the error page");
+
+ gCategoryUtilities.openType("extension", function() {
+ gCategoryUtilities.openType("discover", function() {
+ is(getURL(browser), MAIN_URL, "Should have loaded the right url");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+
+ ok(isLoading(), "Should be loading");
+ // This will stop the real page load
+ browser.stop();
+ });
+ });
+
+ ok(isLoading(), "Should be loading");
+ // This will actually stop the about:blank load
+ browser.stop();
+ });
+});
+
+// Test for Bug 703929 - Loading the discover view from a chrome XUL file fails when
+// the add-on manager is reopened.
+add_test(function() {
+ const url = "chrome://mochitests/content/" + RELATIVE_DIR + "addon_about.xul";
+ Services.prefs.setCharPref(PREF_DISCOVERURL, url);
+
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), url, "Loading a chrome XUL file should work");
+
+ restart_manager(gManagerWindow, "addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), url, "Should be able to load the chrome XUL file a second time");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+});
+
+// Bug 711693 - Send the compatibility mode when loading the Discovery pane
+add_test(function() {
+ info("Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'normal'");
+ Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL + "?mode=%COMPATIBILITY_MODE%");
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, false);
+
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL + "?mode=normal", "Should have loaded the right url");
+ close_manager(gManagerWindow, run_next_test);
+ });
+});
+
+add_test(function() {
+ info("Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'strict'");
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL + "?mode=strict", "Should have loaded the right url");
+ close_manager(gManagerWindow, run_next_test);
+ });
+});
+
+add_test(function() {
+ info("Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'ignore'");
+ Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, false);
+
+ open_manager("addons://discover/", function(aWindow) {
+ gManagerWindow = aWindow;
+ var browser = gManagerWindow.document.getElementById("discover-browser");
+ is(getURL(browser), MAIN_URL + "?mode=ignore", "Should have loaded the right url");
+ close_manager(gManagerWindow, run_next_test);
+ });
+});
+
+// Test for Bug 601442 - extensions.getAddons.showPane need to be update
+// for the new addon manager.
+function bug_601442_test_elements(visible) {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ if(visible)
+ ok(gCategoryUtilities.isTypeVisible("discover"), "Discover category should be visible");
+ else
+ ok(!gCategoryUtilities.isTypeVisible("discover"), "Discover category should not be visible");
+
+ gManagerWindow.loadView("addons://list/dictionary");
+ wait_for_view_load(gManagerWindow, function(aManager) {
+ var button = aManager.document.getElementById("discover-button-install");
+ if(visible)
+ ok(!is_hidden(button), "Discover button should be visible!");
+ else
+ ok(is_hidden(button), "Discover button should not be visible!");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+}
+
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_DISCOVER_ENABLED, false);
+ Services.prefs.setBoolPref(PREF_XPI_ENABLED, true);
+ bug_601442_test_elements(false);
+});
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_DISCOVER_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_XPI_ENABLED, false);
+ bug_601442_test_elements(false);
+});
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_DISCOVER_ENABLED, false);
+ Services.prefs.setBoolPref(PREF_XPI_ENABLED, false);
+ bug_601442_test_elements(false);
+});
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_DISCOVER_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_XPI_ENABLED, true);
+ bug_601442_test_elements(true);
+});
+
+// Test for Bug 1132971 - if extensions.getAddons.showPane is false,
+// the extensions pane should show by default
+add_test(function() {
+ Services.prefs.clearUserPref(PREF_UI_LASTCATEGORY);
+ Services.prefs.setBoolPref(PREF_DISCOVER_ENABLED, false);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be showing the extension view");
+ close_manager(gManagerWindow, run_next_test);
+ Services.prefs.clearUserPref(PREF_DISCOVER_ENABLED);
+ });
+}); \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/browser/browser_discovery_install.js b/toolkit/mozapps/extensions/test/browser/browser_discovery_install.js
new file mode 100644
index 000000000..bd7d194f2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_discovery_install.js
@@ -0,0 +1,130 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the discovery view can install add-ons correctly
+
+const MAIN_URL = "https://test1.example.com/" + RELATIVE_DIR + "discovery_install.html";
+const GOOD_FRAMED_URL = "https://test1.example.com/" + RELATIVE_DIR + "discovery_frame.html";
+const BAD_FRAMED_URL = "https://test2.example.com/" + RELATIVE_DIR + "discovery_frame.html";
+
+// Temporarily enable caching
+Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+// Allow SSL from non-built-in certs
+Services.prefs.setBoolPref("extensions.install.requireBuiltInCerts", false);
+// Allow installs from the test site
+Services.perms.add(NetUtil.newURI("https://test1.example.com/"), "install",
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+Services.perms.add(NetUtil.newURI("https://test2.example.com/"), "install",
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+
+registerCleanupFunction(() => {
+ Services.perms.remove("test1.example.com", "install");
+ Services.perms.remove("test2.example.com", "install");
+});
+
+function clickLink(frameLoader, id) {
+ let link = frameLoader.contentDocument.getElementById(id);
+ EventUtils.sendMouseEvent({type: "click"}, link);
+}
+
+function waitForInstall() {
+ return new Promise(resolve => {
+ wait_for_window_open((window) => {
+ is(window.location, "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul",
+ "Should have seen the install window");
+ window.document.documentElement.cancelDialog();
+ resolve();
+ });
+ });
+}
+
+function waitForFail() {
+ return new Promise(resolve => {
+ let listener = (subject, topic, data) => {
+ Services.obs.removeObserver(listener, topic);
+ resolve();
+ }
+ Services.obs.addObserver(listener, "addon-install-origin-blocked", false);
+ });
+}
+
+// Tests that navigating to an XPI attempts to install correctly
+add_task(function* test_install_direct() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL);
+
+ let managerWindow = yield open_manager("addons://discover/");
+ let browser = managerWindow.document.getElementById("discover-browser");
+
+ clickLink(browser, "install-direct");
+ yield waitForInstall();
+
+ yield close_manager(managerWindow);
+});
+
+// Tests that installing via JS works correctly
+add_task(function* test_install_js() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL);
+
+ let managerWindow = yield open_manager("addons://discover/");
+ let browser = managerWindow.document.getElementById("discover-browser");
+
+ clickLink(browser, "install-js");
+ yield waitForInstall();
+
+ yield close_manager(managerWindow);
+});
+
+// Installing from an inner-frame of the same origin should work
+add_task(function* test_install_inner_direct() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, GOOD_FRAMED_URL);
+
+ let managerWindow = yield open_manager("addons://discover/");
+ let browser = managerWindow.document.getElementById("discover-browser");
+ let frame = browser.contentDocument.getElementById("frame");
+
+ clickLink(frame, "install-direct");
+ yield waitForInstall();
+
+ yield close_manager(managerWindow);
+});
+
+add_task(function* test_install_inner_js() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, GOOD_FRAMED_URL);
+
+ let managerWindow = yield open_manager("addons://discover/");
+ let browser = managerWindow.document.getElementById("discover-browser");
+ let frame = browser.contentDocument.getElementById("frame");
+
+ clickLink(frame, "install-js");
+ yield waitForInstall();
+
+ yield close_manager(managerWindow);
+});
+
+// Installing from an inner-frame of a different origin should fail
+add_task(function* test_install_xorigin_direct() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, BAD_FRAMED_URL);
+
+ let managerWindow = yield open_manager("addons://discover/");
+ let browser = managerWindow.document.getElementById("discover-browser");
+ let frame = browser.contentDocument.getElementById("frame");
+
+ clickLink(frame, "install-direct");
+ yield waitForFail();
+
+ yield close_manager(managerWindow);
+});
+
+add_task(function* test_install_xorigin_js() {
+ Services.prefs.setCharPref(PREF_DISCOVERURL, BAD_FRAMED_URL);
+
+ let managerWindow = yield open_manager("addons://discover/");
+ let browser = managerWindow.document.getElementById("discover-browser");
+ let frame = browser.contentDocument.getElementById("frame");
+
+ clickLink(frame, "install-js");
+ yield waitForFail();
+
+ yield close_manager(managerWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
new file mode 100644
index 000000000..1df288323
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
@@ -0,0 +1,234 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests simulated drag and drop of files into the add-ons manager.
+// We test with the add-ons manager in its own tab if in Firefox otherwise
+// in its own window.
+// Tests are only simulations of the drag and drop events, we cannot really do
+// this automatically.
+
+// Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests,
+// we only need ChromeUtils.js for a few files which is why we are using loadSubScript.
+var gManagerWindow;
+var ChromeUtils = {};
+this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ getService(Ci.mozIJSSubScriptLoader);
+this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
+
+// This listens for the next opened window and checks it is of the right url.
+// opencallback is called when the new window is fully loaded
+// closecallback is called when the window is closed
+function WindowOpenListener(url, opencallback, closecallback) {
+ this.url = url;
+ this.opencallback = opencallback;
+ this.closecallback = closecallback;
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+ wm.addListener(this);
+}
+
+WindowOpenListener.prototype = {
+ url: null,
+ opencallback: null,
+ closecallback: null,
+ window: null,
+ domwindow: null,
+
+ handleEvent: function(event) {
+ is(this.domwindow.document.location.href, this.url, "Should have opened the correct window");
+
+ this.domwindow.removeEventListener("load", this, false);
+ // Allow any other load handlers to execute
+ var self = this;
+ executeSoon(function() { self.opencallback(self.domwindow); } );
+ },
+
+ onWindowTitleChange: function(window, title) {
+ },
+
+ onOpenWindow: function(window) {
+ if (this.window)
+ return;
+
+ this.window = window;
+ this.domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindow);
+ this.domwindow.addEventListener("load", this, false);
+ },
+
+ onCloseWindow: function(window) {
+ if (this.window != window)
+ return;
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+ wm.removeListener(this);
+ this.opencallback = null;
+ this.window = null;
+ this.domwindow = null;
+
+ // Let the window close complete
+ executeSoon(this.closecallback);
+ this.closecallback = null;
+ }
+};
+
+var gSawInstallNotification = false;
+var gInstallNotificationObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
+ if (gTestInWindow)
+ is(installInfo.browser, null, "Notification should have a null browser");
+ else
+ isnot(installInfo.browser, null, "Notification should have non-null browser");
+ gSawInstallNotification = true;
+ Services.obs.removeObserver(this, "addon-install-started");
+ }
+};
+
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+function test_confirmation(aWindow, aExpectedURLs) {
+ var list = aWindow.document.getElementById("itemList");
+ is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
+
+ for (let url of aExpectedURLs) {
+ let found = false;
+ for (let node of list.children) {
+ if (node.url == url) {
+ found = true;
+ break;
+ }
+ }
+ ok(found, "Should have seen " + url + " in the list");
+ }
+
+ aWindow.document.documentElement.cancelDialog();
+}
+
+// Simulates dropping a URL onto the manager
+add_test(function() {
+ var url = TESTROOT + "addons/browser_dragdrop1.xpi";
+
+ Services.obs.addObserver(gInstallNotificationObserver,
+ "addon-install-started", false);
+
+ new WindowOpenListener(INSTALL_URI, function(aWindow) {
+ test_confirmation(aWindow, [url]);
+ }, function() {
+ is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+ run_next_test();
+ });
+
+ var viewContainer = gManagerWindow.document.getElementById("view-port");
+ var effect = ChromeUtils.synthesizeDrop(viewContainer, viewContainer,
+ [[{type: "text/x-moz-url", data: url}]],
+ "copy", gManagerWindow);
+ is(effect, "copy", "Drag should be accepted");
+});
+
+// Simulates dropping a file onto the manager
+add_test(function() {
+ var fileurl = get_addon_file_url("browser_dragdrop1.xpi");
+
+ Services.obs.addObserver(gInstallNotificationObserver,
+ "addon-install-started", false);
+
+ new WindowOpenListener(INSTALL_URI, function(aWindow) {
+ test_confirmation(aWindow, [fileurl.spec]);
+ }, function() {
+ is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+ run_next_test();
+ });
+
+ var viewContainer = gManagerWindow.document.getElementById("view-port");
+ var effect = ChromeUtils.synthesizeDrop(viewContainer, viewContainer,
+ [[{type: "application/x-moz-file", data: fileurl.file}]],
+ "copy", gManagerWindow);
+ is(effect, "copy", "Drag should be accepted");
+});
+
+// Simulates dropping two urls onto the manager
+add_test(function() {
+ var url1 = TESTROOT + "addons/browser_dragdrop1.xpi";
+ var url2 = TESTROOT2 + "addons/browser_dragdrop2.xpi";
+
+ Services.obs.addObserver(gInstallNotificationObserver,
+ "addon-install-started", false);
+
+ new WindowOpenListener(INSTALL_URI, function(aWindow) {
+ test_confirmation(aWindow, [url1, url2]);
+ }, function() {
+ is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+ run_next_test();
+ });
+
+ var viewContainer = gManagerWindow.document.getElementById("view-port");
+ var effect = ChromeUtils.synthesizeDrop(viewContainer, viewContainer,
+ [[{type: "text/x-moz-url", data: url1}],
+ [{type: "text/x-moz-url", data: url2}]],
+ "copy", gManagerWindow);
+ is(effect, "copy", "Drag should be accepted");
+});
+
+// Simulates dropping two files onto the manager
+add_test(function() {
+ var fileurl1 = get_addon_file_url("browser_dragdrop1.xpi");
+ var fileurl2 = get_addon_file_url("browser_dragdrop2.xpi");
+
+ Services.obs.addObserver(gInstallNotificationObserver,
+ "addon-install-started", false);
+
+ new WindowOpenListener(INSTALL_URI, function(aWindow) {
+ test_confirmation(aWindow, [fileurl1.spec, fileurl2.spec]);
+ }, function() {
+ is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+ run_next_test();
+ });
+
+ var viewContainer = gManagerWindow.document.getElementById("view-port");
+ var effect = ChromeUtils.synthesizeDrop(viewContainer, viewContainer,
+ [[{type: "application/x-moz-file", data: fileurl1.file}],
+ [{type: "application/x-moz-file", data: fileurl2.file}]],
+ "copy", gManagerWindow);
+ is(effect, "copy", "Drag should be accepted");
+});
+
+// Simulates dropping a file and a url onto the manager (weird, but should still work)
+add_test(function() {
+ var url = TESTROOT + "addons/browser_dragdrop1.xpi";
+ var fileurl = get_addon_file_url("browser_dragdrop2.xpi");
+
+ Services.obs.addObserver(gInstallNotificationObserver,
+ "addon-install-started", false);
+
+ new WindowOpenListener(INSTALL_URI, function(aWindow) {
+ test_confirmation(aWindow, [url, fileurl.spec]);
+ }, function() {
+ is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+ run_next_test();
+ });
+
+ var viewContainer = gManagerWindow.document.getElementById("view-port");
+ var effect = ChromeUtils.synthesizeDrop(viewContainer, viewContainer,
+ [[{type: "text/x-moz-url", data: url}],
+ [{type: "application/x-moz-file", data: fileurl.file}]],
+ "copy", gManagerWindow);
+ is(effect, "copy", "Drag should be accepted");
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_eula.js b/toolkit/mozapps/extensions/test/browser/browser_eula.js
new file mode 100644
index 000000000..befe9f1f2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_eula.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the eula is shown correctly for search results
+
+var gManagerWindow;
+var gCategoryUtilities;
+
+var gApp = document.getElementById("bundle_brand").getString("brandShortName");
+var gSearchCount = 0;
+
+function test() {
+ requestLongerTimeout(2);
+ waitForExplicitFinish();
+
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+ Services.prefs.setCharPref("extensions.getAddons.search.url", TESTROOT + "browser_eula.xml");
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, finish);
+}
+
+function get_node(parent, anonid) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
+}
+
+function installSearchResult(aCallback) {
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ // Search for something different each time
+ searchBox.value = "foo" + gSearchCount;
+ gSearchCount++;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ let remote = gManagerWindow.document.getElementById("search-filter-remote")
+ EventUtils.synthesizeMouseAtCenter(remote, { }, gManagerWindow);
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should see the search result in the list");
+
+ let status = get_node(item, "install-status");
+ EventUtils.synthesizeMouseAtCenter(get_node(status, "install-remote-btn"), {}, gManagerWindow);
+
+ item.mInstall.addListener({
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ });
+}
+
+// Install an add-on through the search page, accept the EULA and then undo it
+add_test(function() {
+ // Accept the EULA when it appears
+ let sawEULA = false;
+ wait_for_window_open(function(aWindow) {
+ sawEULA = true;
+ is(aWindow.location.href, "chrome://mozapps/content/extensions/eula.xul", "Window opened should be correct");
+ is(aWindow.document.getElementById("eula").value, "This is the EULA for this add-on", "EULA should be correct");
+
+ aWindow.document.documentElement.acceptDialog();
+ });
+
+ installSearchResult(function() {
+ ok(sawEULA, "Should have seen the EULA");
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ is(aInstalls.length, 1, "Should be one pending install");
+ aInstalls[0].cancel();
+
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_eula.xml b/toolkit/mozapps/extensions/test/browser/browser_eula.xml
new file mode 100644
index 000000000..87b5997cf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_eula.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <addon>
+ <name>Install Tests</name>
+ <type id='1'>Extension</type>
+ <guid>addon1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test add-on</summary>
+ <description>Test add-on</description>
+ <eula>This is the EULA for this add-on</eula>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="2">http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_experiments.js b/toolkit/mozapps/extensions/test/browser/browser_experiments.js
new file mode 100644
index 000000000..72d0ca83e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_experiments.js
@@ -0,0 +1,645 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/Promise.jsm", this);
+
+let {AddonTestUtils} = Components.utils.import("resource://testing-common/AddonManagerTesting.jsm", {});
+let {HttpServer} = Components.utils.import("resource://testing-common/httpd.js", {});
+
+let gManagerWindow;
+let gCategoryUtilities;
+let gExperiments;
+let gHttpServer;
+
+let gSavedManifestURI;
+let gIsEnUsLocale;
+
+const SEC_IN_ONE_DAY = 24 * 60 * 60;
+const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
+
+function getExperimentAddons() {
+ let deferred = Promise.defer();
+ AddonManager.getAddonsByTypes(["experiment"], (addons) => {
+ deferred.resolve(addons);
+ });
+ return deferred.promise;
+}
+
+function getInstallItem() {
+ let doc = gManagerWindow.document;
+ let view = doc.getElementById("view-port").selectedPanel;
+ let list = doc.getElementById("addon-list");
+
+ let node = list.firstChild;
+ while (node) {
+ if (node.getAttribute("status") == "installing") {
+ return node;
+ }
+ node = node.nextSibling;
+ }
+
+ return null;
+}
+
+function patchPolicy(policy, data) {
+ for (let key of Object.keys(data)) {
+ Object.defineProperty(policy, key, {
+ value: data[key],
+ writable: true,
+ });
+ }
+}
+
+function defineNow(policy, time) {
+ patchPolicy(policy, { now: () => new Date(time) });
+}
+
+function openDetailsView(aId) {
+ let item = get_addon_element(gManagerWindow, aId);
+ Assert.ok(item, "Should have got add-on element.");
+ is_element_visible(item, "Add-on element should be visible.");
+
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+
+ let deferred = Promise.defer();
+ wait_for_view_load(gManagerWindow, deferred.resolve);
+ return deferred.promise;
+}
+
+function clickRemoveButton(addonElement) {
+ let btn = gManagerWindow.document.getAnonymousElementByAttribute(addonElement, "anonid", "remove-btn");
+ if (!btn) {
+ return Promise.reject();
+ }
+
+ EventUtils.synthesizeMouseAtCenter(btn, { clickCount: 1 }, gManagerWindow);
+ let deferred = Promise.defer();
+ setTimeout(deferred.resolve, 0);
+ return deferred;
+}
+
+function clickUndoButton(addonElement) {
+ let btn = gManagerWindow.document.getAnonymousElementByAttribute(addonElement, "anonid", "undo-btn");
+ if (!btn) {
+ return Promise.reject();
+ }
+
+ EventUtils.synthesizeMouseAtCenter(btn, { clickCount: 1 }, gManagerWindow);
+ let deferred = Promise.defer();
+ setTimeout(deferred.resolve, 0);
+ return deferred;
+}
+
+add_task(function* initializeState() {
+ gManagerWindow = yield open_manager();
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("experiments.enabled");
+ if (gHttpServer) {
+ gHttpServer.stop(() => {});
+ if (gSavedManifestURI !== undefined) {
+ Services.prefs.setCharPref("experments.manifest.uri", gSavedManifestURI);
+ }
+ }
+ if (gExperiments) {
+ let tmp = {};
+ Cu.import("resource:///modules/experiments/Experiments.jsm", tmp);
+ gExperiments._policy = new tmp.Experiments.Policy();
+ }
+ });
+
+ let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
+ gIsEnUsLocale = chrome.getSelectedLocale("global") == "en-US";
+
+ // The Experiments Manager will interfere with us by preventing installs
+ // of experiments it doesn't know about. We remove it from the equation
+ // because here we are only concerned with core Addon Manager operation,
+ // not the superset Experiments Manager has imposed.
+ if ("@mozilla.org/browser/experiments-service;1" in Components.classes) {
+ let tmp = {};
+ Cu.import("resource:///modules/experiments/Experiments.jsm", tmp);
+ // There is a race condition between XPCOM service initialization and
+ // this test running. We have to initialize the instance first, then
+ // uninitialize it to prevent this.
+ gExperiments = tmp.Experiments.instance();
+ yield gExperiments._mainTask;
+ yield gExperiments.uninit();
+ }
+});
+
+// On an empty profile with no experiments, the experiment category
+// should be hidden.
+add_task(function* testInitialState() {
+ Assert.ok(gCategoryUtilities.get("experiment", false), "Experiment tab is defined.");
+ Assert.ok(!gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab hidden by default.");
+});
+
+add_task(function* testExperimentInfoNotVisible() {
+ yield gCategoryUtilities.openType("extension");
+ let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
+ is_element_hidden(el, "Experiment info not visible on other types.");
+});
+
+// If we have an active experiment, we should see the experiments tab
+// and that tab should have some messages.
+add_task(function* testActiveExperiment() {
+ let addon = yield install_addon("addons/browser_experiment1.xpi");
+
+ Assert.ok(addon.userDisabled, "Add-on is disabled upon initial install.");
+ Assert.equal(addon.isActive, false, "Add-on is not active.");
+
+ Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
+
+ yield gCategoryUtilities.openType("experiment");
+ let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
+ is_element_visible(el, "Experiment info is visible on experiment tab.");
+});
+
+add_task(function* testExperimentLearnMore() {
+ // Actual URL is irrelevant.
+ Services.prefs.setCharPref("toolkit.telemetry.infoURL",
+ "http://mochi.test:8888/server.js");
+
+ yield gCategoryUtilities.openType("experiment");
+ let btn = gManagerWindow.document.getElementById("experiments-learn-more");
+
+ if (!gUseInContentUI) {
+ is_element_hidden(btn, "Learn more button hidden if not using in-content UI.");
+ Services.prefs.clearUserPref("toolkit.telemetry.infoURL");
+
+ return;
+ }
+
+ is_element_visible(btn, "Learn more button visible.");
+
+ let deferred = Promise.defer();
+ window.addEventListener("DOMContentLoaded", function onLoad(event) {
+ info("Telemetry privacy policy window opened.");
+ window.removeEventListener("DOMContentLoaded", onLoad, false);
+
+ let browser = gBrowser.selectedBrowser;
+ let expected = Services.prefs.getCharPref("toolkit.telemetry.infoURL");
+ Assert.equal(browser.currentURI.spec, expected, "New tab should have loaded privacy policy.");
+ browser.contentWindow.close();
+
+ Services.prefs.clearUserPref("toolkit.telemetry.infoURL");
+
+ deferred.resolve();
+ }, false);
+
+ info("Opening telemetry privacy policy.");
+ EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
+
+ yield deferred.promise;
+});
+
+add_task(function* testOpenPreferences() {
+ yield gCategoryUtilities.openType("experiment");
+ let btn = gManagerWindow.document.getElementById("experiments-change-telemetry");
+ if (!gUseInContentUI) {
+ is_element_hidden(btn, "Change telemetry button not enabled in out of window UI.");
+ info("Skipping preferences open test because not using in-content UI.");
+ return;
+ }
+
+ is_element_visible(btn, "Change telemetry button visible in in-content UI.");
+
+ let deferred = Promise.defer();
+ Services.obs.addObserver(function observer(prefWin, topic, data) {
+ Services.obs.removeObserver(observer, "advanced-pane-loaded");
+ info("Advanced preference pane opened.");
+ executeSoon(function() {
+ // We want this test to fail if the preferences pane changes.
+ let el = prefWin.document.getElementById("dataChoicesPanel");
+ is_element_visible(el);
+
+ prefWin.close();
+ info("Closed preferences pane.");
+
+ deferred.resolve();
+ });
+ }, "advanced-pane-loaded", false);
+
+ info("Loading preferences pane.");
+ EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
+
+ yield deferred.promise;
+});
+
+add_task(function* testButtonPresence() {
+ yield gCategoryUtilities.openType("experiment");
+ let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
+ Assert.ok(item, "Got add-on element.");
+ item.parentNode.ensureElementIsVisible(item);
+
+ let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ // Corresponds to the uninstall permission.
+ is_element_visible(el, "Remove button is visible.");
+ // Corresponds to lack of disable permission.
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
+ is_element_hidden(el, "Disable button not visible.");
+ // Corresponds to lack of enable permission.
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
+ is_element_hidden(el, "Enable button not visible.");
+});
+
+// Remove the add-on we've been testing with.
+add_task(function* testCleanup() {
+ yield AddonTestUtils.uninstallAddonByID("test-experiment1@experiments.mozilla.org");
+ // Verify some conditions, just in case.
+ let addons = yield getExperimentAddons();
+ Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
+});
+
+// The following tests should ideally live in browser/experiments/. However,
+// they rely on some of the helper functions from head.js, which can't easily
+// be consumed from other directories. So, they live here.
+
+add_task(function* testActivateExperiment() {
+ if (!gExperiments) {
+ info("Skipping experiments test because that feature isn't available.");
+ return;
+ }
+
+ gHttpServer = new HttpServer();
+ gHttpServer.start(-1);
+ let root = "http://localhost:" + gHttpServer.identity.primaryPort + "/";
+ gHttpServer.registerPathHandler("/manifest", (request, response) => {
+ response.setStatusLine(null, 200, "OK");
+ response.write(JSON.stringify({
+ "version": 1,
+ "experiments": [
+ {
+ id: "experiment-1",
+ xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
+ xpiHash: "IRRELEVANT",
+ startTime: Date.now() / 1000 - 3600,
+ endTime: Date.now() / 1000 + 3600,
+ maxActiveSeconds: 600,
+ appName: [Services.appinfo.name],
+ channel: [gExperiments._policy.updatechannel()],
+ },
+ ],
+ }));
+ response.processAsync();
+ response.finish();
+ });
+
+ gSavedManifestURI = Services.prefs.getCharPref("experiments.manifest.uri");
+ Services.prefs.setCharPref("experiments.manifest.uri", root + "manifest");
+
+ // We need to remove the cache file to help ensure consistent state.
+ yield OS.File.remove(gExperiments._cacheFilePath);
+
+ Services.prefs.setBoolPref("experiments.enabled", true);
+
+ info("Initializing experiments service.");
+ yield gExperiments.init();
+ info("Experiments service finished first run.");
+
+ // Check conditions, just to be sure.
+ let experiments = yield gExperiments.getExperiments();
+ Assert.equal(experiments.length, 0, "No experiments known to the service.");
+
+ // This makes testing easier.
+ gExperiments._policy.ignoreHashes = true;
+
+ info("Manually updating experiments manifest.");
+ yield gExperiments.updateManifest();
+ info("Experiments update complete.");
+
+ let deferred = Promise.defer();
+ gHttpServer.stop(() => {
+ gHttpServer = null;
+
+ info("getting experiment by ID");
+ AddonManager.getAddonByID("test-experiment1@experiments.mozilla.org", (addon) => {
+ Assert.ok(addon, "Add-on installed via Experiments manager.");
+
+ deferred.resolve();
+ });
+ });
+
+ yield deferred.promise;
+
+ Assert.ok(gCategoryUtilities.isTypeVisible, "experiment", "Experiment tab visible.");
+ yield gCategoryUtilities.openType("experiment");
+ let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
+ is_element_visible(el, "Experiment info is visible on experiment tab.");
+});
+
+add_task(function testDeactivateExperiment() {
+ if (!gExperiments) {
+ return;
+ }
+
+ // Fake an empty manifest to purge data from previous manifest.
+ yield gExperiments._updateExperiments({
+ "version": 1,
+ "experiments": [],
+ });
+
+ yield gExperiments.disableExperiment("testing");
+
+ // We should have a record of the previously-active experiment.
+ let experiments = yield gExperiments.getExperiments();
+ Assert.equal(experiments.length, 1, "1 experiment is known.");
+ Assert.equal(experiments[0].active, false, "Experiment is not active.");
+
+ // We should have a previous experiment in the add-ons manager.
+ let deferred = Promise.defer();
+ AddonManager.getAddonsByTypes(["experiment"], (addons) => {
+ deferred.resolve(addons);
+ });
+ let addons = yield deferred.promise;
+ Assert.equal(addons.length, 1, "1 experiment add-on known.");
+ Assert.ok(addons[0].appDisabled, "It is a previous experiment.");
+ Assert.equal(addons[0].id, "experiment-1", "Add-on ID matches expected.");
+
+ // Verify the UI looks sane.
+
+ Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
+ let item = get_addon_element(gManagerWindow, "experiment-1");
+ Assert.ok(item, "Got add-on element.");
+ Assert.ok(!item.active, "Element should not be active.");
+ item.parentNode.ensureElementIsVisible(item);
+
+ // User control buttons should not be present because previous experiments
+ // should have no permissions.
+ let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ is_element_hidden(el, "Remove button is not visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
+ is_element_hidden(el, "Disable button is not visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
+ is_element_hidden(el, "Enable button is not visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
+ is_element_hidden(el, "Preferences button is not visible.");
+});
+
+add_task(function testActivateRealExperiments() {
+ if (!gExperiments) {
+ info("Skipping experiments test because that feature isn't available.");
+ return;
+ }
+
+ yield gExperiments._updateExperiments({
+ "version": 1,
+ "experiments": [
+ {
+ id: "experiment-2",
+ xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
+ xpiHash: "IRRELEVANT",
+ startTime: Date.now() / 1000 - 3600,
+ endTime: Date.now() / 1000 + 3600,
+ maxActiveSeconds: 600,
+ appName: [Services.appinfo.name],
+ channel: [gExperiments._policy.updatechannel()],
+ },
+ ],
+ });
+ yield gExperiments._run();
+
+ // Check the active experiment.
+
+ let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
+ Assert.ok(item, "Got add-on element.");
+ item.parentNode.ensureElementIsVisible(item);
+
+ let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
+ is_element_visible(el, "Experiment state label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Active");
+ }
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
+ is_element_visible(el, "Experiment time label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Less than a day remaining");
+ }
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "error-container");
+ is_element_hidden(el, "error-container should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning-container");
+ is_element_hidden(el, "warning-container should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "pending-container");
+ is_element_hidden(el, "pending-container should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "version");
+ is_element_hidden(el, "version should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
+ is_element_hidden(el, "disabled-postfix should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "update-postfix");
+ is_element_hidden(el, "update-postfix should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "experiment-bullet");
+ is_element_visible(el, "experiment-bullet should be visible.");
+
+ // Check the previous experiment.
+
+ item = get_addon_element(gManagerWindow, "experiment-1");
+ Assert.ok(item, "Got add-on element.");
+ item.parentNode.ensureElementIsVisible(item);
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
+ is_element_visible(el, "Experiment state label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Complete");
+ }
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
+ is_element_visible(el, "Experiment time label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Less than a day ago");
+ }
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "error-container");
+ is_element_hidden(el, "error-container should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning-container");
+ is_element_hidden(el, "warning-container should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "pending-container");
+ is_element_hidden(el, "pending-container should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "version");
+ is_element_hidden(el, "version should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
+ is_element_hidden(el, "disabled-postfix should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "update-postfix");
+ is_element_hidden(el, "update-postfix should be hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "experiment-bullet");
+ is_element_visible(el, "experiment-bullet should be visible.");
+
+ // Install an "older" experiment.
+
+ yield gExperiments.disableExperiment("experiment-2");
+
+ let now = Date.now();
+ let fakeNow = now - 5 * MS_IN_ONE_DAY;
+ defineNow(gExperiments._policy, fakeNow);
+
+ yield gExperiments._updateExperiments({
+ "version": 1,
+ "experiments": [
+ {
+ id: "experiment-3",
+ xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
+ xpiHash: "IRRELEVANT",
+ startTime: fakeNow / 1000 - SEC_IN_ONE_DAY,
+ endTime: now / 1000 + 10 * SEC_IN_ONE_DAY,
+ maxActiveSeconds: 100 * SEC_IN_ONE_DAY,
+ appName: [Services.appinfo.name],
+ channel: [gExperiments._policy.updatechannel()],
+ },
+ ],
+ });
+ yield gExperiments._run();
+
+ // Check the active experiment.
+
+ item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
+ Assert.ok(item, "Got add-on element.");
+ item.parentNode.ensureElementIsVisible(item);
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
+ is_element_visible(el, "Experiment state label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Active");
+ }
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
+ is_element_visible(el, "Experiment time label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "10 days remaining");
+ }
+
+ // Disable it and check it's previous experiment entry.
+
+ yield gExperiments.disableExperiment("experiment-3");
+
+ item = get_addon_element(gManagerWindow, "experiment-3");
+ Assert.ok(item, "Got add-on element.");
+ item.parentNode.ensureElementIsVisible(item);
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
+ is_element_visible(el, "Experiment state label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Complete");
+ }
+
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
+ is_element_visible(el, "Experiment time label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "5 days ago");
+ }
+});
+
+add_task(function testDetailView() {
+ if (!gExperiments) {
+ info("Skipping experiments test because that feature isn't available.");
+ return;
+ }
+
+ defineNow(gExperiments._policy, Date.now());
+ yield gExperiments._updateExperiments({
+ "version": 1,
+ "experiments": [
+ {
+ id: "experiment-4",
+ xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
+ xpiHash: "IRRELEVANT",
+ startTime: Date.now() / 1000 - 3600,
+ endTime: Date.now() / 1000 + 3600,
+ maxActiveSeconds: 600,
+ appName: [Services.appinfo.name],
+ channel: [gExperiments._policy.updatechannel()],
+ },
+ ],
+ });
+ yield gExperiments._run();
+
+ // Check active experiment.
+
+ yield openDetailsView("test-experiment1@experiments.mozilla.org");
+
+ let el = gManagerWindow.document.getElementById("detail-experiment-state");
+ is_element_visible(el, "Experiment state label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Active");
+ }
+
+ el = gManagerWindow.document.getElementById("detail-experiment-time");
+ is_element_visible(el, "Experiment time label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Less than a day remaining");
+ }
+
+ el = gManagerWindow.document.getElementById("detail-version");
+ is_element_hidden(el, "detail-version should be hidden.");
+ el = gManagerWindow.document.getElementById("detail-creator");
+ is_element_hidden(el, "detail-creator should be hidden.");
+ el = gManagerWindow.document.getElementById("detail-experiment-bullet");
+ is_element_visible(el, "experiment-bullet should be visible.");
+
+ // Check previous experiment.
+
+ yield gCategoryUtilities.openType("experiment");
+ yield openDetailsView("experiment-3");
+
+ el = gManagerWindow.document.getElementById("detail-experiment-state");
+ is_element_visible(el, "Experiment state label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "Complete");
+ }
+
+ el = gManagerWindow.document.getElementById("detail-experiment-time");
+ is_element_visible(el, "Experiment time label should be visible.");
+ if (gIsEnUsLocale) {
+ Assert.equal(el.value, "5 days ago");
+ }
+
+ el = gManagerWindow.document.getElementById("detail-version");
+ is_element_hidden(el, "detail-version should be hidden.");
+ el = gManagerWindow.document.getElementById("detail-creator");
+ is_element_hidden(el, "detail-creator should be hidden.");
+ el = gManagerWindow.document.getElementById("detail-experiment-bullet");
+ is_element_visible(el, "experiment-bullet should be visible.");
+});
+
+add_task(function* testRemoveAndUndo() {
+ if (!gExperiments) {
+ info("Skipping experiments test because that feature isn't available.");
+ return;
+ }
+
+ yield gCategoryUtilities.openType("experiment");
+
+ let addon = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
+ Assert.ok(addon, "Got add-on element.");
+
+ yield clickRemoveButton(addon);
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ let el = gManagerWindow.document.getAnonymousElementByAttribute(addon, "class", "pending");
+ is_element_visible(el, "Uninstall undo information should be visible.");
+
+ yield clickUndoButton(addon);
+ addon = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
+ Assert.ok(addon, "Got add-on element.");
+});
+
+add_task(function* testCleanup() {
+ if (gExperiments) {
+ Services.prefs.clearUserPref("experiments.enabled");
+ Services.prefs.setCharPref("experiments.manifest.uri", gSavedManifestURI);
+
+ // We perform the uninit/init cycle to purge any leftover state.
+ yield OS.File.remove(gExperiments._cacheFilePath);
+ yield gExperiments.uninit();
+ yield gExperiments.init();
+ }
+
+ // Check post-conditions.
+ let addons = yield getExperimentAddons();
+ Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
+
+ yield close_manager(gManagerWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_globalinformations.js b/toolkit/mozapps/extensions/test/browser/browser_globalinformations.js
new file mode 100644
index 000000000..33890d8f5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_globalinformations.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 656269 - Add link to Mozilla plugin check from Add-ons Manager
+
+const MAIN_URL = "https://example.com/" + RELATIVE_DIR + "discovery.html";
+const PREF_PLUGINCHECKURL = "plugins.update.url";
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setCharPref(PREF_PLUGINCHECKURL, MAIN_URL);
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref(PREF_PLUGINCHECKURL);
+ });
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+add_test(function() {
+ open_manager("addons://list/extension", function(aManager) {
+ info("Testing plugin check information");
+ var button = aManager.document.querySelector("#list-view button.global-info-plugincheck");
+ is_element_hidden(button, "Plugin Check message button should be hidden");
+
+ info("Changing view to plugins")
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
+
+ wait_for_view_load(aManager, function(aManager) {
+ var button = aManager.document.querySelector("#list-view button.global-info-plugincheck");
+ is_element_visible(button, "Plugin Check message button should be visible");
+
+ info("Clicking 'Plugin Check' button");
+ EventUtils.synthesizeMouseAtCenter(button, { }, aManager);
+ gBrowser.addEventListener("load", function(event) {
+ if (!(event.target instanceof Document) ||
+ event.target.location.href == "about:blank")
+ return;
+ gBrowser.removeEventListener("load", arguments.callee, true);
+
+ is(gBrowser.currentURI.spec, Services.urlFormatter.formatURLPref("plugins.update.url"), "Plugin Check URL should match");
+
+ gBrowser.removeCurrentTab();
+ close_manager(aManager, function() {
+ run_next_test();
+ });
+ }, true);
+ });
+ });
+}); \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/browser/browser_globalwarnings.js b/toolkit/mozapps/extensions/test/browser/browser_globalwarnings.js
new file mode 100644
index 000000000..663905a90
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_globalwarnings.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 566194 - safe mode / security & compatibility check status are not exposed in new addon manager UI
+
+function test() {
+ waitForExplicitFinish();
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+add_test(function() {
+ info("Testing compatibility checking warning");
+
+ info("Setting checkCompatibility to false");
+ AddonManager.checkCompatibility = false;
+
+ open_manager("addons://list/extension", function(aWindow) {
+ var hbox = aWindow.document.querySelector("#list-view hbox.global-warning-checkcompatibility");
+ is_element_visible(hbox, "Check Compatibility warning hbox should be visible");
+ var button = aWindow.document.querySelector("#list-view button.global-warning-checkcompatibility");
+ is_element_visible(button, "Check Compatibility warning button should be visible");
+
+ info("Clicking 'Enable' button");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+ is(AddonManager.checkCompatibility, true, "Check Compatibility pref should be cleared");
+ is_element_hidden(hbox, "Check Compatibility warning hbox should be hidden");
+ is_element_hidden(button, "Check Compatibility warning button should be hidden");
+
+ close_manager(aWindow, function() {
+ run_next_test();
+ });
+ });
+});
+
+add_test(function() {
+ info("Testing update security checking warning");
+
+ var pref = "extensions.checkUpdateSecurity";
+ info("Setting " + pref + " pref to false")
+ Services.prefs.setBoolPref(pref, false);
+
+ open_manager(null, function(aWindow) {
+ var hbox = aWindow.document.querySelector("#list-view hbox.global-warning-updatesecurity");
+ is_element_visible(hbox, "Check Update Security warning hbox should be visible");
+ var button = aWindow.document.querySelector("#list-view button.global-warning-updatesecurity");
+ is_element_visible(button, "Check Update Security warning button should be visible");
+
+ info("Clicking 'Enable' button");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
+ is(Services.prefs.prefHasUserValue(pref), false, "Check Update Security pref should be cleared");
+ is_element_hidden(hbox, "Check Update Security warning hbox should be hidden");
+ is_element_hidden(button, "Check Update Security warning button should be hidden");
+
+ close_manager(aWindow, function() {
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_gmpProvider.js b/toolkit/mozapps/extensions/test/browser/browser_gmpProvider.js
new file mode 100644
index 000000000..1813df78c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_gmpProvider.js
@@ -0,0 +1,401 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+let {AddonTestUtils} = Cu.import("resource://testing-common/AddonManagerTesting.jsm", {});
+let GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+
+const TEST_DATE = new Date(2013, 0, 1, 12);
+
+let gManagerWindow;
+let gCategoryUtilities;
+let gIsEnUsLocale;
+
+let gMockAddons = [];
+
+for (let plugin of GMPScope.GMP_PLUGINS) {
+ let mockAddon = Object.freeze({
+ id: plugin.id,
+ isValid: true,
+ isInstalled: false,
+ isEME: plugin.id.indexOf("gmp-eme-") == 0 ? true : false,
+ });
+ gMockAddons.push(mockAddon);
+}
+
+let gInstalledAddonId = "";
+let gInstallDeferred = null;
+let gPrefs = Services.prefs;
+let getKey = GMPScope.GMPPrefs.getPrefKey;
+
+function MockGMPInstallManager() {
+}
+
+MockGMPInstallManager.prototype = {
+ checkForAddons: () => Promise.resolve(gMockAddons),
+
+ installAddon: addon => {
+ gInstalledAddonId = addon.id;
+ gInstallDeferred.resolve();
+ return Promise.resolve();
+ },
+};
+
+let gOptionsObserver = {
+ lastDisplayed: null,
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == AddonManager.OPTIONS_NOTIFICATION_DISPLAYED) {
+ this.lastDisplayed = aData;
+ }
+ }
+};
+
+function getInstallItem() {
+ let doc = gManagerWindow.document;
+ let list = doc.getElementById("addon-list");
+
+ let node = list.firstChild;
+ while (node) {
+ if (node.getAttribute("status") == "installing") {
+ return node;
+ }
+ node = node.nextSibling;
+ }
+
+ return null;
+}
+
+function openDetailsView(aId) {
+ let item = get_addon_element(gManagerWindow, aId);
+ Assert.ok(item, "Should have got add-on element.");
+ is_element_visible(item, "Add-on element should be visible.");
+
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+
+ let deferred = Promise.defer();
+ wait_for_view_load(gManagerWindow, deferred.resolve);
+ return deferred.promise;
+}
+
+add_task(function* initializeState() {
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_LOGGING_DUMP, true);
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_LOGGING_LEVEL, 0);
+
+ gManagerWindow = yield open_manager();
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ registerCleanupFunction(Task.async(function*() {
+ Services.obs.removeObserver(gOptionsObserver, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
+
+ for (let addon of gMockAddons) {
+ gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id));
+ gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id));
+ gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id));
+ gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id));
+ gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCEVISIBLE, addon.id));
+ }
+ gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_LOGGING_DUMP);
+ gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_LOGGING_LEVEL);
+ gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK);
+ gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_EME_ENABLED);
+ yield GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+ }));
+
+ let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
+ gIsEnUsLocale = chrome.getSelectedLocale("global") == "en-US";
+
+ Services.obs.addObserver(gOptionsObserver, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
+
+ // Start out with plugins not being installed, disabled and automatic updates
+ // disabled.
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
+ for (let addon of gMockAddons) {
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false);
+ gPrefs.setIntPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id), 0);
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id), false);
+ gPrefs.setCharPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), "");
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCEVISIBLE, addon.id),
+ true);
+ }
+ yield GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+});
+
+add_task(function* testNotInstalledDisabled() {
+ Assert.ok(gCategoryUtilities.isTypeVisible("plugin"), "Plugin tab visible.");
+ yield gCategoryUtilities.openType("plugin");
+
+ for (let addon of gMockAddons) {
+ let item = get_addon_element(gManagerWindow, addon.id);
+ Assert.ok(item, "Got add-on element:" + addon.id);
+ item.parentNode.ensureElementIsVisible(item);
+ is(item.getAttribute("active"), "false");
+
+ let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
+ is_element_hidden(el, "Warning notification is hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
+ is_element_visible(el, "disabled-postfix is visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
+ is_element_hidden(el, "Disable button not visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
+ is_element_hidden(el, "Enable button not visible.");
+
+ let menu = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "state-menulist");
+ is_element_visible(menu, "State menu should be visible.");
+
+ let neverActivate = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "never-activate-menuitem");
+ is(menu.selectedItem, neverActivate, "Plugin state should be never-activate.");
+ }
+});
+
+add_task(function* testNotInstalledDisabledDetails() {
+ for (let addon of gMockAddons) {
+ yield openDetailsView(addon.id);
+ let doc = gManagerWindow.document;
+
+ let el = doc.getElementsByClassName("disabled-postfix")[0];
+ is_element_visible(el, "disabled-postfix is visible.");
+ el = doc.getElementById("detail-findUpdates-btn");
+ is_element_visible(el, "Find updates link is visible.");
+ el = doc.getElementById("detail-warning");
+ is_element_hidden(el, "Warning notification is hidden.");
+ el = doc.getElementsByTagName("setting")[0];
+ }
+});
+
+add_task(function* testNotInstalled() {
+ Assert.ok(gCategoryUtilities.isTypeVisible("plugin"), "Plugin tab visible.");
+ yield gCategoryUtilities.openType("plugin");
+
+ for (let addon of gMockAddons) {
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+ let item = get_addon_element(gManagerWindow, addon.id);
+ Assert.ok(item, "Got add-on element:" + addon.id);
+ item.parentNode.ensureElementIsVisible(item);
+ is(item.getAttribute("active"), "true");
+
+ let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
+ is_element_visible(el, "Warning notification is visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
+ is_element_hidden(el, "disabled-postfix is hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
+ is_element_hidden(el, "Disable button not visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
+ is_element_hidden(el, "Enable button not visible.");
+
+ let menu = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "state-menulist");
+ is_element_visible(menu, "State menu should be visible.");
+
+ let alwaysActivate = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "always-activate-menuitem");
+ is(menu.selectedItem, alwaysActivate, "Plugin state should be always-activate.");
+ }
+});
+
+add_task(function* testNotInstalledDetails() {
+ for (let addon of gMockAddons) {
+ yield openDetailsView(addon.id);
+ let doc = gManagerWindow.document;
+
+ let el = doc.getElementsByClassName("disabled-postfix")[0];
+ is_element_hidden(el, "disabled-postfix is hidden.");
+ el = doc.getElementById("detail-findUpdates-btn");
+ is_element_visible(el, "Find updates link is visible.");
+ el = doc.getElementById("detail-warning");
+ is_element_visible(el, "Warning notification is visible.");
+ el = doc.getElementsByTagName("setting")[0];
+ }
+});
+
+add_task(function* testInstalled() {
+ for (let addon of gMockAddons) {
+ gPrefs.setIntPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id),
+ TEST_DATE.getTime());
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id), false);
+ gPrefs.setCharPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), "1.2.3.4");
+
+ yield gCategoryUtilities.openType("plugin");
+
+ let item = get_addon_element(gManagerWindow, addon.id);
+ Assert.ok(item, "Got add-on element.");
+ item.parentNode.ensureElementIsVisible(item);
+ is(item.getAttribute("active"), "true");
+
+ let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
+ is_element_hidden(el, "Warning notification is hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
+ is_element_hidden(el, "disabled-postfix is hidden.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
+ is_element_hidden(el, "Disable button not visible.");
+ el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
+ is_element_hidden(el, "Enable button not visible.");
+
+ let menu = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "state-menulist");
+ is_element_visible(menu, "State menu should be visible.");
+
+ let alwaysActivate = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "always-activate-menuitem");
+ is(menu.selectedItem, alwaysActivate, "Plugin state should be always-activate.");
+ }
+});
+
+add_task(function* testInstalledDetails() {
+ for (let addon of gMockAddons) {
+ yield openDetailsView(addon.id);
+ let doc = gManagerWindow.document;
+
+ let el = doc.getElementsByClassName("disabled-postfix")[0];
+ is_element_hidden(el, "disabled-postfix is hidden.");
+ el = doc.getElementById("detail-findUpdates-btn");
+ is_element_visible(el, "Find updates link is visible.");
+ el = doc.getElementById("detail-warning");
+ is_element_hidden(el, "Warning notification is hidden.");
+ el = doc.getElementsByTagName("setting")[0];
+
+ let contextMenu = doc.getElementById("addonitem-popup");
+ let deferred = Promise.defer();
+ let listener = () => {
+ contextMenu.removeEventListener("popupshown", listener, false);
+ deferred.resolve();
+ };
+ contextMenu.addEventListener("popupshown", listener, false);
+ el = doc.getElementsByClassName("detail-view-container")[0];
+ EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
+ EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
+ yield deferred.promise;
+ let menuSep = doc.getElementById("addonitem-menuseparator");
+ is_element_hidden(menuSep, "Menu separator is hidden.");
+ contextMenu.hidePopup();
+ }
+});
+
+add_task(function* testInstalledGlobalEmeDisabled() {
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, false);
+ for (let addon of gMockAddons) {
+ yield gCategoryUtilities.openType("plugin");
+
+ let item = get_addon_element(gManagerWindow, addon.id);
+ if (addon.isEME) {
+ Assert.ok(!item, "Couldn't get add-on element.");
+ } else {
+ Assert.ok(item, "Got add-on element.");
+ }
+ }
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
+});
+
+add_task(function* testPreferencesButton() {
+
+ let prefValues = [
+ { enabled: false, version: "" },
+ { enabled: false, version: "1.2.3.4" },
+ { enabled: true, version: "" },
+ { enabled: true, version: "1.2.3.4" },
+ ];
+
+ for (let preferences of prefValues) {
+ dump("Testing preferences button with pref settings: " +
+ JSON.stringify(preferences) + "\n");
+ for (let addon of gMockAddons) {
+ yield close_manager(gManagerWindow);
+ gManagerWindow = yield open_manager();
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ gPrefs.setCharPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ preferences.version);
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id),
+ preferences.enabled);
+
+ yield gCategoryUtilities.openType("plugin");
+ let doc = gManagerWindow.document;
+ let item = get_addon_element(gManagerWindow, addon.id);
+
+ let button = doc.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ let deferred = Promise.defer();
+ wait_for_view_load(gManagerWindow, deferred.resolve);
+ yield deferred.promise;
+
+ is(gOptionsObserver.lastDisplayed, addon.id);
+ }
+ }
+});
+
+add_task(function* testUpdateButton() {
+ gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK);
+
+ let originalInstallManager = GMPScope.GMPInstallManager;
+ Object.defineProperty(GMPScope, "GMPInstallManager", {
+ value: MockGMPInstallManager,
+ writable: true,
+ enumerable: true,
+ configurable: true
+ });
+
+ for (let addon of gMockAddons) {
+ yield gCategoryUtilities.openType("plugin");
+ let doc = gManagerWindow.document;
+ let item = get_addon_element(gManagerWindow, addon.id);
+
+ gInstalledAddonId = "";
+ gInstallDeferred = Promise.defer();
+
+ let button = doc.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ let deferred = Promise.defer();
+ wait_for_view_load(gManagerWindow, deferred.resolve);
+ yield deferred.promise;
+
+ button = doc.getElementById("detail-findUpdates-btn");
+ Assert.ok(button != null, "Got detail-findUpdates-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ yield gInstallDeferred.promise;
+
+ Assert.equal(gInstalledAddonId, addon.id);
+ }
+ Object.defineProperty(GMPScope, "GMPInstallManager", {
+ value: originalInstallManager,
+ writable: true,
+ enumerable: true,
+ configurable: true
+ });
+});
+
+add_task(function* testEmeSupport() {
+ for (let addon of gMockAddons) {
+ gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCEVISIBLE, addon.id));
+ }
+ yield GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+
+ for (let addon of gMockAddons) {
+ yield gCategoryUtilities.openType("plugin");
+ let doc = gManagerWindow.document;
+ let item = get_addon_element(gManagerWindow, addon.id);
+ if (addon.id == GMPScope.EME_ADOBE_ID) {
+ if (Services.appinfo.OS == "WINNT" &&
+ Services.sysinfo.getPropertyAsInt32("version") >= 6) {
+ Assert.ok(item, "Adobe EME supported, found add-on element.");
+ } else {
+ Assert.ok(!item,
+ "Adobe EME not supported, couldn't find add-on element.");
+ }
+ } else {
+ Assert.ok(item, "Found add-on element.");
+ }
+ }
+
+ for (let addon of gMockAddons) {
+ gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCEVISIBLE, addon.id),
+ true);
+ }
+ yield GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+
+});
+
+add_task(function* test_cleanup() {
+ yield close_manager(gManagerWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
new file mode 100644
index 000000000..c1dd7f762
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
@@ -0,0 +1,677 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests various aspects of the details view
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+const SETTINGS_ROWS = 9;
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+var observer = {
+ lastDisplayed: null,
+ callback: null,
+ checkDisplayed: function(aExpected) {
+ is(this.lastDisplayed, aExpected, "'addon-options-displayed' notification should have fired");
+ this.lastDisplayed = null;
+ },
+ checkNotDisplayed: function() {
+ is(this.lastDisplayed, null, "'addon-options-displayed' notification should not have fired");
+ },
+ lastHidden: null,
+ checkHidden: function(aExpected) {
+ is(this.lastHidden, aExpected, "'addon-options-hidden' notification should have fired");
+ this.lastHidden = null;
+ },
+ checkNotHidden: function() {
+ is(this.lastHidden, null, "'addon-options-hidden' notification should not have fired");
+ },
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == AddonManager.OPTIONS_NOTIFICATION_DISPLAYED) {
+ this.lastDisplayed = aData;
+ // Test if the binding has applied before the observers are notified. We test the second setting here,
+ // because the code operates on the first setting and we want to check it applies to all.
+ var setting = aSubject.querySelector("rows > setting[first-row] ~ setting");
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(setting, "class", "preferences-title");
+ isnot(input, null, "XBL binding should be applied");
+
+ // Add some extra height to the scrolling pane to ensure that it needs to scroll when appropriate.
+ gManagerWindow.document.getElementById("detail-controls").style.marginBottom = "1000px";
+
+ if (this.callback) {
+ var tempCallback = this.callback;
+ this.callback = null;
+ tempCallback();
+ }
+ } else if (aTopic == AddonManager.OPTIONS_NOTIFICATION_HIDDEN) {
+ this.lastHidden = aData;
+ }
+ }
+};
+
+function installAddon(aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_inlinesettings1.xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function checkScrolling(aShouldHaveScrolled) {
+ var detailView = gManagerWindow.document.getElementById("detail-view");
+ var boxObject = detailView.boxObject;
+ ok(detailView.scrollHeight > boxObject.height, "Page should require scrolling");
+ if (aShouldHaveScrolled)
+ isnot(detailView.scrollTop, 0, "Page should have scrolled");
+ else
+ is(detailView.scrollTop, 0, "Page should not have scrolled");
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "inlinesettings2@tests.mozilla.org",
+ name: "Inline Settings (Regular)",
+ version: "1",
+ optionsURL: CHROMEROOT + "options.xul",
+ optionsType: AddonManager.OPTIONS_TYPE_INLINE,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_DISABLE,
+ },{
+ id: "inlinesettings3@tests.mozilla.org",
+ name: "Inline Settings (More Options)",
+ description: "Tests for option types introduced after Mozilla 7.0",
+ version: "1",
+ optionsURL: CHROMEROOT + "more_options.xul",
+ optionsType: AddonManager.OPTIONS_TYPE_INLINE
+ },{
+ id: "noninlinesettings@tests.mozilla.org",
+ name: "Non-Inline Settings",
+ version: "1",
+ optionsURL: CHROMEROOT + "addon_prefs.xul"
+ }]);
+
+ installAddon(function () {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ Services.obs.addObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_DISPLAYED,
+ false);
+ Services.obs.addObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_HIDDEN,
+ false);
+
+ run_next_test();
+ });
+ });
+}
+
+function end_test() {
+ Services.obs.removeObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
+
+ Services.prefs.clearUserPref("extensions.inlinesettings1.bool");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.boolint");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.integer");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.string");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.color");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.file");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.directory");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.radioBool");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.radioInt");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.radioString");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.menulist");
+
+ MockFilePicker.cleanup();
+
+ close_manager(gManagerWindow, function() {
+ observer.checkHidden("inlinesettings3@tests.mozilla.org");
+ Services.obs.removeObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_HIDDEN);
+
+ AddonManager.getAddonByID("inlinesettings1@tests.mozilla.org", function(aAddon) {
+ aAddon.uninstall();
+ finish();
+ });
+ });
+}
+
+// Addon with options.xul
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, "Options should be inline type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ run_next_test();
+});
+
+// Addon with inline preferences as optionsURL
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, "Options should be inline type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ run_next_test();
+});
+
+// Addon with non-inline preferences as optionsURL
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "noninlinesettings@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_DIALOG, "Options should be dialog type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ run_next_test();
+});
+
+// Addon with options.xul, also a test for the setting.xml bindings
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+ is(gManagerWindow.gViewController.currentViewId,
+ "addons://detail/inlinesettings1%40tests.mozilla.org/preferences",
+ "Current view should scroll to preferences");
+ checkScrolling(true);
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+
+ ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
+ Services.prefs.setBoolPref("extensions.inlinesettings1.bool", false);
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input");
+ isnot(input.checked, true, "Checkbox should have initial value");
+ is(input.label, "Check box label", "Checkbox should be labelled");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ is(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), true, "Bool pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ isnot(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), false, "Bool pref should have been updated");
+
+ ok(!settings[1].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings1.boolint", 0);
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[1], "anonid", "input");
+ isnot(input.checked, true, "Checkbox should have initial value");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ is(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.boolint"), 1, "BoolInt pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ isnot(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.boolint"), 2, "BoolInt pref should have been updated");
+
+ ok(!settings[2].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings1.integer", 0);
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[2], "anonid", "input");
+ is(input.value, "0", "Number box should have initial value");
+ input.select();
+ EventUtils.synthesizeKey("1", {}, gManagerWindow);
+ EventUtils.synthesizeKey("3", {}, gManagerWindow);
+ is(input.value, "13", "Number box should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.integer"), 13, "Integer pref should have been updated");
+ EventUtils.synthesizeKey("VK_DOWN", {}, gManagerWindow);
+ is(input.value, "12", "Number box should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.integer"), 12, "Integer pref should have been updated");
+
+ ok(!settings[3].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setCharPref("extensions.inlinesettings1.string", "foo");
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[3], "anonid", "input");
+ is(input.value, "foo", "Text box should have initial value");
+ input.select();
+ EventUtils.synthesizeKey("b", {}, gManagerWindow);
+ EventUtils.synthesizeKey("a", {}, gManagerWindow);
+ EventUtils.synthesizeKey("r", {}, gManagerWindow);
+ is(input.value, "bar", "Text box should have updated value");
+ EventUtils.synthesizeKey("/", {}, gManagerWindow);
+ is(input.value, "bar/", "Text box should have updated value");
+ is(gManagerWindow.document.getBindingParent(gManagerWindow.document.activeElement), input, "Search box should not have focus");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.string"), "bar/", "String pref should have been updated");
+
+ ok(!settings[4].hasAttribute("first-row"), "Not the first row");
+ var input = settings[4].firstElementChild;
+ is(input.value, "1", "Menulist should have initial value");
+ input.focus();
+ EventUtils.synthesizeKey("b", {}, gManagerWindow);
+ is(input.value, "2", "Menulist should have updated value");
+ is(gManagerWindow._testValue, "2", "Menulist oncommand handler should've updated the test value");
+ delete gManagerWindow._testValue;
+
+ ok(!settings[5].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setCharPref("extensions.inlinesettings1.color", "#FF0000");
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[5], "anonid", "input");
+ is(input.color, "#FF0000", "Color picker should have initial value");
+ input.focus();
+ EventUtils.synthesizeKey("VK_RIGHT", {}, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RIGHT", {}, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", {}, gManagerWindow);
+ input.hidePopup();
+ is(input.color, "#FF9900", "Color picker should have updated value");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.color"), "#FF9900", "Color pref should have been updated");
+
+ try {
+ ok(!settings[6].hasAttribute("first-row"), "Not the first row");
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(settings[6], "anonid", "button");
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[6], "anonid", "input");
+ is(input.value, "", "Label value should be empty");
+ is(input.tooltipText, "", "Label tooltip should be empty");
+
+ var profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ var curProcD = Services.dirsvc.get("CurProcD", Ci.nsIFile);
+
+ MockFilePicker.returnFiles = [profD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeOpen, "File picker mode should be open file");
+ is(input.value, profD.path, "Label value should match file chosen");
+ is(input.tooltipText, profD.path, "Label tooltip should match file chosen");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.file"), profD.path, "File pref should match file chosen");
+
+ MockFilePicker.returnFiles = [curProcD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnCancel;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeOpen, "File picker mode should be open file");
+ is(input.value, profD.path, "Label value should not have changed");
+ is(input.tooltipText, profD.path, "Label tooltip should not have changed");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.file"), profD.path, "File pref should not have changed");
+
+ ok(!settings[7].hasAttribute("first-row"), "Not the first row");
+ button = gManagerWindow.document.getAnonymousElementByAttribute(settings[7], "anonid", "button");
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[7], "anonid", "input");
+ is(input.value, "", "Label value should be empty");
+ is(input.tooltipText, "", "Label tooltip should be empty");
+
+ MockFilePicker.returnFiles = [profD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeGetFolder, "File picker mode should be directory");
+ is(input.value, profD.path, "Label value should match file chosen");
+ is(input.tooltipText, profD.path, "Label tooltip should match file chosen");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.directory"), profD.path, "Directory pref should match file chosen");
+
+ MockFilePicker.returnFiles = [curProcD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnCancel;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeGetFolder, "File picker mode should be directory");
+ is(input.value, profD.path, "Label value should not have changed");
+ is(input.tooltipText, profD.path, "Label tooltip should not have changed");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.directory"), profD.path, "Directory pref should not have changed");
+
+ var unsizedInput = gManagerWindow.document.getAnonymousElementByAttribute(settings[2], "anonid", "input");
+ var sizedInput = gManagerWindow.document.getAnonymousElementByAttribute(settings[8], "anonid", "input");
+ is(unsizedInput.clientWidth > sizedInput.clientWidth, true, "Input with size attribute should be smaller than input without");
+ } finally {
+ button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ }
+ });
+});
+
+// Tests for the setting.xml bindings introduced after Mozilla 7
+add_test(function() {
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings3@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings3@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 4, "Grid should have settings children");
+
+ ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
+ Services.prefs.setBoolPref("extensions.inlinesettings3.radioBool", false);
+ var radios = settings[0].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+ EventUtils.synthesizeMouseAtCenter(radios[0], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getBoolPref("extensions.inlinesettings3.radioBool"), true, "Radio pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(radios[1], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getBoolPref("extensions.inlinesettings3.radioBool"), false, "Radio pref should have been updated");
+
+ ok(!settings[1].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings3.radioInt", 5);
+ var radios = settings[1].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+ isnot(radios[2].selected, true, "Correct radio button should be selected");
+ EventUtils.synthesizeMouseAtCenter(radios[0], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getIntPref("extensions.inlinesettings3.radioInt"), 4, "Radio pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(radios[2], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getIntPref("extensions.inlinesettings3.radioInt"), 6, "Radio pref should have been updated");
+
+ ok(!settings[2].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setCharPref("extensions.inlinesettings3.radioString", "juliet");
+ var radios = settings[2].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+ isnot(radios[2].selected, true, "Correct radio button should be selected");
+ EventUtils.synthesizeMouseAtCenter(radios[0], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getCharPref("extensions.inlinesettings3.radioString"), "india", "Radio pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(radios[2], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getCharPref("extensions.inlinesettings3.radioString"), "kilo", "Radio pref should have been updated");
+
+ ok(!settings[3].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings3.menulist", 8);
+ var input = settings[3].firstElementChild;
+ is(input.value, "8", "Menulist should have initial value");
+ input.focus();
+ EventUtils.synthesizeKey("n", {}, gManagerWindow);
+ is(input.value, "9", "Menulist should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings3.menulist"), 9, "Menulist pref should have been updated");
+
+ button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Addon with inline preferences as optionsURL
+add_test(function() {
+ observer.checkHidden("inlinesettings3@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings2@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 5, "Grid should have settings children");
+
+ var node = settings[0];
+ node = settings[0];
+ is_element_hidden(node, "Unsupported settings should not be visible");
+ ok(!node.hasAttribute("first-row"), "Hidden row is not the first row");
+
+ node = settings[1];
+ is(node.nodeName, "setting", "Should be a setting node");
+ ok(node.hasAttribute("first-row"), "First visible row should have first-row attribute");
+ var description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+ is(description.textContent, "Description Attribute", "Description node should contain description");
+
+ node = settings[2];
+ is(node.nodeName, "setting", "Should be a setting node");
+ ok(!node.hasAttribute("first-row"), "Not the first row");
+ description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+ is(description.textContent, "Description Text Node", "Description node should contain description");
+
+ node = settings[3];
+ is(node.nodeName, "setting", "Should be a setting node");
+ ok(!node.hasAttribute("first-row"), "Not the first row");
+ description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+ is(description.textContent, "This is a test, all this text should be visible", "Description node should contain description");
+ var button = node.firstElementChild;
+ isnot(button, null, "There should be a button");
+
+ node = settings[4];
+ is_element_hidden(node, "Unsupported settings should not be visible");
+ ok(!node.hasAttribute("first-row"), "Hidden row is not the first row");
+
+ var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Addon with non-inline preferences as optionsURL
+add_test(function() {
+ observer.checkHidden("inlinesettings2@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "noninlinesettings@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkNotDisplayed();
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 0, "Grid should not have settings children");
+
+ var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Addon with options.xul, disabling and enabling should hide and show settings UI
+add_test(function() {
+ observer.checkNotHidden();
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+ is(gManagerWindow.gViewController.currentViewId,
+ "addons://detail/inlinesettings1%40tests.mozilla.org",
+ "Current view should not scroll to preferences");
+ checkScrolling(false);
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+
+ // disable
+ var button = gManagerWindow.document.getElementById("detail-disable-btn");
+ button.focus(); // make sure it's in view
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 0, "Grid should not have settings children");
+
+ gCategoryUtilities.openType("extension", function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 0, "Grid should not have settings children");
+
+ // enable
+ var button = gManagerWindow.document.getElementById("detail-enable-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ observer.callback = function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+
+ settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ };
+ });
+ });
+ });
+});
+
+
+// Addon with options.xul that requires a restart to disable,
+// disabling and enabling should not hide and show settings UI.
+add_test(function() {
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings2@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ ok(settings.length > 0, "Grid should have settings children");
+
+ // disable
+ var button = gManagerWindow.document.getElementById("detail-disable-btn");
+ button.focus(); // make sure it's in view
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ observer.checkNotHidden();
+
+ settings = grid.querySelectorAll("rows > setting");
+ ok(settings.length > 0, "Grid should still have settings children");
+
+ // cancel pending disable
+ button = gManagerWindow.document.getElementById("detail-enable-btn");
+ button.focus(); // make sure it's in view
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ observer.checkNotDisplayed();
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Tests bindings with existing prefs.
+add_test(function() {
+ observer.checkHidden("inlinesettings2@tests.mozilla.org");
+
+ // Ensure these prefs are set. They should be set above, but somebody might
+ // change the tests above.
+ var profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ Services.prefs.setBoolPref("extensions.inlinesettings1.bool", false);
+ Services.prefs.setIntPref("extensions.inlinesettings1.boolint", 1);
+ Services.prefs.setIntPref("extensions.inlinesettings1.integer", 12);
+ Services.prefs.setCharPref("extensions.inlinesettings1.string", "bar/");
+ Services.prefs.setCharPref("extensions.inlinesettings1.color", "#FF9900");
+ Services.prefs.setCharPref("extensions.inlinesettings1.file", profD.path);
+ Services.prefs.setCharPref("extensions.inlinesettings1.directory", profD.path);
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input");
+ is(input.checked, false, "Checkbox should have initial value");
+
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[1], "anonid", "input");
+ is(input.checked, true, "Checkbox should have initial value");
+
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[2], "anonid", "input");
+ is(input.value, "12", "Number box should have initial value");
+
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[3], "anonid", "input");
+ is(input.value, "bar/", "Text box should have initial value");
+
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[5], "anonid", "input");
+ is(input.color, "#FF9900", "Color picker should have initial value");
+
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[6], "anonid", "input");
+ is(input.value, profD.path, "Label should have initial value");
+ is(input.tooltipText, profD.path, "Label tooltip should have initial value");
+
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[7], "anonid", "input");
+ is(input.value, profD.path, "Label value should have initial value");
+ is(input.tooltipText, profD.path, "Label tooltip should have initial value");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Tests bindings with existing prefs.
+add_test(function() {
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ // Ensure these prefs are set. They should be set above, but somebody might
+ // change the tests above.
+ Services.prefs.setBoolPref("extensions.inlinesettings3.radioBool", false);
+ Services.prefs.setIntPref("extensions.inlinesettings3.radioInt", 6);
+ Services.prefs.setCharPref("extensions.inlinesettings3.radioString", "kilo");
+ Services.prefs.setIntPref("extensions.inlinesettings3.menulist", 9);
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings3@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings3@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+
+ var radios = settings[0].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+
+ var radios = settings[1].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ isnot(radios[1].selected, true, "Correct radio button should be selected");
+ is(radios[2].selected, true, "Correct radio button should be selected");
+
+ var radios = settings[2].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ isnot(radios[1].selected, true, "Correct radio button should be selected");
+ is(radios[2].selected, true, "Correct radio button should be selected");
+
+ var input = settings[3].firstElementChild;
+ is(input.value, "9", "Menulist should have initial value");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings_custom.js b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings_custom.js
new file mode 100644
index 000000000..ecd10852d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings_custom.js
@@ -0,0 +1,92 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests various aspects of the details view
+
+var gManagerWindow;
+var gCategoryUtilities;
+
+function installAddon(aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_inlinesettings1_custom.xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ installAddon(function () {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ run_next_test();
+ });
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ AddonManager.getAddonByID("inlinesettings1@tests.mozilla.org", function(aAddon) {
+ aAddon.uninstall();
+ finish();
+ });
+ });
+}
+
+// Addon with options.xul, with custom <setting> binding
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, "Options should be inline type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ run_next_test();
+});
+
+// Addon with options.xul, also a test for the setting.xml bindings
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gManagerWindow.gViewController.currentViewId,
+ "addons://detail/inlinesettings1%40tests.mozilla.org/preferences",
+ "Current view should scroll to preferences");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 1, "Grid should have settings children");
+
+ ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
+
+ var style = window.getComputedStyle(settings[0], null);
+ is(style.getPropertyValue("background-color"), "rgb(0, 0, 255)", "Background color should be set");
+ is(style.getPropertyValue("display"), "-moz-grid-line", "Display should be set");
+ is(style.getPropertyValue("-moz-binding"), 'url("chrome://inlinesettings/content/binding.xml#custom")', "Binding should be set");
+
+ var label = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "label");
+ is(label.textContent, "Custom", "Localized string should be shown");
+
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input");
+ isnot(input, null, "Binding should be applied");
+ is(input.value, "Woah!", "Binding should be applied");
+
+ button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings_info.js b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings_info.js
new file mode 100644
index 000000000..05b43a238
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings_info.js
@@ -0,0 +1,569 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests various aspects of the details view
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+const SETTINGS_ROWS = 8;
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+var observer = {
+ lastDisplayed: null,
+ callback: null,
+ checkDisplayed: function(aExpected) {
+ is(this.lastDisplayed, aExpected, "'addon-options-displayed' notification should have fired");
+ this.lastDisplayed = null;
+ },
+ checkNotDisplayed: function() {
+ is(this.lastDisplayed, null, "'addon-options-displayed' notification should not have fired");
+ },
+ lastHidden: null,
+ checkHidden: function(aExpected) {
+ is(this.lastHidden, aExpected, "'addon-options-hidden' notification should have fired");
+ this.lastHidden = null;
+ },
+ checkNotHidden: function() {
+ is(this.lastHidden, null, "'addon-options-hidden' notification should not have fired");
+ },
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == AddonManager.OPTIONS_NOTIFICATION_DISPLAYED) {
+ this.lastDisplayed = aData;
+ // Test if the binding has applied before the observers are notified. We test the second setting here,
+ // because the code operates on the first setting and we want to check it applies to all.
+ var setting = aSubject.querySelector("rows > setting[first-row] ~ setting");
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(setting, "class", "preferences-title");
+ isnot(input, null, "XBL binding should be applied");
+
+ // Add some extra height to the scrolling pane to ensure that it needs to scroll when appropriate.
+ gManagerWindow.document.getElementById("detail-controls").style.marginBottom = "1000px";
+
+ if (this.callback) {
+ var tempCallback = this.callback;
+ this.callback = null;
+ tempCallback();
+ }
+ } else if (aTopic == AddonManager.OPTIONS_NOTIFICATION_HIDDEN) {
+ this.lastHidden = aData;
+ }
+ }
+};
+
+function installAddon(aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_inlinesettings1_info.xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function checkScrolling(aShouldHaveScrolled) {
+ var detailView = gManagerWindow.document.getElementById("detail-view");
+ var boxObject = detailView.boxObject;
+ ok(detailView.scrollHeight > boxObject.height, "Page should require scrolling");
+ if (aShouldHaveScrolled)
+ isnot(detailView.scrollTop, 0, "Page should have scrolled");
+ else
+ is(detailView.scrollTop, 0, "Page should not have scrolled");
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "inlinesettings2@tests.mozilla.org",
+ name: "Inline Settings (Regular)",
+ version: "1",
+ optionsURL: CHROMEROOT + "options.xul",
+ optionsType: AddonManager.OPTIONS_TYPE_INLINE_INFO,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_DISABLE,
+ },{
+ id: "inlinesettings3@tests.mozilla.org",
+ name: "Inline Settings (More Options)",
+ description: "Tests for option types introduced after Mozilla 7.0",
+ version: "1",
+ optionsURL: CHROMEROOT + "more_options.xul",
+ optionsType: AddonManager.OPTIONS_TYPE_INLINE_INFO
+ },{
+ id: "noninlinesettings@tests.mozilla.org",
+ name: "Non-Inline Settings",
+ version: "1",
+ optionsURL: CHROMEROOT + "addon_prefs.xul"
+ }]);
+
+ installAddon(function () {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ Services.obs.addObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_DISPLAYED,
+ false);
+ Services.obs.addObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_HIDDEN,
+ false);
+
+ run_next_test();
+ });
+ });
+}
+
+function end_test() {
+ Services.obs.removeObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
+
+ Services.prefs.clearUserPref("extensions.inlinesettings1.bool");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.boolint");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.integer");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.string");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.color");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.file");
+ Services.prefs.clearUserPref("extensions.inlinesettings1.directory");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.radioBool");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.radioInt");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.radioString");
+ Services.prefs.clearUserPref("extensions.inlinesettings3.menulist");
+
+ MockFilePicker.cleanup();
+
+ close_manager(gManagerWindow, function() {
+ observer.checkHidden("inlinesettings2@tests.mozilla.org");
+ Services.obs.removeObserver(observer,
+ AddonManager.OPTIONS_NOTIFICATION_HIDDEN);
+
+ AddonManager.getAddonByID("inlinesettings1@tests.mozilla.org", function(aAddon) {
+ aAddon.uninstall();
+ finish();
+ });
+ });
+}
+
+// Addon with options.xul
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE_INFO, "Options should be inline info type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_hidden(button, "Preferences button should be hidden");
+
+ run_next_test();
+});
+
+// Addon with inline preferences as optionsURL
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE_INFO, "Options should be inline info type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_hidden(button, "Preferences button should be hidden");
+
+ run_next_test();
+});
+
+// Addon with non-inline preferences as optionsURL
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "noninlinesettings@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_DIALOG, "Options should be dialog type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ run_next_test();
+});
+
+// Addon with options.xul, also a test for the setting.xml bindings
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+
+ ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
+ Services.prefs.setBoolPref("extensions.inlinesettings1.bool", false);
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input");
+ isnot(input.checked, true, "Checkbox should have initial value");
+ is(input.label, "Check box label", "Checkbox should be labelled");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ is(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), true, "Bool pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ isnot(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), false, "Bool pref should have been updated");
+
+ ok(!settings[1].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings1.boolint", 0);
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[1], "anonid", "input");
+ isnot(input.checked, true, "Checkbox should have initial value");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ is(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.boolint"), 1, "BoolInt pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+ isnot(input.checked, true, "Checkbox should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.boolint"), 2, "BoolInt pref should have been updated");
+
+ ok(!settings[2].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings1.integer", 0);
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[2], "anonid", "input");
+ is(input.value, "0", "Number box should have initial value");
+ input.select();
+ EventUtils.synthesizeKey("1", {}, gManagerWindow);
+ EventUtils.synthesizeKey("3", {}, gManagerWindow);
+ is(input.value, "13", "Number box should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.integer"), 13, "Integer pref should have been updated");
+ EventUtils.synthesizeKey("VK_DOWN", {}, gManagerWindow);
+ is(input.value, "12", "Number box should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings1.integer"), 12, "Integer pref should have been updated");
+
+ ok(!settings[3].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setCharPref("extensions.inlinesettings1.string", "foo");
+ var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[3], "anonid", "input");
+ is(input.value, "foo", "Text box should have initial value");
+ input.select();
+ EventUtils.synthesizeKey("b", {}, gManagerWindow);
+ EventUtils.synthesizeKey("a", {}, gManagerWindow);
+ EventUtils.synthesizeKey("r", {}, gManagerWindow);
+ is(input.value, "bar", "Text box should have updated value");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.string"), "bar", "String pref should have been updated");
+
+ ok(!settings[4].hasAttribute("first-row"), "Not the first row");
+ var input = settings[4].firstElementChild;
+ is(input.value, "1", "Menulist should have initial value");
+ input.focus();
+ EventUtils.synthesizeKey("b", {}, gManagerWindow);
+ is(input.value, "2", "Menulist should have updated value");
+ is(gManagerWindow._testValue, "2", "Menulist oncommand handler should've updated the test value");
+ delete gManagerWindow._testValue;
+
+ ok(!settings[5].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setCharPref("extensions.inlinesettings1.color", "#FF0000");
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[5], "anonid", "input");
+ is(input.color, "#FF0000", "Color picker should have initial value");
+ input.focus();
+ EventUtils.synthesizeKey("VK_RIGHT", {}, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RIGHT", {}, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", {}, gManagerWindow);
+ input.hidePopup();
+ is(input.color, "#FF9900", "Color picker should have updated value");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.color"), "#FF9900", "Color pref should have been updated");
+
+ try {
+ ok(!settings[6].hasAttribute("first-row"), "Not the first row");
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(settings[6], "anonid", "button");
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[6], "anonid", "input");
+ is(input.value, "", "Label value should be empty");
+ is(input.tooltipText, "", "Label tooltip should be empty");
+
+ var profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ var curProcD = Services.dirsvc.get("CurProcD", Ci.nsIFile);
+
+ MockFilePicker.returnFiles = [profD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeOpen, "File picker mode should be open file");
+ is(input.value, profD.path, "Label value should match file chosen");
+ is(input.tooltipText, profD.path, "Label tooltip should match file chosen");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.file"), profD.path, "File pref should match file chosen");
+
+ MockFilePicker.returnFiles = [curProcD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnCancel;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeOpen, "File picker mode should be open file");
+ is(input.value, profD.path, "Label value should not have changed");
+ is(input.tooltipText, profD.path, "Label tooltip should not have changed");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.file"), profD.path, "File pref should not have changed");
+
+ ok(!settings[7].hasAttribute("first-row"), "Not the first row");
+ button = gManagerWindow.document.getAnonymousElementByAttribute(settings[7], "anonid", "button");
+ input = gManagerWindow.document.getAnonymousElementByAttribute(settings[7], "anonid", "input");
+ is(input.value, "", "Label value should be empty");
+ is(input.tooltipText, "", "Label tooltip should be empty");
+
+ MockFilePicker.returnFiles = [profD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeGetFolder, "File picker mode should be directory");
+ is(input.value, profD.path, "Label value should match file chosen");
+ is(input.tooltipText, profD.path, "Label tooltip should match file chosen");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.directory"), profD.path, "Directory pref should match file chosen");
+
+ MockFilePicker.returnFiles = [curProcD];
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnCancel;
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ is(MockFilePicker.mode, Ci.nsIFilePicker.modeGetFolder, "File picker mode should be directory");
+ is(input.value, profD.path, "Label value should not have changed");
+ is(input.tooltipText, profD.path, "Label tooltip should not have changed");
+ is(Services.prefs.getCharPref("extensions.inlinesettings1.directory"), profD.path, "Directory pref should not have changed");
+
+ } finally {
+ button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ }
+ });
+});
+
+// Tests for the setting.xml bindings introduced after Mozilla 7
+add_test(function() {
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings3@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings3@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 4, "Grid should have settings children");
+
+ ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
+ Services.prefs.setBoolPref("extensions.inlinesettings3.radioBool", false);
+ var radios = settings[0].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+ EventUtils.synthesizeMouseAtCenter(radios[0], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getBoolPref("extensions.inlinesettings3.radioBool"), true, "Radio pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(radios[1], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getBoolPref("extensions.inlinesettings3.radioBool"), false, "Radio pref should have been updated");
+
+ ok(!settings[1].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings3.radioInt", 5);
+ var radios = settings[1].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+ isnot(radios[2].selected, true, "Correct radio button should be selected");
+ EventUtils.synthesizeMouseAtCenter(radios[0], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getIntPref("extensions.inlinesettings3.radioInt"), 4, "Radio pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(radios[2], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getIntPref("extensions.inlinesettings3.radioInt"), 6, "Radio pref should have been updated");
+
+ ok(!settings[2].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setCharPref("extensions.inlinesettings3.radioString", "juliet");
+ var radios = settings[2].getElementsByTagName("radio");
+ isnot(radios[0].selected, true, "Correct radio button should be selected");
+ is(radios[1].selected, true, "Correct radio button should be selected");
+ isnot(radios[2].selected, true, "Correct radio button should be selected");
+ EventUtils.synthesizeMouseAtCenter(radios[0], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getCharPref("extensions.inlinesettings3.radioString"), "india", "Radio pref should have been updated");
+ EventUtils.synthesizeMouseAtCenter(radios[2], { clickCount: 1 }, gManagerWindow);
+ is(Services.prefs.getCharPref("extensions.inlinesettings3.radioString"), "kilo", "Radio pref should have been updated");
+
+ ok(!settings[3].hasAttribute("first-row"), "Not the first row");
+ Services.prefs.setIntPref("extensions.inlinesettings3.menulist", 8);
+ var input = settings[3].firstElementChild;
+ is(input.value, "8", "Menulist should have initial value");
+ input.focus();
+ EventUtils.synthesizeKey("n", {}, gManagerWindow);
+ is(input.value, "9", "Menulist should have updated value");
+ is(Services.prefs.getIntPref("extensions.inlinesettings3.menulist"), 9, "Menulist pref should have been updated");
+
+ button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Addon with inline preferences as optionsURL
+add_test(function() {
+ observer.checkHidden("inlinesettings3@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings2@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 5, "Grid should have settings children");
+
+ var node = settings[0];
+ node = settings[0];
+ is_element_hidden(node, "Unsupported settings should not be visible");
+ ok(!node.hasAttribute("first-row"), "Hidden row is not the first row");
+
+ node = settings[1];
+ is(node.nodeName, "setting", "Should be a setting node");
+ ok(node.hasAttribute("first-row"), "First visible row should have first-row attribute");
+ var description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+ is(description.textContent, "Description Attribute", "Description node should contain description");
+
+ node = settings[2];
+ is(node.nodeName, "setting", "Should be a setting node");
+ ok(!node.hasAttribute("first-row"), "Not the first row");
+ description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+ is(description.textContent, "Description Text Node", "Description node should contain description");
+
+ node = settings[3];
+ is(node.nodeName, "setting", "Should be a setting node");
+ ok(!node.hasAttribute("first-row"), "Not the first row");
+ description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+ is(description.textContent, "This is a test, all this text should be visible", "Description node should contain description");
+ var button = node.firstElementChild;
+ isnot(button, null, "There should be a button");
+
+ node = settings[4];
+ is_element_hidden(node, "Unsupported settings should not be visible");
+ ok(!node.hasAttribute("first-row"), "Hidden row is not the first row");
+
+ var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Addon with non-inline preferences as optionsURL
+add_test(function() {
+ observer.checkHidden("inlinesettings2@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "noninlinesettings@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkNotDisplayed();
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 0, "Grid should not have settings children");
+
+ var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
+
+// Addon with options.xul, disabling and enabling should hide and show settings UI
+add_test(function() {
+ observer.checkNotHidden();
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+ is(gManagerWindow.gViewController.currentViewId,
+ "addons://detail/inlinesettings1%40tests.mozilla.org",
+ "Current view should not scroll to preferences");
+ checkScrolling(false);
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+
+ // disable
+ var button = gManagerWindow.document.getElementById("detail-disable-btn");
+ button.focus(); // make sure it's in view
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 0, "Grid should not have settings children");
+
+ gCategoryUtilities.openType("extension", function() {
+ var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_hidden(button, "Preferences button should not be visible");
+
+ button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, 0, "Grid should not have settings children");
+
+ // enable
+ var button = gManagerWindow.document.getElementById("detail-enable-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ observer.callback = function() {
+ observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+
+ settings = grid.querySelectorAll("rows > setting");
+ is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ };
+ });
+ });
+ });
+});
+
+
+// Addon with options.xul that requires a restart to disable,
+// disabling and enabling should not hide and show settings UI.
+add_test(function() {
+ observer.checkHidden("inlinesettings1@tests.mozilla.org");
+
+ var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ observer.checkDisplayed("inlinesettings2@tests.mozilla.org");
+
+ var grid = gManagerWindow.document.getElementById("detail-grid");
+ var settings = grid.querySelectorAll("rows > setting");
+ ok(settings.length > 0, "Grid should have settings children");
+
+ // disable
+ var button = gManagerWindow.document.getElementById("detail-disable-btn");
+ button.focus(); // make sure it's in view
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ observer.checkNotHidden();
+
+ settings = grid.querySelectorAll("rows > setting");
+ ok(settings.length > 0, "Grid should still have settings children");
+
+ // cancel pending disable
+ button = gManagerWindow.document.getElementById("detail-enable-btn");
+ button.focus(); // make sure it's in view
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+ observer.checkNotDisplayed();
+
+ gCategoryUtilities.openType("extension", run_next_test);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_install.js b/toolkit/mozapps/extensions/test/browser/browser_install.js
new file mode 100644
index 000000000..3f7d17d37
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_install.js
@@ -0,0 +1,312 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests tha installs and undoing installs show up correctly
+
+var gManagerWindow;
+var gCategoryUtilities;
+
+var gApp = document.getElementById("bundle_brand").getString("brandShortName");
+var gSearchCount = 0;
+
+function test() {
+ requestLongerTimeout(2);
+ waitForExplicitFinish();
+
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+ Services.prefs.setCharPref("extensions.getAddons.search.url", TESTROOT + "browser_install.xml");
+ // Allow http update checks
+ Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ Services.prefs.clearUserPref("extensions.checkUpdateSecurity");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ aAddon.uninstall();
+ finish();
+ });
+ });
+}
+
+function get_node(parent, anonid) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
+}
+
+function installAddon(aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_install1_2.xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function installUpgrade(aCallback) {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ aAddon.findUpdates({
+ onUpdateAvailable: function(aAddon, aInstall) {
+ is(get_list_item_count(), 1, "Should be only one item in the list");
+
+ aInstall.addListener({
+ onDownloadEnded: function() {
+ is(get_list_item_count(), 1, "Should be only one item in the list once the update has started");
+ },
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ aInstall.install();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function cancelInstall(aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_install1_2.xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onDownloadEnded: function(aInstall) {
+ executeSoon(function() {
+ aInstall.cancel();
+ aCallback();
+ });
+ return false;
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function installSearchResult(aCallback) {
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ // Search for something different each time
+ searchBox.value = "foo" + gSearchCount;
+ gSearchCount++;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ let remote = gManagerWindow.document.getElementById("search-filter-remote")
+ EventUtils.synthesizeMouseAtCenter(remote, { }, gManagerWindow);
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should see the search result in the list");
+
+ let status = get_node(item, "install-status");
+ EventUtils.synthesizeMouseAtCenter(get_node(status, "install-remote-btn"), {}, gManagerWindow);
+
+ item.mInstall.addListener({
+ onInstallEnded: function() {
+ executeSoon(aCallback);
+ }
+ });
+ });
+}
+
+function get_list_item_count() {
+ return get_test_items_in_list(gManagerWindow).length;
+}
+
+function check_undo_install() {
+ is(get_list_item_count(), 1, "Should be only one item in the list");
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should see the pending install in the list");
+ // Force XBL to apply
+ item.clientTop;
+ is_element_visible(get_node(item, "pending"), "Pending message should be visible");
+ is(get_node(item, "pending").textContent, "Install Tests will be installed after you restart " + gApp + ".", "Pending message should be correct");
+
+ EventUtils.synthesizeMouseAtCenter(get_node(item, "undo-btn"), {}, gManagerWindow);
+
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!item, "Should no longer see the pending install");
+}
+
+function check_undo_upgrade() {
+ is(get_list_item_count(), 1, "Should be only one item in the list");
+
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should see the pending upgrade in the list");
+ // Force XBL to apply
+ item.clientTop;
+ is_element_visible(get_node(item, "pending"), "Pending message should be visible");
+ is(get_node(item, "pending").textContent, "Install Tests will be updated after you restart " + gApp + ".", "Pending message should be correct");
+
+ EventUtils.synthesizeMouseAtCenter(get_node(item, "undo-btn"), {}, gManagerWindow);
+
+ is(get_list_item_count(), 1, "Should be only one item in the list");
+
+ item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should still see installed item in the list");
+ is_element_hidden(get_node(item, "pending"), "Pending message should be hidden");
+}
+
+// Install an add-on through the API with the manager open
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ installAddon(function() {
+ check_undo_install();
+ run_next_test();
+ });
+ });
+});
+
+// Install an add-on with the manager closed then open it
+add_test(function() {
+ close_manager(gManagerWindow, function() {
+ installAddon(function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ check_undo_install();
+ run_next_test();
+ });
+ });
+ });
+});
+
+// Install an add-on through the search page and then undo it
+add_test(function() {
+ installSearchResult(function() {
+ check_undo_install();
+ run_next_test();
+ });
+});
+
+// Install an add-on through the search page then switch to the extensions page
+// and then undo it
+add_test(function() {
+ installSearchResult(function() {
+ gCategoryUtilities.openType("extension", function() {
+ check_undo_install();
+ run_next_test();
+ });
+ });
+});
+
+// Install an add-on through the search page then re-open the manager and then
+// undo it
+add_test(function() {
+ installSearchResult(function() {
+ close_manager(gManagerWindow, function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ check_undo_install();
+ run_next_test();
+ });
+ });
+ });
+});
+
+// Cancel an install after download with the manager open
+add_test(function() {
+ cancelInstall(function() {
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ run_next_test();
+ });
+});
+
+// Cancel an install after download with the manager closed
+add_test(function() {
+ close_manager(gManagerWindow, function() {
+ cancelInstall(function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(get_list_item_count(), 0, "Should be no items in the list");
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+// Install an existing add-on for the subsequent tests
+add_test(function() {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_install1_1.xpi",
+ function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: run_next_test
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+});
+
+// Install an upgrade through the API with the manager open
+add_test(function() {
+ installAddon(function() {
+ check_undo_upgrade();
+ run_next_test();
+ });
+});
+
+// Install an upgrade through the API with the manager open
+add_test(function() {
+ installUpgrade(function() {
+ check_undo_upgrade();
+ run_next_test();
+ });
+});
+
+// Install an upgrade through the API with the manager closed
+add_test(function() {
+ close_manager(gManagerWindow, function() {
+ installAddon(function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ check_undo_upgrade();
+ run_next_test();
+ });
+ });
+ });
+});
+
+// Cancel an upgrade after download with the manager open
+add_test(function() {
+ cancelInstall(function() {
+ is(get_list_item_count(), 1, "Should be no items in the list");
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should still see installed item in the list");
+ is_element_hidden(get_node(item, "pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+});
+
+// Cancel an upgrade after download with the manager closed
+add_test(function() {
+ close_manager(gManagerWindow, function() {
+ cancelInstall(function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(get_list_item_count(), 1, "Should be no items in the list");
+ let item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ ok(!!item, "Should still see installed item in the list");
+ is_element_hidden(get_node(item, "pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_install.rdf b/toolkit/mozapps/extensions/test/browser/browser_install.rdf
new file mode 100644
index 000000000..7dc0477f0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_install.rdf
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>https://example.com/browser/toolkit/mozapps/extensions/test/browser/browser_install1_3.xpi</em:updateLink>
+ <em:updateHash>sha1:6760e51269941245105a17076afeb5f45621de0e</em:updateHash>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_install.rdf^headers^ b/toolkit/mozapps/extensions/test/browser/browser_install.rdf^headers^
new file mode 100644
index 000000000..2e4f8163b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_install.rdf^headers^
@@ -0,0 +1 @@
+Connection: close
diff --git a/toolkit/mozapps/extensions/test/browser/browser_install.xml b/toolkit/mozapps/extensions/test/browser/browser_install.xml
new file mode 100644
index 000000000..84067a6a3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_install.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <addon>
+ <name>Install Tests</name>
+ <type id='1'>Extension</type>
+ <guid>addon1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test add-on</summary>
+ <description>Test add-on</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="2">http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_install1_3.xpi b/toolkit/mozapps/extensions/test/browser/browser_install1_3.xpi
new file mode 100644
index 000000000..31bb4b2a6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_install1_3.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/browser/browser_installssl.js b/toolkit/mozapps/extensions/test/browser/browser_installssl.js
new file mode 100644
index 000000000..b0726ef9e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_installssl.js
@@ -0,0 +1,374 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const xpi = RELATIVE_DIR + "addons/browser_installssl.xpi";
+const redirect = RELATIVE_DIR + "redirect.sjs?";
+const SUCCESS = 0;
+const NETWORK_FAILURE = AddonManager.ERROR_NETWORK_FAILURE;
+
+const HTTP = "http://example.com/";
+const HTTPS = "https://example.com/";
+const NOCERT = "https://nocert.example.com/";
+const SELFSIGNED = "https://self-signed.example.com/";
+const UNTRUSTED = "https://untrusted.example.com/";
+const EXPIRED = "https://expired.example.com/";
+
+const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
+
+var gTests = [];
+var gStart = 0;
+var gLast = 0;
+var gPendingInstall = null;
+
+function test() {
+ gStart = Date.now();
+ requestLongerTimeout(4);
+ waitForExplicitFinish();
+
+ registerCleanupFunction(function() {
+ var cos = Cc["@mozilla.org/security/certoverride;1"].
+ getService(Ci.nsICertOverrideService);
+ cos.clearValidityOverride("nocert.example.com", -1);
+ cos.clearValidityOverride("self-signed.example.com", -1);
+ cos.clearValidityOverride("untrusted.example.com", -1);
+ cos.clearValidityOverride("expired.example.com", -1);
+
+ try {
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+ }
+ catch (e) {
+ }
+
+ if (gPendingInstall) {
+ gTests = [];
+ ok(false, "Timed out in the middle of downloading " + gPendingInstall.sourceURI.spec);
+ try {
+ gPendingInstall.cancel();
+ }
+ catch (e) {
+ }
+ }
+ });
+
+ run_next_test();
+}
+
+function end_test() {
+ info("All tests completed in " + (Date.now() - gStart) + "ms");
+ finish();
+}
+
+function add_install_test(mainURL, redirectURL, expectedStatus) {
+ gTests.push([mainURL, redirectURL, expectedStatus]);
+}
+
+function run_install_tests(callback) {
+ function run_next_install_test() {
+ if (gTests.length == 0) {
+ callback();
+ return;
+ }
+ gLast = Date.now();
+
+ let [mainURL, redirectURL, expectedStatus] = gTests.shift();
+ if (redirectURL) {
+ var url = mainURL + redirect + redirectURL + xpi;
+ var message = "Should have seen the right result for an install redirected from " +
+ mainURL + " to " + redirectURL;
+ }
+ else {
+ url = mainURL + xpi;
+ message = "Should have seen the right result for an install from " +
+ mainURL;
+ }
+
+ AddonManager.getInstallForURL(url, function(install) {
+ gPendingInstall = install;
+ install.addListener({
+ onDownloadEnded: function(install) {
+ is(SUCCESS, expectedStatus, message);
+ info("Install test ran in " + (Date.now() - gLast) + "ms");
+ // Don't proceed with the install
+ install.cancel();
+ gPendingInstall = null;
+ run_next_install_test();
+ return false;
+ },
+
+ onDownloadFailed: function(install) {
+ is(install.error, expectedStatus, message);
+ info("Install test ran in " + (Date.now() - gLast) + "ms");
+ gPendingInstall = null;
+ run_next_install_test();
+ }
+ });
+ install.install();
+ }, "application/x-xpinstall");
+ }
+
+ run_next_install_test();
+}
+
+// Add overrides for the bad certificates
+function addCertOverrides() {
+ addCertOverride("nocert.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH);
+ addCertOverride("self-signed.example.com", Ci.nsICertOverrideService.ERROR_UNTRUSTED);
+ addCertOverride("untrusted.example.com", Ci.nsICertOverrideService.ERROR_UNTRUSTED);
+ addCertOverride("expired.example.com", Ci.nsICertOverrideService.ERROR_TIME);
+}
+
+// Runs tests with built-in certificates required, no certificate exceptions
+// and no hashes
+add_test(function() {
+ // Tests that a simple install works as expected.
+ add_install_test(HTTP, null, SUCCESS);
+ add_install_test(HTTPS, null, NETWORK_FAILURE);
+ add_install_test(NOCERT, null, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, null, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, null, NETWORK_FAILURE);
+ add_install_test(EXPIRED, null, NETWORK_FAILURE);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_install_test(HTTP, HTTP, SUCCESS);
+ add_install_test(HTTP, HTTPS, SUCCESS);
+ add_install_test(HTTP, NOCERT, NETWORK_FAILURE);
+ add_install_test(HTTP, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(HTTP, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(HTTP, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_install_test(HTTPS, HTTP, NETWORK_FAILURE);
+ add_install_test(HTTPS, HTTPS, NETWORK_FAILURE);
+ add_install_test(HTTPS, NOCERT, NETWORK_FAILURE);
+ add_install_test(HTTPS, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(HTTPS, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(HTTPS, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_install_test(NOCERT, HTTP, NETWORK_FAILURE);
+ add_install_test(NOCERT, HTTPS, NETWORK_FAILURE);
+ add_install_test(NOCERT, NOCERT, NETWORK_FAILURE);
+ add_install_test(NOCERT, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(NOCERT, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(NOCERT, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_install_test(SELFSIGNED, HTTP, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, HTTPS, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, NOCERT, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_install_test(UNTRUSTED, HTTP, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, HTTPS, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, NOCERT, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_install_test(EXPIRED, HTTP, NETWORK_FAILURE);
+ add_install_test(EXPIRED, HTTPS, NETWORK_FAILURE);
+ add_install_test(EXPIRED, NOCERT, NETWORK_FAILURE);
+ add_install_test(EXPIRED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(EXPIRED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(EXPIRED, EXPIRED, NETWORK_FAILURE);
+
+ run_install_tests(run_next_test);
+});
+
+// Runs tests without requiring built-in certificates, no certificate
+// exceptions and no hashes
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ // Tests that a simple install works as expected.
+ add_install_test(HTTP, null, SUCCESS);
+ add_install_test(HTTPS, null, SUCCESS);
+ add_install_test(NOCERT, null, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, null, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, null, NETWORK_FAILURE);
+ add_install_test(EXPIRED, null, NETWORK_FAILURE);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_install_test(HTTP, HTTP, SUCCESS);
+ add_install_test(HTTP, HTTPS, SUCCESS);
+ add_install_test(HTTP, NOCERT, NETWORK_FAILURE);
+ add_install_test(HTTP, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(HTTP, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(HTTP, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_install_test(HTTPS, HTTP, NETWORK_FAILURE);
+ add_install_test(HTTPS, HTTPS, SUCCESS);
+ add_install_test(HTTPS, NOCERT, NETWORK_FAILURE);
+ add_install_test(HTTPS, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(HTTPS, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(HTTPS, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_install_test(NOCERT, HTTP, NETWORK_FAILURE);
+ add_install_test(NOCERT, HTTPS, NETWORK_FAILURE);
+ add_install_test(NOCERT, NOCERT, NETWORK_FAILURE);
+ add_install_test(NOCERT, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(NOCERT, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(NOCERT, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_install_test(SELFSIGNED, HTTP, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, HTTPS, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, NOCERT, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_install_test(UNTRUSTED, HTTP, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, HTTPS, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, NOCERT, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_install_test(EXPIRED, HTTP, NETWORK_FAILURE);
+ add_install_test(EXPIRED, HTTPS, NETWORK_FAILURE);
+ add_install_test(EXPIRED, NOCERT, NETWORK_FAILURE);
+ add_install_test(EXPIRED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(EXPIRED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(EXPIRED, EXPIRED, NETWORK_FAILURE);
+
+ run_install_tests(run_next_test);
+});
+
+// Runs tests with built-in certificates required, all certificate exceptions
+// and no hashes
+add_test(function() {
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+ addCertOverrides();
+
+ // Tests that a simple install works as expected.
+ add_install_test(HTTP, null, SUCCESS);
+ add_install_test(HTTPS, null, NETWORK_FAILURE);
+ add_install_test(NOCERT, null, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, null, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, null, NETWORK_FAILURE);
+ add_install_test(EXPIRED, null, NETWORK_FAILURE);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_install_test(HTTP, HTTP, SUCCESS);
+ add_install_test(HTTP, HTTPS, SUCCESS);
+ add_install_test(HTTP, NOCERT, SUCCESS);
+ add_install_test(HTTP, SELFSIGNED, SUCCESS);
+ add_install_test(HTTP, UNTRUSTED, SUCCESS);
+ add_install_test(HTTP, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_install_test(HTTPS, HTTP, NETWORK_FAILURE);
+ add_install_test(HTTPS, HTTPS, NETWORK_FAILURE);
+ add_install_test(HTTPS, NOCERT, NETWORK_FAILURE);
+ add_install_test(HTTPS, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(HTTPS, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(HTTPS, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_install_test(NOCERT, HTTP, NETWORK_FAILURE);
+ add_install_test(NOCERT, HTTPS, NETWORK_FAILURE);
+ add_install_test(NOCERT, NOCERT, NETWORK_FAILURE);
+ add_install_test(NOCERT, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(NOCERT, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(NOCERT, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_install_test(SELFSIGNED, HTTP, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, HTTPS, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, NOCERT, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_install_test(UNTRUSTED, HTTP, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, HTTPS, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, NOCERT, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, EXPIRED, NETWORK_FAILURE);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_install_test(EXPIRED, HTTP, NETWORK_FAILURE);
+ add_install_test(EXPIRED, HTTPS, NETWORK_FAILURE);
+ add_install_test(EXPIRED, NOCERT, NETWORK_FAILURE);
+ add_install_test(EXPIRED, SELFSIGNED, NETWORK_FAILURE);
+ add_install_test(EXPIRED, UNTRUSTED, NETWORK_FAILURE);
+ add_install_test(EXPIRED, EXPIRED, NETWORK_FAILURE);
+
+ run_install_tests(run_next_test);
+});
+
+// Runs tests without requiring built-in certificates, all certificate
+// exceptions and no hashes
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ // Tests that a simple install works as expected.
+ add_install_test(HTTP, null, SUCCESS);
+ add_install_test(HTTPS, null, SUCCESS);
+ add_install_test(NOCERT, null, SUCCESS);
+ add_install_test(SELFSIGNED, null, SUCCESS);
+ add_install_test(UNTRUSTED, null, SUCCESS);
+ add_install_test(EXPIRED, null, SUCCESS);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_install_test(HTTP, HTTP, SUCCESS);
+ add_install_test(HTTP, HTTPS, SUCCESS);
+ add_install_test(HTTP, NOCERT, SUCCESS);
+ add_install_test(HTTP, SELFSIGNED, SUCCESS);
+ add_install_test(HTTP, UNTRUSTED, SUCCESS);
+ add_install_test(HTTP, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_install_test(HTTPS, HTTP, NETWORK_FAILURE);
+ add_install_test(HTTPS, HTTPS, SUCCESS);
+ add_install_test(HTTPS, NOCERT, SUCCESS);
+ add_install_test(HTTPS, SELFSIGNED, SUCCESS);
+ add_install_test(HTTPS, UNTRUSTED, SUCCESS);
+ add_install_test(HTTPS, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_install_test(NOCERT, HTTP, NETWORK_FAILURE);
+ add_install_test(NOCERT, HTTPS, SUCCESS);
+ add_install_test(NOCERT, NOCERT, SUCCESS);
+ add_install_test(NOCERT, SELFSIGNED, SUCCESS);
+ add_install_test(NOCERT, UNTRUSTED, SUCCESS);
+ add_install_test(NOCERT, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_install_test(SELFSIGNED, HTTP, NETWORK_FAILURE);
+ add_install_test(SELFSIGNED, HTTPS, SUCCESS);
+ add_install_test(SELFSIGNED, NOCERT, SUCCESS);
+ add_install_test(SELFSIGNED, SELFSIGNED, SUCCESS);
+ add_install_test(SELFSIGNED, UNTRUSTED, SUCCESS);
+ add_install_test(SELFSIGNED, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_install_test(UNTRUSTED, HTTP, NETWORK_FAILURE);
+ add_install_test(UNTRUSTED, HTTPS, SUCCESS);
+ add_install_test(UNTRUSTED, NOCERT, SUCCESS);
+ add_install_test(UNTRUSTED, SELFSIGNED, SUCCESS);
+ add_install_test(UNTRUSTED, UNTRUSTED, SUCCESS);
+ add_install_test(UNTRUSTED, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_install_test(EXPIRED, HTTP, NETWORK_FAILURE);
+ add_install_test(EXPIRED, HTTPS, SUCCESS);
+ add_install_test(EXPIRED, NOCERT, SUCCESS);
+ add_install_test(EXPIRED, SELFSIGNED, SUCCESS);
+ add_install_test(EXPIRED, UNTRUSTED, SUCCESS);
+ add_install_test(EXPIRED, EXPIRED, SUCCESS);
+
+ run_install_tests(run_next_test);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_list.js b/toolkit/mozapps/extensions/test/browser/browser_list.js
new file mode 100644
index 000000000..fd6cfed7e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_list.js
@@ -0,0 +1,760 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the list view
+
+let tempScope = {};
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
+let LightweightThemeManager = tempScope.LightweightThemeManager;
+
+
+var gProvider;
+var gManagerWindow;
+var gCategoryUtilities;
+
+var gApp = document.getElementById("bundle_brand").getString("brandShortName");
+var gVersion = Services.appinfo.version;
+var gBlocklistURL = Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL");
+var gPluginURL = Services.urlFormatter.formatURLPref("plugins.update.url");
+var gDate = new Date(2010, 7, 16);
+
+var gLWTheme = {
+ id: "4",
+ version: "1",
+ name: "Bling",
+ description: "SO MUCH BLING!",
+ author: "Pixel Pusher",
+ homepageURL: "http://mochi.test:8888/data/index.html",
+ headerURL: "http://mochi.test:8888/data/header.png",
+ footerURL: "http://mochi.test:8888/data/footer.png",
+ previewURL: "http://mochi.test:8888/data/preview.png",
+ iconURL: "http://mochi.test:8888/data/icon.png"
+ };
+
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test add-on",
+ version: "1.0",
+ description: "A test add-on",
+ longDescription: " A longer description",
+ updateDate: gDate
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "Test add-on 2",
+ version: "2.0",
+ longDescription: " A longer description",
+ _userDisabled: true,
+ isActive: false,
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "Test add-on 3",
+ longDescription: " A longer description",
+ isActive: false,
+ isCompatible: false,
+ appDisabled: true,
+ permissions: AddonManager.PERM_CAN_ENABLE |
+ AddonManager.PERM_CAN_DISABLE |
+ AddonManager.PERM_CAN_UPGRADE
+ }, {
+ id: "addon4@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon4@tests.mozilla.org",
+ name: "Test add-on 4",
+ _userDisabled: true,
+ isActive: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED
+ }, {
+ id: "addon5@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon5@tests.mozilla.org",
+ name: "Test add-on 5",
+ isActive: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
+ appDisabled: true
+ }, {
+ id: "addon6@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon6@tests.mozilla.org",
+ name: "Test add-on 6",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon7@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon7@tests.mozilla.org",
+ name: "Test add-on 7",
+ blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED,
+ }, {
+ id: "addon8@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon8@tests.mozilla.org",
+ name: "Test add-on 8",
+ blocklistState: Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE,
+ }, {
+ id: "addon9@tests.mozilla.org",
+ blocklistURL: "http://example.com/addon9@tests.mozilla.org",
+ name: "Test add-on 9",
+ blocklistState: Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE,
+ }]);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+function get_test_items() {
+ var tests = "@tests.mozilla.org";
+
+ var items = {};
+ var item = gManagerWindow.document.getElementById("addon-list").firstChild;
+
+ while (item) {
+ if (item.mAddon.id.substring(item.mAddon.id.length - tests.length) == tests)
+ items[item.mAddon.name] = item;
+ item = item.nextSibling;
+ }
+
+ return items;
+}
+
+function get_node(parent, anonid) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
+}
+
+function get_class_node(parent, cls) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "class", cls);
+}
+
+// Check that the list appears to have displayed correctly and trigger some
+// changes
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ let items = get_test_items();
+ is(Object.keys(items).length, 9, "Should be nine add-ons installed");
+
+ info("Addon 1");
+ let addon = items["Test add-on"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on", "Name should be correct");
+ is_element_visible(get_node(addon, "version"), "Version should be visible");
+ is(get_node(addon, "version").value, "1.0", "Version should be correct");
+ is_element_visible(get_node(addon, "description"), "Description should be visible");
+ is(get_node(addon, "description").value, "A test add-on", "Description should be correct");
+ is_element_hidden(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be hidden");
+ is_element_hidden(get_class_node(addon, "update-postfix"), "Update postfix should be hidden");
+ is(get_node(addon, "date-updated").value, formatDate(gDate), "Update date should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Disabling");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "disable-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be visible");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Addon 2");
+ addon = items["Test add-on 2"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 2", "Name should be correct");
+ is_element_visible(get_node(addon, "version"), "Version should be visible");
+ is(get_node(addon, "version").value, "2.0", "Version should be correct");
+ is_element_hidden(get_node(addon, "description"), "Description should be hidden");
+ is_element_visible(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be visible");
+ is_element_hidden(get_class_node(addon, "update-postfix"), "Update postfix should be hidden");
+ is(get_node(addon, "date-updated").value, "Unknown", "Date should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Enabling");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "enable-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on 2 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Addon 3");
+ addon = items["Test add-on 3"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 3", "Name should be correct");
+ is_element_hidden(get_node(addon, "version"), "Version should be hidden");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_hidden(get_node(addon, "remove-btn"), "Remove button should be hidden");
+
+ is_element_visible(get_node(addon, "warning"), "Warning message should be visible");
+ is(get_node(addon, "warning").textContent, "Test add-on 3 is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 4");
+ addon = items["Test add-on 4"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 4", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_visible(get_node(addon, "warning"), "Warning message should be visible");
+ is(get_node(addon, "warning").textContent, "Test add-on 4 is known to cause security or stability issues.", "Warning message should be correct");
+ is_element_visible(get_node(addon, "warning-link"), "Warning link should be visible");
+ is(get_node(addon, "warning-link").value, "More Information", "Warning link text should be correct");
+ is(get_node(addon, "warning-link").href, "http://example.com/addon4@tests.mozilla.org", "Warning link should be correct");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Enabling");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "enable-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on 4 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Addon 5");
+ addon = items["Test add-on 5"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 5", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_visible(get_node(addon, "error"), "Error message should be visible");
+ is(get_node(addon, "error").textContent, "Test add-on 5 has been disabled due to security or stability issues.", "Error message should be correct");
+ is_element_visible(get_node(addon, "error-link"), "Error link should be visible");
+ is(get_node(addon, "error-link").value, "More Information", "Error link text should be correct");
+ is(get_node(addon, "error-link").href, "http://example.com/addon5@tests.mozilla.org", "Error link should be correct");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 6");
+ addon = items["Test add-on 6"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 6", "Name should be correct");
+ is_element_hidden(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be hidden");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be visible");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Disabling");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "disable-btn"), {}, gManagerWindow);
+ is_element_visible(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be visible");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be visible");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 7");
+ addon = items["Test add-on 7"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 7", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_visible(get_node(addon, "warning"), "Warning message should be hidden");
+ is(get_node(addon, "warning").textContent, "An important update is available for Test add-on 7.", "Warning message should be correct");
+ is_element_visible(get_node(addon, "warning-link"), "Warning link should be visible");
+ is(get_node(addon, "warning-link").value, "Update Now", "Warning link text should be correct");
+ is(get_node(addon, "warning-link").href, gPluginURL, "Warning link should be correct");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Disabling");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "disable-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be visible");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on 7 will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Addon 8");
+ addon = items["Test add-on 8"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 8", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_visible(get_node(addon, "error"), "Error message should be visible");
+ is(get_node(addon, "error").textContent, "Test add-on 8 is known to be vulnerable and should be updated.", "Error message should be correct");
+ is_element_visible(get_node(addon, "error-link"), "Error link should be visible");
+ is(get_node(addon, "error-link").value, "Update Now", "Error link text should be correct");
+ is(get_node(addon, "error-link").href, "http://example.com/addon8@tests.mozilla.org", "Error link should be correct");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 9");
+ addon = items["Test add-on 9"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 9", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_visible(get_node(addon, "error"), "Error message should be visible");
+ is(get_node(addon, "error").textContent, "Test add-on 9 is known to be vulnerable. Use with caution.", "Error message should be correct");
+ is_element_visible(get_node(addon, "error-link"), "Error link should be visible");
+ is(get_node(addon, "error-link").value, "More Information", "Error link text should be correct");
+ is(get_node(addon, "error-link").href, "http://example.com/addon9@tests.mozilla.org", "Error link should be correct");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+});
+
+// Check the add-ons are now in the right state
+add_test(function() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1, a2, a4, a6]) {
+ is(a1.pendingOperations, AddonManager.PENDING_DISABLE, "Add-on 1 should be pending disable");
+ is(a2.pendingOperations, AddonManager.PENDING_ENABLE, "Add-on 2 should be pending enable");
+ is(a4.pendingOperations, AddonManager.PENDING_ENABLE, "Add-on 4 should be pending enable");
+
+ run_next_test();
+ });
+});
+
+// Reload the list to make sure the changes are still pending and that undoing
+// works
+add_test(function() {
+ gCategoryUtilities.openType("plugin", function() {
+ gCategoryUtilities.openType("extension", function() {
+ let items = get_test_items();
+ is(Object.keys(items).length, 9, "Should be nine add-ons installed");
+
+ info("Addon 1");
+ let addon = items["Test add-on"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on", "Name should be correct");
+ is_element_visible(get_node(addon, "version"), "Version should be visible");
+ is(get_node(addon, "version").value, "1.0", "Version should be correct");
+ is_element_visible(get_node(addon, "description"), "Description should be visible");
+ is(get_node(addon, "description").value, "A test add-on", "Description should be correct");
+ is_element_hidden(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be hidden");
+ is_element_hidden(get_class_node(addon, "update-postfix"), "Update postfix should be hidden");
+ is(get_node(addon, "date-updated").value, formatDate(gDate), "Update date should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Undoing");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 2");
+ addon = items["Test add-on 2"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 2", "Name should be correct");
+ is_element_visible(get_node(addon, "version"), "Version should be visible");
+ is(get_node(addon, "version").value, "2.0", "Version should be correct");
+ is_element_hidden(get_node(addon, "description"), "Description should be hidden");
+ is_element_visible(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be visible");
+ is_element_hidden(get_class_node(addon, "update-postfix"), "Update postfix should be hidden");
+ is(get_node(addon, "date-updated").value, "Unknown", "Date should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on 2 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Undoing");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 4");
+ addon = items["Test add-on 4"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 4", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on 4 will be enabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Undoing");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_visible(get_node(addon, "warning"), "Warning message should be visible");
+ is(get_node(addon, "warning").textContent, "Test add-on 4 is known to cause security or stability issues.", "Warning message should be correct");
+ is_element_visible(get_node(addon, "warning-link"), "Warning link should be visible");
+ is(get_node(addon, "warning-link").value, "More Information", "Warning link text should be correct");
+ is(get_node(addon, "warning-link").href, "http://example.com/addon4@tests.mozilla.org", "Warning link should be correct");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 6");
+ addon = items["Test add-on 6"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 6", "Name should be correct");
+ is_element_visible(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be visible");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be visible");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Enabling");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "enable-btn"), {}, gManagerWindow);
+ is_element_hidden(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be hidden");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be visible");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ info("Addon 7");
+ addon = items["Test add-on 7"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on 7", "Name should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be visible");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_visible(get_node(addon, "pending"), "Pending message should be visible");
+ is(get_node(addon, "pending").textContent, "Test add-on 7 will be disabled after you restart " + gApp + ".", "Pending message should be correct");
+
+ info("Undoing");
+ EventUtils.synthesizeMouseAtCenter(get_node(addon, "undo-btn"), {}, gManagerWindow);
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_visible(get_node(addon, "warning"), "Warning message should be hidden");
+ is(get_node(addon, "warning").textContent, "An important update is available for Test add-on 7.", "Warning message should be correct");
+ is_element_visible(get_node(addon, "warning-link"), "Warning link should be visible");
+ is(get_node(addon, "warning-link").value, "Update Now", "Warning link text should be correct");
+ is(get_node(addon, "warning-link").href, gPluginURL, "Warning link should be correct");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ run_next_test();
+ });
+ });
+});
+
+// Check the add-ons are now in the right state
+add_test(function() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a4]) {
+ is(a1.pendingOperations, 0, "Add-on 1 should not have any pending operations");
+ is(a2.pendingOperations, 0, "Add-on 1 should not have any pending operations");
+ is(a4.pendingOperations, 0, "Add-on 1 should not have any pending operations");
+
+ run_next_test();
+ });
+});
+
+// Check that upgrades with onExternalInstall take effect immediately
+add_test(function() {
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test add-on replacement",
+ version: "2.0",
+ description: "A test add-on with a new description",
+ updateDate: gDate,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }]);
+
+ let items = get_test_items();
+ is(Object.keys(items).length, 9, "Should be nine add-ons installed");
+
+ let addon = items["Test add-on replacement"];
+ addon.parentNode.ensureElementIsVisible(addon);
+ is(get_node(addon, "name").value, "Test add-on replacement", "Name should be correct");
+ is_element_visible(get_node(addon, "version"), "Version should be visible");
+ is(get_node(addon, "version").value, "2.0", "Version should be correct");
+ is_element_visible(get_node(addon, "description"), "Description should be visible");
+ is(get_node(addon, "description").value, "A test add-on with a new description", "Description should be correct");
+ is_element_hidden(get_class_node(addon, "disabled-postfix"), "Disabled postfix should be hidden");
+ is_element_hidden(get_class_node(addon, "update-postfix"), "Update postfix should be hidden");
+ is(get_node(addon, "date-updated").value, formatDate(gDate), "Update date should be correct");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
+ is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
+ is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
+ is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
+ is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
+
+ run_next_test();
+});
+
+// Check that focus changes correctly move around the selected list item
+add_test(function() {
+ function is_node_in_list(aNode) {
+ var list = gManagerWindow.document.getElementById("addon-list");
+
+ while (aNode && aNode != list)
+ aNode = aNode.parentNode;
+
+ if (aNode)
+ return true;
+ return false;
+ }
+
+ // Ignore the OSX full keyboard access setting
+ Services.prefs.setBoolPref("accessibility.tabfocus_applies_to_xul", false);
+
+ let items = get_test_items();
+
+ var fm = Cc["@mozilla.org/focus-manager;1"].
+ getService(Ci.nsIFocusManager);
+
+ let addon = items["Test add-on 6"];
+ EventUtils.synthesizeMouseAtCenter(addon, { }, gManagerWindow);
+ is(fm.focusedElement, addon.parentNode, "Focus should have moved to the list");
+
+ EventUtils.synthesizeKey("VK_TAB", { }, gManagerWindow);
+ is(fm.focusedElement, get_node(addon, "details-btn"), "Focus should have moved to the more button");
+
+ EventUtils.synthesizeKey("VK_TAB", { }, gManagerWindow);
+ is(fm.focusedElement, get_node(addon, "disable-btn"), "Focus should have moved to the disable button");
+
+ EventUtils.synthesizeKey("VK_TAB", { }, gManagerWindow);
+ is(fm.focusedElement, get_node(addon, "remove-btn"), "Focus should have moved to the remove button");
+
+ EventUtils.synthesizeKey("VK_TAB", { }, gManagerWindow);
+ ok(!is_node_in_list(fm.focusedElement), "Focus should be outside the list");
+
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }, gManagerWindow);
+ is(fm.focusedElement, get_node(addon, "remove-btn"), "Focus should have moved to the remove button");
+
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }, gManagerWindow);
+ is(fm.focusedElement, get_node(addon, "details-btn"), "Focus should have moved to the more button");
+
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }, gManagerWindow);
+ is(fm.focusedElement, addon.parentNode, "Focus should have moved to the list");
+
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }, gManagerWindow);
+ ok(!is_node_in_list(fm.focusedElement), "Focus should be outside the list");
+
+ try {
+ Services.prefs.clearUserPref("accessibility.tabfocus_applies_to_xul");
+ }
+ catch (e) { }
+
+ run_next_test();
+});
+
+
+add_test(function() {
+ info("Enabling lightweight theme");
+ LightweightThemeManager.currentTheme = gLWTheme;
+
+ gManagerWindow.loadView("addons://list/theme");
+ wait_for_view_load(gManagerWindow, function() {
+ var addon = get_addon_element(gManagerWindow, "4@personas.mozilla.org");
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ info("Disabling lightweight theme");
+ LightweightThemeManager.currentTheme = null;
+
+ is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
+ is_element_visible(get_node(addon, "enable-btn"), "Enable button should be hidden");
+ is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be visible");
+ is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
+
+ AddonManager.getAddonByID("4@personas.mozilla.org", function(aAddon) {
+ aAddon.uninstall();
+ run_next_test();
+ });
+ });
+});
+
+// Check that onPropertyChanges for appDisabled updates the UI
+add_test(function() {
+ info("Checking that onPropertyChanges for appDisabled updates the UI");
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
+ aAddon.userDisabled = true;
+ aAddon.isCompatible = true;
+ aAddon.appDisabled = false;
+
+ gManagerWindow.loadView("addons://list/extension");
+ wait_for_view_load(gManagerWindow, function() {
+ var el = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
+
+ is(el.getAttribute("active"), "false", "Addon should not be marked as active");
+ is_element_hidden(get_node(el, "warning"), "Warning message should not be visible");
+
+ info("Making addon incompatible and appDisabled");
+ aAddon.isCompatible = false;
+ aAddon.appDisabled = true;
+
+ is(el.getAttribute("active"), "false", "Addon should not be marked as active");
+ is_element_visible(get_node(el, "warning"), "Warning message should be visible");
+ is(get_node(el, "warning").textContent, "Test add-on 2 is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
+
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js b/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js
new file mode 100644
index 000000000..27a4a6cd8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js
@@ -0,0 +1,242 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests manual updates, including the Available Updates pane
+
+var gProvider;
+var gManagerWindow;
+var gCategoryUtilities;
+var gAvailableCategory;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "auto updating addon",
+ version: "1.0",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+
+add_test(function() {
+ gAvailableCategory = gManagerWindow.gCategories.get("addons://updates/available");
+ is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should initially be hidden");
+
+ gProvider.createAddons([{
+ id: "addon2@tests.mozilla.org",
+ name: "manually updating addon",
+ version: "1.0",
+ isCompatible: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+ }]);
+
+ is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should still be hidden");
+
+ run_next_test();
+});
+
+
+add_test(function() {
+ gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
+ gAvailableCategory.removeEventListener("CategoryBadgeUpdated", arguments.callee, false);
+ is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should now be visible");
+ is(gAvailableCategory.badgeCount, 1, "Badge for Available Updates should now be 1");
+ run_next_test();
+ }, false);
+
+ gCategoryUtilities.openType("extension", function() {
+ gProvider.createInstalls([{
+ name: "manually updating addon (new and improved!)",
+ existingAddon: gProvider.addons[1],
+ version: "1.1",
+ releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml", null, null)
+ }]);
+
+ var item = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
+ is(item._version.value, "1.0", "Should still show the old version in the normal list");
+ });
+});
+
+
+add_test(function() {
+ wait_for_view_load(gManagerWindow, function() {
+ is(gManagerWindow.document.getElementById("categories").selectedItem.value, "addons://updates/available", "Available Updates category should now be selected");
+ is(gManagerWindow.gViewController.currentViewId, "addons://updates/available", "Available Updates view should be the current view");
+ run_next_test();
+ }, true);
+ EventUtils.synthesizeMouseAtCenter(gAvailableCategory, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ var list = gManagerWindow.document.getElementById("updates-list");
+ is(list.itemCount, 1, "Should be 1 available update listed");
+ var item = list.firstChild;
+ is(item.mAddon.id, "addon2@tests.mozilla.org", "Update item should be for the manually updating addon");
+
+ // for manual update items, update-related properties are updated asynchronously,
+ // so we poll for one of the expected changes to know when its done
+ function waitForAsyncInit() {
+ if (item._version.value == "1.1") {
+ run_next_test();
+ return;
+ }
+ info("Update item not initialized yet, checking again in 100ms");
+ setTimeout(waitForAsyncInit, 100);
+ }
+ waitForAsyncInit();
+});
+
+add_test(function() {
+ var list = gManagerWindow.document.getElementById("updates-list");
+ var item = list.firstChild;
+ is(item._version.value, "1.1", "Update item should have version number of the update");
+ var postfix = gManagerWindow.document.getAnonymousElementByAttribute(item, "class", "update-postfix");
+ is_element_visible(postfix, "'Update' postfix should be visible");
+ is_element_visible(item._updateAvailable, "");
+ is_element_visible(item._relNotesToggle, "Release notes toggle should be visible");
+ is_element_hidden(item._warning, "Incompatible warning should be hidden");
+ is_element_hidden(item._error, "Blocklist error should be hidden");
+
+ info("Opening release notes");
+ item.addEventListener("RelNotesToggle", function() {
+ item.removeEventListener("RelNotesToggle", arguments.callee, false);
+ info("Release notes now open");
+
+ is_element_hidden(item._relNotesLoading, "Release notes loading message should be hidden");
+ is_element_visible(item._relNotesError, "Release notes error message should be visible");
+ is(item._relNotes.childElementCount, 0, "Release notes should be empty");
+
+ info("Closing release notes");
+ item.addEventListener("RelNotesToggle", function() {
+ item.removeEventListener("RelNotesToggle", arguments.callee, false);
+ info("Release notes now closed");
+ info("Setting Release notes URI to something that should load");
+ gProvider.installs[0].releaseNotesURI = Services.io.newURI(TESTROOT + "releaseNotes.xhtml", null, null)
+
+ info("Re-opening release notes");
+ item.addEventListener("RelNotesToggle", function() {
+ item.removeEventListener("RelNotesToggle", arguments.callee, false);
+ info("Release notes now open");
+
+ is_element_hidden(item._relNotesLoading, "Release notes loading message should be hidden");
+ is_element_hidden(item._relNotesError, "Release notes error message should be hidden");
+ isnot(item._relNotes.childElementCount, 0, "Release notes should have been inserted into container");
+ run_next_test();
+
+ }, false);
+ EventUtils.synthesizeMouseAtCenter(item._relNotesToggle, { }, gManagerWindow);
+ is_element_visible(item._relNotesLoading, "Release notes loading message should be visible");
+
+ }, false);
+ EventUtils.synthesizeMouseAtCenter(item._relNotesToggle, { }, gManagerWindow);
+
+ }, false);
+ EventUtils.synthesizeMouseAtCenter(item._relNotesToggle, { }, gManagerWindow);
+ is_element_visible(item._relNotesLoading, "Release notes loading message should be visible");
+});
+
+
+add_test(function() {
+ var badgeUpdated = false;
+ var installCompleted = false;
+
+ gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
+ gAvailableCategory.removeEventListener("CategoryBadgeUpdated", arguments.callee, false);
+ if (installCompleted)
+ run_next_test();
+ else
+ badgeUpdated = true;
+ }, false);
+
+ var list = gManagerWindow.document.getElementById("updates-list");
+ var item = list.firstChild;
+ var updateBtn = item._updateBtn;
+ is_element_visible(updateBtn, "Update button should be visible");
+
+ var install = gProvider.installs[0];
+ var listener = {
+ onInstallStarted: function() {
+ info("Install started");
+ is_element_visible(item._installStatus, "Install progress widget should be visible");
+ },
+ onInstallEnded: function() {
+ install.removeTestListener(this);
+ info("Install ended");
+ is_element_hidden(item._installStatus, "Install progress widget should be hidden");
+
+ if (badgeUpdated)
+ run_next_test();
+ else
+ installCompleted = true;
+ }
+ };
+ install.addTestListener(listener);
+ EventUtils.synthesizeMouseAtCenter(updateBtn, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should still be visible");
+ is(gAvailableCategory.badgeCount, 0, "Badge for Available Updates should now be 0");
+
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should be hidden");
+
+ close_manager(gManagerWindow, function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ gAvailableCategory = gManagerWindow.gCategories.get("addons://updates/available");
+
+ is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should be hidden");
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function() {
+ gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
+ gAvailableCategory.removeEventListener("CategoryBadgeUpdated", arguments.callee, false);
+ is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should now be visible");
+ is(gAvailableCategory.badgeCount, 1, "Badge for Available Updates should now be 1");
+
+ gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
+ gAvailableCategory.removeEventListener("CategoryBadgeUpdated", arguments.callee, false);
+ is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should now be hidden");
+
+ run_next_test();
+ }, false);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
+ aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ });
+ }, false);
+
+ gProvider.createInstalls([{
+ name: "manually updating addon (new and even more improved!)",
+ existingAddon: gProvider.addons[1],
+ version: "1.2",
+ releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml", null, null)
+ }]);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_metadataTimeout.js b/toolkit/mozapps/extensions/test/browser/browser_metadataTimeout.js
new file mode 100644
index 000000000..e2aae6c13
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_metadataTimeout.js
@@ -0,0 +1,114 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test how update window behaves when metadata ping times out
+// bug 965788
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_MIN_PLATFORM_COMPAT = "extensions.minCompatiblePlatformVersion";
+const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+let repo = {};
+let ARContext = Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm", repo);
+
+// Mock out the XMLHttpRequest factory for AddonRepository so
+// we can reply with a timeout
+let pXHRStarted = Promise.defer();
+let oldXHRConstructor = ARContext.XHRequest;
+ARContext.XHRequest = function() {
+ this._handlers = new Map();
+ this.mozBackgroundRequest = false;
+ this.timeout = undefined;
+ this.open = function(aMethod, aURI, aAsync) {
+ this.method = aMethod;
+ this.uri = aURI;
+ this.async = aAsync;
+ info("Opened XHR for " + aMethod + " " + aURI);
+ };
+ this.overrideMimeType = function(aMimeType) {
+ this.mimeType = aMimeType;
+ };
+ this.addEventListener = function(aEvent, aHandler, aCapture) {
+ this._handlers.set(aEvent, aHandler);
+ };
+ this.send = function(aBody) {
+ info("Send XHR for " + this.method + " " + this.uri + " handlers: " + [this._handlers.keys()].join(", "));
+ pXHRStarted.resolve(this);
+ }
+};
+
+
+// Returns promise{window}, resolves with a handle to the compatibility
+// check window
+function promise_open_compatibility_window(aInactiveAddonIds) {
+ let deferred = Promise.defer();
+ // This will reset the longer timeout multiplier to 2 which will give each
+ // test that calls open_compatibility_window a minimum of 60 seconds to
+ // complete.
+ requestLongerTimeout(2);
+
+ var variant = Cc["@mozilla.org/variant;1"].
+ createInstance(Ci.nsIWritableVariant);
+ variant.setFromVariant(aInactiveAddonIds);
+
+ // Cannot be modal as we want to interract with it, shouldn't cause problems
+ // with testing though.
+ var features = "chrome,centerscreen,dialog,titlebar";
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ var win = ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
+
+ win.addEventListener("load", function() {
+ function page_shown(aEvent) {
+ if (aEvent.target.pageid)
+ info("Page " + aEvent.target.pageid + " shown");
+ }
+
+ win.removeEventListener("load", arguments.callee, false);
+
+ info("Compatibility dialog opened");
+
+ win.addEventListener("pageshow", page_shown, false);
+ win.addEventListener("unload", function() {
+ win.removeEventListener("unload", arguments.callee, false);
+ win.removeEventListener("pageshow", page_shown, false);
+ dump("Compatibility dialog closed\n");
+ }, false);
+
+ deferred.resolve(win);
+ }, false);
+ return deferred.promise;
+}
+
+function promise_window_close(aWindow) {
+ let deferred = Promise.defer();
+ aWindow.addEventListener("unload", function() {
+ aWindow.removeEventListener("unload", arguments.callee, false);
+ deferred.resolve(aWindow);
+ }, false);
+ return deferred.promise;
+}
+
+// Start the compatibility update dialog, but use the mock XHR to respond with
+// a timeout
+add_task(function* amo_ping_timeout() {
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+ let compatWindow = yield promise_open_compatibility_window([]);
+
+ let xhr = yield pXHRStarted.promise;
+ is(xhr.timeout, 30000, "XHR request should have 30 second timeout");
+ ok(xhr._handlers.has("timeout"), "Timeout handler set on XHR");
+ // call back the timeout handler
+ xhr._handlers.get("timeout")();
+
+ // Put the old XHR constructor back
+ ARContext.XHRequest = oldXHRConstructor;
+ // The window should close without further interaction
+ yield promise_window_close(compatWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_newaddon.js b/toolkit/mozapps/extensions/test/browser/browser_newaddon.js
new file mode 100644
index 000000000..a460d79d8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_newaddon.js
@@ -0,0 +1,186 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the new add-on tab
+
+var gProvider;
+
+function loadPage(aURL, aCallback) {
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(aURL);
+ gBrowser.addEventListener("AddonDisplayed", function(event) {
+ gBrowser.removeEventListener("AddonDisplayed", arguments.callee, false);
+
+ aCallback(gBrowser.selectedTab);
+ });
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Test 1",
+ version: "5.3",
+ userDisabled: true,
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "Test 2",
+ version: "7.1",
+ creator: "Dave Townsend",
+ userDisabled: true
+ }]);
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+// Tests that ignoring a restartless add-on works
+add_test(function() {
+ loadPage("about:newaddon?id=addon1@tests.mozilla.org", function(aTab) {
+ var doc = aTab.linkedBrowser.contentDocument;
+ is(doc.getElementById("name").value, "Test 1 5.3", "Should say the right name");
+
+ is_element_hidden(doc.getElementById("author"), "Should be no author displayed");
+ is_element_hidden(doc.getElementById("location"), "Should be no location displayed");
+
+ is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
+ "Should be showing the right buttons");
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ is(gBrowser.tabs.length, 1, "Page should have been closed");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ ok(aAddon.userDisabled, "Add-on should not have been enabled");
+
+ ok(!aAddon.isActive, "Add-on should not be running");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that enabling a restartless add-on works
+add_test(function() {
+ loadPage("about:newaddon?id=addon1@tests.mozilla.org", function(aTab) {
+ var doc = aTab.linkedBrowser.contentDocument;
+ is(doc.getElementById("name").value, "Test 1 5.3", "Should say the right name");
+
+ is_element_hidden(doc.getElementById("author"), "Should be no author displayed");
+ is_element_hidden(doc.getElementById("location"), "Should be no location displayed");
+
+ is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
+ "Should be showing the right buttons");
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("allow"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ is(gBrowser.tabs.length, 1, "Page should have been closed");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ ok(!aAddon.userDisabled, "Add-on should now have been enabled");
+
+ ok(aAddon.isActive, "Add-on should now be running");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that ignoring a non-restartless add-on works
+add_test(function() {
+ loadPage("about:newaddon?id=addon2@tests.mozilla.org", function(aTab) {
+ var doc = aTab.linkedBrowser.contentDocument;
+ is(doc.getElementById("name").value, "Test 2 7.1", "Should say the right name");
+
+ is_element_visible(doc.getElementById("author"), "Should be an author displayed");
+ is(doc.getElementById("author").value, "By Dave Townsend", "Should have the right author");
+ is_element_hidden(doc.getElementById("location"), "Should be no location displayed");
+
+ is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
+ "Should be showing the right buttons");
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ is(gBrowser.tabs.length, 1, "Page should have been closed");
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
+ ok(aAddon.userDisabled, "Add-on should not have been enabled");
+
+ ok(!aAddon.isActive, "Add-on should not be running");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that enabling a non-restartless add-on works
+add_test(function() {
+ loadPage("about:newaddon?id=addon2@tests.mozilla.org", function(aTab) {
+ var doc = aTab.linkedBrowser.contentDocument;
+ is(doc.getElementById("name").value, "Test 2 7.1", "Should say the right name");
+
+ is_element_visible(doc.getElementById("author"), "Should be an author displayed");
+ is(doc.getElementById("author").value, "By Dave Townsend", "Should have the right author");
+ is_element_hidden(doc.getElementById("location"), "Should be no location displayed");
+
+ is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
+ "Should be showing the right buttons");
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("allow"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("restartPanel"),
+ "Should be showing the right buttons");
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
+ ok(!aAddon.userDisabled, "Add-on should now have been enabled");
+
+ ok(!aAddon.isActive, "Add-on should not be running");
+
+ ok(doc.getElementById("allow").disabled, "Should have disabled checkbox");
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("cancel-button"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
+ "Should be showing the right buttons");
+
+ ok(!doc.getElementById("allow").disabled, "Should have enabled checkbox");
+
+ ok(aAddon.userDisabled, "Add-on should not have been enabled");
+
+ ok(!aAddon.isActive, "Add-on should not be running");
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("allow"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
+ {}, aTab.linkedBrowser.contentWindow);
+
+ ok(aAddon.userDisabled, "Add-on should not have been enabled");
+
+ ok(!aAddon.isActive, "Add-on should not be running");
+
+ is(gBrowser.tabs.length, 1, "Page should have been closed");
+
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_openDialog.js b/toolkit/mozapps/extensions/test/browser/browser_openDialog.js
new file mode 100644
index 000000000..bdbe9caee
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_openDialog.js
@@ -0,0 +1,176 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the dialog open by the Options button for addons that provide a
+// custom chrome-like protocol for optionsURL.
+
+let CustomChromeProtocol = {
+ scheme: "khrome",
+ defaultPort: -1,
+ protocolFlags: Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
+ Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE |
+ Ci.nsIProtocolHandler.URI_NORELATIVE |
+ Ci.nsIProtocolHandler.URI_NOAUTH,
+
+ newURI: function CCP_newURI(aSpec, aOriginCharset, aBaseUri) {
+ let uri = Cc["@mozilla.org/network/simple-uri;1"].
+ createInstance(Ci.nsIURI);
+ uri.spec = aSpec;
+ return uri;
+ },
+
+ newChannel: function CCP_newChannel(aURI) {
+ let url = "chrome:" + aURI.path;
+ let ch = NetUtil.newChannel2(url,
+ null,
+ null,
+ null, // aLoadingNode
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_NORMAL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ ch.originalURI = aURI;
+ return ch;
+ },
+
+ allowPort: function CCP_allowPort(aPort, aScheme) {
+ return false;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIProtocolHandler
+ ]),
+
+ classID: Components.ID("{399cb2d1-05dd-4363-896f-63b78e008cf8}"),
+
+ factory: {
+ registrar: Components.manager.QueryInterface(Ci.nsIComponentRegistrar),
+
+ register: function CCP_register() {
+ this.registrar.registerFactory(
+ CustomChromeProtocol.classID,
+ "CustomChromeProtocol",
+ "@mozilla.org/network/protocol;1?name=khrome",
+ this
+ );
+ },
+
+ unregister: function CCP_register() {
+ this.registrar.unregisterFactory(CustomChromeProtocol.classID, this);
+ },
+
+ // nsIFactory
+ createInstance: function BNPH_createInstance(aOuter, aIID) {
+ if (aOuter) {
+ throw Components.Exception("Class does not allow aggregation",
+ Components.results.NS_ERROR_NO_AGGREGATION);
+ }
+ return CustomChromeProtocol.QueryInterface(aIID);
+ },
+
+ lockFactory: function BNPH_lockFactory(aLock) {
+ throw Components.Exception("Function lockFactory is not implemented",
+ Components.results.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIFactory
+ ])
+ }
+}
+
+function test() {
+ waitForExplicitFinish();
+ requestLongerTimeout(2);
+
+ info("Registering custom chrome-like protocol.");
+ CustomChromeProtocol.factory.register();
+ registerCleanupFunction(function () CustomChromeProtocol.factory.unregister());
+
+ const ADDONS_LIST = [
+ { id: "test1@tests.mozilla.org",
+ name: "Test add-on 1",
+ optionsURL: CHROMEROOT + "addon_prefs.xul" },
+ { id: "test2@tests.mozilla.org",
+ name: "Test add-on 2",
+ optionsURL: (CHROMEROOT + "addon_prefs.xul").replace("chrome:", "khrome:") },
+ ];
+
+ var gProvider = new MockProvider();
+ gProvider.createAddons(ADDONS_LIST);
+
+ open_manager("addons://list/extension", function(aManager) {
+ let addonList = aManager.document.getElementById("addon-list");
+ let currentAddon;
+ let instantApply = Services.prefs.getBoolPref("browser.preferences.instantApply");
+
+ function getAddonByName(aName) {
+ for (let addonItem of addonList.childNodes) {
+ if (addonItem.hasAttribute("name") &&
+ addonItem.getAttribute("name") == aName)
+ return addonItem;
+ }
+ return null;
+ }
+
+ function observer(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "domwindowclosed":
+ // Give the preference window a chance to finish closing before
+ // closing the add-ons manager.
+ waitForFocus(function () {
+ test_next_addon();
+ });
+ break;
+ case "domwindowopened":
+ let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+ waitForFocus(function () {
+ // If the openDialog privileges are wrong a new browser window
+ // will open, let the test proceed (and fail) rather than timeout.
+ if (win.location != currentAddon.optionsURL &&
+ win.location != "chrome://browser/content/browser.xul")
+ return;
+
+ is(win.location, currentAddon.optionsURL,
+ "The correct addon pref window should have opened");
+
+ let chromeFlags = win.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebNavigation).
+ QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIXULWindow).chromeFlags;
+ ok(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME &&
+ (instantApply || chromeFlags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG),
+ "Window was open as a chrome dialog.");
+
+ win.close();
+ }, win);
+ break;
+ }
+ }
+
+ function test_next_addon() {
+ currentAddon = ADDONS_LIST.shift();
+ if (!currentAddon) {
+ Services.ww.unregisterNotification(observer);
+ close_manager(aManager, finish);
+ return;
+ }
+
+ info("Testing " + currentAddon.name);
+ let addonItem = getAddonByName(currentAddon.name, addonList);
+ let optionsBtn =
+ aManager.document.getAnonymousElementByAttribute(addonItem, "anonid",
+ "preferences-btn");
+ is(optionsBtn.hidden, false, "Prefs button should be visible.")
+
+ addonList.ensureElementIsVisible(addonItem);
+ EventUtils.synthesizeMouseAtCenter(optionsBtn, { }, aManager);
+ }
+
+ Services.ww.registerNotification(observer);
+ test_next_addon();
+ });
+
+}
diff --git a/toolkit/mozapps/extensions/test/browser/browser_plugin_enabled_state_locked.js b/toolkit/mozapps/extensions/test/browser/browser_plugin_enabled_state_locked.js
new file mode 100644
index 000000000..b32d74336
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_plugin_enabled_state_locked.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that state menu is displayed correctly (enabled or disabled) in the add-on manager
+// when the preference is unlocked / locked
+const {classes: Cc, interfaces: Ci} = Components;
+const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
+const gIsOSX = ("nsILocalFileMac" in Ci);
+const gIsLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc) ||
+ ("@mozilla.org/gio-service;1" in Cc);
+
+let gManagerWindow;
+let gCategoryUtilities;
+let gPluginElement;
+
+function getTestPluginPref() {
+ let prefix = "plugin.state.";
+ if (gIsWindows)
+ return prefix + "nptest";
+ else if (gIsLinux)
+ return prefix + "libnptest";
+ else
+ return prefix + "test";
+}
+
+registerCleanupFunction(() => {
+ Services.prefs.unlockPref(getTestPluginPref());
+ Services.prefs.clearUserPref(getTestPluginPref());
+});
+
+function getPlugins() {
+ let deferred = Promise.defer();
+ AddonManager.getAddonsByTypes(["plugin"], plugins => deferred.resolve(plugins));
+ return deferred.promise;
+}
+
+function getTestPlugin(aPlugins) {
+ let testPluginId;
+
+ for (let plugin of aPlugins) {
+ if (plugin.name == "Test Plug-in") {
+ testPluginId = plugin.id;
+ break;
+ }
+ }
+
+ Assert.ok(testPluginId, "Test Plug-in should exist");
+
+ let pluginElement = get_addon_element(gManagerWindow, testPluginId);
+ pluginElement.parentNode.ensureElementIsVisible(pluginElement);
+
+ return pluginElement;
+}
+
+function checkStateMenu(locked) {
+ Assert.equal(Services.prefs.prefIsLocked(getTestPluginPref()), locked,
+ "Preference lock state should be correct.");
+ let menuList = gManagerWindow.document.getAnonymousElementByAttribute(gPluginElement, "anonid", "state-menulist");
+ // State menu should always have a selected item which must be visible
+ let selectedMenuItem = menuList.querySelector(".addon-control[selected=\"true\"]");
+
+ is_element_visible(menuList, "State menu should be visible.");
+ Assert.equal(menuList.disabled, locked,
+ "State menu should" + (locked === true ? "" : " not") + " be disabled.");
+
+ is_element_visible(selectedMenuItem, "State menu's selected item should be visible.");
+}
+
+function checkStateMenuDetail(locked) {
+ Assert.equal(Services.prefs.prefIsLocked(getTestPluginPref()), locked,
+ "Preference should be " + (locked === true ? "" : "un") + "locked.");
+
+ // open details menu
+ let details = gManagerWindow.document.getAnonymousElementByAttribute(gPluginElement, "anonid", "details-btn");
+ is_element_visible(details, "Details link should be visible.");
+ EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
+
+ let deferred = Promise.defer();
+ wait_for_view_load(gManagerWindow, function() {
+ let menuList = gManagerWindow.document.getElementById("detail-state-menulist");
+ is_element_visible(menuList, "Details state menu should be visible.");
+ Assert.equal(menuList.disabled, locked,
+ "Details state menu enabled state should be correct.");
+ deferred.resolve();
+ });
+ return deferred.promise;
+}
+
+add_task(function* initializeState() {
+ Services.prefs.setIntPref(getTestPluginPref(), Ci.nsIPluginTag.STATE_ENABLED);
+ Services.prefs.unlockPref(getTestPluginPref());
+ gManagerWindow = yield open_manager();
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ yield gCategoryUtilities.openType("plugin");
+
+ let plugins = yield getPlugins();
+ gPluginElement = getTestPlugin(plugins);
+});
+
+// Tests that plugin state menu is enabled if the preference is unlocked
+add_task(function* taskCheckStateMenuIsEnabled() {
+ checkStateMenu(false);
+ yield checkStateMenuDetail(false);
+});
+
+// Lock the preference and then reload the plugin category
+add_task(function* reinitializeState() {
+ // lock the preference
+ Services.prefs.lockPref(getTestPluginPref());
+ yield gCategoryUtilities.openType("plugin");
+ // Retrieve the test plugin element
+ let plugins = yield getPlugins();
+ gPluginElement = getTestPlugin(plugins);
+});
+
+// Tests that plugin state menu is disabled if the preference is locked
+add_task(function* taskCheckStateMenuIsDisabled() {
+ checkStateMenu(true);
+ yield checkStateMenuDetail(true);
+});
+
+add_task(function* testCleanup() {
+ yield close_manager(gManagerWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_pluginprefs.js b/toolkit/mozapps/extensions/test/browser/browser_pluginprefs.js
new file mode 100644
index 000000000..458e8e334
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_pluginprefs.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the detail view of plugins
+
+var gManagerWindow;
+
+function test() {
+ waitForExplicitFinish();
+
+ open_manager("addons://list/plugin", function(aWindow) {
+ gManagerWindow = aWindow;
+
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+add_test(function() {
+ AddonManager.getAddonsByTypes(["plugin"], function(plugins) {
+ let testPluginId;
+ for (let plugin of plugins) {
+ if (plugin.name == "Test Plug-in") {
+ testPluginId = plugin.id;
+ break;
+ }
+ }
+ ok(testPluginId, "Test Plug-in should exist")
+
+ AddonManager.getAddonByID(testPluginId, function(testPlugin) {
+ let pluginEl = get_addon_element(gManagerWindow, testPluginId);
+ is(pluginEl.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE_INFO, "Options should be inline info type");
+ pluginEl.parentNode.ensureElementIsVisible(pluginEl);
+
+ let button = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "preferences-btn");
+ is_element_hidden(button, "Preferences button should be hidden");
+
+ button = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "details-btn");
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ let pluginLibraries = gManagerWindow.document.getElementById("pluginLibraries");
+ ok(pluginLibraries, "Plugin file name row should be displayed");
+ // the file name depends on the platform
+ ok(pluginLibraries.textContent, testPlugin.pluginLibraries, "Plugin file name should be displayed");
+
+ let pluginMimeTypes = gManagerWindow.document.getElementById("pluginMimeTypes");
+ ok(pluginMimeTypes, "Plugin mime type row should be displayed");
+ ok(pluginMimeTypes.textContent, "application/x-test (tst)", "Plugin mime type should be displayed");
+
+ run_next_test();
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_purchase.js b/toolkit/mozapps/extensions/test/browser/browser_purchase.js
new file mode 100644
index 000000000..bb30a1eb4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_purchase.js
@@ -0,0 +1,195 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that marketplace results show up in searches, are sorted right and
+// attempting to buy links through to the right webpage
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const SEARCH_URL = TESTROOT + "browser_purchase.xml";
+
+var gManagerWindow;
+
+function test() {
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, SEARCH_URL);
+
+ waitForExplicitFinish();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+
+ waitForFocus(function() {
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "foo";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ var remoteFilter = gManagerWindow.document.getElementById("search-filter-remote");
+ EventUtils.synthesizeMouseAtCenter(remoteFilter, { }, gManagerWindow);
+
+ run_next_test();
+ });
+ }, aWindow);
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ // Will have created an install so cancel it
+ AddonManager.getAllInstalls(function(aInstalls) {
+ is(aInstalls.length, 1, "Should have been one install created");
+ aInstalls[0].cancel();
+
+ finish();
+ });
+ });
+}
+
+function get_node(parent, anonid) {
+ return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
+}
+
+function get_install_btn(parent) {
+ var installStatus = get_node(parent, "install-status");
+ return get_node(installStatus, "install-remote-btn");
+}
+
+function get_purchase_btn(parent) {
+ var installStatus = get_node(parent, "install-status");
+ return get_node(installStatus, "purchase-remote-btn");
+}
+
+// Tests that the expected results appeared
+add_test(function() {
+ var list = gManagerWindow.document.getElementById("search-list");
+ var items = Array.filter(list.childNodes, function(e) {
+ return e.tagName == "richlistitem";
+ });
+
+ is(items.length, 5, "Should be 5 results");
+
+ is(get_node(items[0], "name").value, "Ludicrously Expensive Add-on", "Add-on 0 should be in expected position");
+ is_element_hidden(get_install_btn(items[0]), "Add-on 0 install button should be hidden");
+ is_element_visible(get_purchase_btn(items[0]), "Add-on 0 purchase button should be visible");
+ is(get_purchase_btn(items[0]).label, "Purchase for $101\u2026", "Add-on 0 should have the right price");
+
+ is(get_node(items[1], "name").value, "Cheap Add-on", "Add-on 1 should be in expected position");
+ is_element_hidden(get_install_btn(items[1]), "Add-on 1 install button should be hidden");
+ is_element_visible(get_purchase_btn(items[1]), "Add-on 1 purchase button should be visible");
+ is(get_purchase_btn(items[1]).label, "Purchase for $0.99\u2026", "Add-on 2 should have the right price");
+
+ is(get_node(items[2], "name").value, "Reasonable Add-on", "Add-on 2 should be in expected position");
+ is_element_hidden(get_install_btn(items[2]), "Add-on 2 install button should be hidden");
+ is_element_visible(get_purchase_btn(items[2]), "Add-on 2 purchase button should be visible");
+ is(get_purchase_btn(items[2]).label, "Purchase for $1\u2026", "Add-on 3 should have the right price");
+
+ is(get_node(items[3], "name").value, "Free Add-on", "Add-on 3 should be in expected position");
+ is_element_visible(get_install_btn(items[3]), "Add-on 3 install button should be visible");
+ is_element_hidden(get_purchase_btn(items[3]), "Add-on 3 purchase button should be hidden");
+
+ is(get_node(items[4], "name").value, "More Expensive Add-on", "Add-on 4 should be in expected position");
+ is_element_hidden(get_install_btn(items[4]), "Add-on 4 install button should be hidden");
+ is_element_visible(get_purchase_btn(items[4]), "Add-on 4 purchase button should be visible");
+ is(get_purchase_btn(items[4]).label, "Purchase for $1.01\u2026", "Add-on 4 should have the right price");
+
+ run_next_test();
+});
+
+// Tests that sorting by price works
+add_test(function() {
+ var list = gManagerWindow.document.getElementById("search-list");
+
+ var sorters = gManagerWindow.document.getElementById("search-sorters");
+ var priceSorter = get_node(sorters, "price-btn");
+ info("Changing sort order");
+ EventUtils.synthesizeMouseAtCenter(priceSorter, { }, gManagerWindow);
+
+ var items = Array.filter(list.childNodes, function(e) {
+ return e.tagName == "richlistitem";
+ });
+
+ is(get_node(items[0], "name").value, "Free Add-on", "Add-on 0 should be in expected position");
+ is(get_node(items[1], "name").value, "Cheap Add-on", "Add-on 1 should be in expected position");
+ is(get_node(items[2], "name").value, "Reasonable Add-on", "Add-on 2 should be in expected position");
+ is(get_node(items[3], "name").value, "More Expensive Add-on", "Add-on 3 should be in expected position");
+ is(get_node(items[4], "name").value, "Ludicrously Expensive Add-on", "Add-on 4 should be in expected position");
+
+ info("Changing sort order");
+ EventUtils.synthesizeMouseAtCenter(priceSorter, { }, gManagerWindow);
+
+ var items = Array.filter(list.childNodes, function(e) {
+ return e.tagName == "richlistitem";
+ });
+
+ is(get_node(items[0], "name").value, "Ludicrously Expensive Add-on", "Add-on 0 should be in expected position");
+ is(get_node(items[1], "name").value, "More Expensive Add-on", "Add-on 1 should be in expected position");
+ is(get_node(items[2], "name").value, "Reasonable Add-on", "Add-on 2 should be in expected position");
+ is(get_node(items[3], "name").value, "Cheap Add-on", "Add-on 3 should be in expected position");
+ is(get_node(items[4], "name").value, "Free Add-on", "Add-on 4 should be in expected position");
+
+ run_next_test();
+});
+
+// Tests that clicking the buy button works from the list
+add_test(function() {
+ gBrowser.addEventListener("load", function(event) {
+ if (!(event.target instanceof Document) ||
+ event.target.location.href == "about:blank")
+ return;
+ gBrowser.removeEventListener("load", arguments.callee, true);
+
+ is(gBrowser.currentURI.spec, TESTROOT + "releaseNotes.xhtml?addon5", "Should have loaded the right page");
+
+ gBrowser.removeCurrentTab();
+
+ if (gUseInContentUI) {
+ is(gBrowser.currentURI.spec, "about:addons", "Should be back to the add-ons manager");
+ run_next_test();
+ }
+ else {
+ waitForFocus(run_next_test, gManagerWindow);
+ }
+ }, true);
+
+ var list = gManagerWindow.document.getElementById("search-list");
+ EventUtils.synthesizeMouseAtCenter(get_purchase_btn(list.firstChild), { }, gManagerWindow);
+});
+
+// Tests that clicking the buy button from the details view works
+add_test(function() {
+ gBrowser.addEventListener("load", function(event) {
+ if (!(event.target instanceof Document) ||
+ event.target.location.href == "about:blank")
+ return;
+ gBrowser.removeEventListener("load", arguments.callee, true);
+
+ is(gBrowser.currentURI.spec, TESTROOT + "releaseNotes.xhtml?addon4", "Should have loaded the right page");
+
+ gBrowser.removeCurrentTab();
+
+ if (gUseInContentUI) {
+ is(gBrowser.currentURI.spec, "about:addons", "Should be back to the add-ons manager");
+ run_next_test();
+ }
+ else {
+ waitForFocus(run_next_test, gManagerWindow);
+ }
+ }, true);
+
+ var list = gManagerWindow.document.getElementById("search-list");
+ var item = list.firstChild.nextSibling;
+ list.ensureElementIsVisible(item);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ var btn = gManagerWindow.document.getElementById("detail-purchase-btn");
+ is_element_visible(btn, "Purchase button should be visible");
+
+ EventUtils.synthesizeMouseAtCenter(btn, { }, gManagerWindow);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_purchase.xml b/toolkit/mozapps/extensions/test/browser/browser_purchase.xml
new file mode 100644
index 000000000..470f47d28
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_purchase.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>Ludicrously Expensive Add-on</name>
+ <type id='1'>Extension</type>
+ <guid>addon5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <payment_data>
+ <link>http://example.com/browser/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml?addon5</link>
+ <amount amount="101">$101</amount>
+ </payment_data>
+ </addon>
+ <addon>
+ <name>Cheap Add-on</name>
+ <type id='1'>Extension</type>
+ <guid>addon2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <payment_data>
+ <link>http://example.com/browser/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml?addon2</link>
+ <amount amount="0.99">$0.99</amount>
+ </payment_data>
+ </addon>
+ <addon>
+ <name>Reasonable Add-on</name>
+ <type id='1'>Extension</type>
+ <guid>addon3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <payment_data>
+ <link>http://example.com/browser/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml?addon3</link>
+ <amount amount="1">$1</amount>
+ </payment_data>
+ </addon>
+ <addon>
+ <name>Free Add-on</name>
+ <type id='1'>Extension</type>
+ <guid>addon1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <install size="1">http://example.com/addon1.xpi</install>
+ </addon>
+ <addon>
+ <name>More Expensive Add-on</name>
+ <type id='1'>Extension</type>
+ <guid>addon4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <payment_data>
+ <link>http://example.com/browser/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml?addon4</link>
+ <amount amount="1.01">$1.01</amount>
+ </payment_data>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_recentupdates.js b/toolkit/mozapps/extensions/test/browser/browser_recentupdates.js
new file mode 100644
index 000000000..1427d5eba
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_recentupdates.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the recent updates pane
+
+var gProvider;
+var gManagerWindow;
+var gCategoryUtilities;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "updated 6 hours ago",
+ version: "1.0",
+ updateDate: new Date(Date.now() - (1000 * 60 * 60 * 6)),
+ releaseNotesURI: Services.io.newURI(TESTROOT + "releaseNotes.xhtml", null, null)
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "updated 5 seconds ago",
+ version: "1.0",
+ updateDate: new Date(Date.now() - (1000 * 5))
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "updated 1 month ago",
+ version: "1.0",
+ updateDate: new Date(Date.now() - (1000 * 60 * 60 * 25 * 30))
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+
+add_test(function() {
+ info("Checking menuitem for Recent Updates opens that pane");
+ var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
+ is(gCategoryUtilities.isVisible(recentCat), false, "Recent Updates category should initially be hidden");
+
+ var utilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
+ utilsBtn.addEventListener("popupshown", function() {
+ utilsBtn.removeEventListener("popupshown", arguments.callee, false);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.isVisible(recentCat), true, "Recent Updates category should now be visible");
+ is(gManagerWindow.document.getElementById("categories").selectedItem.value, "addons://updates/recent", "Recent Updates category should now be selected");
+ is(gManagerWindow.gViewController.currentViewId, "addons://updates/recent", "Recent Updates view should be the current view");
+ run_next_test();
+ }, true);
+ var menuitem = gManagerWindow.document.getElementById("utils-viewUpdates");
+ EventUtils.synthesizeMouse(menuitem, 2, 2, { }, gManagerWindow);
+ }, false);
+ EventUtils.synthesizeMouse(utilsBtn, 2, 2, { }, gManagerWindow);
+});
+
+
+add_test(function() {
+ var updatesList = gManagerWindow.document.getElementById("updates-list");
+ var sorters = gManagerWindow.document.getElementById("updates-sorters");
+ var dateSorter = gManagerWindow.document.getAnonymousElementByAttribute(sorters, "anonid", "date-btn");
+ var nameSorter = gManagerWindow.document.getAnonymousElementByAttribute(sorters, "anonid", "name-btn");
+
+ function check_order(expected) {
+ var items = updatesList.getElementsByTagName("richlistitem");
+ var possible = ["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org"];
+ for (let item of items) {
+ let itemId = item.mAddon.id;
+ if (possible.indexOf(itemId) == -1)
+ continue; // skip over any other addons, such as shipped addons that would update on every build
+ isnot(expected.length, 0, "Should be expecting more items");
+ is(itemId, expected.shift(), "Should get expected item based on sort order");
+ if (itemId == "addon1@tests.mozilla.org")
+ is_element_visible(item._relNotesToggle, "Release notes toggle should be visible for addon with release notes");
+ else
+ is_element_hidden(item._relNotesToggle, "Release notes toggle should be hidden for addon with no release notes");
+ }
+ }
+
+ is_element_visible(dateSorter);
+ is_element_visible(nameSorter);
+
+ // sorted by date, descending
+ check_order(["addon2@tests.mozilla.org", "addon1@tests.mozilla.org"]);
+
+ // sorted by date, ascending
+ EventUtils.synthesizeMouseAtCenter(dateSorter, { }, gManagerWindow);
+ check_order(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org"]);
+
+ // sorted by name, ascending
+ EventUtils.synthesizeMouseAtCenter(nameSorter, { }, gManagerWindow);
+ check_order(["addon2@tests.mozilla.org", "addon1@tests.mozilla.org"]);
+
+ // sorted by name, descending
+ EventUtils.synthesizeMouseAtCenter(nameSorter, { }, gManagerWindow);
+ check_order(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org"]);
+
+ run_next_test();
+});
+
+
+add_test(function() {
+ close_manager(gManagerWindow, function() {
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
+ is(gCategoryUtilities.isVisible(recentCat), true, "Recent Updates category should still be visible");
+
+ run_next_test();
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_searching.js b/toolkit/mozapps/extensions/test/browser/browser_searching.js
new file mode 100644
index 000000000..9e03e8297
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_searching.js
@@ -0,0 +1,695 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that searching for add-ons works correctly
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+const SEARCH_URL = TESTROOT + "browser_searching.xml";
+const NO_MATCH_URL = TESTROOT + "browser_searching_empty.xml";
+
+const QUERY = "SEARCH";
+const NO_MATCH_QUERY = "NOMATCHQUERY";
+const REMOTE_TO_INSTALL = "remote1";
+const REMOTE_INSTALL_URL = TESTROOT + "addons/browser_searching.xpi";
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+var gServer;
+var gAddonInstalled = false;
+
+function test() {
+ requestLongerTimeout(2);
+ // Turn on searching for this test
+ Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "PASS - f",
+ description: "Test description - SEARCH",
+ size: 3,
+ version: "1.0",
+ updateDate: new Date(2010, 4, 2, 0, 0, 1)
+ }, {
+ id: "fail-addon1@tests.mozilla.org",
+ name: "FAIL",
+ description: "Does not match query"
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "PASS - c",
+ description: "Test description - reSEARCHing SEARCH SEARCH",
+ size: 6,
+ version: "2.0",
+ updateDate: new Date(2010, 4, 2, 0, 0, 0)
+ }]);
+
+ var installs = gProvider.createInstalls([{
+ name: "PASS - a - SEARCHing",
+ sourceURI: "http://example.com/install1.xpi"
+ }, {
+ name: "PASS - g - reSEARCHing SEARCH",
+ sourceURI: "http://example.com/install2.xpi"
+ }, {
+ // Does not match query
+ name: "FAIL",
+ sourceURI: "http://example.com/fail-install1.xpi"
+ }]);
+
+ for (let install of installs )
+ install.install();
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ var installedAddon = get_addon_item(REMOTE_TO_INSTALL).mAddon;
+ installedAddon.uninstall();
+
+ AddonManager.getAllInstalls(function(aInstallsList) {
+ for (var install of aInstallsList) {
+ var sourceURI = install.sourceURI.spec;
+ if (sourceURI == REMOTE_INSTALL_URL ||
+ sourceURI.match(/^http:\/\/example\.com\/(.+)\.xpi$/) != null)
+ install.cancel();
+ }
+
+ finish();
+ });
+ });
+}
+
+function getAnonymousElementByAttribute(aElement, aName, aValue) {
+ return gManagerWindow.document.getAnonymousElementByAttribute(aElement,
+ aName,
+ aValue);
+}
+
+/*
+ * Checks whether or not the Add-ons Manager is currently searching
+ *
+ * @param aExpectedSearching
+ * The expected isSearching state
+ */
+function check_is_searching(aExpectedSearching) {
+ var loading = gManagerWindow.document.getElementById("search-loading");
+ is(!is_hidden(loading), aExpectedSearching,
+ "Search throbber should be showing iff currently searching");
+}
+
+/*
+ * Completes a search
+ *
+ * @param aQuery
+ * The query to search for
+ * @param aFinishImmediately
+ * Boolean representing whether or not the search is expected to
+ * finish immediately
+ * @param aCallback
+ * The callback to call when the search is done
+ * @param aCategoryType
+ * The expected selected category after the search is done.
+ * Optional and defaults to "search"
+ */
+function search(aQuery, aFinishImmediately, aCallback, aCategoryType) {
+ // Point search to the correct xml test file
+ var url = (aQuery == NO_MATCH_QUERY) ? NO_MATCH_URL : SEARCH_URL;
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, url);
+
+ aCategoryType = aCategoryType ? aCategoryType : "search";
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = aQuery;
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ var finishImmediately = true;
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, aCategoryType, "Expected category view should be selected");
+ is(gCategoryUtilities.isTypeVisible("search"), aCategoryType == "search",
+ "Search category should only be visible if it is the current view");
+ check_is_searching(false);
+ is(finishImmediately, aFinishImmediately, "Search should finish immediately only if expected");
+
+ aCallback();
+ });
+
+ finishImmediately = false
+ if (!aFinishImmediately)
+ check_is_searching(true);
+}
+
+/*
+ * Return results of a search
+ *
+ * @return Array of objects, each containing the name and item of a specific
+ * result
+ */
+function get_actual_results() {
+ var list = gManagerWindow.document.getElementById("search-list");
+ var rows = list.getElementsByTagName("richlistitem");
+
+ var results = [];
+ for (var item of rows) {
+
+ // Only consider items that are currently showing
+ var style = gManagerWindow.document.defaultView.getComputedStyle(item, "");
+ if (style.display == "none" || style.visibility != "visible")
+ continue;
+
+ if (item.mInstall || item.isPending("install")) {
+ var sourceURI = item.mInstall.sourceURI.spec;
+ if (sourceURI == REMOTE_INSTALL_URL) {
+ results.push({name: REMOTE_TO_INSTALL, item: item});
+ continue;
+ }
+
+ var result = sourceURI.match(/^http:\/\/example\.com\/(.+)\.xpi$/);
+ if (result != null) {
+ is(item.mInstall.name.indexOf("PASS"), 0, "Install name should start with PASS");
+ results.push({name: result[1], item: item});
+ continue;
+ }
+ }
+ else if (item.mAddon) {
+ var result = item.mAddon.id.match(/^(.+)@tests\.mozilla\.org$/);
+ if (result != null) {
+ is(item.mAddon.name.indexOf("PASS"), 0, "Addon name should start with PASS");
+ results.push({name: result[1], item: item});
+ continue;
+ }
+ }
+ else {
+ ok(false, "Found an item in the list that was neither installing or installed");
+ }
+ }
+
+ return results;
+}
+
+/*
+ * Returns expected results when searching for QUERY with default ordering
+ *
+ * @param aSortBy
+ * How the results are sorted (e.g. "name")
+ * @param aLocalExpected
+ * Boolean representing if local results are expected
+ * @return A pair: [array of results with an expected order,
+ * array of results with unknown order]
+ */
+function get_expected_results(aSortBy, aLocalExpected) {
+ var expectedOrder = null, unknownOrder = null;
+ switch (aSortBy) {
+ case "relevancescore":
+ expectedOrder = [ "addon2" , "remote1", "install2", "addon1",
+ "install1", "remote2", "remote3" , "remote4" ];
+ unknownOrder = [];
+ break;
+ case "name":
+ // Defaults to ascending order
+ expectedOrder = [ "install1", "remote1", "addon2" , "remote2",
+ "remote3" , "addon1" , "install2", "remote4" ];
+ unknownOrder = [];
+ break;
+ case "dateUpdated":
+ expectedOrder = [ "addon1", "addon2" ];
+ // Updated date not available for installs and remote add-ons
+ unknownOrder = [ "install1", "install2", "remote1",
+ "remote2" , "remote3" , "remote4" ];
+ break;
+ default:
+ ok(false, "Should recognize sortBy when checking the order of items");
+ }
+
+ // Only keep expected results
+ function filterResults(aId) {
+ // Include REMOTE_TO_INSTALL as a local add-on if it has been installed
+ if (gAddonInstalled && aId == REMOTE_TO_INSTALL)
+ return aLocalExpected;
+
+ if (aId.indexOf("addon") == 0 || aId.indexOf("install") == 0)
+ return aLocalExpected;
+ if (aId.indexOf("remote") == 0)
+ return !aLocalExpected;
+
+ return false;
+ }
+
+
+ return [expectedOrder.filter(filterResults),
+ unknownOrder.filter(filterResults)]
+}
+
+/*
+ * Check that the actual and expected results are the same
+ *
+ * @param aQuery
+ * The search query used
+ * @param aSortBy
+ * How the results are sorted (e.g. "name")
+ * @param aReverseOrder
+ * Boolean representing if the results are in reverse default order
+ * @param aShowLocal
+ * Boolean representing if local results are being shown
+ */
+function check_results(aQuery, aSortBy, aReverseOrder, aShowLocal) {
+
+ var xpinstall_enabled = true;
+ try {
+ xpinstall_enabled = Services.prefs.getBoolPref(PREF_XPI_ENABLED);
+ }
+ catch (e) {};
+
+ // When XPI Instalation is disabled, those buttons are hidden and unused
+ if (xpinstall_enabled) {
+ var localFilterSelected = gManagerWindow.document.getElementById("search-filter-local").selected;
+ var remoteFilterSelected = gManagerWindow.document.getElementById("search-filter-remote").selected;
+ is(localFilterSelected, aShowLocal, "Local filter should be selected if showing local items");
+ is(remoteFilterSelected, !aShowLocal, "Remote filter should be selected if showing remote items");
+ }
+
+ // Get expected order assuming default order
+ var expectedOrder = [], unknownOrder = [];
+ if (aQuery == QUERY)
+ [expectedOrder, unknownOrder] = get_expected_results(aSortBy, aShowLocal);
+
+ // Get actual order of results
+ var actualResults = get_actual_results();
+ var actualOrder = [result.name for each(result in actualResults)];
+
+ // Reverse array of actual results if supposed to be in reverse order.
+ // Reverse actualOrder instead of expectedOrder so can always check
+ // expectedOrder before unknownOrder
+ if (aReverseOrder)
+ actualOrder.reverse();
+
+ // Check actual vs. expected list of results
+ var totalExpectedResults = expectedOrder.length + unknownOrder.length;
+ is(actualOrder.length, totalExpectedResults, "Should get correct number of results");
+
+ // Check the "first" and "last" attributes are set correctly
+ for (let i = 0; i < actualResults.length; i++) {
+ if (i == 0) {
+ is(actualResults[0].item.hasAttribute("first"), true,
+ "First item should have 'first' attribute set");
+ is(actualResults[0].item.hasAttribute("last"), false,
+ "First item should not have 'last' attribute set");
+ } else if (i == (actualResults.length - 1)) {
+ is(actualResults[actualResults.length - 1].item.hasAttribute("first"), false,
+ "Last item should not have 'first' attribute set");
+ is(actualResults[actualResults.length - 1].item.hasAttribute("last"), true,
+ "Last item should have 'last' attribute set");
+ } else {
+ is(actualResults[i].item.hasAttribute("first"), false,
+ "Item " + i + " should not have 'first' attribute set");
+ is(actualResults[i].item.hasAttribute("last"), false,
+ "Item " + i + " should not have 'last' attribute set");
+ }
+ }
+
+ var i = 0;
+ for (; i < expectedOrder.length; i++)
+ is(actualOrder[i], expectedOrder[i], "Should have seen expected item");
+
+ // Items with data that is unknown can appear in any order among themselves,
+ // so just check that these items exist
+ for (; i < actualOrder.length; i++) {
+ var unknownOrderIndex = unknownOrder.indexOf(actualOrder[i]);
+ ok(unknownOrderIndex >= 0, "Should expect to see item with data that is unknown");
+ unknownOrder[unknownOrderIndex] = null;
+ }
+
+ // Check status of empty notice
+ var emptyNotice = gManagerWindow.document.getElementById("search-list-empty");
+ is(emptyNotice.hidden, totalExpectedResults > 0,
+ "Empty notice should be hidden only if expecting shown items");
+}
+
+/*
+ * Check results of a search with different filterings
+ *
+ * @param aQuery
+ * The search query used
+ * @param aSortBy
+ * How the results are sorted (e.g. "name")
+ * @param aReverseOrder
+ * Boolean representing if the results are in reverse default order
+ * @param aLocalOnly
+ * Boolean representing if the results are local only, can be undefined
+ */
+function check_filtered_results(aQuery, aSortBy, aReverseOrder, aLocalOnly) {
+ var localFilter = gManagerWindow.document.getElementById("search-filter-local");
+ var remoteFilter = gManagerWindow.document.getElementById("search-filter-remote");
+
+ var list = gManagerWindow.document.getElementById("search-list");
+ list.ensureElementIsVisible(localFilter);
+
+ // Check with showing local add-ons
+ EventUtils.synthesizeMouseAtCenter(localFilter, { }, gManagerWindow);
+ check_results(aQuery, aSortBy, aReverseOrder, true);
+
+ // Check with showing remote add-ons
+ aLocalOnly = aLocalOnly || false;
+ EventUtils.synthesizeMouseAtCenter(remoteFilter, { }, gManagerWindow);
+ check_results(aQuery, aSortBy, aReverseOrder, aLocalOnly);
+}
+
+/*
+ * Get item for a specific add-on by name
+ *
+ * @param aName
+ * The name of the add-on to search for
+ * @return Row of add-on if found, null otherwise
+ */
+function get_addon_item(aName) {
+ var id = aName + "@tests.mozilla.org";
+ var list = gManagerWindow.document.getElementById("search-list");
+ var rows = list.getElementsByTagName("richlistitem");
+ for (var row of rows) {
+ if (row.mAddon && row.mAddon.id == id)
+ return row;
+ }
+
+ return null;
+}
+
+/*
+ * Get item for a specific install by name
+ *
+ * @param aName
+ * The name of the install to search for
+ * @return Row of install if found, null otherwise
+ */
+function get_install_item(aName) {
+ var sourceURI = "http://example.com/" + aName + ".xpi";
+ var list = gManagerWindow.document.getElementById("search-list");
+ var rows = list.getElementsByTagName("richlistitem");
+ for (var row of rows) {
+ if (row.mInstall && row.mInstall.sourceURI.spec == sourceURI)
+ return row;
+ }
+
+ return null;
+}
+
+/*
+ * Gets the install button for a specific item
+ *
+ * @param aItem
+ * The item to get the install button for
+ * @return The install button for aItem
+ */
+function get_install_button(aItem) {
+ isnot(aItem, null, "Item should not be null when checking state of install button");
+ var installStatus = getAnonymousElementByAttribute(aItem, "anonid", "install-status");
+ return getAnonymousElementByAttribute(installStatus, "anonid", "install-remote-btn");
+}
+
+
+// Tests that searching for the empty string does nothing when not in the search view
+add_test(function() {
+ is(gCategoryUtilities.isTypeVisible("search"), false, "Search category should initially be hidden");
+
+ var selectedCategory = gCategoryUtilities.selectedCategory;
+ isnot(selectedCategory, "search", "Selected type should not initially be the search view");
+ search("", true, run_next_test, selectedCategory);
+});
+
+// Tests that the results from a query are sorted by relevancescore in descending order.
+// Also test that double clicking non-install items goes to the detail view, and that
+// only remote items have install buttons showing
+add_test(function() {
+ search(QUERY, false, function() {
+ check_filtered_results(QUERY, "relevancescore", false);
+
+ var list = gManagerWindow.document.getElementById("search-list");
+ var results = get_actual_results();
+ for (var result of results) {
+ var installBtn = get_install_button(result.item);
+ is(installBtn.hidden, result.name.indexOf("remote") != 0,
+ "Install button should only be showing for remote items");
+ }
+
+ var currentIndex = -1;
+ function run_next_double_click_test() {
+ currentIndex++;
+ if (currentIndex >= results.length) {
+ run_next_test();
+ return;
+ }
+
+ var result = results[currentIndex];
+ if (result.name.indexOf("install") == 0) {
+ run_next_double_click_test();
+ return;
+ }
+
+ var item = result.item;
+ list.ensureElementIsVisible(item);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ var name = gManagerWindow.document.getElementById("detail-name").textContent;
+ is(name, item.mAddon.name, "Name in detail view should be correct");
+ var version = gManagerWindow.document.getElementById("detail-version").value;
+ is(version, item.mAddon.version, "Version in detail view should be correct");
+
+ EventUtils.synthesizeMouseAtCenter(gManagerWindow.document.getElementById("category-search"),
+ { }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, run_next_double_click_test);
+ });
+ }
+
+ run_next_double_click_test();
+ });
+});
+
+// Tests that the sorters and filters correctly manipulate the results
+add_test(function() {
+ var sorters = gManagerWindow.document.getElementById("search-sorters");
+ var originalHandler = sorters.handler;
+
+ var sorterNames = ["name", "dateUpdated"];
+ var buttonIds = ["name-btn", "date-btn"];
+ var currentIndex = 0;
+ var currentReversed = false;
+
+ function run_sort_test() {
+ if (currentIndex >= sorterNames.length) {
+ sorters.handler = originalHandler;
+ run_next_test();
+ return;
+ }
+
+ // Simulate clicking on a specific sorter
+ var buttonId = buttonIds[currentIndex];
+ var sorter = getAnonymousElementByAttribute(sorters, "anonid", buttonId);
+ is_element_visible(sorter);
+ EventUtils.synthesizeMouseAtCenter(sorter, { }, gManagerWindow);
+ }
+
+ sorters.handler = {
+ onSortChanged: function(aSortBy, aAscending) {
+ if (originalHandler && "onSortChanged" in originalHandler)
+ originalHandler.onSortChanged(aSortBy, aAscending);
+
+ check_filtered_results(QUERY, sorterNames[currentIndex], currentReversed);
+
+ if (currentReversed)
+ currentIndex++;
+ currentReversed = !currentReversed;
+
+ run_sort_test();
+ }
+ };
+
+ check_filtered_results(QUERY, "relevancescore", false);
+ run_sort_test();
+});
+
+// Tests that searching for the empty string does nothing when in search view
+add_test(function() {
+ search("", true, function() {
+ check_filtered_results(QUERY, "dateUpdated", true);
+ run_next_test();
+ });
+});
+
+// Tests that clicking a different category hides the search query
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.isTypeVisible("search"), false, "Search category should be hidden");
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+ run_next_test();
+ });
+});
+
+// Tests that re-searching for query doesn't actually complete a new search,
+// and the last sort is still used
+add_test(function() {
+ search(QUERY, true, function() {
+ check_filtered_results(QUERY, "dateUpdated", true);
+ run_next_test();
+ });
+});
+
+// Tests that getting zero results works correctly
+add_test(function() {
+ search(NO_MATCH_QUERY, false, function() {
+ check_filtered_results(NO_MATCH_QUERY, "relevancescore", false);
+ run_next_test();
+ });
+});
+
+// Tests that installing a remote add-on works
+add_test(function() {
+ var installBtn = null;
+
+ var listener = {
+ onInstallEnded: function(aInstall, aAddon) {
+ // Don't immediately consider the installed add-on as local because
+ // if the user was filtering out local add-ons, the installed add-on
+ // would vanish. Only consider add-on as local on new searches.
+
+ aInstall.removeListener(this);
+
+ is(installBtn.hidden, true, "Install button should be hidden after install ended");
+ check_filtered_results(QUERY, "relevancescore", false);
+ run_next_test();
+ }
+ }
+
+ search(QUERY, false, function() {
+ var list = gManagerWindow.document.getElementById("search-list");
+ var remoteItem = get_addon_item(REMOTE_TO_INSTALL);
+ list.ensureElementIsVisible(remoteItem);
+
+ installBtn = get_install_button(remoteItem);
+ is(installBtn.hidden, false, "Install button should be showing before install");
+ remoteItem.mAddon.install.addListener(listener);
+ EventUtils.synthesizeMouseAtCenter(installBtn, { }, gManagerWindow);
+ });
+});
+
+// Tests that re-searching for query results in correct results
+add_test(function() {
+ // Select a different category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.isTypeVisible("search"), false, "Search category should be hidden");
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var installBtn = get_install_button(get_addon_item(REMOTE_TO_INSTALL));
+ is(installBtn.hidden, true, "Install button should be hidden for installed item");
+
+ search(QUERY, true, function() {
+ check_filtered_results(QUERY, "relevancescore", false);
+ run_next_test();
+ });
+ });
+});
+
+// Tests that incompatible add-ons are shown with a warning if compatibility checking is disabled
+add_test(function() {
+ AddonManager.checkCompatibility = false;
+ search("incompatible", false, function() {
+ var item = get_addon_item("remote5");
+ is_element_visible(item, "Incompatible addon should be visible");
+ is(item.getAttribute("notification"), "warning", "Compatibility warning should be shown");
+
+ item = get_addon_item("remote6");
+ is(item, null, "Addon incompatible with the product should not be visible");
+
+ AddonManager.checkCompatibility = true;
+ run_next_test();
+ });
+});
+
+// Tests that compatible-by-default addons are shown if strict compatibility checking is disabled
+add_test(function() {
+ restart_manager(gManagerWindow, null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, false);
+ search("incompatible", false, function() {
+ var item = get_addon_item("remote5");
+ is_element_visible(item, "Incompatible addon should be visible");
+ isnot(item.getAttribute("notification"), "warning", "Compatibility warning should not be shown");
+
+ var item = get_addon_item("remote6");
+ is(item, null, "Addon incompatible with the product should not be visible");
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ run_next_test();
+ });
+ });
+});
+
+
+// Tests that restarting the manager doesn't change search results
+add_test(function() {
+ restart_manager(gManagerWindow, null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ // We never restore to the search pane
+ is(gCategoryUtilities.selectedCategory, "discover", "View should have changed to discover");
+
+ // Installed add-on is considered local on new search
+ gAddonInstalled = true;
+
+ search(QUERY, false, function() {
+ check_filtered_results(QUERY, "relevancescore", false);
+
+ var installBtn = get_install_button(get_addon_item(REMOTE_TO_INSTALL));
+ is(installBtn.hidden, true, "Install button should be hidden for installed item");
+
+ run_next_test();
+ });
+ });
+});
+
+function bug_815120_test_search(aLocalOnly) {
+ restart_manager(gManagerWindow, "addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ // Installed add-on is considered local on new search
+ gAddonInstalled = true;
+
+ // The search buttons should be hidden in the LocalOnly setup
+ var localFilterButton = aWindow.document.getElementById("search-filter-local");
+ is(aLocalOnly, is_hidden(localFilterButton), "Local filter button visibility does not match, aLocalOnly = " + aLocalOnly);
+
+ var remoteFilterButton = aWindow.document.getElementById("search-filter-remote");
+ is(aLocalOnly, is_hidden(remoteFilterButton), "Remote filter button visibility does not match, aLocalOnly = " + aLocalOnly);
+
+ search(QUERY, false, function() {
+ check_filtered_results(QUERY, "relevancescore", false, aLocalOnly);
+ run_next_test();
+ });
+ });
+}
+
+// Tests for Bug 815120
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_XPI_ENABLED, false);
+ bug_815120_test_search(true);
+});
+
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_XPI_ENABLED, true);
+ bug_815120_test_search(false);
+});
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_searching.xml b/toolkit/mozapps/extensions/test/browser/browser_searching.xml
new file mode 100644
index 000000000..e88db289e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_searching.xml
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>addon1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Addon already installed - SEARCH</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/addon1.xpi</install>
+ </addon>
+ <addon>
+ <name>FAIL</name>
+ <type id='9'>lightweight theme</type>
+ <guid>addon12345@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Addon with uninstallable type shouldn't be visible in search</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/addon1.xpi</install>
+ </addon>
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>install1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Install already exists - SEARCH</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/install1.xpi</install>
+ </addon>
+ <addon>
+ <name>PASS - b</name>
+ <type id='1'>Extension</type>
+ <guid>remote1@tests.mozilla.org</guid>
+ <version>3.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary - SEARCH SEARCH</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="2">http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_searching.xpi</install>
+ </addon>
+ <addon>
+ <name>PASS - d</name>
+ <type id='1'>Extension</type>
+ <guid>remote2@tests.mozilla.org</guid>
+ <version>4.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary - SEARCHing SEARCH</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="5">http://example.com/remote2.xpi</install>
+ </addon>
+ <addon>
+ <name>PASS - e</name>
+ <type id='1'>Extension</type>
+ <guid>remote3@tests.mozilla.org</guid>
+ <version>5.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary - Does not match query</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/remote3.xpi</install>
+ </addon>
+ <addon>
+ <name>PASS - h</name>
+ <type id='1'>Extension</type>
+ <guid>remote4@tests.mozilla.org</guid>
+ <version>6.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Test summary - SEARCHing SEARCH SEARCH</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="4">http://example.com/remote4.xpi</install>
+ </addon>
+ <addon>
+ <name>PASS - i</name>
+ <type id='1'>Extension</type>
+ <guid>remote5@tests.mozilla.org</guid>
+ <version>6.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Incompatible test</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>1</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/addon1.xpi</install>
+ </addon>
+ <addon>
+ <name>FAIL - j</name>
+ <type id='1'>Extension</type>
+ <guid>remote6@tests.mozilla.org</guid>
+ <version>6.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Incompatible test</summary>
+ <description>Test description</description>
+ <compatible_applications>
+ <application>
+ <name>Fake Product</name>
+ <appID>fakeproduct@mozilla.org</appID>
+ <min_version>0</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="1">http://example.com/addon1.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_searching_empty.xml b/toolkit/mozapps/extensions/test/browser/browser_searching_empty.xml
new file mode 100644
index 000000000..24f6cb89f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_searching_empty.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100" />
+
diff --git a/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js
new file mode 100644
index 000000000..747811e63
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that compatibility overrides are refreshed when showing the addon
+// selection UI.
+
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_MIN_PLATFORM_COMPAT = "extensions.minCompatiblePlatformVersion";
+
+var gTestAddon = null;
+var gWin;
+
+function waitForView(aView, aCallback) {
+ var view = gWin.document.getElementById(aView);
+ if (view.parentNode.selectedPanel == view) {
+ aCallback();
+ return;
+ }
+
+ view.addEventListener("ViewChanged", function() {
+ view.removeEventListener("ViewChanged", arguments.callee, false);
+ aCallback();
+ }, false);
+}
+
+function install_test_addon(aCallback) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/browser_select_compatoverrides_1.xpi", function(aInstall) {
+ var listener = {
+ onInstallEnded: function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ gTestAddon = addon;
+ executeSoon(aCallback);
+ });
+ }
+ };
+ aInstall.addListener(listener);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+registerCleanupFunction(function() {
+ if (gWin)
+ gWin.close();
+ if (gTestAddon)
+ gTestAddon.uninstall();
+
+ Services.prefs.clearUserPref(PREF_MIN_PLATFORM_COMPAT);
+});
+
+function end_test() {
+ finish();
+}
+
+
+function test() {
+ waitForExplicitFinish();
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, false);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ install_test_addon(run_next_test);
+}
+
+add_test(function() {
+ gWin = Services.ww.openWindow(null,
+ "chrome://mozapps/content/extensions/selectAddons.xul",
+ "",
+ "chrome,centerscreen,dialog,titlebar",
+ null);
+ waitForFocus(function() {
+ waitForView("select", run_next_test);
+ }, gWin);
+});
+
+add_test(function() {
+ for (var row = gWin.document.getElementById("select-rows").firstChild; row; row = row.nextSibling) {
+ if (row.localName == "separator")
+ continue;
+ if (row.id.substr(-18) != "@tests.mozilla.org")
+ continue;
+
+ is(row.id, "addon1@tests.mozilla.org", "Should get expected addon");
+ isnot(row.action, "incompatible", "Addon should not be incompatible");
+
+ gWin.close();
+ gWin = null;
+ run_next_test();
+ }
+});
+
+add_test(function() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, TESTROOT + "browser_select_compatoverrides.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ gWin = Services.ww.openWindow(null,
+ "chrome://mozapps/content/extensions/selectAddons.xul",
+ "",
+ "chrome,centerscreen,dialog,titlebar",
+ null);
+ waitForFocus(function() {
+ waitForView("select", run_next_test);
+ }, gWin);
+});
+
+add_test(function() {
+ for (var row = gWin.document.getElementById("select-rows").firstChild; row; row = row.nextSibling) {
+ if (row.localName == "separator")
+ continue;
+ if (row.id.substr(-18) != "@tests.mozilla.org")
+ continue;
+ is(row.id, "addon1@tests.mozilla.org", "Should get expected addon");
+ is(row.action, "incompatible", "Addon should be incompatible");
+ run_next_test();
+ }
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.xml b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.xml
new file mode 100644
index 000000000..76d00aa2c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <addon_compatibility hosted="false">
+ <guid>addon1@tests.mozilla.org</guid>
+ <name>Addon1</name>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>1.0</min_version>
+ <max_version>2.0</max_version>
+ <compatible_applications>
+ <application>
+ <min_version>0.1</min_version>
+ <max_version>999.0</max_version>
+ <appID>toolkit@mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_select_confirm.js b/toolkit/mozapps/extensions/test/browser/browser_select_confirm.js
new file mode 100644
index 000000000..1204777ce
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_confirm.js
@@ -0,0 +1,181 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the confirmation part of the post-app-update dialog
+
+var gProvider;
+var gWin;
+
+function waitForView(aView, aCallback) {
+ var view = gWin.document.getElementById(aView);
+ if (view.parentNode.selectedPanel == view) {
+ aCallback();
+ return;
+ }
+
+ view.addEventListener("ViewChanged", function() {
+ view.removeEventListener("ViewChanged", arguments.callee, false);
+ try {
+ aCallback();
+ }
+ catch (e) {
+ ok(false, e);
+ }
+ }, false);
+}
+
+/**
+ * Creates 4 test add-ons. Two are disabled and two enabled.
+ *
+ * @param aAppDisabled
+ * The appDisabled property for the test add-ons
+ * @param aUpdateAvailable
+ * True if the test add-ons should claim to have an update available
+ */
+function setupUI(aAppDisabled, aUpdateAvailable, aCallback) {
+ if (gProvider)
+ gProvider.unregister();
+
+ gProvider = new MockProvider();
+
+ for (var i = 1; i < 5; i++) {
+ var addon = new MockAddon("test" + i + "@tests.mozilla.org",
+ "Test Add-on " + i, "extension");
+ addon.version = "1.0";
+ addon.userDisabled = (i > 2);
+ addon.appDisabled = aAppDisabled;
+ addon.isActive = !addon.userDisabled && !addon.appDisabled;
+
+ addon.findUpdates = function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ if (aUpdateAvailable) {
+ var newAddon = new MockAddon(this.id, this.name, "extension");
+ newAddon.version = "2.0";
+ var install = new MockInstall(this.name, this.type, newAddon);
+ install.existingAddon = this;
+ aListener.onUpdateAvailable(this, install);
+ }
+
+ aListener.onUpdateFinished(this, AddonManager.UPDATE_STATUS_NO_ERROR);
+ };
+
+ gProvider.addAddon(addon);
+ }
+
+ gWin = Services.ww.openWindow(null,
+ "chrome://mozapps/content/extensions/selectAddons.xul",
+ "",
+ "chrome,centerscreen,dialog,titlebar",
+ null);
+ waitForFocus(function() {
+ waitForView("select", function() {
+ var row = gWin.document.getElementById("select-rows").firstChild.nextSibling;
+ while (row) {
+ if (!row.id || row.id.indexOf("@tests.mozilla.org") < 0) {
+ // not a test add-on
+ row = row.nextSibling;
+ continue;
+ }
+
+ if (row.id == "test2@tests.mozilla.org" ||
+ row.id == "test4@tests.mozilla.org") {
+ row.disable();
+ }
+ else {
+ row.keep();
+ }
+ row = row.nextSibling;
+ }
+
+ waitForView("confirm", aCallback);
+ EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
+ });
+ }, gWin);
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+// Test for disabling
+add_test(function disabling_test() {
+ setupUI(false, false, function() {
+ ok(gWin.document.getElementById("incompatible-list").hidden, "Incompatible list should be hidden");
+ ok(gWin.document.getElementById("update-list").hidden, "Update list should be hidden");
+
+ var list = gWin.document.getElementById("disable-list");
+ ok(!list.hidden, "Disable list should be visible");
+ is(list.childNodes.length, 2, "Should be one add-on getting disabled (plus the header)");
+ is(list.childNodes[1].id, "test2@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[1].getAttribute("name"), "Test Add-on 2", "Should be the right add-on name");
+
+ var list = gWin.document.getElementById("enable-list");
+ ok(!list.hidden, "Enable list should be visible");
+ is(list.childNodes.length, 2, "Should be one add-on getting disabled (plus the header)");
+ is(list.childNodes[1].id, "test3@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[1].getAttribute("name"), "Test Add-on 3", "Should be the right add-on name");
+
+ ok(gWin.document.getElementById("next").hidden, "Next button should be hidden");
+ ok(!gWin.document.getElementById("done").hidden, "Done button should be visible");
+ gWin.close();
+
+ run_next_test();
+ });
+});
+
+// Test for incompatible
+add_test(function incompatible_test() {
+ setupUI(true, false, function() {
+ ok(gWin.document.getElementById("update-list").hidden, "Update list should be hidden");
+ ok(gWin.document.getElementById("disable-list").hidden, "Disable list should be hidden");
+ ok(gWin.document.getElementById("enable-list").hidden, "Enable list should be hidden");
+
+ var list = gWin.document.getElementById("incompatible-list");
+ ok(!list.hidden, "Incompatible list should be visible");
+ is(list.childNodes.length, 3, "Should be two add-ons waiting to be compatible (plus the header)");
+ is(list.childNodes[1].id, "test1@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[1].getAttribute("name"), "Test Add-on 1", "Should be the right add-on name");
+ is(list.childNodes[2].id, "test3@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[2].getAttribute("name"), "Test Add-on 3", "Should be the right add-on name");
+
+ ok(gWin.document.getElementById("next").hidden, "Next button should be hidden");
+ ok(!gWin.document.getElementById("done").hidden, "Done button should be visible");
+ gWin.close();
+
+ run_next_test();
+ });
+});
+
+// Test for updates
+add_test(function update_test() {
+ setupUI(false, true, function() {
+ ok(gWin.document.getElementById("incompatible-list").hidden, "Incompatible list should be hidden");
+ ok(gWin.document.getElementById("enable-list").hidden, "Enable list should be hidden");
+
+ var list = gWin.document.getElementById("update-list");
+ ok(!list.hidden, "Update list should be visible");
+ is(list.childNodes.length, 3, "Should be two add-ons waiting to be updated (plus the header)");
+ is(list.childNodes[1].id, "test1@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[1].getAttribute("name"), "Test Add-on 1", "Should be the right add-on name");
+ is(list.childNodes[2].id, "test3@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[2].getAttribute("name"), "Test Add-on 3", "Should be the right add-on name");
+
+ list = gWin.document.getElementById("disable-list");
+ ok(!list.hidden, "Disable list should be visible");
+ is(list.childNodes.length, 2, "Should be one add-on getting disabled (plus the header)");
+ is(list.childNodes[1].id, "test2@tests.mozilla.org", "Should be the right add-on ID");
+ is(list.childNodes[1].getAttribute("name"), "Test Add-on 2", "Should be the right add-on name");
+
+ ok(!gWin.document.getElementById("next").hidden, "Next button should be visible");
+ ok(gWin.document.getElementById("done").hidden, "Done button should be hidden");
+ gWin.close();
+
+ run_next_test();
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_select_selection.js b/toolkit/mozapps/extensions/test/browser/browser_select_selection.js
new file mode 100644
index 000000000..cf83e7c1e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_selection.js
@@ -0,0 +1,268 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the selection part of the post-app-update dialog
+
+var gProvider;
+var gWin;
+
+const PROFILE = AddonManager.SCOPE_PROFILE;
+const USER = AddonManager.SCOPE_USER;
+const APP = AddonManager.SCOPE_APPLICATION;
+const SYSTEM = AddonManager.SCOPE_SYSTEM;
+const DIST = -1;
+
+// The matrix of testcases for the selection part of the UI
+// Note that the isActive flag has the value it had when the previous version
+// of the application ran with this add-on.
+var ADDONS = [
+ //userDisabled wasAppDisabled isAppDisabled isActive hasUpdate autoUpdate scope defaultKeep position keepString disableString
+ [false, true, false, false, false, true, PROFILE, true, 42, "enabled", ""], // 0
+ [false, true, false, false, true, true, PROFILE, true, 43, "enabled", ""], // 1
+ [false, true, false, false, true, false, PROFILE, true, 52, "unneededupdate", ""], // 2
+ [false, false, false, true, false, true, PROFILE, true, 53, "", "disabled"], // 3
+ [false, false, false, true, true, true, PROFILE, true, 54, "", "disabled"], // 4
+ [false, false, false, true, true, false, PROFILE, true, 55, "unneededupdate", "disabled"], // 5
+ [false, true, true, false, false, true, PROFILE, true, 56, "incompatible", ""], // 6
+ [false, true, true, false, true, true, PROFILE, true, 57, "autoupdate", ""], // 7
+ [false, true, true, false, true, false, PROFILE, true, 58, "neededupdate", ""], // 8
+ [false, false, true, true, false, true, PROFILE, true, 59, "incompatible", "disabled"], // 9
+ [false, true, true, true, true, true, PROFILE, true, 44, "autoupdate", "disabled"], // 10
+ [false, true, true, true, true, false, PROFILE, true, 45, "neededupdate", "disabled"], // 11
+ [true, false, false, false, false, true, PROFILE, false, 46, "enabled", ""], // 12
+ [true, false, false, false, true, true, PROFILE, false, 47, "enabled", ""], // 13
+ [true, false, false, false, true, false, PROFILE, false, 48, "unneededupdate", ""], // 14
+
+ // userDisabled and isActive cannot be true on startup
+
+ [true, true, true, false, false, true, PROFILE, false, 49, "incompatible", ""], // 15
+ [true, true, true, false, true, true, PROFILE, false, 50, "autoupdate", ""], // 16
+ [true, true, true, false, true, false, PROFILE, false, 51, "neededupdate", ""], // 17
+
+ // userDisabled and isActive cannot be true on startup
+
+ // Being in a different scope should make little difference except no updates are possible so don't exhaustively test each
+ [false, false, false, true, true, false, USER, false, 0, "", "disabled"], // 18
+ [true, true, false, false, true, false, USER, false, 1, "enabled", ""], // 19
+ [false, true, true, true, true, false, USER, false, 2, "incompatible", "disabled"], // 20
+ [true, true, true, false, true, false, USER, false, 3, "incompatible", ""], // 21
+ [false, false, false, true, true, false, SYSTEM, false, 4, "", "disabled"], // 22
+ [true, true, false, false, true, false, SYSTEM, false, 5, "enabled", ""], // 23
+ [false, true, true, true, true, false, SYSTEM, false, 6, "incompatible", "disabled"], // 24
+ [true, true, true, false, true, false, SYSTEM, false, 7, "incompatible", ""], // 25
+ [false, false, false, true, true, false, APP, false, 8, "", "disabled"], // 26
+ [true, true, false, false, true, false, APP, false, 9, "enabled", ""], // 27
+ [false, true, true, true, true, false, APP, false, 10, "incompatible", "disabled"], // 28
+ [true, true, true, false, true, false, APP, false, 11, "incompatible", ""], // 29
+];
+
+function waitForView(aView, aCallback) {
+ var view = gWin.document.getElementById(aView);
+ if (view.parentNode.selectedPanel == view) {
+ aCallback();
+ return;
+ }
+
+ view.addEventListener("ViewChanged", function() {
+ view.removeEventListener("ViewChanged", arguments.callee, false);
+ aCallback();
+ }, false);
+}
+
+function getString(aName) {
+ if (!aName)
+ return "";
+
+ var strings = Services.strings.createBundle("chrome://mozapps/locale/extensions/selectAddons.properties");
+ return strings.GetStringFromName("action." + aName);
+}
+
+function getSourceString(aSource) {
+ if (!aSource)
+ return "";
+
+ var strings = Services.strings.createBundle("chrome://mozapps/locale/extensions/selectAddons.properties");
+ switch (aSource) {
+ case PROFILE:
+ return strings.GetStringFromName("source.profile");
+ case DIST:
+ return strings.GetStringFromName("source.bundled");
+ default:
+ return strings.GetStringFromName("source.other");
+ }
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ // Set prefs for Distributed Extension Source tests.
+ Services.prefs.setBoolPref("extensions.installedDistroAddon.test3@tests.mozilla.org", true);
+ Services.prefs.setBoolPref("extensions.installedDistroAddon.test12@tests.mozilla.org", true);
+ Services.prefs.setBoolPref("extensions.installedDistroAddon.test15@tests.mozilla.org", true);
+
+ for (let pos in ADDONS) {
+ let addonItem = ADDONS[pos];
+ let addon = new MockAddon("test" + pos + "@tests.mozilla.org",
+ "Test Add-on " + pos, "extension");
+ addon.version = "1.0";
+ addon.userDisabled = addonItem[0];
+ addon.appDisabled = addonItem[1];
+ addon.isActive = addonItem[3];
+ addon.applyBackgroundUpdates = addonItem[5] ? AddonManager.AUTOUPDATE_ENABLE
+ : AddonManager.AUTOUPDATE_DISABLE;
+ addon.scope = addonItem[6];
+
+ // Remove the upgrade permission from non-profile add-ons
+ if (addon.scope != AddonManager.SCOPE_PROFILE)
+ addon._permissions -= AddonManager.PERM_CAN_UPGRADE;
+
+ addon.findUpdates = function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ addon.appDisabled = addonItem[2];
+ addon.isActive = addon.shouldBeActive;
+
+ if (addonItem[4]) {
+ var newAddon = new MockAddon(this.id, this.name, "extension");
+ newAddon.version = "2.0";
+ var install = new MockInstall(this.name, this.type, newAddon);
+ install.existingAddon = this;
+ aListener.onUpdateAvailable(this, install);
+ }
+
+ aListener.onUpdateFinished(this, AddonManager.UPDATE_STATUS_NO_ERROR);
+ };
+
+ gProvider.addAddon(addon);
+ }
+
+ gWin = Services.ww.openWindow(null,
+ "chrome://mozapps/content/extensions/selectAddons.xul",
+ "",
+ "chrome,centerscreen,dialog,titlebar",
+ null);
+ waitForFocus(function() {
+ waitForView("select", run_next_test);
+ }, gWin);
+}
+
+function end_test() {
+ gWin.close();
+ finish();
+}
+
+// Minimal test for the checking UI
+add_test(function checking_test() {
+ // By the time we're here the progress bar should be full
+ var progress = gWin.document.getElementById("checking-progress");
+ is(progress.mode, "determined", "Should be a determined progress bar");
+ is(progress.value, progress.max, "Should be at full progress");
+
+ run_next_test();
+});
+
+// Tests that the selection UI behaves correctly
+add_test(function selection_test() {
+ function check_state() {
+ var str = addon[keep.checked ? 9 : 10];
+ var expected = getString(str);
+ var showCheckbox = str == "neededupdate" || str == "unneededupdate";
+ is(action.textContent, expected, "Action message should have the right text");
+ is(!is_hidden(update), showCheckbox, "Checkbox should have the right visibility");
+ is(is_hidden(action), showCheckbox, "Message should have the right visibility");
+ if (showCheckbox)
+ ok(update.checked, "Optional update checkbox should be checked");
+
+ if (keep.checked) {
+ is(row.hasAttribute("active"), !addon[2] || hasUpdate,
+ "Add-on will be active if it isn't appDisabled or an update is available");
+
+ if (showCheckbox) {
+ info("Flipping update checkbox");
+ EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
+ is(row.hasAttribute("active"), str == "unneededupdate",
+ "If the optional update isn't needed then the add-on will still be active");
+
+ info("Flipping update checkbox");
+ EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
+ is(row.hasAttribute("active"), !addon[2] || hasUpdate,
+ "Add-on will be active if it isn't appDisabled or an update is available");
+ }
+ }
+ else {
+ ok(!row.hasAttribute("active"), "Add-on won't be active when not keeping");
+
+ if (showCheckbox) {
+ info("Flipping update checkbox");
+ EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
+ ok(!row.hasAttribute("active"),
+ "Unchecking the update checkbox shouldn't make the add-on active");
+
+ info("Flipping update checkbox");
+ EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
+ ok(!row.hasAttribute("active"),
+ "Re-checking the update checkbox shouldn't make the add-on active");
+ }
+ }
+ }
+
+ is(gWin.document.getElementById("view-deck").selectedPanel.id, "select",
+ "Should be on the right view");
+
+ var pos = 0;
+ var scrollbox = gWin.document.getElementById("select-scrollbox");
+ var scrollBoxObject = scrollbox.boxObject;
+ for (var row = gWin.document.getElementById("select-rows").firstChild; row; row = row.nextSibling) {
+ // Ignore separators but increase the position by a large amount so we
+ // can verify they were in the right place
+ if (row.localName == "separator") {
+ pos += 30;
+ continue;
+ }
+
+ is(row._addon.type, "extension", "Should only be listing extensions");
+
+ // Ignore non-test add-ons that may be present
+ if (row.id.substr(-18) != "@tests.mozilla.org")
+ continue;
+
+ var id = parseInt(row.id.substring(4, row.id.length - 18));
+ var addon = ADDONS[id];
+
+ info("Testing add-on " + id);
+ scrollBoxObject.ensureElementIsVisible(row);
+ var keep = gWin.document.getAnonymousElementByAttribute(row, "anonid", "keep");
+ var action = gWin.document.getAnonymousElementByAttribute(row, "class", "addon-action-message");
+ var update = gWin.document.getAnonymousElementByAttribute(row, "anonid", "update");
+ var source = gWin.document.getAnonymousElementByAttribute(row, "class", "addon-source");
+
+ if (id == 3 || id == 12 || id == 15) {
+ // Distro Installed To Profile
+ is(source.textContent, getSourceString(DIST), "Source message should have the right text for Distributed Addons");
+ } else {
+ is(source.textContent, getSourceString(addon[6]), "Source message should have the right text");
+ }
+
+ // Non-profile add-ons don't appear to have updates since we won't install
+ // them
+ var hasUpdate = addon[4] && addon[6] == PROFILE;
+
+ is(pos, addon[8], "Should have been in the right position");
+ is(keep.checked, addon[7], "Keep checkbox should be in the right state");
+
+ check_state();
+
+ info("Flipping keep");
+ EventUtils.synthesizeMouseAtCenter(keep, { }, gWin);
+ is(keep.checked, !addon[7], "Keep checkbox should be in the right state");
+
+ check_state();
+
+ pos++;
+ }
+
+ is(pos, 60, "Should have seen the right number of add-ons");
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_select_update.js b/toolkit/mozapps/extensions/test/browser/browser_select_update.js
new file mode 100644
index 000000000..58f1de687
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_update.js
@@ -0,0 +1,181 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the update part of the post-app-update dialog
+
+var gProvider;
+var gWin;
+
+function waitForView(aView, aCallback) {
+ var view = gWin.document.getElementById(aView);
+ if (view.parentNode.selectedPanel == view) {
+ aCallback();
+ return;
+ }
+
+ view.addEventListener("ViewChanged", function() {
+ view.removeEventListener("ViewChanged", arguments.callee, false);
+ aCallback();
+ }, false);
+}
+
+function waitForClose(aCallback) {
+ gWin.addEventListener("unload", function() {
+ gWin.removeEventListener("unload", arguments.callee, false);
+
+ aCallback();
+ }, false);
+}
+
+/**
+ * Creates 4 test add-ons. Two are disabled and two enabled.
+ */
+function setupUI(aFailDownloads, aFailInstalls, aCallback) {
+ if (gProvider)
+ gProvider.unregister();
+
+ gProvider = new MockProvider();
+
+ for (var i = 1; i < 5; i++) {
+ var addon = new MockAddon("test" + i + "@tests.mozilla.org",
+ "Test Add-on " + i, "extension");
+ addon.version = "1.0";
+ addon.userDisabled = (i > 2);
+ addon.appDisabled = false;
+ addon.isActive = !addon.userDisabled && !addon.appDisabled;
+
+ addon.findUpdates = function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ var newAddon = new MockAddon(this.id, this.name, "extension");
+ newAddon.version = "2.0";
+ var install = new MockInstall(this.name, this.type, newAddon);
+ install.existingAddon = this;
+
+ install.install = function() {
+ this.state = AddonManager.STATE_DOWNLOADING;
+ this.callListeners("onDownloadStarted");
+
+ var self = this;
+ executeSoon(function() {
+ if (aFailDownloads) {
+ self.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ self.callListeners("onDownloadFailed");
+ return;
+ }
+
+ self.type = self._type;
+ self.addon = new MockAddon(self.existingAddon.id, self.name, self.type);
+ self.addon.version = self.version;
+ self.addon.pendingOperations = AddonManager.PENDING_INSTALL;
+ self.addon.install = self;
+
+ self.existingAddon.pendingUpgrade = self.addon;
+ self.existingAddon.pendingOperations |= AddonManager.PENDING_UPGRADE;
+
+ self.state = AddonManager.STATE_DOWNLOADED;
+ self.callListeners("onDownloadEnded");
+
+ self.state = AddonManager.STATE_INSTALLING;
+ self.callListeners("onInstallStarted");
+
+ if (aFailInstalls) {
+ self.state = AddonManager.STATE_INSTALL_FAILED;
+ self.callListeners("onInstallFailed");
+ return;
+ }
+
+ self.state = AddonManager.STATE_INSTALLED;
+ self.callListeners("onInstallEnded");
+ });
+ }
+
+ aListener.onUpdateAvailable(this, install);
+
+ aListener.onUpdateFinished(this, AddonManager.UPDATE_STATUS_NO_ERROR);
+ };
+
+ gProvider.addAddon(addon);
+ }
+
+ gWin = Services.ww.openWindow(null,
+ "chrome://mozapps/content/extensions/selectAddons.xul",
+ "",
+ "chrome,centerscreen,dialog,titlebar",
+ null);
+ waitForFocus(function() {
+ waitForView("select", function() {
+ var row = gWin.document.getElementById("select-rows").firstChild.nextSibling;
+ while (row) {
+ if (!row.id || row.id.indexOf("@tests.mozilla.org") < 0) {
+ // not a test add-on
+ row = row.nextSibling;
+ continue;
+ }
+
+ if (row.id == "test2@tests.mozilla.org" ||
+ row.id == "test4@tests.mozilla.org") {
+ row.disable();
+ }
+ else {
+ row.keep();
+ }
+ row = row.nextSibling;
+ }
+
+ waitForView("confirm", function() {
+ waitForView("update", aCallback);
+ EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
+ });
+ EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
+ });
+ }, gWin);
+}
+
+function test() {
+ waitForExplicitFinish();
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+// Test for working updates
+add_test(function working_test() {
+ setupUI(false, false, function() {
+ waitForClose(function() {
+ is(gWin.document.getElementById("update-progress").value, 2, "Should have finished 2 downloads");
+ run_next_test();
+ });
+
+ EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
+ });
+});
+
+// Test for failed updates
+add_test(function working_test() {
+ setupUI(true, false, function() {
+ waitForView("errors", function() {
+ is(gWin.document.getElementById("update-progress").value, 2, "Should have finished 2 downloads");
+ gWin.close();
+
+ run_next_test();
+ });
+
+ EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
+ });
+});
+
+// Test for failed updates
+add_test(function working_test() {
+ setupUI(false, true, function() {
+ waitForView("errors", function() {
+ is(gWin.document.getElementById("update-progress").value, 2, "Should have finished 2 downloads");
+ gWin.close();
+
+ run_next_test();
+ });
+
+ EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_sorting.js b/toolkit/mozapps/extensions/test/browser/browser_sorting.js
new file mode 100644
index 000000000..7bf697b36
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_sorting.js
@@ -0,0 +1,372 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that sorting of add-ons works correctly
+// (this test uses the list view, even though it no longer has sort buttons - see bug 623207)
+
+var gManagerWindow;
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+ gProvider.createAddons([{
+ // enabledInstalled group
+ // * Enabled
+ // * Incompatible but enabled because compatibility checking is off
+ // * Waiting to be installed
+ // * Waiting to be enabled
+ id: "test1@tests.mozilla.org",
+ name: "Test add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 02, 00, 00, 00),
+ size: 1,
+ pendingOperations: AddonManager.PENDING_NONE,
+ }, {
+ id: "test2@tests.mozilla.org",
+ name: "a first add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 01, 23, 59, 59),
+ size: 0265,
+ pendingOperations: AddonManager.PENDING_UPGRADE,
+ isActive: true,
+ isCompatible: false,
+ }, {
+ id: "test3@tests.mozilla.org",
+ name: "\u010Cesk\u00FD slovn\u00EDk", // Český slovník
+ description: "foo",
+ updateDate: new Date(2010, 04, 02, 00, 00, 01),
+ size: 12,
+ pendingOperations: AddonManager.PENDING_INSTALL,
+ isActive: false,
+ }, {
+ id: "test4@tests.mozilla.org",
+ name: "canadian dictionary",
+ updateDate: new Date(1970, 0, 01, 00, 00, 00),
+ description: "foo",
+ isActive: true,
+ }, {
+ id: "test5@tests.mozilla.org",
+ name: "croatian dictionary",
+ description: "foo",
+ updateDate: new Date(2012, 12, 12, 00, 00, 00),
+ size: 5,
+ pendingOperations: AddonManager.PENDING_ENABLE,
+ isActive: false,
+ }, {
+ // pendingDisable group
+ // * Waiting to be disabled
+ id: "test6@tests.mozilla.org",
+ name: "orange Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 02, 00, 00, 00),
+ size: 142,
+ isCompatible: false,
+ isActive: true,
+ pendingOperations: AddonManager.PENDING_DISABLE,
+ }, {
+ id: "test7@tests.mozilla.org",
+ name: "Blue Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 01, 23, 59, 59),
+ size: 65,
+ isActive: true,
+ pendingOperations: AddonManager.PENDING_DISABLE,
+ }, {
+ id: "test8@tests.mozilla.org",
+ name: "Green Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 03, 00, 00, 01),
+ size: 125,
+ pendingOperations: AddonManager.PENDING_DISABLE,
+ }, {
+ id: "test9@tests.mozilla.org",
+ name: "red Add-on",
+ updateDate: new Date(2011, 04, 01, 00, 00, 00),
+ description: "foo",
+ isCompatible: false,
+ pendingOperations: AddonManager.PENDING_DISABLE,
+ }, {
+ id: "test10@tests.mozilla.org",
+ name: "Purple Add-on",
+ description: "foo",
+ updateDate: new Date(2012, 12, 12, 00, 00, 00),
+ size: 56,
+ isCompatible: false,
+ pendingOperations: AddonManager.PENDING_DISABLE,
+ }, {
+ // pendingUninstall group
+ // * Waiting to be removed
+ id: "test11@tests.mozilla.org",
+ name: "amber Add-on",
+ description: "foo",
+ updateDate: new Date(1978, 04, 02, 00, 00, 00),
+ size: 142,
+ isActive: false,
+ appDisabled: true,
+ pendingOperations: AddonManager.PENDING_UNINSTALL,
+ }, {
+ id: "test12@tests.mozilla.org",
+ name: "Salmon Add-on - pending disable",
+ description: "foo",
+ updateDate: new Date(2054, 04, 01, 23, 59, 59),
+ size: 65,
+ isActive: true,
+ pendingOperations: AddonManager.PENDING_UNINSTALL,
+ }, {
+ id: "test13@tests.mozilla.org",
+ name: "rose Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 02, 00, 00, 01),
+ size: 125,
+ isActive: false,
+ userDisabled: true,
+ pendingOperations: AddonManager.PENDING_UNINSTALL,
+ }, {
+ id: "test14@tests.mozilla.org",
+ name: "Violet Add-on",
+ updateDate: new Date(2010, 05, 01, 00, 00, 00),
+ description: "foo",
+ isActive: false,
+ appDisabled: true,
+ pendingOperations: AddonManager.PENDING_UNINSTALL,
+ }, {
+ id: "test15@tests.mozilla.org",
+ name: "white Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 12, 00, 00, 00),
+ size: 56,
+ isActive: false,
+ userDisabled: true,
+ pendingOperations: AddonManager.PENDING_UNINSTALL,
+ }, {
+ // disabledIncompatibleBlocked group
+ // * Disabled
+ // * Incompatible
+ // * Blocklisted
+ id: "test16@tests.mozilla.org",
+ name: "grimsby Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 01, 00, 00, 00),
+ size: 142,
+ isActive: false,
+ appDisabled: true,
+ }, {
+ id: "test17@tests.mozilla.org",
+ name: "beamsville Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 8, 23, 59, 59),
+ size: 65,
+ isActive: false,
+ userDisabled: true,
+ }, {
+ id: "test18@tests.mozilla.org",
+ name: "smithville Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 03, 00, 00, 01),
+ size: 125,
+ isActive: false,
+ userDisabled: true,
+ blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED,
+ }, {
+ id: "test19@tests.mozilla.org",
+ name: "dunnville Add-on",
+ updateDate: new Date(2010, 04, 02, 00, 00, 00),
+ description: "foo",
+ isActive: false,
+ appDisabled: true,
+ isCompatible: false,
+ blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
+ }, {
+ id: "test20@tests.mozilla.org",
+ name: "silverdale Add-on",
+ description: "foo",
+ updateDate: new Date(2010, 04, 12, 00, 00, 00),
+ size: 56,
+ isActive: false,
+ appDisabled: true,
+ blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
+ }]);
+
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+function set_order(aSortBy, aAscending) {
+ var list = gManagerWindow.document.getElementById("addon-list");
+ var elements = [];
+ var node = list.firstChild;
+ while (node) {
+ elements.push(node);
+ node = node.nextSibling;
+ }
+ gManagerWindow.sortElements(elements, ["uiState", aSortBy], aAscending);
+ for (let element of elements)
+ list.appendChild(element);
+}
+
+function check_order(aExpectedOrder) {
+ var order = [];
+ var list = gManagerWindow.document.getElementById("addon-list");
+ var node = list.firstChild;
+ while (node) {
+ var id = node.getAttribute("value");
+ if (id && id.endsWith("@tests.mozilla.org"))
+ order.push(node.getAttribute("value"));
+ node = node.nextSibling;
+ }
+
+ is(order.toSource(), aExpectedOrder.toSource(), "Should have seen the right order");
+}
+
+// Tests that ascending name ordering was the default
+add_test(function() {
+
+ check_order([
+ "test2@tests.mozilla.org",
+ "test4@tests.mozilla.org",
+ "test3@tests.mozilla.org",
+ "test5@tests.mozilla.org",
+ "test1@tests.mozilla.org",
+ "test7@tests.mozilla.org",
+ "test8@tests.mozilla.org",
+ "test6@tests.mozilla.org",
+ "test10@tests.mozilla.org",
+ "test9@tests.mozilla.org",
+ "test11@tests.mozilla.org",
+ "test13@tests.mozilla.org",
+ "test12@tests.mozilla.org",
+ "test14@tests.mozilla.org",
+ "test15@tests.mozilla.org",
+ "test17@tests.mozilla.org",
+ "test19@tests.mozilla.org",
+ "test16@tests.mozilla.org",
+ "test20@tests.mozilla.org",
+ "test18@tests.mozilla.org",
+ ]);
+ run_next_test();
+});
+
+// Tests that switching to date ordering works
+add_test(function() {
+ set_order("updateDate", false);
+
+ // When we're ascending with updateDate, it's from newest
+ // to oldest.
+
+ check_order([
+ "test5@tests.mozilla.org",
+ "test3@tests.mozilla.org",
+ "test1@tests.mozilla.org",
+ "test2@tests.mozilla.org",
+ "test4@tests.mozilla.org",
+ "test10@tests.mozilla.org",
+ "test9@tests.mozilla.org",
+ "test8@tests.mozilla.org",
+ "test6@tests.mozilla.org",
+ "test7@tests.mozilla.org",
+ "test12@tests.mozilla.org",
+ "test14@tests.mozilla.org",
+ "test15@tests.mozilla.org",
+ "test13@tests.mozilla.org",
+ "test11@tests.mozilla.org",
+ "test20@tests.mozilla.org",
+ "test17@tests.mozilla.org",
+ "test18@tests.mozilla.org",
+ "test19@tests.mozilla.org",
+ "test16@tests.mozilla.org",
+ ]);
+
+ set_order("updateDate", true);
+
+ check_order([
+ "test4@tests.mozilla.org",
+ "test2@tests.mozilla.org",
+ "test1@tests.mozilla.org",
+ "test3@tests.mozilla.org",
+ "test5@tests.mozilla.org",
+ "test7@tests.mozilla.org",
+ "test6@tests.mozilla.org",
+ "test8@tests.mozilla.org",
+ "test9@tests.mozilla.org",
+ "test10@tests.mozilla.org",
+ "test11@tests.mozilla.org",
+ "test13@tests.mozilla.org",
+ "test15@tests.mozilla.org",
+ "test14@tests.mozilla.org",
+ "test12@tests.mozilla.org",
+ "test16@tests.mozilla.org",
+ "test19@tests.mozilla.org",
+ "test18@tests.mozilla.org",
+ "test17@tests.mozilla.org",
+ "test20@tests.mozilla.org",
+ ]);
+
+ run_next_test();
+});
+
+// Tests that switching to name ordering works
+add_test(function() {
+ set_order("name", true);
+
+ check_order([
+ "test2@tests.mozilla.org",
+ "test4@tests.mozilla.org",
+ "test3@tests.mozilla.org",
+ "test5@tests.mozilla.org",
+ "test1@tests.mozilla.org",
+ "test7@tests.mozilla.org",
+ "test8@tests.mozilla.org",
+ "test6@tests.mozilla.org",
+ "test10@tests.mozilla.org",
+ "test9@tests.mozilla.org",
+ "test11@tests.mozilla.org",
+ "test13@tests.mozilla.org",
+ "test12@tests.mozilla.org",
+ "test14@tests.mozilla.org",
+ "test15@tests.mozilla.org",
+ "test17@tests.mozilla.org",
+ "test19@tests.mozilla.org",
+ "test16@tests.mozilla.org",
+ "test20@tests.mozilla.org",
+ "test18@tests.mozilla.org",
+ ]);
+
+ set_order("name", false);
+
+ check_order([
+ "test1@tests.mozilla.org",
+ "test5@tests.mozilla.org",
+ "test3@tests.mozilla.org",
+ "test4@tests.mozilla.org",
+ "test2@tests.mozilla.org",
+ "test9@tests.mozilla.org",
+ "test10@tests.mozilla.org",
+ "test6@tests.mozilla.org",
+ "test8@tests.mozilla.org",
+ "test7@tests.mozilla.org",
+ "test15@tests.mozilla.org",
+ "test14@tests.mozilla.org",
+ "test12@tests.mozilla.org",
+ "test13@tests.mozilla.org",
+ "test11@tests.mozilla.org",
+ "test18@tests.mozilla.org",
+ "test20@tests.mozilla.org",
+ "test16@tests.mozilla.org",
+ "test19@tests.mozilla.org",
+ "test17@tests.mozilla.org",
+ ]);
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_sorting_plugins.js b/toolkit/mozapps/extensions/test/browser/browser_sorting_plugins.js
new file mode 100644
index 000000000..2bb6b4ba4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_sorting_plugins.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that sorting of plugins works correctly
+// (this test checks that plugins with "ask to activate" state appear after those with
+// "always activate" and before those with "never activate")
+
+var gManagerWindow;
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+ gProvider.createAddons([{
+ // enabledInstalled group
+ // * Always activate
+ // * Ask to activate
+ // * Never activate
+ id: "test1@tests.mozilla.org",
+ name: "Java Applet Plug-in Java 7 Update 51",
+ description: "foo",
+ type: "plugin",
+ isActive: true,
+ userDisabled: AddonManager.STATE_ASK_TO_ACTIVATE
+ }, {
+ id: "test2@tests.mozilla.org",
+ name: "Quick Time Plug-in",
+ description: "foo",
+ type: "plugin",
+ isActive: true,
+ userDisabled: false
+ }, {
+ id: "test3@tests.mozilla.org",
+ name: "Shockwave Flash",
+ description: "foo",
+ type: "plugin",
+ isActive: false,
+ userDisabled: true
+ }, {
+ id: "test4@tests.mozilla.org",
+ name: "Adobe Reader Plug-in",
+ description: "foo",
+ type: "plugin",
+ isActive: true,
+ userDisabled: AddonManager.STATE_ASK_TO_ACTIVATE
+ }, {
+ id: "test5@tests.mozilla.org",
+ name: "3rd Party Plug-in",
+ description: "foo",
+ type: "plugin",
+ isActive: true,
+ userDisabled: false
+ }]);
+
+ open_manager("addons://list/plugin", function(aWindow) {
+ gManagerWindow = aWindow;
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+function check_order(aExpectedOrder) {
+ var order = [];
+ var list = gManagerWindow.document.getElementById("addon-list");
+ var node = list.firstChild;
+ while (node) {
+ var id = node.getAttribute("value");
+ if (id && id.endsWith("@tests.mozilla.org"))
+ order.push(node.getAttribute("value"));
+ node = node.nextSibling;
+ }
+
+ is(order.toSource(), aExpectedOrder.toSource(), "Should have seen the right order");
+}
+
+// Tests that ascending name ordering was the default
+add_test(function() {
+
+ check_order([
+ "test5@tests.mozilla.org",
+ "test2@tests.mozilla.org",
+ "test4@tests.mozilla.org",
+ "test1@tests.mozilla.org",
+ "test3@tests.mozilla.org"
+ ]);
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_tabsettings.js b/toolkit/mozapps/extensions/test/browser/browser_tabsettings.js
new file mode 100644
index 000000000..2838698c7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_tabsettings.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests various aspects of the details view
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "tabsettings@tests.mozilla.org",
+ name: "Tab Settings",
+ version: "1",
+ optionsURL: CHROMEROOT + "addon_prefs.xul",
+ optionsType: AddonManager.OPTIONS_TYPE_TAB
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+add_test(function() {
+ var addon = get_addon_element(gManagerWindow, "tabsettings@tests.mozilla.org");
+ is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_TAB, "Options should be inline type");
+ addon.parentNode.ensureElementIsVisible(addon);
+
+ var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+ is_element_visible(button, "Preferences button should be visible");
+
+ if (gUseInContentUI) {
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+ var browser = gBrowser.selectedBrowser;
+ browser.addEventListener("DOMContentLoaded", function() {
+ browser.removeEventListener("DOMContentLoaded", arguments.callee, false);
+ is(browser.currentURI.spec, addon.mAddon.optionsURL, "New tab should have loaded the options URL");
+ browser.contentWindow.close();
+ run_next_test();
+ }, false);
+ return;
+ }
+
+ let instantApply = Services.prefs.getBoolPref("browser.preferences.instantApply");
+
+ function observer(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "domwindowclosed":
+ // Give the preference window a chance to finish closing before
+ // closing the add-ons manager.
+ waitForFocus(function () {
+ Services.ww.unregisterNotification(observer);
+ run_next_test();
+ });
+ break;
+ case "domwindowopened":
+ let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+ waitForFocus(function () {
+ // If the openDialog privileges are wrong a new browser window
+ // will open, let the test proceed (and fail) rather than timeout.
+ if (win.location != addon.mAddon.optionsURL &&
+ win.location != "chrome://browser/content/browser.xul")
+ return;
+
+ is(win.location, addon.mAddon.optionsURL,
+ "The correct addon pref window should have opened");
+
+ let chromeFlags = win.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebNavigation).
+ QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIXULWindow).chromeFlags;
+ ok(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME &&
+ (instantApply || chromeFlags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG),
+ "Window was open as a chrome dialog.");
+
+ win.close();
+ }, win);
+ break;
+ }
+ }
+
+ Services.ww.registerNotification(observer);
+ EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_task_next_test.js b/toolkit/mozapps/extensions/test/browser/browser_task_next_test.js
new file mode 100644
index 000000000..5ff2aff78
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_task_next_test.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that we throw if a test created with add_task()
+// calls run_next_test
+
+add_task(function* run_next_throws() {
+ let err = null;
+ try {
+ run_next_test();
+ } catch (e) {
+ err = e;
+ info("run_next_test threw " + err);
+ }
+ ok(err, "run_next_test() should throw an error inside an add_task test");
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_types.js b/toolkit/mozapps/extensions/test/browser/browser_types.js
new file mode 100644
index 000000000..8abb0ff73
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_types.js
@@ -0,0 +1,473 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that registering new types works
+
+var gManagerWindow;
+var gCategoryUtilities;
+
+var gProvider = {
+};
+
+var gTypes = [
+ new AddonManagerPrivate.AddonType("type1", null, "Type 1",
+ AddonManager.VIEW_TYPE_LIST, 4500),
+ new AddonManagerPrivate.AddonType("missing1", null, "Missing 1"),
+ new AddonManagerPrivate.AddonType("type2", null, "Type 1",
+ AddonManager.VIEW_TYPE_LIST, 5100,
+ AddonManager.TYPE_UI_HIDE_EMPTY),
+ {
+ id: "type3",
+ name: "Type 3",
+ uiPriority: 5200,
+ viewType: AddonManager.VIEW_TYPE_LIST
+ }
+];
+
+function go_back(aManager) {
+ if (gUseInContentUI) {
+ gBrowser.goBack();
+ } else {
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("back-btn"),
+ { }, aManager);
+ }
+}
+
+function go_forward(aManager) {
+ if (gUseInContentUI) {
+ gBrowser.goForward();
+ } else {
+ EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("forward-btn"),
+ { }, aManager);
+ }
+}
+
+function check_state(aManager, canGoBack, canGoForward) {
+ var doc = aManager.document;
+
+ if (gUseInContentUI) {
+ is(gBrowser.canGoBack, canGoBack, "canGoBack should be correct");
+ is(gBrowser.canGoForward, canGoForward, "canGoForward should be correct");
+ }
+
+ if (!is_hidden(doc.getElementById("back-btn"))) {
+ is(!doc.getElementById("back-btn").disabled, canGoBack, "Back button should have the right state");
+ is(!doc.getElementById("forward-btn").disabled, canGoForward, "Forward button should have the right state");
+ }
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ run_next_test();
+}
+
+function end_test() {
+ finish();
+}
+
+// Add a new type, open the manager and make sure it is in the right place
+add_test(function() {
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.get("type2"), "Type 2 should be present");
+ ok(!gCategoryUtilities.get("missing1", true), "Missing 1 should be absent");
+
+ is(gCategoryUtilities.get("type1").previousSibling.getAttribute("value"),
+ "addons://list/extension", "Type 1 should be in the right place");
+ is(gCategoryUtilities.get("type2").previousSibling.getAttribute("value"),
+ "addons://list/theme", "Type 2 should be in the right place");
+
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+ ok(!gCategoryUtilities.isTypeVisible("type2"), "Type 2 should be hidden");
+
+ run_next_test();
+ });
+});
+
+// Select the type, close the manager and remove it then open the manager and
+// check we're back to the default view
+add_test(function() {
+ gCategoryUtilities.openType("type1", function() {
+ close_manager(gManagerWindow, function() {
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should be absent");
+ ok(!gCategoryUtilities.get("type2", true), "Type 2 should be absent");
+ ok(!gCategoryUtilities.get("missing1", true), "Missing 1 should be absent");
+
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be back to the default view");
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+});
+
+// Add a type while the manager is still open and check it appears
+add_test(function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should be absent");
+ ok(!gCategoryUtilities.get("type2", true), "Type 2 should be absent");
+ ok(!gCategoryUtilities.get("missing1", true), "Missing 1 should be absent");
+
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.get("type2"), "Type 2 should be present");
+ ok(!gCategoryUtilities.get("missing1", true), "Missing 1 should be absent");
+
+ is(gCategoryUtilities.get("type1").previousSibling.getAttribute("value"),
+ "addons://list/extension", "Type 1 should be in the right place");
+ is(gCategoryUtilities.get("type2").previousSibling.getAttribute("value"),
+ "addons://list/theme", "Type 2 should be in the right place");
+
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+ ok(!gCategoryUtilities.isTypeVisible("type2"), "Type 2 should be hidden");
+
+ run_next_test();
+ });
+});
+
+// Remove the type while it is beng viewed and check it is replaced with the
+// default view
+add_test(function() {
+ gCategoryUtilities.openType("type1", function() {
+ gCategoryUtilities.openType("plugin", function() {
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "type1", "Should be showing the custom view");
+ check_state(gManagerWindow, true, true);
+
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should be absent");
+ ok(!gCategoryUtilities.get("type2", true), "Type 2 should be absent");
+ ok(!gCategoryUtilities.get("missing1", true), "Missing 1 should be absent");
+
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be back to the default view");
+ check_state(gManagerWindow, true, true);
+
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be showing the extension view");
+ check_state(gManagerWindow, false, true);
+
+ go_forward(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be back to the default view");
+ check_state(gManagerWindow, true, true);
+
+ go_forward(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "Should be back to the plugins view");
+ check_state(gManagerWindow, true, false);
+
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be back to the default view");
+ check_state(gManagerWindow, true, true);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Test that when going back to a now missing category we skip it
+add_test(function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type1", function() {
+ gCategoryUtilities.openType("plugin", function() {
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be back to the first view");
+ check_state(gManagerWindow, false, true);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Test that when going forward to a now missing category we skip it
+add_test(function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type1", function() {
+ gCategoryUtilities.openType("plugin", function() {
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be back to the extension view");
+
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_forward(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "Should be back to the plugin view");
+ check_state(gManagerWindow, true, false);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Test that when going back to a now missing category and we can't go back any
+// any further then we just display the default view
+add_test(function() {
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ open_manager("addons://list/type1", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "type1", "Should be at the custom view");
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("extension", function() {
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be at the default view");
+ check_state(gManagerWindow, false, true);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+});
+
+// Test that when going forward to a now missing category and we can't go
+// forward any further then we just display the default view
+add_test(function() {
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type1", function() {
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be at the extension view");
+
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_forward(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be at the default view");
+ check_state(gManagerWindow, true, false);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Test that when going back we skip multiple missing categories
+add_test(function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type1", function() {
+ gCategoryUtilities.openType("type3", function() {
+ gCategoryUtilities.openType("plugin", function() {
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be back to the first view");
+ check_state(gManagerWindow, false, true);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+ });
+});
+
+// Test that when going forward we skip multiple missing categories
+add_test(function() {
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type1", function() {
+ gCategoryUtilities.openType("type3", function() {
+ gCategoryUtilities.openType("plugin", function() {
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ go_back(gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be back to the extension view");
+
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_forward(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "Should be back to the plugin view");
+ check_state(gManagerWindow, true, false);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Test that when going back we skip all missing categories and when we can't go
+// back any any further then we just display the default view
+add_test(function() {
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ open_manager("addons://list/type1", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ is(gCategoryUtilities.selectedCategory, "type1", "Should be at the custom view");
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type3", function() {
+ gCategoryUtilities.openType("extension", function() {
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be at the default view");
+ check_state(gManagerWindow, false, true);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+});
+
+// Test that when going forward we skip all missing categories and when we can't
+// go back any any further then we just display the default view
+add_test(function() {
+ AddonManagerPrivate.registerProvider(gProvider, gTypes);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+ ok(gCategoryUtilities.get("type1"), "Type 1 should be present");
+ ok(gCategoryUtilities.isTypeVisible("type1"), "Type 1 should be visible");
+
+ gCategoryUtilities.openType("type1", function() {
+ gCategoryUtilities.openType("type3", function() {
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ go_back(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "Should be at the extension view");
+
+ AddonManagerPrivate.unregisterProvider(gProvider);
+
+ ok(!gCategoryUtilities.get("type1", true), "Type 1 should not be present");
+
+ go_forward(gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "discover", "Should be at the default view");
+ check_state(gManagerWindow, true, false);
+
+ close_manager(gManagerWindow, run_next_test);
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js b/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
new file mode 100644
index 000000000..9fcb9de66
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
@@ -0,0 +1,1099 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that searching for add-ons works correctly
+
+var gManagerWindow;
+var gDocument;
+var gCategoryUtilities;
+var gProvider;
+
+function test() {
+ requestLongerTimeout(2);
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "Uninstall needs restart",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_UNINSTALL
+ }, {
+ id: "addon2@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 1",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon3@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 2",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon4@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 3",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon5@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 4",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon6@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 5",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon7@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 6",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon8@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 7",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }, {
+ id: "addon9@tests.mozilla.org",
+ name: "Uninstall doesn't need restart 8",
+ type: "extension",
+ operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+ }]);
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gDocument = gManagerWindow.document;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+function get_item_in_list(aId, aList) {
+ var item = aList.firstChild;
+ while (item) {
+ if ("mAddon" in item && item.mAddon.id == aId) {
+ aList.ensureElementIsVisible(item);
+ return item;
+ }
+ item = item.nextSibling;
+ }
+ return null;
+}
+
+// Tests that uninstalling a normal add-on from the list view can be undone
+add_test(function() {
+ var ID = "addon1@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that uninstalling a restartless add-on from the list view can be undone
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(aAddon.isActive, "Add-on should be active");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that uninstalling a disabled restartless add-on from the list view can
+// be undone and doesn't re-enable
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ aAddon.userDisabled = true;
+
+ ok(!aAddon.isActive, "Add-on should be inactive");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!aAddon.isActive, "Add-on should be inactive");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ aAddon.userDisabled = false;
+ ok(aAddon.isActive, "Add-on should be active");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that uninstalling a normal add-on from the search view can be undone
+add_test(function() {
+ var ID = "addon1@tests.mozilla.org";
+ var list = gDocument.getElementById("search-list");
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ // Make sure to show local add-ons
+ EventUtils.synthesizeMouseAtCenter(gDocument.getElementById("search-filter-local"), { }, gManagerWindow);
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that uninstalling a restartless add-on from the search view can be undone
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var list = gDocument.getElementById("search-list");
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ // Make sure to show local add-ons
+ EventUtils.synthesizeMouseAtCenter(gDocument.getElementById("search-filter-local"), { }, gManagerWindow);
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(aAddon.isActive, "Add-on should be active");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that uninstalling a disabled restartless add-on from the search view can
+// be undone and doesn't re-enable
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var list = gDocument.getElementById("search-list");
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ // Make sure to show local add-ons
+ EventUtils.synthesizeMouseAtCenter(gDocument.getElementById("search-filter-local"), { }, gManagerWindow);
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ aAddon.userDisabled = true;
+
+ ok(!aAddon.isActive, "Add-on should be inactive");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!aAddon.isActive, "Add-on should be inactive");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ aAddon.userDisabled = false;
+ ok(aAddon.isActive, "Add-on should be active");
+
+ run_next_test();
+ });
+ });
+});
+
+// Tests that uninstalling a normal add-on from the details view switches back
+// to the list view and can be undone
+add_test(function() {
+ var ID = "addon1@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gDocument.getElementById("view-port").selectedPanel.id, "detail-view", "Should be in the detail view");
+
+ var button = gDocument.getElementById("detail-uninstall-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ // Force XBL to apply
+ item.clientTop;
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Tests that uninstalling a restartless add-on from the details view switches
+// back to the list view and can be undone
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gDocument.getElementById("view-port").selectedPanel.id, "detail-view", "Should be in the detail view");
+
+ var button = gDocument.getElementById("detail-uninstall-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ // Force XBL to apply
+ item.clientTop;
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(aAddon.isActive, "Add-on should be active");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Tests that uninstalling a restartless add-on from the details view switches
+// back to the list view and can be undone and doesn't re-enable
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ aAddon.userDisabled = true;
+
+ ok(!aAddon.isActive, "Add-on should be inactive");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
+ EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
+ wait_for_view_load(gManagerWindow, function() {
+ is(gDocument.getElementById("view-port").selectedPanel.id, "detail-view", "Should be in the detail view");
+
+ var button = gDocument.getElementById("detail-uninstall-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ // Force XBL to apply
+ item.clientTop;
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ ok(!aAddon.isActive, "Add-on should be inactive");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ aAddon.userDisabled = false;
+ ok(aAddon.isActive, "Add-on should be active");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Tests that a normal add-on pending uninstall shows up in the list view
+add_test(function() {
+ var ID = "addon1@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ gCategoryUtilities.openType("plugin", function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to plugin");
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Tests that a normal add-on pending uninstall shows up in the search view
+add_test(function() {
+ var ID = "addon1@tests.mozilla.org";
+ var list = gDocument.getElementById("search-list");
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ // Make sure to show local add-ons
+ EventUtils.synthesizeMouseAtCenter(gDocument.getElementById("search-filter-local"), { }, gManagerWindow);
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ gCategoryUtilities.openType("plugin", function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to plugin");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(!button.hidden, "Restart button should not be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+});
+
+// Tests that switching away from the list view finalises the uninstall of
+// multiple restartless add-ons
+add_test(function() {
+ var ID = "addon2@tests.mozilla.org";
+ var ID2 = "addon6@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ item = get_item_in_list(ID2, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ gCategoryUtilities.openType("plugin", function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension");
+
+ AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
+ is(aAddon, null, "Add-on should no longer be installed");
+ is(aAddon2, null, "Second add-on should no longer be installed");
+
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var item = get_item_in_list(ID, list);
+ is(item, null, "Should not have found the add-on in the list");
+ item = get_item_in_list(ID2, list);
+ is(item, null, "Should not have found the second add-on in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that switching away from the search view finalises the uninstall of
+// multiple restartless add-ons
+add_test(function() {
+ var ID = "addon3@tests.mozilla.org";
+ var ID2 = "addon7@tests.mozilla.org";
+ var list = gDocument.getElementById("search-list");
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ // Make sure to show local add-ons
+ EventUtils.synthesizeMouseAtCenter(gDocument.getElementById("search-filter-local"), { }, gManagerWindow);
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ item = get_item_in_list(ID2, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ gCategoryUtilities.openType("plugin", function() {
+ is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension");
+
+ AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
+ is(aAddon, null, "Add-on should no longer be installed");
+ is(aAddon2, null, "Second add-on should no longer be installed");
+
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ var item = get_item_in_list(ID, list);
+ is(item, null, "Should not have found the add-on in the list");
+ item = get_item_in_list(ID2, list);
+ is(item, null, "Should not have found the second add-on in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that closing the manager from the list view finalises the uninstall of
+// multiple restartless add-ons
+add_test(function() {
+ var ID = "addon4@tests.mozilla.org";
+ var ID2 = "addon8@tests.mozilla.org";
+ var list = gDocument.getElementById("addon-list");
+
+ // Select the extensions category
+ gCategoryUtilities.openType("extension", function() {
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ item = get_item_in_list(ID2, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ close_manager(gManagerWindow, function() {
+ AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
+ is(aAddon, null, "Add-on should no longer be installed");
+ is(aAddon2, null, "Second add-on should no longer be installed");
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gDocument = gManagerWindow.document;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ var list = gDocument.getElementById("addon-list");
+
+ is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+ var item = get_item_in_list(ID, list);
+ is(item, null, "Should not have found the add-on in the list");
+ item = get_item_in_list(ID2, list);
+ is(item, null, "Should not have found the second add-on in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+ });
+});
+
+// Tests that closing the manager from the search view finalises the uninstall
+// of multiple restartless add-ons
+add_test(function() {
+ var ID = "addon5@tests.mozilla.org";
+ var ID2 = "addon9@tests.mozilla.org";
+ var list = gDocument.getElementById("search-list");
+
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ // Make sure to show local add-ons
+ EventUtils.synthesizeMouseAtCenter(gDocument.getElementById("search-filter-local"), { }, gManagerWindow);
+
+ AddonManager.getAddonByID(ID, function(aAddon) {
+ ok(aAddon.isActive, "Add-on should be active");
+ ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+ ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+ var item = get_item_in_list(ID, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ // Force XBL to apply
+ item.clientTop;
+
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+ ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
+ ok(!aAddon.isActive, "Add-on should be inactive");
+
+ var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+ isnot(button, null, "Should have a restart button");
+ ok(button.hidden, "Restart button should be hidden");
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+ isnot(button, null, "Should have an undo button");
+
+ item = get_item_in_list(ID2, list);
+ isnot(item, null, "Should have found the add-on in the list");
+
+ button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ EventUtils.synthesizeMouseAtCenter(button, { }, gManagerWindow);
+
+ close_manager(gManagerWindow, function() {
+ AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
+ is(aAddon, null, "Add-on should no longer be installed");
+ is(aAddon2, null, "Second add-on should no longer be installed");
+
+ open_manager(null, function(aWindow) {
+ gManagerWindow = aWindow;
+ gDocument = gManagerWindow.document;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ var list = gDocument.getElementById("search-list");
+ var searchBox = gManagerWindow.document.getElementById("header-search");
+
+ searchBox.value = "Uninstall";
+
+ EventUtils.synthesizeMouseAtCenter(searchBox, { }, gManagerWindow);
+ EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+ wait_for_view_load(gManagerWindow, function() {
+ is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+ var item = get_item_in_list(ID, list);
+ is(item, null, "Should not have found the add-on in the list");
+ item = get_item_in_list(ID2, list);
+ is(item, null, "Should not have found the second add-on in the list");
+
+ run_next_test();
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_updateid.js b/toolkit/mozapps/extensions/test/browser/browser_updateid.js
new file mode 100644
index 000000000..a6672e825
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_updateid.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that updates that change an add-on's ID show up correctly in the UI
+
+var gProvider;
+var gManagerWindow;
+var gCategoryUtilities;
+
+var gApp = document.getElementById("bundle_brand").getString("brandShortName");
+
+function test() {
+ waitForExplicitFinish();
+
+ gProvider = new MockProvider();
+
+ gProvider.createAddons([{
+ id: "addon1@tests.mozilla.org",
+ name: "manually updating addon",
+ version: "1.0",
+ applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+ }]);
+
+ open_manager("addons://list/extension", function(aWindow) {
+ gManagerWindow = aWindow;
+ gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+ run_next_test();
+ });
+}
+
+function end_test() {
+ close_manager(gManagerWindow, function() {
+ finish();
+ });
+}
+
+add_test(function() {
+ gCategoryUtilities.openType("extension", function() {
+ gProvider.createInstalls([{
+ name: "updated add-on",
+ existingAddon: gProvider.addons[0],
+ version: "2.0"
+ }]);
+ var newAddon = new MockAddon("addon2@tests.mozilla.org");
+ newAddon.name = "updated add-on";
+ newAddon.version = "2.0";
+ newAddon.pendingOperations = AddonManager.PENDING_INSTALL;
+ gProvider.installs[0]._addonToInstall = newAddon;
+
+ var item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ is(item._version.value, "1.0", "Should still show the old version in the normal list");
+ var name = gManagerWindow.document.getAnonymousElementByAttribute(item, "anonid", "name");
+ is(name.value, "manually updating addon", "Should show the old name in the list");
+ var update = gManagerWindow.document.getAnonymousElementByAttribute(item, "anonid", "update-btn");
+ is_element_visible(update, "Update button should be visible");
+
+ item = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
+ is(item, null, "Should not show the new version in the list");
+
+ run_next_test();
+ });
+});
+
+add_test(function() {
+ var item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
+ var update = gManagerWindow.document.getAnonymousElementByAttribute(item, "anonid", "update-btn");
+ EventUtils.synthesizeMouseAtCenter(update, { }, gManagerWindow);
+
+ var pending = gManagerWindow.document.getAnonymousElementByAttribute(item, "anonid", "pending");
+ is_element_visible(pending, "Pending message should be visible");
+ is(pending.textContent,
+ get_string("notification.upgrade", "manually updating addon", gApp),
+ "Pending message should be correct");
+
+ item = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
+ is(item, null, "Should not show the new version in the list");
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_updatessl.js b/toolkit/mozapps/extensions/test/browser/browser_updatessl.js
new file mode 100644
index 000000000..7a9149aa5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_updatessl.js
@@ -0,0 +1,370 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+let tempScope = {};
+Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm", tempScope);
+let AddonUpdateChecker = tempScope.AddonUpdateChecker;
+
+const updaterdf = RELATIVE_DIR + "browser_updatessl.rdf";
+const redirect = RELATIVE_DIR + "redirect.sjs?";
+const SUCCESS = 0;
+const DOWNLOAD_ERROR = AddonUpdateChecker.ERROR_DOWNLOAD_ERROR;
+
+const HTTP = "http://example.com/";
+const HTTPS = "https://example.com/";
+const NOCERT = "https://nocert.example.com/";
+const SELFSIGNED = "https://self-signed.example.com/";
+const UNTRUSTED = "https://untrusted.example.com/";
+const EXPIRED = "https://expired.example.com/";
+
+const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
+
+var gTests = [];
+var gStart = 0;
+var gLast = 0;
+
+var HTTPObserver = {
+ observeActivity: function(aChannel, aType, aSubtype, aTimestamp, aSizeData,
+ aStringData) {
+ aChannel.QueryInterface(Ci.nsIChannel);
+
+ dump("*** HTTP Activity 0x" + aType.toString(16) + " 0x" + aSubtype.toString(16) +
+ " " + aChannel.URI.spec + "\n");
+ }
+};
+
+function test() {
+ gStart = Date.now();
+ requestLongerTimeout(4);
+ waitForExplicitFinish();
+
+ let observerService = Cc["@mozilla.org/network/http-activity-distributor;1"].
+ getService(Ci.nsIHttpActivityDistributor);
+ observerService.addObserver(HTTPObserver);
+
+ registerCleanupFunction(function() {
+ observerService.removeObserver(HTTPObserver);
+ });
+
+ run_next_test();
+}
+
+function end_test() {
+ Services.prefs.clearUserPref(PREF_UPDATE_REQUIREBUILTINCERTS);
+
+ var cos = Cc["@mozilla.org/security/certoverride;1"].
+ getService(Ci.nsICertOverrideService);
+ cos.clearValidityOverride("nocert.example.com", -1);
+ cos.clearValidityOverride("self-signed.example.com", -1);
+ cos.clearValidityOverride("untrusted.example.com", -1);
+ cos.clearValidityOverride("expired.example.com", -1);
+
+ info("All tests completed in " + (Date.now() - gStart) + "ms");
+ finish();
+}
+
+function add_update_test(mainURL, redirectURL, expectedStatus) {
+ gTests.push([mainURL, redirectURL, expectedStatus]);
+}
+
+function run_update_tests(callback) {
+ function run_next_update_test() {
+ if (gTests.length == 0) {
+ callback();
+ return;
+ }
+ gLast = Date.now();
+
+ let [mainURL, redirectURL, expectedStatus] = gTests.shift();
+ if (redirectURL) {
+ var url = mainURL + redirect + redirectURL + updaterdf;
+ var message = "Should have seen the right result for an update check redirected from " +
+ mainURL + " to " + redirectURL;
+ }
+ else {
+ url = mainURL + updaterdf;
+ message = "Should have seen the right result for an update check from " +
+ mainURL;
+ }
+
+ AddonUpdateChecker.checkForUpdates("addon1@tests.mozilla.org",
+ null, url, {
+ onUpdateCheckComplete: function(updates) {
+ is(updates.length, 1, "Should be the right number of results");
+ is(SUCCESS, expectedStatus, message);
+ info("Update test ran in " + (Date.now() - gLast) + "ms");
+ run_next_update_test();
+ },
+
+ onUpdateCheckError: function(status) {
+ is(status, expectedStatus, message);
+ info("Update test ran in " + (Date.now() - gLast) + "ms");
+ run_next_update_test();
+ }
+ });
+ }
+
+ run_next_update_test();
+}
+
+// Add overrides for the bad certificates
+function addCertOverrides() {
+ addCertOverride("nocert.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH);
+ addCertOverride("self-signed.example.com", Ci.nsICertOverrideService.ERROR_UNTRUSTED);
+ addCertOverride("untrusted.example.com", Ci.nsICertOverrideService.ERROR_UNTRUSTED);
+ addCertOverride("expired.example.com", Ci.nsICertOverrideService.ERROR_TIME);
+}
+
+// Runs tests with built-in certificates required and no certificate exceptions.
+add_test(function() {
+ // Tests that a simple update.rdf retrieval works as expected.
+ add_update_test(HTTP, null, SUCCESS);
+ add_update_test(HTTPS, null, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, null, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, null, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, null, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, null, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_update_test(HTTP, HTTP, SUCCESS);
+ add_update_test(HTTP, HTTPS, SUCCESS);
+ add_update_test(HTTP, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(HTTP, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(HTTP, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(HTTP, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_update_test(HTTPS, HTTP, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_update_test(NOCERT, HTTP, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_update_test(SELFSIGNED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_update_test(UNTRUSTED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_update_test(EXPIRED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, EXPIRED, DOWNLOAD_ERROR);
+
+ run_update_tests(run_next_test);
+});
+
+// Runs tests without requiring built-in certificates and no certificate
+// exceptions.
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, false);
+
+ // Tests that a simple update.rdf retrieval works as expected.
+ add_update_test(HTTP, null, SUCCESS);
+ add_update_test(HTTPS, null, SUCCESS);
+ add_update_test(NOCERT, null, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, null, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, null, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, null, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_update_test(HTTP, HTTP, SUCCESS);
+ add_update_test(HTTP, HTTPS, SUCCESS);
+ add_update_test(HTTP, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(HTTP, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(HTTP, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(HTTP, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_update_test(HTTPS, HTTP, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, HTTPS, SUCCESS);
+ add_update_test(HTTPS, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_update_test(NOCERT, HTTP, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_update_test(SELFSIGNED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_update_test(UNTRUSTED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_update_test(EXPIRED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, EXPIRED, DOWNLOAD_ERROR);
+
+ run_update_tests(run_next_test);
+});
+
+// Runs tests with built-in certificates required and all certificate exceptions.
+add_test(function() {
+ Services.prefs.clearUserPref(PREF_UPDATE_REQUIREBUILTINCERTS);
+ addCertOverrides();
+
+ // Tests that a simple update.rdf retrieval works as expected.
+ add_update_test(HTTP, null, SUCCESS);
+ add_update_test(HTTPS, null, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, null, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, null, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, null, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, null, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_update_test(HTTP, HTTP, SUCCESS);
+ add_update_test(HTTP, HTTPS, SUCCESS);
+ add_update_test(HTTP, NOCERT, SUCCESS);
+ add_update_test(HTTP, SELFSIGNED, SUCCESS);
+ add_update_test(HTTP, UNTRUSTED, SUCCESS);
+ add_update_test(HTTP, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_update_test(HTTPS, HTTP, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_update_test(NOCERT, HTTP, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_update_test(SELFSIGNED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_update_test(UNTRUSTED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, EXPIRED, DOWNLOAD_ERROR);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_update_test(EXPIRED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, HTTPS, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, NOCERT, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, SELFSIGNED, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, UNTRUSTED, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, EXPIRED, DOWNLOAD_ERROR);
+
+ run_update_tests(run_next_test);
+});
+
+// Runs tests without requiring built-in certificates and all certificate
+// exceptions.
+add_test(function() {
+ Services.prefs.setBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, false);
+
+ // Tests that a simple update.rdf retrieval works as expected.
+ add_update_test(HTTP, null, SUCCESS);
+ add_update_test(HTTPS, null, SUCCESS);
+ add_update_test(NOCERT, null, SUCCESS);
+ add_update_test(SELFSIGNED, null, SUCCESS);
+ add_update_test(UNTRUSTED, null, SUCCESS);
+ add_update_test(EXPIRED, null, SUCCESS);
+
+ // Tests that redirecting from http to other servers works as expected
+ add_update_test(HTTP, HTTP, SUCCESS);
+ add_update_test(HTTP, HTTPS, SUCCESS);
+ add_update_test(HTTP, NOCERT, SUCCESS);
+ add_update_test(HTTP, SELFSIGNED, SUCCESS);
+ add_update_test(HTTP, UNTRUSTED, SUCCESS);
+ add_update_test(HTTP, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from valid https to other servers works as expected
+ add_update_test(HTTPS, HTTP, DOWNLOAD_ERROR);
+ add_update_test(HTTPS, HTTPS, SUCCESS);
+ add_update_test(HTTPS, NOCERT, SUCCESS);
+ add_update_test(HTTPS, SELFSIGNED, SUCCESS);
+ add_update_test(HTTPS, UNTRUSTED, SUCCESS);
+ add_update_test(HTTPS, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from nocert https to other servers works as expected
+ add_update_test(NOCERT, HTTP, DOWNLOAD_ERROR);
+ add_update_test(NOCERT, HTTPS, SUCCESS);
+ add_update_test(NOCERT, NOCERT, SUCCESS);
+ add_update_test(NOCERT, SELFSIGNED, SUCCESS);
+ add_update_test(NOCERT, UNTRUSTED, SUCCESS);
+ add_update_test(NOCERT, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from self-signed https to other servers works as expected
+ add_update_test(SELFSIGNED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(SELFSIGNED, HTTPS, SUCCESS);
+ add_update_test(SELFSIGNED, NOCERT, SUCCESS);
+ add_update_test(SELFSIGNED, SELFSIGNED, SUCCESS);
+ add_update_test(SELFSIGNED, UNTRUSTED, SUCCESS);
+ add_update_test(SELFSIGNED, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from untrusted https to other servers works as expected
+ add_update_test(UNTRUSTED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(UNTRUSTED, HTTPS, SUCCESS);
+ add_update_test(UNTRUSTED, NOCERT, SUCCESS);
+ add_update_test(UNTRUSTED, SELFSIGNED, SUCCESS);
+ add_update_test(UNTRUSTED, UNTRUSTED, SUCCESS);
+ add_update_test(UNTRUSTED, EXPIRED, SUCCESS);
+
+ // Tests that redirecting from expired https to other servers works as expected
+ add_update_test(EXPIRED, HTTP, DOWNLOAD_ERROR);
+ add_update_test(EXPIRED, HTTPS, SUCCESS);
+ add_update_test(EXPIRED, NOCERT, SUCCESS);
+ add_update_test(EXPIRED, SELFSIGNED, SUCCESS);
+ add_update_test(EXPIRED, UNTRUSTED, SUCCESS);
+ add_update_test(EXPIRED, EXPIRED, SUCCESS);
+
+ run_update_tests(run_next_test);
+});
diff --git a/toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf b/toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf
new file mode 100644
index 000000000..f24573847
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>20</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf^headers^ b/toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf^headers^
new file mode 100644
index 000000000..2e4f8163b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_updatessl.rdf^headers^
@@ -0,0 +1 @@
+Connection: close
diff --git a/toolkit/mozapps/extensions/test/browser/cancelCompatCheck.sjs b/toolkit/mozapps/extensions/test/browser/cancelCompatCheck.sjs
new file mode 100644
index 000000000..38bc25d08
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/cancelCompatCheck.sjs
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Delay before responding to an HTTP call attempting to read
+// an addon update RDF file
+
+function handleRequest(req, resp) {
+ resp.processAsync();
+ resp.setHeader("Cache-Control", "no-cache, no-store", false);
+ resp.setHeader("Content-Type", "text/xml;charset=utf-8", false);
+
+ let file = null;
+ getObjectState("SERVER_ROOT", function(serverRoot)
+ {
+ file = serverRoot.getFile("browser/toolkit/mozapps/extensions/test/browser/browser_bug557956.rdf");
+ });
+ dump("*** cancelCompatCheck.sjs: " + file.path + "\n");
+ let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ fstream.init(file, -1, 0, 0);
+ let cstream = null;
+ cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Components.interfaces.nsIConverterInputStream);
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ // The delay can be passed on the query string
+ let delay = req.queryString + 0;
+
+ timer = Components.classes["@mozilla.org/timer;1"].
+ createInstance(Components.interfaces.nsITimer);
+ timer.init(function sendFile() {
+ dump("cancelCompatCheck: starting to send file\n");
+ let str = {};
+ let read = 0;
+ do {
+ // read as much as we can and put it in str.value
+ read = cstream.readString(0xffffffff, str);
+ resp.write(str.value);
+ } while (read != 0);
+ cstream.close();
+ resp.finish();
+ }, delay, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
diff --git a/toolkit/mozapps/extensions/test/browser/discovery.html b/toolkit/mozapps/extensions/test/browser/discovery.html
new file mode 100644
index 000000000..72f4fe374
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/discovery.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+ <h1>Test page for the discovery pane</h1>
+ <p><a id="link-normal" href="https://example.com/browser/toolkit/mozapps/extensions/test/browser/discovery.html">Load normal</a></p>
+ <p><a id="link-http" href="http://example.com/browser/toolkit/mozapps/extensions/test/browser/discovery.html">Load insecure</a></p>
+ <p><a id="link-domain" href="https://test1.example.com/browser/toolkit/mozapps/extensions/test/browser/discovery.html">Load other domain</a></p>
+ <p><a id="link-bad" href="https://example.com/browser/toolkit/mozapps/extensions/test/browser/foo.html">Load missing page</a></p>
+ <p><a id="link-good" href="https://example.com/browser/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml">Load other page</a></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/browser/discovery_frame.html b/toolkit/mozapps/extensions/test/browser/discovery_frame.html
new file mode 100644
index 000000000..ca0223a9c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/discovery_frame.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+
+<html>
+<body>
+<iframe id="frame" width="100%" height="100%" src="https://test1.example.com/browser/toolkit/mozapps/extensions/test/browser/discovery_install.html"></iframe>
+</body>
diff --git a/toolkit/mozapps/extensions/test/browser/discovery_install.html b/toolkit/mozapps/extensions/test/browser/discovery_install.html
new file mode 100644
index 000000000..3832adff6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/discovery_install.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<script type="text/javascript">
+function install() {
+ InstallTrigger.install({
+ "Test Add-on": {
+ URL: "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi"
+ }
+ });
+}
+</script>
+</head>
+<body>
+ <h1>Test page for the discovery pane</h1>
+ <p><a id="install-direct" href="https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi">Direct install</a></p>
+ <p><a id="install-js" href="javascript:install()">JS install</a></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/browser/head.js b/toolkit/mozapps/extensions/test/browser/head.js
new file mode 100644
index 000000000..8e96b9b3f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -0,0 +1,1393 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+let tmp = {};
+Components.utils.import("resource://gre/modules/AddonManager.jsm", tmp);
+Components.utils.import("resource://gre/modules/Log.jsm", tmp);
+let AddonManager = tmp.AddonManager;
+let AddonManagerPrivate = tmp.AddonManagerPrivate;
+let Log = tmp.Log;
+
+var pathParts = gTestPath.split("/");
+// Drop the test filename
+pathParts.splice(pathParts.length - 1, pathParts.length);
+
+var gTestInWindow = /-window$/.test(pathParts[pathParts.length - 1]);
+
+// Drop the UI type
+if (gTestInWindow) {
+ pathParts.splice(pathParts.length - 1, pathParts.length);
+}
+
+const RELATIVE_DIR = pathParts.slice(4).join("/") + "/";
+
+const TESTROOT = "http://example.com/" + RELATIVE_DIR;
+const TESTROOT2 = "http://example.org/" + RELATIVE_DIR;
+const CHROMEROOT = pathParts.join("/") + "/";
+const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
+const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
+const PREF_XPI_ENABLED = "xpinstall.enabled";
+const PREF_UPDATEURL = "extensions.update.url";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
+
+const MANAGER_URI = "about:addons";
+const INSTALL_URI = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults";
+const PREF_STRICT_COMPAT = "extensions.strictCompatibility";
+
+var PREF_CHECK_COMPATIBILITY;
+(function() {
+ var channel = "default";
+ try {
+ channel = Services.prefs.getCharPref("app.update.channel");
+ } catch (e) { }
+ if (channel != "aurora" &&
+ channel != "beta" &&
+ channel != "release") {
+ var version = "nightly";
+ } else {
+ version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1");
+ }
+ PREF_CHECK_COMPATIBILITY = "extensions.checkCompatibility." + version;
+})();
+
+var gPendingTests = [];
+var gTestsRun = 0;
+var gTestStart = null;
+
+var gUseInContentUI = !gTestInWindow && ("switchToTabHavingURI" in window);
+
+var gRestorePrefs = [{name: PREF_LOGGING_ENABLED},
+ {name: "extensions.webservice.discoverURL"},
+ {name: "extensions.update.url"},
+ {name: "extensions.update.background.url"},
+ {name: "extensions.update.enabled"},
+ {name: "extensions.update.autoUpdateDefault"},
+ {name: "extensions.getAddons.get.url"},
+ {name: "extensions.getAddons.getWithPerformance.url"},
+ {name: "extensions.getAddons.search.browseURL"},
+ {name: "extensions.getAddons.search.url"},
+ {name: "extensions.getAddons.cache.enabled"},
+ {name: "devtools.chrome.enabled"},
+ {name: "devtools.debugger.remote-enabled"},
+ {name: PREF_SEARCH_MAXRESULTS},
+ {name: PREF_STRICT_COMPAT},
+ {name: PREF_CHECK_COMPATIBILITY}];
+
+for (let pref of gRestorePrefs) {
+ if (!Services.prefs.prefHasUserValue(pref.name)) {
+ pref.type = "clear";
+ continue;
+ }
+ pref.type = Services.prefs.getPrefType(pref.name);
+ if (pref.type == Services.prefs.PREF_BOOL)
+ pref.value = Services.prefs.getBoolPref(pref.name);
+ else if (pref.type == Services.prefs.PREF_INT)
+ pref.value = Services.prefs.getIntPref(pref.name);
+ else if (pref.type == Services.prefs.PREF_STRING)
+ pref.value = Services.prefs.getCharPref(pref.name);
+}
+
+// Turn logging on for all tests
+Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
+
+// Helper to register test failures and close windows if any are left open
+function checkOpenWindows(aWindowID) {
+ let windows = Services.wm.getEnumerator(aWindowID);
+ let found = false;
+ while (windows.hasMoreElements()) {
+ let win = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+ if (!win.closed) {
+ found = true;
+ win.close();
+ }
+ }
+ if (found)
+ ok(false, "Found unexpected " + aWindowID + " window still open");
+}
+
+// Tools to disable and re-enable the background update and blocklist timers
+// so that tests can protect themselves from unwanted timer events.
+let gCatMan = Components.classes["@mozilla.org/categorymanager;1"]
+ .getService(Components.interfaces.nsICategoryManager);
+// Default values from toolkit/mozapps/extensions/extensions.manifest, but disable*UpdateTimer()
+// records the actual value so we can put it back in enable*UpdateTimer()
+let backgroundUpdateConfig = "@mozilla.org/addons/integration;1,getService,addon-background-update-timer,extensions.update.interval,86400";
+let blocklistUpdateConfig = "@mozilla.org/extensions/blocklist;1,getService,blocklist-background-update-timer,extensions.blocklist.interval,86400";
+
+let UTIMER = "update-timer";
+let AMANAGER = "addonManager";
+let BLOCKLIST = "nsBlocklistService";
+
+function disableBackgroundUpdateTimer() {
+ info("Disabling " + UTIMER + " " + AMANAGER);
+ backgroundUpdateConfig = gCatMan.getCategoryEntry(UTIMER, AMANAGER);
+ gCatMan.deleteCategoryEntry(UTIMER, AMANAGER, true);
+}
+
+function enableBackgroundUpdateTimer() {
+ info("Enabling " + UTIMER + " " + AMANAGER);
+ gCatMan.addCategoryEntry(UTIMER, AMANAGER, backgroundUpdateConfig, false, true);
+}
+
+function disableBlocklistUpdateTimer() {
+ info("Disabling " + UTIMER + " " + BLOCKLIST);
+ blocklistUpdateConfig = gCatMan.getCategoryEntry(UTIMER, BLOCKLIST);
+ gCatMan.deleteCategoryEntry(UTIMER, BLOCKLIST, true);
+}
+
+function enableBlocklistUpdateTimer() {
+ info("Enabling " + UTIMER + " " + BLOCKLIST);
+ gCatMan.addCategoryEntry(UTIMER, BLOCKLIST, blocklistUpdateConfig, false, true);
+}
+
+registerCleanupFunction(function() {
+ // Restore prefs
+ for (let pref of gRestorePrefs) {
+ if (pref.type == "clear")
+ Services.prefs.clearUserPref(pref.name);
+ else if (pref.type == Services.prefs.PREF_BOOL)
+ Services.prefs.setBoolPref(pref.name, pref.value);
+ else if (pref.type == Services.prefs.PREF_INT)
+ Services.prefs.setIntPref(pref.name, pref.value);
+ else if (pref.type == Services.prefs.PREF_STRING)
+ Services.prefs.setCharPref(pref.name, pref.value);
+ }
+
+ // Throw an error if the add-ons manager window is open anywhere
+ checkOpenWindows("Addons:Manager");
+ checkOpenWindows("Addons:Compatibility");
+ checkOpenWindows("Addons:Install");
+
+ return new Promise((resolve, reject) => AddonManager.getAllInstalls(resolve))
+ .then(aInstalls => {
+ for (let install of aInstalls) {
+ if (install instanceof MockInstall)
+ continue;
+
+ ok(false, "Should not have seen an install of " + install.sourceURI.spec + " in state " + install.state);
+ install.cancel();
+ }
+ });
+});
+
+function log_exceptions(aCallback, ...aArgs) {
+ try {
+ return aCallback.apply(null, aArgs);
+ }
+ catch (e) {
+ info("Exception thrown: " + e);
+ throw e;
+ }
+}
+
+function log_callback(aPromise, aCallback) {
+ aPromise.then(aCallback)
+ .then(null, e => info("Exception thrown: " + e));
+ return aPromise;
+}
+
+function add_test(test) {
+ gPendingTests.push(test);
+}
+
+function run_next_test() {
+ // Make sure we're not calling run_next_test from inside an add_task() test
+ // We're inside the browser_test.js 'testScope' here
+ if (this.__tasks) {
+ throw new Error("run_next_test() called from an add_task() test function. " +
+ "run_next_test() should not be called from inside add_task() " +
+ "under any circumstances!");
+ }
+ if (gTestsRun > 0)
+ info("Test " + gTestsRun + " took " + (Date.now() - gTestStart) + "ms");
+
+ if (gPendingTests.length == 0) {
+ executeSoon(end_test);
+ return;
+ }
+
+ gTestsRun++;
+ var test = gPendingTests.shift();
+ if (test.name)
+ info("Running test " + gTestsRun + " (" + test.name + ")");
+ else
+ info("Running test " + gTestsRun);
+
+ gTestStart = Date.now();
+ executeSoon(() => log_exceptions(test));
+}
+
+function get_addon_file_url(aFilename) {
+ try {
+ var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
+ getService(Ci.nsIChromeRegistry);
+ var fileurl = cr.convertChromeURL(makeURI(CHROMEROOT + "addons/" + aFilename));
+ return fileurl.QueryInterface(Ci.nsIFileURL);
+ } catch(ex) {
+ var jar = getJar(CHROMEROOT + "addons/" + aFilename);
+ var tmpDir = extractJarToTmp(jar);
+ tmpDir.append(aFilename);
+
+ return Services.io.newFileURI(tmpDir).QueryInterface(Ci.nsIFileURL);
+ }
+}
+
+function get_test_items_in_list(aManager) {
+ var tests = "@tests.mozilla.org";
+
+ let view = aManager.document.getElementById("view-port").selectedPanel;
+ let listid = view.id == "search-view" ? "search-list" : "addon-list";
+ let item = aManager.document.getElementById(listid).firstChild;
+ let items = [];
+
+ while (item) {
+ if (item.localName != "richlistitem") {
+ item = item.nextSibling;
+ continue;
+ }
+
+ if (!item.mAddon || item.mAddon.id.substring(item.mAddon.id.length - tests.length) == tests)
+ items.push(item);
+ item = item.nextSibling;
+ }
+
+ return items;
+}
+
+function check_all_in_list(aManager, aIds, aIgnoreExtras) {
+ var doc = aManager.document;
+ var view = doc.getElementById("view-port").selectedPanel;
+ var listid = view.id == "search-view" ? "search-list" : "addon-list";
+ var list = doc.getElementById(listid);
+
+ var inlist = [];
+ var node = list.firstChild;
+ while (node) {
+ if (node.value)
+ inlist.push(node.value);
+ node = node.nextSibling;
+ }
+
+ for (let id of aIds) {
+ if (inlist.indexOf(id) == -1)
+ ok(false, "Should find " + id + " in the list");
+ }
+
+ if (aIgnoreExtras)
+ return;
+
+ for (let inlistItem of inlist) {
+ if (aIds.indexOf(inlistItem) == -1)
+ ok(false, "Shouldn't have seen " + inlistItem + " in the list");
+ }
+}
+
+function get_addon_element(aManager, aId) {
+ var doc = aManager.document;
+ var view = doc.getElementById("view-port").selectedPanel;
+ var listid = "addon-list";
+ if (view.id == "search-view")
+ listid = "search-list";
+ else if (view.id == "updates-view")
+ listid = "updates-list";
+ var list = doc.getElementById(listid);
+
+ var node = list.firstChild;
+ while (node) {
+ if (node.value == aId)
+ return node;
+ node = node.nextSibling;
+ }
+ return null;
+}
+
+function wait_for_view_load(aManagerWindow, aCallback, aForceWait, aLongerTimeout) {
+ requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2);
+
+ if (!aForceWait && !aManagerWindow.gViewController.isLoading) {
+ log_exceptions(aCallback, aManagerWindow);
+ return;
+ }
+
+ aManagerWindow.document.addEventListener("ViewChanged", function() {
+ aManagerWindow.document.removeEventListener("ViewChanged", arguments.callee, false);
+ log_exceptions(aCallback, aManagerWindow);
+ }, false);
+}
+
+function wait_for_manager_load(aManagerWindow, aCallback) {
+ if (!aManagerWindow.gIsInitializing) {
+ log_exceptions(aCallback, aManagerWindow);
+ return;
+ }
+
+ info("Waiting for initialization");
+ aManagerWindow.document.addEventListener("Initialized", function() {
+ aManagerWindow.document.removeEventListener("Initialized", arguments.callee, false);
+ log_exceptions(aCallback, aManagerWindow);
+ }, false);
+}
+
+function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) {
+ let p = new Promise((resolve, reject) => {
+
+ function setup_manager(aManagerWindow) {
+ if (aLoadCallback)
+ log_exceptions(aLoadCallback, aManagerWindow);
+
+ if (aView)
+ aManagerWindow.loadView(aView);
+
+ ok(aManagerWindow != null, "Should have an add-ons manager window");
+ is(aManagerWindow.location, MANAGER_URI, "Should be displaying the correct UI");
+
+ waitForFocus(function() {
+ info("window has focus, waiting for manager load");
+ wait_for_manager_load(aManagerWindow, function() {
+ info("Manager waiting for view load");
+ wait_for_view_load(aManagerWindow, function() {
+ resolve(aManagerWindow);
+ }, null, aLongerTimeout);
+ });
+ }, aManagerWindow);
+ }
+
+ if (gUseInContentUI) {
+ info("Loading manager window in tab");
+ Services.obs.addObserver(function (aSubject, aTopic, aData) {
+ Services.obs.removeObserver(arguments.callee, aTopic);
+ if (aSubject.location.href != MANAGER_URI) {
+ info("Ignoring load event for " + aSubject.location.href);
+ return;
+ }
+ setup_manager(aSubject);
+ }, "EM-loaded", false);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ switchToTabHavingURI(MANAGER_URI, true);
+ } else {
+ info("Loading manager window in dialog");
+ Services.obs.addObserver(function (aSubject, aTopic, aData) {
+ Services.obs.removeObserver(arguments.callee, aTopic);
+ setup_manager(aSubject);
+ }, "EM-loaded", false);
+
+ openDialog(MANAGER_URI);
+ }
+ });
+
+ // The promise resolves with the manager window, so it is passed to the callback
+ return log_callback(p, aCallback);
+}
+
+function close_manager(aManagerWindow, aCallback, aLongerTimeout) {
+ let p = new Promise((resolve, reject) => {
+ requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2);
+
+ ok(aManagerWindow != null, "Should have an add-ons manager window to close");
+ is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI");
+
+ aManagerWindow.addEventListener("unload", function() {
+ try {
+ dump("Manager window unload handler\n");
+ this.removeEventListener("unload", arguments.callee, false);
+ resolve();
+ } catch(e) {
+ reject(e);
+ }
+ }, false);
+ });
+
+ info("Telling manager window to close");
+ aManagerWindow.close();
+ info("Manager window close() call returned");
+
+ return log_callback(p, aCallback);
+}
+
+function restart_manager(aManagerWindow, aView, aCallback, aLoadCallback) {
+ if (!aManagerWindow) {
+ return open_manager(aView, aCallback, aLoadCallback);
+ }
+
+ return close_manager(aManagerWindow)
+ .then(() => open_manager(aView, aCallback, aLoadCallback));
+}
+
+function wait_for_window_open(aCallback) {
+ Services.wm.addListener({
+ onOpenWindow: function(aWindow) {
+ Services.wm.removeListener(this);
+
+ let domwindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ domwindow.addEventListener("load", function() {
+ domwindow.removeEventListener("load", arguments.callee, false);
+ executeSoon(function() {
+ aCallback(domwindow);
+ });
+ }, false);
+ },
+
+ onCloseWindow: function(aWindow) {
+ },
+
+ onWindowTitleChange: function(aWindow, aTitle) {
+ }
+ });
+}
+
+function get_string(aName, ...aArgs) {
+ var bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
+ if (aArgs.length == 0)
+ return bundle.GetStringFromName(aName);
+ return bundle.formatStringFromName(aName, aArgs, aArgs.length);
+}
+
+function formatDate(aDate) {
+ return Cc["@mozilla.org/intl/scriptabledateformat;1"]
+ .getService(Ci.nsIScriptableDateFormat)
+ .FormatDate("",
+ Ci.nsIScriptableDateFormat.dateFormatLong,
+ aDate.getFullYear(),
+ aDate.getMonth() + 1,
+ aDate.getDate()
+ );
+}
+
+function is_hidden(aElement) {
+ var style = aElement.ownerDocument.defaultView.getComputedStyle(aElement, "");
+ if (style.display == "none")
+ return true;
+ if (style.visibility != "visible")
+ return true;
+
+ // Hiding a parent element will hide all its children
+ if (aElement.parentNode != aElement.ownerDocument)
+ return is_hidden(aElement.parentNode);
+
+ return false;
+}
+
+function is_element_visible(aElement, aMsg) {
+ isnot(aElement, null, "Element should not be null, when checking visibility");
+ ok(!is_hidden(aElement), aMsg || (aElement + " should be visible"));
+}
+
+function is_element_hidden(aElement, aMsg) {
+ isnot(aElement, null, "Element should not be null, when checking visibility");
+ ok(is_hidden(aElement), aMsg || (aElement + " should be hidden"));
+}
+
+/**
+ * Install an add-on and call a callback when complete.
+ *
+ * The callback will receive the Addon for the installed add-on.
+ */
+function install_addon(path, cb, pathPrefix=TESTROOT) {
+ let p = new Promise((resolve, reject) => {
+ AddonManager.getInstallForURL(pathPrefix + path, (install) => {
+ install.addListener({
+ onInstallEnded: () => resolve(install.addon),
+ });
+
+ install.install();
+ }, "application/x-xpinstall");
+ });
+
+ return log_callback(p, cb);
+}
+
+function CategoryUtilities(aManagerWindow) {
+ this.window = aManagerWindow;
+
+ var self = this;
+ this.window.addEventListener("unload", function() {
+ self.window.removeEventListener("unload", arguments.callee, false);
+ self.window = null;
+ }, false);
+}
+
+CategoryUtilities.prototype = {
+ window: null,
+
+ get selectedCategory() {
+ isnot(this.window, null, "Should not get selected category when manager window is not loaded");
+ var selectedItem = this.window.document.getElementById("categories").selectedItem;
+ isnot(selectedItem, null, "A category should be selected");
+ var view = this.window.gViewController.parseViewId(selectedItem.value);
+ return (view.type == "list") ? view.param : view.type;
+ },
+
+ get: function(aCategoryType, aAllowMissing) {
+ isnot(this.window, null, "Should not get category when manager window is not loaded");
+ var categories = this.window.document.getElementById("categories");
+
+ var viewId = "addons://list/" + aCategoryType;
+ var items = categories.getElementsByAttribute("value", viewId);
+ if (items.length)
+ return items[0];
+
+ viewId = "addons://" + aCategoryType + "/";
+ items = categories.getElementsByAttribute("value", viewId);
+ if (items.length)
+ return items[0];
+
+ if (!aAllowMissing)
+ ok(false, "Should have found a category with type " + aCategoryType);
+ return null;
+ },
+
+ getViewId: function(aCategoryType) {
+ isnot(this.window, null, "Should not get view id when manager window is not loaded");
+ return this.get(aCategoryType).value;
+ },
+
+ isVisible: function(aCategory) {
+ isnot(this.window, null, "Should not check visible state when manager window is not loaded");
+ if (aCategory.hasAttribute("disabled") &&
+ aCategory.getAttribute("disabled") == "true")
+ return false;
+
+ return !is_hidden(aCategory);
+ },
+
+ isTypeVisible: function(aCategoryType) {
+ return this.isVisible(this.get(aCategoryType));
+ },
+
+ open: function(aCategory, aCallback) {
+
+ isnot(this.window, null, "Should not open category when manager window is not loaded");
+ ok(this.isVisible(aCategory), "Category should be visible if attempting to open it");
+
+ EventUtils.synthesizeMouse(aCategory, 2, 2, { }, this.window);
+ let p = new Promise((resolve, reject) => wait_for_view_load(this.window, resolve));
+
+ return log_callback(p, aCallback);
+ },
+
+ openType: function(aCategoryType, aCallback) {
+ return this.open(this.get(aCategoryType), aCallback);
+ }
+}
+
+function CertOverrideListener(host, bits) {
+ this.host = host;
+ this.bits = bits;
+}
+
+CertOverrideListener.prototype = {
+ host: null,
+ bits: null,
+
+ getInterface: function (aIID) {
+ return this.QueryInterface(aIID);
+ },
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIBadCertListener2) ||
+ aIID.equals(Ci.nsIInterfaceRequestor) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE);
+ },
+
+ notifyCertProblem: function (socketInfo, sslStatus, targetHost) {
+ var cert = sslStatus.QueryInterface(Components.interfaces.nsISSLStatus)
+ .serverCert;
+ var cos = Cc["@mozilla.org/security/certoverride;1"].
+ getService(Ci.nsICertOverrideService);
+ cos.rememberValidityOverride(this.host, -1, cert, this.bits, false);
+ return true;
+ }
+}
+
+// Add overrides for the bad certificates
+function addCertOverride(host, bits) {
+ var req = new XMLHttpRequest();
+ try {
+ req.open("GET", "https://" + host + "/", false);
+ req.channel.notificationCallbacks = new CertOverrideListener(host, bits);
+ req.send(null);
+ }
+ catch (e) {
+ // This request will fail since the SSL server is not trusted yet
+ }
+}
+
+/***** Mock Provider *****/
+
+function MockProvider(aUseAsyncCallbacks, aTypes) {
+ this.addons = [];
+ this.installs = [];
+ this.callbackTimers = [];
+ this.timerLocations = new Map();
+ this.useAsyncCallbacks = (aUseAsyncCallbacks === undefined) ? true : aUseAsyncCallbacks;
+ this.types = (aTypes === undefined) ? [{
+ id: "extension",
+ name: "Extensions",
+ uiPriority: 4000,
+ flags: AddonManager.TYPE_UI_VIEW_LIST |
+ AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL,
+ }] : aTypes;
+
+ var self = this;
+ registerCleanupFunction(function() {
+ if (self.started)
+ self.unregister();
+ });
+
+ this.register();
+}
+
+MockProvider.prototype = {
+ addons: null,
+ installs: null,
+ started: null,
+ apiDelay: 10,
+ callbackTimers: null,
+ timerLocations: null,
+ useAsyncCallbacks: null,
+ types: null,
+
+ /***** Utility functions *****/
+
+ /**
+ * Register this provider with the AddonManager
+ */
+ register: function MP_register() {
+ info("Registering mock add-on provider");
+ AddonManagerPrivate.registerProvider(this, this.types);
+ },
+
+ /**
+ * Unregister this provider with the AddonManager
+ */
+ unregister: function MP_unregister() {
+ info("Unregistering mock add-on provider");
+ AddonManagerPrivate.unregisterProvider(this);
+ },
+
+ /**
+ * Adds an add-on to the list of add-ons that this provider exposes to the
+ * AddonManager, dispatching appropriate events in the process.
+ *
+ * @param aAddon
+ * The add-on to add
+ */
+ addAddon: function MP_addAddon(aAddon) {
+ var oldAddons = this.addons.filter(function(aOldAddon) aOldAddon.id == aAddon.id);
+ var oldAddon = oldAddons.length > 0 ? oldAddons[0] : null;
+
+ this.addons = this.addons.filter(function(aOldAddon) aOldAddon.id != aAddon.id);
+
+ this.addons.push(aAddon);
+ aAddon._provider = this;
+
+ if (!this.started)
+ return;
+
+ let requiresRestart = (aAddon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL) != 0;
+ AddonManagerPrivate.callInstallListeners("onExternalInstall", null, aAddon,
+ oldAddon, requiresRestart)
+ },
+
+ /**
+ * Removes an add-on from the list of add-ons that this provider exposes to
+ * the AddonManager, dispatching the onUninstalled event in the process.
+ *
+ * @param aAddon
+ * The add-on to add
+ */
+ removeAddon: function MP_removeAddon(aAddon) {
+ var pos = this.addons.indexOf(aAddon);
+ if (pos == -1) {
+ ok(false, "Tried to remove an add-on that wasn't registered with the mock provider");
+ return;
+ }
+
+ this.addons.splice(pos, 1);
+
+ if (!this.started)
+ return;
+
+ AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon);
+ },
+
+ /**
+ * Adds an add-on install to the list of installs that this provider exposes
+ * to the AddonManager, dispatching appropriate events in the process.
+ *
+ * @param aInstall
+ * The add-on install to add
+ */
+ addInstall: function MP_addInstall(aInstall) {
+ this.installs.push(aInstall);
+ aInstall._provider = this;
+
+ if (!this.started)
+ return;
+
+ aInstall.callListeners("onNewInstall");
+ },
+
+ removeInstall: function MP_removeInstall(aInstall) {
+ var pos = this.installs.indexOf(aInstall);
+ if (pos == -1) {
+ ok(false, "Tried to remove an install that wasn't registered with the mock provider");
+ return;
+ }
+
+ this.installs.splice(pos, 1);
+ },
+
+ /**
+ * Creates a set of mock add-on objects and adds them to the list of add-ons
+ * managed by this provider.
+ *
+ * @param aAddonProperties
+ * An array of objects containing properties describing the add-ons
+ * @return Array of the new MockAddons
+ */
+ createAddons: function MP_createAddons(aAddonProperties) {
+ var newAddons = [];
+ for (let addonProp of aAddonProperties) {
+ let addon = new MockAddon(addonProp.id);
+ for (let prop in addonProp) {
+ if (prop == "id")
+ continue;
+ if (prop == "applyBackgroundUpdates") {
+ addon._applyBackgroundUpdates = addonProp[prop];
+ continue;
+ }
+ if (prop == "appDisabled") {
+ addon._appDisabled = addonProp[prop];
+ continue;
+ }
+ addon[prop] = addonProp[prop];
+ }
+ if (!addon.optionsType && !!addon.optionsURL)
+ addon.optionsType = AddonManager.OPTIONS_TYPE_DIALOG;
+
+ // Make sure the active state matches the passed in properties
+ addon.isActive = addon.shouldBeActive;
+
+ this.addAddon(addon);
+ newAddons.push(addon);
+ }
+
+ return newAddons;
+ },
+
+ /**
+ * Creates a set of mock add-on install objects and adds them to the list
+ * of installs managed by this provider.
+ *
+ * @param aInstallProperties
+ * An array of objects containing properties describing the installs
+ * @return Array of the new MockInstalls
+ */
+ createInstalls: function MP_createInstalls(aInstallProperties) {
+ var newInstalls = [];
+ for (let installProp of aInstallProperties) {
+ let install = new MockInstall(installProp.name || null,
+ installProp.type || null,
+ null);
+ for (let prop in installProp) {
+ switch (prop) {
+ case "name":
+ case "type":
+ break;
+ case "sourceURI":
+ install[prop] = NetUtil.newURI(installProp[prop]);
+ break;
+ default:
+ install[prop] = installProp[prop];
+ }
+ }
+ this.addInstall(install);
+ newInstalls.push(install);
+ }
+
+ return newInstalls;
+ },
+
+ /***** AddonProvider implementation *****/
+
+ /**
+ * Called to initialize the provider.
+ */
+ startup: function MP_startup() {
+ this.started = true;
+ },
+
+ /**
+ * Called when the provider should shutdown.
+ */
+ shutdown: function MP_shutdown() {
+ if (this.callbackTimers.length) {
+ info("MockProvider: pending callbacks at shutdown(): calling immediately");
+ }
+ while (this.callbackTimers.length > 0) {
+ // When we notify the callback timer, it removes itself from our array
+ let timer = this.callbackTimers[0];
+ try {
+ let setAt = this.timerLocations.get(timer);
+ info("Notifying timer set at " + (setAt || "unknown location"));
+ timer.callback.notify(timer);
+ timer.cancel();
+ } catch(e) {
+ info("Timer notify failed: " + e);
+ }
+ }
+ this.callbackTimers = [];
+ this.timerLocations = null;
+
+ this.started = false;
+ },
+
+ /**
+ * Called to get an Addon with a particular ID.
+ *
+ * @param aId
+ * The ID of the add-on to retrieve
+ * @param aCallback
+ * A callback to pass the Addon to
+ */
+ getAddonByID: function MP_getAddon(aId, aCallback) {
+ for (let addon of this.addons) {
+ if (addon.id == aId) {
+ this._delayCallback(aCallback, addon);
+ return;
+ }
+ }
+
+ aCallback(null);
+ },
+
+ /**
+ * Called to get Addons of a particular type.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types.
+ * @param callback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsByTypes: function MP_getAddonsByTypes(aTypes, aCallback) {
+ var addons = this.addons.filter(function(aAddon) {
+ if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1)
+ return false;
+ return true;
+ });
+ this._delayCallback(aCallback, addons);
+ },
+
+ /**
+ * Called to get Addons that have pending operations.
+ *
+ * @param aTypes
+ * An array of types to fetch. Can be null to get all types
+ * @param aCallback
+ * A callback to pass an array of Addons to
+ */
+ getAddonsWithOperationsByTypes: function MP_getAddonsWithOperationsByTypes(aTypes, aCallback) {
+ var addons = this.addons.filter(function(aAddon) {
+ if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1)
+ return false;
+ return aAddon.pendingOperations != 0;
+ });
+ this._delayCallback(aCallback, addons);
+ },
+
+ /**
+ * Called to get the current AddonInstalls, optionally restricting by type.
+ *
+ * @param aTypes
+ * An array of types or null to get all types
+ * @param aCallback
+ * A callback to pass the array of AddonInstalls to
+ */
+ getInstallsByTypes: function MP_getInstallsByTypes(aTypes, aCallback) {
+ var installs = this.installs.filter(function(aInstall) {
+ // Appear to have actually removed cancelled installs from the provider
+ if (aInstall.state == AddonManager.STATE_CANCELLED)
+ return false;
+
+ if (aTypes && aTypes.length > 0 && aTypes.indexOf(aInstall.type) == -1)
+ return false;
+
+ return true;
+ });
+ this._delayCallback(aCallback, installs);
+ },
+
+ /**
+ * Called when a new add-on has been enabled when only one add-on of that type
+ * can be enabled.
+ *
+ * @param aId
+ * The ID of the newly enabled add-on
+ * @param aType
+ * The type of the newly enabled add-on
+ * @param aPendingRestart
+ * true if the newly enabled add-on will only become enabled after a
+ * restart
+ */
+ addonChanged: function MP_addonChanged(aId, aType, aPendingRestart) {
+ // Not implemented
+ },
+
+ /**
+ * Update the appDisabled property for all add-ons.
+ */
+ updateAddonAppDisabledStates: function MP_updateAddonAppDisabledStates() {
+ // Not needed
+ },
+
+ /**
+ * Called to get an AddonInstall to download and install an add-on from a URL.
+ *
+ * @param aUrl
+ * The URL to be installed
+ * @param aHash
+ * A hash for the install
+ * @param aName
+ * A name for the install
+ * @param aIconURL
+ * An icon URL for the install
+ * @param aVersion
+ * A version for the install
+ * @param aLoadGroup
+ * An nsILoadGroup to associate requests with
+ * @param aCallback
+ * A callback to pass the AddonInstall to
+ */
+ getInstallForURL: function MP_getInstallForURL(aUrl, aHash, aName, aIconURL,
+ aVersion, aLoadGroup, aCallback) {
+ // Not yet implemented
+ },
+
+ /**
+ * Called to get an AddonInstall to install an add-on from a local file.
+ *
+ * @param aFile
+ * The file to be installed
+ * @param aCallback
+ * A callback to pass the AddonInstall to
+ */
+ getInstallForFile: function MP_getInstallForFile(aFile, aCallback) {
+ // Not yet implemented
+ },
+
+ /**
+ * Called to test whether installing add-ons is enabled.
+ *
+ * @return true if installing is enabled
+ */
+ isInstallEnabled: function MP_isInstallEnabled() {
+ return false;
+ },
+
+ /**
+ * Called to test whether this provider supports installing a particular
+ * mimetype.
+ *
+ * @param aMimetype
+ * The mimetype to check for
+ * @return true if the mimetype is supported
+ */
+ supportsMimetype: function MP_supportsMimetype(aMimetype) {
+ return false;
+ },
+
+ /**
+ * Called to test whether installing add-ons from a URI is allowed.
+ *
+ * @param aUri
+ * The URI being installed from
+ * @return true if installing is allowed
+ */
+ isInstallAllowed: function MP_isInstallAllowed(aUri) {
+ return false;
+ },
+
+
+ /***** Internal functions *****/
+
+ /**
+ * Delay calling a callback to fake a time-consuming async operation.
+ * The delay is specified by the apiDelay property, in milliseconds.
+ * Parameters to send to the callback should be specified as arguments after
+ * the aCallback argument.
+ *
+ * @param aCallback Callback to eventually call
+ */
+ _delayCallback: function MP_delayCallback(aCallback, ...aArgs) {
+ if (!this.useAsyncCallbacks) {
+ aCallback(...aArgs);
+ return;
+ }
+
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ // Need to keep a reference to the timer, so it doesn't get GC'ed
+ this.callbackTimers.push(timer);
+ // Capture a stack trace where the timer was set
+ // needs the 'new Error' hack until bug 1007656
+ this.timerLocations.set(timer, Log.stackTrace(new Error("dummy")));
+ timer.initWithCallback(() => {
+ let idx = this.callbackTimers.indexOf(timer);
+ if (idx == -1) {
+ dump("MockProvider._delayCallback lost track of timer set at "
+ + (this.timerLocations.get(timer) || "unknown location") + "\n");
+ } else {
+ this.callbackTimers.splice(idx, 1);
+ }
+ this.timerLocations.delete(timer);
+ aCallback(...aArgs);
+ }, this.apiDelay, timer.TYPE_ONE_SHOT);
+ }
+};
+
+/***** Mock Addon object for the Mock Provider *****/
+
+function MockAddon(aId, aName, aType, aOperationsRequiringRestart) {
+ // Only set required attributes.
+ this.id = aId || "";
+ this.name = aName || "";
+ this.type = aType || "extension";
+ this.version = "";
+ this.isCompatible = true;
+ this.isDebuggable = false;
+ this.providesUpdatesSecurely = true;
+ this.blocklistState = 0;
+ this._appDisabled = false;
+ this._userDisabled = false;
+ this._applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ this.scope = AddonManager.SCOPE_PROFILE;
+ this.isActive = true;
+ this.creator = "";
+ this.pendingOperations = 0;
+ this._permissions = AddonManager.PERM_CAN_UNINSTALL |
+ AddonManager.PERM_CAN_ENABLE |
+ AddonManager.PERM_CAN_DISABLE |
+ AddonManager.PERM_CAN_UPGRADE;
+ this.operationsRequiringRestart = aOperationsRequiringRestart ||
+ (AddonManager.OP_NEEDS_RESTART_INSTALL |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL |
+ AddonManager.OP_NEEDS_RESTART_ENABLE |
+ AddonManager.OP_NEEDS_RESTART_DISABLE);
+}
+
+MockAddon.prototype = {
+ get shouldBeActive() {
+ return !this.appDisabled && !this._userDisabled &&
+ !(this.pendingOperations & AddonManager.PENDING_UNINSTALL);
+ },
+
+ get appDisabled() {
+ return this._appDisabled;
+ },
+
+ set appDisabled(val) {
+ if (val == this._appDisabled)
+ return val;
+
+ AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["appDisabled"]);
+
+ var currentActive = this.shouldBeActive;
+ this._appDisabled = val;
+ var newActive = this.shouldBeActive;
+ this._updateActiveState(currentActive, newActive);
+
+ return val;
+ },
+
+ get userDisabled() {
+ return this._userDisabled;
+ },
+
+ set userDisabled(val) {
+ if (val == this._userDisabled)
+ return val;
+
+ var currentActive = this.shouldBeActive;
+ this._userDisabled = val;
+ var newActive = this.shouldBeActive;
+ this._updateActiveState(currentActive, newActive);
+
+ return val;
+ },
+
+ get permissions() {
+ let permissions = this._permissions;
+ if (this.appDisabled || !this._userDisabled)
+ permissions &= ~AddonManager.PERM_CAN_ENABLE;
+ if (this.appDisabled || this._userDisabled)
+ permissions &= ~AddonManager.PERM_CAN_DISABLE;
+ return permissions;
+ },
+
+ set permissions(val) {
+ return this._permissions = val;
+ },
+
+ get applyBackgroundUpdates() {
+ return this._applyBackgroundUpdates;
+ },
+
+ set applyBackgroundUpdates(val) {
+ if (val != AddonManager.AUTOUPDATE_DEFAULT &&
+ val != AddonManager.AUTOUPDATE_DISABLE &&
+ val != AddonManager.AUTOUPDATE_ENABLE) {
+ ok(false, "addon.applyBackgroundUpdates set to an invalid value: " + val);
+ }
+ this._applyBackgroundUpdates = val;
+ AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]);
+ },
+
+ isCompatibleWith: function(aAppVersion, aPlatformVersion) {
+ return true;
+ },
+
+ findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ // Tests can implement this if they need to
+ },
+
+ uninstall: function(aAlwaysAllowUndo = false) {
+ if ((this.operationsRequiringRestart & AddonManager.OP_NEED_RESTART_UNINSTALL)
+ && this.pendingOperations & AddonManager.PENDING_UNINSTALL)
+ throw Components.Exception("Add-on is already pending uninstall");
+
+ var needsRestart = aAlwaysAllowUndo || !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ this.pendingOperations |= AddonManager.PENDING_UNINSTALL;
+ AddonManagerPrivate.callAddonListeners("onUninstalling", this, needsRestart);
+ if (!needsRestart) {
+ this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
+ this._provider.removeAddon(this);
+ } else if (!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE)) {
+ this.isActive = false;
+ }
+ },
+
+ cancelUninstall: function() {
+ if (!(this.pendingOperations & AddonManager.PENDING_UNINSTALL))
+ throw Components.Exception("Add-on is not pending uninstall");
+
+ this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
+ this.isActive = this.shouldBeActive;
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
+ },
+
+ _updateActiveState: function(currentActive, newActive) {
+ if (currentActive == newActive)
+ return;
+
+ if (newActive == this.isActive) {
+ this.pendingOperations -= (newActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE);
+ AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
+ }
+ else if (newActive) {
+ var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE);
+ this.pendingOperations |= AddonManager.PENDING_ENABLE;
+ AddonManagerPrivate.callAddonListeners("onEnabling", this, needsRestart);
+ if (!needsRestart) {
+ this.isActive = newActive;
+ this.pendingOperations -= AddonManager.PENDING_ENABLE;
+ AddonManagerPrivate.callAddonListeners("onEnabled", this);
+ }
+ }
+ else {
+ var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE);
+ this.pendingOperations |= AddonManager.PENDING_DISABLE;
+ AddonManagerPrivate.callAddonListeners("onDisabling", this, needsRestart);
+ if (!needsRestart) {
+ this.isActive = newActive;
+ this.pendingOperations -= AddonManager.PENDING_DISABLE;
+ AddonManagerPrivate.callAddonListeners("onDisabled", this);
+ }
+ }
+ }
+};
+
+/***** Mock AddonInstall object for the Mock Provider *****/
+
+function MockInstall(aName, aType, aAddonToInstall) {
+ this.name = aName || "";
+ // Don't expose type until download completed
+ this._type = aType || "extension";
+ this.type = null;
+ this.version = "1.0";
+ this.iconURL = "";
+ this.infoURL = "";
+ this.state = AddonManager.STATE_AVAILABLE;
+ this.error = 0;
+ this.sourceURI = null;
+ this.file = null;
+ this.progress = 0;
+ this.maxProgress = -1;
+ this.certificate = null;
+ this.certName = "";
+ this.existingAddon = null;
+ this.addon = null;
+ this._addonToInstall = aAddonToInstall;
+ this.listeners = [];
+
+ // Another type of install listener for tests that want to check the results
+ // of code run from standard install listeners
+ this.testListeners = [];
+}
+
+MockInstall.prototype = {
+ install: function() {
+ switch (this.state) {
+ case AddonManager.STATE_AVAILABLE:
+ this.state = AddonManager.STATE_DOWNLOADING;
+ if (!this.callListeners("onDownloadStarted")) {
+ this.state = AddonManager.STATE_CANCELLED;
+ this.callListeners("onDownloadCancelled");
+ return;
+ }
+
+ this.type = this._type;
+
+ // Adding addon to MockProvider to be implemented when needed
+ if (this._addonToInstall)
+ this.addon = this._addonToInstall;
+ else {
+ this.addon = new MockAddon("", this.name, this.type);
+ this.addon.version = this.version;
+ this.addon.pendingOperations = AddonManager.PENDING_INSTALL;
+ }
+ this.addon.install = this;
+ if (this.existingAddon) {
+ if (!this.addon.id)
+ this.addon.id = this.existingAddon.id;
+ this.existingAddon.pendingUpgrade = this.addon;
+ this.existingAddon.pendingOperations |= AddonManager.PENDING_UPGRADE;
+ }
+
+ this.state = AddonManager.STATE_DOWNLOADED;
+ this.callListeners("onDownloadEnded");
+
+ case AddonManager.STATE_DOWNLOADED:
+ this.state = AddonManager.STATE_INSTALLING;
+ if (!this.callListeners("onInstallStarted")) {
+ this.state = AddonManager.STATE_CANCELLED;
+ this.callListeners("onInstallCancelled");
+ return;
+ }
+
+ AddonManagerPrivate.callAddonListeners("onInstalling", this.addon);
+
+ this.state = AddonManager.STATE_INSTALLED;
+ this.callListeners("onInstallEnded");
+ break;
+ case AddonManager.STATE_DOWNLOADING:
+ case AddonManager.STATE_CHECKING:
+ case AddonManger.STATE_INSTALLING:
+ // Installation is already running
+ return;
+ default:
+ ok(false, "Cannot start installing when state = " + this.state);
+ }
+ },
+
+ cancel: function() {
+ switch (this.state) {
+ case AddonManager.STATE_AVAILABLE:
+ this.state = AddonManager.STATE_CANCELLED;
+ break;
+ case AddonManager.STATE_INSTALLED:
+ this.state = AddonManager.STATE_CANCELLED;
+ this._provider.removeInstall(this);
+ this.callListeners("onInstallCancelled");
+ break;
+ default:
+ // Handling cancelling when downloading to be implemented when needed
+ ok(false, "Cannot cancel when state = " + this.state);
+ }
+ },
+
+
+ addListener: function(aListener) {
+ if (!this.listeners.some(function(i) i == aListener))
+ this.listeners.push(aListener);
+ },
+
+ removeListener: function(aListener) {
+ this.listeners = this.listeners.filter(function(i) i != aListener);
+ },
+
+ addTestListener: function(aListener) {
+ if (!this.testListeners.some(function(i) i == aListener))
+ this.testListeners.push(aListener);
+ },
+
+ removeTestListener: function(aListener) {
+ this.testListeners = this.testListeners.filter(function(i) i != aListener);
+ },
+
+ callListeners: function(aMethod) {
+ var result = AddonManagerPrivate.callInstallListeners(aMethod, this.listeners,
+ this, this.addon);
+
+ // Call test listeners after standard listeners to remove race condition
+ // between standard and test listeners
+ for (let listener of this.testListeners) {
+ try {
+ if (aMethod in listener)
+ if (listener[aMethod].call(listener, this, this.addon) === false)
+ result = false;
+ }
+ catch (e) {
+ ok(false, "Test listener threw exception: " + e);
+ }
+ }
+
+ return result;
+ }
+};
+
+function waitForCondition(condition, nextTest, errorMsg) {
+ let tries = 0;
+ let interval = setInterval(function() {
+ if (tries >= 30) {
+ ok(false, errorMsg);
+ moveOn();
+ }
+ var conditionPassed;
+ try {
+ conditionPassed = condition();
+ } catch (e) {
+ ok(false, e + "\n" + e.stack);
+ conditionPassed = false;
+ }
+ if (conditionPassed) {
+ moveOn();
+ }
+ tries++;
+ }, 100);
+ let moveOn = function() { clearInterval(interval); nextTest(); };
+}
+
+function getTestPluginTag() {
+ let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ let tags = ph.getPluginTags();
+
+ // Find the test plugin
+ for (let i = 0; i < tags.length; i++) {
+ if (tags[i].name == "Test Plug-in")
+ return tags[i];
+ }
+ ok(false, "Unable to find plugin");
+ return null;
+}
diff --git a/toolkit/mozapps/extensions/test/browser/more_options.xul b/toolkit/mozapps/extensions/test/browser/more_options.xul
new file mode 100644
index 000000000..4c8474a79
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/more_options.xul
@@ -0,0 +1,32 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting pref="extensions.inlinesettings3.radioBool" type="radio" title="Radio">
+ <radiogroup>
+ <radio label="Delta" value="true" />
+ <radio label="Echo" value="false" />
+ </radiogroup>
+ </setting>
+ <setting pref="extensions.inlinesettings3.radioInt" type="radio" title="Radio">
+ <radiogroup>
+ <radio label="Foxtrot" value="4" />
+ <radio label="Golf" value="5" />
+ <radio label="Hotel" value="6" />
+ </radiogroup>
+ </setting>
+ <setting pref="extensions.inlinesettings3.radioString" type="radio" title="Radio">
+ <radiogroup>
+ <radio label="India" value="india" />
+ <radio label="Juliet" value="juliet" />
+ <radio label="Kilo" value="kilo" />
+ </radiogroup>
+ </setting>
+ <setting pref="extensions.inlinesettings3.menulist" type="menulist" title="Menulist">
+ <menulist sizetopopup="always">
+ <menupopup>
+ <menuitem label="Lima" value="7" />
+ <menuitem label="Mike" value="8" />
+ <menuitem label="November" value="9" />
+ </menupopup>
+ </menulist>
+ </setting>
+</vbox>
diff --git a/toolkit/mozapps/extensions/test/browser/moz.build b/toolkit/mozapps/extensions/test/browser/moz.build
new file mode 100644
index 000000000..9acb42133
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/moz.build
@@ -0,0 +1,10 @@
+# -*- 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/.
+
+BROWSER_CHROME_MANIFESTS += [
+ 'browser-window.ini',
+ 'browser.ini',
+]
diff --git a/toolkit/mozapps/extensions/test/browser/options.xul b/toolkit/mozapps/extensions/test/browser/options.xul
new file mode 100644
index 000000000..1b6827915
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/options.xul
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting />
+ <setting pref="extensions.inlinesettings2.bool1" type="bool" title="Bool 1" desc="Description Attribute"/>
+ <setting pref="extensions.inlinesettings2.bool2" type="bool" title="Bool 2">Description Text Node</setting>
+ <setting type="control" title="Button">
+ This is a test, <button label="button" />all this text should be visible
+ </setting>
+ <setting type="unsupported">
+ This setting should never appear
+ </setting>
+</vbox>
diff --git a/toolkit/mozapps/extensions/test/browser/plugin_test.html b/toolkit/mozapps/extensions/test/browser/plugin_test.html
new file mode 100644
index 000000000..0709eda06
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/plugin_test.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<object id="test" width=200 height=200 type="application/x-test"></object>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/browser/redirect.sjs b/toolkit/mozapps/extensions/test/browser/redirect.sjs
new file mode 100644
index 000000000..8f9d1c08a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/redirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response) {
+ dump("*** Received redirect for " + request.queryString + "\n");
+ response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+ response.setHeader("Location", request.queryString, false);
+}
diff --git a/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml b/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml
new file mode 100644
index 000000000..63ae07901
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/releaseNotes.xhtml
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html lang="en-US" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title></title>
+</head>
+
+<body>
+ <h1>OMG, an update!!!!</h1>
+ <ul>
+ <li>Made everything more awesome</li>
+ <li>Added hot sauce</li>
+ </ul>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/mochitest/file_bug687194.xpi b/toolkit/mozapps/extensions/test/mochitest/file_bug687194.xpi
new file mode 100644
index 000000000..dfc035053
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/mochitest/file_bug687194.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/mochitest/file_empty.html b/toolkit/mozapps/extensions/test/mochitest/file_empty.html
new file mode 100644
index 000000000..b6c8a53b4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/mochitest/file_empty.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head></head><body><span id="text">Nothing to see here</span></body></html>
diff --git a/toolkit/mozapps/extensions/test/mochitest/mochitest.ini b/toolkit/mozapps/extensions/test/mochitest/mochitest.ini
new file mode 100644
index 000000000..375f619b4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/mochitest/mochitest.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+skip-if = buildapp == 'b2g'
+support-files =
+ file_empty.html
+ file_bug687194.xpi
+
+[test_bug609794.html]
+[test_bug687194.html]
+skip-if = e10s || os == "android" # this test creates its own child process, no need to run it in e10s
+[test_bug887098.html]
diff --git a/toolkit/mozapps/extensions/test/mochitest/test_bug609794.html b/toolkit/mozapps/extensions/test/mochitest/test_bug609794.html
new file mode 100644
index 000000000..d13e6ef2f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/mochitest/test_bug609794.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=609794
+-->
+<head>
+ <title>Test for Bug 609794</title>
+ <script type="application/javascript" 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=609794">Mozilla Bug 609794</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 609794 **/
+var obj = Object.create(window);
+is(Object.prototype.toString.call(obj.InstallTrigger), "[object InstallTriggerImpl]", "can get InstallTrigger through the prototype");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/mochitest/test_bug687194.html b/toolkit/mozapps/extensions/test/mochitest/test_bug687194.html
new file mode 100644
index 000000000..8f99ea73a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/mochitest/test_bug687194.html
@@ -0,0 +1,133 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for registering/unregistering chrome OOP</title>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+ <script type="application/javascript;version=1.8">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+
+ const childFrameURL =
+ "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+ function childFrameScript() {
+ "use strict";
+
+ var ios =
+ Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+ let cr =
+ Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Ci.nsIXULChromeRegistry);
+ addMessageListener("test687194:resolveChromeURI", function(message) {
+ let result;
+ let threw = false;
+ try {
+ let uri = ios.newURI(message.data.URI, null, null);
+ result = cr.convertChromeURL(uri).spec;
+ } catch (e) {
+ threw = true;
+ result = "EXCEPTION: " + e;
+ }
+
+ message.target.sendAsyncMessage("test687194:resolveChromeURI:Answer",
+ { threw: threw, result: result });
+ });
+ }
+
+ let test;
+ function* testStructure(mm) {
+ let lastResult;
+
+ mm.addMessageListener("test687194:resolveChromeURI:Answer", function(msg) {
+ test.next(msg.data);
+ });
+
+ mm.sendAsyncMessage("test687194:resolveChromeURI",
+ { URI: "chrome://bug687194/content/e10sbug.js" });
+ lastResult = yield;
+ is(lastResult.threw, true, "URI shouldn't resolve to begin with");
+
+ let { AddonManager } = SpecialPowers.Cu.import("resource://gre/modules/AddonManager.jsm", {});
+ const INSTALL_URI =
+ "http://mochi.test:8888/tests/toolkit/mozapps/extensions/test/mochitest/file_bug687194.xpi"
+ AddonManager.getInstallForURL(INSTALL_URI, (install) => {
+ install = SpecialPowers.wrap(install);
+ install.addListener(SpecialPowers.wrapCallbackObject({
+ onInstallEnded: function(install, addon) {
+ SimpleTest.executeSoon(() => test.next(addon));
+ }
+ }));
+ install.install();
+ }, "application/x-xpinstall");
+
+ let addon = SpecialPowers.wrap(yield);
+
+ mm.sendAsyncMessage("test687194:resolveChromeURI",
+ { URI: "chrome://bug687194/content/e10sbug.js" });
+ lastResult = yield;
+ is(lastResult.threw, false, "able to resolve after the installation");
+
+ let listener = SpecialPowers.wrapCallbackObject({
+ onUninstalled: function(removedAddon) {
+ if (removedAddon.id === addon.id) {
+ AddonManager.removeAddonListener(listener);
+ SimpleTest.executeSoon(() => test.next());
+ }
+ }
+ });
+ AddonManager.addAddonListener(listener);
+ addon.uninstall();
+
+ yield;
+
+ mm.sendAsyncMessage("test687194:resolveChromeURI",
+ { URI: "chrome://bug687194/content/e10sbug.js" });
+ lastResult = yield;
+ is(lastResult.threw, true, "should have unregistered the URI");
+ SimpleTest.finish();
+ }
+
+ function runTests() {
+ info("Browser prefs set.");
+
+ let iframe = document.createElement("iframe");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ iframe.id = "iframe";
+ iframe.src = childFrameURL;
+
+ iframe.addEventListener("mozbrowserloadend", function() {
+ info("Got iframe load event.");
+ let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+ mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+ false);
+
+ test = testStructure(mm);
+ test.next();
+ });
+
+ document.body.appendChild(iframe);
+ }
+
+ addEventListener("load", function() {
+ info("Got load event.");
+
+ SpecialPowers.addPermission("browser", true, document);
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.ipc.browser_frames.oop_by_default", true],
+ ["dom.mozBrowserFramesEnabled", true],
+ ["browser.pagethumbnails.capturing_disabled", true]
+ ]
+ }, runTests);
+ });
+ </script>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/mochitest/test_bug887098.html b/toolkit/mozapps/extensions/test/mochitest/test_bug887098.html
new file mode 100644
index 000000000..2b2033a37
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/mochitest/test_bug887098.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=887098
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 887098</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 887098 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function loaded() {
+ var iwin = $('ifr').contentWindow;
+ var href = SpecialPowers.wrap(iwin).location.href;
+ if (/file_empty/.test(href)) {
+ window.evalRef = iwin.eval;
+ window.installTriggerRef = iwin.InstallTrigger; // Force lazy instantiation.
+ // about: is privileged, so we need to be privileged to load it.
+ SpecialPowers.wrap(iwin).location.href = 'about:';
+ } else {
+ is(href, 'about:', "Successfully navigated to about:");
+ try {
+ evalRef('InstallTrigger.install({URL: "chrome://global/skin/global.css"});');
+ ok(false, "Should have thrown when trying to install restricted URI from InstallTrigger");
+ } catch (e) {
+ //XXXgijs this test broke because of the switch to webidl. I'm told
+ // it has to do with compartments and the fact that we eval in "about:".
+ // Tracking in bug 1007671
+ todo(/permission/.test(e), "We should throw a security exception. Got: " + e);
+ }
+ SimpleTest.finish();
+ }
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887098">Mozilla Bug 887098</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe onload="loaded();" id="ifr" src="file_empty.html"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/moz.build b/toolkit/mozapps/extensions/test/moz.build
new file mode 100644
index 000000000..d74976bda
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/moz.build
@@ -0,0 +1,20 @@
+# -*- 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/.
+
+if CONFIG['MOZ_BUILD_APP'] != 'mobile':
+ DIRS += ['browser']
+
+ BROWSER_CHROME_MANIFESTS += ['xpinstall/browser.ini']
+ MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
+
+TESTING_JS_MODULES += [
+ 'AddonManagerTesting.jsm',
+]
+
+XPCSHELL_TESTS_MANIFESTS += [
+ 'xpcshell/xpcshell-unpack.ini',
+ 'xpcshell/xpcshell.ini',
+]
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_change.xml b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_change.xml
new file mode 100644
index 000000000..a229a653a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_change.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="3"/>
+ </emItem>
+ <!-- Two RegExp matches, so test flags work - first shouldn't match. -->
+ <emItem id="/^RegExp/">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="/^RegExp/i">
+ <versionRange severity="2" minVersion="2" maxVersion="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update1.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update1.rdf
new file mode 100644
index 000000000..588290968
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update1.rdf
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:softblock1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft1_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft2_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft3_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft4_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:theme:softblock5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft5_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:hardblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_hard1_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:regexpblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_regexp1_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update2.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update2.rdf
new file mode 100644
index 000000000..5c3747f5f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update2.rdf
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:softblock1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft1_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft2_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft3_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft4_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:theme:softblock5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft5_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:hardblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_hard1_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:regexpblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_regexp1_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update3.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update3.rdf
new file mode 100644
index 000000000..d60708414
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/addon_update3.rdf
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:softblock1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft1_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft2_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft3_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft4_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:theme:softblock5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft5_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:hardblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_hard1_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:regexpblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_regexp1_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/app_update.xml b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/app_update.xml
new file mode 100644
index 000000000..85a66fe55
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/app_update.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/^RegExp/">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/^RegExp/i">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml
new file mode 100644
index 000000000..87011cd39
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist"/>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml
new file mode 100644
index 000000000..867a34255
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org"/>
+ <emItem id="/^RegExp/">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="/^RegExp/i"/>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/manual_update.xml b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/manual_update.xml
new file mode 100644
index 000000000..df9276525
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/blocklistchange/manual_update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="/^RegExp/i">
+ <versionRange minVersion="1" maxVersion="2"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_block.xml b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_block.xml
new file mode 100644
index 000000000..1f673ef2f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_block.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug455906_1@tests.mozilla.org" blockID="test_bug455906_1@tests.mozilla.org"/>
+ <emItem id="test_bug455906_2@tests.mozilla.org" blockID="test_bug455906_2@tests.mozilla.org"/>
+ <emItem id="test_bug455906_3@tests.mozilla.org" blockID="test_bug455906_3@tests.mozilla.org"/>
+ <emItem id="test_bug455906_4@tests.mozilla.org" blockID="test_bug455906_4@tests.mozilla.org"/>
+ <emItem id="test_bug455906_5@tests.mozilla.org" blockID="test_bug455906_5@tests.mozilla.org"/>
+ <emItem id="test_bug455906_6@tests.mozilla.org" blockID="test_bug455906_6@tests.mozilla.org"/>
+ <emItem id="test_bug455906_7@tests.mozilla.org" blockID="test_bug455906_7@tests.mozilla.org"/>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="test_bug455906_plugin">
+ <match name="name" exp="^test_bug455906"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_empty.xml b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_empty.xml
new file mode 100644
index 000000000..88d22f281
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_empty.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="dummy_bug455906_2@tests.mozilla.org"/>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_start.xml b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_start.xml
new file mode 100644
index 000000000..daba6f4c1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_start.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug455906_4@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_5@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="test_bug455906_6@tests.mozilla.org">
+ <versionRange severity="2"/>
+ </emItem>
+ <emItem id="dummy_bug455906_1@tests.mozilla.org"/>
+ </emItems>
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906_4$"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906_5$"/>
+ <versionRange severity="1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906_6$"/>
+ <versionRange severity="2"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_warn.xml b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_warn.xml
new file mode 100644
index 000000000..232fd0d07
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/bug455906_warn.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug455906_1@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_2@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_3@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_4@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_5@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_6@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_7@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906"/>
+ <versionRange severity="-1"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/corrupt.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/corrupt.xpi
new file mode 100644
index 000000000..35d7bd5e5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/corrupt.xpi
@@ -0,0 +1 @@
+This is a corrupt zip file
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/corruptfile.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/corruptfile.xpi
new file mode 100644
index 000000000..0c30989aa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/corruptfile.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/empty.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/empty.xpi
new file mode 100644
index 000000000..74ed2b817
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/empty.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/pluginInfoURL_block.xml b/toolkit/mozapps/extensions/test/xpcshell/data/pluginInfoURL_block.xml
new file mode 100644
index 000000000..6c6ce90ef
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/pluginInfoURL_block.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="test_plugin_wInfoURL">
+ <match name="name" exp="^test_with_infoURL"/>
+ <match name="version" exp="^5"/>
+ <infoURL>http://test.url.com/</infoURL>
+ </pluginItem>
+ <pluginItem blockID="test_plugin_wAltInfoURL">
+ <match name="name" exp="^test_with_altInfoURL"/>
+ <match name="version" exp="^5"/>
+ <infoURL>http://alt.test.url.com/</infoURL>
+ </pluginItem>
+ <pluginItem blockID="test_plugin_noInfoURL">
+ <match name="name" exp="^test_no_infoURL"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository.xml
new file mode 100644
index 000000000..0bebca2c1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository.xml
@@ -0,0 +1,820 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1111">
+ <!-- Passes all requirements -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <status id="8">Preliminarily Reviewed</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that a negative rating is ignored -->
+ <rating>-2</rating>
+ <!-- Test that a <reviews> with a blank review URL is ignored -->
+ <reviews num=" 1111 "> </reviews>
+ <!-- Test that a negative total_downloads is ignored -->
+ <total_downloads>-2</total_downloads>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+
+ <!-- Passes requirements. Tests optional attributes. Also tests that
+ integer properties that are NaN in the XML are ignored -->
+ <addon>
+ <name>PASS</name>
+ <!-- Test that extensions pass -->
+ <type id="1">Extension</type>
+ <guid>test2@tests.mozilla.org</guid>
+ <version>1.2</version>
+ <authors>
+ <!-- Test that the first author becomes the creator,
+ and the second one is a developer -->
+ <author>
+ <name>Test Creator 2</name>
+ <link>http://localhost:%PORT%/creator2.html</link>
+ </author>
+ <author>
+ <name>Test Developer 2</name>
+ <link>http://localhost:%PORT%/developer2.html</link>
+ </author>
+ </authors>
+ <summary>&lt;h1&gt;Test Summary 2&lt;/h1&gt;&lt;p&gt;paragraph&lt;/p&gt;</summary>
+ <description>Test Description 2&lt;br&gt;newline</description>
+ <developer_comments>Test Developer
+ Comments 2</developer_comments>
+ <eula>Test EULA 2</eula>
+ <icon size="64">http://localhost:%PORT%/icon2-64.png</icon>
+ <icon size="48">http://localhost:%PORT%/icon2-48.png</icon>
+ <icon size="32">http://localhost:%PORT%/icon2-32.png</icon>
+ <status id="4">Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that multiple preview images are correctly parsed -->
+ <previews>
+ <preview primary="0">
+ <full type="image/png">http://localhost:%PORT%/full1-2.png</full>
+ <thumbnail type="image/png">http://localhost:%PORT%/thumbnail1-2.png</thumbnail>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:%PORT%/full2-2.png</full>
+ <thumbnail type="image/png">http://localhost:%PORT%/thumbnail2-2.png</thumbnail>
+ <caption>Caption 2</caption>
+ </preview>
+ </previews>
+ <rating>NaN</rating>
+ <!-- Test that learnmore is used as the add-on's homepageURL
+ if there is no homepage defined -->
+ <learnmore>http://localhost:%PORT%/learnmore2.html</learnmore>
+ <homepage/>
+ <support>http://localhost:%PORT%/support2.html</support>
+ <contribution_data>
+ <link>http://localhost:%PORT%/contribution2.html</link>
+ <meet_developers>http://localhost:%PORT%/meetDevelopers2.html</meet_developers>
+ </contribution_data>
+ <reviews num="NaN">http://localhost:%PORT%/review2.html</reviews>
+ <total_downloads>NaN</total_downloads>
+ <weekly_downloads>NaN</weekly_downloads>
+ <daily_users>NaN</daily_users>
+ <last_updated epoch="NaN">Not an acual date</last_updated>
+ <install size="NaN" os="ALL">http://localhost:%PORT%/test2.xpi</install>
+ </addon>
+
+ <!-- Passes requirements. Tests optional attributes with extra whitespace. -->
+ <addon>
+ <name> PASS </name>
+ <!-- Test that themes pass -->
+ <type id=" 2 ">Theme</type>
+ <guid> test3@tests.mozilla.org </guid>
+ <version> 1.3 </version>
+ <authors>
+ <!-- Test that authors with blank names are ignored -->
+ <author>
+ <name> </name>
+ <link> http://localhost:%PORT%/ignore3.html </link>
+ </author>
+ <!-- Test that authors with blank links are ignored -->
+ <author>
+ <name> Test Creator Ignore </name>
+ <link> </link>
+ </author>
+ <author>
+ <name> Test Creator 3 </name>
+ <link> http://localhost:%PORT%/creator3.html </link>
+ </author>
+ <author>
+ <name> First Test Developer 3 </name>
+ <link> http://localhost:%PORT%/developer1-3.html </link>
+ </author>
+ <author>
+ <name> </name>
+ <link> </link>
+ </author>
+ <author>
+ <name> Second Test Developer 3 </name>
+ <link> http://localhost:%PORT%/developer2-3.html </link>
+ </author>
+ </authors>
+ <summary> Test Summary 3 </summary>
+ <description> Test Description 3&lt;br&gt;&lt;ul&gt;&lt;li&gt;List item 1&lt;li&gt;List item 2&lt;/ul&gt; </description>
+ <developer_comments> Test Developer Comments 3 </developer_comments>
+ <eula> Test EULA 3 </eula>
+ <icon size="32"> http://localhost:%PORT%/icon3.png </icon>
+ <status id=" 8 ">Preliminarily Reviewed</status>
+ <!-- Test that an incompatible + compatible application list passes -->
+ <compatible_applications>
+ <application>
+ <appID> unknown@tests.mozilla.org </appID>
+ <min_version> 1 </min_version>
+ <max_version> 1 </max_version>
+ </application>
+ <application>
+ <appID> xpcshell@tests.mozilla.org </appID>
+ <min_version> 1 </min_version>
+ <max_version> 1 </max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that primary images appear first in the add-on's screenshots array -->
+ <previews>
+ <preview primary=" 0 ">
+ <full type=" image/png "> http://localhost:%PORT%/full2-3.png </full>
+ <caption> Caption 2 - 3 </caption>
+ </preview>
+ <!-- Test that a preview without a <full> element is ignored -->
+ <preview primary=" 0 ">
+ <caption> Caption ignore - 3 </caption>
+ </preview>
+ <!-- Test that a preview with an empty <full> element is ignored -->
+ <preview primary=" 0 ">
+ <full type=" image/png "> </full>
+ <caption> Caption ignore - 3 </caption>
+ <preview primary=" 1 ">
+ <full type=" image/png "> http://localhost:%PORT%/full1-3.png </full>
+ <thumbnail type=" image/png "> http://localhost:%PORT%/thumbnail1-3.png </thumbnail>
+ <caption> Caption 1 - 3 </caption>
+ </preview>
+ <preview primary=" 0 ">
+ <full type=" image/png "> http://localhost:%PORT%/full3-3.png </full>
+ <thumbnail type=" image/png "> http://localhost:%PORT%/thumbnail3-3.png </thumbnail>
+ <caption> Caption 3 - 3 </caption>
+ </preview>
+ </preview>
+ </previews>
+ <!-- Test that a rating between 1 and 5 is correctly parsed -->
+ <rating> 2 </rating>
+ <!-- Test that hompage is used as the add-on's homepageURL
+ even if learnmore is defined -->
+ <learnmore> http://localhost:%PORT%/learnmore3.html </learnmore>
+ <homepage> http://localhost:%PORT%/homepage3.html </homepage>
+ <support> http://localhost:%PORT%/support3.html </support>
+ <contribution_data>
+ <link> http://localhost:%PORT%/contribution3.html </link>
+ <suggested_amount currency="USD"> $11.11 </suggested_amount>
+ <meet_developers> http://localhost:%PORT%/meetDevelopers3.html </meet_developers>
+ </contribution_data>
+ <reviews num=" 1111 "> http://localhost:%PORT%/review3.html </reviews>
+ <total_downloads> 2222 </total_downloads>
+ <weekly_downloads> 3333 </weekly_downloads>
+ <daily_users> 4444 </daily_users>
+ <last_updated epoch=" 1265033045 "> 2010-02-01T14:04:05Z </last_updated>
+ <!-- Test that an incompatible install is ignored -->
+ <install size=" 9999 " os=" UNKNOWN "> http://localhost:%PORT%/fail3.xpi </install>
+ <!-- Test that OS matching is case-insensitive -->
+ <install size=" 5555 " os=" xpCShell " hash=" sha1:c26f0b0d62e5dcddcda95074d3f3fedb9bbc26e3 "> http://localhost:%PORT%/test3.xpi </install>
+ </addon>
+
+ <!-- Fails because name is undefined -->
+ <addon>
+ <type id="1">Extension</type>
+ <guid>test4@tests.mozilla.org</guid>
+ <version>1.4</version>
+ <authors><author><name>Test Creator 4</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined name should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test4.xpi</install>
+ </addon>
+
+ <!-- Fails because name is empty-->
+ <addon>
+ <name> </name>
+ <type id="1">Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.5</version>
+ <authors><author><name>Test Creator 5</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty name should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test5.xpi</install>
+ </addon>
+
+ <!-- Fails because type is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <guid>test6@tests.mozilla.org</guid>
+ <version>1.6</version>
+ <authors><author><name>Test Creator 6</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined type should be ignored</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test6.xpi</install>
+ </addon>
+
+ <!-- Fails because type is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="">Empty id attribute</type>
+ <guid>test7@tests.mozilla.org</guid>
+ <version>1.7</version>
+ <authors><author><name>Test Creator 7</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty type should be ignored</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test7.xpi</install>
+ </addon>
+
+ <!-- Fails because type is unknown -->
+ <addon>
+ <name>FAIL</name>
+ <type id="9999">Unknown</type>
+ <guid>test8@tests.mozilla.org</guid>
+ <version>1.8</version>
+ <authors><author><name>Test Creator 8</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with unknown type should be ignored</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test8.xpi</install>
+ </addon>
+
+ <!-- Fails because guid is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <version>1.9</version>
+ <authors><author><name>Test Creator 9</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined guid should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test9.xpi</install>
+ </addon>
+
+ <!-- Fails because guid is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid> </guid>
+ <version>1.10</version>
+ <authors><author><name>Test Creator 10</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty guid should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test10.xpi</install>
+ </addon>
+
+ <!-- Fails because guid matches previously successful result -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.11</version>
+ <authors><author><name>Test Creator 11</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that matches a previously successful result should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test11.xpi</install>
+ </addon>
+
+ <!-- Fails because guid matches already installed add-on -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <version>1.12</version>
+ <authors><author><name>Test Creator 12</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that matches an installed Addon should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test12.xpi</install>
+ </addon>
+
+ <!-- Fails because version is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test13@tests.mozilla.org</guid>
+ <authors><author><name>Test Creator 13</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test13.xpi</install>
+ </addon>
+
+ <!-- Fails because version is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test14@tests.mozilla.org</guid>
+ <version> </version>
+ <authors><author><name>Test Creator 14</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test14.xpi</install>
+ </addon>
+
+ <!-- Fails because authors undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test15@tests.mozilla.org</guid>
+ <version>1.15</version>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined authors should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test15.xpi</install>
+ </addon>
+
+ <!-- Fails because it has no defined author elements -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test16@tests.mozilla.org</guid>
+ <version>1.16</version>
+ <authors></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no defined author elements should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test16.xpi</install>
+ </addon>
+
+ <!-- Fails because no non-empty author elements -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test17@tests.mozilla.org</guid>
+ <version>1.17</version>
+ <authors>
+ <author><name></name></author>
+ <author><name></name> </author>
+ </authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no non-empty author elements should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test17.xpi</install>
+ </addon>
+
+ <!-- Fails because status is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test18@tests.mozilla.org</guid>
+ <version>1.18</version>
+ <authors><author><name>Test Creator 18</name></author></authors>
+ <summary>Add-on with undefined status should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test18.xpi</install>
+ </addon>
+
+ <!-- Fails because status is not Public -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test19@tests.mozilla.org</guid>
+ <version>1.19</version>
+ <authors><author><name>Test Creator 19</name></author></authors>
+ <status id="9999">Unknown</status>
+ <summary>Add-on with non-Public status should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test19.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible_applications is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test20@tests.mozilla.org</guid>
+ <version>1.20</version>
+ <authors><author><name>Test Creator 20</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined compatible_applications should be ignored.</summary>
+ <install>http://localhost:%PORT%/test20.xpi</install>
+ </addon>
+
+ <!-- Fails because no compatible applications matched -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test21@tests.mozilla.org</guid>
+ <version>1.21</version>
+ <authors><author><name>Test Creator 21</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no compatible applications should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>unknown@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test21.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's min version is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test22@tests.mozilla.org</guid>
+ <version>1.22</version>
+ <authors><author><name>Test Creator 22</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too high of a compatible application min version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <max_version>2.0</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test22.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's min version too high -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test23@tests.mozilla.org</guid>
+ <version>1.23</version>
+ <authors><author><name>Test Creator 23</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too high of a compatible application min version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1.1</min_version>
+ <max_version>2.0</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test23.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's max version is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test24@tests.mozilla.org</guid>
+ <version>1.24</version>
+ <authors><author><name>Test Creator 24</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too low of a compatible application max version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.9</min_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test24.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's max version is too low -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test25@tests.mozilla.org</guid>
+ <version>1.25</version>
+ <authors><author><name>Test Creator 25</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too low of a compatible application max version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.9</min_version>
+ <max_version>0.9.9</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test25.xpi</install>
+ </addon>
+
+ <!-- Fails because XPI URL is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test26@tests.mozilla.org</guid>
+ <version>1.26</version>
+ <authors><author><name>Test Creator 26</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined XPI URL should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ </addon>
+
+ <!-- Fails because XPI URL is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test27@tests.mozilla.org</guid>
+ <version>1.27</version>
+ <authors><author><name>Test Creator 27</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with an empty XPI URL should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install> </install>
+ </addon>
+
+ <!-- Fails because install not compatible with OS -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test28@tests.mozilla.org</guid>
+ <version>1.28</version>
+ <authors><author><name>Test Creator 28</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no installs with compatible OS should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install os="UNKNOWN1">http://localhost:%PORT%/test28.xpi</install>
+ <install os="UNKNOWN2">http://localhost:%PORT%/test28.xpi</install>
+ </addon>
+
+ <!-- Fails because XPI URL matches an installing AddonInstall -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test29@tests.mozilla.org</guid>
+ <version>1.29</version>
+ <authors><author><name>Test Creator 29</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with an XPI URL that matches an installing AddonInstall should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/addons/test_AddonRepository_2.xpi</install>
+ </addon>
+
+ <!-- Passes because the add-on has the right payment info -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>purchase1@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <rating>5</rating>
+ <payment_data>
+ <link>http://localhost:%PORT%/purchaseURL1</link>
+ <amount amount="5">$5</amount>
+ </payment_data>
+ </addon>
+
+ <!-- Passes because the add-on has the right payment info -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>purchase2@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <all_compatible_os>
+ <os>XPCShell</os>
+ </all_compatible_os>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <rating>5</rating>
+ <payment_data>
+ <link>http://localhost:%PORT%/purchaseURL2</link>
+ <amount amount="10.0">$10</amount>
+ </payment_data>
+ </addon>
+
+ <!-- Fails because the add-on doesn't match the platform -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>purchase3@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <all_compatible_os>
+ <os>FOO</os>
+ </all_compatible_os>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <rating>5</rating>
+ <payment_data>
+ <link>http://localhost:%PORT%/purchaseURL3</link>
+ <amount amount="10">$10</amount>
+ </payment_data>
+ </addon>
+
+ <!-- Passes because the Addon that has a matching XPI URL
+ has a state = STATE_AVAILABLE (non-active install). This is the
+ last passing add-on. -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test-lastPassing@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that a rating > 5 becomes a rating = 5 -->
+ <rating>10</rating>
+ <install>http://localhost:%PORT%/addons/test_AddonRepository_3.xpi</install>
+ </addon>
+
+ <!-- Fails because of MAX_RESULTS limit. The previous <addon> should
+ be the last passing add-on in order to correctly test the limit. -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test-surpassesLimit@tests.mozilla.org</guid>
+ <version>9.9</version>
+ <authors><author><name>Test Creator - Surpasses Limit</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on should not be added because doing so would surpass MAX_RESULTS limit</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test-surpassesLimit.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_cache.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_cache.xml
new file mode 100644
index 000000000..f707f1217
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_cache.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1111">
+ <addon>
+ <name>Repo Add-on 1</name>
+ <type id="1">Extension</type>
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <version>2.1</version>
+ <authors>
+ <author>
+ <name>Repo Add-on 1 - Creator</name>
+ <link>http://localhost:4444/repo/1/creator.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 1 - First Developer</name>
+ <link>http://localhost:4444/repo/1/firstDeveloper.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 1 - Second Developer</name>
+ <link>http://localhost:4444/repo/1/secondDeveloper.html</link>
+ </author>
+ </authors>
+ <summary>Repo Add-on 1 - Description&lt;br&gt;Second line</summary>
+ <description>&lt;p&gt;Repo Add-on 1 - Full Description &amp;amp; some extra&lt;/p&gt;</description>
+ <eula>Repo Add-on 1 - EULA</eula>
+ <developer_comments>Repo Add-on 1
+ Developer Comments</developer_comments>
+ <icon size="32">http://localhost/repo/1/icon.png</icon>
+ <status id="4">Public</status>
+ <rating>1</rating>
+ <learnmore>http://localhost/repo/1/learnmore.html</learnmore>
+ <homepage>http://localhost/repo/1/homepage.html</homepage>
+ <support>http://localhost/repo/1/support.html</support>
+ <contribution_data>
+ <link>http://localhost/repo/1/contribution.html</link>
+ <suggested_amount currency="USD">$11.11</suggested_amount>
+ <meet_developers>http://localhost/repo/1/meetDevelopers.html</meet_developers>
+ </contribution_data>
+ <reviews num="1111">http://localhost/repo/1/review.html</reviews>
+ <total_downloads>2221</total_downloads>
+ <weekly_downloads>3331</weekly_downloads>
+ <daily_users>4441</daily_users>
+ <last_updated epoch="9">1970-01-01T00:00:09Z</last_updated>
+ <install size="9">http://localhost:4444/repo/1/install.xpi</install>
+ </addon>
+
+ <addon>
+ <name>Repo Add-on 2</name>
+ <type id="2">Theme</type>
+ <guid>test_AddonRepository_2@tests.mozilla.org</guid>
+ <version>2.2</version>
+ <authors>
+ <author>
+ <name>Repo Add-on 2 - Creator</name>
+ <link>http://localhost:4444/repo/2/creator.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 2 - First Developer</name>
+ <link>http://localhost:4444/repo/2/firstDeveloper.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 2 - Second Developer</name>
+ <link>http://localhost:4444/repo/2/secondDeveloper.html</link>
+ </author>
+ </authors>
+ <summary>Repo Add-on 2 - Description</summary>
+ <description>Repo Add-on 2 - Full Description</description>
+ <eula>Repo Add-on 2 - EULA</eula>
+ <developer_comments>Repo Add-on 2 - Developer Comments</developer_comments>
+ <icon size="32">http://localhost/repo/2/icon.png</icon>
+ <status id="9">Unknown</status>
+ <previews>
+ <preview primary="1">
+ <full type="image/png">http://localhost:4444/repo/2/firstFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/2/firstThumbnail.png</thumbnail>
+ <caption>Repo Add-on 2 - First Caption</caption>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:4444/repo/2/secondFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/2/secondThumbnail.png</thumbnail>
+ <caption>Repo Add-on 2 - Second Caption</caption>
+ </preview>
+ </previews>
+ <rating>2</rating>
+ <learnmore>http://localhost/repo/2/learnmore.html</learnmore>
+ <homepage>http://localhost/repo/2/homepage.html</homepage>
+ <support>http://localhost/repo/2/support.html</support>
+ <contribution_data>
+ <link>http://localhost/repo/2/contribution.html</link>
+ <meet_developers>http://localhost/repo/2/meetDevelopers.html</meet_developers>
+ </contribution_data>
+ <reviews num="1112">http://localhost/repo/2/review.html</reviews>
+ <total_downloads>2222</total_downloads>
+ <weekly_downloads>3332</weekly_downloads>
+ <daily_users>4442</daily_users>
+ <last_updated epoch="9">1970-01-01T00:00:09Z</last_updated>
+ <install size="9">http://localhost:4444/repo/2/install.xpi</install>
+ </addon>
+
+ <addon>
+ <name>Repo Add-on 3</name>
+ <type id="2">Theme</type>
+ <guid>test_AddonRepository_3@tests.mozilla.org</guid>
+ <version>2.3</version>
+ <icon size="32">http://localhost/repo/3/icon.png</icon>
+ <previews>
+ <preview primary="1">
+ <full type="image/png">http://localhost:4444/repo/3/firstFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/3/firstThumbnail.png</thumbnail>
+ <caption>Repo Add-on 3 - First Caption</caption>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:4444/repo/3/secondFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/3/secondThumbnail.png</thumbnail>
+ <caption>Repo Add-on 3 - Second Caption</caption>
+ </preview>
+ </previews>
+ </addon>
+
+ <addon_compatibility hosted="true" id="123">
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <name>PASS</name>
+ <version_ranges>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.1</min_version>
+ <max_version>0.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>3.0</min_version>
+ <max_version>4.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>5.0</min_version>
+ <max_version>6.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - invalid type attribute -->
+ <version_range type="unknown">
+ <min_version>9</min_version>
+ <max_version>10</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>10.0</min_version>
+ <max_version>11.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - no matching appID -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>Unknown App</name>
+ <application_id>123</application_id>
+ <min_version>1.0</min_version>
+ <max_version>999.0</max_version>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml
new file mode 100644
index 000000000..003095727
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="123">
+ <addon>
+ <name>Test Repo Add-on - ignore</name>
+ <type id="1">Extension</type>
+ <guid>compatmode-ignore@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml
new file mode 100644
index 000000000..fec8b09ca
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="123">
+ <addon>
+ <name>Test Repo Add-on - normal</name>
+ <type id="1">Extension</type>
+ <guid>compatmode-normal@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml
new file mode 100644
index 000000000..f99256b87
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="123">
+ <addon>
+ <name>Test Repo Add-on - strict</name>
+ <type id="1">Extension</type>
+ <guid>compatmode-strict@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_empty.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_empty.xml
new file mode 100644
index 000000000..4cd5c1443
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_empty.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9999" />
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_failed.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_failed.xml
new file mode 100644
index 000000000..d02fa0249
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_failed.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9999">
+<!-- Cause XML parse error so that the search fails -->
+<!-- <addon> -->
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors><author><name>Test Creator 1</name></author></authors>
+ <status id="4">Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml
new file mode 100644
index 000000000..8a6167969
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1111">
+ <!-- Passes even though XPI URL matches an installing AddonInstall.
+ Tests optional attributes. -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ <author>
+ <name>Test Developer 1</name>
+ <link>http://localhost:%PORT%/developer1.html</link>
+ </author>
+ </authors>
+ <summary>Test Summary 1</summary>
+ <description>Test Description 1</description>
+ <eula>Test EULA 1</eula>
+ <developer_comments>Test Developer Comments 1</developer_comments>
+ <icon size="32">http://localhost:%PORT%/icon1.png</icon>
+ <status id="8">Preliminarily Reviewed</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <previews>
+ <preview primary="1">
+ <full type="image/png" width="400" height="300">
+ http://localhost:%PORT%/full1-1.png
+ </full>
+ <thumbnail type="image/png" width="200" height="150">
+ http://localhost:%PORT%/thumbnail1-1.png
+ </thumbnail>
+ <caption>Caption 1 - 1</caption>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:%PORT%/full2-1.png</full>
+ <thumbnail type="image/png">http://localhost:%PORT%/thumbnail2-1.png</thumbnail>
+ <caption>Caption 2 - 1</caption>
+ </preview>
+ </previews>
+ <rating>4</rating>
+ <learnmore>http://localhost:%PORT%/learnmore1.html</learnmore>
+ <support>http://localhost:%PORT%/support1.html</support>
+ <contribution_data>
+ <link>http://localhost:%PORT%/contribution1.html</link>
+ <suggested_amount currency="USD">$11.11</suggested_amount>
+ <meet_developers>http://localhost:%PORT%/meetDevelopers1.html</meet_developers>
+ </contribution_data>
+ <reviews num="1111">http://localhost:%PORT%/review1.html</reviews>
+ <total_downloads>2222</total_downloads>
+ <weekly_downloads>3333</weekly_downloads>
+ <daily_users>4444</daily_users>
+ <last_updated epoch="1265033045">2010-02-01T14:04:05Z</last_updated>
+ <install size="5555">http://localhost:%PORT%/addons/test_AddonRepository_2.xpi</install>
+ </addon>
+
+ <addon_compatibility hosted="true" id="123">
+ <guid>test1@tests.mozilla.org</guid>
+ <name>PASS</name>
+ <version_ranges>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.1</min_version>
+ <max_version>0.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>3.0</min_version>
+ <max_version>4.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>5.0</min_version>
+ <max_version>6.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - invalid type attribute -->
+ <version_range type="unknown">
+ <min_version>9</min_version>
+ <max_version>10</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>10.0</min_version>
+ <max_version>11.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - no matching appID -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>Unknown App</name>
+ <application_id>123</application_id>
+ <min_version>1.0</min_version>
+ <max_version>999.0</max_version>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <!-- Fails because guid matches previously successful result -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.2</version>
+ <authors><author><name>Test Creator 2</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that matches a previously successful result should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test2.xpi</install>
+ </addon>
+
+ <!-- Fails because guid was not requested -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>notRequested@tests.mozilla.org</guid>
+ <version>1.3</version>
+ <authors><author><name>Test Creator 3</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that wasn't requested should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test3.xpi</install>
+ </addon>
+
+ <!-- Passes even though guid matches already installed add-on,
+ type is unknown, no defined author elements, status is not Public,
+ no compatible applications matched, no installs compatible with OS
+ -->
+ <addon>
+ <name>PASS</name>
+ <type id="2">Theme</type>
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <version>1.4</version>
+ <status id="9999">Unknown</status>
+ <compatible_applications>
+ <application>
+ <appID>unknown@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install os="UNKNOWN1">http://localhost:%PORT%/test4.xpi</install>
+ <install os="UNKNOWN2">http://localhost:%PORT%/test4.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.rdf
new file mode 100644
index 000000000..ab7cdef34
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.rdf
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml
new file mode 100644
index 000000000..368a6ed53
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem name="/^Mozilla Corp\.$/">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/block2/" name="/^Moz/" creator="Dangerous"
+ homepageURL="/\.dangerous\.com/" updateURL="/\.dangerous\.com/">
+ <versionRange severity="3">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_prefs_1.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_prefs_1.xml
new file mode 100644
index 000000000..41df457b0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_prefs_1.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="block1@tests.mozilla.org">
+ <prefs>
+ <pref>test.blocklist.pref1</pref>
+ <pref>test.blocklist.pref2</pref>
+ </prefs>
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="block2@tests.mozilla.org">
+ <prefs>
+ <pref>test.blocklist.pref3</pref>
+ <pref>test.blocklist.pref4</pref>
+ </prefs>
+ <versionRange severity="3">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_regexp_1.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_regexp_1.xml
new file mode 100644
index 000000000..20035c6a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_regexp_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="/block1/">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/block1/">
+ <versionRange severity="2">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716.rdf
new file mode 100644
index 000000000..d60d8ca3f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716.rdf
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE RDF:RDF [
+ <!ENTITY bug299716 "urn:mozilla:extension:bug299716">
+ <!ENTITY addons_prefix "http://localhost:4444/addons/test_bug299716">
+ <!ENTITY v0.2 "<em:version>0.2</em:version>">
+
+ <!ENTITY xpcshell.app "
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ ">
+
+ <!ENTITY toolkit.app "
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ ">
+
+ <!ENTITY invalidRange "
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ ">
+
+ <!ENTITY xpcshell.invalid "
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ &invalidRange;
+ ">
+
+ <!ENTITY toolkit.invalid "
+ <em:id>toolkit@mozilla.org</em:id>
+ &invalidRange;
+ ">
+]>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <!-- XPCShell -->
+ <RDF:Description about="&bug299716;-a@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-a@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-a@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_a_2.xpi">
+ &xpcshell.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- Toolkit -->
+ <RDF:Description about="&bug299716;-b@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-b@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-b@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_b_2.xpi">
+ &toolkit.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- XPCShell + Toolkit -->
+ <RDF:Description about="&bug299716;-c@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-c@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-c@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_c_2.xpi">
+ &xpcshell.app;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_c_2.xpi">
+ &toolkit.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- XPCShell (Toolkit invalid) -->
+ <RDF:Description about="&bug299716;-d@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-d@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-d@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_d_2.xpi">
+ &xpcshell.app;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_d_2.xpi">
+ &toolkit.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- Toolkit (XPCShell invalid), should not install -->
+ <RDF:Description about="&bug299716;-e@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-e@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-e@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_e_2.xpi">
+ &xpcshell.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_e_2.xpi">
+ &toolkit.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- None (XPCShell, Toolkit invalid), should not install -->
+ <RDF:Description about="&bug299716;-f@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-f@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-f@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_f_2.xpi">
+ &xpcshell.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_f_2.xpi">
+ &toolkit.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- Toolkit (invalid), should not install -->
+ <RDF:Description about="&bug299716;-g@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-g@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-g@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_g_2.xpi">
+ &toolkit.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716_2.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716_2.rdf
new file mode 100644
index 000000000..94a4ea450
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug299716_2.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:bug299716-2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>0.1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>2.0.*</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug324121.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug324121.rdf
new file mode 100644
index 000000000..2c453f756
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug324121.rdf
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id incompatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_6@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- toolkit id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_7@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- toolkit id incompatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug393285.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug393285.xml
new file mode 100644
index 000000000..1767b4332
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug393285.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug393285_2@tests.mozilla.org"/>
+ <emItem id="test_bug393285_3a@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0"/>
+ </emItem>
+ <emItem id="test_bug393285_3b@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0"/>
+ </emItem>
+ <emItem id="test_bug393285_4@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug393285_5@tests.mozilla.org" os="Darwin"/>
+ <emItem id="test_bug393285_6@tests.mozilla.org" os="XPCShell"/>
+ <emItem id="test_bug393285_7@tests.mozilla.org" os="Darwin,XPCShell,WINNT"/>
+ <emItem id="test_bug393285_8@tests.mozilla.org" xpcomabi="x86-msvc"/>
+ <emItem id="test_bug393285_9@tests.mozilla.org" xpcomabi="noarch-spidermonkey"/>
+ <emItem id="test_bug393285_10@tests.mozilla.org" xpcomabi="ppc-gcc3,noarch-spidermonkey,x86-msvc"/>
+ <emItem id="test_bug393285_11@tests.mozilla.org" os="Darwin" xpcomabi="ppc-gcc3,x86-msvc"/>
+ <emItem id="test_bug393285_12@tests.mozilla.org" os="Darwin" xpcomabi="ppc-gcc3,noarch-spidermonkey,x86-msvc"/>
+ <emItem id="test_bug393285_13@tests.mozilla.org" os="XPCShell" xpcomabi="ppc-gcc3,x86-msvc"/>
+ <emItem id="test_bug393285_14@tests.mozilla.org" os="XPCShell,WINNT" xpcomabi="ppc-gcc3,x86-msvc,noarch-spidermonkey"/>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug394300.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug394300.rdf
new file mode 100644
index 000000000..94e12527f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug394300.rdf
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:bug394300_1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>20</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install should be the version detected -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>10</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install. Detecting this would indicate that the order
+ of entries is playing a part in the update detection. -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>6</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - no minVersion or maxVersion specified -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>40</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>30</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug394300_2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>20</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install should be the version detected -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>10</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install. Detecting this would indicate that the order
+ of entries is playing a part in the update detection. -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>6</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - no minVersion or maxVersion specified -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>40</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>30</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug424262.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug424262.xml
new file mode 100644
index 000000000..d797debbb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug424262.xml
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>TEST</name>
+ <type id='1'>Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>-5</rating>
+ <type id='1'>Extension</type>
+ <guid>test2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>0</rating>
+ <type id='1'>Extension</type>
+ <guid>test3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>2</rating>
+ <type id='1'>Extension</type>
+ <guid>test4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>4</rating>
+ <type id='1'>Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>5</rating>
+ <type id='1'>Extension</type>
+ <guid>test6@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>10</rating>
+ <type id='1'>Extension</type>
+ <guid>test7@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>100</rating>
+ <type id='1'>Extension</type>
+ <guid>test8@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_app.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_app.xml
new file mode 100644
index 000000000..f12ca1fa6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_app.xml
@@ -0,0 +1,333 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <!-- All extensions are version 5 and tests run against appVersion 3 -->
+
+ <!-- Test 1 not listed, should never get blocked -->
+ <!-- Always blocked -->
+ <emItem id="test_bug449027_2@tests.mozilla.org"/>
+ <!-- Always blocked -->
+ <emItem id="test_bug449027_3@tests.mozilla.org">
+ <versionRange/>
+ </emItem>
+ <!-- Not blocked since neither version range matches -->
+ <emItem id="test_bug449027_4@tests.mozilla.org">
+ <versionRange minVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </emItem>
+ <!-- Invalid version range, should not block -->
+ <emItem id="test_bug449027_5@tests.mozilla.org">
+ <versionRange minVersion="6" maxVersion="4"/>
+ </emItem>
+ <!-- Should block all of these -->
+ <emItem id="test_bug449027_6@tests.mozilla.org">
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </emItem>
+ <emItem id="test_bug449027_7@tests.mozilla.org">
+ <versionRange maxVersion="4"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ <versionRange minVersion="6" maxVersion="7"/>
+ </emItem>
+ <emItem id="test_bug449027_8@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2"/>
+ <versionRange minVersion="4" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ </emItem>
+ <emItem id="test_bug449027_9@tests.mozilla.org">
+ <versionRange minVersion="4"/>
+ </emItem>
+ <emItem id="test_bug449027_10@tests.mozilla.org">
+ <versionRange minVersion="5"/>
+ </emItem>
+ <emItem id="test_bug449027_11@tests.mozilla.org">
+ <versionRange maxVersion="6"/>
+ </emItem>
+ <emItem id="test_bug449027_12@tests.mozilla.org">
+ <versionRange maxVersion="5"/>
+ </emItem>
+
+ <!-- This should block all versions for any application -->
+ <emItem id="test_bug449027_13@tests.mozilla.org">
+ <versionRange>
+ <targetApplication/>
+ </versionRange>
+ </emItem>
+ <!-- Shouldn't block -->
+ <emItem id="test_bug449027_14@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <!-- Should block for any version of the app -->
+ <emItem id="test_bug449027_15@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org"/>
+ </versionRange>
+ </emItem>
+ <!-- Should still block -->
+ <emItem id="test_bug449027_16@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Not blocked since neither version range matches -->
+ <emItem id="test_bug449027_17@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Invalid version range, should not block -->
+ <emItem id="test_bug449027_18@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="6" maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Should block all of these -->
+ <emItem id="test_bug449027_19@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange minVersion="3" maxVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_20@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="2"/>
+ <versionRange minVersion="2" maxVersion="3"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_21@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="1"/>
+ <versionRange minVersion="2" maxVersion="4"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_22@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_23@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_24@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_25@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <!-- All plugins are version 5 and tests run against appVersion 3 -->
+
+ <!-- Test 1 not listed, should never get blocked -->
+ <!-- Always blocked -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_2$"/>
+ </pluginItem>
+ <!-- Always blocked -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_3$"/>
+ <versionRange/>
+ </pluginItem>
+ <!-- Not blocked since neither version range matches -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_4$"/>
+ <versionRange minVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </pluginItem>
+ <!-- Invalid version range, should not block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_5$"/>
+ <versionRange minVersion="6" maxVersion="4"/>
+ </pluginItem>
+ <!-- Should block all of these -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_6$"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_7$"/>
+ <versionRange maxVersion="4"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ <versionRange minVersion="6" maxVersion="7"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_8$"/>
+ <versionRange minVersion="2" maxVersion="2"/>
+ <versionRange minVersion="4" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_9$"/>
+ <versionRange minVersion="4"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_10$"/>
+ <versionRange minVersion="5"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_11$"/>
+ <versionRange maxVersion="6"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_12$"/>
+ <versionRange maxVersion="5"/>
+ </pluginItem>
+
+ <!-- This should block all versions for any application -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_13$"/>
+ <versionRange>
+ <targetApplication/>
+ </versionRange>
+ </pluginItem>
+ <!-- Shouldn't block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_14$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <!-- Should block for any version of the app -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_15$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org"/>
+ </versionRange>
+ </pluginItem>
+ <!-- Should still block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_16$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Not blocked since neither version range matches -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_17$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Invalid version range, should not block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_18$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="6" maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Should block all of these -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_19$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange minVersion="3" maxVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_20$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="2"/>
+ <versionRange minVersion="2" maxVersion="3"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_21$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="1"/>
+ <versionRange minVersion="2" maxVersion="4"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_22$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_23$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_24$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_25$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_toolkit.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_toolkit.xml
new file mode 100644
index 000000000..ad8ec5ed9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug449027_toolkit.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <!-- All extensions are version 5 and tests run against toolkitVersion 8 -->
+
+ <!-- Test 1-14 not listed, should never get blocked -->
+
+ <!-- Should block for any version of the app -->
+ <emItem id="test_bug449027_15@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org"/>
+ </versionRange>
+ </emItem>
+ <!-- Should still block -->
+ <emItem id="test_bug449027_16@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Not blocked since neither version range matches -->
+ <emItem id="test_bug449027_17@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Invalid version range, should not block -->
+ <emItem id="test_bug449027_18@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="11" maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Should block all of these -->
+ <emItem id="test_bug449027_19@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="10" maxVersion="11"/>
+ <versionRange minVersion="8" maxVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_20@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="7"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="9" maxVersion="10"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_21@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="6" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="9"/>
+ <versionRange minVersion="10" maxVersion="11"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_22@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_23@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="7"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_24@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_25@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <!-- All plugins are version 5 and tests run against appVersion 3 -->
+
+ <!-- Test 1-14 not listed, should never get blocked -->
+ <!-- Should block for any version of the app -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_15$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org"/>
+ </versionRange>
+ </pluginItem>
+ <!-- Should still block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_16$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Not blocked since neither version range matches -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_17$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Invalid version range, should not block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_18$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="11" maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Should block all of these -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_19$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="10" maxVersion="11"/>
+ <versionRange minVersion="8" maxVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_20$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="7"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="9" maxVersion="10"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_21$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="6" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="9"/>
+ <versionRange minVersion="10" maxVersion="11"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_22$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_23$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="7"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_24$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_25$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug468528.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug468528.xml
new file mode 100644
index 000000000..85f0da57c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug468528.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug468528_1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug468528_2["/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug468528_3"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_1.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_1.rdf
new file mode 100644
index 000000000..5397e8a87
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_1.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_1@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_2.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_2.rdf
new file mode 100644
index 000000000..b1dde7f7a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_2.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_2@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_3.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_3.rdf
new file mode 100644
index 000000000..ae483434a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_3.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_3@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_4.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_4.rdf
new file mode 100644
index 000000000..97abacc5e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_4.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_4@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_5.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_5.rdf
new file mode 100644
index 000000000..bff1104a7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/install_5.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<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>bug470377_5@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_1.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_1.rdf
new file mode 100644
index 000000000..e4ad91ae9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_1.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_2.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_2.rdf
new file mode 100644
index 000000000..10fcafd39
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_2.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_3.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_3.rdf
new file mode 100644
index 000000000..684002462
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_3.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_4.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_4.rdf
new file mode 100644
index 000000000..6e7116239
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_4.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_5.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_5.rdf
new file mode 100644
index 000000000..c926af934
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug470377/update_5.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_1.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_1.xml
new file mode 100644
index 000000000..c4cc2fe37
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug514327_1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug514327_2"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug514327_3"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_2.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_2.xml
new file mode 100644
index 000000000..cc0a0c69d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="Test Plug-in"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_empty.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_empty.xml
new file mode 100644
index 000000000..0261794f8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_empty.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml
new file mode 100644
index 000000000..d651f8799
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_outdated"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml
new file mode 100644
index 000000000..208444681
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_2"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_outdated"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_1.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_1.xpi
new file mode 100644
index 000000000..2dbcc0b50
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_2.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_2.xpi
new file mode 100644
index 000000000..86fc6baaa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug526598_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug541420.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug541420.xpi
new file mode 100644
index 000000000..adb7be9ad
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug541420.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug542391.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug542391.rdf
new file mode 100644
index 000000000..db82cf675
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug542391.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:override1x2-1x3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug554133.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug554133.xml
new file mode 100644
index 000000000..736c10514
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug554133.xml
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>test2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Should not return an incompatible add-on</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>2</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>test4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Should not return an add-on for a different OS</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install os="UNKNOWN">http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Should not include the same result twice</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test6@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test7@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test8@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test9@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test10@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test11@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test12@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug619730.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug619730.xml
new file mode 100644
index 000000000..f2511c0de
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug619730.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems testattr="GFX"><gfxItem/><gfxItem/></gfxItems>
+ <testItems testattr="FOO"><testItem/><testItem/><testItem/></testItems>
+ <fooItems/>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug655254.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug655254.rdf
new file mode 100644
index 000000000..9857dcb55
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug655254.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_compatoverrides.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_compatoverrides.xml
new file mode 100644
index 000000000..c0d67d033
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_compatoverrides.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9">
+ <addon>
+ <name>Test addon 2</name>
+ <type id="1">Extension</type>
+ <guid>addon2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+
+ <addon>
+ <name>Test addon 3</name>
+ <type id="1">Extension</type>
+ <guid>addon3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 3</name>
+ <guid>addon3@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.9</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 4</name>
+ <type id="1">Extension</type>
+ <guid>addon4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 4</name>
+ <guid>addon4@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.9</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 5</name>
+ <type id="1">Extension</type>
+ <guid>addon5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 5</name>
+ <guid>addon5@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.9</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>Unknown App</name>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 6</name>
+ <type id="1">Extension</type>
+ <guid>addon6@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 6</name>
+ <guid>addon6@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>0.9</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 7</name>
+ <type id="1">Extension</type>
+ <guid>addon7@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 7</name>
+ <guid>addon7@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>0.9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 8</name>
+ <type id="1">Extension</type>
+ <guid>addon8@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 8</name>
+ <guid>addon8@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>6</min_version>
+ <max_version>6.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.9</min_version>
+ <max_version>9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>9</max_version>
+ </application>
+ <application>
+ <name>Unknown app</name>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <version_range type="incompatible">
+ <min_version>0.1</min_version>
+ <max_version>0.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>0.9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon_compatibility hosted="false">
+ <name>Test addon 9</name>
+ <guid>addon9@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon_compatibility hosted="false">
+ <name>Test addon 10</name>
+ <guid>addon10@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="compatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_corrupt.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_corrupt.rdf
new file mode 100644
index 000000000..f3341bdcf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_corrupt.rdf
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_dictionary.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_dictionary.rdf
new file mode 100644
index 000000000..364b3bba1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_dictionary.rdf
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:ab-CD@dictionaries.addons.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_dictionary_3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:ef@dictionaries.addons.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_dictionary_4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:gh@dictionaries.addons.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_dictionary_5.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/bootstrap.js b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/bootstrap.js
new file mode 100644
index 000000000..01682d3b7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/bootstrap.js
@@ -0,0 +1,21 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 2);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 2);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/install.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/install.rdf
new file mode 100644
index 000000000..ebe547ccc
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<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>addon2@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Distributed add-ons test</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt
new file mode 100644
index 000000000..051aef85a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt
@@ -0,0 +1 @@
+Test of a file in a sub directory \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt
new file mode 100644
index 000000000..9eddc4493
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt
@@ -0,0 +1 @@
+Nested dummy file \ No newline at end of file
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist.xml
new file mode 100644
index 000000000..639f2d20f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist.xml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.0</os>
+ <vendor>0xdcba</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xabab</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> GREATER_THAN_OR_EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1111 </driverVersion>
+ <driverVersionComparator> EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Darwin 9</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>abcd</vendor>
+ <devices>
+ <device>wxyz</device>
+ <device>asdf</device>
+ <device>erty</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 5 </driverVersion>
+ <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>dcdc</vendor>
+ <devices>
+ <device>uiop</device>
+ <device>vbnm</device>
+ <device>hjkl</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 5 </driverVersion>
+ <driverVersionComparator> EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>abab</vendor>
+ <devices>
+ <device>ghjk</device>
+ <device>cvbn</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 7 </driverVersion>
+ <driverVersionComparator> GREATER_THAN_OR_EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Darwin 9</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist2.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist2.xml
new file mode 100644
index 000000000..0ad8f6819
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.0</os>
+ <vendor>0xdcba</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml
new file mode 100644
index 000000000..22af6f712
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.0</os>
+ <vendor>0xdcba</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml
new file mode 100644
index 000000000..463207d14
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.2</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Darwin 12</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_install.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_install.rdf
new file mode 100644
index 000000000..fe82334fa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_install.rdf
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon7@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_install.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_install.xml
new file mode 100644
index 000000000..5f0aab75f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_install.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <addon>
+ <name>Real Test 2</name>
+ <type id='1'>Extension</type>
+ <guid>addon2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Repository summary</summary>
+ <description>Repository description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="2">http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2.xpi</install>
+ </addon>
+
+ <addon_compatibility hosted="false">
+ <guid>addon6@tests.mozilla.org</guid>
+ <name>Addon Test 6</name>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>1.0</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <min_version>1.0</min_version>
+ <max_version>1.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_migrate.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_migrate.rdf
new file mode 100644
index 000000000..d1dc992d5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_migrate.rdf
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+
+<!-- This is a copy of extensions.rdf from Firefox 3.5 including four
+ test extensions. Addon1 was user enabled, addon2 was user disabled, addon3
+ was pending user disable at the next restart and addon4 was pending user
+ enable at the next restart. Additionally addon1 and 2 have had
+ compatibility updates applies to make them compatible with the app and
+ toolkit respectively, addon3 and 4 have not. addon5 is disabled however
+ at the same time as the migration a new version should be detected. addon6
+ is pending install and needs a compatibility update to be compatible.
+ It also contains two themes in the profile -->
+
+<RDF:RDF xmlns:NS1="http://www.mozilla.org/2004/em-rdf#"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="rdf:#$w8dNC3"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="1" />
+ <RDF:Description RDF:about="rdf:#$w8dNC4"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC5"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC6"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC2"
+ NS1:id="toolkit@mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="1" />
+ <RDF:Description RDF:about="rdf:#$w8dNC1"
+ NS1:id="toolkit@mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC7"
+ NS1:id="toolkit@mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$oadNC1"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$TpnM4"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:updatedMinVersion="1"
+ NS1:updatedMaxVersion="2" />
+ <RDF:Description RDF:about="urn:mozilla:item:addon1@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="1.0"
+ NS1:name="Test 1">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$oadNC1"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon2@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Test 2"
+ NS1:userDisabled="true">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC1"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon3@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Test 3"
+ NS1:userDisabled="needs-disable">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC3"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon4@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Test 4"
+ NS1:userDisabled="needs-enable">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC2"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon5@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="1.0"
+ NS1:name="Test 5"
+ NS1:userDisabled="true">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC7"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon6@tests.mozilla.org"
+ NS1:name="Test 6"
+ NS1:version="1.0"
+ NS1:newVersion="1.0"
+ NS1:installLocation="app-profile">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$TpnM4"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:theme1@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="1.0"
+ NS1:name="Theme 2"
+ NS1:internalName="theme1/1.0">
+ <NS1:type NC:parseType="Integer">4</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC5"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:theme2@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Theme 2"
+ NS1:internalName="theme2/1.0">
+ <NS1:type NC:parseType="Integer">4</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC6"/>
+ </RDF:Description>
+ <RDF:Seq RDF:about="urn:mozilla:item:root">
+ <RDF:li RDF:resource="urn:mozilla:item:addon1@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon2@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon3@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon4@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon5@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon6@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:theme1@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:theme2@tests.mozilla.org"/>
+ </RDF:Seq>
+</RDF:RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_migrate4.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_migrate4.rdf
new file mode 100644
index 000000000..a3bf4f8ae
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_migrate4.rdf
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon5@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon6@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_migrate4_6.xpi</em:updateLink>
+ <em:updateInfoURL>http://example.com/updateInfo.xhtml</em:updateInfoURL>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/ancient.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/ancient.xml
new file mode 100644
index 000000000..699257f87
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/ancient.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem blockID="i454" id="ancient@tests.mozilla.org">
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/new.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/new.xml
new file mode 100644
index 000000000..8cbfb5d6a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/new.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1396046918000">
+ <emItems>
+ <emItem blockID="i454" id="new@tests.mozilla.org">
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/old.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/old.xml
new file mode 100644
index 000000000..75bd6e934
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_overrideblocklist/old.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1296046918000">
+ <emItems>
+ <emItem blockID="i454" id="old@tests.mozilla.org">
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtp.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtp.xml
new file mode 100644
index 000000000..d3564aebd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtp.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_0"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="0"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_1"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_2"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="2"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_3"/>
+ <versionRange minVersion="0" maxVersion="*" vulnerabilitystatus="2"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_4"/>
+ <versionRange minVersion="0" maxVersion="*" severity="1" vulnerabilitystatus="2"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml
new file mode 100644
index 000000000..7cd8496b3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^Test Plug-in"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="2"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_sourceURI.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_sourceURI.xml
new file mode 100644
index 000000000..949288e3f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_sourceURI.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <!-- Passes all requirements -->
+ <addon>
+ <name>Test</name>
+ <type id="1">Extension</type>
+ <guid>addon@tests.mozilla.org</guid>
+ <version>1</version>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://www.example.com/testaddon.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
new file mode 100644
index 000000000..4d4640f60
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <!-- Shouldn't fire onCompatibilityUpdateAvailable since this
+ information is already in the install.rdf -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Should be ignored as it is not for the present version of the
+ application -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update.xpi</em:updateLink>
+ <em:updateInfoURL>http://example.com/updateInfo.xhtml</em:updateInfoURL>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon7@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon8@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update8.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon9@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Incompatible when strict compatibility is enabled -->
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.9</em:minVersion>
+ <em:maxVersion>0.9</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Incompatible due to compatibility override -->
+ <li>
+ <Description>
+ <em:version>4.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.9</em:minVersion>
+ <em:maxVersion>0.9</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Addon for future version of app -->
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>6</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_5.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon10@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.4</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update10.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon11@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:strictCompatibility>true</em:strictCompatibility>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update11.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:item:addon12@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update12.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_update.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.xml
new file mode 100644
index 000000000..62928815b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="11">
+ <addon>
+ <name>Test Addon 9</name>
+ <type id="1">Extension</type>
+ <guid>addon9@tests.mozilla.org</guid>
+ </addon>
+ <addon_compatibility hosted="true">
+ <guid>addon9@tests.mozilla.org</guid>
+ <name>Test Addon 9</name>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>4</min_version>
+ <max_version>4</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
new file mode 100644
index 000000000..93c82886a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:updatecheck1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- This update is incompatible and so should not be considered a valid
+ update -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- This update is incompatible and so should not be considered a valid
+ update -->
+ <li>
+ <Description>
+ <em:version>4.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <!-- An update with no signature which will fail if retrieved with an update
+ key. The updateLink will also be ignored since it is not secure and there
+ is no updateHash. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <!-- An update with a broken signature which will fail if retrieved with an
+ update key. The updateLink will also be ignored since it is not secure
+ and there is no updateHash. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_7@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAMO1O2gwSCCth1GwYMgscfaNakpN40PJfOWt
+ ub2HVdg8+OXMciF8d/9eVWm8eH/IxuxyZlmRZTs3O5tv9eWAY5uBCtqDf1WgTsGk
+ jrgZow1fITkZI7w0//C8eKdMLAtGueGfNs2IlTd5P/0KH/hf1rPc1wUqEqKCd4+L
+ BcVq13ad</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will be ignored since it
+ is not secure and there is no updateHash. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_8@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAMH/33P/bn148mVkAB8i5X8c4LhY52E+MPUT
+ yKHGpowZnRLgL2O0dfpm+rljOBfKi51322PFrsc6VIFml6x4Lrb5foxSyB0Vs9pb
+ SEDFWUKquOQvceQ9iEx5Pe0VzrmUZgcQxd8ksSunWL4wJaBZ/evE5amFC6sw3pv/
+ fjt8p3GN</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since there is
+ an updateHash to verify it. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_9@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAJ5Dv3Zd7/j5dLchCw9iO/cxPq8oOhOYD2M+
+ jUKvmHCyTBRIEaJrE4N7yVbRYk++ERIfyVepLivsVi4pBmF7JTdw0NaKUA0LiOoT
+ mRL8I7s5NPjCiiNcdqbncWyiZwIj1w1nkbWGTlH/gEjRW/LbvT4JAuec8yNFDa4S
+ X8mOMf7k</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since it is
+ a secure URL. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_10@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAGvf7XqqoTl5WofrNq55E7W+UttOEDXLB3Oi
+ XDiXe0i6njlozilseaUo1hgfQhhzN9gkyetP5tGBVcLRrVyliKpJmD6ABCVGW1lS
+ qS+SEw7gDHyHkvwKMyWKedpRGChqLYnnf+Y+CX3MWLZLkwPXMKdTYgN3Rx0lEnJk
+ 37LSEMKE</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since it is
+ a secure URL. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_11@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBACMX/KReOGSJ8CMGRroH1v3Gjv/Qs/pqH+Ow
+ o+hCKWLUKx7hpJgVJkXXdAHW0U88NXlp1S2H0WqA7I/CdmNXJSPzzV/J4z1dZgXh
+ JbW6mqNb0pj6nIe7g8OLzSxDgBmO4DUP5DAmnmqciJLWQzN7OdbcwrWz6xPN5kZF
+ A90eF5zy</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since the
+ updateHash verifying it is not strong enough. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_12@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>md2:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAJRfcFvHIWxVyycCw8IjNmEhabc2uqA1zQwp
+ 5oKh3Y23hwIsQ6xy68Wtjte1NEYFRt5fWkbMXj9YQj6LpVbzBKiGATcrq6MycZKK
+ o5N22cWbrKKRweJezTyN4eLfQg21pG7r8mdfS0bIA28ZVFtQOmORejoUesEouCGy
+ eKYk9nS2</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since it is
+ a secure URL. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_13@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>md2:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBALQKwzLFr/VOw3gJvv/LCh3/PWDd9FqmFnX+
+ hJjBmCaUDtG7CXn1i0h8ed8IeRHpLLT7FCzVwU3bH9BUjdm8wc3ObtlNbd8go01a
+ CoXz50r3rYPcYz4WS+7/+lvrUqsuWd9Wj+q0NeCPiNaaro6/AolE2Qf5JFRL3lxY
+ lsKWAnVO</em:signature>
+ </RDF:Description>
+
+ <!-- There should be no information present for test_bug378216_14 -->
+
+ <!-- Invalid update RDF -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_15@tests.mozilla.org">
+ <em:updates>Foo</em:updates>
+ </RDF:Description>
+
+ <!-- Various updates available - one is not compatible, but compatibility checking is disabled -->
+ <Description about="urn:mozilla:extension:ignore-compat@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.5</em:minVersion>
+ <em:maxVersion>0.6</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Update for future app versions - should never be compatible -->
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <!-- Various updates available - one is not compatible, but compatibility checking is disabled -->
+ <Description about="urn:mozilla:extension:compat-override@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <!-- Has compatibility override, but it doesn't match this app version -->
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Has compatibility override, so is incompaible -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.5</em:minVersion>
+ <em:maxVersion>0.6</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Update for future app versions - should never be compatible -->
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <!-- Opt-in to strict compatibility checking -->
+ <Description about="urn:mozilla:extension:compat-strict-optin@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:strictCompatibility>true</em:strictCompatibility>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf
new file mode 100644
index 000000000..ec6e88ec4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:compatmode-ignore@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:%PORT%/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.rdf
new file mode 100644
index 000000000..2ef88860e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:compatmode-normal@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:%PORT%/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.rdf
new file mode 100644
index 000000000..2f72c181d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:compatmode-strict@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:%PORT%/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_updateid.rdf b/toolkit/mozapps/extensions/test/xpcshell/data/test_updateid.rdf
new file mode 100644
index 000000000..d59df9736
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updateid.rdf
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/addons/test_updateid2_2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/addons/test_updateid3_3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>4.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/addons/test_updateid4_4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/addons/test_updateid2_5.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/unsigned.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/unsigned.xpi
new file mode 100644
index 000000000..51b00475a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/unsigned.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
new file mode 100644
index 000000000..60259944e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -0,0 +1,1759 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const AM_Cc = Components.classes;
+const AM_Ci = Components.interfaces;
+
+const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
+const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
+
+const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
+const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
+const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
+const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_GETADDONS_BYIDS_PERFORMANCE = "extensions.getAddons.getWithPerformance.url";
+
+// Forcibly end the test if it runs longer than 15 minutes
+const TIMEOUT_MS = 900000;
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.import("resource://gre/modules/Promise.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+Components.utils.import("resource://gre/modules/AsyncShutdown.jsm");
+
+Services.prefs.setBoolPref("toolkit.osfile.log", true);
+
+// We need some internal bits of AddonManager
+let AMscope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
+let AddonManager = AMscope.AddonManager;
+let AddonManagerInternal = AMscope.AddonManagerInternal;
+// Mock out AddonManager's reference to the AsyncShutdown module so we can shut
+// down AddonManager from the test
+let MockAsyncShutdown = {
+ hook: null,
+ status: null,
+ profileBeforeChange: {
+ addBlocker: function(aName, aBlocker, aOptions) {
+ do_print("Mock profileBeforeChange blocker for '" + aName + "'");
+ MockAsyncShutdown.hook = aBlocker;
+ MockAsyncShutdown.status = aOptions.fetchState;
+ }
+ },
+ // We can use the real Barrier
+ Barrier: AsyncShutdown.Barrier
+};
+
+AMscope.AsyncShutdown = MockAsyncShutdown;
+
+var gInternalManager = null;
+var gAppInfo = null;
+var gAddonsList;
+
+var gPort = null;
+var gUrlToFileMap = {};
+
+var TEST_UNPACKED = false;
+
+function isNightlyChannel() {
+ var channel = "default";
+ try {
+ channel = Services.prefs.getCharPref("app.update.channel");
+ }
+ catch (e) { }
+
+ return channel != "aurora" && channel != "beta" && channel != "release" && channel != "esr";
+}
+
+function createAppInfo(id, name, version, platformVersion) {
+ gAppInfo = {
+ // nsIXULAppInfo
+ vendor: "Mozilla",
+ name: name,
+ ID: id,
+ version: version,
+ appBuildID: "2007010101",
+ platformVersion: platformVersion ? platformVersion : "1.0",
+ platformBuildID: "2007010101",
+
+ // nsIXULRuntime
+ inSafeMode: false,
+ logConsoleErrors: true,
+ OS: "XPCShell",
+ XPCOMABI: "noarch-spidermonkey",
+ invalidateCachesOnRestart: function invalidateCachesOnRestart() {
+ // Do nothing
+ },
+
+ // nsICrashReporter
+ annotations: {},
+
+ annotateCrashReport: function(key, data) {
+ this.annotations[key] = data;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIXULAppInfo,
+ AM_Ci.nsIXULRuntime,
+ AM_Ci.nsICrashReporter,
+ AM_Ci.nsISupports])
+ };
+
+ var XULAppInfoFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return gAppInfo.QueryInterface(iid);
+ }
+ };
+ var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+ registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
+ XULAPPINFO_CONTRACTID, XULAppInfoFactory);
+}
+
+/**
+ * Tests that an add-on does appear in the crash report annotations, if
+ * crash reporting is enabled. The test will fail if the add-on is not in the
+ * annotation.
+ * @param aId
+ * The ID of the add-on
+ * @param aVersion
+ * The version of the add-on
+ */
+function do_check_in_crash_annotation(aId, aVersion) {
+ if (!("nsICrashReporter" in AM_Ci))
+ return;
+
+ if (!("Add-ons" in gAppInfo.annotations)) {
+ do_check_false(true);
+ return;
+ }
+
+ let addons = gAppInfo.annotations["Add-ons"].split(",");
+ do_check_false(addons.indexOf(encodeURIComponent(aId) + ":" +
+ encodeURIComponent(aVersion)) < 0);
+}
+
+/**
+ * Tests that an add-on does not appear in the crash report annotations, if
+ * crash reporting is enabled. The test will fail if the add-on is in the
+ * annotation.
+ * @param aId
+ * The ID of the add-on
+ * @param aVersion
+ * The version of the add-on
+ */
+function do_check_not_in_crash_annotation(aId, aVersion) {
+ if (!("nsICrashReporter" in AM_Ci))
+ return;
+
+ if (!("Add-ons" in gAppInfo.annotations)) {
+ do_check_true(true);
+ return;
+ }
+
+ let addons = gAppInfo.annotations["Add-ons"].split(",");
+ do_check_true(addons.indexOf(encodeURIComponent(aId) + ":" +
+ encodeURIComponent(aVersion)) < 0);
+}
+
+/**
+ * Returns a testcase xpi
+ *
+ * @param aName
+ * The name of the testcase (without extension)
+ * @return an nsIFile pointing to the testcase xpi
+ */
+function do_get_addon(aName) {
+ return do_get_file("addons/" + aName + ".xpi");
+}
+
+function do_get_addon_hash(aName, aAlgorithm) {
+ let file = do_get_addon(aName);
+ return do_get_file_hash(file);
+}
+
+function do_get_file_hash(aFile, aAlgorithm) {
+ if (!aAlgorithm)
+ aAlgorithm = "sha1";
+
+ let crypto = AM_Cc["@mozilla.org/security/hash;1"].
+ createInstance(AM_Ci.nsICryptoHash);
+ crypto.initWithString(aAlgorithm);
+ let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ fis.init(aFile, -1, -1, false);
+ crypto.updateFromStream(fis, aFile.fileSize);
+
+ // return the two-digit hexadecimal code for a byte
+ function toHexString(charCode)
+ ("0" + charCode.toString(16)).slice(-2);
+
+ let binary = crypto.finish(false);
+ return aAlgorithm + ":" + [toHexString(binary.charCodeAt(i)) for (i in binary)].join("")
+}
+
+/**
+ * Returns an extension uri spec
+ *
+ * @param aProfileDir
+ * The extension install directory
+ * @return a uri spec pointing to the root of the extension
+ */
+function do_get_addon_root_uri(aProfileDir, aId) {
+ let path = aProfileDir.clone();
+ path.append(aId);
+ if (!path.exists()) {
+ path.leafName += ".xpi";
+ return "jar:" + Services.io.newFileURI(path).spec + "!/";
+ }
+ else {
+ return Services.io.newFileURI(path).spec;
+ }
+}
+
+function do_get_expected_addon_name(aId) {
+ if (TEST_UNPACKED)
+ return aId;
+ return aId + ".xpi";
+}
+
+/**
+ * Check that an array of actual add-ons is the same as an array of
+ * expected add-ons.
+ *
+ * @param aActualAddons
+ * The array of actual add-ons to check.
+ * @param aExpectedAddons
+ * The array of expected add-ons to check against.
+ * @param aProperties
+ * An array of properties to check.
+ */
+function do_check_addons(aActualAddons, aExpectedAddons, aProperties) {
+ do_check_neq(aActualAddons, null);
+ do_check_eq(aActualAddons.length, aExpectedAddons.length);
+ for (let i = 0; i < aActualAddons.length; i++)
+ do_check_addon(aActualAddons[i], aExpectedAddons[i], aProperties);
+}
+
+/**
+ * Check that the actual add-on is the same as the expected add-on.
+ *
+ * @param aActualAddon
+ * The actual add-on to check.
+ * @param aExpectedAddon
+ * The expected add-on to check against.
+ * @param aProperties
+ * An array of properties to check.
+ */
+function do_check_addon(aActualAddon, aExpectedAddon, aProperties) {
+ do_check_neq(aActualAddon, null);
+
+ aProperties.forEach(function(aProperty) {
+ let actualValue = aActualAddon[aProperty];
+ let expectedValue = aExpectedAddon[aProperty];
+
+ // Check that all undefined expected properties are null on actual add-on
+ if (!(aProperty in aExpectedAddon)) {
+ if (actualValue !== undefined && actualValue !== null) {
+ do_throw("Unexpected defined/non-null property for add-on " +
+ aExpectedAddon.id + " (addon[" + aProperty + "] = " +
+ actualValue.toSource() + ")");
+ }
+
+ return;
+ }
+ else if (expectedValue && !actualValue) {
+ do_throw("Missing property for add-on " + aExpectedAddon.id +
+ ": expected addon[" + aProperty + "] = " + expectedValue);
+ return;
+ }
+
+ switch (aProperty) {
+ case "creator":
+ do_check_author(actualValue, expectedValue);
+ break;
+
+ case "developers":
+ case "translators":
+ case "contributors":
+ do_check_eq(actualValue.length, expectedValue.length);
+ for (let i = 0; i < actualValue.length; i++)
+ do_check_author(actualValue[i], expectedValue[i]);
+ break;
+
+ case "screenshots":
+ do_check_eq(actualValue.length, expectedValue.length);
+ for (let i = 0; i < actualValue.length; i++)
+ do_check_screenshot(actualValue[i], expectedValue[i]);
+ break;
+
+ case "sourceURI":
+ do_check_eq(actualValue.spec, expectedValue);
+ break;
+
+ case "updateDate":
+ do_check_eq(actualValue.getTime(), expectedValue.getTime());
+ break;
+
+ case "compatibilityOverrides":
+ do_check_eq(actualValue.length, expectedValue.length);
+ for (let i = 0; i < actualValue.length; i++)
+ do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
+ break;
+
+ case "icons":
+ do_check_icons(actualValue, expectedValue);
+ break;
+
+ default:
+ if (remove_port(actualValue) !== remove_port(expectedValue))
+ do_throw("Failed for " + aProperty + " for add-on " + aExpectedAddon.id +
+ " (" + actualValue + " === " + expectedValue + ")");
+ }
+ });
+}
+
+/**
+ * Check that the actual author is the same as the expected author.
+ *
+ * @param aActual
+ * The actual author to check.
+ * @param aExpected
+ * The expected author to check against.
+ */
+function do_check_author(aActual, aExpected) {
+ do_check_eq(aActual.toString(), aExpected.name);
+ do_check_eq(aActual.name, aExpected.name);
+ do_check_eq(aActual.url, aExpected.url);
+}
+
+/**
+ * Check that the actual screenshot is the same as the expected screenshot.
+ *
+ * @param aActual
+ * The actual screenshot to check.
+ * @param aExpected
+ * The expected screenshot to check against.
+ */
+function do_check_screenshot(aActual, aExpected) {
+ do_check_eq(aActual.toString(), aExpected.url);
+ do_check_eq(aActual.url, aExpected.url);
+ do_check_eq(aActual.width, aExpected.width);
+ do_check_eq(aActual.height, aExpected.height);
+ do_check_eq(aActual.thumbnailURL, aExpected.thumbnailURL);
+ do_check_eq(aActual.thumbnailWidth, aExpected.thumbnailWidth);
+ do_check_eq(aActual.thumbnailHeight, aExpected.thumbnailHeight);
+ do_check_eq(aActual.caption, aExpected.caption);
+}
+
+/**
+ * Check that the actual compatibility override is the same as the expected
+ * compatibility override.
+ *
+ * @param aAction
+ * The actual compatibility override to check.
+ * @param aExpected
+ * The expected compatibility override to check against.
+ */
+function do_check_compatibilityoverride(aActual, aExpected) {
+ do_check_eq(aActual.type, aExpected.type);
+ do_check_eq(aActual.minVersion, aExpected.minVersion);
+ do_check_eq(aActual.maxVersion, aExpected.maxVersion);
+ do_check_eq(aActual.appID, aExpected.appID);
+ do_check_eq(aActual.appMinVersion, aExpected.appMinVersion);
+ do_check_eq(aActual.appMaxVersion, aExpected.appMaxVersion);
+}
+
+function do_check_icons(aActual, aExpected) {
+ for (var size in aExpected) {
+ do_check_eq(remove_port(aActual[size]), remove_port(aExpected[size]));
+ }
+}
+
+// Record the error (if any) from trying to save the XPI
+// database at shutdown time
+let gXPISaveError = null;
+
+/**
+ * Starts up the add-on manager as if it was started by the application.
+ *
+ * @param aAppChanged
+ * An optional boolean parameter to simulate the case where the
+ * application has changed version since the last run. If not passed it
+ * defaults to true
+ */
+function startupManager(aAppChanged) {
+ if (gInternalManager)
+ do_throw("Test attempt to startup manager that was already started.");
+
+ if (aAppChanged || aAppChanged === undefined) {
+ if (gExtensionsINI.exists())
+ gExtensionsINI.remove(true);
+ }
+
+ gInternalManager = AM_Cc["@mozilla.org/addons/integration;1"].
+ getService(AM_Ci.nsIObserver).
+ QueryInterface(AM_Ci.nsITimerCallback);
+
+ gInternalManager.observe(null, "addons-startup", null);
+
+ // Load the add-ons list as it was after extension registration
+ loadAddonsList();
+}
+
+/**
+ * Helper to spin the event loop until a promise resolves or rejects
+ */
+function loopUntilPromise(aPromise) {
+ let done = false;
+ aPromise.then(
+ () => done = true,
+ err => {
+ do_report_unexpected_exception(err);
+ done = true;
+ });
+
+ let thr = Services.tm.mainThread;
+
+ while (!done) {
+ thr.processNextEvent(true);
+ }
+}
+
+/**
+ * Restarts the add-on manager as if the host application was restarted.
+ *
+ * @param aNewVersion
+ * An optional new version to use for the application. Passing this
+ * will change nsIXULAppInfo.version and make the startup appear as if
+ * the application version has changed.
+ */
+function restartManager(aNewVersion) {
+ loopUntilPromise(promiseRestartManager(aNewVersion));
+}
+
+function promiseRestartManager(aNewVersion) {
+ return promiseShutdownManager()
+ .then(null, err => do_report_unexpected_exception(err))
+ .then(() => {
+ if (aNewVersion) {
+ gAppInfo.version = aNewVersion;
+ startupManager(true);
+ }
+ else {
+ startupManager(false);
+ }
+ });
+}
+
+function shutdownManager() {
+ loopUntilPromise(promiseShutdownManager());
+}
+
+function promiseShutdownManager() {
+ if (!gInternalManager) {
+ return Promise.resolve(false);
+ }
+
+ let hookErr = null;
+ Services.obs.notifyObservers(null, "quit-application-granted", null);
+ return MockAsyncShutdown.hook()
+ .then(null, err => hookErr = err)
+ .then( () => {
+ gInternalManager = null;
+
+ // Load the add-ons list as it was after application shutdown
+ loadAddonsList();
+
+ // Clear any crash report annotations
+ gAppInfo.annotations = {};
+
+ // Force the XPIProvider provider to reload to better
+ // simulate real-world usage.
+ let XPIscope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+ // This would be cleaner if I could get it as the rejection reason from
+ // the AddonManagerInternal.shutdown() promise
+ gXPISaveError = XPIscope.XPIProvider._shutdownError;
+ do_print("gXPISaveError set to: " + gXPISaveError);
+ AddonManagerPrivate.unregisterProvider(XPIscope.XPIProvider);
+ Components.utils.unload("resource://gre/modules/addons/XPIProvider.jsm");
+ if (hookErr) {
+ throw hookErr;
+ }
+ });
+}
+
+function loadAddonsList() {
+ function readDirectories(aSection) {
+ var dirs = [];
+ var keys = parser.getKeys(aSection);
+ while (keys.hasMore()) {
+ let descriptor = parser.getString(aSection, keys.getNext());
+ try {
+ let file = AM_Cc["@mozilla.org/file/local;1"].
+ createInstance(AM_Ci.nsIFile);
+ file.persistentDescriptor = descriptor;
+ dirs.push(file);
+ }
+ catch (e) {
+ // Throws if the directory doesn't exist, we can ignore this since the
+ // platform will too.
+ }
+ }
+ return dirs;
+ }
+
+ gAddonsList = {
+ extensions: [],
+ themes: [],
+ mpIncompatible: new Set()
+ };
+
+ if (!gExtensionsINI.exists())
+ return;
+
+ var factory = AM_Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
+ getService(AM_Ci.nsIINIParserFactory);
+ var parser = factory.createINIParser(gExtensionsINI);
+ gAddonsList.extensions = readDirectories("ExtensionDirs");
+ gAddonsList.themes = readDirectories("ThemeDirs");
+ var keys = parser.getKeys("MultiprocessIncompatibleExtensions");
+ while (keys.hasMore()) {
+ let id = parser.getString("MultiprocessIncompatibleExtensions", keys.getNext());
+ gAddonsList.mpIncompatible.add(id);
+ }
+}
+
+function isItemInAddonsList(aType, aDir, aId) {
+ var path = aDir.clone();
+ path.append(aId);
+ var xpiPath = aDir.clone();
+ xpiPath.append(aId + ".xpi");
+ for (var i = 0; i < gAddonsList[aType].length; i++) {
+ let file = gAddonsList[aType][i];
+ if (!file.exists())
+ do_throw("Non-existant path found in extensions.ini: " + file.path)
+ if (file.isDirectory() && file.equals(path))
+ return true;
+ if (file.isFile() && file.equals(xpiPath))
+ return true;
+ }
+ return false;
+}
+
+function isItemMarkedMPIncompatible(aId) {
+ return gAddonsList.mpIncompatible.has(aId);
+}
+
+function isThemeInAddonsList(aDir, aId) {
+ return isItemInAddonsList("themes", aDir, aId);
+}
+
+function isExtensionInAddonsList(aDir, aId) {
+ return isItemInAddonsList("extensions", aDir, aId);
+}
+
+function check_startup_changes(aType, aIds) {
+ var ids = aIds.slice(0);
+ ids.sort();
+ var changes = AddonManager.getStartupChanges(aType);
+ changes = changes.filter(function(aEl) /@tests.mozilla.org$/.test(aEl));
+ changes.sort();
+
+ do_check_eq(JSON.stringify(ids), JSON.stringify(changes));
+}
+
+/**
+ * Escapes any occurances of &, ", < or > with XML entities.
+ *
+ * @param str
+ * The string to escape
+ * @return The escaped string
+ */
+function escapeXML(aStr) {
+ return aStr.toString()
+ .replace(/&/g, "&amp;")
+ .replace(/"/g, "&quot;")
+ .replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;");
+}
+
+function writeLocaleStrings(aData) {
+ let rdf = "";
+ ["name", "description", "creator", "homepageURL"].forEach(function(aProp) {
+ if (aProp in aData)
+ rdf += "<em:" + aProp + ">" + escapeXML(aData[aProp]) + "</em:" + aProp + ">\n";
+ });
+
+ ["developer", "translator", "contributor"].forEach(function(aProp) {
+ if (aProp in aData) {
+ aData[aProp].forEach(function(aValue) {
+ rdf += "<em:" + aProp + ">" + escapeXML(aValue) + "</em:" + aProp + ">\n";
+ });
+ }
+ });
+ return rdf;
+}
+
+/**
+ * Creates an update.rdf structure as a string using for the update data passed.
+ *
+ * @param aData
+ * The update data as a JS object. Each property name is an add-on ID,
+ * the property value is an array of each version of the add-on. Each
+ * array value is a JS object containing the data for the version, at
+ * minimum a "version" and "targetApplications" property should be
+ * included to create a functional update manifest.
+ * @return the update.rdf structure as a string.
+ */
+function createUpdateRDF(aData) {
+ var rdf = '<?xml version="1.0"?>\n';
+ rdf += '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n' +
+ ' xmlns:em="http://www.mozilla.org/2004/em-rdf#">\n';
+
+ for (let addon in aData) {
+ rdf += ' <Description about="urn:mozilla:extension:' + escapeXML(addon) + '"><em:updates><Seq>\n';
+
+ for (let versionData of aData[addon]) {
+ rdf += ' <li><Description>\n';
+
+ for (let prop of ["version", "multiprocessCompatible"]) {
+ if (prop in versionData)
+ rdf += " <em:" + prop + ">" + escapeXML(versionData[prop]) + "</em:" + prop + ">\n";
+ }
+
+ if ("targetApplications" in versionData) {
+ for (let app of versionData.targetApplications) {
+ rdf += " <em:targetApplication><Description>\n";
+ for (let prop of ["id", "minVersion", "maxVersion", "updateLink", "updateHash"]) {
+ if (prop in app)
+ rdf += " <em:" + prop + ">" + escapeXML(app[prop]) + "</em:" + prop + ">\n";
+ }
+ rdf += " </Description></em:targetApplication>\n";
+ }
+ }
+
+ rdf += ' </Description></li>\n';
+ }
+
+ rdf += ' </Seq></em:updates></Description>\n'
+ }
+ rdf += "</RDF>\n";
+
+ return rdf;
+}
+
+function createInstallRDF(aData) {
+ var rdf = '<?xml version="1.0"?>\n';
+ rdf += '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n' +
+ ' xmlns:em="http://www.mozilla.org/2004/em-rdf#">\n';
+ rdf += '<Description about="urn:mozilla:install-manifest">\n';
+
+ ["id", "version", "type", "internalName", "updateURL", "updateKey",
+ "optionsURL", "optionsType", "aboutURL", "iconURL", "icon64URL",
+ "skinnable", "bootstrap", "strictCompatibility", "multiprocessCompatible"].forEach(function(aProp) {
+ if (aProp in aData)
+ rdf += "<em:" + aProp + ">" + escapeXML(aData[aProp]) + "</em:" + aProp + ">\n";
+ });
+
+ rdf += writeLocaleStrings(aData);
+
+ if ("targetPlatforms" in aData) {
+ aData.targetPlatforms.forEach(function(aPlatform) {
+ rdf += "<em:targetPlatform>" + escapeXML(aPlatform) + "</em:targetPlatform>\n";
+ });
+ }
+
+ if ("targetApplications" in aData) {
+ aData.targetApplications.forEach(function(aApp) {
+ rdf += "<em:targetApplication><Description>\n";
+ ["id", "minVersion", "maxVersion"].forEach(function(aProp) {
+ if (aProp in aApp)
+ rdf += "<em:" + aProp + ">" + escapeXML(aApp[aProp]) + "</em:" + aProp + ">\n";
+ });
+ rdf += "</Description></em:targetApplication>\n";
+ });
+ }
+
+ if ("localized" in aData) {
+ aData.localized.forEach(function(aLocalized) {
+ rdf += "<em:localized><Description>\n";
+ if ("locale" in aLocalized) {
+ aLocalized.locale.forEach(function(aLocaleName) {
+ rdf += "<em:locale>" + escapeXML(aLocaleName) + "</em:locale>\n";
+ });
+ }
+ rdf += writeLocaleStrings(aLocalized);
+ rdf += "</Description></em:localized>\n";
+ });
+ }
+
+ rdf += "</Description>\n</RDF>\n";
+ return rdf;
+}
+
+/**
+ * Writes an install.rdf manifest into a directory using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aDir
+ * The directory to add the install.rdf to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @param aExtraFile
+ * An optional dummy file to create in the directory
+ * @return An nsIFile for the directory in which the add-on is installed.
+ */
+function writeInstallRDFToDir(aData, aDir, aId, aExtraFile) {
+ var id = aId ? aId : aData.id
+
+ var dir = aDir.clone();
+ dir.append(id);
+
+ var rdf = createInstallRDF(aData);
+ if (!dir.exists())
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ var file = dir.clone();
+ file.append("install.rdf");
+ if (file.exists())
+ file.remove(true);
+ var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fos.init(file,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ fos.write(rdf, rdf.length);
+ fos.close();
+
+ if (!aExtraFile)
+ return dir;
+
+ file = dir.clone();
+ file.append(aExtraFile);
+ file.create(AM_Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ return dir;
+}
+
+/**
+ * Writes an install.rdf manifest into an extension using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aDir
+ * The install directory to add the extension to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @param aExtraFile
+ * An optional dummy file to create in the extension
+ * @return A file pointing to where the extension was installed
+ */
+function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
+ if (TEST_UNPACKED) {
+ return writeInstallRDFToDir(aData, aDir, aId, aExtraFile);
+ }
+ return writeInstallRDFToXPI(aData, aDir, aId, aExtraFile);
+}
+
+/**
+ * Writes an install.rdf manifest into a packed extension using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aDir
+ * The install directory to add the extension to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @param aExtraFile
+ * An optional dummy file to create in the extension
+ * @return A file pointing to where the extension was installed
+ */
+function writeInstallRDFToXPI(aData, aDir, aId, aExtraFile) {
+ var id = aId ? aId : aData.id
+
+ if (!aDir.exists())
+ aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ var file = aDir.clone();
+ file.append(id + ".xpi");
+ writeInstallRDFToXPIFile(aData, file, aExtraFile);
+
+ return file;
+}
+
+/**
+ * Writes an install.rdf manifest into an XPI file using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aFile
+ * The XPI file to write to. Any existing file will be overwritten
+ * @param aExtraFile
+ * An optional dummy file to create in the extension
+ */
+function writeInstallRDFToXPIFile(aData, aFile, aExtraFile) {
+ var rdf = createInstallRDF(aData);
+ var stream = AM_Cc["@mozilla.org/io/string-input-stream;1"].
+ createInstance(AM_Ci.nsIStringInputStream);
+ stream.setData(rdf, -1);
+ var zipW = AM_Cc["@mozilla.org/zipwriter;1"].
+ createInstance(AM_Ci.nsIZipWriter);
+ zipW.open(aFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
+ zipW.addEntryStream("install.rdf", 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
+ stream, false);
+ if (aExtraFile)
+ zipW.addEntryStream(aExtraFile, 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
+ stream, false);
+ zipW.close();
+}
+
+let temp_xpis = [];
+/**
+ * Creates an XPI file for some manifest data in the temporary directory and
+ * returns the nsIFile for it. The file will be deleted when the test completes.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @return A file pointing to the created XPI file
+ */
+function createTempXPIFile(aData) {
+ var file = gTmpD.clone();
+ file.append("foo.xpi");
+ do {
+ file.leafName = Math.floor(Math.random() * 1000000) + ".xpi";
+ } while (file.exists());
+
+ temp_xpis.push(file);
+ writeInstallRDFToXPIFile(aData, file);
+ return file;
+}
+
+/**
+ * Sets the last modified time of the extension, usually to trigger an update
+ * of its metadata. If the extension is unpacked, this function assumes that
+ * the extension contains only the install.rdf file.
+ *
+ * @param aExt a file pointing to either the packed extension or its unpacked directory.
+ * @param aTime the time to which we set the lastModifiedTime of the extension
+ *
+ * @deprecated Please use promiseSetExtensionModifiedTime instead
+ */
+function setExtensionModifiedTime(aExt, aTime) {
+ aExt.lastModifiedTime = aTime;
+ if (aExt.isDirectory()) {
+ let entries = aExt.directoryEntries
+ .QueryInterface(AM_Ci.nsIDirectoryEnumerator);
+ while (entries.hasMoreElements())
+ setExtensionModifiedTime(entries.nextFile, aTime);
+ entries.close();
+ }
+}
+function promiseSetExtensionModifiedTime(aPath, aTime) {
+ return Task.spawn(function* () {
+ yield OS.File.setDates(aPath, aTime, aTime);
+ let entries, iterator;
+ try {
+ let iterator = new OS.File.DirectoryIterator(aPath);
+ entries = yield iterator.nextBatch();
+ } catch (ex if ex instanceof OS.File.Error) {
+ return;
+ } finally {
+ if (iterator) {
+ iterator.close();
+ }
+ }
+ for (let entry of entries) {
+ yield promiseSetExtensionModifiedTime(entry.path, aTime);
+ }
+ });
+}
+
+/**
+ * Manually installs an XPI file into an install location by either copying the
+ * XPI there or extracting it depending on whether unpacking is being tested
+ * or not.
+ *
+ * @param aXPIFile
+ * The XPI file to install.
+ * @param aInstallLocation
+ * The install location (an nsIFile) to install into.
+ * @param aID
+ * The ID to install as.
+ */
+function manuallyInstall(aXPIFile, aInstallLocation, aID) {
+ if (TEST_UNPACKED) {
+ let dir = aInstallLocation.clone();
+ dir.append(aID);
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(aXPIFile);
+ let entries = zip.findEntries(null);
+ while (entries.hasMore()) {
+ let entry = entries.getNext();
+ let target = dir.clone();
+ entry.split("/").forEach(function(aPart) {
+ target.append(aPart);
+ });
+ zip.extract(entry, target);
+ }
+ zip.close();
+
+ return dir;
+ }
+ else {
+ let target = aInstallLocation.clone();
+ target.append(aID + ".xpi");
+ aXPIFile.copyTo(target.parent, target.leafName);
+ return target;
+ }
+}
+
+/**
+ * Manually uninstalls an add-on by removing its files from the install
+ * location.
+ *
+ * @param aInstallLocation
+ * The nsIFile of the install location to remove from.
+ * @param aID
+ * The ID of the add-on to remove.
+ */
+function manuallyUninstall(aInstallLocation, aID) {
+ let file = getFileForAddon(aInstallLocation, aID);
+
+ // In reality because the app is restarted a flush isn't necessary for XPIs
+ // removed outside the app, but for testing we must flush manually.
+ if (file.isFile())
+ Services.obs.notifyObservers(file, "flush-cache-entry", null);
+
+ file.remove(true);
+}
+
+/**
+ * Gets the nsIFile for where an add-on is installed. It may point to a file or
+ * a directory depending on whether add-ons are being installed unpacked or not.
+ *
+ * @param aDir
+ * The nsIFile for the install location
+ * @param aId
+ * The ID of the add-on
+ * @return an nsIFile
+ */
+function getFileForAddon(aDir, aId) {
+ var dir = aDir.clone();
+ dir.append(do_get_expected_addon_name(aId));
+ return dir;
+}
+
+function registerDirectory(aKey, aDir) {
+ var dirProvider = {
+ getFile: function(aProp, aPersistent) {
+ aPersistent.value = true;
+ if (aProp == aKey)
+ return aDir.clone();
+ return null;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIDirectoryServiceProvider,
+ AM_Ci.nsISupports])
+ };
+ Services.dirsvc.registerProvider(dirProvider);
+}
+
+var gExpectedEvents = {};
+var gExpectedInstalls = [];
+var gNext = null;
+
+function getExpectedEvent(aId) {
+ if (!(aId in gExpectedEvents))
+ do_throw("Wasn't expecting events for " + aId);
+ if (gExpectedEvents[aId].length == 0)
+ do_throw("Too many events for " + aId);
+ let event = gExpectedEvents[aId].shift();
+ if (event instanceof Array)
+ return event;
+ return [event, true];
+}
+
+function getExpectedInstall(aAddon) {
+ if (gExpectedInstalls instanceof Array)
+ return gExpectedInstalls.shift();
+ if (!aAddon || !aAddon.id)
+ return gExpectedInstalls["NO_ID"].shift();
+ let id = aAddon.id;
+ if (!(id in gExpectedInstalls) || !(gExpectedInstalls[id] instanceof Array))
+ do_throw("Wasn't expecting events for " + id);
+ if (gExpectedInstalls[id].length == 0)
+ do_throw("Too many events for " + id);
+ return gExpectedInstalls[id].shift();
+}
+
+const AddonListener = {
+ onPropertyChanged: function(aAddon, aProperties) {
+ do_print(`Got onPropertyChanged event for ${aAddon.id}`);
+ let [event, properties] = getExpectedEvent(aAddon.id);
+ do_check_eq("onPropertyChanged", event);
+ do_check_eq(aProperties.length, properties.length);
+ properties.forEach(function(aProperty) {
+ // Only test that the expected properties are listed, having additional
+ // properties listed is not necessary a problem
+ if (aProperties.indexOf(aProperty) == -1)
+ do_throw("Did not see property change for " + aProperty);
+ });
+ return check_test_completed(arguments);
+ },
+
+ onEnabling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onEnabling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onEnabling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ return check_test_completed(arguments);
+ },
+
+ onEnabled: function(aAddon) {
+ do_print(`Got onEnabled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onEnabled", event);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ return check_test_completed(arguments);
+ },
+
+ onDisabling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onDisabling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onDisabling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE));
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ return check_test_completed(arguments);
+ },
+
+ onDisabled: function(aAddon) {
+ do_print(`Got onDisabled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onDisabled", event);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ return check_test_completed(arguments);
+ },
+
+ onInstalling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onInstalling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onInstalling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL));
+ return check_test_completed(arguments);
+ },
+
+ onInstalled: function(aAddon) {
+ do_print(`Got onInstalled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onInstalled", event);
+ return check_test_completed(arguments);
+ },
+
+ onUninstalling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onUninstalling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onUninstalling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL));
+ return check_test_completed(arguments);
+ },
+
+ onUninstalled: function(aAddon) {
+ do_print(`Got onUninstalled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onUninstalled", event);
+ return check_test_completed(arguments);
+ },
+
+ onOperationCancelled: function(aAddon) {
+ do_print(`Got onOperationCancelled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onOperationCancelled", event);
+ return check_test_completed(arguments);
+ }
+};
+
+const InstallListener = {
+ onNewInstall: function(install) {
+ if (install.state != AddonManager.STATE_DOWNLOADED &&
+ install.state != AddonManager.STATE_AVAILABLE)
+ do_throw("Bad install state " + install.state);
+ do_check_eq(install.error, 0);
+ do_check_eq("onNewInstall", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadStarted: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADING);
+ do_check_eq(install.error, 0);
+ do_check_eq("onDownloadStarted", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadEnded: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.error, 0);
+ do_check_eq("onDownloadEnded", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadFailed: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq("onDownloadFailed", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadCancelled: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_CANCELLED);
+ do_check_eq(install.error, 0);
+ do_check_eq("onDownloadCancelled", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onInstallStarted: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_INSTALLING);
+ do_check_eq(install.error, 0);
+ do_check_eq("onInstallStarted", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onInstallEnded: function(install, newAddon) {
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_eq(install.error, 0);
+ do_check_eq("onInstallEnded", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onInstallFailed: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_INSTALL_FAILED);
+ do_check_eq("onInstallFailed", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onInstallCancelled: function(install) {
+ // If the install was cancelled by a listener returning false from
+ // onInstallStarted, then the state will revert to STATE_DOWNLOADED.
+ let possibleStates = [AddonManager.STATE_CANCELLED,
+ AddonManager.STATE_DOWNLOADED];
+ do_check_true(possibleStates.indexOf(install.state) != -1);
+ do_check_eq(install.error, 0);
+ do_check_eq("onInstallCancelled", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onExternalInstall: function(aAddon, existingAddon, aRequiresRestart) {
+ do_check_eq("onExternalInstall", getExpectedInstall(aAddon));
+ do_check_false(aRequiresRestart);
+ return check_test_completed(arguments);
+ }
+};
+
+function hasFlag(aBits, aFlag) {
+ return (aBits & aFlag) != 0;
+}
+
+// Just a wrapper around setting the expected events
+function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ gExpectedInstalls = aExpectedInstalls;
+ gExpectedEvents = aExpectedEvents;
+ gNext = aNext;
+}
+
+// Checks if all expected events have been seen and if so calls the callback
+function check_test_completed(aArgs) {
+ if (!gNext)
+ return undefined;
+
+ if (gExpectedInstalls instanceof Array &&
+ gExpectedInstalls.length > 0)
+ return undefined;
+ else for each (let installList in gExpectedInstalls) {
+ if (installList.length > 0)
+ return undefined;
+ }
+
+ for (let id in gExpectedEvents) {
+ if (gExpectedEvents[id].length > 0)
+ return undefined;
+ }
+
+ return gNext.apply(null, aArgs);
+}
+
+// Verifies that all the expected events for all add-ons were seen
+function ensure_test_completed() {
+ for (let i in gExpectedEvents) {
+ if (gExpectedEvents[i].length > 0)
+ do_throw("Didn't see all the expected events for " + i);
+ }
+ gExpectedEvents = {};
+ if (gExpectedInstalls)
+ do_check_eq(gExpectedInstalls.length, 0);
+}
+
+/**
+ * A helper method to install an array of AddonInstall to completion and then
+ * call a provided callback.
+ *
+ * @param aInstalls
+ * The array of AddonInstalls to install
+ * @param aCallback
+ * The callback to call when all installs have finished
+ */
+function completeAllInstalls(aInstalls, aCallback) {
+ let count = aInstalls.length;
+
+ if (count == 0) {
+ aCallback();
+ return;
+ }
+
+ function installCompleted(aInstall) {
+ aInstall.removeListener(listener);
+
+ if (--count == 0)
+ do_execute_soon(aCallback);
+ }
+
+ let listener = {
+ onDownloadFailed: installCompleted,
+ onDownloadCancelled: installCompleted,
+ onInstallFailed: installCompleted,
+ onInstallCancelled: installCompleted,
+ onInstallEnded: installCompleted
+ };
+
+ aInstalls.forEach(function(aInstall) {
+ aInstall.addListener(listener);
+ aInstall.install();
+ });
+}
+
+function promiseCompleteAllInstalls(aInstalls) {
+ return new Promise(resolve => {
+ completeAllInstalls(aInstalls, resolve);
+ });
+}
+
+/**
+ * A helper method to install an array of files and call a callback after the
+ * installs are completed.
+ *
+ * @param aFiles
+ * The array of files to install
+ * @param aCallback
+ * The callback to call when all installs have finished
+ * @param aIgnoreIncompatible
+ * Optional parameter to ignore add-ons that are incompatible in
+ * aome way with the application
+ */
+function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
+ let count = aFiles.length;
+ let installs = [];
+ function callback() {
+ if (aCallback) {
+ aCallback();
+ }
+ }
+ aFiles.forEach(function(aFile) {
+ AddonManager.getInstallForFile(aFile, function(aInstall) {
+ if (!aInstall)
+ do_throw("No AddonInstall created for " + aFile.path);
+ do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
+
+ if (!aIgnoreIncompatible || !aInstall.addon.appDisabled)
+ installs.push(aInstall);
+
+ if (--count == 0)
+ completeAllInstalls(installs, callback);
+ });
+ });
+}
+
+function promiseInstallAllFiles(aFiles, aIgnoreIncompatible) {
+ let deferred = Promise.defer();
+ installAllFiles(aFiles, deferred.resolve, aIgnoreIncompatible);
+ return deferred.promise;
+
+}
+
+if ("nsIWindowsRegKey" in AM_Ci) {
+ var MockRegistry = {
+ LOCAL_MACHINE: {},
+ CURRENT_USER: {},
+ CLASSES_ROOT: {},
+
+ getRoot: function(aRoot) {
+ switch (aRoot) {
+ case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
+ return MockRegistry.LOCAL_MACHINE;
+ case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
+ return MockRegistry.CURRENT_USER;
+ case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT:
+ return MockRegistry.CLASSES_ROOT;
+ default:
+ do_throw("Unknown root " + aRootKey);
+ return null;
+ }
+ },
+
+ setValue: function(aRoot, aPath, aName, aValue) {
+ let rootKey = MockRegistry.getRoot(aRoot);
+
+ if (!(aPath in rootKey)) {
+ rootKey[aPath] = [];
+ }
+ else {
+ for (let i = 0; i < rootKey[aPath].length; i++) {
+ if (rootKey[aPath][i].name == aName) {
+ if (aValue === null)
+ rootKey[aPath].splice(i, 1);
+ else
+ rootKey[aPath][i].value = aValue;
+ return;
+ }
+ }
+ }
+
+ if (aValue === null)
+ return;
+
+ rootKey[aPath].push({
+ name: aName,
+ value: aValue
+ });
+ }
+ };
+
+ /**
+ * This is a mock nsIWindowsRegistry implementation. It only implements the
+ * methods that the extension manager requires.
+ */
+ function MockWindowsRegKey() {
+ }
+
+ MockWindowsRegKey.prototype = {
+ values: null,
+
+ // --- Overridden nsISupports interface functions ---
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIWindowsRegKey]),
+
+ // --- Overridden nsIWindowsRegKey interface functions ---
+ open: function(aRootKey, aRelPath, aMode) {
+ let rootKey = MockRegistry.getRoot(aRootKey);
+
+ if (!(aRelPath in rootKey))
+ rootKey[aRelPath] = [];
+ this.values = rootKey[aRelPath];
+ },
+
+ close: function() {
+ this.values = null;
+ },
+
+ get valueCount() {
+ if (!this.values)
+ throw Components.results.NS_ERROR_FAILURE;
+ return this.values.length;
+ },
+
+ getValueName: function(aIndex) {
+ if (!this.values || aIndex >= this.values.length)
+ throw Components.results.NS_ERROR_FAILURE;
+ return this.values[aIndex].name;
+ },
+
+ readStringValue: function(aName) {
+ for (let value of this.values) {
+ if (value.name == aName)
+ return value.value;
+ }
+ return null;
+ }
+ };
+
+ var WinRegFactory = {
+ createInstance: function(aOuter, aIid) {
+ if (aOuter != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+
+ var key = new MockWindowsRegKey();
+ return key.QueryInterface(aIid);
+ }
+ };
+
+ var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+ registrar.registerFactory(Components.ID("{0478de5b-0f38-4edb-851d-4c99f1ed8eba}"),
+ "Mock Windows Registry Implementation",
+ "@mozilla.org/windows-registry-key;1", WinRegFactory);
+}
+
+// Get the profile directory for tests to use.
+const gProfD = do_get_profile();
+
+const EXTENSIONS_DB = "extensions.json";
+let gExtensionsJSON = gProfD.clone();
+gExtensionsJSON.append(EXTENSIONS_DB);
+
+const EXTENSIONS_INI = "extensions.ini";
+let gExtensionsINI = gProfD.clone();
+gExtensionsINI.append(EXTENSIONS_INI);
+
+// Enable more extensive EM logging
+Services.prefs.setBoolPref("extensions.logging.enabled", true);
+
+// By default only load extensions from the profile install location
+Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_PROFILE);
+
+// By default don't disable add-ons from any scope
+Services.prefs.setIntPref("extensions.autoDisableScopes", 0);
+
+// By default, don't cache add-ons in AddonRepository.jsm
+Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
+
+// Disable the compatibility updates window by default
+Services.prefs.setBoolPref("extensions.showMismatchUI", false);
+
+// Point update checks to the local machine for fast failures
+Services.prefs.setCharPref("extensions.update.url", "http://127.0.0.1/updateURL");
+Services.prefs.setCharPref("extensions.update.background.url", "http://127.0.0.1/updateBackgroundURL");
+Services.prefs.setCharPref("extensions.blocklist.url", "http://127.0.0.1/blocklistURL");
+
+// By default ignore bundled add-ons
+Services.prefs.setBoolPref("extensions.installDistroAddons", false);
+
+// By default use strict compatibility
+Services.prefs.setBoolPref("extensions.strictCompatibility", true);
+
+// By default, set min compatible versions to 0
+Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0");
+Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, "0");
+
+// Register a temporary directory for the tests.
+const gTmpD = gProfD.clone();
+gTmpD.append("temp");
+gTmpD.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+registerDirectory("TmpD", gTmpD);
+
+// Write out an empty blocklist.xml file to the profile to ensure nothing
+// is blocklisted by default
+var blockFile = gProfD.clone();
+blockFile.append("blocklist.xml");
+var stream = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+stream.init(blockFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+
+var data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">\n" +
+ "</blocklist>\n";
+stream.write(data, data.length);
+stream.close();
+
+// Copies blocklistFile (an nsIFile) to gProfD/blocklist.xml.
+function copyBlocklistToProfile(blocklistFile) {
+ var dest = gProfD.clone();
+ dest.append("blocklist.xml");
+ if (dest.exists())
+ dest.remove(false);
+ blocklistFile.copyTo(gProfD, "blocklist.xml");
+ dest.lastModifiedTime = Date.now();
+}
+
+// Throw a failure and attempt to abandon the test if it looks like it is going
+// to timeout
+function timeout() {
+ timer = null;
+ do_throw("Test ran longer than " + TIMEOUT_MS + "ms");
+
+ // Attempt to bail out of the test
+ do_test_finished();
+}
+
+var timer = AM_Cc["@mozilla.org/timer;1"].createInstance(AM_Ci.nsITimer);
+timer.init(timeout, TIMEOUT_MS, AM_Ci.nsITimer.TYPE_ONE_SHOT);
+
+// Make sure that a given path does not exist
+function pathShouldntExist(aPath) {
+ if (aPath.exists()) {
+ do_throw("Test cleanup: path " + aPath.path + " exists when it should not");
+ }
+}
+
+do_register_cleanup(function addon_cleanup() {
+ if (timer)
+ timer.cancel();
+
+ for (let file of temp_xpis) {
+ if (file.exists())
+ file.remove(false);
+ }
+
+ // Check that the temporary directory is empty
+ var dirEntries = gTmpD.directoryEntries
+ .QueryInterface(AM_Ci.nsIDirectoryEnumerator);
+ var entry;
+ while ((entry = dirEntries.nextFile)) {
+ do_throw("Found unexpected file in temporary directory: " + entry.leafName);
+ }
+ dirEntries.close();
+
+ var testDir = gProfD.clone();
+ testDir.append("extensions");
+ testDir.append("trash");
+ pathShouldntExist(testDir);
+
+ testDir.leafName = "staged";
+ pathShouldntExist(testDir);
+
+ testDir.leafName = "staged-xpis";
+ pathShouldntExist(testDir);
+
+ shutdownManager();
+
+ // Clear commonly set prefs.
+ try {
+ Services.prefs.clearUserPref(PREF_EM_CHECK_UPDATE_SECURITY);
+ } catch (e) {}
+ try {
+ Services.prefs.clearUserPref(PREF_EM_STRICT_COMPATIBILITY);
+ } catch (e) {}
+});
+
+/**
+ * Handler function that responds with the interpolated
+ * static file associated to the URL specified by request.path.
+ * This replaces the %PORT% entries in the file with the actual
+ * value of the running server's port (stored in gPort).
+ */
+function interpolateAndServeFile(request, response) {
+ try {
+ let file = gUrlToFileMap[request.path];
+ var data = "";
+ var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Components.interfaces.nsIConverterInputStream);
+ fstream.init(file, -1, 0, 0);
+ 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);
+ data = data.replace(/%PORT%/g, gPort);
+
+ response.write(data);
+ } catch (e) {
+ do_throw("Exception while serving interpolated file.");
+ } finally {
+ cstream.close(); // this closes fstream as well
+ }
+}
+
+/**
+ * Sets up a path handler for the given URL and saves the
+ * corresponding file in the global url -> file map.
+ *
+ * @param url
+ * the actual URL
+ * @param file
+ * nsILocalFile representing a static file
+ */
+function mapUrlToFile(url, file, server) {
+ server.registerPathHandler(url, interpolateAndServeFile);
+ gUrlToFileMap[url] = file;
+}
+
+function mapFile(path, server) {
+ mapUrlToFile(path, do_get_file(path), server);
+}
+
+/**
+ * Take out the port number in an URL
+ *
+ * @param url
+ * String that represents an URL with a port number in it
+ */
+function remove_port(url) {
+ if (typeof url === "string")
+ return url.replace(/:\d+/, "");
+ return url;
+}
+// Wrap a function (typically a callback) to catch and report exceptions
+function do_exception_wrap(func) {
+ return function() {
+ try {
+ func.apply(null, arguments);
+ }
+ catch(e) {
+ do_report_unexpected_exception(e);
+ }
+ };
+}
+
+/**
+ * Change the schema version of the JSON extensions database
+ */
+function changeXPIDBVersion(aNewVersion) {
+ let jData = loadJSON(gExtensionsJSON);
+ jData.schemaVersion = aNewVersion;
+ saveJSON(jData, gExtensionsJSON);
+}
+
+/**
+ * Load a file into a string
+ */
+function loadFile(aFile) {
+ let data = "";
+ let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Components.interfaces.nsIConverterInputStream);
+ fstream.init(aFile, -1, 0, 0);
+ cstream.init(fstream, "UTF-8", 0, 0);
+ let str = {};
+ let read = 0;
+ do {
+ read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
+ data += str.value;
+ } while (read != 0);
+ cstream.close();
+ return data;
+}
+
+/**
+ * Raw load of a JSON file
+ */
+function loadJSON(aFile) {
+ let data = loadFile(aFile);
+ do_print("Loaded JSON file " + aFile.path);
+ return(JSON.parse(data));
+}
+
+/**
+ * Raw save of a JSON blob to file
+ */
+function saveJSON(aData, aFile) {
+ do_print("Starting to save JSON file " + aFile.path);
+ let stream = FileUtils.openSafeFileOutputStream(aFile);
+ let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(AM_Ci.nsIConverterOutputStream);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ // XXX pretty print the JSON while debugging
+ converter.writeString(JSON.stringify(aData, null, 2));
+ converter.flush();
+ // nsConverterOutputStream doesn't finish() safe output streams on close()
+ FileUtils.closeSafeFileOutputStream(stream);
+ converter.close();
+ do_print("Done saving JSON file " + aFile.path);
+}
+
+/**
+ * Create a callback function that calls do_execute_soon on an actual callback and arguments
+ */
+function callback_soon(aFunction) {
+ return function(...args) {
+ do_execute_soon(function() {
+ aFunction.apply(null, args);
+ }, aFunction.name ? "delayed callback " + aFunction.name : "delayed callback");
+ }
+}
+
+/**
+ * A promise-based variant of AddonManager.getAddonsByIDs.
+ *
+ * @param {array} list As the first argument of AddonManager.getAddonsByIDs
+ * @return {promise}
+ * @resolve {array} The list of add-ons sent by AddonManaget.getAddonsByIDs to
+ * its callback.
+ */
+function promiseAddonsByIDs(list) {
+ return new Promise(resolve => AddonManager.getAddonsByIDs(list, resolve));
+}
+
+/**
+ * A promise-based variant of AddonManager.getAddonByID.
+ *
+ * @param {string} aId The ID of the add-on.
+ * @return {promise}
+ * @resolve {AddonWrapper} The corresponding add-on, or null.
+ */
+function promiseAddonByID(aId) {
+ return new Promise(resolve => AddonManager.getAddonByID(aId, resolve));
+}
+
+/**
+ * A promise-based variant of AddonManager.getAddonsWithOperationsByTypes
+ *
+ * @param {array} aTypes The first argument to
+ * AddonManager.getAddonsWithOperationsByTypes
+ * @return {promise}
+ * @resolve {array} The list of add-ons sent by
+ * AddonManaget.getAddonsWithOperationsByTypes to its callback.
+ */
+function promiseAddonsWithOperationsByTypes(aTypes) {
+ return new Promise(resolve => AddonManager.getAddonsWithOperationsByTypes(aTypes, resolve));
+}
+
+/**
+ * Returns a promise that will be resolved when an add-on update check is
+ * complete. The value resolved will be an AddonInstall if a new version was
+ * found.
+ */
+function promiseFindAddonUpdates(addon, reason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
+ return new Promise((resolve, reject) => {
+ addon.findUpdates({
+ install: null,
+
+ onUpdateAvailable: function(addon, install) {
+ this.install = install;
+ },
+
+ onUpdateFinished: function(addon, error) {
+ if (error == AddonManager.UPDATE_STATUS_NO_ERROR)
+ resolve(this.install);
+ else
+ reject(error);
+ }
+ }, reason);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_unpack.js b/toolkit/mozapps/extensions/test/xpcshell/head_unpack.js
new file mode 100644
index 000000000..088898b41
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_unpack.js
@@ -0,0 +1,2 @@
+Services.prefs.setBoolPref("extensions.alwaysUnpack", true);
+TEST_UNPACKED = true;
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
new file mode 100644
index 000000000..3f51d7226
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
@@ -0,0 +1,625 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests AddonRepository.jsm
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+
+const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
+const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL";
+const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
+const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+
+const PORT = gServer.identity.primaryPort;
+const BASE_URL = "http://localhost:" + PORT;
+const DEFAULT_URL = "about:blank";
+
+gPort = PORT;
+
+// Path to source URI of installed add-on
+const INSTALL_URL1 = "/addons/test_AddonRepository_1.xpi";
+// Path to source URI of installing add-on
+const INSTALL_URL2 = "/addons/test_AddonRepository_2.xpi";
+// Path to source URI of non-active add-on (state = STATE_AVAILABLE)
+const INSTALL_URL3 = "/addons/test_AddonRepository_3.xpi";
+
+// Properties of an individual add-on that should be checked
+// Note: name is checked separately
+var ADDON_PROPERTIES = ["id", "type", "version", "creator", "developers",
+ "description", "fullDescription", "developerComments",
+ "eula", "iconURL", "icons", "screenshots", "homepageURL",
+ "supportURL", "contributionURL", "contributionAmount",
+ "averageRating", "reviewCount", "reviewURL",
+ "totalDownloads", "weeklyDownloads", "dailyUsers",
+ "sourceURI", "repositoryStatus", "size", "updateDate",
+ "purchaseURL", "purchaseAmount", "purchaseDisplayAmount",
+ "compatibilityOverrides"];
+
+// Results of getAddonsByIDs
+var GET_RESULTS = [{
+ id: "test1@tests.mozilla.org",
+ type: "extension",
+ version: "1.1",
+ creator: {
+ name: "Test Creator 1",
+ url: BASE_URL + "/creator1.html"
+ },
+ developers: [{
+ name: "Test Developer 1",
+ url: BASE_URL + "/developer1.html"
+ }],
+ description: "Test Summary 1",
+ fullDescription: "Test Description 1",
+ developerComments: "Test Developer Comments 1",
+ eula: "Test EULA 1",
+ iconURL: BASE_URL + "/icon1.png",
+ icons: { "32": BASE_URL + "/icon1.png" },
+ screenshots: [{
+ url: BASE_URL + "/full1-1.png",
+ width: 400,
+ height: 300,
+ thumbnailURL: BASE_URL + "/thumbnail1-1.png",
+ thumbnailWidth: 200,
+ thumbnailHeight: 150,
+ caption: "Caption 1 - 1"
+ }, {
+ url: BASE_URL + "/full2-1.png",
+ thumbnailURL: BASE_URL + "/thumbnail2-1.png",
+ caption: "Caption 2 - 1"
+ }],
+ homepageURL: BASE_URL + "/learnmore1.html",
+ learnmoreURL: BASE_URL + "/learnmore1.html",
+ supportURL: BASE_URL + "/support1.html",
+ contributionURL: BASE_URL + "/meetDevelopers1.html",
+ contributionAmount: "$11.11",
+ averageRating: 4,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/review1.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3333,
+ dailyUsers: 4444,
+ sourceURI: BASE_URL + INSTALL_URL2,
+ repositoryStatus: 8,
+ size: 5555,
+ updateDate: new Date(1265033045000),
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: "test_AddonRepository_1@tests.mozilla.org",
+ type: "theme",
+ version: "1.4",
+ repositoryStatus: 9999,
+ icons: {}
+}];
+
+// Results of retrieveRecommendedAddons and searchAddons
+var SEARCH_RESULTS = [{
+ id: "test1@tests.mozilla.org",
+ type: "extension",
+ version: "1.1",
+ creator: {
+ name: "Test Creator 1",
+ url: BASE_URL + "/creator1.html"
+ },
+ repositoryStatus: 8,
+ sourceURI: BASE_URL + "/test1.xpi",
+ icons: {}
+}, {
+ id: "test2@tests.mozilla.org",
+ type: "extension",
+ version: "1.2",
+ creator: {
+ name: "Test Creator 2",
+ url: BASE_URL + "/creator2.html"
+ },
+ developers: [{
+ name: "Test Developer 2",
+ url: BASE_URL + "/developer2.html"
+ }],
+ description: "Test Summary 2\n\nparagraph",
+ fullDescription: "Test Description 2\nnewline",
+ developerComments: "Test Developer\nComments 2",
+ eula: "Test EULA 2",
+ iconURL: BASE_URL + "/icon2-32.png",
+ icons: {
+ "32": BASE_URL + "/icon2-32.png",
+ "48": BASE_URL + "/icon2-48.png",
+ "64": BASE_URL + "/icon2-64.png"
+ },
+ screenshots: [{
+ url: BASE_URL + "/full1-2.png",
+ thumbnailURL: BASE_URL + "/thumbnail1-2.png"
+ }, {
+ url: BASE_URL + "/full2-2.png",
+ thumbnailURL: BASE_URL + "/thumbnail2-2.png",
+ caption: "Caption 2"
+ }],
+ homepageURL: BASE_URL + "/learnmore2.html",
+ supportURL: BASE_URL + "/support2.html",
+ learnmoreURL: BASE_URL + "/learnmore2.html",
+ contributionURL: BASE_URL + "/meetDevelopers2.html",
+ contributionAmount: null,
+ repositoryStatus: 4,
+ sourceURI: BASE_URL + "/test2.xpi"
+}, {
+ id: "test3@tests.mozilla.org",
+ type: "theme",
+ version: "1.3",
+ creator: {
+ name: "Test Creator 3",
+ url: BASE_URL + "/creator3.html"
+ },
+ developers: [{
+ name: "First Test Developer 3",
+ url: BASE_URL + "/developer1-3.html"
+ }, {
+ name: "Second Test Developer 3",
+ url: BASE_URL + "/developer2-3.html"
+ }],
+ description: "Test Summary 3",
+ fullDescription: "Test Description 3\n\n List item 1\n List item 2",
+ developerComments: "Test Developer Comments 3",
+ eula: "Test EULA 3",
+ iconURL: BASE_URL + "/icon3.png",
+ icons: { "32": BASE_URL + "/icon3.png" },
+ screenshots: [{
+ url: BASE_URL + "/full1-3.png",
+ thumbnailURL: BASE_URL + "/thumbnail1-3.png",
+ caption: "Caption 1 - 3"
+ }, {
+ url: BASE_URL + "/full2-3.png",
+ caption: "Caption 2 - 3"
+ }, {
+ url: BASE_URL + "/full3-3.png",
+ thumbnailURL: BASE_URL + "/thumbnail3-3.png",
+ caption: "Caption 3 - 3"
+ }],
+ homepageURL: BASE_URL + "/homepage3.html",
+ supportURL: BASE_URL + "/support3.html",
+ learnmoreURL: BASE_URL + "/learnmore3.html",
+ contributionURL: BASE_URL + "/meetDevelopers3.html",
+ contributionAmount: "$11.11",
+ averageRating: 2,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/review3.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3333,
+ dailyUsers: 4444,
+ sourceURI: BASE_URL + "/test3.xpi",
+ repositoryStatus: 8,
+ size: 5555,
+ updateDate: new Date(1265033045000),
+
+}, {
+ id: "purchase1@tests.mozilla.org",
+ type: "extension",
+ version: "2.0",
+ creator: {
+ name: "Test Creator - Last Passing",
+ url: BASE_URL + "/creatorLastPassing.html"
+ },
+ averageRating: 5,
+ repositoryStatus: 4,
+ purchaseURL: "http://localhost:" + PORT + "/purchaseURL1",
+ purchaseAmount: 5,
+ purchaseDisplayAmount: "$5",
+ icons: {}
+}, {
+ id: "purchase2@tests.mozilla.org",
+ type: "extension",
+ version: "2.0",
+ creator: {
+ name: "Test Creator - Last Passing",
+ url: BASE_URL + "/creatorLastPassing.html"
+ },
+ averageRating: 5,
+ repositoryStatus: 4,
+ purchaseURL: "http://localhost:" + PORT + "/purchaseURL2",
+ purchaseAmount: 10,
+ purchaseDisplayAmount: "$10",
+ icons: {}
+}, {
+ id: "test-lastPassing@tests.mozilla.org",
+ type: "extension",
+ version: "2.0",
+ creator: {
+ name: "Test Creator - Last Passing",
+ url: BASE_URL + "/creatorLastPassing.html"
+ },
+ averageRating: 5,
+ repositoryStatus: 4,
+ sourceURI: BASE_URL + "/addons/test_AddonRepository_3.xpi",
+ icons: {}
+}];
+
+const TOTAL_RESULTS = 1111;
+const MAX_RESULTS = SEARCH_RESULTS.length;
+
+// Used to differentiate between testing that a search success
+// or a search failure for retrieveRecommendedAddons and searchAddons
+const FAILED_MAX_RESULTS = 9999;
+
+// Values for testing AddonRepository.getAddonsByIDs()
+var GET_TEST = {
+ preference: PREF_GETADDONS_BYIDS,
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%/%API_VERSION%/" +
+ "%API_VERSION%/%IDS%",
+ failedIDs: ["test1@tests.mozilla.org"],
+ failedURL: "/XPCShell/1/1.5/1.5/test1%40tests.mozilla.org",
+ successfulIDs: ["test1@tests.mozilla.org",
+ "{00000000-1111-2222-3333-444444444444}",
+ "test_AddonRepository_1@tests.mozilla.org"],
+ successfulURL: "/XPCShell/1/1.5/1.5/test1%40tests.mozilla.org," +
+ "%7B00000000-1111-2222-3333-444444444444%7D," +
+ "test_AddonRepository_1%40tests.mozilla.org"
+};
+
+// Values for testing AddonRepository.retrieveRecommendedAddons()
+var RECOMMENDED_TEST = {
+ preference: PREF_GETADDONS_GETRECOMMENDED,
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%/%API_VERSION%/" +
+ "%API_VERSION%/%MAX_RESULTS%",
+ failedURL: "/XPCShell/1/1.5/1.5/" + (2 * FAILED_MAX_RESULTS),
+ successfulURL: "/XPCShell/1/1.5/1.5/" + (2 * MAX_RESULTS)
+};
+
+// Values for testing AddonRepository.searchAddons()
+var SEARCH_TEST = {
+ searchTerms: "odd=search:with&weird\"characters",
+ preference: PREF_GETADDONS_GETSEARCHRESULTS,
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%/%API_VERSION%/" +
+ "%API_VERSION%/%MAX_RESULTS%/%TERMS%",
+ failedURL: "/XPCShell/1/1.5/1.5/" + (2 * FAILED_MAX_RESULTS) +
+ "/odd%3Dsearch%3Awith%26weird%22characters",
+ successfulURL: "/XPCShell/1/1.5/1.5/" + (2 * MAX_RESULTS) +
+ "/odd%3Dsearch%3Awith%26weird%22characters"
+};
+
+// Test that actual results and expected results are equal
+function check_results(aActualAddons, aExpectedAddons, aAddonCount, aInstallNull) {
+ do_check_false(AddonRepository.isSearching);
+
+ do_check_eq(aActualAddons.length, aAddonCount);
+ do_check_addons(aActualAddons, aExpectedAddons, ADDON_PROPERTIES);
+
+ // Additional tests
+ aActualAddons.forEach(function check_each_addon(aActualAddon) {
+ // Separately check name so better messages are output when test fails
+ if (aActualAddon.name == "FAIL")
+ do_throw(aActualAddon.id + " - " + aActualAddon.description);
+ if (aActualAddon.name != "PASS")
+ do_throw(aActualAddon.id + " - " + "invalid add-on name " + aActualAddon.name);
+
+ do_check_eq(aActualAddon.install == null, !!aInstallNull || !aActualAddon.sourceURI);
+
+ // Check that sourceURI property consistent within actual addon
+ if (aActualAddon.install)
+ do_check_eq(aActualAddon.install.sourceURI.spec, aActualAddon.sourceURI.spec);
+ });
+}
+
+// Complete a search, also testing cancelSearch() and isSearching
+function complete_search(aSearch, aSearchCallback) {
+ var failCallback = {
+ searchSucceeded: function(addons, length, total) {
+ do_throw("failCallback.searchSucceeded should not be called");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_throw("failCallback.searchFailed should not be called");
+ end_test();
+ }
+ };
+
+ var callbackCalled = false;
+ var testCallback = {
+ searchSucceeded: function(addons, length, total) {
+ do_throw("testCallback.searchSucceeded should not be called");
+ end_test();
+ },
+
+ searchFailed: function() {
+ callbackCalled = true;
+ }
+ };
+
+ // Should fail because cancelled it immediately
+ aSearch(failCallback);
+ do_check_true(AddonRepository.isSearching);
+ AddonRepository.cancelSearch();
+ do_check_false(AddonRepository.isSearching);
+
+ aSearch(aSearchCallback);
+ do_check_true(AddonRepository.isSearching);
+
+ // searchFailed should be called immediately because already searching
+ aSearch(testCallback);
+ do_check_true(callbackCalled);
+ do_check_true(AddonRepository.isSearching);
+}
+
+
+function run_test() {
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ startupManager();
+
+ // Install an add-on so can check that it isn't returned in the results
+ installAllFiles([do_get_addon("test_AddonRepository_1")], function addon_1_install_callback() {
+ restartManager();
+
+ // Register other add-on XPI files
+ gServer.registerFile(INSTALL_URL2,
+ do_get_addon("test_AddonRepository_2"));
+ gServer.registerFile(INSTALL_URL3,
+ do_get_addon("test_AddonRepository_3"));
+
+ // Register files used to test search failure
+ mapUrlToFile(GET_TEST.failedURL,
+ do_get_file("data/test_AddonRepository_failed.xml"),
+ gServer);
+ mapUrlToFile(RECOMMENDED_TEST.failedURL,
+ do_get_file("data/test_AddonRepository_failed.xml"),
+ gServer);
+ mapUrlToFile(SEARCH_TEST.failedURL,
+ do_get_file("data/test_AddonRepository_failed.xml"),
+ gServer);
+
+ // Register files used to test search success
+ mapUrlToFile(GET_TEST.successfulURL,
+ do_get_file("data/test_AddonRepository_getAddonsByIDs.xml"),
+ gServer);
+ mapUrlToFile(RECOMMENDED_TEST.successfulURL,
+ do_get_file("data/test_AddonRepository.xml"),
+ gServer);
+ mapUrlToFile(SEARCH_TEST.successfulURL,
+ do_get_file("data/test_AddonRepository.xml"),
+ gServer);
+
+ // Create an active AddonInstall so can check that it isn't returned in the results
+ AddonManager.getInstallForURL(BASE_URL + INSTALL_URL2, function addon_2_get(aInstall) {
+ try {
+ aInstall.install();
+ }
+ catch(e) {
+ do_print("Failed to install add-on " + aInstall.sourceURI.spec);
+ do_report_unexpected_exception(e);
+ }
+
+ // Create a non-active AddonInstall so can check that it is returned in the results
+ AddonManager.getInstallForURL(BASE_URL + INSTALL_URL3,
+ run_test_1, "application/x-xpinstall");
+ }, "application/x-xpinstall");
+ });
+}
+
+function end_test() {
+ let testDir = gProfD.clone();
+ testDir.append("extensions");
+ testDir.append("staged");
+ gServer.stop(function() {
+ function loop() {
+ if (!testDir.exists()) {
+ do_print("Staged directory has been cleaned up");
+ do_test_finished();
+ }
+ do_print("Waiting 1 second until cleanup is complete");
+ do_timeout(1000, loop);
+ }
+ loop();
+ });
+}
+
+// Tests homepageURL, getRecommendedURL() and getSearchURL()
+function run_test_1() {
+ function check_urls(aPreference, aGetURL, aTests) {
+ aTests.forEach(function(aTest) {
+ Services.prefs.setCharPref(aPreference, aTest.preferenceValue);
+ do_check_eq(aGetURL(aTest), aTest.expectedURL);
+ });
+ }
+
+ var urlTests = [{
+ preferenceValue: BASE_URL,
+ expectedURL: BASE_URL
+ }, {
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%",
+ expectedURL: BASE_URL + "/XPCShell/1"
+ }];
+
+ // Extra tests for AddonRepository.getSearchURL();
+ var searchURLTests = [{
+ searchTerms: "test",
+ preferenceValue: BASE_URL + "/search?q=%TERMS%",
+ expectedURL: BASE_URL + "/search?q=test"
+ }, {
+ searchTerms: "test search",
+ preferenceValue: BASE_URL + "/%TERMS%",
+ expectedURL: BASE_URL + "/test%20search"
+ }, {
+ searchTerms: "odd=search:with&weird\"characters",
+ preferenceValue: BASE_URL + "/%TERMS%",
+ expectedURL: BASE_URL + "/odd%3Dsearch%3Awith%26weird%22characters"
+ }];
+
+ // Setup tests for homepageURL, getRecommendedURL() and getSearchURL()
+ var tests = [{
+ initiallyUndefined: true,
+ preference: PREF_GETADDONS_BROWSEADDONS,
+ urlTests: urlTests,
+ getURL: function() AddonRepository.homepageURL
+ }, {
+ initiallyUndefined: true,
+ preference: PREF_GETADDONS_BROWSERECOMMENDED,
+ urlTests: urlTests,
+ getURL: function() AddonRepository.getRecommendedURL()
+ }, {
+ initiallyUndefined: false,
+ preference: PREF_GETADDONS_BROWSESEARCHRESULTS,
+ urlTests: urlTests.concat(searchURLTests),
+ getURL: function getSearchURL(aTest) {
+ var searchTerms = aTest && aTest.searchTerms ? aTest.searchTerms
+ : "unused terms";
+ return AddonRepository.getSearchURL(searchTerms);
+ }
+ }];
+
+ tests.forEach(function url_test(aTest) {
+ if (aTest.initiallyUndefined) {
+ // Preference is not defined by default
+ do_check_eq(Services.prefs.getPrefType(aTest.preference),
+ Services.prefs.PREF_INVALID);
+ do_check_eq(aTest.getURL(), DEFAULT_URL);
+ }
+
+ check_urls(aTest.preference, aTest.getURL, aTest.urlTests);
+ });
+
+ run_test_getAddonsByID_fails();
+}
+
+// Tests failure of AddonRepository.getAddonsByIDs()
+function run_test_getAddonsByID_fails() {
+ Services.prefs.setCharPref(GET_TEST.preference, GET_TEST.preferenceValue);
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_throw("searchAddons should not have succeeded");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_check_false(AddonRepository.isSearching);
+ run_test_getAddonsByID_succeeds();
+ }
+ };
+
+ complete_search(function complete_search_fail_callback(aCallback) {
+ AddonRepository.getAddonsByIDs(GET_TEST.failedIDs, aCallback);
+ }, callback);
+}
+
+// Tests success of AddonRepository.getAddonsByIDs()
+function run_test_getAddonsByID_succeeds() {
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_check_eq(aTotalResults, -1);
+ check_results(aAddonsList, GET_RESULTS, aAddonCount, true);
+ run_test_retrieveRecommended_fails();
+ },
+
+ searchFailed: function() {
+ do_throw("searchAddons should not have failed");
+ end_test();
+ }
+ };
+
+ complete_search(function complete_search_succeed_callback(aCallback) {
+ AddonRepository.getAddonsByIDs(GET_TEST.successfulIDs, aCallback);
+ }, callback);
+}
+
+// Tests failure of AddonRepository.retrieveRecommendedAddons()
+function run_test_retrieveRecommended_fails() {
+ Services.prefs.setCharPref(RECOMMENDED_TEST.preference,
+ RECOMMENDED_TEST.preferenceValue);
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_throw("retrieveRecommendedAddons should not have succeeded");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_check_false(AddonRepository.isSearching);
+ run_test_retrieveRecommended_succeed();
+ }
+ };
+
+ complete_search(function retrieveRecommended_failing_callback(aCallback) {
+ AddonRepository.retrieveRecommendedAddons(FAILED_MAX_RESULTS, aCallback);
+ }, callback);
+}
+
+// Tests success of AddonRepository.retrieveRecommendedAddons()
+function run_test_retrieveRecommended_succeed() {
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_check_eq(aTotalResults, -1);
+ check_results(aAddonsList, SEARCH_RESULTS, aAddonCount);
+ run_test_searchAddons_fails();
+ },
+
+ searchFailed: function() {
+ do_throw("retrieveRecommendedAddons should not have failed");
+ end_test();
+ }
+ };
+
+ complete_search(function retrieveRecommended_succeed_callback(aCallback) {
+ AddonRepository.retrieveRecommendedAddons(MAX_RESULTS, aCallback);
+ }, callback);
+}
+
+// Tests failure of AddonRepository.searchAddons()
+function run_test_searchAddons_fails() {
+ Services.prefs.setCharPref(SEARCH_TEST.preference, SEARCH_TEST.preferenceValue);
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_throw("searchAddons should not have succeeded");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_check_false(AddonRepository.isSearching);
+ run_test_searchAddons_succeeds();
+ }
+ };
+
+ complete_search(function(aCallback) {
+ var searchTerms = SEARCH_TEST.searchTerms;
+ AddonRepository.searchAddons(searchTerms, FAILED_MAX_RESULTS, aCallback);
+ }, callback);
+}
+
+// Tests success of AddonRepository.searchAddons()
+function run_test_searchAddons_succeeds() {
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_check_eq(aTotalResults, TOTAL_RESULTS);
+ check_results(aAddonsList, SEARCH_RESULTS, aAddonCount);
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_throw("searchAddons should not have failed");
+ end_test();
+ }
+ };
+
+ complete_search(function(aCallback) {
+ var searchTerms = SEARCH_TEST.searchTerms;
+ AddonRepository.searchAddons(searchTerms, MAX_RESULTS, aCallback);
+ }, callback);
+}
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
new file mode 100644
index 000000000..0327ab6d0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
@@ -0,0 +1,710 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests caching in AddonRepository.jsm
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+Components.utils.import("resource://testing-common/httpd.js");
+let gServer;
+
+const PORT = 4444;
+const BASE_URL = "http://localhost:" + PORT;
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+const PREF_GETADDONS_CACHE_TYPES = "extensions.getAddons.cache.types";
+const GETADDONS_RESULTS = BASE_URL + "/data/test_AddonRepository_cache.xml";
+const GETADDONS_EMPTY = BASE_URL + "/data/test_AddonRepository_empty.xml";
+const GETADDONS_FAILED = BASE_URL + "/data/test_AddonRepository_failed.xml";
+
+const FILE_DATABASE = "addons.json";
+const ADDON_NAMES = ["test_AddonRepository_1",
+ "test_AddonRepository_2",
+ "test_AddonRepository_3"];
+const ADDON_IDS = ADDON_NAMES.map(function(aName) aName + "@tests.mozilla.org");
+const ADDON_FILES = ADDON_NAMES.map(do_get_addon);
+
+const PREF_ADDON0_CACHE_ENABLED = "extensions." + ADDON_IDS[0] + ".getAddons.cache.enabled";
+const PREF_ADDON1_CACHE_ENABLED = "extensions." + ADDON_IDS[1] + ".getAddons.cache.enabled";
+
+// Properties of an individual add-on that should be checked
+// Note: size and updateDate are checked separately
+const ADDON_PROPERTIES = ["id", "type", "name", "version", "creator",
+ "developers", "translators", "contributors",
+ "description", "fullDescription",
+ "developerComments", "eula", "iconURL", "icons",
+ "screenshots", "homepageURL", "supportURL",
+ "optionsURL", "aboutURL", "contributionURL",
+ "contributionAmount", "averageRating", "reviewCount",
+ "reviewURL", "totalDownloads", "weeklyDownloads",
+ "dailyUsers", "sourceURI", "repositoryStatus",
+ "compatibilityOverrides"];
+
+// The size and updateDate properties are annoying to test for XPI add-ons.
+// However, since we only care about whether the repository value vs. the
+// XPI value is used, we can just test if the property value matches
+// the repository value
+const REPOSITORY_SIZE = 9;
+const REPOSITORY_UPDATEDATE = 9;
+
+// Get the URI of a subfile locating directly in the folder of
+// the add-on corresponding to the specified id
+function get_subfile_uri(aId, aFilename) {
+ let file = gProfD.clone();
+ file.append("extensions");
+ return do_get_addon_root_uri(file, aId) + aFilename;
+}
+
+
+// Expected repository add-ons
+const REPOSITORY_ADDONS = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "Repo Add-on 1",
+ version: "2.1",
+ creator: {
+ name: "Repo Add-on 1 - Creator",
+ url: BASE_URL + "/repo/1/creator.html"
+ },
+ developers: [{
+ name: "Repo Add-on 1 - First Developer",
+ url: BASE_URL + "/repo/1/firstDeveloper.html"
+ }, {
+ name: "Repo Add-on 1 - Second Developer",
+ url: BASE_URL + "/repo/1/secondDeveloper.html"
+ }],
+ description: "Repo Add-on 1 - Description\nSecond line",
+ fullDescription: "Repo Add-on 1 - Full Description & some extra",
+ developerComments: "Repo Add-on 1\nDeveloper Comments",
+ eula: "Repo Add-on 1 - EULA",
+ iconURL: BASE_URL + "/repo/1/icon.png",
+ icons: { "32": BASE_URL + "/repo/1/icon.png" },
+ homepageURL: BASE_URL + "/repo/1/homepage.html",
+ supportURL: BASE_URL + "/repo/1/support.html",
+ contributionURL: BASE_URL + "/repo/1/meetDevelopers.html",
+ contributionAmount: "$11.11",
+ averageRating: 1,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/repo/1/review.html",
+ totalDownloads: 2221,
+ weeklyDownloads: 3331,
+ dailyUsers: 4441,
+ sourceURI: BASE_URL + "/repo/1/install.xpi",
+ repositoryStatus: 4,
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "Repo Add-on 2",
+ version: "2.2",
+ creator: {
+ name: "Repo Add-on 2 - Creator",
+ url: BASE_URL + "/repo/2/creator.html"
+ },
+ developers: [{
+ name: "Repo Add-on 2 - First Developer",
+ url: BASE_URL + "/repo/2/firstDeveloper.html"
+ }, {
+ name: "Repo Add-on 2 - Second Developer",
+ url: BASE_URL + "/repo/2/secondDeveloper.html"
+ }],
+ description: "Repo Add-on 2 - Description",
+ fullDescription: "Repo Add-on 2 - Full Description",
+ developerComments: "Repo Add-on 2 - Developer Comments",
+ eula: "Repo Add-on 2 - EULA",
+ iconURL: BASE_URL + "/repo/2/icon.png",
+ icons: { "32": BASE_URL + "/repo/2/icon.png" },
+ screenshots: [{
+ url: BASE_URL + "/repo/2/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/firstThumbnail.png",
+ caption: "Repo Add-on 2 - First Caption"
+ } , {
+ url: BASE_URL + "/repo/2/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/secondThumbnail.png",
+ caption: "Repo Add-on 2 - Second Caption"
+ }],
+ homepageURL: BASE_URL + "/repo/2/homepage.html",
+ supportURL: BASE_URL + "/repo/2/support.html",
+ contributionURL: BASE_URL + "/repo/2/meetDevelopers.html",
+ contributionAmount: null,
+ averageRating: 2,
+ reviewCount: 1112,
+ reviewURL: BASE_URL + "/repo/2/review.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3332,
+ dailyUsers: 4442,
+ sourceURI: BASE_URL + "/repo/2/install.xpi",
+ repositoryStatus: 9
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "Repo Add-on 3",
+ version: "2.3",
+ iconURL: BASE_URL + "/repo/3/icon.png",
+ icons: { "32": BASE_URL + "/repo/3/icon.png" },
+ screenshots: [{
+ url: BASE_URL + "/repo/3/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/firstThumbnail.png",
+ caption: "Repo Add-on 3 - First Caption"
+ } , {
+ url: BASE_URL + "/repo/3/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/secondThumbnail.png",
+ caption: "Repo Add-on 3 - Second Caption"
+ }]
+}];
+
+
+// Expected add-ons when not using cache
+const WITHOUT_CACHE = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "XPI Add-on 1",
+ version: "1.1",
+ creator: { name: "XPI Add-on 1 - Creator" },
+ developers: [{ name: "XPI Add-on 1 - First Developer" },
+ { name: "XPI Add-on 1 - Second Developer" }],
+ translators: [{ name: "XPI Add-on 1 - First Translator" },
+ { name: "XPI Add-on 1 - Second Translator" }],
+ contributors: [{ name: "XPI Add-on 1 - First Contributor" },
+ { name: "XPI Add-on 1 - Second Contributor" }],
+ description: "XPI Add-on 1 - Description",
+ iconURL: BASE_URL + "/xpi/1/icon.png",
+ icons: { "32": BASE_URL + "/xpi/1/icon.png" },
+ homepageURL: BASE_URL + "/xpi/1/homepage.html",
+ optionsURL: BASE_URL + "/xpi/1/options.html",
+ aboutURL: BASE_URL + "/xpi/1/about.html",
+ sourceURI: NetUtil.newURI(ADDON_FILES[0]).spec
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "XPI Add-on 2",
+ version: "1.2",
+ sourceURI: NetUtil.newURI(ADDON_FILES[1]).spec,
+ icons: {}
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "XPI Add-on 3",
+ version: "1.3",
+ get iconURL () {
+ return get_subfile_uri(ADDON_IDS[2], "icon.png");
+ },
+ get icons () {
+ return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
+ },
+ screenshots: [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }],
+ sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec
+}];
+
+
+// Expected add-ons when using cache
+const WITH_CACHE = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "XPI Add-on 1",
+ version: "1.1",
+ creator: {
+ name: "Repo Add-on 1 - Creator",
+ url: BASE_URL + "/repo/1/creator.html"
+ },
+ developers: [{ name: "XPI Add-on 1 - First Developer" },
+ { name: "XPI Add-on 1 - Second Developer" }],
+ translators: [{ name: "XPI Add-on 1 - First Translator" },
+ { name: "XPI Add-on 1 - Second Translator" }],
+ contributors: [{ name: "XPI Add-on 1 - First Contributor" },
+ { name: "XPI Add-on 1 - Second Contributor" }],
+ description: "XPI Add-on 1 - Description",
+ fullDescription: "Repo Add-on 1 - Full Description & some extra",
+ developerComments: "Repo Add-on 1\nDeveloper Comments",
+ eula: "Repo Add-on 1 - EULA",
+ iconURL: BASE_URL + "/xpi/1/icon.png",
+ icons: { "32": BASE_URL + "/xpi/1/icon.png" },
+ homepageURL: BASE_URL + "/xpi/1/homepage.html",
+ supportURL: BASE_URL + "/repo/1/support.html",
+ optionsURL: BASE_URL + "/xpi/1/options.html",
+ aboutURL: BASE_URL + "/xpi/1/about.html",
+ contributionURL: BASE_URL + "/repo/1/meetDevelopers.html",
+ contributionAmount: "$11.11",
+ averageRating: 1,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/repo/1/review.html",
+ totalDownloads: 2221,
+ weeklyDownloads: 3331,
+ dailyUsers: 4441,
+ sourceURI: NetUtil.newURI(ADDON_FILES[0]).spec,
+ repositoryStatus: 4,
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "XPI Add-on 2",
+ version: "1.2",
+ creator: {
+ name: "Repo Add-on 2 - Creator",
+ url: BASE_URL + "/repo/2/creator.html"
+ },
+ developers: [{
+ name: "Repo Add-on 2 - First Developer",
+ url: BASE_URL + "/repo/2/firstDeveloper.html"
+ }, {
+ name: "Repo Add-on 2 - Second Developer",
+ url: BASE_URL + "/repo/2/secondDeveloper.html"
+ }],
+ description: "Repo Add-on 2 - Description",
+ fullDescription: "Repo Add-on 2 - Full Description",
+ developerComments: "Repo Add-on 2 - Developer Comments",
+ eula: "Repo Add-on 2 - EULA",
+ iconURL: BASE_URL + "/repo/2/icon.png",
+ icons: { "32": BASE_URL + "/repo/2/icon.png" },
+ screenshots: [{
+ url: BASE_URL + "/repo/2/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/firstThumbnail.png",
+ caption: "Repo Add-on 2 - First Caption"
+ } , {
+ url: BASE_URL + "/repo/2/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/secondThumbnail.png",
+ caption: "Repo Add-on 2 - Second Caption"
+ }],
+ homepageURL: BASE_URL + "/repo/2/homepage.html",
+ supportURL: BASE_URL + "/repo/2/support.html",
+ contributionURL: BASE_URL + "/repo/2/meetDevelopers.html",
+ contributionAmount: null,
+ averageRating: 2,
+ reviewCount: 1112,
+ reviewURL: BASE_URL + "/repo/2/review.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3332,
+ dailyUsers: 4442,
+ sourceURI: NetUtil.newURI(ADDON_FILES[1]).spec,
+ repositoryStatus: 9
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "XPI Add-on 3",
+ version: "1.3",
+ get iconURL () {
+ return get_subfile_uri(ADDON_IDS[2], "icon.png");
+ },
+ get icons () {
+ return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
+ },
+ screenshots: [{
+ url: BASE_URL + "/repo/3/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/firstThumbnail.png",
+ caption: "Repo Add-on 3 - First Caption"
+ } , {
+ url: BASE_URL + "/repo/3/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/secondThumbnail.png",
+ caption: "Repo Add-on 3 - Second Caption"
+ }],
+ sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec
+}];
+
+// Expected add-ons when using cache
+const WITH_EXTENSION_CACHE = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "XPI Add-on 1",
+ version: "1.1",
+ creator: {
+ name: "Repo Add-on 1 - Creator",
+ url: BASE_URL + "/repo/1/creator.html"
+ },
+ developers: [{ name: "XPI Add-on 1 - First Developer" },
+ { name: "XPI Add-on 1 - Second Developer" }],
+ translators: [{ name: "XPI Add-on 1 - First Translator" },
+ { name: "XPI Add-on 1 - Second Translator" }],
+ contributors: [{ name: "XPI Add-on 1 - First Contributor" },
+ { name: "XPI Add-on 1 - Second Contributor" }],
+ description: "XPI Add-on 1 - Description",
+ fullDescription: "Repo Add-on 1 - Full Description & some extra",
+ developerComments: "Repo Add-on 1\nDeveloper Comments",
+ eula: "Repo Add-on 1 - EULA",
+ iconURL: BASE_URL + "/xpi/1/icon.png",
+ icons: { "32": BASE_URL + "/xpi/1/icon.png" },
+ homepageURL: BASE_URL + "/xpi/1/homepage.html",
+ supportURL: BASE_URL + "/repo/1/support.html",
+ optionsURL: BASE_URL + "/xpi/1/options.html",
+ aboutURL: BASE_URL + "/xpi/1/about.html",
+ contributionURL: BASE_URL + "/repo/1/meetDevelopers.html",
+ contributionAmount: "$11.11",
+ averageRating: 1,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/repo/1/review.html",
+ totalDownloads: 2221,
+ weeklyDownloads: 3331,
+ dailyUsers: 4441,
+ sourceURI: NetUtil.newURI(ADDON_FILES[0]).spec,
+ repositoryStatus: 4,
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "XPI Add-on 2",
+ version: "1.2",
+ sourceURI: NetUtil.newURI(ADDON_FILES[1]).spec,
+ icons: {}
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "XPI Add-on 3",
+ version: "1.3",
+ get iconURL () {
+ return get_subfile_uri(ADDON_IDS[2], "icon.png");
+ },
+ get icons () {
+ return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
+ },
+ screenshots: [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }],
+ sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec
+}];
+
+let gDBFile = gProfD.clone();
+gDBFile.append(FILE_DATABASE);
+
+/*
+ * Check the actual add-on results against the expected add-on results
+ *
+ * @param aActualAddons
+ * The array of actual add-ons to check
+ * @param aExpectedAddons
+ * The array of expected add-ons to check against
+ * @param aFromRepository
+ * An optional boolean representing if the add-ons are from
+ * the repository
+ */
+function check_results(aActualAddons, aExpectedAddons, aFromRepository) {
+ aFromRepository = !!aFromRepository;
+
+ do_check_addons(aActualAddons, aExpectedAddons, ADDON_PROPERTIES);
+
+ // Separately test size and updateDate (they should only be equal to the
+ // REPOSITORY values if they are from the repository)
+ aActualAddons.forEach(function(aActualAddon) {
+ if (aActualAddon.size)
+ do_check_eq(aActualAddon.size === REPOSITORY_SIZE, aFromRepository);
+
+ if (aActualAddon.updateDate) {
+ let time = aActualAddon.updateDate.getTime();
+ do_check_eq(time === 1000 * REPOSITORY_UPDATEDATE, aFromRepository);
+ }
+ });
+}
+
+/*
+ * Check the add-ons in the cache. This function also tests
+ * AddonRepository.getCachedAddonByID()
+ *
+ * @param aExpectedToFind
+ * An array of booleans representing which REPOSITORY_ADDONS are
+ * expected to be found in the cache
+ * @param aExpectedImmediately
+ * A boolean representing if results from the cache are expected
+ * immediately. Results are not immediate if the cache has not been
+ * initialized yet.
+ * @return Promise{null}
+ * Resolves once the checks are complete
+ */
+function check_cache(aExpectedToFind, aExpectedImmediately) {
+ do_check_eq(aExpectedToFind.length, REPOSITORY_ADDONS.length);
+
+ let lookups = [];
+
+ for (let i = 0 ; i < REPOSITORY_ADDONS.length ; i++) {
+ lookups.push(new Promise((resolve, reject) => {
+ let immediatelyFound = true;
+ let expected = aExpectedToFind[i] ? REPOSITORY_ADDONS[i] : null;
+ // can't Promise-wrap this because we're also testing whether the callback is
+ // sync or async
+ AddonRepository.getCachedAddonByID(REPOSITORY_ADDONS[i].id, function(aAddon) {
+ do_check_eq(immediatelyFound, aExpectedImmediately);
+ if (expected == null)
+ do_check_eq(aAddon, null);
+ else
+ check_results([aAddon], [expected], true);
+ resolve();
+ });
+ immediatelyFound = false;
+ }));
+ }
+ return Promise.all(lookups);
+}
+
+/*
+ * Task to check an initialized cache by checking the cache, then restarting the
+ * manager, and checking the cache. This checks that the cache is consistent
+ * across manager restarts.
+ *
+ * @param aExpectedToFind
+ * An array of booleans representing which REPOSITORY_ADDONS are
+ * expected to be found in the cache
+ */
+function* check_initialized_cache(aExpectedToFind) {
+ yield check_cache(aExpectedToFind, true);
+ yield promiseRestartManager();
+
+ // If cache is disabled, then expect results immediately
+ let cacheEnabled = Services.prefs.getBoolPref(PREF_GETADDONS_CACHE_ENABLED);
+ yield check_cache(aExpectedToFind, !cacheEnabled);
+}
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* setup() {
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ startupManager();
+
+ // Install XPI add-ons
+ yield promiseInstallAllFiles(ADDON_FILES);
+ yield promiseRestartManager();
+
+ gServer = new HttpServer();
+ gServer.registerDirectory("/data/", do_get_file("data"));
+ gServer.start(PORT);
+});
+
+// Tests AddonRepository.cacheEnabled
+add_task(function* run_test_1() {
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ do_check_false(AddonRepository.cacheEnabled);
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ do_check_true(AddonRepository.cacheEnabled);
+});
+
+// Tests that the cache and database begin as empty
+add_task(function* run_test_2() {
+ do_check_false(gDBFile.exists());
+ yield check_cache([false, false, false], false);
+ yield AddonRepository.flush();
+});
+
+// Tests repopulateCache when the search fails
+add_task(function* run_test_3() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_FAILED);
+
+ yield AddonRepository.repopulateCache();
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests repopulateCache when search returns no results
+add_task(function* run_test_4() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_EMPTY);
+
+ yield AddonRepository.repopulateCache();
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests repopulateCache when search returns results
+add_task(function* run_test_5() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ yield AddonRepository.repopulateCache();
+ yield check_initialized_cache([true, true, true]);
+});
+
+// Tests repopulateCache when caching is disabled for a single add-on
+add_task(function* run_test_5_1() {
+ Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, false);
+
+ yield AddonRepository.repopulateCache();
+
+ // Reset pref for next test
+ Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, true);
+
+ yield check_initialized_cache([false, true, true]);
+});
+
+// Tests repopulateCache when caching is disabled
+add_task(function* run_test_6() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+
+ yield AddonRepository.repopulateCache();
+ // Database should have been deleted
+ do_check_false(gDBFile.exists());
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ yield check_cache([false, false, false], false);
+ yield AddonRepository.flush();
+});
+
+// Tests cacheAddons when the search fails
+add_task(function* run_test_7() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_FAILED);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests cacheAddons when the search returns no results
+add_task(function* run_test_8() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_EMPTY);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests cacheAddons for a single add-on when search returns results
+add_task(function* run_test_9() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons([ADDON_IDS[0]], resolve));
+ yield check_initialized_cache([true, false, false]);
+});
+
+// Tests cacheAddons when caching is disabled for a single add-on
+add_task(function* run_test_9_1() {
+ Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, false);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+
+ // Reset pref for next test
+ Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, true);
+
+ yield check_initialized_cache([true, false, true]);
+});
+
+// Tests cacheAddons for multiple add-ons, some already in the cache,
+add_task(function* run_test_10() {
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ yield check_initialized_cache([true, true, true]);
+});
+
+// Tests cacheAddons when caching is disabled
+add_task(function* run_test_11() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ do_check_true(gDBFile.exists());
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ yield check_initialized_cache([true, true, true]);
+});
+
+// Tests that XPI add-ons do not use any of the repository properties if
+// caching is disabled, even if there are repository properties available
+add_task(function* run_test_12() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITHOUT_CACHE);
+});
+
+// Tests that a background update with caching disabled deletes the add-ons
+// database, and that XPI add-ons still do not use any of repository properties
+add_task(function* run_test_13() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_EMPTY);
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ // Database should have been deleted
+ do_check_false(gDBFile.exists());
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITHOUT_CACHE);
+});
+
+// Tests that the XPI add-ons have the correct properties if caching is
+// enabled but has no information
+add_task(function* run_test_14() {
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ yield AddonRepository.flush();
+ do_check_true(gDBFile.exists());
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITHOUT_CACHE);
+});
+
+// Tests that the XPI add-ons correctly use the repository properties when
+// caching is enabled and the repository information is available
+add_task(function* run_test_15() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_RESULTS);
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITH_CACHE);
+});
+
+// Tests that restarting the manager does not change the checked properties
+// on the XPI add-ons (repository properties still exist and are still properly
+// used)
+add_task(function* run_test_16() {
+ yield promiseRestartManager();
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITH_CACHE);
+});
+
+// Tests that setting a list of types to cache works
+add_task(function* run_test_17() {
+ Services.prefs.setCharPref(PREF_GETADDONS_CACHE_TYPES, "foo,bar,extension,baz");
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITH_EXTENSION_CACHE);
+});
+
+add_task(function* end_test() {
+ yield new Promise((resolve, reject) => gServer.stop(resolve));
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_compatmode.js b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_compatmode.js
new file mode 100644
index 000000000..6aec96ea1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_compatmode.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that AddonRepository correctly fills in the
+// %COMPATIBILITY_MODE% token in the Search API URL.
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+var COMPATIBILITY_PREF;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_AddonRepository_compatmode_ignore.xml", gServer);
+mapFile("/data/test_AddonRepository_compatmode_normal.xml", gServer);
+mapFile("/data/test_AddonRepository_compatmode_strict.xml", gServer);
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS,
+ "http://localhost:" + gPort + "/data/test_AddonRepository_compatmode_%COMPATIBILITY_MODE%.xml");
+ startupManager();
+ run_test_1();
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
+
+// Strict compatibility checking disabled.
+function run_test_1() {
+ do_print("Testing with strict compatibility checking disabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+ AddonRepository.searchAddons("test", 6, {
+ searchSucceeded: function(aAddons) {
+ do_check_neq(aAddons, null);
+ do_check_eq(aAddons.length, 1);
+ do_check_eq(aAddons[0].id, "compatmode-normal@tests.mozilla.org");
+
+ run_test_2();
+ },
+ searchFailed: function() {
+ do_throw("Search should not have failed");
+ }
+ });
+}
+
+// Strict compatibility checking enabled.
+function run_test_2() {
+ do_print("Testing with strict compatibility checking enabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ AddonRepository.searchAddons("test", 6, {
+ searchSucceeded: function(aAddons) {
+ do_check_neq(aAddons, null);
+ do_check_eq(aAddons.length, 1);
+ do_check_eq(aAddons[0].id, "compatmode-strict@tests.mozilla.org");
+
+ run_test_3();
+ },
+ searchFailed: function() {
+ do_throw("Search should not have failed");
+ }
+ });
+}
+
+// Compatibility checking disabled.
+function run_test_3() {
+ do_print("Testing with all compatibility checking disabled");
+ AddonManager.checkCompatibility = false;
+
+ AddonRepository.searchAddons("test", 6, {
+ searchSucceeded: function(aAddons) {
+ do_check_neq(aAddons, null);
+ do_check_eq(aAddons.length, 1);
+ do_check_eq(aAddons[0].id, "compatmode-ignore@tests.mozilla.org");
+
+ end_test();
+ },
+ searchFailed: function() {
+ do_throw("Search should not have failed");
+ }
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js b/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js
new file mode 100644
index 000000000..2e4adbe0f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests ChromeManifestParser.js
+
+Components.utils.import("resource://gre/modules/ChromeManifestParser.jsm");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_1"),
+ do_get_addon("test_chromemanifest_2"),
+ do_get_addon("test_chromemanifest_3"),
+ do_get_addon("test_chromemanifest_4")],
+ function() {
+
+ restartManager();
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ // addon1
+ let a1Uri = a1.getResourceURI("/").spec;
+ let expected = [
+ {type: "content", baseURI: a1Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a1Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}
+ ];
+ let manifestURI = a1.getResourceURI("chrome.manifest");
+ let manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ // addon2
+ let a2Uri = a2.getResourceURI("/").spec;
+ expected = [
+ {type: "content", baseURI: a2Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a2Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+ {type: "binary-component", baseURI: a2Uri, args: ["components/something.so"]}
+ ];
+ manifestURI = a2.getResourceURI("chrome.manifest");
+ manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ // addon3
+ let a3Uri = a3.getResourceURI("/").spec;
+ expected = [
+ {type: "content", baseURI: a3Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a3Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+ {type: "binary-component", baseURI: a3Uri, args: ["components/something.so"]},
+ {type: "locale", baseURI: "jar:" + a3.getResourceURI("/inner.jar").spec + "!/", args: ["test-addon-1", "en-NZ", "locale/en-NZ"]},
+ ];
+ manifestURI = a3.getResourceURI("chrome.manifest");
+ manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ // addon4
+ let a4Uri = a4.getResourceURI("/").spec;
+ expected = [
+ {type: "content", baseURI: a4Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a4Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+ {type: "binary-component", baseURI: a4.getResourceURI("components/").spec, args: ["mycomponent.dll"]},
+ {type: "binary-component", baseURI: a4.getResourceURI("components/other/").spec, args: ["thermalnuclearwar.dll"]}
+ ];
+ manifestURI = a4.getResourceURI("chrome.manifest");
+ manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js b/toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js
new file mode 100644
index 000000000..7599c8b80
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js
@@ -0,0 +1,550 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test behaviour of module to perform deferred save of data
+// files to disk
+
+"use strict";
+
+const testFile = gProfD.clone();
+testFile.append("DeferredSaveTest");
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+let DSContext = Components.utils.import("resource://gre/modules/DeferredSave.jsm", {});
+let DeferredSave = DSContext.DeferredSave;
+
+// Test wrapper to let us do promise/task based testing of DeferredSave
+function DeferredSaveTester(aDataProvider) {
+ let tester = {
+ // Deferred for the promise returned by the mock writeAtomic
+ waDeferred: null,
+
+ // The most recent data "written" by the mock OS.File.writeAtomic
+ writtenData: undefined,
+
+ dataToSave: "Data to save",
+
+ save: (aData, aWriteHandler) => {
+ tester.writeHandler = aWriteHandler || writer;
+ tester.dataToSave = aData;
+ return tester.saver.saveChanges();
+ },
+
+ flush: (aWriteHandler) => {
+ tester.writeHandler = aWriteHandler || writer;
+ return tester.saver.flush();
+ },
+
+ get lastError() {
+ return tester.saver.lastError;
+ }
+ };
+
+ // Default write handler for most cases where the test case doesn't need
+ // to do anything while the write is in progress; just completes the write
+ // on the next event loop
+ function writer(aTester) {
+ do_print("default write callback");
+ let length = aTester.writtenData.length;
+ do_execute_soon(() => aTester.waDeferred.resolve(length));
+ }
+
+ if (!aDataProvider)
+ aDataProvider = () => tester.dataToSave;
+
+ tester.saver = new DeferredSave(testFile.path, aDataProvider);
+
+ // Install a mock for OS.File.writeAtomic to let us control the async
+ // behaviour of the promise
+ DSContext.OS.File.writeAtomic = function mock_writeAtomic(aFile, aData, aOptions) {
+ do_print("writeAtomic: " + aFile + " data: '" + aData + "', " + aOptions.toSource());
+ tester.writtenData = aData;
+ tester.waDeferred = Promise.defer();
+ tester.writeHandler(tester);
+ return tester.waDeferred.promise;
+ };
+
+ return tester;
+};
+
+/**
+ * Install a mock nsITimer factory that triggers on the next spin of
+ * the event loop after it is scheduled
+ */
+function setQuickMockTimer() {
+ let quickTimer = {
+ initWithCallback: function(aFunction, aDelay, aType) {
+ do_print("Starting quick timer, delay = " + aDelay);
+ do_execute_soon(aFunction);
+ },
+ cancel: function() {
+ do_throw("Attempted to cancel a quickMockTimer");
+ }
+ };
+ DSContext.MakeTimer = () => {
+ do_print("Creating quick timer");
+ return quickTimer;
+ };
+}
+
+/**
+ * Install a mock nsITimer factory in DeferredSave.jsm, returning a promise that resolves
+ * when the client code sets the timer. Test cases can use this to wait for client code to
+ * be ready for a timer event, and then signal the event by calling mockTimer.callback().
+ * This could use some enhancement; clients can re-use the returned timer,
+ * but with this implementation it's not possible for the test to wait for
+ * a second call to initWithCallback() on the re-used timer.
+ * @return Promise{mockTimer} that resolves when initWithCallback()
+ * is called
+ */
+function setPromiseMockTimer() {
+ let waiter = Promise.defer();
+ let mockTimer = {
+ callback: null,
+ delay: null,
+ type: null,
+ isCancelled: false,
+
+ initWithCallback: function(aFunction, aDelay, aType) {
+ do_print("Starting timer, delay = " + aDelay);
+ this.callback = aFunction;
+ this.delay = aDelay;
+ this.type = aType;
+ // cancelled timers can be re-used
+ this.isCancelled = false;
+ waiter.resolve(this);
+ },
+ cancel: function() {
+ do_print("Cancelled mock timer");
+ this.callback = null;
+ this.delay = null;
+ this.type = null;
+ this.isCancelled = true;
+ // If initWithCallback was never called, resolve to let tests check for cancel
+ waiter.resolve(this);
+ }
+ };
+ DSContext.MakeTimer = () => {
+ do_print("Creating mock timer");
+ return mockTimer;
+ };
+ return waiter.promise;
+}
+
+/**
+ * Return a Promise<null> that resolves after the specified number of milliseconds
+ */
+function delay(aDelayMS) {
+ let deferred = Promise.defer();
+ do_timeout(aDelayMS, () => deferred.resolve(null));
+ return deferred.promise;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+// Modify set data once, ask for save, make sure it saves cleanly
+add_task(function test_basic_save_succeeds() {
+ setQuickMockTimer();
+ let tester = DeferredSaveTester();
+ let data = "Test 1 Data";
+
+ yield tester.save(data);
+ do_check_eq(tester.writtenData, data);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Two saves called during the same event loop, both with callbacks
+// Make sure we save only the second version of the data
+add_task(function test_two_saves() {
+ setQuickMockTimer();
+ let tester = DeferredSaveTester();
+ let firstCallback_happened = false;
+ let firstData = "Test first save";
+ let secondData = "Test second save";
+
+ // first save should not resolve until after the second one is called,
+ // so we can't just yield this promise
+ tester.save(firstData).then(count => {
+ do_check_eq(secondData, tester.writtenData);
+ do_check_false(firstCallback_happened);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ yield tester.save(secondData);
+ do_check_true(firstCallback_happened);
+ do_check_eq(secondData, tester.writtenData);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Two saves called with a delay in between, both with callbacks
+// Make sure we save the second version of the data
+add_task(function test_two_saves_delay() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstCallback_happened = false;
+ let delayDone = false;
+
+ let firstData = "First data to save with delay";
+ let secondData = "Modified data to save with delay";
+
+ tester.save(firstData).then(count => {
+ do_check_false(firstCallback_happened);
+ do_check_true(delayDone);
+ do_check_eq(secondData, tester.writtenData);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ // Wait a short time to let async events possibly spawned by the
+ // first tester.save() to run
+ yield delay(2);
+ delayDone = true;
+ // request to save modified data
+ let saving = tester.save(secondData);
+ // Yield to wait for client code to set the timer
+ let activeTimer = yield timerPromise;
+ // and then trigger it
+ activeTimer.callback();
+ // now wait for the DeferredSave to finish saving
+ yield saving;
+ do_check_true(firstCallback_happened);
+ do_check_eq(secondData, tester.writtenData);
+ do_check_eq(1, tester.saver.totalSaves);
+ do_check_eq(0, tester.saver.overlappedSaves);
+});
+
+// Test case where OS.File immediately reports an error when the write begins
+// Also check that the "error" getter correctly returns the error
+// Then do a write that succeeds, and make sure the error is cleared
+add_task(function test_error_immediate() {
+ let tester = DeferredSaveTester();
+ let testError = new Error("Forced failure");
+ function writeFail(aTester) {
+ aTester.waDeferred.reject(testError);
+ }
+
+ setQuickMockTimer();
+ yield tester.save("test_error_immediate", writeFail).then(
+ count => do_throw("Did not get expected error"),
+ error => do_check_eq(testError.message, error.message)
+ );
+ do_check_eq(testError, tester.lastError);
+
+ // This write should succeed and clear the error
+ yield tester.save("test_error_immediate succeeds");
+ do_check_eq(null, tester.lastError);
+ // The failed save attempt counts in our total
+ do_check_eq(2, tester.saver.totalSaves);
+});
+
+// Save one set of changes, then while the write is in progress, modify the
+// data two more times. Test that we re-write the dirty data exactly once
+// after the first write succeeds
+add_task(function dirty_while_writing() {
+ let tester = DeferredSaveTester();
+ let firstData = "First data";
+ let secondData = "Second data";
+ let thirdData = "Third data";
+ let firstCallback_happened = false;
+ let secondCallback_happened = false;
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ setQuickMockTimer();
+ do_print("First save");
+ tester.save(firstData, writeCallback).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ do_check_false(secondCallback_happened);
+ do_check_eq(tester.writtenData, firstData);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ do_print("waiting for writer");
+ let writer = yield writeStarted.promise;
+ do_print("Write started");
+
+ // Delay a bit, modify the data and call saveChanges, delay a bit more,
+ // modify the data and call saveChanges again, another delay,
+ // then complete the in-progress write
+ yield delay(1);
+
+ tester.save(secondData).then(
+ count => {
+ do_check_true(firstCallback_happened);
+ do_check_false(secondCallback_happened);
+ do_check_eq(tester.writtenData, thirdData);
+ secondCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ // wait and then do the third change
+ yield delay(1);
+ let thirdWrite = tester.save(thirdData);
+
+ // wait a bit more and then finally finish the first write
+ yield delay(1);
+ writer.resolve(firstData.length);
+
+ // Now let everything else finish
+ yield thirdWrite;
+ do_check_true(firstCallback_happened);
+ do_check_true(secondCallback_happened);
+ do_check_eq(tester.writtenData, thirdData);
+ do_check_eq(2, tester.saver.totalSaves);
+ do_check_eq(1, tester.saver.overlappedSaves);
+});
+
+// A write callback for the OS.File.writeAtomic mock that rejects write attempts
+function disabled_write_callback(aTester) {
+ do_throw("Should not have written during clean flush");
+ deferred.reject(new Error("Write during supposedly clean flush"));
+}
+
+// special write callback that disables itself to make sure
+// we don't try to write twice
+function write_then_disable(aTester) {
+ do_print("write_then_disable");
+ let length = aTester.writtenData.length;
+ aTester.writeHandler = disabled_write_callback;
+ do_execute_soon(() => aTester.waDeferred.resolve(length));
+}
+
+// Flush tests. First, do an ordinary clean save and then call flush;
+// there should not be another save
+add_task(function flush_after_save() {
+ setQuickMockTimer();
+ let tester = DeferredSaveTester();
+ let dataToSave = "Flush after save";
+
+ yield tester.save(dataToSave);
+ yield tester.flush(disabled_write_callback);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Flush while a write is in progress, but the in-memory data is clean
+add_task(function flush_during_write() {
+ let tester = DeferredSaveTester();
+ let dataToSave = "Flush during write";
+ let firstCallback_happened = false;
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ setQuickMockTimer();
+ tester.save(dataToSave, writeCallback).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ let writer = yield writeStarted.promise;
+
+ // call flush with the write callback disabled, delay a bit more, complete in-progress write
+ let flushing = tester.flush(disabled_write_callback);
+ yield delay(2);
+ writer.resolve(dataToSave.length);
+
+ // now wait for the flush to finish
+ yield flushing;
+ do_check_true(firstCallback_happened);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Flush while dirty but write not in progress
+// The data written should be the value at the time
+// flush() is called, even if it is changed later
+add_task(function flush_while_dirty() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstData = "Flush while dirty, valid data";
+ let firstCallback_happened = false;
+
+ tester.save(firstData, write_then_disable).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ firstCallback_happened = true;
+ do_check_eq(tester.writtenData, firstData);
+ }, do_report_unexpected_exception);
+
+ // Wait for the timer to be set, but don't trigger it so the write won't start
+ let activeTimer = yield timerPromise;
+
+ let flushing = tester.flush();
+
+ // Make sure the timer was cancelled
+ do_check_true(activeTimer.isCancelled);
+
+ // Also make sure that data changed after the flush call
+ // (even without a saveChanges() call) doesn't get written
+ tester.dataToSave = "Flush while dirty, invalid data";
+
+ yield flushing;
+ do_check_true(firstCallback_happened);
+ do_check_eq(tester.writtenData, firstData);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// And the grand finale - modify the data, start writing,
+// modify the data again so we're in progress and dirty,
+// then flush, then modify the data again
+// Data for the second write should be taken at the time
+// flush() is called, even if it is modified later
+add_task(function flush_writing_dirty() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstData = "Flush first pass data";
+ let secondData = "Flush second pass data";
+ let firstCallback_happened = false;
+ let secondCallback_happened = false;
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ tester.save(firstData, writeCallback).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ do_check_eq(tester.writtenData, firstData);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ // Trigger the timer callback as soon as the DeferredSave sets it
+ let activeTimer = yield timerPromise;
+ activeTimer.callback();
+ let writer = yield writeStarted.promise;
+ // the first write has started
+
+ // dirty the data and request another save
+ // after the second save completes, there should not be another write
+ tester.save(secondData, write_then_disable).then(
+ count => {
+ do_check_true(firstCallback_happened);
+ do_check_false(secondCallback_happened);
+ do_check_eq(tester.writtenData, secondData);
+ secondCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ let flushing = tester.flush(write_then_disable);
+ // Flush should have cancelled our timer
+ do_check_true(activeTimer.isCancelled);
+ tester.dataToSave = "Flush, invalid data: changed late";
+ // complete the first write
+ writer.resolve(firstData.length);
+ // now wait for the second write / flush to complete
+ yield flushing;
+ do_check_true(firstCallback_happened);
+ do_check_true(secondCallback_happened);
+ do_check_eq(tester.writtenData, secondData);
+ do_check_eq(2, tester.saver.totalSaves);
+ do_check_eq(1, tester.saver.overlappedSaves);
+});
+
+// A data provider callback that throws an error the first
+// time it is called, and a different error the second time
+// so that tests can (a) make sure the promise is rejected
+// with the error and (b) make sure the provider is only
+// called once in case of error
+const expectedDataError = "Failed to serialize data";
+let badDataError = null;
+function badDataProvider() {
+ let err = new Error(badDataError);
+ badDataError = "badDataProvider called twice";
+ throw err;
+}
+
+// Handle cases where data provider throws
+// First, throws during a normal save
+add_task(function data_throw() {
+ setQuickMockTimer();
+ badDataError = expectedDataError;
+ let tester = DeferredSaveTester(badDataProvider);
+ yield tester.save("data_throw").then(
+ count => do_throw("Expected serialization failure"),
+ error => do_check_eq(error.message, expectedDataError));
+});
+
+// Now, throws during flush
+add_task(function data_throw_during_flush() {
+ badDataError = expectedDataError;
+ let tester = DeferredSaveTester(badDataProvider);
+ let firstCallback_happened = false;
+
+ setPromiseMockTimer();
+ // Write callback should never be called
+ tester.save("data_throw_during_flush", disabled_write_callback).then(
+ count => do_throw("Expected serialization failure"),
+ error => {
+ do_check_false(firstCallback_happened);
+ do_check_eq(error.message, expectedDataError);
+ firstCallback_happened = true;
+ });
+
+ // flush() will cancel the timer
+ yield tester.flush(disabled_write_callback).then(
+ count => do_throw("Expected serialization failure"),
+ error => do_check_eq(error.message, expectedDataError)
+ );
+
+ do_check_true(firstCallback_happened);
+});
+
+// Try to reproduce race condition. The observed sequence of events:
+// saveChanges
+// start writing
+// saveChanges
+// finish writing (need to restart delayed timer)
+// saveChanges
+// flush
+// write starts
+// actually restart timer for delayed write
+// write completes
+// delayed timer goes off, throws error because DeferredSave has been torn down
+add_task(function delay_flush_race() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstData = "First save";
+ let secondData = "Second save";
+ let thirdData = "Third save";
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ // This promise won't resolve until after writeStarted
+ let firstSave = tester.save(firstData, writeCallback);
+ (yield timerPromise).callback();
+
+ let writer = yield writeStarted.promise;
+ // the first write has started
+
+ // dirty the data and request another save
+ let secondSave = tester.save(secondData);
+
+ // complete the first write
+ writer.resolve(firstData.length);
+ yield firstSave;
+ do_check_eq(tester.writtenData, firstData);
+
+ tester.save(thirdData);
+ let flushing = tester.flush();
+
+ yield secondSave;
+ do_check_eq(tester.writtenData, thirdData);
+
+ yield flushing;
+ do_check_eq(tester.writtenData, thirdData);
+
+ // Our DeferredSave should not have a _timer here; if it
+ // does, the bug caused a reschedule
+ do_check_eq(null, tester.saver._timer);
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_LightweightThemeManager.js b/toolkit/mozapps/extensions/test/xpcshell/test_LightweightThemeManager.js
new file mode 100644
index 000000000..c0cf78a89
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_LightweightThemeManager.js
@@ -0,0 +1,514 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const MANDATORY = ["id", "name", "headerURL"];
+const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL",
+ "previewURL", "author", "description", "homepageURL",
+ "updateURL", "version"];
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function dummy(id) {
+ return {
+ id: id || Math.random().toString(),
+ name: Math.random().toString(),
+ headerURL: "http://lwttest.invalid/a.png",
+ footerURL: "http://lwttest.invalid/b.png",
+ textcolor: Math.random().toString(),
+ accentcolor: Math.random().toString()
+ };
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 8);
+
+ var temp = {};
+ Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
+ do_check_eq(typeof temp.LightweightThemeManager, "object");
+
+ var ltm = temp.LightweightThemeManager;
+
+ do_check_eq(typeof ltm.usedThemes, "object");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ ltm.previewTheme(dummy("preview0"));
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ ltm.previewTheme(dummy("preview1"));
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+ ltm.resetPreview();
+
+ ltm.currentTheme = dummy("x0");
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.id, "x0");
+ do_check_eq(ltm.usedThemes[0].id, "x0");
+ do_check_eq(ltm.getUsedTheme("x0").id, "x0");
+
+ ltm.previewTheme(dummy("preview0"));
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.id, "x0");
+
+ ltm.resetPreview();
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.id, "x0");
+
+ ltm.currentTheme = dummy("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x0");
+
+ ltm.currentTheme = dummy("x2");
+ do_check_eq(ltm.usedThemes.length, 3);
+ do_check_eq(ltm.currentTheme.id, "x2");
+ do_check_eq(ltm.usedThemes[1].id, "x1");
+ do_check_eq(ltm.usedThemes[2].id, "x0");
+
+ ltm.currentTheme = dummy("x3");
+ ltm.currentTheme = dummy("x4");
+ ltm.currentTheme = dummy("x5");
+ ltm.currentTheme = dummy("x6");
+ ltm.currentTheme = dummy("x7");
+ do_check_eq(ltm.usedThemes.length, 8);
+ do_check_eq(ltm.currentTheme.id, "x7");
+ do_check_eq(ltm.usedThemes[1].id, "x6");
+ do_check_eq(ltm.usedThemes[7].id, "x0");
+
+ ltm.currentTheme = dummy("x8");
+ do_check_eq(ltm.usedThemes.length, 8);
+ do_check_eq(ltm.currentTheme.id, "x8");
+ do_check_eq(ltm.usedThemes[1].id, "x7");
+ do_check_eq(ltm.usedThemes[7].id, "x1");
+ do_check_eq(ltm.getUsedTheme("x0"), null);
+
+ ltm.forgetUsedTheme("nonexistent");
+ do_check_eq(ltm.usedThemes.length, 8);
+ do_check_neq(ltm.currentTheme, null);
+
+ ltm.forgetUsedTheme("x8");
+ do_check_eq(ltm.usedThemes.length, 7);
+ do_check_eq(ltm.currentTheme, null);
+ do_check_eq(ltm.usedThemes[0].id, "x7");
+ do_check_eq(ltm.usedThemes[6].id, "x1");
+
+ ltm.forgetUsedTheme("x7");
+ ltm.forgetUsedTheme("x6");
+ ltm.forgetUsedTheme("x5");
+ ltm.forgetUsedTheme("x4");
+ ltm.forgetUsedTheme("x3");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme, null);
+ do_check_eq(ltm.usedThemes[0].id, "x2");
+ do_check_eq(ltm.usedThemes[1].id, "x1");
+
+ ltm.currentTheme = dummy("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[0].id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x2");
+
+ ltm.currentTheme = dummy("x2");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x2");
+ do_check_eq(ltm.usedThemes[0].id, "x2");
+ do_check_eq(ltm.usedThemes[1].id, "x1");
+
+ ltm.currentTheme = ltm.getUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[0].id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x2");
+
+ ltm.forgetUsedTheme("x1");
+ ltm.forgetUsedTheme("x2");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Use chinese name to test utf-8, for bug #541943
+ var chineseTheme = dummy("chinese0");
+ chineseTheme.name = "笢恅0";
+ chineseTheme.description = "笢恅1";
+ ltm.currentTheme = chineseTheme;
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.name, "笢恅0");
+ do_check_eq(ltm.currentTheme.description, "笢恅1");
+ do_check_eq(ltm.usedThemes[0].name, "笢恅0");
+ do_check_eq(ltm.usedThemes[0].description, "笢恅1");
+ do_check_eq(ltm.getUsedTheme("chinese0").name, "笢恅0");
+ do_check_eq(ltm.getUsedTheme("chinese0").description, "笢恅1");
+
+ // This name used to break the usedTheme JSON causing all LWTs to be lost
+ var chineseTheme1 = dummy("chinese1");
+ chineseTheme1.name = "眵昜湮桵蔗坌~郔乾";
+ chineseTheme1.description = "眵昜湮桵蔗坌~郔乾";
+ ltm.currentTheme = chineseTheme1;
+ do_check_neq(ltm.currentTheme, null);
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.name, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(ltm.currentTheme.description, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(ltm.usedThemes[1].name, "笢恅0");
+ do_check_eq(ltm.usedThemes[1].description, "笢恅1");
+ do_check_eq(ltm.usedThemes[0].name, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(ltm.usedThemes[0].description, "眵昜湮桵蔗坌~郔乾");
+
+ ltm.forgetUsedTheme("chinese0");
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_neq(ltm.currentTheme, null);
+
+ ltm.forgetUsedTheme("chinese1");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ do_check_eq(ltm.parseTheme("invalid json"), null);
+ do_check_eq(ltm.parseTheme('"json string"'), null);
+
+ function roundtrip(data, secure) {
+ return ltm.parseTheme(JSON.stringify(data),
+ "http" + (secure ? "s" : "") + "://lwttest.invalid/");
+ }
+
+ var data = dummy();
+ do_check_neq(roundtrip(data), null);
+ data.id = null;
+ do_check_eq(roundtrip(data), null);
+ data.id = 1;
+ do_check_eq(roundtrip(data), null);
+ data.id = 1.5;
+ do_check_eq(roundtrip(data), null);
+ data.id = true;
+ do_check_eq(roundtrip(data), null);
+ data.id = {};
+ do_check_eq(roundtrip(data), null);
+ data.id = [];
+ do_check_eq(roundtrip(data), null);
+
+ // Check whether parseTheme handles international characters right
+ var chineseTheme2 = dummy();
+ chineseTheme2.name = "眵昜湮桵蔗坌~郔乾";
+ chineseTheme2.description = "眵昜湮桵蔗坌~郔乾";
+ do_check_neq(roundtrip(chineseTheme2), null);
+ do_check_eq(roundtrip(chineseTheme2).name, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(roundtrip(chineseTheme2).description, "眵昜湮桵蔗坌~郔乾");
+
+ data = dummy();
+ data.unknownProperty = "Foo";
+ do_check_eq(typeof roundtrip(data).unknownProperty, "undefined");
+
+ data = dummy();
+ data.unknownURL = "http://lwttest.invalid/";
+ do_check_eq(typeof roundtrip(data).unknownURL, "undefined");
+
+ function roundtripSet(props, modify, test, secure) {
+ props.forEach(function (prop) {
+ var data = dummy();
+ modify(data, prop);
+ test(roundtrip(data, secure), prop, data);
+ });
+ }
+
+ roundtripSet(MANDATORY, function (data, prop) {
+ delete data[prop];
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(OPTIONAL, function (data, prop) {
+ delete data[prop];
+ }, function (after) {
+ do_check_neq(after, null);
+ });
+
+ roundtripSet(MANDATORY, function (data, prop) {
+ data[prop] = "";
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(OPTIONAL, function (data, prop) {
+ data[prop] = "";
+ }, function (after, prop) {
+ do_check_eq(typeof after[prop], "undefined");
+ });
+
+ roundtripSet(MANDATORY, function (data, prop) {
+ data[prop] = " ";
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(OPTIONAL, function (data, prop) {
+ data[prop] = " ";
+ }, function (after, prop) {
+ do_check_neq(after, null);
+ do_check_eq(typeof after[prop], "undefined");
+ });
+
+ function non_urls(props) {
+ return props.filter(function (prop) !/URL$/.test(prop));
+ }
+
+ function urls(props) {
+ return props.filter(function (prop) /URL$/.test(prop));
+ }
+
+ roundtripSet(non_urls(MANDATORY.concat(OPTIONAL)), function (data, prop) {
+ data[prop] = prop;
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], before[prop]);
+ });
+
+ roundtripSet(non_urls(MANDATORY.concat(OPTIONAL)), function (data, prop) {
+ data[prop] = " " + prop + " ";
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], before[prop].trim());
+ });
+
+ roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function (data, prop) {
+ data[prop] = Math.random().toString();
+ }, function (after, prop, before) {
+ if (prop == "updateURL")
+ do_check_eq(typeof after[prop], "undefined");
+ else
+ do_check_eq(after[prop], "http://lwttest.invalid/" + before[prop]);
+ });
+
+ roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function (data, prop) {
+ data[prop] = Math.random().toString();
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], "https://lwttest.invalid/" + before[prop]);
+ }, true);
+
+ roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function (data, prop) {
+ data[prop] = "https://sub.lwttest.invalid/" + Math.random().toString();
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], before[prop]);
+ });
+
+ roundtripSet(urls(MANDATORY), function (data, prop) {
+ data[prop] = "ftp://lwttest.invalid/" + Math.random().toString();
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(urls(OPTIONAL), function (data, prop) {
+ data[prop] = "ftp://lwttest.invalid/" + Math.random().toString();
+ }, function (after, prop) {
+ do_check_eq(typeof after[prop], "undefined");
+ });
+
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ data = dummy();
+ delete data.name;
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with no name");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.headerURL = "foo";
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with a bad headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.headerURL = "ftp://lwtest.invalid/test.png";
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with a non-http(s) headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.headerURL = "file:///test.png";
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with a non-http(s) headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.updateURL = "file:///test.json";
+ ltm.setLocalTheme(data);
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.updateURL, undefined);
+ ltm.forgetUsedTheme(ltm.currentTheme.id);
+ do_check_eq(ltm.usedThemes.length, 0);
+
+ data = dummy();
+ data.headerURL = "file:///test.png";
+ ltm.setLocalTheme(data);
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.headerURL, "file:///test.png");
+ ltm.forgetUsedTheme(ltm.currentTheme.id);
+ do_check_eq(ltm.usedThemes.length, 0);
+
+ data = dummy();
+ data.headerURL = "ftp://lwtest.invalid/test.png";
+ try {
+ ltm.setLocalTheme(data);
+ do_throw("Should have rejected a theme with a non-http(s), non-file headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ delete data.id;
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with no ID");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Force the theme into the prefs anyway
+ let prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ let themes = [data];
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+ do_check_eq(ltm.usedThemes.length, 1);
+
+ // This should silently drop the bad theme.
+ ltm.currentTheme = dummy();
+ do_check_eq(ltm.usedThemes.length, 1);
+ ltm.forgetUsedTheme(ltm.currentTheme.id);
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Add one broken and some working.
+ themes = [data, dummy("x1"), dummy("x2")];
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+ do_check_eq(ltm.usedThemes.length, 3);
+
+ // Switching to an existing theme should drop the bad theme.
+ ltm.currentTheme = ltm.getUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ ltm.forgetUsedTheme("x1");
+ ltm.forgetUsedTheme("x2");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+ do_check_eq(ltm.usedThemes.length, 3);
+
+ // Forgetting an existing theme should drop the bad theme.
+ ltm.forgetUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 1);
+ ltm.forgetUsedTheme("x2");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Test whether a JSON set with setCharPref can be retrieved with usedThemes
+ ltm.currentTheme = dummy("x0");
+ ltm.currentTheme = dummy("x1");
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(ltm.usedThemes));
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x0");
+ do_check_eq(ltm.usedThemes[0].id, "x1");
+
+ ltm.forgetUsedTheme("x0");
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_neq(ltm.currentTheme, null);
+
+ ltm.forgetUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ Services.prefs.clearUserPref("lightweightThemes.maxUsedThemes");
+
+ ltm.currentTheme = dummy("x1");
+ ltm.currentTheme = dummy("x2");
+ ltm.currentTheme = dummy("x3");
+ ltm.currentTheme = dummy("x4");
+ ltm.currentTheme = dummy("x5");
+ ltm.currentTheme = dummy("x6");
+ ltm.currentTheme = dummy("x7");
+ ltm.currentTheme = dummy("x8");
+ ltm.currentTheme = dummy("x9");
+ ltm.currentTheme = dummy("x10");
+ ltm.currentTheme = dummy("x11");
+ ltm.currentTheme = dummy("x12");
+ ltm.currentTheme = dummy("x13");
+ ltm.currentTheme = dummy("x14");
+ ltm.currentTheme = dummy("x15");
+ ltm.currentTheme = dummy("x16");
+ ltm.currentTheme = dummy("x17");
+ ltm.currentTheme = dummy("x18");
+ ltm.currentTheme = dummy("x19");
+ ltm.currentTheme = dummy("x20");
+ ltm.currentTheme = dummy("x21");
+ ltm.currentTheme = dummy("x22");
+ ltm.currentTheme = dummy("x23");
+ ltm.currentTheme = dummy("x24");
+ ltm.currentTheme = dummy("x25");
+ ltm.currentTheme = dummy("x26");
+ ltm.currentTheme = dummy("x27");
+ ltm.currentTheme = dummy("x28");
+ ltm.currentTheme = dummy("x29");
+ ltm.currentTheme = dummy("x30");
+
+ do_check_eq(ltm.usedThemes.length, 30);
+
+ ltm.currentTheme = dummy("x31");
+
+ do_check_eq(ltm.usedThemes.length, 30);
+ do_check_eq(ltm.getUsedTheme("x1"), null);
+
+ Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 15);
+
+ do_check_eq(ltm.usedThemes.length, 15);
+
+ Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 32);
+
+ ltm.currentTheme = dummy("x1");
+ ltm.currentTheme = dummy("x2");
+ ltm.currentTheme = dummy("x3");
+ ltm.currentTheme = dummy("x4");
+ ltm.currentTheme = dummy("x5");
+ ltm.currentTheme = dummy("x6");
+ ltm.currentTheme = dummy("x7");
+ ltm.currentTheme = dummy("x8");
+ ltm.currentTheme = dummy("x9");
+ ltm.currentTheme = dummy("x10");
+ ltm.currentTheme = dummy("x11");
+ ltm.currentTheme = dummy("x12");
+ ltm.currentTheme = dummy("x13");
+ ltm.currentTheme = dummy("x14");
+ ltm.currentTheme = dummy("x15");
+ ltm.currentTheme = dummy("x16");
+
+ ltm.currentTheme = dummy("x32");
+
+ do_check_eq(ltm.usedThemes.length, 32);
+
+ ltm.currentTheme = dummy("x33");
+
+ do_check_eq(ltm.usedThemes.length, 32);
+
+ Services.prefs.clearUserPref("lightweightThemes.maxUsedThemes");
+
+ do_check_eq(ltm.usedThemes.length, 30);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js b/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
new file mode 100644
index 000000000..37ac161ca
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
@@ -0,0 +1,299 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that we only check manifest age for disabled extensions
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+/* We want one add-on installed packed, and one installed unpacked
+ */
+
+function run_test() {
+ // Shut down the add-on manager after all tests run.
+ do_register_cleanup(promiseShutdownManager);
+ // Kick off the task-based tests...
+ run_next_test();
+}
+
+// Use bootstrap extensions so the changes will be immediate.
+// A packed extension, to be enabled
+writeInstallRDFToXPI({
+ id: "packed-enabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Packed, Enabled",
+}, profileDir);
+
+// Packed, will be disabled
+writeInstallRDFToXPI({
+ id: "packed-disabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Packed, Disabled",
+}, profileDir);
+
+// Unpacked, enabled
+writeInstallRDFToDir({
+ id: "unpacked-enabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Unpacked, Enabled",
+}, profileDir, null, "extraFile.js");
+
+
+// Unpacked, disabled
+writeInstallRDFToDir({
+ id: "unpacked-disabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Unpacked, disabled",
+}, profileDir, null, "extraFile.js");
+
+// Keep track of the last time stamp we've used, so that we can keep moving
+// it forward (if we touch two different files in the same add-on with the same
+// timestamp we may not consider the change significant)
+let lastTimestamp = Date.now();
+
+/*
+ * Helper function to touch a file and then test whether we detect the change.
+ * @param XS The XPIState object.
+ * @param aPath File path to touch.
+ * @param aChange True if we should notice the change, False if we shouldn't.
+ */
+function checkChange(XS, aPath, aChange) {
+ do_check_true(aPath.exists());
+ lastTimestamp += 10000;
+ do_print("Touching file " + aPath.path + " with " + lastTimestamp);
+ aPath.lastModifiedTime = lastTimestamp;
+ do_check_eq(XS.getInstallState(), aChange);
+ // Save the pref so we don't detect this change again
+ XS.save();
+}
+
+// Get a reference to the XPIState (loaded by startupManager) so we can unit test it.
+function getXS() {
+ let XPI = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+ return XPI.XPIStates;
+}
+
+add_task(function* detect_touches() {
+ startupManager();
+ let [pe, pd, ue, ud] = yield promiseAddonsByIDs([
+ "packed-enabled@tests.mozilla.org",
+ "packed-disabled@tests.mozilla.org",
+ "unpacked-enabled@tests.mozilla.org",
+ "unpacked-disabled@tests.mozilla.org"
+ ]);
+
+ do_print("Disable test add-ons");
+ pd.userDisabled = true;
+ ud.userDisabled = true;
+
+ let XS = getXS();
+
+ // Should be no changes detected here, because everything should start out up-to-date.
+ do_check_false(XS.getInstallState());
+
+ let states = XS.getLocation("app-profile");
+
+ // State should correctly reflect enabled/disabled
+ do_check_true(states.get("packed-enabled@tests.mozilla.org").enabled);
+ do_check_false(states.get("packed-disabled@tests.mozilla.org").enabled);
+ do_check_true(states.get("unpacked-enabled@tests.mozilla.org").enabled);
+ do_check_false(states.get("unpacked-disabled@tests.mozilla.org").enabled);
+
+ // Touch various files and make sure the change is detected.
+
+ // We notice that a packed XPI is touched for an enabled add-on.
+ let peFile = profileDir.clone();
+ peFile.append("packed-enabled@tests.mozilla.org.xpi");
+ checkChange(XS, peFile, true);
+
+ // We should notice the packed XPI change for a disabled add-on too.
+ let pdFile = profileDir.clone();
+ pdFile.append("packed-disabled@tests.mozilla.org.xpi");
+ checkChange(XS, pdFile, true);
+
+ // We notice changing install.rdf for an enabled unpacked add-on.
+ let ueDir = profileDir.clone();
+ ueDir.append("unpacked-enabled@tests.mozilla.org");
+ let manifest = ueDir.clone();
+ manifest.append("install.rdf");
+ checkChange(XS, manifest, true);
+ // We also notice changing another file for enabled unpacked add-on.
+ let otherFile = ueDir.clone();
+ otherFile.append("extraFile.js");
+ checkChange(XS, otherFile, true);
+
+ // We notice changing install.rdf for a *disabled* unpacked add-on.
+ let udDir = profileDir.clone();
+ udDir.append("unpacked-disabled@tests.mozilla.org");
+ manifest = udDir.clone();
+ manifest.append("install.rdf");
+ checkChange(XS, manifest, true);
+ // Finally, the case we actually care about...
+ // We *don't* notice changing another file for disabled unpacked add-on.
+ otherFile = udDir.clone();
+ otherFile.append("extraFile.js");
+ checkChange(XS, otherFile, false);
+
+ /*
+ * When we enable an unpacked add-on that was modified while it was
+ * disabled, we reflect the new timestamp in the add-on DB (otherwise, we'll
+ * think it changed on next restart).
+ */
+ ud.userDisabled = false;
+ let xState = XS.getAddon("app-profile", ud.id);
+ do_check_true(xState.enabled);
+ do_check_eq(xState.scanTime, ud.updateDate.getTime());
+});
+
+/*
+ * Uninstalling bootstrap add-ons should immediately remove them from the
+ * extensions.xpiState preference.
+ */
+add_task(function* uninstall_bootstrap() {
+ let [pe, pd, ue, ud] = yield promiseAddonsByIDs([
+ "packed-enabled@tests.mozilla.org",
+ "packed-disabled@tests.mozilla.org",
+ "unpacked-enabled@tests.mozilla.org",
+ "unpacked-disabled@tests.mozilla.org"
+ ]);
+ pe.uninstall();
+ let xpiState = Services.prefs.getCharPref("extensions.xpiState");
+ do_check_false(xpiState.includes("\"packed-enabled@tests.mozilla.org\""));
+});
+
+/*
+ * Installing a restartless add-on should immediately add it to XPIState
+ */
+add_task(function* install_bootstrap() {
+ let XS = getXS();
+
+ let installer = yield new Promise((resolve, reject) =>
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+
+ let promiseInstalled = new Promise((resolve, reject) => {
+ AddonManager.addInstallListener({
+ onInstallFailed: reject,
+ onInstallEnded: (install, newAddon) => resolve(newAddon)
+ });
+ });
+
+ installer.install();
+
+ let newAddon = yield promiseInstalled;
+ let xState = XS.getAddon("app-profile", newAddon.id);
+ do_check_true(!!xState);
+ do_check_true(xState.enabled);
+ do_check_eq(xState.scanTime, newAddon.updateDate.getTime());
+ newAddon.uninstall();
+});
+
+/*
+ * Installing an add-on that requires restart doesn't add to XPIState
+ * until after the restart; disable and enable happen immediately so that
+ * the next restart won't / will scan as necessary on the next restart,
+ * uninstalling it marks XPIState as disabled immediately
+ * and removes XPIState after restart.
+ */
+add_task(function* install_restart() {
+ let XS = getXS();
+
+ let installer = yield new Promise((resolve, reject) =>
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_4"), resolve));
+
+ let promiseInstalled = new Promise((resolve, reject) => {
+ AddonManager.addInstallListener({
+ onInstallFailed: reject,
+ onInstallEnded: (install, newAddon) => resolve(newAddon)
+ });
+ });
+
+ installer.install();
+
+ let newAddon = yield promiseInstalled;
+ let newID = newAddon.id;
+ let xState = XS.getAddon("app-profile", newID);
+ do_check_false(xState);
+
+ // Now we restart the add-on manager, and we need to get the XPIState again
+ // because the add-on manager reloads it.
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+
+ newAddon = yield promiseAddonByID(newID);
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_true(xState.enabled);
+ do_check_eq(xState.scanTime, newAddon.updateDate.getTime());
+
+ // Check that XPIState enabled flag is updated immediately,
+ // and doesn't change over restart.
+ newAddon.userDisabled = true;
+ do_check_false(xState.enabled);
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_false(xState.enabled);
+
+ newAddon = yield promiseAddonByID(newID);
+ newAddon.userDisabled = false;
+ do_check_true(xState.enabled);
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_true(xState.enabled);
+
+ // Uninstalling immediately marks XPIState disabled,
+ // removes state after restart.
+ newAddon = yield promiseAddonByID(newID);
+ newAddon.uninstall();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_false(xState.enabled);
+
+ // Restart to finish uninstall.
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_false(xState);
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js b/toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js
new file mode 100644
index 000000000..7d8778301
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test the cancellable doing/done/cancelAll API in XPIProvider
+
+let scope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+let XPIProvider = scope.XPIProvider;
+
+function run_test() {
+ // Check that cancelling with nothing in progress doesn't blow up
+ XPIProvider.cancelAll();
+
+ // Check that a basic object gets cancelled
+ let getsCancelled = {
+ isCancelled: false,
+ cancel: function () {
+ if (this.isCancelled)
+ do_throw("Already cancelled");
+ this.isCancelled = true;
+ }
+ };
+ XPIProvider.doing(getsCancelled);
+ XPIProvider.cancelAll();
+ do_check_true(getsCancelled.isCancelled);
+
+ // Check that if we complete a cancellable, it doesn't get cancelled
+ let doesntGetCancelled = {
+ cancel: () => do_throw("This should not have been cancelled")
+ };
+ XPIProvider.doing(doesntGetCancelled);
+ do_check_true(XPIProvider.done(doesntGetCancelled));
+ XPIProvider.cancelAll();
+
+ // A cancellable that adds a cancellable
+ getsCancelled.isCancelled = false;
+ let addsAnother = {
+ isCancelled: false,
+ cancel: function () {
+ if (this.isCancelled)
+ do_throw("Already cancelled");
+ this.isCancelled = true;
+ XPIProvider.doing(getsCancelled);
+ }
+ }
+ XPIProvider.doing(addsAnother);
+ XPIProvider.cancelAll();
+ do_check_true(addsAnother.isCancelled);
+ do_check_true(getsCancelled.isCancelled);
+
+ // A cancellable that removes another. This assumes that Set() iterates in the
+ // order that members were added
+ let removesAnother = {
+ isCancelled: false,
+ cancel: function () {
+ if (this.isCancelled)
+ do_throw("Already cancelled");
+ this.isCancelled = true;
+ XPIProvider.done(doesntGetCancelled);
+ }
+ }
+ XPIProvider.doing(removesAnother);
+ XPIProvider.doing(doesntGetCancelled);
+ XPIProvider.cancelAll();
+ do_check_true(removesAnother.isCancelled);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_addon_path_service.js b/toolkit/mozapps/extensions/test/xpcshell/test_addon_path_service.js
new file mode 100644
index 000000000..30bb577a1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_addon_path_service.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+let service = Components.classes["@mozilla.org/addon-path-service;1"].getService(Components.interfaces.amIAddonPathService);
+
+function insert(path, value)
+{
+ service.insertPath("/test/" + path, value);
+}
+
+function find(path)
+{
+ return service.findAddonId("/test/" + path);
+}
+
+function run_test()
+{
+ insert("abc", "10");
+ insert("def", "11");
+ insert("axy", "12");
+ insert("defghij", "13");
+ insert("defghi", "14");
+
+ do_check_eq(find("abc"), "10");
+ do_check_eq(find("abc123"), "10");
+ do_check_eq(find("def"), "11");
+ do_check_eq(find("axy"), "12");
+ do_check_eq(find("axy1"), "12");
+ do_check_eq(find("defghij"), "13");
+ do_check_eq(find("abd"), "");
+ do_check_eq(find("x"), "");
+
+ insert("file:///home/billm/mozilla/in4/objdir-ff-dbg/dist/bin/browser/extensions/%7B972ce4c6-7e08-4474-a285-3208198ce6fd%7D/", "{972ce4c6-7e08-4474-a285-3208198ce6fd}");
+ insert("file:///home/billm/mozilla/addons/dl-helper-workspace/addon/", "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}");
+
+ do_check_eq(find("file:///home/billm/mozilla/addons/dl-helper-workspace/addon/local/modules/medialist-manager.jsm"), "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
new file mode 100644
index 000000000..11d9f2943
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function () {
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService().wrappedJSObject;
+ let scope = Components.utils.import("resource://gre/modules/osfile.jsm");
+
+ // sync -> async
+ blocklist._loadBlocklist();
+ do_check_true(blocklist._isBlocklistLoaded());
+ yield blocklist._preloadBlocklist();
+ do_check_false(blocklist._isBlocklistPreloaded());
+ blocklist._clear();
+
+ // async -> sync
+ yield blocklist._preloadBlocklist();
+ do_check_false(blocklist._isBlocklistLoaded());
+ do_check_true(blocklist._isBlocklistPreloaded());
+ blocklist._loadBlocklist();
+ do_check_true(blocklist._isBlocklistLoaded());
+ do_check_false(blocklist._isBlocklistPreloaded());
+ blocklist._clear();
+
+ // async -> sync -> async
+ let read = scope.OS.File.read;
+ scope.OS.File.read = function(...args) {
+ return new Promise((resolve, reject) => {
+ do_execute_soon(() => {
+ blocklist._loadBlocklist();
+ resolve(read(...args));
+ });
+ });
+ }
+
+ yield blocklist._preloadBlocklist();
+ do_check_true(blocklist._isBlocklistLoaded());
+ do_check_false(blocklist._isBlocklistPreloaded());
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js b/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
new file mode 100644
index 000000000..d69c33e33
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
@@ -0,0 +1,122 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that background updates & notifications work as expected
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_backgroundupdate.rdf", testserver);
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Verify that with no add-ons installed the background update notifications get
+// called
+function run_test_1() {
+ AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
+ do_check_eq(aAddons.length, 0);
+
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+ do_execute_soon(run_test_2);
+ }, "addons-background-update-complete", false);
+
+ // Trigger the background update timer handler
+ gInternalManager.notify(null);
+ });
+}
+
+// Verify that with two add-ons installed both of which claim to have updates
+// available we get the notification after both updates attempted to start
+function run_test_2() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_backgroundupdate.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_backgroundupdate.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ // Background update uses a different pref, if set
+ Services.prefs.setCharPref("extensions.update.background.url",
+ "http://localhost:" + gPort +"/data/test_backgroundupdate.rdf");
+ restartManager();
+
+ let installCount = 0;
+ let completeCount = 0;
+ let sawCompleteNotification = false;
+
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+ do_check_eq(installCount, 3);
+ sawCompleteNotification = true;
+ }, "addons-background-update-complete", false);
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ installCount++;
+ },
+
+ onDownloadFailed: function(aInstall) {
+ completeCount++;
+ if (completeCount == 3) {
+ do_check_true(sawCompleteNotification);
+ end_test();
+ }
+ }
+ });
+
+ // Trigger the background update timer handler
+ gInternalManager.notify(null);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bad_json.js b/toolkit/mozapps/extensions/test/xpcshell/test_bad_json.js
new file mode 100644
index 000000000..d3ccf68f3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bad_json.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that we rebuild the database correctly if it contains
+// JSON data that parses correctly but doesn't contain required fields
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending("Bad JSON");
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // This addon will be auto-installed at startup
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ startupManager();
+
+ shutdownManager();
+
+ // First startup/shutdown finished
+ // Replace the JSON store with something bogus
+ saveJSON({not: "what we expect to find"}, gExtensionsJSON);
+
+ startupManager(false);
+ // Retrieve an addon to force the database to rebuild
+ AddonManager.getAddonsByIDs([addon1.id], callback_soon(after_db_rebuild));
+}
+
+function after_db_rebuild([a1]) {
+ do_check_eq(a1.id, addon1.id);
+
+ shutdownManager();
+
+ // Make sure our JSON database has schemaVersion and our installed extension
+ let data = loadJSON(gExtensionsJSON);
+ do_check_true("schemaVersion" in data);
+ do_check_eq(data.addons[0].id, addon1.id);
+
+ do_test_finished("Bad JSON");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js b/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
new file mode 100644
index 000000000..6ebf088d6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
@@ -0,0 +1,404 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a database with a bad schema
+
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_corrupt.rdf", testserver);
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be disabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Stays incompatible
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Create and configure the HTTP server.
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a2, a3, a4,
+ a7, t2]) {
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(run_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_execute_soon(run_test_1_modified_db);
+ });
+}
+
+
+function run_test_1_modified_db() {
+ // After restarting the database won't be open so we can alter
+ // the schema
+ shutdownManager();
+ changeXPIDBVersion(100);
+ startupManager();
+
+ // Accessing the add-ons should open and recover the database. Since
+ // migration occurs everything should be recovered correctly
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_execute_soon(run_test_1_after_rebuild);
+ });
+}
+
+function run_test_1_after_rebuild() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
new file mode 100644
index 000000000..15e951bce
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
@@ -0,0 +1,159 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests blocking of extensions by ID, name, creator, homepageURL, updateURL
+// and RegExps for each. See bug 897735.
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_metadata_filters_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = arguments.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ // Should get blocked by name
+ writeInstallRDFForExtension({
+ id: "block1@tests.mozilla.org",
+ version: "1.0",
+ name: "Mozilla Corp.",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ // Should get blocked by all the attributes.
+ writeInstallRDFForExtension({
+ id: "block2@tests.mozilla.org",
+ version: "1.0",
+ name: "Moz-addon",
+ creator: "Dangerous",
+ homepageURL: "www.extension.dangerous.com",
+ updateURL: "www.extension.dangerous.com/update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ // Fails to get blocked because of a different ID even though other
+ // attributes match against a blocklist entry.
+ writeInstallRDFForExtension({
+ id: "block3@tests.mozilla.org",
+ version: "1.0",
+ name: "Moz-addon",
+ creator: "Dangerous",
+ homepageURL: "www.extensions.dangerous.com",
+ updateURL: "www.extension.dangerous.com/update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org",
+ "block3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_blocklist_metadata_filters_1.xml", function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org",
+ "block3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
new file mode 100644
index 000000000..71112387b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
@@ -0,0 +1,159 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests resetting of preferences in blocklist entry when an add-on is blocked.
+// See bug 802434.
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() {
+ return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
+ QueryInterface(Ci.nsIPrefBranch);
+});
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_prefs_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// A window watcher to handle the blocklist UI.
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = arguments.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcherFactory);
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ // Add 2 extensions
+ writeInstallRDFForExtension({
+ id: "block1@tests.mozilla.org",
+ version: "1.0",
+ name: "Blocked add-on-1 with to-be-reset prefs",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "block2@tests.mozilla.org",
+ version: "1.0",
+ name: "Blocked add-on-2 with to-be-reset prefs",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ // Pre-set the preferences that we expect to get reset.
+ gPref.setIntPref("test.blocklist.pref1", 15);
+ gPref.setIntPref("test.blocklist.pref2", 15);
+ gPref.setBoolPref("test.blocklist.pref3", true);
+ gPref.setBoolPref("test.blocklist.pref4", true);
+
+ startupManager();
+
+ // Before blocklist is loaded.
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ do_check_eq(gPref.getIntPref("test.blocklist.pref1"), 15);
+ do_check_eq(gPref.getIntPref("test.blocklist.pref2"), 15);
+ do_check_eq(gPref.getBoolPref("test.blocklist.pref3"), true);
+ do_check_eq(gPref.getBoolPref("test.blocklist.pref4"), true);
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_blocklist_prefs_1.xml", function() {
+ restartManager();
+
+ // Blocklist changes should have applied and the prefs must be reset.
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ do_check_neq(a2, null);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ // All these prefs must be reset to defaults.
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref1"), false);
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref2"), false);
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref3"), false);
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref4"), false);
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
new file mode 100644
index 000000000..d9acf7170
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that blocklist entries using RegExp work as expected. This only covers
+// behavior specific to RegExp entries - general behavior is already tested
+// in test_blocklistchange.js.
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_regexp_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = arguments.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ writeInstallRDFForExtension({
+ id: "block1@tests.mozilla.org",
+ version: "1.0",
+ name: "RegExp blocked add-on",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org"], function([a1]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_blocklist_regexp_1.xml", function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org"], function([a1]) {
+ // Blocklist contains two entries that will match this addon - ensure
+ // that the first one is applied.
+ do_check_neq(a1, null);
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js b/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
new file mode 100644
index 000000000..46f939943
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
@@ -0,0 +1,1321 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that changes that cause an add-on to become unblocked or blocked have
+// the right effect
+
+// The tests follow a mostly common pattern. First they start with the add-ons
+// unblocked, then they make a change that causes the add-ons to become blocked
+// then they make a similar change that keeps the add-ons blocked then they make
+// a change that unblocks the add-ons. Some tests skip the initial part and
+// start with add-ons detected as blocked.
+
+// softblock1 is enabled/disabled by the blocklist changes so its softDisabled
+// property should always match its userDisabled property
+
+// softblock2 gets manually enabled then disabled after it becomes blocked so
+// its softDisabled property should never become true after that
+
+// softblock3 does the same as softblock2 however it remains disabled
+
+// softblock4 is disabled while unblocked and so should never have softDisabled
+// set to true and stay userDisabled. This add-on is not used in tests that
+// start with add-ons blocked as it would be identical to softblock3
+
+// softblock5 is a theme. Currently themes just get disabled when they become
+// softblocked and have to be manually re-enabled if they become completely
+// unblocked (bug 657520)
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+// Allow insecure updates
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false)
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/blocklistchange/addon_update1.rdf", testserver);
+mapFile("/data/blocklistchange/addon_update2.rdf", testserver);
+mapFile("/data/blocklistchange/addon_update3.rdf", testserver);
+mapFile("/data/blocklistchange/addon_change.xml", testserver);
+mapFile("/data/blocklistchange/app_update.xml", testserver);
+mapFile("/data/blocklistchange/blocklist_update1.xml", testserver);
+mapFile("/data/blocklistchange/blocklist_update2.xml", testserver);
+mapFile("/data/blocklistchange/manual_update.xml", testserver);
+
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+
+var default_theme = {
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock1_1 = {
+ id: "softblock1@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock1_2 = {
+ id: "softblock1@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock1_3 = {
+ id: "softblock1@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock2_1 = {
+ id: "softblock2@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock2_2 = {
+ id: "softblock2@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock2_3 = {
+ id: "softblock2@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock3_1 = {
+ id: "softblock3@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock3_2 = {
+ id: "softblock3@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock3_3 = {
+ id: "softblock3@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock4_1 = {
+ id: "softblock4@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock4_2 = {
+ id: "softblock4@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock4_3 = {
+ id: "softblock4@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock5_1 = {
+ id: "softblock5@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock5_2 = {
+ id: "softblock5@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock5_3 = {
+ id: "softblock5@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var hardblock_1 = {
+ id: "hardblock@tests.mozilla.org",
+ version: "1.0",
+ name: "Hardblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var hardblock_2 = {
+ id: "hardblock@tests.mozilla.org",
+ version: "2.0",
+ name: "Hardblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var hardblock_3 = {
+ id: "hardblock@tests.mozilla.org",
+ version: "3.0",
+ name: "Hardblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var regexpblock_1 = {
+ id: "regexpblock@tests.mozilla.org",
+ version: "1.0",
+ name: "RegExp-blocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var regexpblock_2 = {
+ id: "regexpblock@tests.mozilla.org",
+ version: "2.0",
+ name: "RegExp-blocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var regexpblock_3 = {
+ id: "regexpblock@tests.mozilla.org",
+ version: "3.0",
+ name: "RegExp-blocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+const ADDON_IDS = ["softblock1@tests.mozilla.org",
+ "softblock2@tests.mozilla.org",
+ "softblock3@tests.mozilla.org",
+ "softblock4@tests.mozilla.org",
+ "softblock5@tests.mozilla.org",
+ "hardblock@tests.mozilla.org",
+ "regexpblock@tests.mozilla.org"];
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, openArgs) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = openArgs.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var InstallConfirm = {
+ confirm: function(aWindow, aUrl, aInstalls, aInstallCount) {
+ aInstalls.forEach(function(aInstall) {
+ aInstall.install();
+ });
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.amIWebInstallPrompt)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var InstallConfirmFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return InstallConfirm.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+registrar.registerFactory(Components.ID("{f0863905-4dde-42e2-991c-2dc8209bc9ca}"),
+ "Fake Install Prompt",
+ "@mozilla.org/addons/web-install-prompt;1", InstallConfirmFactory);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function Pload_blocklist(aFile) {
+ let blocklist_updated = new Promise((resolve, reject) => {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ resolve();
+ }, "blocklist-updated", false);
+ });
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/blocklistchange/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+ return blocklist_updated;
+}
+
+// Does a background update check for add-ons and returns a promise that
+// resolves when any started installs complete
+function Pbackground_update() {
+ var installCount = 0;
+ var backgroundCheckCompleted = false;
+
+ let updated = new Promise((resolve, reject) => {
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ installCount++;
+ },
+
+ onInstallEnded: function(aInstall) {
+ installCount--;
+ // Wait until all started installs have completed
+ if (installCount)
+ return;
+
+ AddonManager.removeInstallListener(this);
+
+ // If the background check hasn't yet completed then let that call the
+ // callback when it is done
+ if (!backgroundCheckCompleted)
+ return;
+
+ resolve();
+ }
+ })
+
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+ backgroundCheckCompleted = true;
+
+ // If any new installs have started then we'll call the callback once they
+ // are completed
+ if (installCount)
+ return;
+
+ resolve();
+ }, "addons-background-update-complete", false);
+ });
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+ return updated;
+}
+
+// Manually updates the test add-ons to the given version
+function Pmanual_update(aVersion) {
+ let Pinstalls = [];
+ for (let name of ["soft1", "soft2", "soft3", "soft4", "soft5", "hard1", "regexp1"]) {
+ Pinstalls.push(new Promise((resolve, reject) => {
+ AddonManager.getInstallForURL("http://localhost:" + gPort + "/addons/blocklist_"
+ + name + "_" + aVersion + ".xpi",
+ resolve, "application/x-xpinstall");
+ }));
+ }
+
+ return Promise.all(Pinstalls).then(installs => {
+ let completePromises = [];
+ for (let install of installs) {
+ completePromises.push(new Promise(resolve => {
+ install.addListener({
+ onDownloadCancelled: resolve,
+ onInstallEnded: resolve
+ })
+ }));
+ }
+
+ // Use the default web installer to cancel/allow installs based on whether
+ // the add-on is valid or not.
+ let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"]
+ .getService(Ci.amIWebInstallListener);
+ webInstaller.onWebInstallRequested(null, null, installs, installs.length);
+
+ return Promise.all(completePromises);
+ });
+}
+
+// Checks that an add-ons properties match expected values
+function check_addon(aAddon, aExpectedVersion, aExpectedUserDisabled,
+ aExpectedSoftDisabled, aExpectedState) {
+ do_check_neq(aAddon, null);
+ do_print("Testing " + aAddon.id + " version " + aAddon.version + " user "
+ + aAddon.userDisabled + " soft " + aAddon.softDisabled
+ + " perms " + aAddon.permissions);
+
+ do_check_eq(aAddon.version, aExpectedVersion);
+ do_check_eq(aAddon.blocklistState, aExpectedState);
+ do_check_eq(aAddon.userDisabled, aExpectedUserDisabled);
+ do_check_eq(aAddon.softDisabled, aExpectedSoftDisabled);
+ if (aAddon.softDisabled)
+ do_check_true(aAddon.userDisabled);
+
+ if (aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ do_print("blocked, PERM_CAN_ENABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_print("blocked, PERM_CAN_DISABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ }
+ else if (aAddon.userDisabled) {
+ do_print("userDisabled, PERM_CAN_ENABLE " + aAddon.id);
+ do_check_true(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_print("userDisabled, PERM_CAN_DISABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ }
+ else {
+ do_print("other, PERM_CAN_ENABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ if (aAddon.type != "theme") {
+ do_print("other, PERM_CAN_DISABLE " + aAddon.id);
+ do_check_true(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ }
+ }
+ do_check_eq(aAddon.appDisabled, aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ let willBeActive = aAddon.isActive;
+ if (hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE))
+ willBeActive = false;
+ else if (hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE))
+ willBeActive = true;
+
+ if (aExpectedUserDisabled || aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ do_check_false(willBeActive);
+ }
+ else {
+ do_check_true(willBeActive);
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ run_next_test();
+}
+
+add_task(function* init() {
+ writeInstallRDFForExtension(default_theme, profileDir);
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+ startupManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+ s4.userDisabled = true;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then switches application versions to
+// change add-ons to blocked and back
+add_task(function* run_app_update_test() {
+ do_print("Test: " + arguments.callee.name);
+ yield promiseRestartManager();
+ yield Pload_blocklist("app_update.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+});
+
+add_task(function* app_update_step_2() {
+ yield promiseRestartManager("2");
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* app_update_step_3() {
+ yield promiseRestartManager();
+
+ yield promiseRestartManager("2.5");
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* app_update_step_4() {
+ yield promiseRestartManager("1");
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then switches application versions to
+// change add-ons to blocked and back. A DB schema change is faked to force a
+// rebuild when the application version changes
+add_task(function* run_app_update_schema_test() {
+ do_print("Test: " + arguments.callee.name);
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+});
+
+add_task(function* update_schema_2() {
+ yield promiseShutdownManager();
+
+ changeXPIDBVersion(100);
+ gAppInfo.version = "2";
+ startupManager(true);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* update_schema_3() {
+ yield promiseRestartManager();
+
+ yield promiseShutdownManager();
+ changeXPIDBVersion(100);
+ gAppInfo.version = "2.5";
+ startupManager(true);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* update_schema_4() {
+ yield promiseShutdownManager();
+
+ changeXPIDBVersion(100);
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* update_schema_5() {
+ yield promiseShutdownManager();
+
+ changeXPIDBVersion(100);
+ gAppInfo.version = "1";
+ startupManager(true);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then loads new blocklists to change add-ons
+// to blocked and back again.
+add_task(function* run_blocklist_update_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield Pload_blocklist("blocklist_update1.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+
+ yield Pload_blocklist("blocklist_update2.xml");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pload_blocklist("blocklist_update2.xml");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ yield Pload_blocklist("blocklist_update1.xml");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then new versions are installed outside of
+// the app to change them to blocked and back again.
+add_task(function* run_addon_change_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield Pload_blocklist("addon_change.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+});
+
+add_task(function* run_addon_change_2() {
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock2_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock3_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock4_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock5_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(hardblock_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(regexpblock_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_2.id), Date.now() + 10000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* run_addon_change_3() {
+ yield promiseRestartManager();
+
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock2_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock3_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock4_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock5_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(hardblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(regexpblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_3.id), Date.now() + 20000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* run_addon_change_4() {
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_1.id), Date.now() + 30000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons blocked and then new versions are installed outside of
+// the app to change them to unblocked.
+add_task(function* run_addon_change_2_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_2, profileDir);
+ writeInstallRDFForExtension(softblock2_2, profileDir);
+ writeInstallRDFForExtension(softblock3_2, profileDir);
+ writeInstallRDFForExtension(softblock4_2, profileDir);
+ writeInstallRDFForExtension(softblock5_2, profileDir);
+ writeInstallRDFForExtension(hardblock_2, profileDir);
+ writeInstallRDFForExtension(regexpblock_2, profileDir);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* addon_change_2_test_2() {
+ yield promiseRestartManager();
+
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock2_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock3_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock4_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock5_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(hardblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(regexpblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_3.id), Date.now() + 10000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+});
+
+add_task(function* addon_change_2_test_3() {
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_1.id), Date.now() + 20000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s4.userDisabled = true;
+ s5.userDisabled = false;
+});
+
+// Add-ons are initially unblocked then attempts to upgrade to blocked versions
+// in the background which should fail
+add_task(function* run_background_update_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ yield Pbackground_update();
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+});
+
+// Starts with add-ons blocked and then new versions are detected and installed
+// automatically for unblocked versions.
+add_task(function* run_background_update_2_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_3, profileDir);
+ writeInstallRDFForExtension(softblock2_3, profileDir);
+ writeInstallRDFForExtension(softblock3_3, profileDir);
+ writeInstallRDFForExtension(softblock4_3, profileDir);
+ writeInstallRDFForExtension(softblock5_3, profileDir);
+ writeInstallRDFForExtension(hardblock_3, profileDir);
+ writeInstallRDFForExtension(regexpblock_3, profileDir);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pbackground_update();
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s4.userDisabled = true;
+ s5.userDisabled = true;
+});
+
+// Starts with add-ons blocked and then simulates the user upgrading them to
+// unblocked versions.
+add_task(function* run_manual_update_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseRestartManager();
+ yield Pload_blocklist("manual_update.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pmanual_update("2");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ // Can't manually update to a hardblocked add-on
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ yield Pmanual_update("3");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+});
+
+// Starts with add-ons blocked and then new versions are installed outside of
+// the app to change them to unblocked.
+add_task(function* run_manual_update_2_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ yield promiseRestartManager();
+
+ yield Pmanual_update("2");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ // Can't manually update to a hardblocked add-on
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pmanual_update("3");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s4.userDisabled = true;
+});
+
+// Uses the API to install blocked add-ons from the local filesystem
+add_task(function* run_local_install_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+
+ yield promiseInstallAllFiles([
+ do_get_file("addons/blocklist_soft1_1.xpi"),
+ do_get_file("addons/blocklist_soft2_1.xpi"),
+ do_get_file("addons/blocklist_soft3_1.xpi"),
+ do_get_file("addons/blocklist_soft4_1.xpi"),
+ do_get_file("addons/blocklist_soft5_1.xpi"),
+ do_get_file("addons/blocklist_hard1_1.xpi"),
+ do_get_file("addons/blocklist_regexp1_1.xpi")
+ ]);
+
+ let aInstalls = yield new Promise((resolve, reject) => {
+ AddonManager.getAllInstalls(resolve)
+ });
+ // Should have finished all installs without needing to restart
+ do_check_eq(aInstalls.length, 0);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+});
+
+add_task(function* shutdown_httpserver() {
+ yield new Promise((resolve, reject) => {
+ testserver.stop(resolve);
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
new file mode 100644
index 000000000..e8d12c1fa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -0,0 +1,1434 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const APP_STARTUP = 1;
+const APP_SHUTDOWN = 2;
+const ADDON_ENABLE = 3;
+const ADDON_DISABLE = 4;
+const ADDON_INSTALL = 5;
+const ADDON_UNINSTALL = 6;
+const ADDON_UPGRADE = 7;
+const ADDON_DOWNGRADE = 8;
+
+// This verifies that bootstrappable add-ons can be used without restarts.
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+function resetPrefs() {
+ Services.prefs.setIntPref("bootstraptest.active_version", -1);
+ Services.prefs.setIntPref("bootstraptest.installed_version", -1);
+ Services.prefs.setIntPref("bootstraptest2.active_version", -1);
+ Services.prefs.setIntPref("bootstraptest2.installed_version", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.install_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", -1);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", -1);
+}
+
+function waitForPref(aPref, aCallback) {
+ function prefChanged() {
+ Services.prefs.removeObserver(aPref, prefChanged);
+ // Always let whoever set the preference keep running
+ do_execute_soon(aCallback);
+ }
+ Services.prefs.addObserver(aPref, prefChanged, false);
+}
+
+function promisePref(aPref) {
+ let deferred = Promise.defer();
+
+ waitForPref(aPref, deferred.resolve.bind(deferred));
+
+ return deferred.promise;
+}
+
+function promiseInstall(aFiles) {
+ let deferred = Promise.defer();
+
+ installAllFiles(aFiles, function() {
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function getInstalledVersion() {
+ return Services.prefs.getIntPref("bootstraptest.installed_version");
+}
+
+function getActiveVersion2() {
+ return Services.prefs.getIntPref("bootstraptest2.active_version");
+}
+
+function getInstalledVersion2() {
+ return Services.prefs.getIntPref("bootstraptest2.installed_version");
+}
+
+function getStartupReason() {
+ return Services.prefs.getIntPref("bootstraptest.startup_reason");
+}
+
+function getShutdownReason() {
+ return Services.prefs.getIntPref("bootstraptest.shutdown_reason");
+}
+
+function getInstallReason() {
+ return Services.prefs.getIntPref("bootstraptest.install_reason");
+}
+
+function getUninstallReason() {
+ return Services.prefs.getIntPref("bootstraptest.uninstall_reason");
+}
+
+function getStartupOldVersion() {
+ return Services.prefs.getIntPref("bootstraptest.startup_oldversion");
+}
+
+function getShutdownNewVersion() {
+ return Services.prefs.getIntPref("bootstraptest.shutdown_newversion");
+}
+
+function getInstallOldVersion() {
+ return Services.prefs.getIntPref("bootstraptest.install_oldversion");
+}
+
+function getUninstallNewVersion() {
+ return Services.prefs.getIntPref("bootstraptest.uninstall_newversion");
+}
+
+function do_check_bootstrappedPref(aCallback) {
+ let data = Services.prefs.getCharPref("extensions.bootstrappedAddons");
+ data = JSON.parse(data);
+
+ AddonManager.getAddonsByTypes(["extension"], function(aAddons) {
+ for (let addon of aAddons) {
+ if (!addon.id.endsWith("@tests.mozilla.org"))
+ continue;
+ if (!addon.isActive)
+ continue;
+ if (addon.operationsRequiringRestart != AddonManager.OP_NEEDS_RESTART_NONE)
+ continue;
+
+ do_check_true(addon.id in data);
+ let addonData = data[addon.id];
+ delete data[addon.id];
+
+ do_check_eq(addonData.version, addon.version);
+ do_check_eq(addonData.type, addon.type);
+ let file = addon.getResourceURI().QueryInterface(Components.interfaces.nsIFileURL).file;
+ do_check_eq(addonData.descriptor, file.persistentDescriptor);
+ }
+ do_check_eq(Object.keys(data).length, 0);
+
+ do_execute_soon(aCallback);
+ });
+}
+
+
+function run_test() {
+ do_test_pending();
+
+ resetPrefs();
+
+ startupManager();
+
+ do_check_false(gExtensionsJSON.exists());
+
+ do_check_false(gExtensionsINI.exists());
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.addon.syncGUID, null);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_true(install.addon.hasResource("bootstrap.js"));
+ do_check_false(install.addon.hasResource("foo.bar"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ let addon = install.addon;
+
+ waitForPref("bootstraptest.startup_reason", function() {
+ do_check_bootstrappedPref(function() {
+ check_test_1(addon.syncGUID);
+ });
+ });
+
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+
+ // startup should not have been called yet.
+ do_check_eq(getActiveVersion(), -1);
+ });
+ install.install();
+ });
+}
+
+function check_test_1(installSyncGUID) {
+ do_check_false(gExtensionsINI.exists());
+
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_neq(b1.syncGUID, null);
+ do_check_eq(b1.syncGUID, installSyncGUID);
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_true(b1.hasResource("bootstrap.js"));
+ do_check_false(b1.hasResource("foo.bar"));
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, "bootstrap1@tests.mozilla.org");
+ do_check_eq(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 0);
+
+ do_execute_soon(run_test_2);
+ });
+ });
+ });
+}
+
+// Tests that disabling doesn't require a restart
+function run_test_2() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_true(newb1.userDisabled);
+ do_check_false(newb1.isActive);
+
+ do_check_bootstrappedPref(run_test_3);
+ });
+ });
+}
+
+// Test that restarting doesn't accidentally re-enable
+function run_test_3() {
+ shutdownManager();
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), 0);
+ startupManager(false);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ do_check_false(gExtensionsINI.exists());
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ do_check_bootstrappedPref(run_test_4);
+ });
+}
+
+// Tests that enabling doesn't require a restart
+function run_test_4() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), ADDON_ENABLE);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_false(newb1.userDisabled);
+ do_check_true(newb1.isActive);
+
+ do_check_bootstrappedPref(run_test_5);
+ });
+ });
+}
+
+// Tests that a restart shuts down and restarts the add-on
+function run_test_5() {
+ shutdownManager();
+ // By the time we've shut down, the database must have been written
+ do_check_true(gExtensionsJSON.exists());
+
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+ startupManager(false);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), APP_STARTUP);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, b1.id));
+
+ do_check_bootstrappedPref(run_test_6);
+ });
+}
+
+// Tests that installing an upgrade doesn't require a restart
+function run_test_6() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ waitForPref("bootstraptest.startup_reason", check_test_6);
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ });
+ install.install();
+ });
+}
+
+function check_test_6() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+ do_check_eq(getStartupReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupOldVersion(), 1);
+ do_check_eq(getShutdownReason(), ADDON_UPGRADE);
+ do_check_eq(getShutdownNewVersion(), 2);
+ do_check_eq(getUninstallNewVersion(), 2);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
+
+ do_check_bootstrappedPref(run_test_7);
+ });
+}
+
+// Tests that uninstalling doesn't require a restart
+function run_test_7() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ b1.uninstall();
+
+ do_check_bootstrappedPref(check_test_7);
+ });
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_eq(b1, null);
+
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ do_check_eq(newb1, null);
+
+ do_check_bootstrappedPref(run_test_8);
+ });
+ }));
+}
+
+// Test that a bootstrapped extension dropped into the profile loads properly
+// on startup and doesn't cause an EM restart
+function run_test_8() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
+ "bootstrap1@tests.mozilla.org");
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ do_check_bootstrappedPref(run_test_9);
+ });
+}
+
+// Test that items detected as removed during startup get removed properly
+function run_test_9() {
+ shutdownManager();
+
+ manuallyUninstall(profileDir, "bootstrap1@tests.mozilla.org");
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_eq(b1, null);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ do_check_bootstrappedPref(run_test_10);
+ });
+}
+
+
+// Tests that installing a downgrade sends the right reason
+function run_test_10() {
+ resetPrefs();
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_true(install.addon.hasResource("bootstrap.js"));
+ do_check_false(install.addon.hasResource("foo.bar"));
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
+
+ waitForPref("bootstraptest.startup_reason", check_test_10_pt1);
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_print("Waiting for startup of bootstrap1_2");
+ });
+ install.install();
+ });
+}
+
+function check_test_10_pt1() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_true(b1.hasResource("bootstrap.js"));
+ do_check_false(b1.hasResource("foo.bar"));
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ waitForPref("bootstraptest.startup_reason", check_test_10_pt2);
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() { });
+ install.install();
+ });
+ });
+}
+
+function check_test_10_pt2() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 2);
+ do_check_eq(getStartupOldVersion(), 2);
+ do_check_eq(getShutdownReason(), ADDON_DOWNGRADE);
+ do_check_eq(getShutdownNewVersion(), 1);
+ do_check_eq(getUninstallNewVersion(), 1);
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
+
+ do_check_bootstrappedPref(run_test_11);
+ });
+}
+
+// Tests that uninstalling a disabled add-on still calls the uninstall method
+function run_test_11() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ b1.userDisabled = true;
+
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ b1.uninstall();
+
+ check_test_11();
+ });
+}
+
+function check_test_11() {
+ ensure_test_completed();
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ do_check_bootstrappedPref(run_test_12);
+}
+
+// Tests that bootstrapped extensions are correctly loaded even if the app is
+// upgraded at the same time
+function run_test_12() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
+ "bootstrap1@tests.mozilla.org");
+
+ startupManager(true);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ b1.uninstall();
+ do_execute_soon(test_12_restart);
+ });
+}
+
+function test_12_restart() {
+ restartManager();
+ do_check_bootstrappedPref(run_test_13);
+}
+
+
+// Tests that installing a bootstrapped extension with an invalid application
+// entry doesn't call it's startup method
+function run_test_13() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_3"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "3.0");
+
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_13));
+ install.install();
+ });
+}
+
+function check_test_13() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "3.0");
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 3); // We call install even for disabled add-ons
+ do_check_eq(getActiveVersion(), 0); // Should not have called startup though
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "3.0");
+
+ do_execute_soon(test_13_restart);
+ });
+ });
+}
+
+function test_13_restart() {
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "3.0");
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 3); // We call install even for disabled add-ons
+ do_check_eq(getActiveVersion(), 0); // Should not have called startup though
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "3.0");
+
+ do_check_bootstrappedPref(function() {
+ b1.uninstall();
+ do_execute_soon(run_test_14);
+ });
+ });
+}
+
+// Tests that a bootstrapped extension with an invalid target application entry
+// does not get loaded when detected during startup
+function run_test_14() {
+ restartManager();
+
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_3"), profileDir,
+ "bootstrap1@tests.mozilla.org");
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "3.0");
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 3); // We call install even for disabled add-ons
+ do_check_eq(getActiveVersion(), 0); // Should not have called startup though
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "3.0");
+
+ do_check_bootstrappedPref(function() {
+ b1.uninstall();
+
+ run_test_15();
+ });
+ });
+}
+
+// Tests that upgrading a disabled bootstrapped extension still calls uninstall
+// and install but doesn't startup the new version
+function run_test_15() {
+ resetPrefs();
+ waitForPref("bootstraptest.startup_reason", function test_15_after_startup() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+
+ b1.userDisabled = true;
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_true(install.addon.userDisabled);
+
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_15));
+ install.install();
+ });
+ });
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function test_15_addon_installed() { });
+}
+
+function check_test_15() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 0);
+
+ do_check_bootstrappedPref(function() {
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 0);
+
+ b1.uninstall();
+
+ run_test_16();
+ }));
+ });
+ });
+}
+
+// Tests that bootstrapped extensions don't get loaded when in safe mode
+function run_test_16() {
+ resetPrefs();
+ waitForPref("bootstraptest.startup_reason", function test_16_after_startup() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_true(b1.isActive);
+ do_check_eq(b1.iconURL, "chrome://foo/skin/icon.png");
+ do_check_eq(b1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(b1.optionsURL, "chrome://foo/content/options.xul");
+
+ shutdownManager();
+
+ // Should have stopped
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+
+ gAppInfo.inSafeMode = true;
+ startupManager(false);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ // Should still be stopped
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_false(b1.isActive);
+ do_check_eq(b1.iconURL, null);
+ do_check_eq(b1.aboutURL, null);
+ do_check_eq(b1.optionsURL, null);
+
+ shutdownManager();
+ gAppInfo.inSafeMode = false;
+ startupManager(false);
+
+ // Should have started
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ b1.uninstall();
+
+ do_execute_soon(run_test_17);
+ });
+ }));
+ }));
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function() { });
+}
+
+// Check that a bootstrapped extension in a non-profile location is loaded
+function run_test_17() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), userExtDir,
+ "bootstrap1@tests.mozilla.org");
+
+ resetPrefs();
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+
+ do_check_bootstrappedPref(run_test_18);
+ });
+}
+
+// Check that installing a new bootstrapped extension in the profile replaces
+// the existing one
+function run_test_18() {
+ resetPrefs();
+ waitForPref("bootstraptest.startup_reason", function test_18_after_startup() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+
+ do_check_eq(getShutdownReason(), ADDON_UPGRADE);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallReason(), ADDON_UPGRADE);
+ do_check_eq(getStartupReason(), ADDON_UPGRADE);
+
+ do_check_eq(getShutdownNewVersion(), 2);
+ do_check_eq(getUninstallNewVersion(), 2);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupOldVersion(), 1);
+
+ do_check_bootstrappedPref(run_test_19);
+ });
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_2")], function() { });
+}
+
+// Check that uninstalling the profile version reveals the non-profile one
+function run_test_19() {
+ resetPrefs();
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // The revealed add-on gets activated asynchronously
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled",
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [], check_test_19);
+
+ b1.uninstall();
+ });
+}
+
+function check_test_19() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // Should have reverted to the older version
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+
+ // TODO these reasons really should be ADDON_DOWNGRADE (bug 607818)
+ do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
+ do_check_eq(getUninstallReason(), ADDON_UNINSTALL);
+ do_check_eq(getInstallReason(), ADDON_INSTALL);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_eq(getUninstallNewVersion(), 0);
+ do_check_eq(getInstallOldVersion(), 0);
+ do_check_eq(getStartupOldVersion(), 0);
+
+ do_check_bootstrappedPref(run_test_20);
+ });
+}
+
+// Check that a new profile extension detected at startup replaces the non-profile
+// one
+function run_test_20() {
+ resetPrefs();
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir,
+ "bootstrap1@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallReason(), ADDON_UPGRADE);
+ do_check_eq(getStartupReason(), APP_STARTUP);
+
+ do_check_eq(getShutdownNewVersion(), 0);
+ do_check_eq(getUninstallNewVersion(), 2);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupOldVersion(), 0);
+
+ do_execute_soon(run_test_21);
+ });
+}
+
+// Check that a detected removal reveals the non-profile one
+function run_test_21() {
+ resetPrefs();
+ shutdownManager();
+
+ manuallyUninstall(profileDir, "bootstrap1@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(), 0);
+
+ // This won't be set as the bootstrap script was gone so we couldn't
+ // uninstall it properly
+ do_check_eq(getUninstallReason(), -1);
+ do_check_eq(getUninstallNewVersion(), -1);
+
+ // TODO this reason should probably be ADDON_DOWNGRADE (bug 607818)
+ do_check_eq(getInstallReason(), ADDON_INSTALL);
+ do_check_eq(getInstallOldVersion(), 0);
+
+ do_check_eq(getStartupReason(), APP_STARTUP);
+ do_check_eq(getStartupOldVersion(), 0);
+
+ do_check_bootstrappedPref(function() {
+ manuallyUninstall(userExtDir, "bootstrap1@tests.mozilla.org");
+
+ restartManager();
+ run_test_22();
+ });
+ });
+}
+
+// Check that an upgrade from the filesystem is detected and applied correctly
+function run_test_22() {
+ shutdownManager();
+
+ let file = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
+ "bootstrap1@tests.mozilla.org");
+
+ // Make it look old so changes are detected
+ setExtensionModifiedTime(file, file.lastModifiedTime - 5000);
+
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+
+ resetPrefs();
+ shutdownManager();
+
+ manuallyUninstall(profileDir, "bootstrap1@tests.mozilla.org");
+ manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir,
+ "bootstrap1@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ // Should have installed and started
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(), 0);
+
+ // This won't be set as the bootstrap script was gone so we couldn't
+ // uninstall it properly
+ do_check_eq(getUninstallReason(), -1);
+ do_check_eq(getUninstallNewVersion(), -1);
+
+ do_check_eq(getInstallReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupReason(), APP_STARTUP);
+ do_check_eq(getStartupOldVersion(), 0);
+
+ do_check_bootstrappedPref(function() {
+ b1.uninstall();
+
+ run_test_23();
+ });
+ });
+ }));
+}
+
+
+// Tests that installing from a URL doesn't require a restart
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_bootstrap1_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], function() {
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_true(install.addon.hasResource("bootstrap.js"));
+ do_check_false(install.addon.hasResource("foo.bar"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ let addon = install.addon;
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ do_check_bootstrappedPref(check_test_23);
+ });
+ });
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_23() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_execute_soon(function test_23_after_startup() {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), 0);
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_true(b1.hasResource("bootstrap.js"));
+ do_check_false(b1.hasResource("foo.bar"));
+ do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, "bootstrap1@tests.mozilla.org");
+ do_check_eq(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 0);
+
+ restartManager();
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ b1.uninstall();
+ restartManager();
+
+ testserver.stop(run_test_24);
+ }));
+ }));
+ });
+ });
+ });
+}
+
+// Tests that we recover from a broken preference
+function run_test_24() {
+ resetPrefs();
+ do_print("starting 24");
+
+ Promise.all([promisePref("bootstraptest2.active_version"),
+ promiseInstall([do_get_addon("test_bootstrap1_1"), do_get_addon("test_bootstrap2_1")])])
+ .then(function test_24_pref() {
+ do_print("test 24 got prefs");
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getInstalledVersion2(), 1);
+ do_check_eq(getActiveVersion2(), 1);
+
+ resetPrefs();
+
+ restartManager();
+
+ do_check_eq(getInstalledVersion(), -1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getInstalledVersion2(), -1);
+ do_check_eq(getActiveVersion2(), 1);
+
+ shutdownManager();
+
+ do_check_eq(getInstalledVersion(), -1);
+ do_check_eq(getActiveVersion(), 0);
+ do_check_eq(getInstalledVersion2(), -1);
+ do_check_eq(getActiveVersion2(), 0);
+
+ // Break the preferece
+ let bootstrappedAddons = JSON.parse(Services.prefs.getCharPref("extensions.bootstrappedAddons"));
+ bootstrappedAddons["bootstrap1@tests.mozilla.org"].descriptor += "foo";
+ Services.prefs.setCharPref("extensions.bootstrappedAddons", JSON.stringify(bootstrappedAddons));
+
+ startupManager(false);
+
+ do_check_eq(getInstalledVersion(), -1);
+ do_check_eq(getActiveVersion(), 1);
+ do_check_eq(getInstalledVersion2(), -1);
+ do_check_eq(getActiveVersion2(), 1);
+
+ run_test_25();
+ });
+}
+
+// Tests that updating from a bootstrappable add-on to a normal add-on calls
+// the uninstall method
+function run_test_25() {
+ waitForPref("bootstraptest.startup_reason", function test_25_after_pref() {
+ do_print("test 25 pref change detected");
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+
+ installAllFiles([do_get_addon("test_bootstrap1_4")], function() {
+ // Needs a restart to complete this so the old version stays running
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getUninstallNewVersion(), 4);
+ do_check_eq(getActiveVersion(), 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "4.0");
+ do_check_true(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_bootstrappedPref(run_test_26);
+ });
+ }));
+ });
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function test_25_installed() {
+ do_print("test 25 install done");
+ });
+}
+
+// Tests that updating from a normal add-on to a bootstrappable add-on calls
+// the install method
+function run_test_26() {
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function() {
+ // Needs a restart to complete this
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getActiveVersion(), 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "4.0");
+ do_check_true(b1.isActive);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getInstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 4);
+ do_check_eq(getActiveVersion(), 1);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_bootstrappedPref(run_test_27);
+ });
+ }));
+ });
+}
+
+// Tests that updating from a bootstrappable add-on to a normal add-on while
+// disabled calls the uninstall method
+function run_test_27() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ b1.userDisabled = true;
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+
+ installAllFiles([do_get_addon("test_bootstrap1_4")], function() {
+ // Updating disabled things happens immediately
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getUninstallNewVersion(), 4);
+ do_check_eq(getActiveVersion(), 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "4.0");
+ do_check_false(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ restartManager();
+
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getActiveVersion(), 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "4.0");
+ do_check_false(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_bootstrappedPref(run_test_28);
+ });
+ }));
+ });
+ });
+}
+
+// Tests that updating from a normal add-on to a bootstrappable add-on when
+// disabled calls the install method but not the startup method
+function run_test_28() {
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function() {
+ do_execute_soon(function bootstrap_disabled_downgrade_check() {
+ // Doesn't need a restart to complete this
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getInstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 4);
+ do_check_eq(getActiveVersion(), 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.isActive);
+ do_check_true(b1.userDisabled);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ restartManager();
+
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 0);
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_true(b1.userDisabled);
+ b1.userDisabled = false;
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_eq(getInstalledVersion(), 1);
+ do_check_eq(getActiveVersion(), 1);
+
+ do_check_bootstrappedPref(do_test_finished);
+ });
+ }));
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_globals.js b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_globals.js
new file mode 100644
index 000000000..2243a21a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_globals.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that bootstrap.js has the expected globals defined
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+const EXPECTED_GLOBALS = [
+ ["Worker", "function"],
+ ["ChromeWorker", "function"],
+ ["console", "object"]
+];
+
+function run_test() {
+ do_test_pending();
+ startupManager();
+ let sawGlobals = false;
+
+ Services.obs.addObserver(function(subject) {
+ subject.wrappedJSObject.expectedGlobals = EXPECTED_GLOBALS;
+ }, "bootstrap-request-globals", false);
+
+ Services.obs.addObserver(function({ wrappedJSObject: seenGlobals }) {
+ for (let [name,] of EXPECTED_GLOBALS)
+ do_check_true(seenGlobals.has(name));
+
+ sawGlobals = true;
+ }, "bootstrap-seen-globals", false);
+
+ installAllFiles([do_get_addon("bootstrap_globals")], function() {
+ do_check_true(sawGlobals);
+ shutdownManager();
+ do_test_finished();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_resource.js b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_resource.js
new file mode 100644
index 000000000..7b7883225
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_resource.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that resource protocol substitutions are set and unset for bootstrapped add-ons.
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ let resourceProtocol = Services.io.getProtocolHandler("resource")
+ .QueryInterface(Components.interfaces.nsIResProtocolHandler);
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_6")],
+ function() {
+
+ AddonManager.getAddonByID("addon6@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+ do_check_true(resourceProtocol.hasSubstitution("test-addon-1"));
+
+ prepare_test({
+ "addon6@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ addon.userDisabled = true;
+ ensure_test_completed();
+ do_check_false(resourceProtocol.hasSubstitution("test-addon-1"))
+
+ prepare_test({
+ "addon6@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ addon.userDisabled = false;
+ ensure_test_completed();
+ do_check_true(resourceProtocol.hasSubstitution("test-addon-1"));
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug299716.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug299716.js
new file mode 100644
index 000000000..5de941f32
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug299716.js
@@ -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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Update check listener.
+const checkListener = {
+ pendingCount: 0,
+
+ onUpdateAvailable: function onUpdateAvailable(aAddon, aInstall) {
+ for (let currentAddon of ADDONS) {
+ if (currentAddon.id == aAddon.id) {
+ currentAddon.newInstall = aInstall;
+ return;
+ }
+ }
+ },
+
+ onUpdateFinished: function onUpdateFinished() {
+ if (--this.pendingCount == 0)
+ next_test();
+ }
+}
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+var ADDONS = [
+ // XPCShell
+ {
+ id: "bug299716-a@tests.mozilla.org",
+ addon: "test_bug299716_a_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // Toolkit
+ {
+ id: "bug299716-b@tests.mozilla.org",
+ addon: "test_bug299716_b_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // XPCShell + Toolkit
+ {
+ id: "bug299716-c@tests.mozilla.org",
+ addon: "test_bug299716_c_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // XPCShell (Toolkit invalid)
+ {
+ id: "bug299716-d@tests.mozilla.org",
+ addon: "test_bug299716_d_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // Toolkit (XPCShell invalid)
+ {
+ id: "bug299716-e@tests.mozilla.org",
+ addon: "test_bug299716_e_1",
+ installed: false,
+ item: null,
+ newInstall: null,
+ failedAppName: "XPCShell"
+ },
+
+ // None (XPCShell, Toolkit invalid)
+ {
+ id: "bug299716-f@tests.mozilla.org",
+ addon: "test_bug299716_f_1",
+ installed: false,
+ item: null,
+ newInstall: null,
+ failedAppName: "XPCShell"
+ },
+
+ // None (Toolkit invalid)
+ {
+ id: "bug299716-g@tests.mozilla.org",
+ addon: "test_bug299716_g_1",
+ installed: false,
+ item: null,
+ newInstall: null,
+ failedAppName: "Toolkit"
+ },
+];
+
+var next_test = function() {};
+
+function do_check_item(aItem, aVersion, aAddonsEntry) {
+ if (aAddonsEntry.installed) {
+ if (aItem == null)
+ do_throw("Addon " + aAddonsEntry.id + " wasn't detected");
+ if (aItem.version != aVersion)
+ do_throw("Addon " + aAddonsEntry.id + " was version " + aItem.version + " instead of " + aVersion);
+ } else {
+ if (aItem != null)
+ do_throw("Addon " + aAddonsEntry.id + " was detected");
+ }
+}
+
+/**
+ * Start the test by installing extensions.
+ */
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "5", "1.9");
+
+ const dataDir = do_get_file("data");
+ const addonsDir = do_get_addon(ADDONS[0].addon).parent;
+
+ // Make sure we can actually get our data files.
+ const xpiFile = addonsDir.clone();
+ xpiFile.append("test_bug299716_a_2.xpi");
+ do_check_true(xpiFile.exists());
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", addonsDir);
+ testserver.registerDirectory("/data/", dataDir);
+ testserver.start(4444);
+
+ // Make sure we can fetch the files over HTTP.
+ const Ci = Components.interfaces;
+ const xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest)
+ xhr.open("GET", "http://localhost:4444/addons/test_bug299716_a_2.xpi", false);
+ xhr.send(null);
+ do_check_true(xhr.status == 200);
+
+ xhr.open("GET", "http://localhost:4444/data/test_bug299716.rdf", false);
+ xhr.send(null);
+ do_check_true(xhr.status == 200);
+
+ // Start the real test.
+ startupManager();
+ dump("\n\n*** INSTALLING NEW ITEMS\n\n");
+
+ installAllFiles([do_get_addon(a.addon) for each (a in ADDONS)], run_test_pt2,
+ true);
+}
+
+/**
+ * Check the versions of all items, and ask the extension manager to find updates.
+ */
+function run_test_pt2() {
+ dump("\n\n*** DONE INSTALLING NEW ITEMS\n\n");
+ dump("\n\n*** RESTARTING EXTENSION MANAGER\n\n");
+ restartManager();
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(items) {
+ dump("\n\n*** REQUESTING UPDATE\n\n");
+ // checkListener will call run_test_pt3().
+ next_test = run_test_pt3;
+
+ // Try to update the items.
+ for (var i = 0; i < ADDONS.length; i++) {
+ var item = items[i];
+ do_check_item(item, "0.1", ADDONS[i]);
+
+ if (item) {
+ checkListener.pendingCount++;
+ ADDONS[i].item = item;
+ item.findUpdates(checkListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ }
+ }
+ });
+}
+
+/**
+ * Install new items for each enabled extension.
+ */
+function run_test_pt3() {
+ // Install the new items.
+ dump("\n\n*** UPDATING ITEMS\n\n");
+ completeAllInstalls([a.newInstall for each(a in ADDONS) if (a.newInstall)],
+ run_test_pt4);
+}
+
+/**
+ * Check the final version of each extension.
+ */
+function run_test_pt4() {
+ dump("\n\n*** RESTARTING EXTENSION MANAGER\n\n");
+ restartManager();
+
+ dump("\n\n*** FINAL CHECKS\n\n");
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(items) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ var item = items[i];
+ do_check_item(item, "0.2", ADDONS[i]);
+ }
+
+ testserver.stop(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug299716_2.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug299716_2.js
new file mode 100644
index 000000000..c183edad4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug299716_2.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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+var ADDON = {
+ id: "bug299716-2@tests.mozilla.org",
+ addon: "test_bug299716_2"
+};
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9");
+
+ const dataDir = do_get_file("data");
+ const addonsDir = do_get_addon(ADDON.addon).parent;
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", addonsDir);
+ testserver.registerDirectory("/data/", dataDir);
+ testserver.start(4444);
+
+ startupManager();
+
+ installAllFiles([do_get_addon(ADDON.addon)], function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ADDON.id, function(item) {
+ do_check_eq(item.version, 0.1);
+ do_check_false(item.isCompatible);
+
+ item.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_false(item.isCompatible);
+
+ testserver.stop(do_test_finished);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug324121.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug324121.js
new file mode 100644
index 000000000..b88c07b23
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug324121.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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+var next_test = null;
+var gItemsNotChecked =[];
+
+var ADDONS = [ {id: "bug324121_1@tests.mozilla.org",
+ addon: "test_bug324121_1",
+ shouldCheck: false },
+ {id: "bug324121_2@tests.mozilla.org",
+ addon: "test_bug324121_2",
+ shouldCheck: true },
+ {id: "bug324121_3@tests.mozilla.org",
+ addon: "test_bug324121_3",
+ shouldCheck: true },
+ {id: "bug324121_4@tests.mozilla.org",
+ addon: "test_bug324121_4",
+ shouldCheck: true },
+ {id: "bug324121_5@tests.mozilla.org",
+ addon: "test_bug324121_5",
+ shouldCheck: false },
+ {id: "bug324121_6@tests.mozilla.org",
+ addon: "test_bug324121_6",
+ shouldCheck: true },
+ {id: "bug324121_7@tests.mozilla.org",
+ addon: "test_bug324121_7",
+ shouldCheck: true },
+ {id: "bug324121_8@tests.mozilla.org",
+ addon: "test_bug324121_8",
+ shouldCheck: true },
+ {id: "bug324121_9@tests.mozilla.org",
+ addon: "test_bug324121_9",
+ shouldCheck: false } ];
+
+// nsIAddonUpdateCheckListener
+var updateListener = {
+ pendingCount: 0,
+
+ onUpdateAvailable: function onAddonUpdateEnded(aAddon) {
+ switch (aAddon.id) {
+ // add-on disabled - should not happen
+ case "bug324121_1@tests.mozilla.org":
+ // app id already compatible - should not happen
+ case "bug324121_5@tests.mozilla.org":
+ // toolkit id already compatible - should not happen
+ case "bug324121_9@tests.mozilla.org":
+ do_throw("Should not have seen an update check for " + aAddon.id);
+ break;
+
+ // app id incompatible update available
+ case "bug324121_3@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_4@tests.mozilla.org":
+ // toolkit id incompatible update available
+ case "bug324121_7@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_8@tests.mozilla.org":
+ do_throw("Should be no update available for " + aAddon.id);
+ break;
+
+ // Updates available
+ case "bug324121_2@tests.mozilla.org":
+ case "bug324121_6@tests.mozilla.org":
+ break;
+
+ default:
+ do_throw("Update check for unknown " + aAddon.id);
+ }
+
+ // pos should always be >= 0 so just let this throw if this fails
+ var pos = gItemsNotChecked.indexOf(aAddon.id);
+ gItemsNotChecked.splice(pos, 1);
+ },
+
+ onNoUpdateAvailable: function onNoUpdateAvailable(aAddon) {
+ switch (aAddon.id) {
+ // add-on disabled - should not happen
+ case "bug324121_1@tests.mozilla.org":
+ // app id already compatible - should not happen
+ case "bug324121_5@tests.mozilla.org":
+ // toolkit id already compatible - should not happen
+ case "bug324121_9@tests.mozilla.org":
+ do_throw("Should not have seen an update check for " + aAddon.id);
+ break;
+
+ // app id incompatible update available
+ case "bug324121_3@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_4@tests.mozilla.org":
+ // toolkit id incompatible update available
+ case "bug324121_7@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_8@tests.mozilla.org":
+ break;
+
+ // Updates available
+ case "bug324121_2@tests.mozilla.org":
+ case "bug324121_6@tests.mozilla.org":
+ do_throw("Should be an update available for " + aAddon.id);
+ break;
+
+ default:
+ do_throw("Update check for unknown " + aAddon.id);
+ }
+
+ // pos should always be >= 0 so just let this throw if this fails
+ var pos = gItemsNotChecked.indexOf(aAddon.id);
+ gItemsNotChecked.splice(pos, 1);
+ },
+
+ onUpdateFinished: function onUpdateFinished(aAddon) {
+ if (--this.pendingCount == 0)
+ test_complete();
+ }
+};
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ const dataDir = do_get_file("data");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", dataDir);
+ testserver.start(4444);
+
+ startupManager();
+
+ installAllFiles([do_get_addon(a.addon) for each (a in ADDONS)], function() {
+ restartManager();
+ AddonManager.getAddonByID(ADDONS[0].id, callback_soon(function(addon) {
+ do_check_true(!(!addon));
+ addon.userDisabled = true;
+ restartManager();
+
+ AddonManager.getAddonsByTypes(["extension"], function(installedItems) {
+ var items = [];
+
+ for (let addon of ADDONS) {
+ for (let installedItem of installedItems) {
+ if (addon.id != installedItem.id)
+ continue;
+ if (installedItem.userDisabled)
+ continue;
+
+ if (addon.shouldCheck == installedItem.isCompatibleWith("3", "3")) {
+ do_throw(installedItem.id + " had the wrong compatibility: " +
+ installedItem.isCompatibleWith("3", "3"));
+ }
+
+ if (addon.shouldCheck) {
+ gItemsNotChecked.push(addon.id);
+ updateListener.pendingCount++;
+ installedItem.findUpdates(updateListener,
+ AddonManager.UPDATE_WHEN_USER_REQUESTED,
+ "3", "3");
+ }
+ }
+ }
+ });
+ }));
+ });
+}
+
+function test_complete() {
+ do_check_eq(gItemsNotChecked.length, 0);
+ testserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
new file mode 100644
index 000000000..e691bb570
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.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/.
+ */
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://testing-common/httpd.js");
+
+// This is the data we expect to see sent as part of the update url.
+var EXPECTED = [
+ {
+ id: "bug335238_1@tests.mozilla.org",
+ version: "1.3.4",
+ maxAppVersion: "5",
+ status: "userEnabled",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ },
+ {
+ id: "bug335238_2@tests.mozilla.org",
+ version: "28at",
+ maxAppVersion: "7",
+ status: "userDisabled",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ },
+ {
+ id: "bug335238_3@tests.mozilla.org",
+ version: "58",
+ maxAppVersion: "*",
+ status: "userDisabled,softblocked",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ },
+ {
+ id: "bug335238_4@tests.mozilla.org",
+ version: "4",
+ maxAppVersion: "2+",
+ status: "userEnabled,blocklisted",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ }
+];
+
+var ADDONS = [
+ {id: "bug335238_1@tests.mozilla.org",
+ addon: "test_bug335238_1"},
+ {id: "bug335238_2@tests.mozilla.org",
+ addon: "test_bug335238_2"},
+ {id: "bug335238_3@tests.mozilla.org",
+ addon: "test_bug335238_3"},
+ {id: "bug335238_4@tests.mozilla.org",
+ addon: "test_bug335238_4"}
+];
+
+// This is a replacement for the blocklist service
+var BlocklistService = {
+ getAddonBlocklistState: function(aAddon, aAppVersion, aToolkitVersion) {
+ if (aAddon.id == "bug335238_3@tests.mozilla.org")
+ return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
+ if (aAddon.id == "bug335238_4@tests.mozilla.org")
+ return Ci.nsIBlocklistService.STATE_BLOCKED;
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ },
+
+ getPluginBlocklistState: function(aPlugin, aVersion, aAppVersion, aToolkitVersion) {
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ },
+
+ isAddonBlocklisted: function(aAddon, aAppVersion, aToolkitVersion) {
+ return this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) ==
+ Ci.nsIBlocklistService.STATE_BLOCKED;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIBlocklistService)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var BlocklistServiceFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return BlocklistService.QueryInterface(iid);
+ }
+};
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{61189e7a-6b1b-44b8-ac81-f180a6105085}"), "BlocklistService",
+ "@mozilla.org/extensions/blocklist;1", BlocklistServiceFactory);
+
+var server;
+
+var updateListener = {
+ pendingCount: 0,
+
+ onUpdateAvailable: function(aAddon) {
+ do_throw("Should not have seen an update for " + aAddon.id);
+ },
+
+ onUpdateFinished: function() {
+ if (--this.pendingCount == 0)
+ server.stop(do_test_finished);
+ }
+}
+
+var requestHandler = {
+ handle: function(metadata, response)
+ {
+ var expected = EXPECTED[metadata.path.substring(1)];
+ var params = metadata.queryString.split("&");
+ do_check_eq(params.length, 10);
+ for (var k in params) {
+ var pair = params[k].split("=");
+ var name = decodeURIComponent(pair[0]);
+ var value = decodeURIComponent(pair[1]);
+ do_check_eq(expected[name], value);
+ }
+ response.setStatusLine(metadata.httpVersion, 404, "Not Found");
+ }
+}
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ server = new HttpServer();
+ server.registerPathHandler("/0", requestHandler);
+ server.registerPathHandler("/1", requestHandler);
+ server.registerPathHandler("/2", requestHandler);
+ server.registerPathHandler("/3", requestHandler);
+ server.start(4444);
+
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "en-US");
+
+ startupManager();
+ installAllFiles([do_get_addon(a.addon) for each (a in ADDONS)], function() {
+
+ restartManager();
+ AddonManager.getAddonByID(ADDONS[1].id, callback_soon(function(addon) {
+ do_check_true(!(!addon));
+ addon.userDisabled = true;
+ restartManager();
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(installedItems) {
+ installedItems.forEach(function(item) {
+ updateListener.pendingCount++;
+ item.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+ }));
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug371495.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug371495.js
new file mode 100644
index 000000000..3a80c1945
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug371495.js
@@ -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/.
+ */
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+const ADDON = "test_bug371495";
+const ID = "bug371495@tests.mozilla.org";
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+
+ // Install test add-on
+ startupManager();
+ installAllFiles([do_get_addon(ADDON)], function() {
+ AddonManager.getAddonByID(ID, callback_soon(function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "Test theme");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, callback_soon(function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.optionsURL, null);
+ do_check_eq(addon.aboutURL, null);
+
+ do_execute_soon(do_test_finished);
+ }));
+ }));
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug384052.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug384052.js
new file mode 100644
index 000000000..aeaaf3d8f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug384052.js
@@ -0,0 +1,103 @@
+const CLASS_ID = Components.ID("{12345678-1234-1234-1234-123456789abc}");
+const CONTRACT_ID = "@mozilla.org/test-parameter-source;1";
+
+// Get and create the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+var gTestURL = "http://127.0.0.1:" + gPort + "/update.rdf?itemID=%ITEM_ID%&custom1=%CUSTOM1%&custom2=%CUSTOM2%";
+var gExpectedQuery = "itemID=test@mozilla.org&custom1=custom_parameter_1&custom2=custom_parameter_2";
+var gSeenExpectedURL = false;
+
+var gComponentRegistrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+var gCategoryManager = AM_Cc["@mozilla.org/categorymanager;1"].getService(AM_Ci.nsICategoryManager);
+
+// Factory for our parameter handler
+var paramHandlerFactory = {
+ QueryInterface: function(iid) {
+ if (iid.equals(AM_Ci.nsIFactory) || iid.equals(AM_Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ createInstance: function(outer, iid) {
+ var bag = AM_Cc["@mozilla.org/hash-property-bag;1"].
+ createInstance(AM_Ci.nsIWritablePropertyBag);
+ bag.setProperty("CUSTOM1", "custom_parameter_1");
+ bag.setProperty("CUSTOM2", "custom_parameter_2");
+ return bag.QueryInterface(iid);
+ }
+};
+
+function initTest()
+{
+ do_test_pending();
+ // Setup extension manager
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ // Configure the HTTP server.
+ testserver.registerPathHandler("/update.rdf", function(aRequest, aResponse) {
+ gSeenExpectedURL = aRequest.queryString == gExpectedQuery;
+ aResponse.setStatusLine(null, 404, "Not Found");
+ });
+
+ // Register our parameter handlers
+ gComponentRegistrar.registerFactory(CLASS_ID, "Test component", CONTRACT_ID, paramHandlerFactory);
+ gCategoryManager.addCategoryEntry("extension-update-params", "CUSTOM1", CONTRACT_ID, false, false);
+ gCategoryManager.addCategoryEntry("extension-update-params", "CUSTOM2", CONTRACT_ID, false, false);
+
+ // Install a test extension into the profile
+ let dir = gProfD.clone();
+ dir.append("extensions");
+ writeInstallRDFForExtension({
+ id: "test@mozilla.org",
+ version: "1.0",
+ name: "Test extension",
+ updateURL: gTestURL,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ }, dir);
+
+ startupManager();
+}
+
+function shutdownTest()
+{
+ shutdownManager();
+
+ gComponentRegistrar.unregisterFactory(CLASS_ID, paramHandlerFactory);
+ gCategoryManager.deleteCategoryEntry("extension-update-params", "CUSTOM1", false);
+ gCategoryManager.deleteCategoryEntry("extension-update-params", "CUSTOM2", false);
+
+ do_test_finished();
+}
+
+function run_test()
+{
+ initTest();
+
+ AddonManager.getAddonByID("test@mozilla.org", function(item) {
+ // Initiate update
+ item.findUpdates({
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen a compatibility update");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function(addon, error) {
+ do_check_eq(error, AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
+ do_check_true(gSeenExpectedURL);
+ do_execute_soon(shutdownTest);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug393285.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug393285.js
new file mode 100644
index 000000000..90cf29753
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug393285.js
@@ -0,0 +1,327 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_bug393285.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+let addonIDs = ["test_bug393285_1@tests.mozilla.org",
+ "test_bug393285_2@tests.mozilla.org",
+ "test_bug393285_3a@tests.mozilla.org",
+ "test_bug393285_3b@tests.mozilla.org",
+ "test_bug393285_4@tests.mozilla.org",
+ "test_bug393285_5@tests.mozilla.org",
+ "test_bug393285_6@tests.mozilla.org",
+ "test_bug393285_7@tests.mozilla.org",
+ "test_bug393285_8@tests.mozilla.org",
+ "test_bug393285_9@tests.mozilla.org",
+ "test_bug393285_10@tests.mozilla.org",
+ "test_bug393285_11@tests.mozilla.org",
+ "test_bug393285_12@tests.mozilla.org",
+ "test_bug393285_13@tests.mozilla.org",
+ "test_bug393285_14@tests.mozilla.org"];
+
+// A window watcher to deal with the blocklist UI dialog.
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = arguments.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_1@tests.mozilla.org",
+ name: "extension 1",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_2@tests.mozilla.org",
+ name: "extension 2",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_3a@tests.mozilla.org",
+ name: "extension 3a",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_3b@tests.mozilla.org",
+ name: "extension 3b",
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_4@tests.mozilla.org",
+ name: "extension 4",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_5@tests.mozilla.org",
+ name: "extension 5",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_6@tests.mozilla.org",
+ name: "extension 6",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_7@tests.mozilla.org",
+ name: "extension 7",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_8@tests.mozilla.org",
+ name: "extension 8",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_9@tests.mozilla.org",
+ name: "extension 9",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_10@tests.mozilla.org",
+ name: "extension 10",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_11@tests.mozilla.org",
+ name: "extension 11",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_12@tests.mozilla.org",
+ name: "extension 12",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_13@tests.mozilla.org",
+ name: "extension 13",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_14@tests.mozilla.org",
+ name: "extension 14",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(addonIDs, function(addons) {
+ for (addon of addons) {
+ do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ }
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_bug393285.xml", function() {
+ restartManager();
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsIBlocklistService);
+
+ AddonManager.getAddonsByIDs(addonIDs,
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15]) {
+ // No info in blocklist, shouldn't be blocked
+ do_check_false(blocklist.isAddonBlocklisted(a1, "1", "1.9"));
+
+ // Should always be blocked
+ do_check_true(blocklist.isAddonBlocklisted(a2, "1", "1.9"));
+
+ // Only version 1 should be blocked
+ do_check_true(blocklist.isAddonBlocklisted(a3, "1", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a4, "1", "1.9"));
+
+ // Should be blocked for app version 1
+ do_check_true(blocklist.isAddonBlocklisted(a5, "1", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a5, "2", "1.9"));
+
+ // Not blocklisted because we are a different OS
+ do_check_false(blocklist.isAddonBlocklisted(a6, "2", "1.9"));
+
+ // Blocklisted based on OS
+ do_check_true(blocklist.isAddonBlocklisted(a7, "2", "1.9"));
+ do_check_true(blocklist.isAddonBlocklisted(a8, "2", "1.9"));
+
+ // Not blocklisted because we are a different ABI
+ do_check_false(blocklist.isAddonBlocklisted(a9, "2", "1.9"));
+
+ // Blocklisted based on ABI
+ do_check_true(blocklist.isAddonBlocklisted(a10, "2", "1.9"));
+ do_check_true(blocklist.isAddonBlocklisted(a11, "2", "1.9"));
+
+ // Doesnt match both os and abi so not blocked
+ do_check_false(blocklist.isAddonBlocklisted(a12, "2", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a13, "2", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a14, "2", "1.9"));
+
+ // Matches both os and abi so blocked
+ do_check_true(blocklist.isAddonBlocklisted(a15, "2", "1.9"));
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug394300.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug394300.js
new file mode 100644
index 000000000..bd393b91c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug394300.js
@@ -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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+// nsIAddonUpdateCheckListener implementation
+var updateListener = {
+ _count: 0,
+
+ onUpdateAvailable: function onAddonUpdateEnded(aAddon, aInstall) {
+ do_check_eq(aInstall.version, 10);
+ },
+
+ onNoUpdateAvailable: function onNoUpdateAvailable(aAddon) {
+ do_throw("Expected an available update for " + aAddon.id);
+ },
+
+ onUpdateFinished: function onUpdateFinished() {
+ if (++this._count == 2)
+ server.stop(do_test_finished);
+ },
+}
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ installAllFiles([do_get_addon("test_bug394300_1"),
+ do_get_addon("test_bug394300_2")], function() {
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug394300_1@tests.mozilla.org",
+ "bug394300_2@tests.mozilla.org"], function(updates) {
+
+ do_check_neq(updates[0], null);
+ do_check_neq(updates[1], null);
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data"));
+ server.start(4444);
+
+ updates[0].findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ updates[1].findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug397778.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug397778.js
new file mode 100644
index 000000000..aa18a6946
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug397778.js
@@ -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/.
+ */
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+const ADDON = "test_bug397778";
+const ID = "bug397778@tests.mozilla.org";
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ // Install test add-on
+ startupManager();
+ installAllFiles([do_get_addon(ADDON)], function() {
+ restartManager();
+
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ AddonManager.getAddonByID(ID, callback_soon(function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "fr Name");
+ do_check_eq(addon.description, "fr Description");
+
+ // Disable item
+ addon.userDisabled = true;
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(newAddon) {
+ do_check_neq(newAddon, null);
+ do_check_eq(newAddon.name, "fr Name");
+
+ do_execute_soon(run_test_2);
+ });
+ }));
+}
+
+function run_test_2() {
+ // Change locale. The more specific de-DE is the best match
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "de");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "de-DE Name");
+ do_check_eq(addon.description, null);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+function run_test_3() {
+ // Change locale. Locale case should have no effect
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "DE-de");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "de-DE Name");
+ do_check_eq(addon.description, null);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+function run_test_4() {
+ // Change locale. es-ES should closely match
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "es-AR");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "es-ES Name");
+ do_check_eq(addon.description, "es-ES Description");
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+function run_test_5() {
+ // Change locale. Either zh-CN or zh-TW could match
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "zh");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ if (addon.name != "zh-TW Name" && addon.name != "zh-CN Name")
+ do_throw("zh matched to " + addon.name);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+function run_test_6() {
+ // Unknown locale should try to match against en-US as well. Of en,en-GB
+ // en should match as being less specific
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "nl-NL");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "en Name");
+ do_check_eq(addon.description, "en Description");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug406118.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug406118.js
new file mode 100644
index 000000000..724b48dd5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug406118.js
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 addonIDs = ["test_bug393285_1@tests.mozilla.org",
+ "test_bug393285_2@tests.mozilla.org",
+ "test_bug393285_3a@tests.mozilla.org",
+ "test_bug393285_4@tests.mozilla.org"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_bug393285.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// A window watcher to deal with the blocklist UI dialog.
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = arguments.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_1@tests.mozilla.org",
+ name: "extension 1",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_2@tests.mozilla.org",
+ name: "extension 2",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_3a@tests.mozilla.org",
+ name: "extension 3a",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_4@tests.mozilla.org",
+ name: "extension 4",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(addonIDs, function(addons) {
+ for (addon of addons) {
+ do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ }
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_bug393285.xml", function() {
+ restartManager();
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsIBlocklistService);
+
+ AddonManager.getAddonsByIDs(addonIDs,
+ function([a1, a2, a3, a4]) {
+ // No info in blocklist, shouldn't be blocked
+ do_check_false(blocklist.isAddonBlocklisted(a1, null, null));
+
+ // All these should be blocklisted for the current app.
+ do_check_true(blocklist.isAddonBlocklisted(a2, null, null));
+ do_check_true(blocklist.isAddonBlocklisted(a3, null, null));
+ do_check_true(blocklist.isAddonBlocklisted(a4, null, null));
+
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug424262.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug424262.js
new file mode 100644
index 000000000..8b29e15a5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug424262.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/.
+ */
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+var RESULTS = [
+ null,
+ null,
+ 0,
+ 2,
+ 4,
+ 5,
+ 5,
+ 5
+];
+
+var RecommendedCallback = {
+ searchSucceeded: function(addons, length, total) {
+ dump("loaded");
+ // Search is complete
+ do_check_eq(length, RESULTS.length);
+
+ for (var i = 0; i < length; i++) {
+ if (addons[i].averageRating != RESULTS[i])
+ do_throw("Rating for " + addons[i].id + " was " + addons[i].averageRating + ", should have been " + RESULTS[i]);
+ }
+ server.stop(do_test_finished);
+ },
+
+ searchFailed: function() {
+ server.stop(do_test_finished);
+ do_throw("Recommended results failed");
+ }
+};
+
+function run_test()
+{
+ // EM needs to be running.
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ server = new HttpServer();
+ server.start(-1);
+ gPort = server.identity.primaryPort;
+ mapFile("/data/test_bug424262.xml", server);
+
+ // Point the addons repository to the test server
+ Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, "http://localhost:" +
+ gPort + "/data/test_bug424262.xml");
+
+ do_check_neq(AddonRepository, null);
+
+ do_test_pending();
+ // Pull some results.
+ AddonRepository.retrieveRecommendedAddons(RESULTS.length, RecommendedCallback);
+}
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug425657.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug425657.js
new file mode 100644
index 000000000..f11a942fb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug425657.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/.
+ */
+
+const ADDON = "test_bug425657";
+const ID = "bug425657@tests.mozilla.org";
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+
+ // Install test add-on
+ startupManager();
+ installAllFiles([do_get_addon(ADDON)], function() {
+ restartManager();
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "Deutsches W\u00f6rterbuch");
+ do_check_eq(addon.name.length, 20);
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug430120.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug430120.js
new file mode 100644
index 000000000..74080dba9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug430120.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 BLOCKLIST_TIMER = "blocklist-background-update-timer";
+const PREF_BLOCKLIST_URL = "extensions.blocklist.url";
+const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
+const PREF_APP_DISTRIBUTION = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
+const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
+const PREF_GENERAL_USERAGENT_LOCALE = "general.useragent.locale";
+const CATEGORY_UPDATE_TIMER = "update-timer";
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+var gOSVersion;
+var gBlocklist;
+
+// This is a replacement for the timer service so we can trigger timers
+var timerService = {
+
+ hasTimer: function(id) {
+ var catMan = Components.classes["@mozilla.org/categorymanager;1"]
+ .getService(Components.interfaces.nsICategoryManager);
+ var entries = catMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
+ while (entries.hasMoreElements()) {
+ var entry = entries.getNext().QueryInterface(Components.interfaces.nsISupportsCString).data;
+ var value = catMan.getCategoryEntry(CATEGORY_UPDATE_TIMER, entry);
+ var timerID = value.split(",")[2];
+ if (id == timerID) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ fireTimer: function(id) {
+ gBlocklist.QueryInterface(Components.interfaces.nsITimerCallback).notify(null);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIUpdateTimerManager)
+ || iid.equals(Components.interfaces.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var TimerServiceFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return timerService.QueryInterface(iid);
+ }
+};
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{61189e7a-6b1b-44b8-ac81-f180a6105085}"), "TimerService",
+ "@mozilla.org/updates/timer-manager;1", TimerServiceFactory);
+
+function failHandler(metadata, response) {
+ do_throw("Should not have attempted to retrieve the blocklist when it is disabled");
+}
+
+function pathHandler(metadata, response) {
+ var ABI = "noarch-spidermonkey";
+ // the blacklist service special-cases ABI for Universal binaries,
+ // so do the same here.
+ if ("@mozilla.org/xpcom/mac-utils;1" in Components.classes) {
+ var macutils = Components.classes["@mozilla.org/xpcom/mac-utils;1"]
+ .getService(Components.interfaces.nsIMacUtils);
+ if (macutils.isUniversalBinary)
+ ABI += "-u-" + macutils.architecturesInBinary;
+ }
+ do_check_eq(metadata.queryString,
+ "xpcshell@tests.mozilla.org&1&XPCShell&1&2007010101&" +
+ "XPCShell_" + ABI + "&locale&updatechannel&" +
+ gOSVersion + "&1.9&distribution&distribution-version");
+ gBlocklist.observe(null, "quit-application", "");
+ gBlocklist.observe(null, "xpcom-shutdown", "");
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ var osVersion;
+ var sysInfo = Components.classes["@mozilla.org/system-info;1"]
+ .getService(Components.interfaces.nsIPropertyBag2);
+ try {
+ osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
+ if (osVersion) {
+ try {
+ osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+ }
+ catch (e) {
+ }
+ gOSVersion = encodeURIComponent(osVersion);
+ }
+ }
+ catch (e) {
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ testserver = new HttpServer();
+ testserver.registerPathHandler("/1", failHandler);
+ testserver.registerPathHandler("/2", pathHandler);
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ // Initialise the blocklist service
+ gBlocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+ .getService(Components.interfaces.nsIBlocklistService)
+ .QueryInterface(Components.interfaces.nsIObserver);
+ gBlocklist.observe(null, "profile-after-change", "");
+
+ do_check_true(timerService.hasTimer(BLOCKLIST_TIMER));
+
+ do_test_pending();
+
+ // This should have no effect as the blocklist is disabled
+ Services.prefs.setCharPref(PREF_BLOCKLIST_URL, "http://localhost:" + gPort + "/1");
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, false);
+ timerService.fireTimer(BLOCKLIST_TIMER);
+
+ // Some values have to be on the default branch to work
+ var defaults = Services.prefs.QueryInterface(Components.interfaces.nsIPrefService)
+ .getDefaultBranch(null);
+ defaults.setCharPref(PREF_APP_UPDATE_CHANNEL, "updatechannel");
+ defaults.setCharPref(PREF_APP_DISTRIBUTION, "distribution");
+ defaults.setCharPref(PREF_APP_DISTRIBUTION_VERSION, "distribution-version");
+ defaults.setCharPref(PREF_GENERAL_USERAGENT_LOCALE, "locale");
+
+ // This should correctly escape everything
+ Services.prefs.setCharPref(PREF_BLOCKLIST_URL, "http://localhost:" + gPort + "/2?" +
+ "%APP_ID%&%APP_VERSION%&%PRODUCT%&%VERSION%&%BUILD_ID%&" +
+ "%BUILD_TARGET%&%LOCALE%&%CHANNEL%&" +
+ "%OS_VERSION%&%PLATFORM_VERSION%&%DISTRIBUTION%&%DISTRIBUTION_VERSION%");
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, true);
+ timerService.fireTimer(BLOCKLIST_TIMER);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug449027.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug449027.js
new file mode 100644
index 000000000..623a6a14a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug449027.js
@@ -0,0 +1,448 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://testing-common/httpd.js");
+
+var ADDONS = [{
+ id: "test_bug449027_1@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 1",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_2@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 2",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_3@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 3",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_4@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 4",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_5@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 5",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_6@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 6",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_7@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 7",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_8@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 8",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_9@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 9",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_10@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 10",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_11@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 11",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_12@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 12",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_13@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 13",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_14@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 14",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_15@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 15",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_16@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 16",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_17@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 17",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_18@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 18",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_19@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 19",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_20@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 20",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_21@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 21",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_22@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 22",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_23@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 23",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_24@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 24",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_25@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 25",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}];
+
+function MockPluginTag(name, version, start, appBlocks, toolkitBlocks)
+{
+ this.name = name;
+ this.version = version;
+ this.start = start;
+ this.appBlocks = appBlocks;
+ this.toolkitBlocks = toolkitBlocks;
+}
+Object.defineProperty(MockPluginTag.prototype, "blocklisted", {
+ get: function MockPluginTag_getBlocklisted() {
+ let bls = AM_Cc["@mozilla.org/extensions/blocklist;1"].getService(Ci.nsIBlocklistService);
+ return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
+ }
+});
+
+var PLUGINS = [
+ new MockPluginTag("test_bug449027_1", "5", false, false, false),
+ new MockPluginTag("test_bug449027_2", "5", false, true, false),
+ new MockPluginTag("test_bug449027_3", "5", false, true, false),
+ new MockPluginTag("test_bug449027_4", "5", false, false, false),
+ new MockPluginTag("test_bug449027_5", "5", false, false, false),
+ new MockPluginTag("test_bug449027_6", "5", false, true, false),
+ new MockPluginTag("test_bug449027_7", "5", false, true, false),
+ new MockPluginTag("test_bug449027_8", "5", false, true, false),
+ new MockPluginTag("test_bug449027_9", "5", false, true, false),
+ new MockPluginTag("test_bug449027_10", "5", false, true, false),
+ new MockPluginTag("test_bug449027_11", "5", false, true, false),
+ new MockPluginTag("test_bug449027_12", "5", false, true, false),
+ new MockPluginTag("test_bug449027_13", "5", false, true, false),
+ new MockPluginTag("test_bug449027_14", "5", false, false, false),
+ new MockPluginTag("test_bug449027_15", "5", false, true, true),
+ new MockPluginTag("test_bug449027_16", "5", false, true, true),
+ new MockPluginTag("test_bug449027_17", "5", false, false, false),
+ new MockPluginTag("test_bug449027_18", "5", false, false, false),
+ new MockPluginTag("test_bug449027_19", "5", false, true, true),
+ new MockPluginTag("test_bug449027_20", "5", false, true, true),
+ new MockPluginTag("test_bug449027_21", "5", false, true, true),
+ new MockPluginTag("test_bug449027_22", "5", false, true, true),
+ new MockPluginTag("test_bug449027_23", "5", false, true, true),
+ new MockPluginTag("test_bug449027_24", "5", false, true, true),
+ new MockPluginTag("test_bug449027_25", "5", false, true, true)
+];
+
+var gCallback = null;
+var gTestserver = null;
+var gNewBlocks = [];
+
+// A fake plugin host for the blocklist service to use
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIPluginHost)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var PluginHostFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return PluginHost.QueryInterface(iid);
+ }
+};
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+ do_check_neq(gCallback, null);
+
+ var args = arguments.wrappedJSObject;
+
+ gNewBlocks = [];
+ var list = args.list;
+ for (let listItem of list)
+ gNewBlocks.push(listItem.name + " " + listItem.version);
+
+ // Call the callback after the blocklist has finished up
+ do_timeout(0, gCallback);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{721c3e73-969e-474b-a6dc-059fd288c428}"),
+ "Fake Plugin Host",
+ "@mozilla.org/plugin/host;1", PluginHostFactory);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+function create_addon(addon) {
+ var installrdf = "<?xml version=\"1.0\"?>\n" +
+ "\n" +
+ "<RDF xmlns=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n" +
+ " xmlns:em=\"http://www.mozilla.org/2004/em-rdf#\">\n" +
+ " <Description about=\"urn:mozilla:install-manifest\">\n" +
+ " <em:id>" + addon.id + "</em:id>\n" +
+ " <em:version>" + addon.version + "</em:version>\n" +
+ " <em:targetApplication>\n" +
+ " <Description>\n" +
+ " <em:id>xpcshell@tests.mozilla.org</em:id>\n" +
+ " <em:minVersion>3</em:minVersion>\n" +
+ " <em:maxVersion>3</em:maxVersion>\n" +
+ " </Description>\n" +
+ " </em:targetApplication>\n" +
+ " <em:name>" + addon.name + "</em:name>\n" +
+ " </Description>\n" +
+ "</RDF>\n";
+ var target = gProfD.clone();
+ target.append("extensions");
+ target.append(addon.id);
+ target.append("install.rdf");
+ target.create(target.NORMAL_FILE_TYPE, 0644);
+ var stream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(target, 0x04 | 0x08 | 0x20, 0664, 0); // write, create, truncate
+ stream.write(installrdf, installrdf.length);
+ stream.close();
+}
+
+/**
+ * Checks that items are blocklisted correctly according to the current test.
+ * If a lastTest is provided checks that the notification dialog got passed
+ * the newly blocked items compared to the previous test.
+ */
+function check_state(test, lastTest, callback) {
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(addons) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ var blocked = addons[i].blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED;
+ if (blocked != ADDONS[i][test])
+ do_throw("Blocklist state did not match expected for extension " + (i + 1) + ", test " + test);
+ }
+
+ for (i = 0; i < PLUGINS.length; i++) {
+ if (PLUGINS[i].blocklisted != PLUGINS[i][test])
+ do_throw("Blocklist state did not match expected for plugin " + (i + 1) + ", test " + test);
+ }
+
+ if (lastTest) {
+ var expected = 0;
+ for (i = 0; i < ADDONS.length; i++) {
+ if (ADDONS[i][test] && !ADDONS[i][lastTest]) {
+ if (gNewBlocks.indexOf(ADDONS[i].name + " " + ADDONS[i].version) < 0)
+ do_throw("Addon " + (i + 1) + " should have been listed in the blocklist notification for test " + test);
+ expected++;
+ }
+ }
+
+ for (i = 0; i < PLUGINS.length; i++) {
+ if (PLUGINS[i][test] && !PLUGINS[i][lastTest]) {
+ if (gNewBlocks.indexOf(PLUGINS[i].name + " " + PLUGINS[i].version) < 0)
+ do_throw("Plugin " + (i + 1) + " should have been listed in the blocklist notification for test " + test);
+ expected++;
+ }
+ }
+
+ do_check_eq(expected, gNewBlocks.length);
+ }
+ do_execute_soon(callback);
+ });
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + file);
+ var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function run_test() {
+ // Setup for test
+ dump("Setting up tests\n");
+ // Rather than keeping lots of identical add-ons in version control, just
+ // write them into the profile.
+ for (let addon of ADDONS)
+ create_addon(addon);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ gTestserver = new HttpServer();
+ gTestserver.registerDirectory("/data/", do_get_file("data"));
+ gTestserver.start(-1);
+ gPort = gTestserver.identity.primaryPort;
+
+ do_test_pending();
+ check_test_pt1();
+}
+
+/**
+ * Checks the initial state is correct
+ */
+function check_test_pt1() {
+ dump("Checking pt 1\n");
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(addons) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ if (!addons[i])
+ do_throw("Addon " + (i + 1) + " did not get installed correctly");
+ }
+
+ do_execute_soon(function checkstate1() {check_state("start", null, run_test_pt2);});
+ });
+}
+
+/**
+ * Load the toolkit based blocks
+ */
+function run_test_pt2() {
+ dump("Running test pt 2\n");
+ gCallback = check_test_pt2;
+ load_blocklist("test_bug449027_toolkit.xml");
+}
+
+function check_test_pt2() {
+ dump("Checking pt 2\n");
+ check_state("toolkitBlocks", "start", run_test_pt3);
+}
+
+/**
+ * Load the application based blocks
+ */
+function run_test_pt3() {
+ dump("Running test pt 3\n");
+ gCallback = check_test_pt3;
+ load_blocklist("test_bug449027_app.xml");
+}
+
+function check_test_pt3() {
+ dump("Checking pt 3\n");
+ check_state("appBlocks", "toolkitBlocks", end_test);
+}
+
+function end_test() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
new file mode 100644
index 000000000..9a41e827c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
@@ -0,0 +1,536 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/bug455906_warn.xml", gTestserver);
+mapFile("/data/bug455906_start.xml", gTestserver);
+mapFile("/data/bug455906_block.xml", gTestserver);
+mapFile("/data/bug455906_empty.xml", gTestserver);
+
+// Workaround for Bug 658720 - URL formatter can leak during xpcshell tests
+const PREF_BLOCKLIST_ITEM_URL = "extensions.blocklist.itemURL";
+Services.prefs.setCharPref(PREF_BLOCKLIST_ITEM_URL, "http://localhost:" + gPort + "/blocklist/%blockID%");
+
+var ADDONS = [{
+ // Tests how the blocklist affects a disabled add-on
+ id: "test_bug455906_1@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 1",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an enabled add-on
+ id: "test_bug455906_2@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 2",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an enabled add-on, to be disabled by the notification
+ id: "test_bug455906_3@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 3",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects a disabled add-on that was already warned about
+ id: "test_bug455906_4@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 4",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an enabled add-on that was already warned about
+ id: "test_bug455906_5@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 5",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an already blocked add-on
+ id: "test_bug455906_6@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 6",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an incompatible add-on
+ id: "test_bug455906_7@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 7",
+ version: "5",
+ appVersion: "2"
+}, {
+ // Spare add-on used to ensure we get a notification when switching lists
+ id: "dummy_bug455906_1@tests.mozilla.org",
+ name: "Dummy Addon 1",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Spare add-on used to ensure we get a notification when switching lists
+ id: "dummy_bug455906_2@tests.mozilla.org",
+ name: "Dummy Addon 2",
+ version: "5",
+ appVersion: "3"
+}];
+
+function MockPlugin(name, version, enabledState) {
+ this.name = name;
+ this.version = version;
+ this.enabledState = enabledState;
+}
+Object.defineProperty(MockPlugin.prototype, "blocklisted", {
+ get: function MockPlugin_getBlocklisted() {
+ let bls = Cc["@mozilla.org/extensions/blocklist;1"].getService(Ci.nsIBlocklistService);
+ return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
+ }
+});
+Object.defineProperty(MockPlugin.prototype, "disabled", {
+ get: function MockPlugin_getDisabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ }
+});
+
+var PLUGINS = [
+ // Tests how the blocklist affects a disabled plugin
+ new MockPlugin("test_bug455906_1", "5", Ci.nsIPluginTag.STATE_DISABLED),
+ // Tests how the blocklist affects an enabled plugin
+ new MockPlugin("test_bug455906_2", "5", Ci.nsIPluginTag.STATE_ENABLED),
+ // Tests how the blocklist affects an enabled plugin, to be disabled by the notification
+ new MockPlugin("test_bug455906_3", "5", Ci.nsIPluginTag.STATE_ENABLED),
+ // Tests how the blocklist affects a disabled plugin that was already warned about
+ new MockPlugin("test_bug455906_4", "5", Ci.nsIPluginTag.STATE_DISABLED),
+ // Tests how the blocklist affects an enabled plugin that was already warned about
+ new MockPlugin("test_bug455906_5", "5", Ci.nsIPluginTag.STATE_ENABLED),
+ // Tests how the blocklist affects an already blocked plugin
+ new MockPlugin("test_bug455906_6", "5", Ci.nsIPluginTag.STATE_ENABLED)
+];
+
+var gNotificationCheck = null;
+var gTestCheck = null;
+
+// A fake plugin host for the blocklist service to use
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIPluginHost)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var PluginHostFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return PluginHost.QueryInterface(iid);
+ }
+};
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, windowArguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ if (gNotificationCheck) {
+ var args = windowArguments.wrappedJSObject;
+ gNotificationCheck(args);
+ }
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ // Call the next test after the blocklist has finished up
+ do_timeout(0, gTestCheck);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{721c3e73-969e-474b-a6dc-059fd288c428}"),
+ "Fake Plugin Host",
+ "@mozilla.org/plugin/host;1", PluginHostFactory);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+function create_addon(addon) {
+ var installrdf = "<?xml version=\"1.0\"?>\n" +
+ "\n" +
+ "<RDF xmlns=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n" +
+ " xmlns:em=\"http://www.mozilla.org/2004/em-rdf#\">\n" +
+ " <Description about=\"urn:mozilla:install-manifest\">\n" +
+ " <em:id>" + addon.id + "</em:id>\n" +
+ " <em:version>" + addon.version + "</em:version>\n" +
+ " <em:targetApplication>\n" +
+ " <Description>\n" +
+ " <em:id>xpcshell@tests.mozilla.org</em:id>\n" +
+ " <em:minVersion>" + addon.appVersion + "</em:minVersion>\n" +
+ " <em:maxVersion>" + addon.appVersion + "</em:maxVersion>\n" +
+ " </Description>\n" +
+ " </em:targetApplication>\n" +
+ " <em:name>" + addon.name + "</em:name>\n" +
+ " </Description>\n" +
+ "</RDF>\n";
+ var target = gProfD.clone();
+ target.append("extensions");
+ target.append(addon.id);
+ target.append("install.rdf");
+ target.create(target.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ var stream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ stream.init(target,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ stream.write(installrdf, installrdf.length);
+ stream.close();
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function check_addon_state(addon) {
+ return addon.userDisabled + "," + addon.softDisabled + "," + addon.appDisabled;
+}
+
+function check_plugin_state(plugin) {
+ return plugin.disabled + "," + plugin.blocklisted;
+}
+
+function create_blocklistURL(blockID){
+ let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
+ url = url.replace(/%blockID%/g, blockID);
+ return url;
+}
+
+// Performs the initial setup
+function run_test() {
+ // Setup for test
+ dump("Setting up tests\n");
+ // Rather than keeping lots of identical add-ons in version control, just
+ // write them into the profile.
+ for (let addon of ADDONS)
+ create_addon(addon);
+
+ // Copy the initial blocklist into the profile to check add-ons start in the
+ // right state.
+ copyBlocklistToProfile(do_get_file("data/bug455906_start.xml"));
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+ check_test_pt1();
+}
+
+// Before every main test this is the state the add-ons are meant to be in
+function check_initial_state(callback) {
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(addons) {
+ do_check_eq(check_addon_state(addons[0]), "true,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[2]), "false,false,false");
+ do_check_eq(check_addon_state(addons[3]), "true,true,false");
+ do_check_eq(check_addon_state(addons[4]), "false,false,false");
+ do_check_eq(check_addon_state(addons[5]), "false,false,true");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "false,true");
+
+ callback();
+ });
+}
+
+// Tests the add-ons were installed and the initial blocklist applied as expected
+function check_test_pt1() {
+ dump("Checking pt 1\n");
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], callback_soon(function(addons) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ if (!addons[i])
+ do_throw("Addon " + (i + 1) + " did not get installed correctly");
+ }
+
+ do_check_eq(check_addon_state(addons[0]), "false,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[2]), "false,false,false");
+
+ // Warn add-ons should be soft disabled automatically
+ do_check_eq(check_addon_state(addons[3]), "true,true,false");
+ do_check_eq(check_addon_state(addons[4]), "true,true,false");
+
+ // Blocked and incompatible should be app disabled only
+ do_check_eq(check_addon_state(addons[5]), "false,false,true");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+
+ // We've overridden the plugin host so we cannot tell what that would have
+ // initialised the plugins as
+
+ // Put the add-ons into the base state
+ addons[0].userDisabled = true;
+ addons[4].userDisabled = false;
+
+ restartManager();
+ check_initial_state(function() {
+ gNotificationCheck = check_notification_pt2;
+ gTestCheck = check_test_pt2;
+ load_blocklist("bug455906_warn.xml");
+ });
+ }));
+}
+
+function check_notification_pt2(args) {
+ dump("Checking notification pt 2\n");
+ do_check_eq(args.list.length, 4);
+
+ for (let addon of args.list) {
+ if (addon.item instanceof Ci.nsIPluginTag) {
+ switch (addon.item.name) {
+ case "test_bug455906_2":
+ do_check_false(addon.blocked);
+ break;
+ case "test_bug455906_3":
+ do_check_false(addon.blocked);
+ addon.disable = true;
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.name);
+ }
+ }
+ else {
+ switch (addon.item.id) {
+ case "test_bug455906_2@tests.mozilla.org":
+ do_check_false(addon.blocked);
+ break;
+ case "test_bug455906_3@tests.mozilla.org":
+ do_check_false(addon.blocked);
+ addon.disable = true;
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.id);
+ }
+ }
+ }
+}
+
+function check_test_pt2() {
+ restartManager();
+ dump("Checking results pt 2\n");
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], callback_soon(function(addons) {
+ // Should have disabled this add-on as requested
+ do_check_eq(check_addon_state(addons[2]), "true,true,false");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "true,false");
+
+ // The blocked add-on should have changed to soft disabled
+ do_check_eq(check_addon_state(addons[5]), "true,true,false");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "true,false");
+
+ // These should have been unchanged
+ do_check_eq(check_addon_state(addons[0]), "true,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[3]), "true,true,false");
+ do_check_eq(check_addon_state(addons[4]), "false,false,false");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,false");
+
+ // Back to starting state
+ addons[2].userDisabled = false;
+ addons[5].userDisabled = false;
+ PLUGINS[2].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ PLUGINS[5].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ restartManager();
+ gNotificationCheck = null;
+ gTestCheck = run_test_pt3;
+ load_blocklist("bug455906_start.xml");
+ }));
+}
+
+function run_test_pt3() {
+ restartManager();
+ check_initial_state(function() {
+ gNotificationCheck = check_notification_pt3;
+ gTestCheck = check_test_pt3;
+ load_blocklist("bug455906_block.xml");
+ });
+}
+
+function check_notification_pt3(args) {
+ dump("Checking notification pt 3\n");
+ do_check_eq(args.list.length, 6);
+
+ for (let addon of args.list) {
+ if (addon.item instanceof Ci.nsIPluginTag) {
+ switch (addon.item.name) {
+ case "test_bug455906_2":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_3":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_5":
+ do_check_true(addon.blocked);
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.name);
+ }
+ }
+ else {
+ switch (addon.item.id) {
+ case "test_bug455906_2@tests.mozilla.org":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_3@tests.mozilla.org":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_5@tests.mozilla.org":
+ do_check_true(addon.blocked);
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.id);
+ }
+ }
+ }
+}
+
+function check_test_pt3() {
+ restartManager();
+ dump("Checking results pt 3\n");
+
+ let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsIBlocklistService);
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(addons) {
+ // All should have gained the blocklist state, user disabled as previously
+ do_check_eq(check_addon_state(addons[0]), "true,false,true");
+ do_check_eq(check_addon_state(addons[1]), "false,false,true");
+ do_check_eq(check_addon_state(addons[2]), "false,false,true");
+ do_check_eq(check_addon_state(addons[4]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,true");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,true");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "false,true");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,true");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,true");
+
+ // Should have gained the blocklist state but no longer be soft disabled
+ do_check_eq(check_addon_state(addons[3]), "false,false,true");
+
+ // Check blockIDs are correct
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[0]),create_blocklistURL(addons[0].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[1]),create_blocklistURL(addons[1].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[2]),create_blocklistURL(addons[2].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[3]),create_blocklistURL(addons[3].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[4]),create_blocklistURL(addons[4].id));
+
+ // All plugins have the same blockID on the test
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[0]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[1]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[2]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[3]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[4]), create_blocklistURL('test_bug455906_plugin'));
+
+ // Shouldn't be changed
+ do_check_eq(check_addon_state(addons[5]), "false,false,true");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "false,true");
+
+ // Back to starting state
+ gNotificationCheck = null;
+ gTestCheck = run_test_pt4;
+ load_blocklist("bug455906_start.xml");
+ });
+}
+
+function run_test_pt4() {
+ AddonManager.getAddonByID(ADDONS[4].id, callback_soon(function(addon) {
+ addon.userDisabled = false;
+ PLUGINS[4].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ restartManager();
+ check_initial_state(function() {
+ gNotificationCheck = check_notification_pt4;
+ gTestCheck = check_test_pt4;
+ load_blocklist("bug455906_empty.xml");
+ });
+ }));
+}
+
+function check_notification_pt4(args) {
+ dump("Checking notification pt 4\n");
+
+ // Should be just the dummy add-on to force this notification
+ do_check_eq(args.list.length, 1);
+ do_check_false(args.list[0].item instanceof Ci.nsIPluginTag);
+ do_check_eq(args.list[0].item.id, "dummy_bug455906_2@tests.mozilla.org");
+}
+
+function check_test_pt4() {
+ restartManager();
+ dump("Checking results pt 4\n");
+
+ AddonManager.getAddonsByIDs([a.id for each (a in ADDONS)], function(addons) {
+ // This should have become unblocked
+ do_check_eq(check_addon_state(addons[5]), "false,false,false");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "false,false");
+
+ // Should get re-enabled
+ do_check_eq(check_addon_state(addons[3]), "false,false,false");
+
+ // No change for anything else
+ do_check_eq(check_addon_state(addons[0]), "true,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[2]), "false,false,false");
+ do_check_eq(check_addon_state(addons[4]), "false,false,false");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,false");
+
+ finish();
+ });
+}
+
+function finish() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug465190.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug465190.js
new file mode 100644
index 000000000..fc8c772c9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug465190.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/.
+ */
+
+var installLocation = gProfD.clone();
+installLocation.append("baddir");
+installLocation.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);
+
+var dirProvider2 = {
+ getFile: function(prop, persistent) {
+ persistent.value = true;
+ if (prop == "XREUSysExt")
+ return installLocation.clone();
+ return null;
+ },
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
+ iid.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+};
+Services.dirsvc.QueryInterface(Components.interfaces.nsIDirectoryService)
+ .registerProvider(dirProvider2);
+
+function run_test()
+{
+ var log = gProfD.clone();
+ log.append("extensions.log");
+ do_check_false(log.exists());
+
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+
+ startupManager();
+ do_check_false(log.exists());
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug468528.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug468528.js
new file mode 100644
index 000000000..5e8702eb7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug468528.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/. */
+
+const nsIBLS = Components.interfaces.nsIBlocklistService;
+
+var PLUGINS = [{
+ // Normal blacklisted plugin, before an invalid regexp
+ name: "test_bug468528_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // Normal blacklisted plugin, with an invalid regexp
+ name: "test_bug468528_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // Normal blacklisted plugin, after an invalid regexp
+ name: "test_bug468528_3",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // Non-blocklisted plugin
+ name: "test_bug468528_4",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}];
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ // We cannot force the blocklist to update so just copy our test list to the profile
+ copyBlocklistToProfile(do_get_file("data/test_bug468528.xml"));
+
+ var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+ .getService(nsIBLS);
+
+ // blocked (sanity check)
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ // not blocked - won't match due to invalid regexp
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+ // blocked - the invalid regexp for the previous item shouldn't affect this one
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ // not blocked - the previous invalid regexp shouldn't act as a wildcard
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1.js
new file mode 100644
index 000000000..c456506ce
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1.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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+var ADDONS = [
+ "test_bug470377_1",
+ "test_bug470377_2",
+ "test_bug470377_3",
+ "test_bug470377_4",
+ "test_bug470377_5",
+];
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data/test_bug470377"));
+ server.start(-1);
+
+ startupManager();
+
+ installAllFiles([do_get_addon(a) for each (a in ADDONS)], function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+ do_check_neq(a3, null);
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+
+ server.stop(do_test_finished);
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1_strictcompat.js
new file mode 100644
index 000000000..1e542dff8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_1_strictcompat.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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+var ADDONS = [
+ "test_bug470377_1",
+ "test_bug470377_2",
+ "test_bug470377_3",
+ "test_bug470377_4",
+ "test_bug470377_5",
+];
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data/test_bug470377"));
+ server.start(-1);
+
+ startupManager();
+
+ installAllFiles([do_get_addon(a) for each (a in ADDONS)], function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+ do_check_eq(a3, null);
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+
+ server.stop(do_test_finished);
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_2.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_2.js
new file mode 100644
index 000000000..15e8d54c4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_2.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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var ADDONS = [
+ "test_bug470377_1",
+ "test_bug470377_2",
+ "test_bug470377_3",
+ "test_bug470377_4",
+ "test_bug470377_5",
+];
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data/test_bug470377"));
+ server.start(-1);
+
+ startupManager();
+ AddonManager.checkCompatibility = false;
+
+ installAllFiles([do_get_addon(a) for each (a in ADDONS)], function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+ do_check_neq(a3, null);
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+
+ server.stop(do_test_finished);
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3.js
new file mode 100644
index 000000000..fcac471ee
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3.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/.
+ */
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.2.3", "2");
+
+ // inject the add-ons into the profile
+ var dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_1@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ var source = do_get_file("data/test_bug470377/install_1.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_2@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_2.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_3@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_3.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_4@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_4.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_5@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_5.rdf");
+ source.copyTo(dest, "install.rdf");
+
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ AddonManager.checkCompatibility = false;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3_strictcompat.js
new file mode 100644
index 000000000..7a3347320
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_3_strictcompat.js
@@ -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/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.2.3", "2");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ // inject the add-ons into the profile
+ var dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_1@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ var source = do_get_file("data/test_bug470377/install_1.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_2@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_2.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_3@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_3.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_4@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_4.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_5@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_5.rdf");
+ source.copyTo(dest, "install.rdf");
+
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ AddonManager.checkCompatibility = false;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_4.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_4.js
new file mode 100644
index 000000000..701cbe448
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_4.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/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.1a4", "2");
+
+ // inject the add-ons into the profile
+ var profileDir = gProfD.clone();
+ profileDir.append("extensions");
+ var dest = profileDir.clone();
+ dest.append("bug470377_1@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ var source = do_get_file("data/test_bug470377/install_1.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_2@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_2.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_3@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_3.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_4@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_4.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_5@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+ source = do_get_file("data/test_bug470377/install_5.rdf");
+ source.copyTo(dest, "install.rdf");
+
+ run_test_1();
+}
+
+function run_test_1() {
+ startupManager();
+ AddonManager.checkCompatibility = false;
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ AddonManager.checkCompatibility = true;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_1.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_1.js
new file mode 100644
index 000000000..c684e0ca2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_1.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/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const nsIBLS = Ci.nsIBlocklistService;
+
+var PLUGINS = [{
+ // blocklisted - default severity
+ name: "test_bug514327_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // outdated - severity of "0"
+ name: "test_bug514327_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // outdated - severity of "0"
+ name: "test_bug514327_3",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // not blocklisted, not outdated
+ name: "test_bug514327_4",
+ version: "5",
+ disabled: false,
+ blocklisted: false,
+ outdated: false
+}];
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ copyBlocklistToProfile(do_get_file("data/test_bug514327_1.xml"));
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+
+ // blocked (sanity check)
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ // outdated
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ // outdated
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ // not blocked
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_2.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_2.js
new file mode 100644
index 000000000..a8c369f1b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_2.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/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const nsIBLS = Ci.nsIBlocklistService;
+
+// Finds the test nsIPluginTag
+function get_test_plugintag() {
+ var host = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ var tags = host.getPluginTags();
+ for (let tag of tags) {
+ if (tag.name == "Test Plug-in")
+ return tag;
+ }
+ return null;
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ copyBlocklistToProfile(do_get_file("data/test_bug514327_2.xml"));
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+ var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+
+ var plugin = get_test_plugintag();
+ if (!plugin)
+ do_throw("Plugin tag not found");
+
+ //run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+ do_execute_soon(function() {
+ // should be marked as outdated by the blocklist
+ do_check_true(blocklist.getPluginBlocklistState(plugin, "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ // should indicate that a warning should be shown
+ do_check_true(prefs.getBoolPref("plugins.update.notifyUser"));
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
new file mode 100644
index 000000000..1267a8772
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
@@ -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/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://testing-common/httpd.js");
+
+const nsIBLS = Ci.nsIBlocklistService;
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+var gBlocklist = null;
+var gPrefs = null;
+var gTestserver = null;
+
+var gNextTestPart = null;
+
+
+var PLUGINS = [{
+ // Tests a plugin whose state goes from not-blocked, to outdated
+ name: "test_bug514327_outdated",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}, {
+ // Used to trigger the blocklist dialog, which indicates the blocklist has updated
+ name: "test_bug514327_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}, {
+ // Used to trigger the blocklist dialog, which indicates the blocklist has updated
+ name: "test_bug514327_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+} ];
+
+
+// A fake plugin host for the blocklist service to use
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIPluginHost)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var PluginHostFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return PluginHost.QueryInterface(iid);
+ }
+};
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+ // Should only include one item
+ do_check_eq(arguments.wrappedJSObject.list.length, 1);
+ // And that item should be the blocked plugin, not the outdated one
+ var item = arguments.wrappedJSObject.list[0];
+ do_check_true(item.item instanceof Ci.nsIPluginTag);
+ do_check_neq(item.name, "test_bug514327_outdated");
+
+ // Call the next test after the blocklist has finished up
+ do_timeout(0, gNextTestPart);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{721c3e73-969e-474b-a6dc-059fd288c428}"),
+ "Fake Plugin Host",
+ "@mozilla.org/plugin/host;1", PluginHostFactory);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+
+function do_update_blocklist(aDatafile, aNextPart) {
+ gNextTestPart = aNextPart;
+
+ gPrefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + aDatafile);
+ gBlocklist.QueryInterface(Ci.nsITimerCallback).notify(null);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ gTestserver = new HttpServer();
+ gTestserver.registerDirectory("/data/", do_get_file("data"));
+ gTestserver.start(-1);
+ gPort = gTestserver.identity.primaryPort;
+
+ startupManager();
+
+ // initialize the blocklist with no entries
+ copyBlocklistToProfile(do_get_file("data/test_bug514327_3_empty.xml"));
+
+ gPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+ gBlocklist = Cc["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+
+ // should NOT be marked as outdated by the blocklist
+ do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+ do_test_pending();
+
+ // update blocklist with data that marks the plugin as outdated
+ do_update_blocklist("test_bug514327_3_outdated_1.xml", test_part_1);
+}
+
+function test_part_1() {
+ // plugin should now be marked as outdated
+ do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+ // and the notifyUser pref should be set to true
+ do_check_true(gPrefs.getBoolPref("plugins.update.notifyUser"));
+
+ // preternd the user has been notified, reset the pref
+ gPrefs.setBoolPref("plugins.update.notifyUser", false);
+
+ // update blocklist with data that marks the plugin as outdated
+ do_update_blocklist("test_bug514327_3_outdated_2.xml", test_part_2);
+}
+
+function test_part_2() {
+ // plugin should still be marked as outdated
+ do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+ // and the notifyUser pref should NOT be set to true, as the plugin was already outdated
+ do_check_false(gPrefs.getBoolPref("plugins.update.notifyUser"));
+
+ finish();
+}
+
+function finish() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug521905.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug521905.js
new file mode 100644
index 000000000..b507fc100
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug521905.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/.
+ */
+
+const ADDON = "test_bug521905";
+const ID = "bug521905@tests.mozilla.org";
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+function run_test() {
+ // This test is only relevant on builds where the version is included in the
+ // checkCompatibility preference name
+ if (isNightlyChannel()) {
+ return;
+ }
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.0pre", "2");
+
+ startupManager();
+ AddonManager.checkCompatibility = false;
+
+ installAllFiles([do_get_addon(ADDON)], function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+
+ do_execute_soon(run_test_1);
+ });
+ });
+}
+
+function run_test_1() {
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.0pre", true);
+
+ restartManager();
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_false(addon.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.0p", false);
+
+ restartManager();
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_false(addon.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug526598.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug526598.js
new file mode 100644
index 000000000..debf59172
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug526598.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ installAllFiles([do_get_file("data/test_bug526598_1.xpi"),
+ do_get_file("data/test_bug526598_2.xpi")], function() {
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug526598_1@tests.mozilla.org",
+ "bug526598_2@tests.mozilla.org"],
+ callback_soon(function([a1, a2]) {
+
+ do_check_neq(a1, null);
+ do_check_true(a1.hasResource("install.rdf"));
+ let uri = a1.getResourceURI("install.rdf");
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ let file = uri.file;
+ do_check_true(file.exists());
+ do_check_true(file.isReadable());
+ do_check_true(file.isWritable());
+
+ do_check_neq(a2, null);
+ do_check_true(a2.hasResource("install.rdf"));
+ uri = a2.getResourceURI("install.rdf");
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ file = uri.file;
+ do_check_true(file.exists());
+ do_check_true(file.isReadable());
+ do_check_true(file.isWritable());
+
+ a1.uninstall();
+ a2.uninstall();
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug526598_1@tests.mozilla.org",
+ "bug526598_2@tests.mozilla.org"],
+ function([newa1, newa2]) {
+ do_check_eq(newa1, null);
+ do_check_eq(newa2, null);
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug541420.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug541420.js
new file mode 100644
index 000000000..1f70b42d5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug541420.js
@@ -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/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ installAllFiles([do_get_file("data/test_bug541420.xpi")], function() {
+
+ restartManager();
+
+ AddonManager.getAddonByID("bug541420@tests.mozilla.org", function(addon) {
+
+ do_check_neq(addon, null);
+ do_check_true(addon.hasResource("binary"));
+ let uri = addon.getResourceURI("binary");
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ let file = uri.file;
+ do_check_true(file.exists());
+ do_check_true(file.isReadable());
+ do_check_true(file.isWritable());
+
+ // We don't understand executable permissions on Windows since we don't
+ // support NTFS permissions so we don't need to test there. OSX's isExecutable
+ // only tests if the file is an application so it is better to just check the
+ // raw permission bits
+ if (!("nsIWindowsRegKey" in Components.interfaces))
+ do_check_true((file.permissions & 0100) == 0100);
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js
new file mode 100644
index 000000000..ceb472f98
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js
@@ -0,0 +1,486 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver;
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gInstallUpdate = false;
+var gCheckUpdates = false;
+
+// This will be called to show the compatibility update dialog.
+var WindowWatcher = {
+ expected: false,
+ arguments: null,
+
+ openWindow: function(parent, url, name, features, args) {
+ do_check_true(Services.startup.interrupted);
+ do_check_eq(url, URI_EXTENSION_UPDATE_DIALOG);
+ do_check_true(this.expected);
+ this.expected = false;
+ this.arguments = args.QueryInterface(AM_Ci.nsIVariant);
+
+ var updated = !gCheckUpdates;
+ if (gCheckUpdates) {
+ AddonManager.getAddonByID("override1x2-1x3@tests.mozilla.org", function(a6) {
+ a6.findUpdates({
+ onUpdateFinished: function() {
+ AddonManagerPrivate.removeStartupChange("disabled", "override1x2-1x3@tests.mozilla.org");
+ updated = true;
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ }
+
+ var installed = !gInstallUpdate;
+ if (gInstallUpdate) {
+ // Simulate installing an update while in the dialog
+ installAllFiles([do_get_addon("upgradeable1x2-3_2")], function() {
+ AddonManagerPrivate.removeStartupChange("disabled", "upgradeable1x2-3@tests.mozilla.org");
+ AddonManagerPrivate.addStartupChange("updated", "upgradeable1x2-3@tests.mozilla.org");
+ installed = true;
+ });
+ }
+
+ // The dialog is meant to be opened modally and the install operation can be
+ // asynchronous, so we must spin an event loop (like the modal window does)
+ // until the install is complete
+ let thr = AM_Cc["@mozilla.org/thread-manager;1"].
+ getService(AM_Ci.nsIThreadManager).
+ mainThread;
+
+ while (!installed || !updated)
+ thr.processNextEvent(false);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+function check_state_v1([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "1.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v1_2([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "2.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v2([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "1.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v3([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "1.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v3_2([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "2.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+// Install all the test add-ons, disable two of them and "upgrade" the app to
+// version 2 which will appDisable one.
+add_task(function* init() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+
+ // Add an extension to the profile to make sure the dialog doesn't show up
+ // on new profiles
+ var dest = writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.start(4444);
+
+ startupManager();
+
+ // Remove the add-on we installed directly in the profile directory;
+ // this should show as uninstalled on next restart
+ dest.remove(true);
+
+ // Load up an initial set of add-ons
+ yield promiseInstallAllFiles([do_get_addon("min1max1"),
+ do_get_addon("min1max2"),
+ do_get_addon("upgradeable1x2-3_1"),
+ do_get_addon("min1max3"),
+ do_get_addon("min1max3b"),
+ do_get_addon("override1x2-1x3")]);
+ yield promiseRestartManager();
+
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", ["addon1@tests.mozilla.org"]);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ // user-disable two add-ons
+ let [a2, a4] = yield promiseAddonsByIDs(["min1max2@tests.mozilla.org",
+ "min1max3@tests.mozilla.org"]);
+ do_check_true(a2 != null && a4 != null);
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ yield promiseRestartManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v1(addons);
+
+ // Restart as version 2, add-on _1 should become app-disabled
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("2");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", ["min1max1@tests.mozilla.org"]);
+ check_startup_changes("enabled", []);
+ do_check_false(WindowWatcher.expected);
+
+ addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v2(addons);
+});
+
+// Upgrade to version 3 which will appDisable addons
+// upgradeable1x2-3 and override1x2-1x3
+// Only the newly disabled add-ons should be passed to the
+// upgrade window
+add_task(function* run_test_1() {
+ gCheckUpdates = true;
+ WindowWatcher.expected = true;
+
+ yield promiseRestartManager("3");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", ["upgradeable1x2-3@tests.mozilla.org"]);
+ check_startup_changes("enabled", []);
+ do_check_false(WindowWatcher.expected);
+ gCheckUpdates = false;
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v3(addons);
+
+ do_check_eq(WindowWatcher.arguments.length, 2);
+ do_check_true(WindowWatcher.arguments.indexOf("upgradeable1x2-3@tests.mozilla.org") >= 0);
+ do_check_true(WindowWatcher.arguments.indexOf("override1x2-1x3@tests.mozilla.org") >= 0);
+});
+
+// Downgrade to version 2 which will remove appDisable from two add-ons
+// Still displays the compat window, because metadata is not recently updated
+add_task(function* run_test_2() {
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("2");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", ["upgradeable1x2-3@tests.mozilla.org"]);
+ do_check_false(WindowWatcher.expected);
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v2(addons);
+});
+
+// Upgrade back to version 3 which should only appDisable
+// upgradeable1x2-3, because we already have the override
+// stored in our DB for override1x2-1x3. Ensure that when
+// the upgrade dialog updates an add-on no restart is necessary
+add_task(function* run_test_5() {
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+ // tell the mock compatibility window to install the available upgrade
+ gInstallUpdate = true;
+
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("3");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", ["upgradeable1x2-3@tests.mozilla.org"]);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+ do_check_false(WindowWatcher.expected);
+ gInstallUpdate = false;
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v3_2(addons);
+
+ do_check_eq(WindowWatcher.arguments.length, 1);
+ do_check_true(WindowWatcher.arguments.indexOf("upgradeable1x2-3@tests.mozilla.org") >= 0);
+});
+
+// Downgrade to version 1 which will appEnable all the add-ons
+// except upgradeable1x2-3; the update we installed isn't compatible with 1
+add_task(function* run_test_6() {
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("1");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", ["upgradeable1x2-3@tests.mozilla.org"]);
+ check_startup_changes("enabled", ["min1max1@tests.mozilla.org"]);
+ do_check_false(WindowWatcher.expected);
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v1_2(addons);
+});
+
+add_task(function* cleanup() {
+ return new Promise((resolve, reject) => {
+ testserver.stop(resolve);
+ });
+});
+
+function run_test() {
+ run_next_test();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug554133.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug554133.js
new file mode 100644
index 000000000..c252e1ced
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug554133.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that if the AMO response provides total_results,
+// searchSucceeded is called with the correct number of total results
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+var TESTS = [
+{
+ query: "bug554133",
+ maxResults: 2,
+ length: 2,
+ total: 100
+},
+{
+ query: "bug554133",
+ maxResults: 10,
+ length: 10,
+ total: 100
+},
+{
+ query: "bug554133",
+ maxResults: 100,
+ length: 10,
+ total: 100
+}
+];
+
+var gCurrentTest = 0;
+var SearchCallback = {
+ searchSucceeded: function(addons, length, total) {
+ do_check_false(AddonRepository.isSearching);
+ do_check_eq(addons.length, length);
+ do_check_eq(length, TESTS[gCurrentTest].length);
+ do_check_eq(total, TESTS[gCurrentTest].total);
+
+ gCurrentTest++;
+ run_current_test();
+ },
+
+ searchFailed: function() {
+ server.stop(do_test_finished);
+ do_throw("Search results failed");
+ }
+};
+
+function run_current_test() {
+ if (gCurrentTest < TESTS.length) {
+ var query = TESTS[gCurrentTest].query;
+ var maxResults = TESTS[gCurrentTest].maxResults;
+ AddonRepository.searchAddons(query, maxResults, SearchCallback);
+ }
+ else
+ server.stop(do_test_finished);
+}
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ startupManager();
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data"));
+ mapFile("/data/test_bug554133.xml", server);
+ server.start(-1);
+ gPort = server.identity.primaryPort;
+
+ // Point search to the test server
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS,
+ "http://localhost:" + gPort + "/data/test_%TERMS%.xml");
+
+ do_check_neq(AddonRepository, null);
+ gCurrentTest = 0;
+ run_current_test();
+}
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug559800.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug559800.js
new file mode 100644
index 000000000..41057cd76
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug559800.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that deleting the database from the profile doesn't break
+// anything
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// getting an unused port
+Components.utils.import("resource://testing-common/httpd.js");
+let gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ startupManager();
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
+
+function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ shutdownManager();
+
+ gExtensionsJSON.remove(true);
+
+ do_execute_soon(check_test_1);
+ }));
+}
+
+function check_test_1() {
+ startupManager(false);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ // due to delayed write, the file may not exist until
+ // after shutdown
+ shutdownManager();
+ do_check_true(gExtensionsJSON.exists());
+ do_check_true(gExtensionsJSON.fileSize > 0);
+
+ end_test();
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug563256.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug563256.js
new file mode 100644
index 000000000..2437cf748
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug563256.js
@@ -0,0 +1,259 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the themes switch as expected
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "alternate@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ internalName: "alternate/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], function([d, a]) {
+ do_check_neq(d, null);
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(a, null);
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ run_test_1(d, a);
+ });
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Checks switching to a different theme and back again leaves everything the
+// same
+function run_test_1(d, a) {
+ a.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ d.userDisabled = false;
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ do_execute_soon(run_test_2);
+}
+
+// Tests that after the restart themes can be changed as expected
+function run_test_2() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], function([d, a]) {
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ do_check_neq(d, null);
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(a, null);
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ a.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ d.userDisabled = false;
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ a.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ do_execute_soon(check_test_2);
+ });
+}
+
+function check_test_2() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], callback_soon(function([d, a]) {
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "alternate/1.0");
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_true(a.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ d.userDisabled = false;
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_true(a.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "alternate/1.0");
+
+ restartManager();
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], function([d, a]) {
+ do_check_neq(d, null);
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(a, null);
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ end_test();
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug564030.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug564030.js
new file mode 100644
index 000000000..b5ac157c7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug564030.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that upgrading an incompatible add-on to a compatible one forces an
+// EM restart
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9.2");
+
+ var dest = writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+ }, profileDir);
+ // Attempt to make this look like it was added some time in the past so
+ // the update makes the last modified time change.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a) {
+ do_check_neq(a, null);
+ do_check_eq(a.version, "1.0");
+ do_check_false(a.userDisabled);
+ do_check_true(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a.id));
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a) {
+ do_check_neq(a, null);
+ do_check_eq(a.version, "2.0");
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_true(a.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a.id));
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug566626.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug566626.js
new file mode 100644
index 000000000..641ff87c9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug566626.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that multiple calls to the async API return fully formed
+// add-ons
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gAddon;
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Verifies that multiple calls to get an add-on at various stages of execution
+// return an add-on with a valid name.
+function run_test_1() {
+ var count = 0;
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+
+ do_execute_soon(function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+ });
+
+ do_execute_soon(function() {
+ do_execute_soon(function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+ });
+ });
+}
+
+// Verifies that a subsequent call gets the same add-on from the cache
+function run_test_2() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ do_check_eq(a1, gAddon);
+
+ do_execute_soon(do_test_finished);
+ });
+
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug567184.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug567184.js
new file mode 100644
index 000000000..0e7863068
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug567184.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug567184"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({
+ "bug567184@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_1);
+ install.install();
+ });
+}
+
+function check_test_1() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("bug567184@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug569138.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug569138.js
new file mode 100644
index 000000000..4869fc117
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug569138.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons with invalid target application entries show
+// up in the API but are correctly appDisabled
+
+// A working add-on
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Missing id
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+// Missing minVersion
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ maxVersion: "1"
+ }]
+};
+
+// Missing maxVersion
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1"
+ }]
+};
+
+// Blank id
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+// Blank minVersion
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "",
+ maxVersion: "1"
+ }]
+};
+
+// Blank maxVersion
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: ""
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Set up the profile
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.appDisabled);
+ do_check_false(a2.isActive);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+
+ do_check_neq(a4, null);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isActive);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.appDisabled);
+ do_check_false(a6.isActive);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.appDisabled);
+ do_check_false(a6.isActive);
+
+ do_execute_soon(do_test_finished);
+
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug570173.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug570173.js
new file mode 100644
index 000000000..70de3b426
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug570173.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update check failures are propogated correctly
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_missing.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Verify that an update check returns the correct errors.
+function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let sawCompat = false;
+ let sawUpdate = false;
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ sawCompat = true;
+ },
+
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen a compatibility update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ sawUpdate = true;
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an update");
+ },
+
+ onUpdateFinished: function(addon, error) {
+ do_check_true(sawCompat);
+ do_check_true(sawUpdate);
+ do_check_eq(error, AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
+ end_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js
new file mode 100644
index 000000000..007e82706
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that we recover gracefully from an extension directory disappearing
+// when we were expecting to uninstall it.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ a1.uninstall();
+
+ shutdownManager();
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+
+ writeInstallRDFForExtension(addon2, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ function([a1, a2]) {
+ // Addon1 should no longer be installed
+ do_check_eq(a1, null);
+
+ // Addon2 should have been detected
+ do_check_neq(a2, null);
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug587088.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug587088.js
new file mode 100644
index 000000000..01de80634
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug587088.js
@@ -0,0 +1,174 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that trying to upgrade or uninstall an extension that has a file locked
+// will roll back the upgrade or uninstall and retry at the next restart
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ // This is only an issue on windows.
+ if (!("nsIWindowsRegKey" in AM_Ci))
+ return;
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ run_test_1();
+}
+
+function check_addon(aAddon, aVersion) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, aVersion);
+ do_check_true(aAddon.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, aAddon.id));
+
+ do_check_true(aAddon.hasResource("testfile"));
+ if (aVersion == "1.0") {
+ do_check_true(aAddon.hasResource("testfile1"));
+ do_check_false(aAddon.hasResource("testfile2"));
+ }
+ else {
+ do_check_false(aAddon.hasResource("testfile1"));
+ do_check_true(aAddon.hasResource("testfile2"));
+ }
+
+ do_check_eq(aAddon.pendingOperations, AddonManager.PENDING_NONE);
+}
+
+function check_addon_upgrading(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "1.0");
+ do_check_true(aAddon.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, aAddon.id));
+
+ do_check_true(aAddon.hasResource("testfile"));
+ do_check_true(aAddon.hasResource("testfile1"));
+ do_check_false(aAddon.hasResource("testfile2"));
+
+ do_check_eq(aAddon.pendingOperations, AddonManager.PENDING_UPGRADE);
+
+ do_check_eq(aAddon.pendingUpgrade.version, "2.0");
+}
+
+function check_addon_uninstalling(aAddon, aAfterRestart) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "1.0");
+
+ if (aAfterRestart) {
+ do_check_false(aAddon.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, aAddon.id));
+ }
+ else {
+ do_check_true(aAddon.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, aAddon.id));
+ }
+
+ do_check_true(aAddon.hasResource("testfile"));
+ do_check_true(aAddon.hasResource("testfile1"));
+ do_check_false(aAddon.hasResource("testfile2"));
+
+ do_check_eq(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL);
+}
+
+function run_test_1() {
+ installAllFiles([do_get_addon("test_bug587088_1")], function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ check_addon(a1, "1.0");
+
+ // Lock either install.rdf for unpacked add-ons or the xpi for packed add-ons.
+ let uri = a1.getResourceURI("install.rdf");
+ if (uri.schemeIs("jar"))
+ uri = a1.getResourceURI();
+
+ let fstream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ fstream.init(uri.QueryInterface(AM_Ci.nsIFileURL).file, -1, 0, 0);
+
+ installAllFiles([do_get_addon("test_bug587088_2")], function() {
+
+ check_addon_upgrading(a1);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ check_addon_upgrading(a1);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ check_addon_upgrading(a1);
+
+ fstream.close();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ check_addon(a1, "2.0");
+
+ a1.uninstall();
+ do_execute_soon(run_test_2);
+ });
+ }));
+ }));
+ });
+ });
+ });
+}
+
+// Test that a failed uninstall gets rolled back
+function run_test_2() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_bug587088_1")], function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ check_addon(a1, "1.0");
+
+ // Lock either install.rdf for unpacked add-ons or the xpi for packed add-ons.
+ let uri = a1.getResourceURI("install.rdf");
+ if (uri.schemeIs("jar"))
+ uri = a1.getResourceURI();
+
+ let fstream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ fstream.init(uri.QueryInterface(AM_Ci.nsIFileURL).file, -1, 0, 0);
+
+ a1.uninstall();
+
+ check_addon_uninstalling(a1);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ check_addon_uninstalling(a1, true);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ check_addon_uninstalling(a1, true);
+
+ fstream.close();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+ var dir = profileDir.clone();
+ dir.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ do_check_false(dir.exists());
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+ }));
+ }));
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug594058.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug594058.js
new file mode 100644
index 000000000..9bbda59a8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug594058.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests is modifying a file in an unpacked extension
+// causes cache invalidation.
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Allow the mismatch UI to show
+Services.prefs.setBoolPref("extensions.showMismatchUI", true);
+
+const Ci = Components.interfaces;
+const extDir = gProfD.clone();
+extDir.append("extensions");
+
+var gCachePurged = false;
+
+// Override the window watcher
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ do_check_false(gCachePurged);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+/**
+ * Start the test by installing extensions.
+ */
+function run_test() {
+ do_test_pending();
+ gCachePurged = false;
+
+ let obs = AM_Cc["@mozilla.org/observer-service;1"].
+ getService(AM_Ci.nsIObserverService);
+ obs.addObserver({
+ observe: function(aSubject, aTopic, aData) {
+ gCachePurged = true;
+ }
+ }, "startupcache-invalidate", false);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+ // nsAppRunner takes care of clearing this when a new app is installed
+ do_check_false(gCachePurged);
+
+ installAllFiles([do_get_addon("test_bug594058")], function() {
+ restartManager();
+ do_check_true(gCachePurged);
+ gCachePurged = false;
+
+ // Now, make it look like we've updated the file. First, start the EM
+ // so it records the bogus old time, then update the file and restart.
+ let extFile = extDir.clone();
+ let pastTime = extFile.lastModifiedTime - 5000;
+ extFile.append("bug594058@tests.mozilla.org");
+ setExtensionModifiedTime(extFile, pastTime);
+ let otherFile = extFile.clone();
+ otherFile.append("directory");
+ otherFile.lastModifiedTime = pastTime;
+ otherFile.append("file1");
+ otherFile.lastModifiedTime = pastTime;
+
+ restartManager();
+ gCachePurged = false;
+
+ otherFile.lastModifiedTime = pastTime + 5000;
+ restartManager();
+ do_check_true(gCachePurged);
+ gCachePurged = false;
+
+ restartManager();
+ do_check_false(gCachePurged);
+
+ do_test_finished();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug595081.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug595081.js
new file mode 100644
index 000000000..db53dc747
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug595081.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the AddonManager objects cannot be tampered with
+
+function run_test() {
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ // Verify that properties cannot be changed
+ let old = AddonManager.STATE_AVAILABLE;
+ AddonManager.STATE_AVAILABLE = 28;
+ do_check_eq(AddonManager.STATE_AVAILABLE, old);
+
+ // Verify that functions cannot be replaced
+ AddonManager.isInstallEnabled = function() {
+ do_throw("Should not be able to replace a function");
+ }
+ AddonManager.isInstallEnabled("application/x-xpinstall");
+
+ // Verify that properties cannot be added
+ AddonManager.foo = "bar";
+ do_check_false("foo" in AddonManager);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug595573.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug595573.js
new file mode 100644
index 000000000..7e2bf7d77
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug595573.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests if addons with UUID based ids install and stay installed
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ run_test_1();
+}
+
+function run_test_1() {
+ installAllFiles([do_get_addon("test_bug595573")], function() {
+ restartManager();
+
+ AddonManager.getAddonByID("{2f69dacd-03df-4150-a9f1-e8a7b2748829}", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_execute_soon(run_test_2);
+ });
+ });
+}
+
+function run_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID("{2f69dacd-03df-4150-a9f1-e8a7b2748829}", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug596343.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug596343.js
new file mode 100644
index 000000000..96e95c5ad
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug596343.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/.
+ */
+
+const URI_EXTENSION_SELECT_DIALOG = "chrome://mozapps/content/extensions/selectAddons.xul";
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI";
+const PREF_SHOWN_SELECTION_UI = "extensions.shownSelectionUI";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gExpectedURL = null;
+
+// This will be called to show the any update dialog.
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, arguments) {
+ do_check_eq(url, gExpectedURL);
+ gExpectedURL = null;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(AM_Ci.nsIWindowWatcher)
+ || iid.equals(AM_Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+// Tests that the selection UI is displayed when upgrading an existing profile
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+
+ var dest = writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ // For a new profile it should disable showing the selection UI in the future
+ // without showing the selection UI
+ gExpectedURL = URI_EXTENSION_SELECT_DIALOG;
+ startupManager();
+
+ do_check_true(Services.prefs.getBoolPref(PREF_SHOWN_SELECTION_UI));
+ do_check_eq(gExpectedURL, URI_EXTENSION_SELECT_DIALOG);
+
+ // Reset the 'already shown' pref so that we can test that the first upgrade of
+ // an existing profile shows the selection UI
+ Services.prefs.clearUserPref(PREF_SHOWN_SELECTION_UI);
+
+ restartManager("2");
+
+ do_check_true(Services.prefs.getBoolPref(PREF_SHOWN_SELECTION_UI));
+ do_check_eq(gExpectedURL, null);
+
+ // Once we've seen the selection UI once, future upgrades will show the update dialog
+ // but only if this upgrade disabled an add-on
+ gExpectedURL = URI_EXTENSION_UPDATE_DIALOG;
+
+ restartManager("3");
+
+ do_check_eq(gExpectedURL, null);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug596607.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug596607.js
new file mode 100644
index 000000000..3e655dc87
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug596607.js
@@ -0,0 +1,140 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that a reference to a non-existent extension in the registry doesn't
+// break things
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const addon1Dir = writeInstallRDFForExtension(addon1, gProfD, "addon1");
+const addon2Dir = writeInstallRDFForExtension(addon2, gProfD, "addon2");
+const addon3Dir = gProfD.clone();
+addon3Dir.append("addon3@tests.mozilla.org");
+
+function run_test() {
+ // This test only works where there is a registry.
+ if (!("nsIWindowsRegKey" in AM_Ci))
+ return;
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+// Tests whether starting a fresh profile with a bad entry works
+function run_test_1() {
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon1Dir.path);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon2Dir.path);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon3@tests.mozilla.org", addon3Dir.path);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests whether removing the bad entry has any effect
+function run_test_2() {
+ shutdownManager();
+
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon3@tests.mozilla.org", addon3Dir.path);
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Tests adding the bad entry to an existing profile has any effect
+function run_test_3() {
+ shutdownManager();
+
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon3@tests.mozilla.org", null);
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js
new file mode 100644
index 000000000..d0c973960
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that string comparisons work correctly in callbacks
+
+function test_string_compare() {
+ do_check_true("C".localeCompare("D") < 0);
+ do_check_true("D".localeCompare("C") > 0);
+ do_check_true("\u010C".localeCompare("D") < 0);
+ do_check_true("D".localeCompare("\u010C") > 0);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ do_test_pending();
+
+ test_string_compare();
+
+ AddonManager.getAddonByID("foo", function(aAddon) {
+ test_string_compare();
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug619730.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug619730.js
new file mode 100644
index 000000000..761daf4eb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug619730.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests whether
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_bug619730.xml", gTestserver);
+
+function load_blocklist(file, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+var gSawGFX = false;
+var gSawTest = false;
+
+// Performs the initial setup
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ do_check_true(aSubject instanceof AM_Ci.nsIDOMElement);
+ do_check_eq(aSubject.getAttribute("testattr"), "GFX");
+ do_check_eq(aSubject.childNodes.length, 2);
+ gSawGFX = true;
+ }, "blocklist-data-gfxItems", false);
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ do_check_true(aSubject instanceof AM_Ci.nsIDOMElement);
+ do_check_eq(aSubject.getAttribute("testattr"), "FOO");
+ do_check_eq(aSubject.childNodes.length, 3);
+ gSawTest = true;
+ }, "blocklist-data-testItems", false);
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ do_check_true(gSawGFX);
+ do_check_true(gSawTest);
+ }, "blocklist-data-fooItems", false);
+
+ // Need to wait for the blocklist to load; Bad Things happen if the test harness
+ // shuts down AddonManager before the blocklist service is done telling it about
+ // changes
+ load_blocklist("test_bug619730.xml", () => gTestserver.stop(do_test_finished));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug620837.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug620837.js
new file mode 100644
index 000000000..6bfbfcaf2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug620837.js
@@ -0,0 +1,145 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+const PREF_BLOCKLIST_LASTUPDATETIME = "app.update.lastUpdateTime.blocklist-background-update-timer";
+const PREF_BLOCKLIST_PINGCOUNTTOTAL = "extensions.blocklist.pingCountTotal";
+const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
+
+const SECONDS_IN_DAY = 60 * 60 * 24;
+
+var gExpectedQueryString = null;
+var gNextTest = null;
+var gTestserver = null;
+
+function notify_blocklist() {
+ var blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function pathHandler(metadata, response) {
+ do_check_eq(metadata.queryString, gExpectedQueryString);
+ gNextTest();
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ gTestserver = new HttpServer();
+ gTestserver.registerPathHandler("/", pathHandler);
+ gTestserver.start(-1);
+ gPort = gTestserver.identity.primaryPort;
+
+ Services.prefs.setCharPref("extensions.blocklist.url",
+ "http://localhost:" + gPort +
+ "/?%PING_COUNT%&%TOTAL_PING_COUNT%&%DAYS_SINCE_LAST_PING%");
+
+ do_test_pending();
+ test1();
+}
+
+function getNowInSeconds() {
+ return Math.round(Date.now() / 1000);
+}
+
+function test1() {
+ gNextTest = test2;
+ gExpectedQueryString = "1&1&new";
+ notify_blocklist();
+}
+
+function test2() {
+ gNextTest = test3;
+ gExpectedQueryString = "invalid&invalid&invalid";
+ notify_blocklist();
+}
+
+function test3() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - SECONDS_IN_DAY));
+ gNextTest = test4;
+ gExpectedQueryString = "2&2&1";
+ notify_blocklist();
+}
+
+function test4() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, -1);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 2)));
+ gNextTest = test5;
+ gExpectedQueryString = "1&3&2";
+ notify_blocklist();
+}
+
+function test5() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME, getNowInSeconds());
+ gNextTest = test6;
+ gExpectedQueryString = "invalid&invalid&0";
+ notify_blocklist();
+}
+
+function test6() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 3)));
+ gNextTest = test7;
+ gExpectedQueryString = "2&4&3";
+ notify_blocklist();
+}
+
+function test7() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, 2147483647);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 4)));
+ gNextTest = test8;
+ gExpectedQueryString = "2147483647&5&4";
+ notify_blocklist();
+}
+
+function test8() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 5)));
+ gNextTest = test9;
+ gExpectedQueryString = "1&6&5";
+ notify_blocklist();
+}
+
+function test9() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTTOTAL, 2147483647);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 6)));
+ gNextTest = test10;
+ gExpectedQueryString = "2&2147483647&6";
+ notify_blocklist();
+}
+
+function test10() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 7)));
+ gNextTest = test11;
+ gExpectedQueryString = "3&1&7";
+ notify_blocklist();
+}
+
+function test11() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, -1);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 8)));
+ gNextTest = test12;
+ gExpectedQueryString = "1&2&8";
+ notify_blocklist();
+}
+
+function test12() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 9)));
+ gNextTest = finish;
+ gExpectedQueryString = "2&3&9";
+ notify_blocklist();
+}
+
+function finish() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug655254.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug655254.js
new file mode 100644
index 000000000..45274b734
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug655254.js
@@ -0,0 +1,164 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that moving an extension in the filesystem without any other
+// change still keeps updated compatibility information
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9.2");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_bug655254.rdf", testserver);
+
+var userDir = gProfD.clone();
+userDir.append("extensions2");
+userDir.append(gAppInfo.ID);
+
+var dirProvider = {
+ getFile: function(aProp, aPersistent) {
+ aPersistent.value = false;
+ if (aProp == "XREUSysExt")
+ return userDir.parent;
+ return null;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIDirectoryServiceProvider,
+ AM_Ci.nsISupports])
+};
+Services.dirsvc.registerProvider(dirProvider);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ updateURL: "http://localhost:" + gPort + "/data/test_bug655254.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Set up the profile
+function run_test() {
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ var time = Date.now();
+ var dir = writeInstallRDFForExtension(addon1, userDir);
+ setExtensionModifiedTime(dir, time);
+
+ manuallyInstall(do_get_addon("test_bug655254_2"), userDir, "addon2@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
+
+ a1.findUpdates({
+ onUpdateFinished: function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+
+ shutdownManager();
+
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
+
+ userDir.parent.moveTo(gProfD, "extensions3");
+ userDir = gProfD.clone();
+ userDir.append("extensions3");
+ userDir.append(gAppInfo.ID);
+ do_check_true(userDir.exists());
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
+
+ do_execute_soon(run_test_2);
+ });
+ }));
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+//Set up the profile
+function run_test_2() {
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(a2) {
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
+
+ a2.userDisabled = true;
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
+
+ shutdownManager();
+
+ userDir.parent.moveTo(gProfD, "extensions4");
+ userDir = gProfD.clone();
+ userDir.append("extensions4");
+ userDir.append(gAppInfo.ID);
+ do_check_true(userDir.exists());
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
+
+ end_test();
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js
new file mode 100644
index 000000000..c6e8ab4e2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js
@@ -0,0 +1,340 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that a pending upgrade during a schema update doesn't break things
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ run_test_1();
+}
+
+// Tests whether a schema migration without app version change works
+function run_test_1() {
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "2.0");
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon3.id));
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "2.0");
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Prepare the add-on update, and a bootstrapped addon (bug 693714)
+ installAllFiles([
+ do_get_addon("test_bug659772"),
+ do_get_addon("test_bootstrap1_1")
+ ], function() {
+ shutdownManager();
+
+ // Make it look like the next time the app is started it has a new DB schema
+ changeXPIDBVersion(1);
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ let jsonfile = gProfD.clone();
+ jsonfile.append("extensions");
+ jsonfile.append("staged");
+ jsonfile.append("addon3@tests.mozilla.org.json");
+ do_check_true(jsonfile.exists());
+
+ // Remove an unnecessary property from the cached manifest
+ let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ let json = AM_Cc["@mozilla.org/dom/json;1"].
+ createInstance(AM_Ci.nsIJSON);
+ fis.init(jsonfile, -1, 0, 0);
+ let addonObj = json.decodeFromStream(fis, jsonfile.fileSize);
+ fis.close();
+ delete addonObj.optionsType;
+
+ let stream = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(AM_Ci.nsIConverterOutputStream);
+ stream.init(jsonfile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
+ FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
+ 0);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ converter.writeString(JSON.stringify(addonObj));
+ converter.close();
+ stream.close();
+
+ Services.prefs.clearUserPref("bootstraptest.install_reason");
+ Services.prefs.clearUserPref("bootstraptest.uninstall_reason");
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ // Should stay enabled because we migrate the compat info from
+ // the previous version of the DB
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "2.0");
+ todo_check_false(a3.appDisabled); // XXX unresolved issue
+ do_check_false(a3.userDisabled);
+ todo_check_true(a3.isActive); // XXX same
+ todo_check_true(isExtensionInAddonsList(profileDir, addon3.id)); // XXX same
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "2.0");
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Check that install and uninstall haven't been called on the bootstrapped addon
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.install_reason"));
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.uninstall_reason"));
+
+ a1.uninstall();
+ a2.uninstall();
+ a3.uninstall();
+ a4.uninstall();
+ do_execute_soon(run_test_2);
+ });
+ });
+ });
+}
+
+// Tests whether a schema migration with app version change works
+function run_test_2() {
+ restartManager();
+
+ shutdownManager();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "2.0");
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon3.id));
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "2.0");
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Prepare the add-on update, and a bootstrapped addon (bug 693714)
+ installAllFiles([
+ do_get_addon("test_bug659772"),
+ do_get_addon("test_bootstrap1_1")
+ ], function() { do_execute_soon(prepare_schema_migrate); });
+
+ function prepare_schema_migrate() {
+ shutdownManager();
+
+ // Make it look like the next time the app is started it has a new DB schema
+ changeXPIDBVersion(1);
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ let jsonfile = gProfD.clone();
+ jsonfile.append("extensions");
+ jsonfile.append("staged");
+ jsonfile.append("addon3@tests.mozilla.org.json");
+ do_check_true(jsonfile.exists());
+
+ // Remove an unnecessary property from the cached manifest
+ let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ let json = AM_Cc["@mozilla.org/dom/json;1"].
+ createInstance(AM_Ci.nsIJSON);
+ fis.init(jsonfile, -1, 0, 0);
+ let addonObj = json.decodeFromStream(fis, jsonfile.fileSize);
+ fis.close();
+ delete addonObj.optionsType;
+
+ let stream = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(AM_Ci.nsIConverterOutputStream);
+ stream.init(jsonfile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
+ FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
+ 0);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ converter.writeString(JSON.stringify(addonObj));
+ converter.close();
+ stream.close();
+
+ Services.prefs.clearUserPref("bootstraptest.install_reason");
+ Services.prefs.clearUserPref("bootstraptest.uninstall_reason");
+
+ gAppInfo.version = "2";
+ startupManager(true);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ // Should become appDisabled because we migrate the compat info from
+ // the previous version of the DB
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "2.0");
+ todo_check_true(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ todo_check_false(a3.isActive);
+ todo_check_false(isExtensionInAddonsList(profileDir, addon3.id));
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "2.0");
+ do_check_false(a4.appDisabled);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Check that install and uninstall haven't been called on the bootstrapped addon
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.install_reason"));
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.uninstall_reason"));
+
+ a1.uninstall();
+ a2.uninstall();
+ a3.uninstall();
+ a4.uninstall();
+ restartManager();
+
+ shutdownManager();
+
+ do_test_finished();
+ }));
+ };
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug675371.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug675371.js
new file mode 100644
index 000000000..579335d8a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug675371.js
@@ -0,0 +1,91 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug675371"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test));
+ install.install();
+ });
+}
+
+function check_test() {
+ AddonManager.getAddonByID("bug675371@tests.mozilla.org", do_exception_wrap(function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+
+ // Tests that chrome.manifest is registered when the addon is installed.
+ var target = { active: false };
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ // Tests that chrome.manifest is unregistered when the addon is disabled.
+ addon.userDisabled = true;
+ target.active = false;
+ try {
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_throw("Chrome file should not have been found");
+ } catch (e) {
+ do_check_false(target.active);
+ }
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ // Tests that chrome.manifest is registered when the addon is enabled.
+ addon.userDisabled = false;
+ target.active = false;
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ // Tests that chrome.manifest is unregistered when the addon is uninstalled.
+ addon.uninstall();
+ target.active = false;
+ try {
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_throw("Chrome file should not have been found");
+ } catch (e) {
+ do_check_false(target.active);
+ }
+
+ do_execute_soon(do_test_finished);
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug740612.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug740612.js
new file mode 100644
index 000000000..d17e7acde
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug740612.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that attempts to override the global values fails but doesn't
+// destroy the world with it
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function getInstalledVersion() {
+ return Services.prefs.getIntPref("bootstraptest.installed_version");
+}
+
+function run_test() {
+ do_test_pending();
+
+ manuallyInstall(do_get_addon("test_bug740612_1"), profileDir,
+ "bug740612_1@tests.mozilla.org");
+ manuallyInstall(do_get_addon("test_bug740612_2"), profileDir,
+ "bug740612_2@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["bug740612_1@tests.mozilla.org",
+ "bug740612_2@tests.mozilla.org"],
+ function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_neq(a2, null);
+ do_check_eq(getInstalledVersion(), "1.0");
+ do_check_eq(getActiveVersion(), "1.0");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug753900.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug753900.js
new file mode 100644
index 000000000..206862339
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug753900.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that strange characters in an add-on version don't break the
+// crash annotation.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1,0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1:0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1,0",
+ name: "Test 3",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1:0",
+ name: "Test 4",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+ do_check_neq(a2, null);
+ do_check_in_crash_annotation(addon2.id, addon2.version);
+ do_check_neq(a3, null);
+ do_check_in_crash_annotation(addon3.id, addon3.version);
+ do_check_neq(a4, null);
+ do_check_in_crash_annotation(addon4.id, addon4.version);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js
new file mode 100644
index 000000000..648c7acc3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This test verifies that removing a listener during a callback for that type
+// of listener still results in all listeners being called.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var listener1 = {
+ sawEvent: false,
+ onDisabling: function() {
+ this.sawEvent = true;
+ AddonManager.removeAddonListener(this);
+ },
+ onNewInstall: function() {
+ this.sawEvent = true;
+ AddonManager.removeInstallListener(this);
+ }
+};
+var listener2 = {
+ sawEvent: false,
+ onDisabling: function() {
+ this.sawEvent = true;
+ },
+ onNewInstall: function() {
+ this.sawEvent = true;
+ }
+};
+var listener3 = {
+ sawEvent: false,
+ onDisabling: function() {
+ this.sawEvent = true;
+ },
+ onNewInstall: function() {
+ this.sawEvent = true;
+ }
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.addAddonListener(listener1);
+ AddonManager.addAddonListener(listener2);
+ AddonManager.addAddonListener(listener3);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org"], function([a1]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+
+ a1.userDisabled = true;
+
+ do_check_true(listener1.sawEvent);
+ listener1.sawEvent = false;
+ do_check_true(listener2.sawEvent);
+ listener2.sawEvent = false;
+ do_check_true(listener3.sawEvent);
+ listener3.sawEvent = false;
+
+ AddonManager.removeAddonListener(listener1);
+ AddonManager.removeAddonListener(listener2);
+ AddonManager.removeAddonListener(listener3);
+
+ a1.uninstall();
+ run_test_2();
+ });
+}
+
+function run_test_2() {
+ AddonManager.addInstallListener(listener1);
+ AddonManager.addInstallListener(listener2);
+ AddonManager.addInstallListener(listener3);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug757663"), function(aInstall) {
+
+ do_check_true(listener1.sawEvent);
+ listener1.sawEvent = false;
+ do_check_true(listener2.sawEvent);
+ listener2.sawEvent = false;
+ do_check_true(listener3.sawEvent);
+ listener3.sawEvent = false;
+
+ AddonManager.removeInstallListener(listener1);
+ AddonManager.removeInstallListener(listener2);
+ AddonManager.removeInstallListener(listener3);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug953156.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug953156.js
new file mode 100644
index 000000000..a7acb9ad2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug953156.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug675371"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test));
+ install.install();
+ });
+}
+
+function check_test() {
+ AddonManager.getAddonByID("bug675371@tests.mozilla.org", do_exception_wrap(function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+
+ // Tests that chrome.manifest is registered when the addon is installed.
+ var target = { active: false };
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ shutdownManager();
+
+ // Tests that chrome.manifest remains registered at app shutdown.
+ target.active = false;
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ do_execute_soon(do_test_finished);
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js b/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
new file mode 100644
index 000000000..4e7da0ee7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that flushing the zipreader cache happens when appropriate
+
+var gExpectedFile = null;
+var gCacheFlushCount = 0;
+
+var CacheFlushObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != "flush-cache-entry")
+ return;
+
+ do_check_true(gExpectedFile != null);
+ do_check_true(aSubject instanceof AM_Ci.nsIFile);
+ do_check_eq(aSubject.path, gExpectedFile.path);
+ gCacheFlushCount++;
+ }
+};
+
+function run_test() {
+ do_test_pending();
+ Services.obs.addObserver(CacheFlushObserver, "flush-cache-entry", false);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that the cache is flushed when cancelling a pending install
+function run_test_1() {
+ AddonManager.getInstallForFile(do_get_addon("test_cacheflush1"), function(aInstall) {
+ completeAllInstalls([aInstall], function() {
+ // We should flush the staged XPI when cancelling the install
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("staged");
+ gExpectedFile.append("addon1@tests.mozilla.org.xpi");
+ aInstall.cancel();
+
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ run_test_2();
+ });
+ });
+}
+
+// Tests that the cache is flushed when uninstalling an add-on
+function run_test_2() {
+ installAllFiles([do_get_addon("test_cacheflush1")], function() {
+ // Installing will flush the staged XPI during startup
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("staged");
+ gExpectedFile.append("addon1@tests.mozilla.org.xpi");
+ restartManager();
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ // We should flush the installed XPI when uninstalling
+ do_check_true(a1 != null);
+ a1.uninstall();
+ do_check_eq(gCacheFlushCount, 0);
+
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("addon1@tests.mozilla.org.xpi");
+ restartManager();
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ do_execute_soon(run_test_3);
+ });
+ });
+}
+
+// Tests that the cache is flushed when installing a restartless add-on
+function run_test_3() {
+ AddonManager.getInstallForFile(do_get_addon("test_cacheflush2"), function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function(aInstall) {
+ // We should flush the staged XPI when completing the install
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("staged");
+ gExpectedFile.append("addon2@tests.mozilla.org.xpi");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ do_execute_soon(run_test_4);
+ }
+ });
+
+ aInstall.install();
+ });
+}
+
+// Tests that the cache is flushed when uninstalling a restartless add-on
+function run_test_4() {
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ // We should flush the installed XPI when uninstalling
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("addon2@tests.mozilla.org.xpi");
+
+ a2.uninstall();
+ do_check_eq(gCacheFlushCount, 2);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js b/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js
new file mode 100644
index 000000000..c872d75c3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the (temporary)
+// extensions.checkCompatibility.temporaryThemeOverride_minAppVersion
+// preference works.
+
+var ADDONS = [{
+ id: "addon1@tests.mozilla.org",
+ type: 4,
+ internalName: "theme1/1.0",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1.0",
+ maxVersion: "1.0"
+ }]
+}, {
+ id: "addon2@tests.mozilla.org",
+ type: 4,
+ internalName: "theme2/1.0",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2.0",
+ maxVersion: "2.0"
+ }]
+}];
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3.0", "1");
+
+ for (let a of ADDONS) {
+ writeInstallRDFForExtension(a, profileDir);
+ }
+
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ function([a1, a2]) {
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_false(a1.isCompatible);
+ do_check_true(a1.appDisabled);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+ do_check_true(a1.appDisabled);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ Services.prefs.setCharPref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "2.0");
+ if (isNightlyChannel())
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.3.0", false);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ function([a1, a2]) {
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_false(a1.isCompatible);
+ do_check_true(a1.appDisabled);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_checkcompatibility.js b/toolkit/mozapps/extensions/test/xpcshell/test_checkcompatibility.js
new file mode 100644
index 000000000..b9fc0b3ab
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_checkcompatibility.js
@@ -0,0 +1,196 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the extensions.checkCompatibility.* preferences work.
+
+var ADDONS = [{
+ // Cannot be enabled as it has no target app info for the applciation
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "unknown@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+}, {
+ // Always appears incompatible but can be enabled if compatibility checking is
+ // disabled
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+}, {
+ // Always appears incompatible but can be enabled if compatibility checking is
+ // disabled
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+}, { // Always compatible and enabled
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+}, { // Always compatible and enabled
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+}];
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gIsNightly = false;
+
+function run_test() {
+ do_test_pending("checkcompatibility.js");
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.2.3", "2");
+
+ ADDONS.forEach(function(a) {
+ writeInstallRDFForExtension(a, profileDir);
+ });
+
+ gIsNightly = isNightlyChannel();
+
+ startupManager();
+
+ run_test_1();
+}
+
+/**
+ * Checks that the add-ons are enabled as expected.
+ * @param overridden
+ * A boolean indicating that compatibility checking is overridden
+ * @param a1
+ * The Addon for addon1@tests.mozilla.org
+ * @param a2
+ * The Addon for addon2@tests.mozilla.org
+ * @param a3
+ * The Addon for addon3@tests.mozilla.org
+ * @param a4
+ * The Addon for addon4@tests.mozilla.org
+ * @param a5
+ * The Addon for addon5@tests.mozilla.org
+ */
+function check_state(overridden, a1, a2, a3, a4, a5) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_false(a1.isCompatible);
+
+ do_check_neq(a2, null);
+ if (overridden)
+ do_check_true(a2.isActive);
+ else
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+
+ do_check_neq(a3, null);
+ if (overridden)
+ do_check_true(a3.isActive);
+ else
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_true(a4.isCompatible);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_true(a5.isCompatible);
+}
+
+// Tests that with compatibility checking enabled we see the incompatible
+// add-ons disabled
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(false, a1, a2, a3, a4, a5);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests that with compatibility checking disabled we see the incompatible
+// add-ons enabled
+function run_test_2() {
+ if (gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.2", false);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(true, a1, a2, a3, a4, a5);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Tests that with compatibility checking disabled we see the incompatible
+// add-ons enabled.
+function run_test_3() {
+ if (!gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.1a", false);
+ restartManager("2.1a4");
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(true, a1, a2, a3, a4, a5);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Tests that with compatibility checking enabled we see the incompatible
+// add-ons disabled.
+function run_test_4() {
+ if (gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", true);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.1a", true);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(false, a1, a2, a3, a4, a5);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_childprocess.js b/toolkit/mozapps/extensions/test/xpcshell/test_childprocess.js
new file mode 100644
index 000000000..a6c635eac
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_childprocess.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the AddonManager refuses to load in child processes.
+
+function run_test() {
+ // Already loaded the module by head_addons.js. Need to unload this again, so
+ // that overriding the app-info and re-importing the module works.
+ Components.utils.unload("resource://gre/modules/AddonManager.jsm");
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ gAppInfo.processType = AM_Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+ try {
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+ do_throw("AddonManager should have refused to load");
+ }
+ catch (ex) {
+ do_print(ex.message);
+ do_check_true(!!ex.message);
+ }
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js b/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
new file mode 100644
index 000000000..ef60306db
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
@@ -0,0 +1,259 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests compatibility overrides, for when strict compatibility checking is
+// disabled. See bug 693906.
+
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+const PORT = gPort;
+const BASE_URL = "http://localhost:" + PORT;
+const DEFAULT_URL = "about:blank";
+const REQ_URL = "/data.xml";
+
+// register static file and mark it for interpolation
+mapUrlToFile(REQ_URL, do_get_file("data/test_compatoverrides.xml"), gServer);
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ BASE_URL + REQ_URL);
+
+
+// Not hosted, no overrides
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, no overrides
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, matching override
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, matching override, wouldn't be compatible if strict checking is enabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }]
+};
+
+// Hosted, app ID doesn't match in override
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, addon version range doesn't match in override
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, app version range doesn't match in override
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 7",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, multiple overrides
+var addon8 = {
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 8",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Not hosted, matching override
+var addon9 = {
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 9",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Not hosted, override is of unsupported type (compatible)
+var addon10 = {
+ id: "addon10@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 10",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(addon8, profileDir);
+ writeInstallRDFForExtension(addon9, profileDir);
+ writeInstallRDFForExtension(addon10, profileDir);
+
+ startupManager();
+
+ AddonManagerInternal.backgroundUpdateCheck().then(run_test_1);
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
+
+function check_compat_status(aCallback) {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.compatibilityOverrides, null);
+ do_check_true(a1.isCompatible);
+ do_check_false(a1.appDisabled);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.compatibilityOverrides, null);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+
+ do_check_neq(a3, null);
+ do_check_neq(a3.compatibilityOverrides, null);
+ do_check_eq(a3.compatibilityOverrides.length, 1);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+
+ do_check_neq(a4, null);
+ do_check_neq(a4.compatibilityOverrides, null);
+ do_check_eq(a4.compatibilityOverrides.length, 1);
+ do_check_false(a4.isCompatible);
+ do_check_true(a4.appDisabled);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.compatibilityOverrides, null);
+ do_check_true(a5.isCompatible);
+ do_check_false(a5.appDisabled);
+
+ do_check_neq(a6, null);
+ do_check_neq(a6.compatibilityOverrides, null);
+ do_check_eq(a6.compatibilityOverrides.length, 1);
+ do_check_true(a6.isCompatible);
+ do_check_false(a6.appDisabled);
+
+ do_check_neq(a7, null);
+ do_check_neq(a7.compatibilityOverrides, null);
+ do_check_eq(a7.compatibilityOverrides.length, 1);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+
+ do_check_neq(a8, null);
+ do_check_neq(a8.compatibilityOverrides, null);
+ do_check_eq(a8.compatibilityOverrides.length, 3);
+ do_check_false(a8.isCompatible);
+ do_check_true(a8.appDisabled);
+
+ do_check_neq(a9, null);
+ do_check_neq(a9.compatibilityOverrides, null);
+ do_check_eq(a9.compatibilityOverrides.length, 1);
+ do_check_false(a9.isCompatible);
+ do_check_true(a9.appDisabled);
+
+ do_check_neq(a10, null);
+ do_check_eq(a10.compatibilityOverrides, null);
+ do_check_true(a10.isCompatible);
+ do_check_false(a10.appDisabled);
+
+ do_execute_soon(aCallback);
+ });
+}
+
+function run_test_1() {
+ do_print("Run test 1");
+ check_compat_status(run_test_2);
+}
+
+function run_test_2() {
+ do_print("Run test 2");
+ restartManager();
+ check_compat_status(end_test);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
new file mode 100644
index 000000000..4c8b3750d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
@@ -0,0 +1,403 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+
+Components.utils.import("resource://testing-common/httpd.js");
+// Create and configure the HTTP server.
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register files with server
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_corrupt.rdf", testserver);
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and stay enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Would stay incompatible with strict compat
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a2, a3, a4,
+ a7, t2]) {
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(run_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Shutdown and replace the database with a corrupt file (a directory
+ // serves this purpose). On startup the add-ons manager won't rebuild
+ // because there is a file there still.
+ shutdownManager();
+ gExtensionsJSON.remove(true);
+ gExtensionsJSON.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ startupManager(false);
+
+ // Accessing the add-ons should open and recover the database
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ // Should be correctly recovered
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ // The compatibility update won't be recovered and with strict
+ // compatibility it would not have been able to tell that it was
+ // previously userDisabled. However, without strict compat, it wasn't
+ // appDisabled, so it knows it must have been userDisabled.
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ end_test();
+ }));
+ }));
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt_strictcompat.js
new file mode 100644
index 000000000..3ba6d213b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt_strictcompat.js
@@ -0,0 +1,402 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+
+Components.utils.import("resource://testing-common/httpd.js");
+// Create and configure the HTTP server.
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register files with server
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_corrupt.rdf", testserver);
+
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be disabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Stays incompatible
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a2, a3, a4,
+ a7, t2]) {
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(run_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Shutdown and replace the database with a corrupt file (a directory
+ // serves this purpose). On startup the add-ons manager won't rebuild
+ // because there is a file there still.
+ shutdownManager();
+ gExtensionsJSON.remove(true);
+ gExtensionsJSON.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ startupManager(false);
+
+ // Accessing the add-ons should open and recover the database
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ // Should be correctly recovered
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE);
+
+ // The compatibility update won't be recovered and it will not have been
+ // able to tell that it was previously userDisabled
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ end_test();
+ }));
+ }));
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_corruptfile.js b/toolkit/mozapps/extensions/test/xpcshell/test_corruptfile.js
new file mode 100644
index 000000000..92b375850
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_corruptfile.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that attempting to install a corrupt XPI file doesn't break the universe
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ if (TEST_UNPACKED)
+ run_test_unpacked();
+ else
+ run_test_packed();
+}
+
+// When installing packed we won't detect corruption in the XPI until we attempt
+// to load bootstrap.js so everything will look normal from the outside.
+function run_test_packed() {
+ do_test_pending();
+
+ prepare_test({
+ "corrupt@tests.mozilla.org": [
+ ["onInstalling", false],
+ ["onInstalled", false]
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+
+ installAllFiles([do_get_file("data/corruptfile.xpi")], function() {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("corrupt@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_test_finished();
+ });
+ });
+}
+
+// When extracting the corruption will be detected and the add-on fails to
+// install
+function run_test_unpacked() {
+ do_test_pending();
+
+ prepare_test({
+ "corrupt@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onOperationCancelled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallFailed"
+ ]);
+
+ installAllFiles([do_get_file("data/corruptfile.xpi")], function() {
+ ensure_test_completed();
+
+ // Check the add-on directory isn't left over
+ var addonDir = profileDir.clone();
+ addonDir.append("corrupt@tests.mozilla.org");
+ pathShouldntExist(addonDir);
+
+ // Check the staging directory isn't left over
+ var stageDir = profileDir.clone();
+ stageDir.append("staged");
+ pathShouldntExist(stageDir);
+
+ AddonManager.getAddonByID("corrupt@tests.mozilla.org", function(addon) {
+ do_check_eq(addon, null);
+
+ do_test_finished();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_dataDirectory.js b/toolkit/mozapps/extensions/test/xpcshell/test_dataDirectory.js
new file mode 100644
index 000000000..99babc722
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_dataDirectory.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/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var ADDON = {
+ id: "datadirectory1@tests.mozilla.org",
+ addon: "test_data_directory"
+};
+
+var expectedDir = gProfD.clone();
+expectedDir.append("extension-data");
+expectedDir.append(ADDON.id);
+
+function run_test() {
+ do_test_pending();
+ do_check_false(expectedDir.exists());
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9");
+ startupManager();
+
+ installAllFiles([do_get_addon(ADDON.addon)], function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ADDON.id, function(item) {
+ item.getDataDirectory(promise_callback);
+ });
+ });
+}
+
+function promise_callback() {
+ do_check_eq(arguments.length, 2);
+ var expectedDir = gProfD.clone();
+ expectedDir.append("extension-data");
+ expectedDir.append(ADDON.id);
+
+ do_check_eq(arguments[0], expectedDir.path);
+ do_check_true(expectedDir.exists());
+ do_check_true(expectedDir.isDirectory());
+
+ do_check_eq(arguments[1], null);
+
+ // Cleanup.
+ expectedDir.parent.remove(true);
+
+ do_test_finished();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_default_providers_pref.js b/toolkit/mozapps/extensions/test/xpcshell/test_default_providers_pref.js
new file mode 100644
index 000000000..1b61e033a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_default_providers_pref.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the extensions.defaultProviders.enabled pref which turns
+// off the default XPIProvider and LightweightThemeManager.
+
+function run_test() {
+ Services.prefs.setBoolPref("extensions.defaultProviders.enabled", false);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+ do_check_false(AddonManager.isInstallEnabled("application/x-xpinstall"));
+ Services.prefs.clearUserPref("extensions.defaultProviders.enabled");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js b/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
new file mode 100644
index 000000000..c24b5a1b0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
@@ -0,0 +1,801 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that bootstrappable add-ons can be used without restarts.
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+
+Components.utils.import("resource://testing-common/httpd.js");
+// Create and configure the HTTP server.
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register files with server
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_dictionary.rdf", testserver);
+
+/**
+ * This object is both a factory and an mozISpellCheckingEngine implementation (so, it
+ * is de-facto a service). It's also an interface requestor that gives out
+ * itself when asked for mozISpellCheckingEngine.
+ */
+var HunspellEngine = {
+ dictionaryDirs: [],
+ listener: null,
+
+ QueryInterface: function hunspell_qi(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsIFactory) ||
+ iid.equals(Components.interfaces.mozISpellCheckingEngine))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+ createInstance: function hunspell_ci(outer, iid) {
+ if (outer)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return this.QueryInterface(iid);
+ },
+ lockFactory: function hunspell_lockf(lock) {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ addDirectory: function hunspell_addDirectory(dir) {
+ this.dictionaryDirs.push(dir);
+ if (this.listener)
+ this.listener("addDirectory");
+ },
+
+ removeDirectory: function hunspell_addDirectory(dir) {
+ this.dictionaryDirs.splice(this.dictionaryDirs.indexOf(dir), 1);
+ if (this.listener)
+ this.listener("removeDirectory");
+ },
+
+ getInterface: function hunspell_gi(iid) {
+ if (iid.equals(Components.interfaces.mozISpellCheckingEngine))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ contractID: "@mozilla.org/spellchecker/engine;1",
+ classID: Components.ID("{6f3c63bc-a4fd-449b-9a58-a2d9bd972cce}"),
+
+ activate: function hunspell_activate() {
+ this.origClassID = Components.manager.nsIComponentRegistrar
+ .contractIDToCID(this.contractID);
+ this.origFactory = Components.manager
+ .getClassObject(Components.classes[this.contractID],
+ Components.interfaces.nsIFactory);
+
+ Components.manager.nsIComponentRegistrar
+ .unregisterFactory(this.origClassID, this.origFactory);
+ Components.manager.nsIComponentRegistrar.registerFactory(this.classID,
+ "Test hunspell", this.contractID, this);
+ },
+
+ deactivate: function hunspell_deactivate() {
+ Components.manager.nsIComponentRegistrar.unregisterFactory(this.classID, this);
+ Components.manager.nsIComponentRegistrar.registerFactory(this.origClassID,
+ "Hunspell", this.contractID, this.origFactory);
+ },
+
+ isDictionaryEnabled: function hunspell_isDictionaryEnabled(name) {
+ return this.dictionaryDirs.some(function(dir) {
+ var dic = dir.clone();
+ dic.append(name);
+ return dic.exists();
+ });
+ }
+};
+
+function run_test() {
+ do_test_pending();
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ HunspellEngine.activate();
+
+ AddonManager.getInstallForFile(do_get_addon("test_dictionary"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "dictionary");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Dictionary");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_false(install.addon.hasResource("bootstrap.js"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let addon = install.addon;
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ HunspellEngine.listener = function(aEvent) {
+ HunspellEngine.listener = null;
+ do_check_eq(aEvent, "addDirectory");
+ do_execute_soon(check_test_1);
+ };
+ });
+ install.install();
+ });
+}
+
+function check_test_1() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, "ab-CD@dictionaries.addons.mozilla.org");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 0);
+
+ run_test_2();
+ });
+ });
+ });
+}
+
+// Tests that disabling doesn't require a restart
+function run_test_2() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_true(newb1.userDisabled);
+ do_check_false(newb1.isActive);
+
+ do_execute_soon(run_test_3);
+ });
+ });
+}
+
+// Test that restarting doesn't accidentally re-enable
+function run_test_3() {
+ shutdownManager();
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ startupManager(false);
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ run_test_4();
+ });
+}
+
+// Tests that enabling doesn't require a restart
+function run_test_4() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_false(newb1.userDisabled);
+ do_check_true(newb1.isActive);
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Tests that a restart shuts down and restarts the add-on
+function run_test_5() {
+ shutdownManager();
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+ startupManager(false);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, b1.id));
+
+ run_test_7();
+ });
+}
+
+// Tests that uninstalling doesn't require a restart
+function run_test_7() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ b1.uninstall();
+
+ check_test_7();
+ });
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_eq(b1, null);
+
+ restartManager();
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(newb1) {
+ do_check_eq(newb1, null);
+
+ do_execute_soon(run_test_8);
+ });
+ }));
+}
+
+// Test that a bootstrapped extension dropped into the profile loads properly
+// on startup and doesn't cause an EM restart
+function run_test_8() {
+ shutdownManager();
+
+ let dir = profileDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(do_get_addon("test_dictionary"));
+ dir.append("install.rdf");
+ zip.extract("install.rdf", dir);
+ dir = dir.parent;
+ dir.append("dictionaries");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ dir.append("ab-CD.dic");
+ zip.extract("dictionaries/ab-CD.dic", dir);
+ zip.close();
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Test that items detected as removed during startup get removed properly
+function run_test_9() {
+ shutdownManager();
+
+ let dir = profileDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.remove(true);
+ startupManager(false);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_eq(b1, null);
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+
+// Tests that bootstrapped extensions are correctly loaded even if the app is
+// upgraded at the same time
+function run_test_12() {
+ shutdownManager();
+
+ let dir = profileDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(do_get_addon("test_dictionary"));
+ dir.append("install.rdf");
+ zip.extract("install.rdf", dir);
+ dir = dir.parent;
+ dir.append("dictionaries");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ dir.append("ab-CD.dic");
+ zip.extract("dictionaries/ab-CD.dic", dir);
+ zip.close();
+
+ startupManager(true);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ b1.uninstall();
+ do_execute_soon(run_test_16);
+ });
+}
+
+
+// Tests that bootstrapped extensions don't get loaded when in safe mode
+function run_test_16() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_dictionary")], function() {
+ // spin the event loop to let the addon finish starting
+ do_execute_soon(function check_installed_dictionary() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ // Should have installed and started
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ shutdownManager();
+
+ // Should have stopped
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ gAppInfo.inSafeMode = true;
+ startupManager(false);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ // Should still be stopped
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_false(b1.isActive);
+
+ shutdownManager();
+ gAppInfo.inSafeMode = false;
+ startupManager(false);
+
+ // Should have started
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ b1.uninstall();
+
+ do_execute_soon(run_test_17);
+ });
+ }));
+ }));
+ });
+ });
+}
+
+// Check that a bootstrapped extension in a non-profile location is loaded
+function run_test_17() {
+ shutdownManager();
+
+ let dir = userExtDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(do_get_addon("test_dictionary"));
+ dir.append("install.rdf");
+ zip.extract("install.rdf", dir);
+ dir = dir.parent;
+ dir.append("dictionaries");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ dir.append("ab-CD.dic");
+ zip.extract("dictionaries/ab-CD.dic", dir);
+ zip.close();
+
+ startupManager();
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ // Should have installed and started
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+
+ // From run_test_21
+ dir = userExtDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.remove(true);
+
+ restartManager();
+
+ run_test_23();
+ }));
+}
+
+// Tests that installing from a URL doesn't require a restart
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_dictionary.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], function() {
+ do_check_eq(install.type, "dictionary");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Dictionary");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_false(install.addon.hasResource("bootstrap.js"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let addon = install.addon;
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ // spin to let the addon startup finish
+ do_execute_soon(check_test_23);
+ });
+ });
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_23() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, "ab-CD@dictionaries.addons.mozilla.org");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 0);
+
+ restartManager();
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ b1.uninstall();
+ do_execute_soon(run_test_25);
+ });
+ }));
+ });
+ });
+}
+
+// Tests that updating from a bootstrappable add-on to a normal add-on calls
+// the uninstall method
+function run_test_25() {
+ restartManager();
+
+ HunspellEngine.listener = function(aEvent) {
+ HunspellEngine.listener = null;
+ do_check_eq(aEvent, "addDirectory");
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ installAllFiles([do_get_addon("test_dictionary_2")], function test_25_installed2() {
+ // Needs a restart to complete this so the old version stays running
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_execute_soon(run_test_26);
+ });
+ }));
+ });
+ };
+
+ installAllFiles([do_get_addon("test_dictionary")], function test_25_installed() { });
+}
+
+// Tests that updating from a normal add-on to a bootstrappable add-on calls
+// the install method
+function run_test_26() {
+ installAllFiles([do_get_addon("test_dictionary")], function test_26_install() {
+ // Needs a restart to complete this
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ HunspellEngine.deactivate();
+ b1.uninstall();
+ do_execute_soon(run_test_27);
+ });
+ }));
+ });
+}
+
+// Tests that an update check from a normal add-on to a bootstrappable add-on works
+function run_test_27() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "ab-CD@dictionaries.addons.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_dictionary.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Dictionary",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test_27));
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function check_test_27(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_eq(b1.type, "dictionary");
+ b1.uninstall();
+ do_execute_soon(run_test_28);
+ });
+}
+
+// Tests that an update check from a bootstrappable add-on to a normal add-on works
+function run_test_28() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "ef@dictionaries.addons.mozilla.org",
+ version: "1.0",
+ type: "64",
+ updateURL: "http://localhost:" + gPort + "/data/test_dictionary.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Dictionary ef",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "ef@dictionaries.addons.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test_28));
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function check_test_28(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("ef@dictionaries.addons.mozilla.org", function(b2) {
+ do_check_neq(b2, null);
+ do_check_eq(b2.version, "2.0");
+ do_check_eq(b2.type, "extension");
+ b2.uninstall();
+ do_execute_soon(run_test_29);
+ });
+}
+
+// Tests that an update check from a bootstrappable add-on to a bootstrappable add-on works
+function run_test_29() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "gh@dictionaries.addons.mozilla.org",
+ version: "1.0",
+ type: "64",
+ updateURL: "http://localhost:" + gPort + "/data/test_dictionary.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Dictionary gh",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "gh@dictionaries.addons.mozilla.org": [
+ ["onInstalling", false /* = no restart */],
+ ["onInstalled", false]
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], check_test_29);
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function check_test_29(install) {
+ AddonManager.getAddonByID("gh@dictionaries.addons.mozilla.org", function(b2) {
+ do_check_neq(b2, null);
+ do_check_eq(b2.version, "2.0");
+ do_check_eq(b2.type, "dictionary");
+
+ prepare_test({
+ "gh@dictionaries.addons.mozilla.org": [
+ ["onUninstalling", false],
+ ["onUninstalled", false],
+ ]
+ }, [
+ ], callback_soon(finish_test_29));
+
+ b2.uninstall();
+ });
+}
+
+function finish_test_29() {
+ testserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_disable.js b/toolkit/mozapps/extensions/test/xpcshell/test_disable.js
new file mode 100644
index 000000000..867715863
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_disable.js
@@ -0,0 +1,194 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// This verifies that add-ons can be disabled and enabled.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ optionsURL: "chrome://foo/content/options.xul",
+ aboutURL: "chrome://foo/content/about.xul",
+ iconURL: "chrome://foo/content/icon.png",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gIconURL = null;
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_eq(a1, null);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ writeInstallRDFForExtension(addon1, profileDir, addon1.id, "icon.png");
+ gIconURL = do_get_addon_root_uri(profileDir.clone(), addon1.id) + "icon.png";
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_true(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(newa1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(newa1.iconURL, "chrome://foo/content/icon.png");
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_1();
+ });
+ }));
+}
+
+// Disabling an add-on should work
+function run_test_1() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ a1.userDisabled = true;
+ do_check_eq(a1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(a1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(a1.iconURL, "chrome://foo/content/icon.png");
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_false(newa1.isActive);
+ do_check_true(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, null);
+ do_check_eq(newa1.optionsURL, null);
+ do_check_eq(newa1.iconURL, gIconURL);
+ do_check_false(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_2();
+ });
+ }));
+ });
+}
+
+// Enabling an add-on should work.
+function run_test_2() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ a1.userDisabled = false;
+ do_check_eq(a1.aboutURL, null);
+ do_check_eq(a1.optionsURL, null);
+ do_check_eq(a1.iconURL, gIconURL);
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_true(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(newa1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(newa1.iconURL, "chrome://foo/content/icon.png");
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_3();
+ });
+ }));
+ });
+}
+
+// Disabling then enabling without restart should fire onOperationCancelled.
+function run_test_3() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ a1.userDisabled = true;
+ ensure_test_completed();
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.userDisabled = false;
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_true(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(newa1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(newa1.iconURL, "chrome://foo/content/icon.png");
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js b/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
new file mode 100644
index 000000000..9f5bfacca
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
@@ -0,0 +1,262 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons distributed with the application get installed
+// correctly
+
+// Allow distributed add-ons to install
+Services.prefs.setBoolPref("extensions.installDistroAddons", true);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const distroDir = gProfD.clone();
+distroDir.append("distribution");
+distroDir.append("extensions");
+registerDirectory("XREAppDist", distroDir.parent);
+
+var addon1_1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test version 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }]
+};
+
+var addon1_2 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test version 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }]
+};
+
+var addon1_3 = {
+ id: "addon1@tests.mozilla.org",
+ version: "3.0",
+ name: "Test version 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }]
+};
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function getInstalledVersion() {
+ return Services.prefs.getIntPref("bootstraptest.installed_version");
+}
+
+function setOldModificationTime() {
+ // Make sure the installed extension has an old modification time so any
+ // changes will be detected
+ shutdownManager()
+ let extension = gProfD.clone();
+ extension.append("extensions");
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack"))
+ extension.append("addon1@tests.mozilla.org");
+ else
+ extension.append("addon1@tests.mozilla.org.xpi");
+ setExtensionModifiedTime(extension, Date.now - 10000);
+ startupManager(false);
+}
+
+function run_test() {
+ do_test_pending();
+
+ run_test_1();
+}
+
+// Tests that on the first startup the add-on gets installed
+function run_test_1() {
+ writeInstallRDFForExtension(addon1_1, distroDir);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_false(a1.foreignInstall);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests that starting with a newer version in the distribution dir doesn't
+// install it yet
+function run_test_2() {
+ setOldModificationTime();
+
+ writeInstallRDFForExtension(addon1_2, distroDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that an app upgrade installs the newer version
+function run_test_3() {
+ restartManager("2");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_false(a1.foreignInstall);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Test that an app upgrade doesn't downgrade the extension
+function run_test_4() {
+ setOldModificationTime();
+
+ writeInstallRDFForExtension(addon1_1, distroDir);
+
+ restartManager("3");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Tests that after uninstalling a restart doesn't re-install the extension
+function run_test_5() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ a1.uninstall();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+
+ do_execute_soon(run_test_6);
+ });
+ }));
+}
+
+// Tests that upgrading the application still doesn't re-install the uninstalled
+// extension
+function run_test_6() {
+ restartManager("4");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Tests that a pending install of a newer version of a distributed add-on
+// at app change still gets applied
+function run_test_7() {
+ Services.prefs.clearUserPref("extensions.installedDistroAddon.addon1@tests.mozilla.org");
+
+ installAllFiles([do_get_addon("test_distribution1_2")], function() {
+ restartManager(2);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ a1.uninstall();
+ do_execute_soon(run_test_8);
+ });
+ });
+}
+
+// Tests that a pending install of a older version of a distributed add-on
+// at app change gets replaced by the distributed version
+function run_test_8() {
+ restartManager();
+
+ writeInstallRDFForExtension(addon1_3, distroDir);
+
+ installAllFiles([do_get_addon("test_distribution1_2")], function() {
+ restartManager(3);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "3.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ a1.uninstall();
+ do_execute_soon(run_test_9);
+ });
+ });
+}
+
+// Tests that bootstrapped add-ons distributed start up correctly, also that
+// add-ons with multiple directories get copied fully
+function run_test_9() {
+ restartManager();
+
+ // Copy the test add-on to the distro dir
+ let addon = do_get_file("data/test_distribution2_2");
+ addon.copyTo(distroDir, "addon2@tests.mozilla.org");
+
+ restartManager("5");
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+
+ do_check_true(a2.hasResource("bootstrap.js"));
+ do_check_true(a2.hasResource("subdir/dummy.txt"));
+ do_check_true(a2.hasResource("subdir/subdir2/dummy2.txt"));
+
+ // Currently installs are unpacked if the source is a directory regardless
+ // of the install.rdf property or the global preference
+
+ let addonDir = profileDir.clone();
+ addonDir.append("addon2@tests.mozilla.org");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isDirectory());
+ addonDir.append("subdir");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isDirectory());
+ addonDir.append("subdir2");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isDirectory());
+ addonDir.append("dummy2.txt");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isFile());
+
+ a2.uninstall();
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_dss.js b/toolkit/mozapps/extensions/test/xpcshell/test_dss.js
new file mode 100644
index 000000000..7b171212a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_dss.js
@@ -0,0 +1,824 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// using a dynamic port in the addon metadata
+Components.utils.import("resource://testing-common/httpd.js");
+let gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+// This verifies that themes behave as expected
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+const PREF_EXTENSIONS_DSS_ENABLED = "extensions.dss.enabled";
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Observer to ensure a "lightweight-theme-styling-update" notification is sent
+// when expected
+var gLWThemeChanged = false;
+var LightweightThemeObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != "lightweight-theme-styling-update")
+ return;
+
+ gLWThemeChanged = true;
+ }
+};
+
+AM_Cc["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .addObserver(LightweightThemeObserver, "lightweight-theme-styling-update", false);
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+ Services.prefs.setBoolPref(PREF_EXTENSIONS_DSS_ENABLED, true);
+ writeInstallRDFForExtension({
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ // We need a default theme for some of these things to work but we have hidden
+ // the one in the application directory.
+ writeInstallRDFForExtension({
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+ do_check_eq(t1.screenshots, null);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_eq(t2.screenshots, null);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Checks enabling one theme disables the others
+function run_test_1() {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(t1.userDisabled);
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(check_test_1);
+ });
+}
+
+function check_test_1() {
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme2/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(t2, null);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_true(t2.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Removing the active theme should fall back to the default (not ideal in this
+// case since we don't have the default theme installed)
+function run_test_2() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("theme2@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(t2, null);
+ do_check_false(isThemeInAddonsList(profileDir, "theme2@tests.mozilla.org"));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Installing a lightweight theme should happen instantly and disable the default theme
+function run_test_3() {
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(null, p1);
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.type, "theme");
+ do_check_eq(p1.description, "A test theme");
+ do_check_eq(p1.creator, "Mozilla");
+ do_check_eq(p1.homepageURL, "http://localhost:" + gPort + "/data/index.html");
+ do_check_eq(p1.iconURL, "http://localhost:" + gPort + "/data/icon.png");
+ do_check_eq(p1.screenshots.length, 1);
+ do_check_eq(p1.screenshots[0], "http://localhost:" + gPort + "/data/preview.png");
+ do_check_false(p1.appDisabled);
+ do_check_false(p1.userDisabled);
+ do_check_true(p1.isCompatible);
+ do_check_true(p1.providesUpdatesSecurely);
+ do_check_eq(p1.blocklistState, 0);
+ do_check_true(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+ do_check_eq(p1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true("isCompatibleWith" in p1);
+ do_check_true("findUpdates" in p1);
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "1@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_4);
+ });
+ });
+}
+
+// Installing a second lightweight theme should disable the first with no restart
+function run_test_4() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ],
+ "2@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "2",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A second test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByIDs(["1@personas.mozilla.org",
+ "2@personas.mozilla.org"], function([p1, p2]) {
+ do_check_neq(null, p2);
+ do_check_false(p2.appDisabled);
+ do_check_false(p2.userDisabled);
+ do_check_true(p2.isActive);
+ do_check_eq(p2.pendingOperations, 0);
+ do_check_eq(p2.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+
+ do_check_neq(null, p1);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.userDisabled);
+ do_check_false(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_ENABLE);
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "2@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Switching to a custom theme should disable the lightweight theme and require
+// a restart. Cancelling that should also be possible.
+function run_test_5() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onOperationCancelled", true]
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_false(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+ do_check_true(hasFlag(AddonManager.PERM_CAN_ENABLE, p2.permissions));
+ do_check_true(gLWThemeChanged);
+
+ do_execute_soon(check_test_5);
+ });
+}
+
+function check_test_5() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_false(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Switching from a custom theme to a lightweight theme should require a restart
+function run_test_6() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onOperationCancelled",
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_false(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(check_test_6);
+ });
+}
+
+function check_test_6() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_false(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Uninstalling a lightweight theme should not require a restart
+function run_test_7() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ p1.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 1);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Uninstalling a lightweight theme in use should not require a restart and it
+// should reactivate the default theme
+// Also, uninstalling a lightweight theme in use should send a
+// "lightweight-theme-styling-update" notification through the observer service
+function run_test_8() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("2@personas.mozilla.org", function(p2) {
+ p2.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 0);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Uninstalling a theme not in use should not require a restart
+function run_test_9() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ t1.uninstall();
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(newt1) {
+ do_check_eq(newt1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_10);
+ });
+ });
+}
+
+// Uninstalling a custom theme in use should require a restart
+function run_test_10() {
+ AddonManager.getAddonByID("theme2@tests.mozilla.org", callback_soon(function(oldt2) {
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ oldt2.userDisabled = false;
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([d, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ t2.uninstall();
+
+ ensure_test_completed();
+ do_check_false(gLWThemeChanged);
+
+ restartManager();
+
+ do_execute_soon(run_test_11);
+ }));
+ }));
+}
+
+// Installing a custom theme not in use should not require a restart
+function run_test_11() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_11));
+ install.install();
+ });
+}
+
+function check_test_11() {
+ restartManager();
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ var previewSpec = do_get_addon_root_uri(profileDir, "theme1@tests.mozilla.org") + "preview.png";
+ do_check_eq(t1.screenshots.length, 1);
+ do_check_eq(t1.screenshots[0], previewSpec);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Updating a custom theme not in use should not require a restart
+function run_test_12() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_12);
+ install.install();
+ });
+}
+
+function check_test_12() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+// Updating a custom theme in use should require a restart
+function run_test_13() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ t1.userDisabled = false;
+ ensure_test_completed();
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onInstalling",
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_13));
+ install.install();
+ });
+ }));
+}
+
+function check_test_13() {
+ restartManager();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(gLWThemeChanged);
+ t1.uninstall();
+
+ do_execute_soon(run_test_14);
+ });
+}
+
+// Switching from a lightweight theme to the default theme should not require
+// a restart
+function run_test_14() {
+ restartManager();
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png"
+ };
+
+ AddonManager.getAddonByID("default@tests.mozilla.org", function(d) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.isActive);
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ d.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js b/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
new file mode 100644
index 000000000..bad560306
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const Ci = Components.interfaces;
+
+// This verifies that duplicate plugins are coalesced and maintain their ID
+// across restarts.
+
+var PLUGINS = [{
+ name: "Duplicate Plugin 1",
+ description: "A duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "/home/mozilla/.plugins/dupplugin1.so"
+}, {
+ name: "Duplicate Plugin 1",
+ description: "A duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "",
+ filename: "/usr/lib/plugins/dupplugin1.so"
+}, {
+ name: "Duplicate Plugin 2",
+ description: "Another duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "/home/mozilla/.plugins/dupplugin2.so"
+}, {
+ name: "Duplicate Plugin 2",
+ description: "Another duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "",
+ filename: "/usr/lib/plugins/dupplugin2.so"
+}, {
+ name: "Non-duplicate Plugin", // 3
+ description: "Not a duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "/home/mozilla/.plugins/dupplugin3.so"
+}, {
+ name: "Non-duplicate Plugin", // 4
+ description: "Not a duplicate because the descriptions are different",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "",
+ filename: "/usr/lib/plugins/dupplugin4.so"
+}, {
+ name: "Another Non-duplicate Plugin", // 5
+ description: "Not a duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() this.enabledState == Ci.nsIPluginTag.STATE_DISABLED,
+ filename: "/home/mozilla/.plugins/dupplugin5.so"
+}];
+
+// A fake plugin host to return the plugins defined above
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIPluginHost)
+ || iid.equals(Components.interfaces.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var PluginHostFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return PluginHost.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{721c3e73-969e-474b-a6dc-059fd288c428}"),
+ "Fake Plugin Host",
+ "@mozilla.org/plugin/host;1", PluginHostFactory);
+
+var gPluginIDs = [null, null, null, null, null];
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref("media.gmp-provider.enabled", false);
+
+ startupManager();
+
+ run_test_1();
+}
+
+function found_plugin(aNum, aId) {
+ if (gPluginIDs[aNum])
+ do_throw("Found duplicate of plugin " + aNum);
+ gPluginIDs[aNum] = aId;
+}
+
+// Test that the plugins were coalesced and all appear in the returned list
+function run_test_1() {
+ AddonManager.getAddonsByTypes(["plugin"], function(aAddons) {
+ do_check_eq(aAddons.length, 5);
+ aAddons.forEach(function(aAddon) {
+ if (aAddon.name == "Duplicate Plugin 1") {
+ found_plugin(0, aAddon.id);
+ do_check_eq(aAddon.description, "A duplicate plugin");
+ }
+ else if (aAddon.name == "Duplicate Plugin 2") {
+ found_plugin(1, aAddon.id);
+ do_check_eq(aAddon.description, "Another duplicate plugin");
+ }
+ else if (aAddon.name == "Another Non-duplicate Plugin") {
+ found_plugin(5, aAddon.id);
+ do_check_eq(aAddon.description, "Not a duplicate plugin");
+ }
+ else if (aAddon.name == "Non-duplicate Plugin") {
+ if (aAddon.description == "Not a duplicate plugin")
+ found_plugin(3, aAddon.id);
+ else if (aAddon.description == "Not a duplicate because the descriptions are different")
+ found_plugin(4, aAddon.id);
+ else
+ do_throw("Found unexpected plugin with description " + aAddon.description);
+ }
+ else {
+ do_throw("Found unexpected plugin " + aAddon.name);
+ }
+ });
+
+ run_test_2();
+ });
+}
+
+// Test that disabling a coalesced plugin disables all its tags
+function run_test_2() {
+ AddonManager.getAddonByID(gPluginIDs[0], function(p) {
+ do_check_false(p.userDisabled);
+ p.userDisabled = true;
+ do_check_true(PLUGINS[0].disabled);
+ do_check_true(PLUGINS[1].disabled);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that IDs persist across restart
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID(gPluginIDs[0], callback_soon(function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Duplicate Plugin 1");
+ do_check_eq(p.description, "A duplicate plugin");
+
+ // Reorder the plugins and restart again
+ [PLUGINS[0], PLUGINS[1]] = [PLUGINS[1], PLUGINS[0]];
+ restartManager();
+
+ AddonManager.getAddonByID(gPluginIDs[0], function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Duplicate Plugin 1");
+ do_check_eq(p.description, "A duplicate plugin");
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_error.js b/toolkit/mozapps/extensions/test/xpcshell/test_error.js
new file mode 100644
index 000000000..2184399e2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_error.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that various error conditions are handled correctly
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+// Checks that a local file validates ok
+function run_test_1() {
+ AddonManager.getInstallForFile(do_get_file("data/unsigned.xpi"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.error, 0);
+
+ install.cancel();
+
+ run_test_2();
+ });
+}
+
+// Checks that a corrupt file shows an error
+function run_test_2() {
+ AddonManager.getInstallForFile(do_get_file("data/corrupt.xpi"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+
+ run_test_3();
+ });
+}
+
+// Checks that an empty file shows an error
+function run_test_3() {
+ AddonManager.getInstallForFile(do_get_file("data/empty.xpi"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+
+ run_test_4();
+ });
+}
+
+// Checks that a file that doesn't match its hash shows an error
+function run_test_4() {
+ let url = Services.io.newFileURI(do_get_file("data/unsigned.xpi")).spec;
+ AddonManager.getInstallForURL(url, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_INCORRECT_HASH);
+
+ run_test_5();
+ }, "application/x-xpinstall", "sha1:foo");
+}
+
+// Checks that a file that doesn't exist shows an error
+function run_test_4() {
+ let file = do_get_file("data");
+ file.append("missing.xpi");
+ AddonManager.getInstallForFile(file, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_NETWORK_FAILURE);
+
+ run_test_5();
+ });
+}
+
+// Checks that an add-on with an illegal ID shows an error
+function run_test_5() {
+ AddonManager.getInstallForFile(do_get_addon("test_bug567173"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_experiment.js b/toolkit/mozapps/extensions/test/xpcshell/test_experiment.js
new file mode 100644
index 000000000..25172749d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_experiment.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let scope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+const XPIProvider = scope.XPIProvider;
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ run_next_test();
+}
+
+add_test(function test_experiment() {
+ AddonManager.getInstallForFile(do_get_addon("test_experiment1"), (install) => {
+ completeAllInstalls([install], () => {
+ AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
+ Assert.ok(addon, "Addon is found.");
+
+ Assert.ok(addon.userDisabled, "Experiments are userDisabled by default.");
+ Assert.equal(addon.isActive, false, "Add-on is not active.");
+ Assert.equal(addon.updateURL, null, "No updateURL for experiments.");
+ Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
+ "Background updates are disabled.");
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UNINSTALL,
+ "Permissions are minimal.");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_ENABLE),
+ "Should not be pending enable");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_DISABLE),
+ "Should not be pending disable");
+
+ // Setting applyBackgroundUpdates should not work.
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
+ "Setting applyBackgroundUpdates shouldn't do anything.");
+
+ let noCompatibleCalled = false;
+ let noUpdateCalled = false;
+ let finishedCalled = false;
+
+ let listener = {
+ onNoCompatibilityUpdateAvailable: () => { noCompatibleCalled = true; },
+ onNoUpdateAvailable: () => { noUpdateCalled = true; },
+ onUpdateFinished: () => { finishedCalled = true; },
+ };
+
+ addon.findUpdates(listener, "testing", null, null);
+ Assert.ok(noCompatibleCalled, "Listener called.");
+ Assert.ok(noUpdateCalled, "Listener called.");
+ Assert.ok(finishedCalled, "Listener called.");
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+// Changes to userDisabled should not be persisted to the database.
+add_test(function test_userDisabledNotPersisted() {
+ AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
+ Assert.ok(addon, "Add-on is found.");
+ Assert.ok(addon.userDisabled, "Add-on is user disabled.");
+
+ let listener = {
+ onEnabled: (addon2) => {
+ AddonManager.removeAddonListener(listener);
+
+ Assert.equal(addon2.id, addon.id, "Changed add-on matches expected.");
+ Assert.equal(addon2.userDisabled, false, "Add-on is no longer user disabled.");
+ Assert.ok(addon2.isActive, "Add-on is active.");
+
+ Assert.ok("experiment1@tests.mozilla.org" in XPIProvider.bootstrappedAddons,
+ "Experiment add-on listed in XPIProvider bootstrapped list.");
+
+ AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
+ Assert.ok(addon, "Add-on retrieved.");
+ Assert.equal(addon.userDisabled, false, "Add-on is still enabled after API retrieve.");
+ Assert.ok(addon.isActive, "Add-on is still active.");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_ENABLE),
+ "Should not be pending enable");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_DISABLE),
+ "Should not be pending disable");
+
+ // Now when we restart the manager the add-on should revert state.
+ restartManager();
+ let persisted = JSON.parse(Services.prefs.getCharPref("extensions.bootstrappedAddons"));
+ Assert.ok(!("experiment1@tests.mozilla.org" in persisted),
+ "Experiment add-on not persisted to bootstrappedAddons.");
+
+ AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
+ Assert.ok(addon, "Add-on retrieved.");
+ Assert.ok(addon.userDisabled, "Add-on is disabled after restart.");
+ Assert.equal(addon.isActive, false, "Add-on is not active after restart.");
+
+ run_next_test();
+ });
+ });
+ },
+ };
+
+ AddonManager.addAddonListener(listener);
+ addon.userDisabled = false;
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_filepointer.js b/toolkit/mozapps/extensions/test/xpcshell/test_filepointer.js
new file mode 100644
index 000000000..cb661e495
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_filepointer.js
@@ -0,0 +1,403 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that various operations with file pointers work and do not affect the
+// source files
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon1_2 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+profileDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+
+const sourceDir = gProfD.clone();
+sourceDir.append("source");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+function writePointer(aId, aName) {
+ let file = profileDir.clone();
+ file.append(aName ? aName : aId);
+
+ let target = sourceDir.clone();
+ target.append(do_get_expected_addon_name(aId));
+
+ var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fos.init(file,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ fos.write(target.path, target.path.length);
+ fos.close();
+}
+
+function writeRelativePointer(aId, aName) {
+ let file = profileDir.clone();
+ file.append(aName ? aName : aId);
+
+ let absTarget = sourceDir.clone();
+ absTarget.append(do_get_expected_addon_name(aId));
+
+ var relTarget = absTarget.getRelativeDescriptor(profileDir);
+
+ var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fos.init(file,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ fos.write(relTarget, relTarget.length);
+ fos.close();
+}
+
+function run_test() {
+ // pointer files only work with unpacked directories
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack") == false)
+ return;
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Tests that installing a new add-on by pointer works
+function run_test_1() {
+ writeInstallRDFForExtension(addon1, sourceDir);
+ writePointer(addon1.id);
+
+ startupManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let file = a1.getResourceURI().QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_eq(file.parent.path, sourceDir.path);
+
+ let rootUri = do_get_addon_root_uri(sourceDir, addon1.id);
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+ uri = a1.getResourceURI("install.rdf");
+ do_check_eq(uri.spec, rootUri + "install.rdf");
+
+ // Check that upgrade is disabled for addons installed by file-pointers.
+ do_check_eq(a1.permissions & AddonManager.PERM_CAN_UPGRADE, 0);
+ run_test_2();
+ });
+}
+
+// Tests that installing the addon from some other source doesn't clobber
+// the original sources
+function run_test_2() {
+ prepare_test({}, [
+ "onNewInstall",
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_filepointer.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test_2));
+
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+
+ let file = a1.getResourceURI().QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_eq(file.parent.path, profileDir.path);
+
+ let rootUri = do_get_addon_root_uri(profileDir, addon1.id);
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+ uri = a1.getResourceURI("install.rdf");
+ do_check_eq(uri.spec, rootUri + "install.rdf");
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ a1.uninstall();
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Tests that uninstalling doesn't clobber the original sources
+function run_test_3() {
+ restartManager();
+
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ a1.uninstall();
+
+ restartManager();
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ do_execute_soon(run_test_4);
+ }));
+}
+
+// Tests that misnaming a pointer doesn't clobber the sources
+function run_test_4() {
+ writePointer("addon2@tests.mozilla.org", addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ let pointer = profileDir.clone();
+ pointer.append("addon2@tests.mozilla.org");
+ do_check_false(pointer.exists());
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Tests that changing the ID of an existing add-on doesn't clobber the sources
+function run_test_5() {
+ var dest = writeInstallRDFForExtension(addon1, sourceDir);
+ // Make sure the modification time changes enough to be detected.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ writeInstallRDFForExtension(addon2, sourceDir, addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ do_check_false(pointer.exists());
+
+ do_execute_soon(run_test_6);
+ });
+ }));
+}
+
+// Removing the pointer file should uninstall the add-on
+function run_test_6() {
+ var dest = writeInstallRDFForExtension(addon1, sourceDir);
+ // Make sure the modification time changes enough to be detected in run_test_8.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ pointer.remove(false);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+
+ do_execute_soon(run_test_7);
+ });
+ }));
+}
+
+// Removing the pointer file and replacing it with a directory should work
+function run_test_7() {
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ pointer.remove(false);
+
+ writeInstallRDFForExtension(addon1_2, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+
+ a1.uninstall();
+
+ do_execute_soon(run_test_8);
+ });
+ }));
+}
+
+// Changes to the source files should be detected
+function run_test_8() {
+ restartManager();
+
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ writeInstallRDFForExtension(addon1_2, sourceDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+
+ a1.uninstall();
+
+ do_execute_soon(run_test_9);
+ });
+ }));
+}
+
+// Removing the add-on the pointer file points at should uninstall the add-on
+function run_test_9() {
+ restartManager();
+
+ var dest = writeInstallRDFForExtension(addon1, sourceDir);
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ dest.remove(true);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_eq(a1, null);
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ do_check_false(pointer.exists());
+
+ do_execute_soon(run_test_10);
+ });
+ }));
+}
+
+// Tests that installing a new add-on by pointer with a relative path works
+function run_test_10() {
+ writeInstallRDFForExtension(addon1, sourceDir);
+ writeRelativePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let file = a1.getResourceURI().QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_eq(file.parent.path, sourceDir.path);
+
+ let rootUri = do_get_addon_root_uri(sourceDir, addon1.id);
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+ uri = a1.getResourceURI("install.rdf");
+ do_check_eq(uri.spec, rootUri + "install.rdf");
+
+ // Check that upgrade is disabled for addons installed by file-pointers.
+ do_check_eq(a1.permissions & AddonManager.PERM_CAN_UPGRADE, 0);
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_fuel.js b/toolkit/mozapps/extensions/test/xpcshell/test_fuel.js
new file mode 100644
index 000000000..800933220
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_fuel.js
@@ -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/.
+ */
+
+// This just verifies that FUEL integrates to the add-ons manager
+
+var testdata = {
+ dummyid: "fuel-dummy-extension@mozilla.org",
+ dummyname: "Dummy Extension",
+ inspectorid: "addon1@tests.mozilla.org",
+ inspectorname: "Test Addon",
+ missing: "fuel.fuel-test-missing",
+ dummy: "fuel.fuel-test"
+};
+
+var Application = null
+
+function run_test() {
+ var cm = AM_Cc["@mozilla.org/categorymanager;1"].
+ getService(AM_Ci.nsICategoryManager);
+
+ try {
+ var contract = cm.getCategoryEntry("JavaScript-global-privileged-property",
+ "Application");
+ Application = AM_Cc[contract].getService(AM_Ci.extIApplication);
+ }
+ catch (e) {
+ // This application does not include a FUEL variant.
+ return;
+ }
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ const profileDir = gProfD.clone();
+ profileDir.append("extensions");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test Addon",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ }, profileDir);
+
+ startupManager();
+
+ Application.getExtensions(function(extensions) {
+ // test to see if the extensions object is available
+ do_check_neq(extensions, null);
+
+ // test to see if a non-existant extension exists
+ do_check_true(!extensions.has(testdata.dummyid));
+
+ // test to see if an extension exists
+ do_check_true(extensions.has(testdata.inspectorid));
+
+ var inspector = extensions.get(testdata.inspectorid);
+ do_check_eq(inspector.id, testdata.inspectorid);
+ do_check_eq(inspector.name, testdata.inspectorname);
+ do_check_eq(inspector.version, "1.0");
+ do_check_true(inspector.firstRun, true);
+ do_check_true(inspector.enabled);
+
+ // test to see if extension find works
+ do_check_eq(extensions.all.length, 1);
+ // STORAGE TESTING
+ // Make sure the we are given the same extension (cached) so things like .storage work right
+ inspector.storage.set("test", "simple check");
+ do_check_true(inspector.storage.has("test"));
+
+ var inspector2 = extensions.get(testdata.inspectorid);
+ do_check_eq(inspector2.id, testdata.inspectorid);
+ do_check_true(inspector.storage.has("test"));
+ do_check_eq(inspector2.storage.get("test", "cache"), inspector.storage.get("test", "original"));
+
+ inspector.events.addListener("disable", onGenericEvent);
+ inspector.events.addListener("enable", onGenericEvent);
+ inspector.events.addListener("uninstall", onGenericEvent);
+ inspector.events.addListener("cancel", onGenericEvent);
+
+ AddonManager.getAddonByID(testdata.inspectorid, function(a) {
+ a.userDisabled = true;
+
+ do_check_eq(gLastEvent, "disable");
+
+ // enabling after a disable will only fire a 'cancel' event
+ // see - http://mxr.mozilla.org/seamonkey/source/toolkit/mozapps/extensions/src/nsExtensionManager.js.in#5216
+ a.userDisabled = false;
+ do_check_eq(gLastEvent, "cancel");
+
+ a.uninstall();
+ do_check_eq(gLastEvent, "uninstall");
+
+ a.cancelUninstall();
+ do_check_eq(gLastEvent, "cancel");
+
+ // PREF TESTING
+ // Reset the install event preference, so that we can test it again later
+ //inspector.prefs.get("install-event-fired").reset();
+
+ // test the value of the preference root
+ do_check_eq(extensions.all[0].prefs.root, "extensions.addon1@tests.mozilla.org.");
+
+ // test getting nonexistent values
+ var itemValue = inspector.prefs.getValue(testdata.missing, "default");
+ do_check_eq(itemValue, "default");
+
+ do_check_eq(inspector.prefs.get(testdata.missing), null);
+
+ // test setting and getting a value
+ inspector.prefs.setValue(testdata.dummy, "dummy");
+ itemValue = inspector.prefs.getValue(testdata.dummy, "default");
+ do_check_eq(itemValue, "dummy");
+
+ // test for overwriting an existing value
+ inspector.prefs.setValue(testdata.dummy, "smarty");
+ itemValue = inspector.prefs.getValue(testdata.dummy, "default");
+ do_check_eq(itemValue, "smarty");
+
+ // test setting and getting a value
+ inspector.prefs.get(testdata.dummy).value = "dummy2";
+ itemValue = inspector.prefs.get(testdata.dummy).value;
+ do_check_eq(itemValue, "dummy2");
+
+ // test resetting a pref [since there is no default value, the pref should disappear]
+ inspector.prefs.get(testdata.dummy).reset();
+ var itemValue = inspector.prefs.getValue(testdata.dummy, "default");
+ do_check_eq(itemValue, "default");
+
+ // test to see if a non-existant property exists
+ do_check_true(!inspector.prefs.has(testdata.dummy));
+
+ inspector.prefs.events.addListener("change", onPrefChange);
+ inspector.prefs.setValue("fuel.fuel-test", "change event");
+ });
+ });
+}
+
+function onGenericEvent(event) {
+ gLastEvent = event.type;
+}
+
+function onPrefChange(evt) {
+ Application.getExtensions(function(extensions) {
+ var inspector3 = extensions.get(testdata.inspectorid);
+
+ do_check_eq(evt.data, testdata.dummy);
+ inspector3.prefs.events.removeListener("change", onPrefChange);
+
+ inspector3.prefs.get("fuel.fuel-test").events.addListener("change", onPrefChange2);
+ inspector3.prefs.setValue("fuel.fuel-test", "change event2");
+ });
+}
+
+function onPrefChange2(evt) {
+ do_check_eq(evt.data, testdata.dummy);
+
+ do_execute_soon(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_general.js b/toolkit/mozapps/extensions/test/xpcshell/test_general.js
new file mode 100644
index 000000000..e69b13314
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_general.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This just verifies that the EM can actually startup and shutdown a few times
+// without any errors
+
+// We have to look up how many add-ons are present since there will be plugins
+// etc. detected
+var gCount;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ var count = 0;
+ startupManager();
+ AddonManager.getAddonsByTypes(null, function(list) {
+ gCount = list.length;
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByTypes(null, function(addons) {
+ do_check_eq(gCount, addons.length);
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(pendingAddons) {
+ do_check_eq(0, pendingAddons.length);
+
+ do_execute_soon(run_test_2);
+ });
+ });
+}
+
+function run_test_2() {
+ shutdownManager();
+
+ startupManager(false);
+
+ AddonManager.getAddonsByTypes(null, function(addons) {
+ do_check_eq(gCount, addons.length);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonsByTypes(null, callback_soon(function(addons) {
+ do_check_eq(gCount, addons.length);
+ do_test_finished();
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_getresource.js b/toolkit/mozapps/extensions/test/xpcshell/test_getresource.js
new file mode 100644
index 000000000..4dce15aec
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_getresource.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// install.rdf size, icon.png size, subfile.txt size
+const ADDON_SIZE = 672 + 15 + 26;
+
+// This verifies the functionality of getResourceURI
+// There are two cases - with a filename it returns an nsIFileURL to the filename
+// and with no parameters, it returns an nsIFileURL to the root of the addon
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ AddonManager.getInstallForFile(do_get_addon("test_getresource"), function(aInstall) {
+ do_check_true(aInstall.addon.hasResource("install.rdf"));
+ do_check_eq(aInstall.addon.getResourceURI().spec, aInstall.sourceURI.spec);
+
+ do_check_true(aInstall.addon.hasResource("icon.png"));
+ do_check_eq(aInstall.addon.getResourceURI("icon.png").spec,
+ "jar:" + aInstall.sourceURI.spec + "!/icon.png");
+
+ do_check_false(aInstall.addon.hasResource("missing.txt"));
+
+ do_check_true(aInstall.addon.hasResource("subdir/subfile.txt"));
+ do_check_eq(aInstall.addon.getResourceURI("subdir/subfile.txt").spec,
+ "jar:" + aInstall.sourceURI.spec + "!/subdir/subfile.txt");
+
+ do_check_false(aInstall.addon.hasResource("subdir/missing.txt"));
+
+ do_check_eq(aInstall.addon.size, ADDON_SIZE);
+
+ completeAllInstalls([aInstall], function() {
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+
+ let addonDir = gProfD.clone();
+ addonDir.append("extensions");
+ let rootUri = do_get_addon_root_uri(addonDir, "addon1@tests.mozilla.org");
+
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+
+ let file = rootUri + "install.rdf";
+ do_check_true(a1.hasResource("install.rdf"));
+ uri = a1.getResourceURI("install.rdf")
+ do_check_eq(uri.spec, file);
+
+ file = rootUri + "icon.png";
+ do_check_true(a1.hasResource("icon.png"));
+ uri = a1.getResourceURI("icon.png")
+ do_check_eq(uri.spec, file);
+
+ do_check_false(a1.hasResource("missing.txt"));
+
+ file = rootUri + "subdir/subfile.txt";
+ do_check_true(a1.hasResource("subdir/subfile.txt"));
+ uri = a1.getResourceURI("subdir/subfile.txt")
+ do_check_eq(uri.spec, file);
+
+ do_check_false(a1.hasResource("subdir/missing.txt"));
+
+ do_check_eq(a1.size, ADDON_SIZE);
+
+ a1.uninstall();
+
+ try {
+ // hasResource should never throw an exception.
+ do_check_false(a1.hasResource("icon.png"));
+ } catch (e) {
+ do_check_true(false);
+ }
+
+ AddonManager.getInstallForFile(do_get_addon("test_getresource"),
+ callback_soon(function(aInstall) {
+ do_check_false(a1.hasResource("icon.png"));
+ do_check_true(aInstall.addon.hasResource("icon.png"));
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_eq(newa1, null);
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Device.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Device.js
new file mode 100644
index 000000000..5f781edf4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Device.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which differs only on device ID, but otherwise
+// exactly matches the blacklist entry, is not blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x9876");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x9876");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x9876");
+ gfxInfo.spoofOSVersion(0x1050);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("aabb");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_DriverNew.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_DriverNew.js
new file mode 100644
index 000000000..802fe11e2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_DriverNew.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a new-enough driver bypasses the blacklist, even if the rest of
+// the attributes match the blacklist entry.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2202");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("wxyz");
+ gfxInfo.spoofDriverVersion("6");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js
new file mode 100644
index 000000000..08e87c38c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which is newer than the equal
+// blacklist entry is allowed.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcdc");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.1112");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("dcdc");
+ gfxInfo.spoofDeviceID("uiop");
+ gfxInfo.spoofDriverVersion("6");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js
new file mode 100644
index 000000000..73253c89b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which is older than the equal
+// blacklist entry is correctly allowed.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcdc");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.1110");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("dcdc");
+ gfxInfo.spoofDeviceID("uiop");
+ gfxInfo.spoofDriverVersion("4");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js
new file mode 100644
index 000000000..2dde268a3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which exactly matches the equal
+// blacklist entry is successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcdc");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.1111");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("dcdc");
+ gfxInfo.spoofDeviceID("uiop");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js
new file mode 100644
index 000000000..fd3145f5a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which is lower than the greater-than-or-equal
+// blacklist entry is allowed.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabab");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("abab");
+ gfxInfo.spoofDeviceID("ghjk");
+ gfxInfo.spoofDriverVersion("6");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js
new file mode 100644
index 000000000..0c2c65572
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which exactly matches the greater-than-or-equal
+// blacklist entry is successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabab");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2202");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("abab");
+ gfxInfo.spoofDeviceID("ghjk");
+ gfxInfo.spoofDriverVersion("7");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js
new file mode 100644
index 000000000..61372cff8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which exactly matches the blacklist entry is
+// successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x6666");
+
+ // Spoof the OS version so it matches the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ break;
+ case "Darwin":
+ gfxInfo.spoofOSVersion(0x1050);
+ break;
+ case "Android":
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OK.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OK.js
new file mode 100644
index 000000000..dcd578dfb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OK.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which exactly matches the blacklist entry is
+// successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1050);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OS.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OS.js
new file mode 100644
index 000000000..81edc9cd1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OS.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which differs only on OS version, but otherwise
+// exactly matches the blacklist entry, is not blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows Vista
+ gfxInfo.spoofOSVersion(0x60000);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ // Snow Leopard
+ gfxInfo.spoofOSVersion(0x1060);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js
new file mode 100644
index 000000000..3472c6d7e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether new OS versions are matched properly.
+// Uses test_gfxBlacklist_OS.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+
+ // Spoof the version of the OS appropriately to test the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 8
+ gfxInfo.spoofOSVersion(0x60002);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ // Mountain Lion
+ gfxInfo.spoofOSVersion(0x1080);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ if (get_platform() == "WINNT") {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ } else if (get_platform() == "Darwin") {
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ }
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_OS.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js
new file mode 100644
index 000000000..fb25b9509
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether blocklists specifying new OSeswcorrectly don't block if driver
+// versions are appropriately up-to-date.
+// Uses test_gfxBlacklist_OS.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ gfxInfo.spoofDriverVersion("8.52.322.2202");
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+
+ // Spoof the version of the OS appropriately to test the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 8
+ gfxInfo.spoofOSVersion(0x60002);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ // Mountain Lion
+ gfxInfo.spoofOSVersion(0x1080);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ if (get_platform() == "WINNT") {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ } else if (get_platform() == "Darwin") {
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ }
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_OS.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js
new file mode 100644
index 000000000..4fb162262
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether old OS versions are not matched when the blacklist contains
+// only new OS versions.
+// Uses test_gfxBlacklist_OS.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+
+ // Spoof the version of the OS appropriately to test the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ // Lion
+ gfxInfo.spoofOSVersion(0x1070);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ if (get_platform() == "WINNT") {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ } else if (get_platform() == "Darwin") {
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ }
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_OS.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Vendor.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Vendor.js
new file mode 100644
index 000000000..a5d1c71cf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Vendor.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether a machine which differs only on vendor, but otherwise
+// exactly matches the blacklist entry, is not blocked.
+// Uses test_gfxBlacklist.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcba");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xdcba");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xdcba");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1050);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("dcba");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_prefs.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_prefs.js
new file mode 100644
index 000000000..bb0e55e2c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_prefs.js
@@ -0,0 +1,132 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test whether the blacklist succesfully adds and removes the prefs that store
+// its decisions when the remote blacklist is changed.
+// Uses test_gfxBlacklist.xml and test_gfxBlacklist2.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1050);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function blacklistAdded(aSubject, aTopic, aData)
+ {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(ensureBlacklistSet);
+ }
+ function ensureBlacklistSet()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ do_check_eq(prefs.getIntPref("gfx.blacklist.direct2d"),
+ Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ Services.obs.removeObserver(blacklistAdded, "blocklist-data-gfxItems");
+ Services.obs.addObserver(blacklistRemoved, "blocklist-data-gfxItems", false);
+ load_blocklist("test_gfxBlacklist2.xml");
+ }
+
+ function blacklistRemoved(aSubject, aTopic, aData)
+ {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(ensureBlacklistUnset);
+ }
+ function ensureBlacklistUnset()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ var exists = false;
+ try {
+ prefs.getIntPref("gfx.blacklist.direct2d");
+ exists = true;
+ } catch(e) {}
+
+ do_check_false(exists);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(blacklistAdded, "blocklist-data-gfxItems", false);
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js b/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
new file mode 100644
index 000000000..8de3ab4a2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
@@ -0,0 +1,332 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+let GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
+ () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
+
+let gMockAddons = new Map();
+let gMockEmeAddons = new Map();
+
+for (let plugin of GMPScope.GMP_PLUGINS) {
+ let mockAddon = Object.freeze({
+ id: plugin.id,
+ isValid: true,
+ isInstalled: false,
+ nameId: plugin.name,
+ descriptionId: plugin.description,
+ });
+ gMockAddons.set(mockAddon.id, mockAddon);
+ if (mockAddon.id.indexOf("gmp-eme-") == 0) {
+ gMockEmeAddons.set(mockAddon.id, mockAddon);
+ }
+}
+
+let gInstalledAddonId = "";
+let gPrefs = Services.prefs;
+let gGetKey = GMPScope.GMPPrefs.getPrefKey;
+
+function MockGMPInstallManager() {
+}
+
+MockGMPInstallManager.prototype = {
+ checkForAddons: () => Promise.resolve([...gMockAddons.values()]),
+
+ installAddon: addon => {
+ gInstalledAddonId = addon.id;
+ return Promise.resolve();
+ },
+};
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_LOGGING_DUMP, true);
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_LOGGING_LEVEL, 0);
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
+ for (let addon of gMockAddons.values()) {
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCEVISIBLE, addon.id),
+ true);
+ }
+ GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+
+ run_next_test();
+}
+
+add_task(function* test_notInstalled() {
+ for (let addon of gMockAddons.values()) {
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), "");
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false);
+ }
+
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ Assert.ok(!addon.isInstalled);
+ Assert.equal(addon.type, "plugin");
+ Assert.equal(addon.version, "");
+
+ let mockAddon = gMockAddons.get(addon.id);
+
+ Assert.notEqual(mockAddon, null);
+ let name = pluginsBundle.GetStringFromName(mockAddon.nameId);
+ Assert.equal(addon.name, name);
+ let description = pluginsBundle.GetStringFromName(mockAddon.descriptionId);
+ Assert.equal(addon.description, description);
+
+ Assert.ok(!addon.isActive);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.userDisabled);
+
+ Assert.equal(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ Assert.equal(addon.size, 0);
+ Assert.equal(addon.scope, AddonManager.SCOPE_APPLICATION);
+ Assert.equal(addon.pendingOperations, AddonManager.PENDING_NONE);
+ Assert.equal(addon.operationsRequiringRestart, AddonManager.PENDING_NONE);
+
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
+ AddonManager.PERM_CAN_ENABLE);
+
+ Assert.equal(addon.updateDate, null);
+
+ Assert.ok(addon.isCompatible);
+ Assert.ok(addon.isPlatformCompatible);
+ Assert.ok(addon.providesUpdatesSecurely);
+ Assert.ok(!addon.foreignInstall);
+
+ let mimetypes = addon.pluginMimeTypes;
+ Assert.ok(mimetypes);
+ Assert.equal(mimetypes.length, 0);
+ let libraries = addon.pluginLibraries;
+ Assert.ok(libraries);
+ Assert.equal(libraries.length, 0);
+ Assert.equal(addon.pluginFullpath, "");
+ }
+});
+
+add_task(function* test_installed() {
+ const TEST_DATE = new Date(2013, 0, 1, 12);
+ const TEST_VERSION = "1.2.3.4";
+ const TEST_TIME_SEC = Math.round(TEST_DATE.getTime() / 1000);
+
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ let mockAddon = gMockAddons.get(addon.id);
+ Assert.notEqual(mockAddon, null);
+
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append(addon.id);
+ file.append(TEST_VERSION);
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, mockAddon.id), false);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, mockAddon.id),
+ "" + TEST_TIME_SEC);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, mockAddon.id),
+ TEST_VERSION);
+
+ Assert.ok(addon.isInstalled);
+ Assert.equal(addon.type, "plugin");
+ Assert.ok(!addon.isActive);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.userDisabled);
+
+ let name = pluginsBundle.GetStringFromName(mockAddon.nameId);
+ Assert.equal(addon.name, name);
+ Assert.equal(addon.version, TEST_VERSION);
+
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
+ AddonManager.PERM_CAN_ENABLE);
+
+ Assert.equal(addon.updateDate.getTime(), TEST_TIME_SEC * 1000);
+
+ let mimetypes = addon.pluginMimeTypes;
+ Assert.ok(mimetypes);
+ Assert.equal(mimetypes.length, 0);
+ let libraries = addon.pluginLibraries;
+ Assert.ok(libraries);
+ Assert.equal(libraries.length, 1);
+ Assert.equal(libraries[0], TEST_VERSION);
+ let fullpath = addon.pluginFullpath;
+ Assert.equal(fullpath.length, 1);
+ Assert.equal(fullpath[0], file.path);
+ }
+});
+
+add_task(function* test_enable() {
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+
+ Assert.ok(addon.isActive);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(!addon.userDisabled);
+
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
+ AddonManager.PERM_CAN_DISABLE);
+ }
+});
+
+add_task(function* test_globalEmeDisabled() {
+ let addons = yield promiseAddonsByIDs([...gMockEmeAddons.keys()]);
+ Assert.equal(addons.length, gMockEmeAddons.size);
+
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, false);
+ GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+ for (let addon of addons) {
+ Assert.ok(!addon.isActive);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.userDisabled);
+
+ Assert.equal(addon.permissions, 0);
+ }
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
+ GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+});
+
+add_task(function* test_autoUpdatePrefPersistance() {
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ let autoupdateKey = gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id);
+ gPrefs.clearUserPref(autoupdateKey);
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ Assert.ok(!gPrefs.getBoolPref(autoupdateKey));
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE);
+ Assert.ok(gPrefs.getBoolPref(autoupdateKey));
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+ Assert.ok(!gPrefs.prefHasUserValue(autoupdateKey));
+ }
+});
+
+add_task(function* test_pluginRegistration() {
+ const TEST_VERSION = "1.2.3.4";
+
+ for (let addon of gMockAddons.values()) {
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append(addon.id);
+ file.append(TEST_VERSION);
+
+ let addedPaths = [];
+ let removedPaths = [];
+ let clearPaths = () => { addedPaths = []; removedPaths = []; }
+
+ let MockGMPService = {
+ addPluginDirectory: path => addedPaths.push(path),
+ removePluginDirectory: path => removedPaths.push(path),
+ removeAndDeletePluginDirectory: path => removedPaths.push(path),
+ };
+
+ GMPScope.gmpService = MockGMPService;
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+
+ // Check that the plugin gets registered after startup.
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.notEqual(addedPaths.indexOf(file.path), -1);
+ Assert.deepEqual(removedPaths, []);
+
+ // Check that clearing the version doesn't trigger registration.
+ clearPaths();
+ gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id));
+ Assert.deepEqual(addedPaths, []);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Restarting with no version set should not trigger registration.
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.equal(addedPaths.indexOf(file.path), -1);
+ Assert.equal(removedPaths.indexOf(file.path), -1);
+
+ // Changing the pref mid-session should cause unregistration and registration.
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ const TEST_VERSION_2 = "5.6.7.8";
+ let file2 = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file2.append(addon.id);
+ file2.append(TEST_VERSION_2);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION_2);
+ Assert.deepEqual(addedPaths, [file2.path]);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Disabling the plugin should cause unregistration.
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false);
+ Assert.deepEqual(addedPaths, []);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Restarting with the plugin disabled should not cause registration.
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.equal(addedPaths.indexOf(file.path), -1);
+ Assert.equal(removedPaths.indexOf(file.path), -1);
+
+ // Re-enabling the plugin should cause registration.
+ clearPaths();
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+ Assert.deepEqual(addedPaths, [file.path]);
+ Assert.deepEqual(removedPaths, []);
+ GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+ }
+});
+
+add_task(function* test_periodicUpdate() {
+ Object.defineProperty(GMPScope, "GMPInstallManager", {
+ value: MockGMPInstallManager,
+ writable: true,
+ enumerable: true,
+ configurable: true
+ });
+
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id));
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK, 0);
+ let result =
+ yield addon.findUpdates({}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ Assert.strictEqual(result, false);
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK, Date.now() / 1000 - 60);
+ result =
+ yield addon.findUpdates({}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ Assert.strictEqual(result, false);
+
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK,
+ Date.now() / 1000 - 2 * GMPScope.SEC_IN_A_DAY);
+ gInstalledAddonId = "";
+ result =
+ yield addon.findUpdates({}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ Assert.strictEqual(result, true);
+ Assert.equal(gInstalledAddonId, addon.id);
+ }
+
+ GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_hasbinarycomponents.js b/toolkit/mozapps/extensions/test/xpcshell/test_hasbinarycomponents.js
new file mode 100644
index 000000000..598e06ed0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_hasbinarycomponents.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests detection of binary components via parsing of chrome manifests.
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_1"),
+ do_get_addon("test_chromemanifest_2"),
+ do_get_addon("test_chromemanifest_3"),
+ do_get_addon("test_chromemanifest_4"),
+ do_get_addon("test_chromemanifest_5")],
+ function() {
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ // addon1 has no binary components
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.hasBinaryComponents);
+ do_check_true(a1.isCompatible);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // addon2 has a binary component, is compatible
+ do_check_neq(a2, null);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.hasBinaryComponents);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ // addon3 has a binary component, is incompatible
+ do_check_neq(a3, null);
+ do_check_false(a3.userDisabled);
+ do_check_true(a2.hasBinaryComponents);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ // addon4 has a binary component listed in a sub-manifest, is incompatible
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_true(a2.hasBinaryComponents);
+ do_check_false(a4.isCompatible);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ // addon5 has a binary component, but is set to not unpack
+ do_check_neq(a5, null);
+ do_check_false(a5.userDisabled);
+ if (TEST_UNPACKED)
+ do_check_true(a5.hasBinaryComponents);
+ else
+ do_check_false(a5.hasBinaryComponents);
+ do_check_true(a5.isCompatible);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install.js b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
new file mode 100644
index 000000000..16db604c5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
@@ -0,0 +1,1761 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons can be installed from XPI files
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+// Maximum error in file modification times. Some file systems don't store
+// modification times exactly. As long as we are closer than this then it
+// still passes.
+const MAX_TIME_DIFFERENCE = 3000;
+
+// install.rdf size, icon.png, icon64.png size
+const ADDON1_SIZE = 705 + 16 + 16;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/httpd.js");
+
+var testserver;
+var gInstallDate;
+var gInstall = null;
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerPathHandler("/redirect", function(aRequest, aResponse) {
+ aResponse.setStatusLine(null, 301, "Moved Permanently");
+ let url = aRequest.host + ":" + aRequest.port + aRequest.queryString;
+ aResponse.setHeader("Location", "http://" + url);
+ });
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Checks that an install from a local file proceeds as expected
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_neq(install.addon.syncGUID, null);
+ do_check_eq(install.addon.install, install);
+ do_check_eq(install.addon.size, ADDON1_SIZE);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ let file = do_get_addon("test_install1");
+ let uri = Services.io.newFileURI(file).spec;
+ do_check_eq(install.addon.getResourceURI("install.rdf").spec, "jar:" + uri + "!/install.rdf");
+ do_check_eq(install.addon.iconURL, "jar:" + uri + "!/icon.png");
+ do_check_eq(install.addon.icon64URL, "jar:" + uri + "!/icon64.png");
+ do_check_eq(install.iconURL, null);
+
+ do_check_eq(install.sourceURI.spec, uri);
+ do_check_eq(install.addon.sourceURI.spec, uri);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ AddonManager.getInstallsByTypes(["foo"], function(fooInstalls) {
+ do_check_eq(fooInstalls.length, 0);
+
+ AddonManager.getInstallsByTypes(["extension"], function(extensionInstalls) {
+ do_check_eq(extensionInstalls.length, 1);
+ do_check_eq(extensionInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ check_test_1(install.addon.syncGUID);
+ });
+ install.install();
+ });
+ });
+ });
+ });
+}
+
+function check_test_1(installSyncGUID) {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(olda1) {
+ do_check_eq(olda1, null);
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(pendingAddons) {
+ do_check_eq(pendingAddons.length, 1);
+ do_check_eq(pendingAddons[0].id, "addon1@tests.mozilla.org");
+ let uri = NetUtil.newURI(pendingAddons[0].iconURL);
+ if (uri instanceof AM_Ci.nsIJARURI) {
+ let jarURI = uri.QueryInterface(AM_Ci.nsIJARURI);
+ let archiveURI = jarURI.JARFile;
+ let archiveFile = archiveURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipReader.open(archiveFile);
+ do_check_true(zipReader.hasEntry(jarURI.JAREntry));
+ }
+ finally {
+ zipReader.close();
+ }
+ }
+ else {
+ let iconFile = uri.QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_true(iconFile.exists());
+ }
+
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = pendingAddons[0].getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ // The pending add-on cannot be disabled or enabled.
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_DISABLE));
+
+ restartManager();
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls, 0);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_neq(a1.syncGUID, null);
+ do_check_true(a1.syncGUID.length >= 9);
+ do_check_eq(a1.syncGUID, installSyncGUID);
+ do_check_eq(a1.type, "extension");
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.name, "Test 1");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(do_get_addon("test_install1").exists());
+ do_check_in_crash_annotation(a1.id, a1.version);
+ do_check_eq(a1.size, ADDON1_SIZE);
+ do_check_false(a1.foreignInstall);
+
+ do_check_eq(a1.sourceURI.spec,
+ Services.io.newFileURI(do_get_addon("test_install1")).spec);
+ let difference = a1.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a1.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ do_check_true(a1.hasResource("install.rdf"));
+ do_check_false(a1.hasResource("foo.bar"));
+
+ let uri = do_get_addon_root_uri(profileDir, "addon1@tests.mozilla.org");
+ do_check_eq(a1.getResourceURI("install.rdf").spec, uri + "install.rdf");
+ do_check_eq(a1.iconURL, uri + "icon.png");
+ do_check_eq(a1.icon64URL, uri + "icon64.png");
+
+ a1.uninstall();
+ restartManager();
+ do_check_not_in_crash_annotation(a1.id, a1.version);
+
+ do_execute_soon(run_test_2);
+ }));
+ });
+ }));
+ });
+}
+
+// Tests that an install from a url downloads.
+function run_test_2() {
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 2");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.iconURL, null);
+ do_check_eq(install.sourceURI.spec, url);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_2);
+
+ install.addListener({
+ onDownloadProgress: function(install) {
+ do_execute_soon(function() {
+ Components.utils.forceGC();
+ });
+ }
+ });
+
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 2", null, "1.0");
+}
+
+function check_test_2(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Real Test 2");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ do_check_eq(install.iconURL, null);
+
+ // Pause the install here and start it again in run_test_3
+ do_execute_soon(function() { run_test_3(install); });
+ return false;
+}
+
+// Tests that the downloaded XPI installs ok
+function run_test_3(install) {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_3);
+ install.install();
+}
+
+function check_test_3(aInstall) {
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = aInstall.addon.getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_eq(olda2, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_neq(a2.syncGUID, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "2.0");
+ do_check_eq(a2.name, "Real Test 2");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_1").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:" + gPort + "/addons/test_install2_1.xpi");
+
+ let difference = a2.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a2.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ gInstallDate = a2.installDate.getTime();
+
+ run_test_4();
+ });
+ });
+ }));
+}
+
+// Tests that installing a new version of an existing add-on works
+function run_test_4() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_4);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+}
+
+function check_test_4(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.existingAddon);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ run_test_5();
+ // Installation will continue when there is nothing returned.
+}
+
+// Continue installing the new version
+function run_test_5() {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_5);
+}
+
+function check_test_5(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ AddonManager.getInstallsByTypes(null, callback_soon(function(installs) {
+ do_check_eq(installs.length, 1);
+ do_check_eq(installs[0].addon, olda2.pendingUpgrade);
+ restartManager();
+
+ AddonManager.getInstallsByTypes(null, function(installs) {
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "3.0");
+ do_check_eq(a2.name, "Real Test 3");
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_2").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:" + gPort + "/addons/test_install2_2.xpi");
+ do_check_false(a2.foreignInstall);
+
+ do_check_eq(a2.installDate.getTime(), gInstallDate);
+ // Update date should be later (or the same if this test is too fast)
+ do_check_true(a2.installDate <= a2.updateDate);
+
+ a2.uninstall();
+ do_execute_soon(run_test_6);
+ });
+ });
+ }));
+ });
+}
+
+// Tests that an install that requires a compatibility update works
+function run_test_6() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_6);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_6(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.existingAddon, null);
+ do_check_false(install.addon.appDisabled);
+ run_test_7();
+ return true;
+}
+
+// Continue the install
+function run_test_7() {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_7);
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", callback_soon(function(olda3) {
+ do_check_eq(olda3, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_neq(a3.syncGUID, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_8);
+ });
+ });
+ }));
+}
+
+function run_test_8() {
+ restartManager();
+
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install3"), function(install) {
+ do_check_true(install.addon.isCompatible);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_8));
+ install.install();
+ });
+}
+
+function check_test_8() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_neq(a3.syncGUID, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Test that after cancelling a download it is removed from the active installs
+function run_test_9() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_9);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_9(install) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ // Allow the file removal to complete
+ do_execute_soon(function() {
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+ do_check_false(file.exists());
+
+ run_test_10();
+ });
+ });
+ });
+
+ install.cancel();
+}
+
+// Tests that after cancelling a pending install it is removed from the active
+// installs
+function run_test_10() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], check_test_10);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_10(install) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(callback_soon(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+
+ restartManager();
+
+ // Check that the install did not complete
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_eq(a3, null);
+
+ do_execute_soon(run_test_11);
+ });
+ }));
+}
+
+// Tests that a multi-package install shows up as multiple installs with the
+// correct sourceURI.
+function run_test_11() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install4"), function(install) {
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_neq(install.linkedInstalls, null);
+ do_check_eq(install.linkedInstalls.length, 3);
+
+ // Might be in any order so sort them based on ID
+ let installs = [install].concat(install.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, install.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[0].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, install.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[1].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon6.xpi and would be incompatible with strict compat enabled
+ do_check_eq(installs[2].sourceURI, install.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_false(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[2].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, install.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[3].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 4);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_11));
+
+ installs[0].install();
+ installs[1].install();
+ installs[3].install();
+
+ // Note that we install addon6 last. Since it doesn't need a restart to
+ // install it completes asynchronously which would otherwise make the
+ // onInstallStarted/onInstallEnded events go out of sequence unless this
+ // is the last install operation
+ installs[2].install();
+ });
+ });
+}
+
+function check_test_11() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Same as test 11 but for a remote XPI
+function run_test_12() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall",
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install4.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ gInstall = install;
+
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "NO_ID": [
+ "onDownloadStarted",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onDownloadEnded"
+ ],
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_12));
+ install.install();
+ }, "application/x-xpinstall", null, "Multi Test 4");
+}
+
+function check_test_12() {
+ do_check_eq(gInstall.linkedInstalls.length, 3);
+
+ // Might be in any order so sort them based on ID
+ let installs = [gInstall].concat(gInstall.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon6.xpi and would be incompatible with strict compat enabled
+ do_check_eq(installs[2].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_false(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_INSTALLED);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+
+// Tests that cancelling an upgrade leaves the original add-on's pendingOperations
+// correct
+function run_test_13() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_13);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+ });
+}
+
+function check_test_13(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, install.addon);
+
+ do_check_true(hasFlag(install.addon.pendingOperations,
+ AddonManager.PENDING_INSTALL));
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ install.cancel();
+
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ do_check_false(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, null);
+
+ restartManager();
+
+ // Check that the upgrade did not complete
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.version, "2.0");
+
+ a2.uninstall();
+
+ do_execute_soon(run_test_14);
+ });
+ }));
+}
+
+// Check that cancelling the install from onDownloadStarted actually cancels it
+function run_test_14() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted"
+ ], check_test_14);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_14(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ install.addListener({
+ onDownloadProgress: function() {
+ do_throw("Download should not have continued");
+ },
+ onDownloadEnded: function() {
+ do_throw("Download should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it continues downloading. The
+ // The listener only really tests if we give it time to see progress, the
+ // file check isn't ideal either
+ do_execute_soon(function() {
+ do_check_false(file.exists());
+
+ run_test_15();
+ });
+ });
+
+ // Wait for the channel to be ready to cancel
+ do_execute_soon(function() {
+ install.cancel();
+ });
+}
+
+// Checks that cancelling the install from onDownloadEnded actually cancels it
+function run_test_15() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], check_test_15);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_15(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ install.addListener({
+ onInstallStarted: function() {
+ do_throw("Install should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it starts installing
+ do_execute_soon(run_test_16);
+}
+
+// Verify that the userDisabled value carries over to the upgrade by default
+function run_test_16() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install2_1_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install2_2_ended() {
+ do_check_true(aInstall.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ a2.uninstall();
+ do_execute_soon(run_test_17);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_17() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install2_1_ended2() {
+ do_check_false(aInstall.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install2_2_ended2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ a2.uninstall();
+ do_execute_soon(run_test_18);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_18() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_1_ended3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_true(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = false;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_2_ended3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ a2.uninstall();
+ do_execute_soon(run_test_18_1);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Checks that metadata is not stored if the pref is set to false
+function run_test_18_1() {
+ restartManager();
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:" + gPort + "/data/test_install.xml");
+
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", false);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall, aAddon) {
+ do_execute_soon(function test18_1_install_ended() {
+ do_check_neq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_19);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Checks that metadata is downloaded for new installs and is visible before and
+// after restart
+function run_test_19() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", true);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall, aAddon) {
+ do_execute_soon(function test19_install_ended() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_20);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Do the same again to make sure it works when the data is already in the cache
+function run_test_20() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall, aAddon) {
+ do_execute_soon(function test20_install_ended() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_21);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that installing an add-on that is already pending install cancels the
+// first install
+function run_test_21() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled",
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallCancelled",
+ "onInstallEnded",
+ ], check_test_21);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+}
+
+function check_test_21(aInstall) {
+ AddonManager.getAllInstalls(callback_soon(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], aInstall);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2, null);
+
+ run_test_22();
+ });
+ }));
+}
+
+// Tests that an install can be restarted after being cancelled
+function run_test_22() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_22);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_22(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_22);
+
+ aInstall.install();
+}
+
+function finish_test_22(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_23();
+}
+
+// Tests that an install can be restarted after being cancelled when a hash
+// was provided
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_23);
+ aInstall.install();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function check_test_23(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_23);
+
+ aInstall.install();
+}
+
+function finish_test_23(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_24();
+}
+
+// Tests that an install with a bad hash can be restarted after it fails, though
+// it will only fail again
+function run_test_24() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadFailed",
+ ], check_test_24);
+ aInstall.install();
+ }, "application/x-xpinstall", "sha1:foo");
+}
+
+function check_test_24(aInstall) {
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadFailed"
+ ], run_test_25);
+
+ aInstall.install();
+}
+
+// Tests that installs with a hash for a local file work
+function run_test_25() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = Services.io.newFileURI(do_get_addon("test_install3")).spec;
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(aInstall.error, 0);
+
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_26();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function run_test_26() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadCancelled"
+ ]);
+
+ let observerService = AM_Cc["@mozilla.org/network/http-activity-distributor;1"].
+ getService(AM_Ci.nsIHttpActivityDistributor);
+ observerService.addObserver({
+ observeActivity: function(aChannel, aType, aSubtype, aTimestamp, aSizeData,
+ aStringData) {
+ aChannel.QueryInterface(AM_Ci.nsIChannel);
+ // Wait for the final event for the redirected URL
+ if (aChannel.URI.spec != "http://localhost:" + gPort + "/addons/test_install1.xpi" ||
+ aType != AM_Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION ||
+ aSubtype != AM_Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
+ return;
+
+ // Request should have been cancelled
+ do_check_eq(aChannel.status, Components.results.NS_BINDING_ABORTED);
+
+ observerService.removeObserver(this);
+
+ run_test_27();
+ }
+ });
+
+ let url = "http://localhost:" + gPort + "/redirect?/addons/test_install1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onDownloadProgress: function(aInstall) {
+ aInstall.cancel();
+ }
+ });
+
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Tests that an install can be restarted during onDownloadCancelled after being
+// cancelled in mid-download
+function run_test_27() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ aInstall.addListener({
+ onDownloadProgress: function() {
+ aInstall.removeListener(this);
+ aInstall.cancel();
+ }
+ });
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadCancelled",
+ ], check_test_27);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_27);
+
+ let file = aInstall.file;
+ aInstall.install();
+ do_check_neq(file.path, aInstall.file.path);
+ do_check_false(file.exists());
+}
+
+function finish_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_28();
+}
+
+// Tests that an install that isn't strictly compatible and has
+// binary components correctly has appDisabled set (see bug 702868).
+function run_test_28() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install5.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 5");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted"
+ ], check_test_28);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 5", null, "1.0");
+}
+
+function check_test_28(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 5");
+ do_check_eq(install.state, AddonManager.STATE_INSTALLING);
+ do_check_eq(install.existingAddon, null);
+ do_check_false(install.addon.isCompatible);
+ do_check_true(install.addon.appDisabled);
+
+ prepare_test({}, [
+ "onInstallCancelled"
+ ], finish_test_28);
+ return false;
+}
+
+function finish_test_28(install) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], run_test_29);
+
+ install.cancel();
+}
+
+// Tests that an install with a matching compatibility override has appDisabled
+// set correctly.
+function run_test_29() {
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install6.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Addon Test 6");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], check_test_29);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Addon Test 6", null, "1.0");
+}
+
+function check_test_29(install) {
+ //ensure_test_completed();
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.addon, null);
+ do_check_false(install.addon.isCompatible);
+ do_check_true(install.addon.appDisabled);
+
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], do_test_finished);
+ install.cancel();
+ return false;
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install_icons.js b/toolkit/mozapps/extensions/test/xpcshell/test_install_icons.js
new file mode 100644
index 000000000..70f91c560
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install_icons.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// use httpserver to find an available port
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+var addon_url = "http://localhost:" + gPort + "/test.xpi";
+var icon32_url = "http://localhost:" + gPort + "/icon.png";
+var icon64_url = "http://localhost:" + gPort + "/icon64.png";
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ test_1();
+}
+
+function test_1() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, null);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], undefined);
+ do_check_eq(aInstall.icons[64], undefined);
+ test_2();
+ }, "application/x-xpinstall", null, null, null, null, null);
+}
+
+function test_2() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, icon32_url);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], icon32_url);
+ do_check_eq(aInstall.icons[64], undefined);
+ test_3();
+ }, "application/x-xpinstall", null, null, icon32_url, null, null);
+}
+
+function test_3() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, icon32_url);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], icon32_url);
+ do_check_eq(aInstall.icons[64], undefined);
+ test_4();
+ }, "application/x-xpinstall", null, null, { "32": icon32_url }, null, null);
+}
+
+function test_4() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, icon32_url);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], icon32_url);
+ do_check_eq(aInstall.icons[64], icon64_url);
+ do_execute_soon(do_test_finished);
+ }, "application/x-xpinstall", null, null, { "32": icon32_url, "64": icon64_url }, null, null);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js
new file mode 100644
index 000000000..0c7003d59
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js
@@ -0,0 +1,1654 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons can be installed from XPI files
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+// Maximum error in file modification times. Some file systems don't store
+// modification times exactly. As long as we are closer than this then it
+// still passes.
+const MAX_TIME_DIFFERENCE = 3000;
+
+// install.rdf size, icon.png, icon64.png size
+const ADDON1_SIZE = 705 + 16 + 16;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/httpd.js");
+
+var testserver;
+var gInstallDate;
+var gInstall = null;
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerPathHandler("/redirect", function(aRequest, aResponse) {
+ aResponse.setStatusLine(null, 301, "Moved Permanently");
+ let url = aRequest.host + ":" + aRequest.port + aRequest.queryString;
+ aResponse.setHeader("Location", "http://" + url);
+ });
+ testserver.start(4444);
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Checks that an install from a local file proceeds as expected
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_eq(install.addon.install, install);
+ do_check_eq(install.addon.size, ADDON1_SIZE);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ let file = do_get_addon("test_install1");
+ let uri = Services.io.newFileURI(file).spec;
+ do_check_eq(install.addon.getResourceURI("install.rdf").spec, "jar:" + uri + "!/install.rdf");
+ do_check_eq(install.addon.iconURL, "jar:" + uri + "!/icon.png");
+ do_check_eq(install.addon.icon64URL, "jar:" + uri + "!/icon64.png");
+ do_check_eq(install.iconURL, null);
+
+ do_check_eq(install.sourceURI.spec, uri);
+ do_check_eq(install.addon.sourceURI.spec, uri);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ AddonManager.getInstallsByTypes(["foo"], function(fooInstalls) {
+ do_check_eq(fooInstalls.length, 0);
+
+ AddonManager.getInstallsByTypes(["extension"], function(extensionInstalls) {
+ do_check_eq(extensionInstalls.length, 1);
+ do_check_eq(extensionInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_1);
+ install.install();
+ });
+ });
+ });
+ });
+}
+
+function check_test_1() {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(olda1) {
+ do_check_eq(olda1, null);
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(pendingAddons) {
+ do_check_eq(pendingAddons.length, 1);
+ do_check_eq(pendingAddons[0].id, "addon1@tests.mozilla.org");
+ let uri = NetUtil.newURI(pendingAddons[0].iconURL);
+ if (uri instanceof AM_Ci.nsIJARURI) {
+ let jarURI = uri.QueryInterface(AM_Ci.nsIJARURI);
+ let archiveURI = jarURI.JARFile;
+ let archiveFile = archiveURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipReader.open(archiveFile);
+ do_check_true(zipReader.hasEntry(jarURI.JAREntry));
+ }
+ finally {
+ zipReader.close();
+ }
+ }
+ else {
+ let iconFile = uri.QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_true(iconFile.exists());
+ }
+
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = pendingAddons[0].getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ // The pending add-on cannot be disabled or enabled.
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_DISABLE));
+
+ restartManager();
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls, 0);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.type, "extension");
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.name, "Test 1");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(do_get_addon("test_install1").exists());
+ do_check_in_crash_annotation(a1.id, a1.version);
+ do_check_eq(a1.size, ADDON1_SIZE);
+
+ do_check_eq(a1.sourceURI.spec,
+ Services.io.newFileURI(do_get_addon("test_install1")).spec);
+ let difference = a1.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a1.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ do_check_true(a1.hasResource("install.rdf"));
+ do_check_false(a1.hasResource("foo.bar"));
+
+ let uri = do_get_addon_root_uri(profileDir, "addon1@tests.mozilla.org");
+ do_check_eq(a1.getResourceURI("install.rdf").spec, uri + "install.rdf");
+ do_check_eq(a1.iconURL, uri + "icon.png");
+ do_check_eq(a1.icon64URL, uri + "icon64.png");
+
+ a1.uninstall();
+ do_execute_soon(function(){run_test_2(a1)});
+ });
+ });
+ }));
+ });
+}
+
+// Tests that an install from a url downloads.
+function run_test_2(aAddon) {
+ restartManager();
+ do_check_not_in_crash_annotation(aAddon.id, aAddon.version);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 2");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.iconURL, null);
+ do_check_eq(install.sourceURI.spec, url);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_2);
+
+ install.addListener({
+ onDownloadProgress: function(install) {
+ do_execute_soon(function() {
+ Components.utils.forceGC();
+ });
+ }
+ });
+
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 2", null, "1.0");
+}
+
+function check_test_2(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Real Test 2");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ do_check_eq(install.iconURL, null);
+
+ // Pause the install here and start it again in run_test_3
+ do_execute_soon(function() { run_test_3(install); });
+ return false;
+}
+
+// Tests that the downloaded XPI installs ok
+function run_test_3(install) {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_3);
+ install.install();
+}
+
+function check_test_3(aInstall) {
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = aInstall.addon.getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_eq(olda2, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "2.0");
+ do_check_eq(a2.name, "Real Test 2");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_1").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:4444/addons/test_install2_1.xpi");
+
+ let difference = a2.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a2.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ gInstallDate = a2.installDate.getTime();
+
+ run_test_4();
+ });
+ });
+ }));
+}
+
+// Tests that installing a new version of an existing add-on works
+function run_test_4() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_4);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+}
+
+function check_test_4(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.existingAddon);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ run_test_5();
+ // Installation will continue when there is nothing returned.
+}
+
+// Continue installing the new version
+function run_test_5() {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_5);
+}
+
+function check_test_5(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ AddonManager.getInstallsByTypes(null, callback_soon(function(installs) {
+ do_check_eq(installs.length, 1);
+ do_check_eq(installs[0].addon, olda2.pendingUpgrade);
+ restartManager();
+
+ AddonManager.getInstallsByTypes(null, function(installs) {
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "3.0");
+ do_check_eq(a2.name, "Real Test 3");
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_2").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:4444/addons/test_install2_2.xpi");
+
+ do_check_eq(a2.installDate.getTime(), gInstallDate);
+ // Update date should be later (or the same if this test is too fast)
+ do_check_true(a2.installDate <= a2.updateDate);
+
+ a2.uninstall();
+ do_execute_soon(run_test_6);
+ });
+ });
+ }));
+ });
+}
+
+// Tests that an install that requires a compatibility update works
+function run_test_6() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_6);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_6(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.existingAddon, null);
+ do_check_false(install.addon.appDisabled);
+ run_test_7();
+ return true;
+}
+
+// Continue the install
+function run_test_7() {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_7);
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", callback_soon(function(olda3) {
+ do_check_eq(olda3, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_8);
+ });
+ });
+ }));
+}
+
+function run_test_8() {
+ restartManager();
+
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install3"), function(install) {
+ do_check_true(install.addon.isCompatible);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_8));
+ install.install();
+ });
+}
+
+function check_test_8() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Test that after cancelling a download it is removed from the active installs
+function run_test_9() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_9);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_9(install) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ // Allow the file removal to complete
+ do_execute_soon(function() {
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+ do_check_false(file.exists());
+
+ run_test_10();
+ });
+ });
+ });
+
+ install.cancel();
+}
+
+// Tests that after cancelling a pending install it is removed from the active
+// installs
+function run_test_10() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], check_test_10);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_10(install) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(callback_soon(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+
+ restartManager();
+
+ // Check that the install did not complete
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_eq(a3, null);
+
+ run_test_11();
+ });
+ }));
+}
+
+// Tests that a multi-package install shows up as multiple installs with the
+// correct sourceURI.
+function run_test_11() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install4"), function(install) {
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_neq(install.linkedInstalls, null);
+ do_check_eq(install.linkedInstalls.length, 3);
+
+ // Might be in any order so sort them based on ID
+ let installs = [install].concat(install.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, install.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[0].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, install.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[1].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon6.xpi and is incompatible
+ do_check_eq(installs[2].sourceURI, install.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_true(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_DOWNLOADED);
+ do_check_false(hasFlag(installs[2].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, install.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[3].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 4);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_11));
+
+ installs[0].install();
+ installs[1].install();
+ installs[3].install();
+
+ // Note that we install addon6 last. Since it doesn't need a restart to
+ // install it completes asynchronously which would otherwise make the
+ // onInstallStarted/onInstallEnded events go out of sequence unless this
+ // is the last install operation
+ installs[2].install();
+ });
+ });
+}
+
+function check_test_11() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Same as test 11 but for a remote XPI
+function run_test_12() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall",
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install4.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ gInstall = install;
+
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "NO_ID": [
+ "onDownloadStarted",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onDownloadEnded"
+ ],
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_12));
+ install.install();
+ }, "application/x-xpinstall", null, "Multi Test 4");
+}
+
+function check_test_12() {
+ do_check_eq(gInstall.linkedInstalls.length, 3);
+
+ // Might be in any order so sort them based on ID
+ let installs = [gInstall].concat(gInstall.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon6.xpi and is incompatible
+ do_check_eq(installs[2].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_true(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_INSTALLED);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+
+// Tests that cancelling an upgrade leaves the original add-on's pendingOperations
+// correct
+function run_test_13() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_13);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+ });
+}
+
+function check_test_13(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, install.addon);
+
+ do_check_true(hasFlag(install.addon.pendingOperations,
+ AddonManager.PENDING_INSTALL));
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ install.cancel();
+
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ do_check_false(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, null);
+
+ restartManager();
+
+ // Check that the upgrade did not complete
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.version, "2.0");
+
+ a2.uninstall();
+
+ do_execute_soon(run_test_14);
+ });
+ }));
+}
+
+// Check that cancelling the install from onDownloadStarted actually cancels it
+function run_test_14() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted"
+ ], check_test_14);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_14(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ install.addListener({
+ onDownloadProgress: function() {
+ do_throw("Download should not have continued");
+ },
+ onDownloadEnded: function() {
+ do_throw("Download should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it continues downloading. The
+ // The listener only really tests if we give it time to see progress, the
+ // file check isn't ideal either
+ do_execute_soon(function() {
+ do_check_false(file.exists());
+
+ run_test_15();
+ });
+ });
+
+ // Wait for the channel to be ready to cancel
+ do_execute_soon(function() {
+ install.cancel();
+ });
+}
+
+// Checks that cancelling the install from onDownloadEnded actually cancels it
+function run_test_15() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], check_test_15);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_15(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ install.addListener({
+ onInstallStarted: function() {
+ do_throw("Install should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it starts installing
+ do_execute_soon(run_test_16);
+}
+
+// Verify that the userDisabled value carries over to the upgrade by default
+function run_test_16() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test16_install1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function test16_install2() {
+ do_check_true(aInstall.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ a2.uninstall();
+ do_execute_soon(run_test_17);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_17() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function test17_install1() {
+ do_check_false(aInstall.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test17_install1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ a2.uninstall();
+ do_execute_soon(run_test_18);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_18() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test18_install1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_true(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = false;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test18_install2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ a2.uninstall();
+ do_execute_soon(run_test_18_1);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Checks that metadata is not stored if the pref is set to false
+function run_test_18_1() {
+ restartManager();
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:4444/data/test_install.xml");
+
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", false);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall, aAddon) {
+ do_execute_soon(function test18_install() {
+ do_check_neq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_19);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Checks that metadata is downloaded for new installs and is visible before and
+// after restart
+function run_test_19() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", true);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall, aAddon) {
+ do_execute_soon(function test19_install() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_20);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Do the same again to make sure it works when the data is already in the cache
+function run_test_20() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall, aAddon) {
+ do_execute_soon(function test20_install() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_21);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that installing an add-on that is already pending install cancels the
+// first install
+function run_test_21() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled",
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallCancelled",
+ "onInstallEnded",
+ ], check_test_21);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+}
+
+function check_test_21(aInstall) {
+ AddonManager.getAllInstalls(callback_soon(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], aInstall);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2, null);
+
+ run_test_22();
+ });
+ }));
+}
+
+// Tests that an install can be restarted after being cancelled
+function run_test_22() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_22);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_22(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_22);
+
+ aInstall.install();
+}
+
+function finish_test_22(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_23();
+}
+
+// Tests that an install can be restarted after being cancelled when a hash
+// was provided
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_23);
+ aInstall.install();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function check_test_23(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_23);
+
+ aInstall.install();
+}
+
+function finish_test_23(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_24();
+}
+
+// Tests that an install with a bad hash can be restarted after it fails, though
+// it will only fail again
+function run_test_24() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadFailed",
+ ], check_test_24);
+ aInstall.install();
+ }, "application/x-xpinstall", "sha1:foo");
+}
+
+function check_test_24(aInstall) {
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadFailed"
+ ], run_test_25);
+
+ aInstall.install();
+}
+
+// Tests that installs with a hash for a local file work
+function run_test_25() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = Services.io.newFileURI(do_get_addon("test_install3")).spec;
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(aInstall.error, 0);
+
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_26();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function run_test_26() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadCancelled"
+ ]);
+
+ let observerService = AM_Cc["@mozilla.org/network/http-activity-distributor;1"].
+ getService(AM_Ci.nsIHttpActivityDistributor);
+ observerService.addObserver({
+ observeActivity: function(aChannel, aType, aSubtype, aTimestamp, aSizeData,
+ aStringData) {
+ aChannel.QueryInterface(AM_Ci.nsIChannel);
+ // Wait for the final event for the redirected URL
+ if (aChannel.URI.spec != "http://localhost:4444/addons/test_install1.xpi" ||
+ aType != AM_Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION ||
+ aSubtype != AM_Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
+ return;
+
+ // Request should have been cancelled
+ do_check_eq(aChannel.status, Components.results.NS_BINDING_ABORTED);
+
+ observerService.removeObserver(this);
+
+ run_test_27();
+ }
+ });
+
+ let url = "http://localhost:4444/redirect?/addons/test_install1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onDownloadProgress: function(aInstall) {
+ aInstall.cancel();
+ }
+ });
+
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Tests that an install can be restarted during onDownloadCancelled after being
+// cancelled in mid-download
+function run_test_27() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ aInstall.addListener({
+ onDownloadProgress: function() {
+ aInstall.removeListener(this);
+ aInstall.cancel();
+ }
+ });
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadCancelled",
+ ], check_test_27);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_27);
+
+ let file = aInstall.file;
+ aInstall.install();
+ do_check_neq(file.path, aInstall.file.path);
+ do_check_false(file.exists());
+}
+
+function finish_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ end_test();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_isDebuggable.js b/toolkit/mozapps/extensions/test/xpcshell/test_isDebuggable.js
new file mode 100644
index 000000000..d4f8a482b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_isDebuggable.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/.
+ */
+
+var ADDONS = [
+ "test_bootstrap2_1", // restartless addon
+ "test_bootstrap1_4", // old-school addon
+ "test_jetpack" // sdk addon
+];
+
+var IDS = [
+ "bootstrap1@tests.mozilla.org",
+ "bootstrap2@tests.mozilla.org",
+ "jetpack@tests.mozilla.org"
+];
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+ AddonManager.checkCompatibility = false;
+
+ installAllFiles(ADDONS.map(do_get_addon), function () {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(IDS, function([a1, a2, a3]) {
+ do_check_eq(a1.isDebuggable, false);
+ do_check_eq(a2.isDebuggable, true);
+ do_check_eq(a3.isDebuggable, true);
+ do_test_finished();
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_isReady.js b/toolkit/mozapps/extensions/test/xpcshell/test_isReady.js
new file mode 100644
index 000000000..6222398a7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_isReady.js
@@ -0,0 +1,49 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* () {
+ equal(AddonManager.isReady, false, "isReady should be false before startup");
+
+ let gotStartupEvent = false;
+ let gotShutdownEvent = false;
+ let listener = {
+ onStartup() {
+ gotStartupEvent = true;
+ },
+ onShutdown() {
+ gotShutdownEvent = true;
+ },
+ };
+ AddonManager.addManagerListener(listener);
+
+ do_print("Starting manager...");
+ startupManager();
+ equal(AddonManager.isReady, true, "isReady should be true after startup");
+ equal(gotStartupEvent, true, "Should have seen onStartup event after startup");
+ equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown");
+
+ gotStartupEvent = false;
+ gotShutdownEvent = false;
+
+ do_print("Shutting down manager...");
+ let shutdownPromise = promiseShutdownManager();
+ equal(AddonManager.isReady, false, "isReady should be false when shutdown commences");
+ yield shutdownPromise;
+
+ equal(AddonManager.isReady, false, "isReady should be false after shutdown");
+ equal(gotStartupEvent, false, "Should not have seen onStartup event after shutdown");
+ equal(gotShutdownEvent, true, "Should have seen onShutdown event after shutdown");
+
+ AddonManager.addManagerListener(listener);
+ gotStartupEvent = false;
+ gotShutdownEvent = false;
+
+ do_print("Starting manager again...");
+ startupManager();
+ equal(AddonManager.isReady, true, "isReady should be true after repeat startup");
+ equal(gotStartupEvent, true, "Should have seen onStartup event after repeat startup");
+ equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown, following repeat startup");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_langpack.js b/toolkit/mozapps/extensions/test/xpcshell/test_langpack.js
new file mode 100644
index 000000000..a97a14d4d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_langpack.js
@@ -0,0 +1,339 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that language packs can be used without restarts.
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+// Enable installing distribution add-ons
+Services.prefs.setBoolPref("extensions.installDistroAddons", true);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+const distroDir = gProfD.clone();
+distroDir.append("distribution");
+distroDir.append("extensions");
+registerDirectory("XREAppDist", distroDir.parent);
+
+var chrome = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Components.interfaces.nsIXULChromeRegistry);
+
+function do_unregister_manifest() {
+ let path = getFileForAddon(profileDir, "langpack-x-testing@tests.mozilla.org");
+ Components.manager.removeBootstrappedManifestLocation(path);
+}
+
+function do_check_locale_not_registered(provider) {
+ let didThrow = false;
+ try {
+ chrome.getSelectedLocale(provider);
+ } catch (e) {
+ didThrow = true;
+ }
+ do_check_true(didThrow);
+}
+
+function run_test() {
+ do_test_pending();
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ do_check_locale_not_registered("test-langpack");
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_langpack"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "locale");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Language Pack x-testing");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_false(install.addon.hasResource("bootstrap.js"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+
+ let addon = install.addon;
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ // spin to let the startup complete
+ do_execute_soon(check_test_1);
+ });
+ install.install();
+ });
+}
+
+function check_test_1() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+
+ let dir = do_get_addon_root_uri(profileDir, "langpack-x-testing@tests.mozilla.org");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 0);
+
+ run_test_2();
+ });
+ });
+ });
+}
+
+// Tests that disabling doesn't require a restart
+function run_test_2() {
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_true(newb1.userDisabled);
+ do_check_false(newb1.isActive);
+
+ do_execute_soon(run_test_3);
+ });
+ });
+}
+
+// Test that restarting doesn't accidentally re-enable
+function run_test_3() {
+ shutdownManager();
+ startupManager(false);
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ run_test_4();
+ });
+}
+
+// Tests that enabling doesn't require a restart
+function run_test_4() {
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_false(newb1.userDisabled);
+ do_check_true(newb1.isActive);
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Tests that a restart shuts down and restarts the add-on
+function run_test_5() {
+ shutdownManager();
+ do_unregister_manifest();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+ startupManager(false);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, b1.id));
+
+ run_test_7();
+ });
+}
+
+// Tests that uninstalling doesn't require a restart
+function run_test_7() {
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ b1.uninstall();
+
+ check_test_7();
+ });
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_eq(b1, null);
+
+ restartManager();
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(newb1) {
+ do_check_eq(newb1, null);
+
+ do_execute_soon(run_test_8);
+ });
+ }));
+}
+
+// Tests that a locale detected in the profile starts working immediately
+function run_test_8() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_langpack"), profileDir, "langpack-x-testing@tests.mozilla.org");
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+
+ shutdownManager();
+ do_unregister_manifest();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+ startupManager(false);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b2) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ b2.uninstall();
+ ensure_test_completed();
+ do_execute_soon(run_test_9);
+ });
+ }));
+}
+
+// Tests that a locale from distribution/extensions gets installed and starts
+// working immediately
+function run_test_9() {
+ shutdownManager();
+ manuallyInstall(do_get_addon("test_langpack"), distroDir, "langpack-x-testing@tests.mozilla.org");
+ gAppInfo.version = "2.0";
+ startupManager(true);
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+
+ shutdownManager();
+ do_unregister_manifest();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+ startupManager(false);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ do_test_finished();
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locale.js b/toolkit/mozapps/extensions/test/xpcshell/test_locale.js
new file mode 100644
index 000000000..b4c7311e5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locale.js
@@ -0,0 +1,149 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that localized properties work as expected
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+
+function run_test() {
+ do_test_pending();
+
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that the localized properties are visible before installation
+function run_test_1() {
+ AddonManager.getInstallForFile(do_get_addon("test_locale"), function(install) {
+ do_check_eq(install.addon.name, "fr-FR Name");
+ do_check_eq(install.addon.description, "fr-FR Description");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(run_test_2));
+ install.install();
+ });
+}
+
+// Tests that the localized properties are visible after installation
+function run_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "fr-FR Name");
+ do_check_eq(addon.description, "fr-FR Description");
+
+ addon.userDisabled = true;
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that the localized properties are still there when disabled.
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "fr-FR Name");
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Localised preference values should be ignored when the add-on is disabled
+function run_test_4() {
+ Services.prefs.setCharPref("extensions.addon1@tests.mozilla.org.name", "Name from prefs");
+ Services.prefs.setCharPref("extensions.addon1@tests.mozilla.org.contributor.1", "Contributor 1");
+ Services.prefs.setCharPref("extensions.addon1@tests.mozilla.org.contributor.2", "Contributor 2");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "fr-FR Name");
+ let contributors = addon.contributors;
+ do_check_eq(contributors.length, 3);
+ do_check_eq(contributors[0], "Fr Contributor 1");
+ do_check_eq(contributors[1], "Fr Contributor 2");
+ do_check_eq(contributors[2], "Fr Contributor 3");
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Test that changing locale works
+function run_test_5() {
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "de-DE");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "de-DE Name");
+ do_check_eq(addon.description, null);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Test that missing locales use the fallbacks
+function run_test_6() {
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "nl-NL");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "Fallback Name");
+ do_check_eq(addon.description, "Fallback Description");
+
+ addon.userDisabled = false;
+ do_execute_soon(run_test_7);
+ }));
+}
+
+// Test that the prefs will override the fallbacks
+function run_test_7() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "Name from prefs");
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Test that the prefs will override localized values from the manifest
+function run_test_8() {
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "Name from prefs");
+ let contributors = addon.contributors;
+ do_check_eq(contributors.length, 2);
+ do_check_eq(contributors[0], "Contributor 1");
+ do_check_eq(contributors[1], "Contributor 2");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locked.js b/toolkit/mozapps/extensions/test/xpcshell/test_locked.js
new file mode 100644
index 000000000..d16c1019d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked.js
@@ -0,0 +1,529 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+Components.utils.import("resource://testing-common/httpd.js");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_corrupt.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and stay enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Would stay incompatible with strict compat
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+add_task(function* init() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ // New profile so new add-ons are ignored
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ let [a2, a3, a4, a7, t2] =
+ yield promiseAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+ let deferredUpdateFinished = Promise.defer();
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ // Let the updates finish before restarting the manager
+ deferredUpdateFinished.resolve();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+
+ yield deferredUpdateFinished.promise;
+});
+
+
+add_task(function* run_test_1() {
+ restartManager();
+ let [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Open another handle on the JSON DB with as much Unix and Windows locking
+ // as we can to simulate some other process interfering with it
+ shutdownManager();
+ do_print("Locking " + gExtensionsJSON.path);
+ let options = {
+ winShare: 0
+ };
+ if (OS.Constants.libc.O_EXLOCK)
+ options.unixFlags = OS.Constants.libc.O_EXLOCK;
+
+ let file = yield OS.File.open(gExtensionsJSON.path, {read:true, write:true, existing:true}, options);
+
+ let filePermissions = gExtensionsJSON.permissions;
+ if (!OS.Constants.Win) {
+ gExtensionsJSON.permissions = 0;
+ }
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ // Accessing the add-ons should open and recover the database
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ // Should be correctly recovered
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // Should be correctly recovered
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ // The compatibility update won't be recovered and with strict
+ // compatibility it would not have been able to tell that it was
+ // previously userDisabled. However, without strict compat, it wasn't
+ // appDisabled, so it knows it must have been userDisabled.
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ // Should be correctly recovered
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Restarting will actually apply changes to extensions.ini which will
+ // then be put into the in-memory database when we next fail to load the
+ // real thing
+ restartManager();
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // After allowing access to the original DB things should go back to as
+ // they were previously
+ shutdownManager();
+ do_print("Unlocking " + gExtensionsJSON.path);
+ yield file.close();
+ gExtensionsJSON.permissions = filePermissions;
+ startupManager();
+
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+});
+
+
+function run_test() {
+ run_next_test();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js b/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js
new file mode 100644
index 000000000..10b13c9f6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js
@@ -0,0 +1,292 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we handle a locked database when there are extension changes
+// in progress
+
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+// Will be left alone
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be enabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be uninstalled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+
+// Will be updated
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+add_task(function() {
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+
+ // Make it look like add-on 5 was installed some time in the past so the update is
+ // detected
+ let path = getFileForAddon(profileDir, addon5.id).path;
+ yield promiseSetExtensionModifiedTime(path, Date.now() - (60000));
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+
+ let a1, a2, a3, a4, a5, a6;
+
+ [a2] = yield promiseAddonsByIDs(["addon2@tests.mozilla.org"]);
+ a2.userDisabled = true;
+
+ restartManager();
+
+ [a1, a2, a3, a4, a5] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"]);
+
+ a2.userDisabled = false;
+ a3.userDisabled = true;
+ a4.uninstall();
+
+ yield promiseInstallAllFiles([do_get_addon("test_locked2_5"),
+ do_get_addon("test_locked2_6")]);
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_ENABLE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_UNINSTALL);
+ do_check_true(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.version, "1.0");
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_UPGRADE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ // Open another handle on the JSON DB with as much Unix and Windows locking
+ // as we can to simulate some other process interfering with it
+ shutdownManager();
+ do_print("Locking " + gExtensionsJSON.path);
+ let options = {
+ winShare: 0
+ };
+ if (OS.Constants.libc.O_EXLOCK)
+ options.unixFlags = OS.Constants.libc.O_EXLOCK;
+
+ let file = yield OS.File.open(gExtensionsJSON.path, {read:true, write:true, existing:true}, options);
+
+ let filePermissions = gExtensionsJSON.permissions;
+ if (!OS.Constants.Win) {
+ gExtensionsJSON.permissions = 0;
+ }
+ startupManager(false);
+
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_eq(a4, null);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.version, "2.0");
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+
+ // After allowing access to the original DB things should still be
+ // back how they were before the lock
+ shutdownManager();
+ yield file.close();
+ gExtensionsJSON.permissions = filePermissions;
+ startupManager();
+
+ // On Unix, we can save the DB even when the original file wasn't
+ // readable, so our changes were saved. On Windows,
+ // these things happened when we had no access to the database so
+ // they are seen as external changes when we get the database back
+ if (gXPISaveError) {
+ do_print("Previous XPI save failed");
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED,
+ ["addon6@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED,
+ ["addon4@tests.mozilla.org"]);
+ }
+ else {
+ do_print("Previous XPI save succeeded");
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ }
+
+ [a1, a2, a3, a4, a5, a6] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_eq(a4, null);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.version, "2.0");
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+});
+
+function run_test() {
+ run_next_test();
+}
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js
new file mode 100644
index 000000000..907c611dd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js
@@ -0,0 +1,551 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+Components.utils.import("resource://testing-common/httpd.js");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_corrupt.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be disabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Stays incompatible
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+add_task(function* init() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ // New profile so new add-ons are ignored
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ let a1, a2, a3, a4, a5, a6, a7, t1, t2;
+
+ [a2, a3, a4, a7, t2] =
+ yield promiseAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ // Set up the initial state
+ let deferredUpdateFinished = Promise.defer();
+
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ deferredUpdateFinished.resolve();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ yield deferredUpdateFinished.promise;
+});
+
+add_task(function* run_test_1() {
+ let a1, a2, a3, a4, a5, a6, a7, t1, t2;
+
+ restartManager();
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Open another handle on the JSON DB with as much Unix and Windows locking
+ // as we can to simulate some other process interfering with it
+ shutdownManager();
+ do_print("Locking " + gExtensionsJSON.path);
+ let options = {
+ winShare: 0
+ };
+ if (OS.Constants.libc.O_EXLOCK)
+ options.unixFlags = OS.Constants.libc.O_EXLOCK;
+
+ let file = yield OS.File.open(gExtensionsJSON.path, {read:true, write:true, existing:true}, options);
+
+ let filePermissions = gExtensionsJSON.permissions;
+ if (!OS.Constants.Win) {
+ gExtensionsJSON.permissions = 0;
+ }
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ // Accessing the add-ons should open and recover the database
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ // Should be correctly recovered
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // Should be correctly recovered
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ // The compatibility update won't be recovered and it will not have been
+ // able to tell that it was previously userDisabled
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ // Should be correctly recovered
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Restarting will actually apply changes to extensions.ini which will
+ // then be put into the in-memory database when we next fail to load the
+ // real thing
+ restartManager();
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // After allowing access to the original DB things should go back to as
+ // back how they were before the lock
+ shutdownManager();
+ do_print("Unlocking " + gExtensionsJSON.path);
+ yield file.close();
+ gExtensionsJSON.permissions = filePermissions;
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.userDisabled);
+ // On Unix, we may be able to save our changes over the locked DB so we
+ // remember that this extension was changed to disabled. On Windows we
+ // couldn't replace the old DB so we read the older version of the DB
+ // where the extension is enabled
+ if (gXPISaveError) {
+ do_print("XPI save failed");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ }
+ else {
+ do_print("XPI save succeeded");
+ do_check_false(a3.isActive);
+ do_check_true(a3.appDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ }
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ // The reverse of the platform difference for a3 - Unix should
+ // stay the same as the last iteration because the save succeeded,
+ // Windows should still say userDisabled
+ if (OS.Constants.Win) {
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ }
+ else {
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ }
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+});
+
+function run_test() {
+ run_next_test();
+}
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js b/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js
new file mode 100644
index 000000000..061a3a6b2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js
@@ -0,0 +1,562 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests that all properties are read from the install manifests and that
+// items are correctly enabled/disabled based on them (blocklist tests are
+// elsewhere)
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ const profileDir = gProfD.clone();
+ profileDir.append("extensions");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ optionsURL: "chrome://test/content/options.xul",
+ aboutURL: "chrome://test/content/about.xul",
+ iconURL: "chrome://test/skin/icon.png",
+ icon64URL: "chrome://test/skin/icon64.png",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ description: "Test Description",
+ creator: "Test Creator",
+ homepageURL: "http://www.example.com",
+ developer: [
+ "Test Developer 1",
+ "Test Developer 2"
+ ],
+ translator: [
+ "Test Translator 1",
+ "Test Translator 2"
+ ],
+ contributor: [
+ "Test Contributor 1",
+ "Test Contributor 2"
+ ]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "https://www.foo.com",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 2"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://www.foo.com",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 3"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://www.foo.com",
+ updateKey: "foo",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 4"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "*"
+ }],
+ name: "Test Addon 5"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 6"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 7"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1.1",
+ maxVersion: "*"
+ }],
+ name: "Test Addon 8"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.*"
+ }],
+ name: "Test Addon 9"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon10@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2.1",
+ maxVersion: "1.9.*"
+ }],
+ name: "Test Addon 10"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.2"
+ }],
+ name: "Test Addon 11"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon12@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.1.*"
+ }],
+ name: "Test Addon 12"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon13@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.*"
+ }, {
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0.5"
+ }],
+ name: "Test Addon 13"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon14@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.1"
+ }, {
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 14"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon15@tests.mozilla.org",
+ version: "1.0",
+ updateKey: "foo",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 15"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon16@tests.mozilla.org",
+ version: "1.0",
+ updateKey: "foo",
+ updateURL: "https://www.foo.com",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 16"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon17@tests.mozilla.org",
+ version: "1.0",
+ optionsURL: "chrome://test/content/options.xul",
+ optionsType: "2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 17"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon18@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 18"
+ }, profileDir, null, "options.xul");
+
+ writeInstallRDFForExtension({
+ id: "addon19@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "99",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 19"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon20@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "1",
+ optionsURL: "chrome://test/content/options.xul",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 20"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon21@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "3",
+ optionsURL: "chrome://test/content/options.xul",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 21"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon22@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 22"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon23@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 23"
+ }, profileDir, null, "options.xul");
+
+ writeInstallRDFForExtension({
+ id: "addon24@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 24"
+ }, profileDir, null, "options.xul");
+
+ writeInstallRDFForExtension({
+ id: "addon25@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 25"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon26@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 26"
+ }, profileDir, null, "options.xul");
+
+ do_test_pending();
+ startupManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org",
+ "addon11@tests.mozilla.org",
+ "addon12@tests.mozilla.org",
+ "addon13@tests.mozilla.org",
+ "addon14@tests.mozilla.org",
+ "addon15@tests.mozilla.org",
+ "addon16@tests.mozilla.org",
+ "addon17@tests.mozilla.org",
+ "addon18@tests.mozilla.org",
+ "addon19@tests.mozilla.org",
+ "addon20@tests.mozilla.org",
+ "addon21@tests.mozilla.org",
+ "addon22@tests.mozilla.org",
+ "addon23@tests.mozilla.org",
+ "addon24@tests.mozilla.org",
+ "addon25@tests.mozilla.org",
+ "addon26@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20,
+ a21, a22, a23, a24, a25, a26]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.type, "extension");
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a1.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
+ do_check_eq(a1.aboutURL, "chrome://test/content/about.xul");
+ do_check_eq(a1.iconURL, "chrome://test/skin/icon.png");
+ do_check_eq(a1.icon64URL, "chrome://test/skin/icon64.png");
+ do_check_eq(a1.icons[32], "chrome://test/skin/icon.png");
+ do_check_eq(a1.icons[64], "chrome://test/skin/icon64.png");
+ do_check_eq(a1.name, "Test Addon 1");
+ do_check_eq(a1.description, "Test Description");
+ do_check_eq(a1.creator, "Test Creator");
+ do_check_eq(a1.homepageURL, "http://www.example.com");
+ do_check_eq(a1.developers[0], "Test Developer 1");
+ do_check_eq(a1.developers[1], "Test Developer 2");
+ do_check_eq(a1.translators[0], "Test Translator 1");
+ do_check_eq(a1.translators[1], "Test Translator 2");
+ do_check_eq(a1.contributors[0], "Test Contributor 1");
+ do_check_eq(a1.contributors[1], "Test Contributor 2");
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isCompatible);
+ do_check_true(a1.providesUpdatesSecurely);
+ do_check_eq(a1.blocklistState, AM_Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_true(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.providesUpdatesSecurely);
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.id, "addon3@tests.mozilla.org");
+ do_check_false(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.providesUpdatesSecurely);
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.id, "addon4@tests.mozilla.org");
+ do_check_true(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.providesUpdatesSecurely);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isCompatible);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_true(a6.isCompatible);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_false(a7.userDisabled);
+ do_check_true(a7.appDisabled);
+ do_check_false(a7.isCompatible);
+
+ do_check_neq(a8, null);
+ do_check_false(a8.isActive);
+ do_check_false(a8.userDisabled);
+ do_check_true(a8.appDisabled);
+ do_check_false(a8.isCompatible);
+
+ do_check_neq(a9, null);
+ do_check_true(a9.isActive);
+ do_check_false(a9.userDisabled);
+ do_check_false(a9.appDisabled);
+ do_check_true(a9.isCompatible);
+
+ do_check_neq(a10, null);
+ do_check_false(a10.isActive);
+ do_check_false(a10.userDisabled);
+ do_check_true(a10.appDisabled);
+ do_check_false(a10.isCompatible);
+
+ do_check_neq(a11, null);
+ do_check_true(a11.isActive);
+ do_check_false(a11.userDisabled);
+ do_check_false(a11.appDisabled);
+ do_check_true(a11.isCompatible);
+
+ do_check_neq(a12, null);
+ do_check_false(a12.isActive);
+ do_check_false(a12.userDisabled);
+ do_check_true(a12.appDisabled);
+ do_check_false(a12.isCompatible);
+
+ do_check_neq(a13, null);
+ do_check_false(a13.isActive);
+ do_check_false(a13.userDisabled);
+ do_check_true(a13.appDisabled);
+ do_check_false(a13.isCompatible);
+
+ do_check_neq(a14, null);
+ do_check_true(a14.isActive);
+ do_check_false(a14.userDisabled);
+ do_check_false(a14.appDisabled);
+ do_check_true(a14.isCompatible);
+
+ do_check_neq(a15, null);
+ do_check_true(a15.isActive);
+ do_check_false(a15.userDisabled);
+ do_check_false(a15.appDisabled);
+ do_check_true(a15.isCompatible);
+ do_check_true(a15.providesUpdatesSecurely);
+
+ do_check_neq(a16, null);
+ do_check_true(a16.isActive);
+ do_check_false(a16.userDisabled);
+ do_check_false(a16.appDisabled);
+ do_check_true(a16.isCompatible);
+ do_check_true(a16.providesUpdatesSecurely);
+
+ do_check_neq(a17, null);
+ do_check_true(a17.isActive);
+ do_check_false(a17.userDisabled);
+ do_check_false(a17.appDisabled);
+ do_check_true(a17.isCompatible);
+ do_check_eq(a17.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a17.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+
+ do_check_neq(a18, null);
+ do_check_true(a18.isActive);
+ do_check_false(a18.userDisabled);
+ do_check_false(a18.appDisabled);
+ do_check_true(a18.isCompatible);
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack")) {
+ do_check_eq(a18.optionsURL, Services.io.newFileURI(profileDir).spec +
+ "addon18@tests.mozilla.org/options.xul");
+ } else {
+ do_check_eq(a18.optionsURL, "jar:" + Services.io.newFileURI(profileDir).spec +
+ "addon18@tests.mozilla.org.xpi!/options.xul");
+ }
+ do_check_eq(a18.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+
+ do_check_eq(a19, null);
+
+ do_check_neq(a20, null);
+ do_check_true(a20.isActive);
+ do_check_false(a20.userDisabled);
+ do_check_false(a20.appDisabled);
+ do_check_true(a20.isCompatible);
+ do_check_eq(a20.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a20.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
+
+ do_check_neq(a21, null);
+ do_check_true(a21.isActive);
+ do_check_false(a21.userDisabled);
+ do_check_false(a21.appDisabled);
+ do_check_true(a21.isCompatible);
+ do_check_eq(a21.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a21.optionsType, AddonManager.OPTIONS_TYPE_TAB);
+
+ do_check_neq(a22, null);
+ do_check_eq(a22.optionsType, null);
+ do_check_eq(a22.optionsURL, null);
+
+ do_check_neq(a23, null);
+ do_check_eq(a23.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+ do_check_neq(a23.optionsURL, null);
+
+ do_check_neq(a24, null);
+ do_check_eq(a24.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+ do_check_neq(a24.optionsURL, null);
+
+ do_check_neq(a25, null);
+ do_check_eq(a25.optionsType, null);
+ do_check_eq(a25.optionsURL, null);
+
+ do_check_neq(a26, null);
+ do_check_eq(a26.optionsType, AddonManager.OPTIONS_TYPE_INLINE_INFO);
+ do_check_neq(a26.optionsURL, null);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js b/toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
new file mode 100644
index 000000000..a6f9c8052
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
@@ -0,0 +1,326 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons URIs can be mapped to add-on IDs
+//
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+
+function TestProvider(result) {
+ this.result = result;
+}
+TestProvider.prototype = {
+ uri: Services.io.newURI("hellow://world", null, null),
+ id: "valid@id",
+ startup: function() {},
+ shutdown: function() {},
+ mapURIToAddonID: function(aURI) {
+ if (aURI.spec === this.uri.spec) {
+ return this.id;
+ }
+ throw Components.Exception("Not mapped", result);
+ }
+};
+
+function TestProviderNoMap() {}
+TestProviderNoMap.prototype = {
+ startup: function() {},
+ shutdown: function() {}
+};
+
+function check_mapping(uri, id) {
+ do_check_eq(AddonManager.mapURIToAddonID(uri), id);
+ let svc = Components.classes["@mozilla.org/addons/integration;1"].
+ getService(Components.interfaces.amIAddonManager);
+ let val = {};
+ do_check_true(svc.mapURIToAddonID(uri, val));
+ do_check_eq(val.value, id);
+}
+
+function resetPrefs() {
+ Services.prefs.setIntPref("bootstraptest.active_version", -1);
+}
+
+function waitForPref(aPref, aCallback) {
+ function prefChanged() {
+ Services.prefs.removeObserver(aPref, prefChanged);
+ aCallback();
+ }
+ Services.prefs.addObserver(aPref, prefChanged, false);
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function run_test() {
+ do_test_pending();
+
+ resetPrefs();
+
+ run_test_early();
+}
+
+function run_test_early() {
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_1")], function() {
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ let uri = addon.getResourceURI(".");
+ let id = addon.id;
+ check_mapping(uri, id);
+
+ shutdownManager();
+
+ // Force an early call, to check that mappings will get correctly
+ // initialized when the manager actually starts up.
+ // See bug 957089
+
+ // First force-initialize the XPIProvider.
+ let s = Components.utils.import(
+ "resource://gre/modules/addons/XPIProvider.jsm", {});
+
+ // Make the early API call.
+ do_check_null(s.XPIProvider.mapURIToAddonID(uri));
+ do_check_null(AddonManager.mapURIToAddonID(uri));
+
+ // Actually start up the manager.
+ startupManager(false);
+
+ // Check that the mapping is there now.
+ check_mapping(uri, id);
+ do_check_eq(s.XPIProvider.mapURIToAddonID(uri), id);
+
+ run_test_nomapping();
+ });
+ });
+}
+
+function run_test_nomapping() {
+ do_check_eq(AddonManager.mapURIToAddonID(TestProvider.prototype.uri), null);
+ try {
+ let svc = Components.classes["@mozilla.org/addons/integration;1"].
+ getService(Components.interfaces.amIAddonManager);
+ let val = {};
+ do_check_false(svc.mapURIToAddonID(TestProvider.prototype.uri, val));
+ }
+ catch (ex) {
+ do_throw(ex);
+ }
+
+ run_test_1();
+}
+
+
+// Tests that add-on URIs are mappable after an install
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
+ ensure_test_completed();
+
+ let addon = install.addon;
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ let uri = addon.getResourceURI(".");
+ check_mapping(uri, addon.id);
+
+ waitForPref("bootstraptest.active_version", function() {
+ run_test_2(uri);
+ });
+ });
+ install.install();
+ });
+}
+
+// Tests that add-on URIs are still mappable, even after the add-on gets
+// disabled in-session.
+function run_test_2(uri) {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ do_check_true(newb1.userDisabled);
+ check_mapping(uri, newb1.id);
+
+ do_execute_soon(() => run_test_3(uri));
+ });
+ });
+}
+
+// Tests that add-on URIs are mappable if the add-on was never started in a
+// session
+function run_test_3(uri) {
+ restartManager();
+
+ check_mapping(uri, "bootstrap1@tests.mozilla.org");
+
+ run_test_4();
+}
+
+// Tests that add-on URIs are mappable after a restart + reenable
+function run_test_4() {
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ let uri = newb1.getResourceURI(".");
+ check_mapping(uri, newb1.id);
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Tests that add-on URIs are mappable after a restart
+function run_test_5() {
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ let uri = b1.getResourceURI(".");
+ check_mapping(uri, b1.id);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Tests that add-on URIs are mappable after being uninstalled
+function run_test_6() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ let uri = b1.getResourceURI(".");
+ b1.uninstall();
+ ensure_test_completed();
+
+ check_mapping(uri, b1.id);
+
+ restartManager();
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Tests that add-on URIs are mappable for add-ons detected at startup
+function run_test_7() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, "bootstrap1@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ let uri = b1.getResourceURI(".");
+ check_mapping(uri, b1.id);
+
+ do_execute_soon(run_test_invalidarg);
+ });
+}
+
+// Tests that the AddonManager will bail when mapURIToAddonID is called with an
+// invalid argument
+function run_test_invalidarg() {
+ restartManager();
+
+ let tests = [undefined,
+ null,
+ 1,
+ "string",
+ "chrome://global/content/",
+ function() {}
+ ];
+ for (var test of tests) {
+ try {
+ AddonManager.mapURIToAddonID(test);
+ throw new Error("Shouldn't be able to map the URI in question");
+ }
+ catch (ex if ex.result) {
+ do_check_eq(ex.result, Components.results.NS_ERROR_INVALID_ARG);
+ }
+ catch (ex) {
+ do_throw(ex);
+ }
+ }
+
+ run_test_provider();
+}
+
+// Tests that custom providers are correctly handled
+function run_test_provider() {
+ restartManager();
+
+ const provider = new TestProvider(Components.results.NS_ERROR_NOT_AVAILABLE);
+ AddonManagerPrivate.registerProvider(provider);
+
+ check_mapping(provider.uri, provider.id);
+
+ let u2 = provider.uri.clone();
+ u2.path = "notmapped";
+ do_check_eq(AddonManager.mapURIToAddonID(u2), null);
+
+ AddonManagerPrivate.unregisterProvider(provider);
+
+ run_test_provider_nomap();
+}
+
+// Tests that custom providers are correctly handled, even not implementing
+// mapURIToAddonID
+function run_test_provider_nomap() {
+ restartManager();
+
+ const provider = new TestProviderNoMap();
+ AddonManagerPrivate.registerProvider(provider);
+
+ do_check_eq(AddonManager.mapURIToAddonID(TestProvider.prototype.uri), null);
+
+ AddonManagerPrivate.unregisterProvider(provider);
+
+ do_test_finished();
+}
+
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_metadata_update.js b/toolkit/mozapps/extensions/test/xpcshell/test_metadata_update.js
new file mode 100644
index 000000000..a87b3f45b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_metadata_update.js
@@ -0,0 +1,184 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 whether we need to block start-up to check add-on compatibility,
+ * based on how long since the last succesful metadata ping.
+ * All tests depend on having one add-on installed in the profile
+ */
+
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI";
+
+// Constants copied from AddonRepository.jsm
+const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
+const PREF_METADATA_UPDATETHRESHOLD_SEC = "extensions.getAddons.cache.updateThreshold";
+const DEFAULT_METADATA_UPDATETHRESHOLD_SEC = 172800; // two days
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// None of this works without the add-on repository cache
+Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver;
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// This will be called to show the compatibility update dialog.
+var WindowWatcher = {
+ expected: false,
+ arguments: null,
+
+ openWindow: function(parent, url, name, features, args) {
+ do_check_true(Services.startup.interrupted);
+ do_check_eq(url, URI_EXTENSION_UPDATE_DIALOG);
+ do_check_true(this.expected);
+ this.expected = false;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+var WindowWatcherFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return WindowWatcher.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+ "Fake Window Watcher",
+ "@mozilla.org/embedcomp/window-watcher;1", WindowWatcherFactory);
+
+// Return Date.now() in seconds, rounded
+function now() {
+ return Math.round(Date.now() / 1000);
+}
+
+// First time with a new profile, so we don't have a cache.lastUpdate pref
+add_task(function* checkFirstMetadata() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+ const BASE_URL = "http://localhost:" + gPort;
+ const GETADDONS_RESULTS = BASE_URL + "/data/test_AddonRepository_cache.xml";
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ // Put an add-on in our profile so the metadata check will have something to do
+ var min1max2 = {
+ id: "min1max2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon compatible with v1->v2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ };
+ writeInstallRDFForExtension(min1max2, profileDir);
+
+ startupManager();
+
+ // Make sure that updating metadata for the first time sets the lastUpdate preference
+ yield AddonRepository.repopulateCache();
+ do_print("Update done, getting last update");
+ let lastUpdate = Services.prefs.getIntPref(PREF_METADATA_LASTUPDATE);
+ do_check_true(lastUpdate > 0);
+
+ // Make sure updating metadata again updates the preference
+ let oldUpdate = lastUpdate - 2 * DEFAULT_METADATA_UPDATETHRESHOLD_SEC;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, oldUpdate);
+ yield AddonRepository.repopulateCache();
+ do_check_neq(oldUpdate, Services.prefs.getIntPref(PREF_METADATA_LASTUPDATE));
+});
+
+// First upgrade with no lastUpdate pref and no add-ons changing shows UI
+add_task(function* upgrade_no_lastupdate() {
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("2");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Upgrade with lastUpdate more than default threshold and no add-ons changing shows UI
+add_task(function* upgrade_old_lastupdate() {
+ let oldEnough = now() - DEFAULT_METADATA_UPDATETHRESHOLD_SEC - 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, oldEnough);
+
+ WindowWatcher.expected = true;
+ // upgrade, downgrade, it has the same effect on the code path under test
+ yield promiseRestartManager("1");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Upgrade with lastUpdate less than default threshold and no add-ons changing doesn't show
+add_task(function* upgrade_young_lastupdate() {
+ let notOldEnough = now() - DEFAULT_METADATA_UPDATETHRESHOLD_SEC + 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, notOldEnough);
+
+ WindowWatcher.expected = false;
+ yield promiseRestartManager("2");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Repeat more-than and less-than but with updateThreshold preference set
+// Upgrade with lastUpdate more than pref threshold and no add-ons changing shows UI
+const TEST_UPDATETHRESHOLD_SEC = 50000;
+add_task(function* upgrade_old_pref_lastupdate() {
+ Services.prefs.setIntPref(PREF_METADATA_UPDATETHRESHOLD_SEC, TEST_UPDATETHRESHOLD_SEC);
+
+ let oldEnough = now() - TEST_UPDATETHRESHOLD_SEC - 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, oldEnough);
+
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("1");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Upgrade with lastUpdate less than pref threshold and no add-ons changing doesn't show
+add_task(function* upgrade_young_pref_lastupdate() {
+ let notOldEnough = now() - TEST_UPDATETHRESHOLD_SEC + 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, notOldEnough);
+
+ WindowWatcher.expected = false;
+ yield promiseRestartManager("2");
+ do_check_false(WindowWatcher.expected);
+});
+
+
+
+add_task(function* cleanup() {
+ return new Promise((resolve, reject) => {
+ testserver.stop(resolve);
+ });
+});
+
+function run_test() {
+ run_next_test();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
new file mode 100644
index 000000000..b3cae5283
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
@@ -0,0 +1,250 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from the old rdf style database
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ type: 4,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ type: 4,
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ let stagedXPIs = profileDir.clone();
+ stagedXPIs.append("staged-xpis");
+ stagedXPIs.append("addon6@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ let addon6 = do_get_addon("test_migrate6");
+ addon6.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ stagedXPIs.append("addon7@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ let addon7 = do_get_addon("test_migrate7");
+ addon7.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ stagedXPIs.append("addon8@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ let addon8 = do_get_addon("test_migrate8");
+ addon8.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ let old = do_get_file("data/test_migrate.rdf");
+ old.copyTo(gProfD, "extensions.rdf");
+
+ let oldCache = gProfD.clone();
+ oldCache.append("extensions.cache");
+ oldCache.create(AM_Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+
+ // Theme state is determined by the selected theme pref
+ Services.prefs.setCharPref("general.skins.selectedSkin", "theme1/1.0");
+
+ Services.prefs.setCharPref("extensions.lastAppVersion", "1");
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ do_check_false(oldCache.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, a8, t1,
+ t2]) {
+ // addon1 was user and app enabled in the old extensions.rdf
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(a1.hasBinaryComponents);
+
+ // addon2 was user disabled and app enabled in the old extensions.rdf
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(a2.hasBinaryComponents);
+
+ // addon3 was pending user disable and app disabled in the old extensions.rdf
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_false(a3.hasBinaryComponents);
+
+ // addon4 was pending user enable and app disabled in the old extensions.rdf
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_false(a4.hasBinaryComponents);
+
+ // addon5 was disabled and compatible but a new version has been installed
+ // since, it should still be disabled but should be incompatible
+ do_check_neq(a5, null);
+ do_check_true(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+ do_check_false(a5.hasBinaryComponents);
+
+ // addon6 should be installed and compatible and packed unless unpacking is
+ // forced
+ do_check_neq(a6, null);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack"))
+ do_check_eq(a6.getResourceURI("install.rdf").scheme, "file");
+ else
+ do_check_eq(a6.getResourceURI("install.rdf").scheme, "jar");
+ do_check_false(a6.hasBinaryComponents);
+
+ // addon7 should be installed and compatible and unpacked
+ do_check_neq(a7, null);
+ do_check_false(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a7.id));
+ do_check_eq(a7.getResourceURI("install.rdf").scheme, "file");
+ do_check_false(a7.hasBinaryComponents);
+
+ // addon8 should be installed and compatible and have binary components
+ do_check_neq(a8, null);
+ do_check_false(a8.userDisabled);
+ do_check_false(a8.appDisabled);
+ do_check_true(a8.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a8.id));
+ do_check_true(a8.hasBinaryComponents);
+
+ // Theme 1 was previously enabled
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ // Theme 2 was previously disabled
+ do_check_neq(t1, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t2.id));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(stagedXPIs.exists());
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js
new file mode 100644
index 000000000..c213bace7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js
@@ -0,0 +1,259 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from SQLITE databases
+// Note that since the database doesn't contain the foreignInstall field we
+// should just assume that no add-ons in the user profile were foreignInstalls
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ strictCompatibility: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }]
+};
+
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }]
+};
+
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 7",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon8 = {
+ id: "addon8@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 8",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const globalDir = gProfD.clone();
+globalDir.append("extensions2");
+globalDir.append(gAppInfo.ID);
+registerDirectory("XRESysSExtPD", globalDir.parent);
+const userDir = gProfD.clone();
+userDir.append("extensions3");
+userDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userDir.parent);
+
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, globalDir);
+ writeInstallRDFForExtension(addon8, userDir);
+
+ // Write out a minimal database
+ let dbfile = gProfD.clone();
+ dbfile.append("extensions.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT, location TEXT, version TEXT, active INTEGER, " +
+ "userDisabled INTEGER, installDate INTEGER");
+ db.createTable("targetApplication", "addon_internal_id INTEGER, " +
+ "id TEXT, minVersion TEXT, maxVersion TEXT");
+ let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " +
+ ":version, :active, :userDisabled, :installDate)");
+
+ let internal_ids = {};
+
+ [["addon1@tests.mozilla.org", "app-profile", "1.0", "1", "0", "0"],
+ ["addon2@tests.mozilla.org", "app-profile", "2.0", "0", "1", "0"],
+ ["addon3@tests.mozilla.org", "app-profile", "2.0", "1", "1", "0"],
+ ["addon4@tests.mozilla.org", "app-profile", "2.0", "0", "0", "0"],
+ ["addon5@tests.mozilla.org", "app-profile", "2.0", "1", "0", "0"],
+ ["addon6@tests.mozilla.org", "app-profile", "1.0", "0", "1", "0"],
+ ["addon7@tests.mozilla.org", "app-system-share", "1.0", "1", "0", "0"],
+ ["addon8@tests.mozilla.org", "app-system-user", "1.0", "1", "0", "0"]].forEach(function(a) {
+ stmt.params.id = a[0];
+ stmt.params.location = a[1];
+ stmt.params.version = a[2];
+ stmt.params.active = a[3];
+ stmt.params.userDisabled = a[4];
+ stmt.params.installDate = a[5];
+ stmt.execute();
+ internal_ids[a[0]] = db.lastInsertRowID;
+ });
+ stmt.finalize();
+
+ // Add updated target application into for addon5
+ stmt = db.createStatement("INSERT INTO targetApplication VALUES " +
+ "(:internal_id, :id, :minVersion, :maxVersion)");
+ stmt.params.internal_id = internal_ids["addon5@tests.mozilla.org"];
+ stmt.params.id = "xpcshell@tests.mozilla.org";
+ stmt.params.minVersion = "0";
+ stmt.params.maxVersion = "1";
+ stmt.execute();
+
+ // Add updated target application into for addon6
+ stmt.params.internal_id = internal_ids["addon6@tests.mozilla.org"];
+ stmt.params.id = "xpcshell@tests.mozilla.org";
+ stmt.params.minVersion = "0";
+ stmt.params.maxVersion = "1";
+ stmt.execute();
+ stmt.finalize();
+
+ db.schemaVersion = 10000;
+ Services.prefs.setIntPref("extensions.databaseSchema", 14);
+ db.close();
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8]) {
+ // addon1 was enabled in the database
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_false(a1.strictCompatibility);
+ do_check_false(a1.foreignInstall);
+ // addon2 was disabled in the database
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(a2.strictCompatibility);
+ do_check_false(a2.foreignInstall);
+ // addon3 was pending-disable in the database
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(a3.strictCompatibility);
+ do_check_false(a3.foreignInstall);
+ // addon4 was pending-enable in the database
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.isActive);
+ do_check_true(a4.strictCompatibility);
+ do_check_false(a4.foreignInstall);
+ // addon5 was enabled in the database but needed a compatibility update
+ do_check_neq(a5, null);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isActive);
+ do_check_false(a5.strictCompatibility);
+ do_check_false(a5.foreignInstall);
+ // addon6 was disabled and compatible but a new version has been installed
+ // since, it should still be disabled but should be incompatible
+ do_check_neq(a6, null);
+ do_check_true(a6.userDisabled);
+ do_check_true(a6.appDisabled);
+ do_check_false(a6.isActive);
+ do_check_false(a6.strictCompatibility);
+ do_check_false(a6.foreignInstall);
+ // addon7 is in the global install location so should be a foreignInstall
+ do_check_neq(a7, null);
+ do_check_false(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isActive);
+ do_check_false(a7.strictCompatibility);
+ do_check_true(a7.foreignInstall);
+ // addon8 is in the user install location so should be a foreignInstall
+ do_check_neq(a8, null);
+ do_check_false(a8.userDisabled);
+ do_check_false(a8.appDisabled);
+ do_check_true(a8.isActive);
+ do_check_false(a8.strictCompatibility);
+ do_check_true(a8.foreignInstall);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js
new file mode 100644
index 000000000..f27d53c3f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js
@@ -0,0 +1,239 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from the old extensions.rdf database. This
+// matches test_migrate1.js however it runs with a lightweight theme selected
+// so the themes should appear disabled.
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ type: 4,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ type: 4,
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Cannot use the LightweightThemeManager before AddonManager has been started
+ // so inject the correct prefs
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify([{
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ }]));
+ Services.prefs.setBoolPref("lightweightThemes.isThemeSelected", true);
+
+ let stagedXPIs = profileDir.clone();
+ stagedXPIs.append("staged-xpis");
+ stagedXPIs.append("addon6@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+
+ let addon6 = do_get_addon("test_migrate6");
+ addon6.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ stagedXPIs.append("addon7@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+
+ let addon7 = do_get_addon("test_migrate7");
+ addon7.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ let old = do_get_file("data/test_migrate.rdf");
+ old.copyTo(gProfD, "extensions.rdf");
+
+ // Theme state is determined by the selected theme pref
+ Services.prefs.setCharPref("general.skins.selectedSkin", "theme1/1.0");
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ // addon1 was user and app enabled in the old extensions.rdf
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // addon2 was user disabled and app enabled in the old extensions.rdf
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ // addon3 was pending user disable and app disabled in the old extensions.rdf
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ // addon4 was pending user enable and app disabled in the old extensions.rdf
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ // addon5 was disabled and compatible but a new version has been installed
+ // since, it should still be disabled but should be incompatible
+ do_check_neq(a5, null);
+ do_check_true(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ // addon6 should be installed and compatible and packed unless unpacking is
+ // forced
+ do_check_neq(a6, null);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack"))
+ do_check_eq(a6.getResourceURI("install.rdf").scheme, "file");
+ else
+ do_check_eq(a6.getResourceURI("install.rdf").scheme, "jar");
+
+ // addon7 should be installed and compatible and unpacked
+ do_check_neq(a7, null);
+ do_check_false(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a7.id));
+ do_check_eq(a7.getResourceURI("install.rdf").scheme, "file");
+
+ // Theme 1 was previously disabled
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ // Theme 2 was previously disabled
+ do_check_neq(t1, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t2.id));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(stagedXPIs.exists());
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
new file mode 100644
index 000000000..b2903ead7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
@@ -0,0 +1,307 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from a previous version of the JSON database
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_migrate4.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ strictCompatibility: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ updateURL: "http://localhost:" + gPort + "/data/test_migrate4.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }]
+};
+
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ updateURL: "http://localhost:" + gPort + "/data/test_migrate4.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }]
+};
+
+var defaultTheme = {
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+let oldSyncGUIDs = {};
+
+function prepare_profile() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(defaultTheme, profileDir);
+
+ startupManager();
+ installAllFiles([do_get_addon("test_migrate8"), do_get_addon("test_migrate9")],
+ function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon9@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a9]) {
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+ a2.userDisabled = true;
+ a2.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ a3.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ a4.userDisabled = true;
+ a6.userDisabled = true;
+ a9.userDisabled = false;
+
+ for each (let addon in [a1, a2, a3, a4, a5, a6]) {
+ oldSyncGUIDs[addon.id] = addon.syncGUID;
+ }
+
+ a6.findUpdates({
+ onUpdateAvailable: function(aAddon, aInstall6) {
+ AddonManager.getInstallForURL("http://localhost:" + gPort + "/addons/test_migrate4_7.xpi", function(aInstall7) {
+ completeAllInstalls([aInstall6, aInstall7], function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6]) {
+ a3.userDisabled = true;
+ a4.userDisabled = false;
+
+ a5.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(perform_migration);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+ }, "application/x-xpinstall");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
+
+function perform_migration() {
+ shutdownManager();
+
+ // Turn on disabling for all scopes
+ Services.prefs.setIntPref("extensions.autoDisableScopes", 15);
+
+ changeXPIDBVersion(1);
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ gAppInfo.version = "2"
+ startupManager(true);
+ test_results();
+}
+
+function test_results() {
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9]) {
+ // addon1 was enabled
+ do_check_neq(a1, null);
+ do_check_eq(a1.syncGUID, oldSyncGUIDs[a1.id]);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a1.foreignInstall);
+ do_check_false(a1.hasBinaryComponents);
+ do_check_false(a1.strictCompatibility);
+
+ // addon2 was disabled
+ do_check_neq(a2, null);
+ do_check_eq(a2.syncGUID, oldSyncGUIDs[a2.id]);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_eq(a2.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
+ do_check_true(a2.foreignInstall);
+ do_check_false(a2.hasBinaryComponents);
+ do_check_false(a2.strictCompatibility);
+
+ // addon3 was pending-disable in the database
+ do_check_neq(a3, null);
+ do_check_eq(a3.syncGUID, oldSyncGUIDs[a3.id]);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_eq(a3.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE);
+ do_check_true(a3.foreignInstall);
+ do_check_false(a3.hasBinaryComponents);
+ do_check_false(a3.strictCompatibility);
+
+ // addon4 was pending-enable in the database
+ do_check_neq(a4, null);
+ do_check_eq(a4.syncGUID, oldSyncGUIDs[a4.id]);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.isActive);
+ do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a4.foreignInstall);
+ do_check_false(a4.hasBinaryComponents);
+ do_check_true(a4.strictCompatibility);
+
+ // addon5 was enabled in the database but needed a compatibility update
+ do_check_neq(a5, null);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isActive);
+ do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a5.foreignInstall);
+ do_check_false(a5.hasBinaryComponents);
+ do_check_false(a5.strictCompatibility);
+
+ // addon6 was disabled and compatible but a new version has been installed
+ do_check_neq(a6, null);
+ do_check_eq(a6.syncGUID, oldSyncGUIDs[a6.id]);
+ do_check_eq(a6.version, "2.0");
+ do_check_true(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.isActive);
+ do_check_eq(a6.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a6.foreignInstall);
+ do_check_eq(a6.sourceURI.spec, "http://localhost:" + gPort + "/addons/test_migrate4_6.xpi");
+ do_check_eq(a6.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ do_check_false(a6.hasBinaryComponents);
+ do_check_false(a6.strictCompatibility);
+
+ // addon7 was installed manually
+ do_check_neq(a7, null);
+ do_check_eq(a7.version, "1.0");
+ do_check_false(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isActive);
+ do_check_eq(a7.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_false(a7.foreignInstall);
+ do_check_eq(a7.sourceURI.spec, "http://localhost:" + gPort + "/addons/test_migrate4_7.xpi");
+ do_check_eq(a7.releaseNotesURI, null);
+ do_check_false(a7.hasBinaryComponents);
+ do_check_false(a7.strictCompatibility);
+
+ // addon8 was enabled and has binary components
+ do_check_neq(a8, null);
+ do_check_false(a8.userDisabled);
+ do_check_false(a8.appDisabled);
+ do_check_true(a8.isActive);
+ do_check_false(a8.foreignInstall);
+ do_check_true(a8.hasBinaryComponents);
+ do_check_false(a8.strictCompatibility);
+
+ // addon9 is the active theme
+ do_check_neq(a9, null);
+ do_check_false(a9.userDisabled);
+ do_check_false(a9.appDisabled);
+ do_check_true(a9.isActive);
+ do_check_false(a9.foreignInstall);
+ do_check_false(a9.hasBinaryComponents);
+ do_check_true(a9.strictCompatibility);
+
+ testserver.stop(do_test_finished);
+ });
+}
+
+function run_test() {
+ do_test_pending();
+
+ prepare_profile();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js
new file mode 100644
index 000000000..0109dcf92
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we fail to migrate but still start up ok when there is a SQLITE database
+// with no useful data in it.
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }]
+};
+
+var defaultTheme = {
+ id: "default@tests.mozilla.org",
+ version: "2.0",
+ name: "Default theme",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test theme",
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(defaultTheme, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+
+ // Write out a broken database (no userDisabled field)
+ let dbfile = gProfD.clone();
+ dbfile.append("extensions.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT, location TEXT, version TEXT, active INTEGER, " +
+ "installDate INTEGER");
+ db.createTable("targetApplication", "addon_internal_id INTEGER, " +
+ "id TEXT, minVersion TEXT, maxVersion TEXT");
+ let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " +
+ ":version, :active, :installDate)");
+
+ let internal_ids = {};
+
+ [["addon1@tests.mozilla.org", "app-profile", "1.0", "1", "0"],
+ ["addon2@tests.mozilla.org", "app-profile", "2.0", "0", "0"],
+ ["default@tests.mozilla.org", "app-profile", "2.0", "1", "0"],
+ ["theme1@tests.mozilla.org", "app-profile", "2.0", "0", "0"]].forEach(function(a) {
+ stmt.params.id = a[0];
+ stmt.params.location = a[1];
+ stmt.params.version = a[2];
+ stmt.params.active = a[3];
+ stmt.params.installDate = a[4];
+ stmt.execute();
+ internal_ids[a[0]] = db.lastInsertRowID;
+ });
+ stmt.finalize();
+
+ db.schemaVersion = 100;
+ Services.prefs.setIntPref("extensions.databaseSchema", 100);
+ db.close();
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"],
+ function([a1, a2, d, t1]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.appDisabled);
+ do_check_false(a2.isActive);
+
+ // Should have enabled the selected theme
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+
+ do_check_neq(d, null);
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js
new file mode 100644
index 000000000..ad8bd5bca
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js
@@ -0,0 +1,127 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const EXPECTED_SCHEMA_VERSION = 4;
+let dbfile;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Write out a minimal database.
+ dbfile = gProfD.clone();
+ dbfile.append("addons.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+
+ db.createTable("addon",
+ "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT UNIQUE, " +
+ "type TEXT, " +
+ "name TEXT, " +
+ "version TEXT, " +
+ "creator TEXT, " +
+ "creatorURL TEXT, " +
+ "description TEXT, " +
+ "fullDescription TEXT, " +
+ "developerComments TEXT, " +
+ "eula TEXT, " +
+ "iconURL TEXT, " +
+ "homepageURL TEXT, " +
+ "supportURL TEXT, " +
+ "contributionURL TEXT, " +
+ "contributionAmount TEXT, " +
+ "averageRating INTEGER, " +
+ "reviewCount INTEGER, " +
+ "reviewURL TEXT, " +
+ "totalDownloads INTEGER, " +
+ "weeklyDownloads INTEGER, " +
+ "dailyUsers INTEGER, " +
+ "sourceURI TEXT, " +
+ "repositoryStatus INTEGER, " +
+ "size INTEGER, " +
+ "updateDate INTEGER");
+
+ db.createTable("developer",
+ "addon_internal_id INTEGER, " +
+ "num INTEGER, " +
+ "name TEXT, " +
+ "url TEXT, " +
+ "PRIMARY KEY (addon_internal_id, num)");
+
+ db.createTable("screenshot",
+ "addon_internal_id INTEGER, " +
+ "num INTEGER, " +
+ "url TEXT, " +
+ "thumbnailURL TEXT, " +
+ "caption TEXT, " +
+ "PRIMARY KEY (addon_internal_id, num)");
+
+ let stmt = db.createStatement("INSERT INTO addon (id) VALUES (:id)");
+ stmt.params.id = "test1@tests.mozilla.org";
+ stmt.execute();
+ stmt.finalize();
+
+ stmt = db.createStatement("INSERT INTO screenshot VALUES " +
+ "(:addon_internal_id, :num, :url, :thumbnailURL, :caption)");
+
+ stmt.params.addon_internal_id = 1;
+ stmt.params.num = 0;
+ stmt.params.url = "http://localhost/full1-1.png";
+ stmt.params.thumbnailURL = "http://localhost/thumbnail1-1.png";
+ stmt.params.caption = "Caption 1 - 1";
+ stmt.execute();
+ stmt.finalize();
+
+ db.schemaVersion = 1;
+ db.close();
+
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ AddonRepository.getCachedAddonByID("test1@tests.mozilla.org", function (aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.screenshots.length, 1);
+ do_check_true(aAddon.screenshots[0].width === null);
+ do_check_true(aAddon.screenshots[0].height === null);
+ do_check_true(aAddon.screenshots[0].thumbnailWidth === null);
+ do_check_true(aAddon.screenshots[0].thumbnailHeight === null);
+ do_check_eq(aAddon.iconURL, undefined);
+ do_check_eq(JSON.stringify(aAddon.icons), "{}");
+ AddonRepository.shutdown().then(
+ function checkAfterRepoShutdown() {
+ // Check the DB schema has changed once AddonRepository has freed it.
+ db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
+ do_check_true(db.indexExists("developer_idx"));
+ do_check_true(db.indexExists("screenshot_idx"));
+ do_check_true(db.indexExists("compatibility_override_idx"));
+ do_check_true(db.tableExists("compatibility_override"));
+ do_check_true(db.indexExists("icon_idx"));
+ do_check_true(db.tableExists("icon"));
+
+ // Check the trigger is working
+ db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
+ let internalID = db.lastInsertRowID;
+ db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
+
+ let stmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
+ stmt.executeStep();
+ do_check_eq(stmt.row.count, 1);
+ stmt.reset();
+
+ db.executeSimpleSQL("DELETE FROM addon");
+ stmt.executeStep();
+ do_check_eq(stmt.row.count, 0);
+ stmt.finalize();
+
+ db.close();
+ do_test_finished();
+ },
+ do_report_unexpected_exception
+ );
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate_max_version.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate_max_version.js
new file mode 100644
index 000000000..133c3a199
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate_max_version.js
@@ -0,0 +1,103 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we don't migrate data from SQLITE if
+// the "extensions.databaseSchema" preference shows we've
+// already upgraded to JSON
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ // Write out a minimal database
+ let dbfile = gProfD.clone();
+ dbfile.append("extensions.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT, location TEXT, version TEXT, active INTEGER, " +
+ "userDisabled INTEGER, installDate INTEGER");
+ db.createTable("targetApplication", "addon_internal_id INTEGER, " +
+ "id TEXT, minVersion TEXT, maxVersion TEXT");
+ let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " +
+ ":version, :active, :userDisabled, :installDate)");
+
+ let internal_ids = {};
+
+ let a = ["addon1@tests.mozilla.org", "app-profile", "1.0", "0", "1", "0"];
+ stmt.params.id = a[0];
+ stmt.params.location = a[1];
+ stmt.params.version = a[2];
+ stmt.params.active = a[3];
+ stmt.params.userDisabled = a[4];
+ stmt.params.installDate = a[5];
+ stmt.execute();
+ internal_ids[a[0]] = db.lastInsertRowID;
+ stmt.finalize();
+
+ db.schemaVersion = 14;
+ Services.prefs.setIntPref("extensions.databaseSchema", 14);
+ db.close();
+
+ startupManager();
+ run_next_test();
+}
+
+add_test(function before_rebuild() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org",
+ function check_before_rebuild (a1) {
+ // First check that it migrated OK once
+ // addon1 was disabled in the database
+ do_check_neq(a1, null);
+ do_check_true(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(a1.strictCompatibility);
+ do_check_false(a1.foreignInstall);
+
+ run_next_test();
+ });
+});
+
+// now shut down, remove the JSON database,
+// start up again, and make sure the data didn't migrate this time
+add_test(function rebuild_again() {
+ shutdownManager();
+ gExtensionsJSON.remove(true);
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org",
+ function check_after_rebuild(a1) {
+ // addon1 was rebuilt from extensions directory,
+ // so it appears enabled as a foreign install
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_false(a1.strictCompatibility);
+ do_check_true(a1.foreignInstall);
+
+ run_next_test();
+ });
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js b/toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js
new file mode 100644
index 000000000..ab5a976cc
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js
@@ -0,0 +1,118 @@
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer;
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+function build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible) {
+ return function* () {
+ dump("Running test" +
+ " multiprocessCompatible: " + multiprocessCompatible +
+ " bootstrap: " + bootstrap +
+ " updateMultiprocessCompatible: " + updateMultiprocessCompatible +
+ "\n");
+
+ let addonData = {
+ id: "addon@tests.mozilla.org",
+ name: "Test Add-on",
+ version: "1.0",
+ multiprocessCompatible,
+ bootstrap,
+ updateURL: "http://localhost:" + gPort + "/updaterdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+ }
+
+ gServer.registerPathHandler("/updaterdf", function(request, response) {
+ let updateData = {};
+ updateData[addonData.id] = [{
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+ }];
+
+ if (updateMultiprocessCompatible !== undefined) {
+ updateData[addonData.id][0].multiprocessCompatible = updateMultiprocessCompatible;
+ }
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.write(createUpdateRDF(updateData));
+ });
+
+ let expectedMPC = updateMultiprocessCompatible === undefined ?
+ multiprocessCompatible :
+ updateMultiprocessCompatible;
+
+ let xpifile = createTempXPIFile(addonData);
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(xpifile, resolve));
+ do_check_eq(install.addon.multiprocessCompatible, multiprocessCompatible);
+ yield promiseCompleteAllInstalls([install]);
+
+ if (!bootstrap) {
+ yield promiseRestartManager();
+ do_check_true(isExtensionInAddonsList(profileDir, addonData.id));
+ do_check_eq(isItemMarkedMPIncompatible(addonData.id), !multiprocessCompatible);
+ }
+
+ let addon = yield promiseAddonByID(addonData.id);
+ do_check_neq(addon, null);
+ do_check_eq(addon.multiprocessCompatible, multiprocessCompatible);
+
+ yield promiseFindAddonUpdates(addon);
+
+ // Should have applied the compatibility change
+ do_check_eq(addon.multiprocessCompatible, expectedMPC);
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(addonData.id);
+ // Should have persisted the compatibility change
+ do_check_eq(addon.multiprocessCompatible, expectedMPC);
+ if (!bootstrap) {
+ do_check_true(isExtensionInAddonsList(profileDir, addonData.id));
+ do_check_eq(isItemMarkedMPIncompatible(addonData.id), !multiprocessCompatible);
+ }
+
+ addon.uninstall();
+ yield promiseRestartManager();
+
+ gServer.registerPathHandler("/updaterdf", null);
+ }
+}
+
+/* Builds a set of tests to run the same steps for every combination of:
+ * The add-on being restartless
+ * The initial add-on supporting multiprocess
+ * The update saying the add-on should or should not support multiprocess (or not say anything at all)
+ */
+for (let bootstrap of [false, true]) {
+ for (let multiprocessCompatible of [false, true]) {
+ for (let updateMultiprocessCompatible of [undefined, false, true]) {
+ add_task(build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible));
+ }
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ startupManager();
+
+ // Create and configure the HTTP server.
+ gServer = new HttpServer();
+ gServer.registerDirectory("/data/", gTmpD);
+ gServer.start(-1);
+ gPort = gServer.identity.primaryPort;
+
+ run_next_test();
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_no_addons.js b/toolkit/mozapps/extensions/test/xpcshell/test_no_addons.js
new file mode 100644
index 000000000..ae75fbb43
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_no_addons.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test startup and restart when no add-ons are installed
+// bug 944006
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// Load XPI Provider to get schema version ID
+let XPIScope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+const DB_SCHEMA = XPIScope.DB_SCHEMA;
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+ // Kick off the task-based tests...
+ run_next_test();
+}
+
+// Test for a preference to either exist with a specified value, or not exist at all
+function checkPending() {
+ try {
+ do_check_false(Services.prefs.getBoolPref("extensions.pendingOperations"));
+ }
+ catch (e) {
+ // OK
+ }
+}
+
+function checkString(aPref, aValue) {
+ try {
+ do_check_eq(Services.prefs.getCharPref(aPref), aValue)
+ }
+ catch (e) {
+ //OK
+ }
+}
+
+// Make sure all our extension state is empty/nonexistent
+function check_empty_state() {
+ do_check_false(gExtensionsJSON.exists());
+ do_check_false(gExtensionsINI.exists());
+
+ do_check_eq(Services.prefs.getIntPref("extensions.databaseSchema"), DB_SCHEMA);
+
+ checkString("extensions.bootstrappedAddons", "{}");
+ checkString("extensions.installCache", "[]");
+ checkPending();
+}
+
+// After first run with no add-ons, we expect:
+// no extensions.json is created
+// no extensions.ini
+// database schema version preference is set
+// bootstrap add-ons preference is not found
+// add-on directory state preference is an empty array
+// no pending operations
+add_task(function first_run() {
+ startupManager();
+ check_empty_state();
+ yield true;
+});
+
+// Now do something that causes a DB load, and re-check
+function trigger_db_load() {
+ let addonDefer = Promise.defer();
+ AddonManager.getAddonsByTypes(['extension'], addonDefer.resolve);
+ let addonList = yield addonDefer.promise;
+
+ do_check_eq(addonList.length, 0);
+ check_empty_state();
+
+ yield true;
+};
+add_task(trigger_db_load);
+
+// Now restart the manager and check again
+add_task(function restart_and_recheck() {
+ restartManager();
+ check_empty_state();
+ yield true;
+});
+
+// and reload the DB again
+add_task(trigger_db_load);
+
+// When we start up with no DB and an old database schema, we should update the
+// schema number but not create a database
+add_task(function upgrade_schema_version() {
+ shutdownManager();
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ startupManager();
+ do_check_eq(Services.prefs.getIntPref("extensions.databaseSchema"), DB_SCHEMA);
+ check_empty_state();
+});
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js b/toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
new file mode 100644
index 000000000..f9b7da073
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.strictCompatibility = false;
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ aAddon.userDisabled = true;
+ do_execute_soon(run_test_1);
+ });
+}
+
+function run_test_1() {
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_true(aAddon.userDisabled);
+ do_check_false(aAddon.isActive);
+ do_check_false(aAddon.appDisabled);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["appDisabled"]]
+ ]
+ }, [], run_test_2);
+
+ AddonManager.strictCompatibility = true;
+ });
+}
+
+function run_test_2() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_true(aAddon.userDisabled);
+ do_check_false(aAddon.isActive);
+ do_check_true(aAddon.appDisabled);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["appDisabled"]]
+ ]
+ }, [], callback_soon(do_test_finished));
+
+ AddonManager.strictCompatibility = false;
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_overrideblocklist.js b/toolkit/mozapps/extensions/test/xpcshell/test_overrideblocklist.js
new file mode 100644
index 000000000..8a6bedea1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_overrideblocklist.js
@@ -0,0 +1,200 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const KEY_PROFILEDIR = "ProfD";
+const KEY_APPDIR = "XCurProcD";
+const FILE_BLOCKLIST = "blocklist.xml";
+
+const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
+
+const OLD = do_get_file("data/test_overrideblocklist/old.xml");
+const NEW = do_get_file("data/test_overrideblocklist/new.xml");
+const ANCIENT = do_get_file("data/test_overrideblocklist/ancient.xml");
+const OLD_TSTAMP = 1296046918000;
+const NEW_TSTAMP = 1396046918000;
+
+const gAppDir = FileUtils.getFile(KEY_APPDIR, []);
+
+let oldAddon = {
+ id: "old@tests.mozilla.org",
+ version: 1
+}
+let newAddon = {
+ id: "new@tests.mozilla.org",
+ version: 1
+}
+let ancientAddon = {
+ id: "ancient@tests.mozilla.org",
+ version: 1
+}
+let invalidAddon = {
+ id: "invalid@tests.mozilla.org",
+ version: 1
+}
+
+function incrementAppVersion() {
+ gAppInfo.version = "" + (parseInt(gAppInfo.version) + 1);
+}
+
+function clearBlocklists() {
+ let blocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+ if (blocklist.exists())
+ blocklist.remove(true);
+
+ blocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+ if (blocklist.exists())
+ blocklist.remove(true);
+}
+
+function reloadBlocklist() {
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, false);
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, true);
+}
+
+function copyToApp(file) {
+ file.clone().copyTo(gAppDir, FILE_BLOCKLIST);
+}
+
+function copyToProfile(file, tstamp) {
+ file = file.clone();
+ file.copyTo(gProfD, FILE_BLOCKLIST);
+ file = gProfD.clone();
+ file.append(FILE_BLOCKLIST);
+ file.lastModifiedTime = tstamp;
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+ if (appBlocklist.exists()) {
+ try {
+ appBlocklist.moveTo(gAppDir, "blocklist.old");
+ }
+ catch (e) {
+ todo(false, "Aborting test due to unmovable blocklist file: " + e);
+ return;
+ }
+ do_register_cleanup(function() {
+ clearBlocklists();
+ appBlocklist.moveTo(gAppDir, FILE_BLOCKLIST);
+ });
+ }
+
+ run_next_test();
+}
+
+// On first run whataver is in the app dir should get copied to the profile
+add_test(function test_copy() {
+ clearBlocklists();
+ copyToApp(OLD);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_true(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_false(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// An ancient blocklist should be ignored
+add_test(function test_ancient() {
+ clearBlocklists();
+ copyToApp(ANCIENT);
+ copyToProfile(OLD, OLD_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_true(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_false(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// A new blocklist should override an old blocklist
+add_test(function test_override() {
+ clearBlocklists();
+ copyToApp(NEW);
+ copyToProfile(OLD, OLD_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_false(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_true(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// An old blocklist shouldn't override a new blocklist
+add_test(function test_retain() {
+ clearBlocklists();
+ copyToApp(OLD);
+ copyToProfile(NEW, NEW_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_false(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_true(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// A missing blocklist in the profile should still load an app-shipped blocklist
+add_test(function test_missing() {
+ clearBlocklists();
+ copyToApp(OLD);
+ copyToProfile(NEW, NEW_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+ shutdownManager();
+
+ let blocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+ blocklist.remove(true);
+ startupManager(false);
+
+ reloadBlocklist();
+ blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_true(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_false(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js b/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js
new file mode 100644
index 000000000..11463768f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// Checks that permissions set in preferences are correctly imported but can
+// be removed by the user.
+
+const XPI_MIMETYPE = "application/x-xpinstall";
+
+function newPrincipal(uri) {
+ return Services.scriptSecurityManager.getNoAppCodebasePrincipal(NetUtil.newURI(uri));
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ Services.prefs.setCharPref("xpinstall.whitelist.add", "test1.com,test2.com");
+ Services.prefs.setCharPref("xpinstall.whitelist.add.36", "test3.com,www.test4.com");
+ Services.prefs.setCharPref("xpinstall.whitelist.add.test5", "test5.com");
+
+ Services.perms.add(NetUtil.newURI("https://www.test9.com"), "install",
+ AM_Ci.nsIPermissionManager.ALLOW_ACTION);
+
+ startupManager();
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://test1.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test1.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test2.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test3.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test4.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test4.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://www.test5.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test5.com")));
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://www.test6.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test6.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test7.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test8.com")));
+
+ // This should remain unaffected
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://www.test9.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test9.com")));
+
+ Services.perms.removeAll();
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test1.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test2.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test3.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test4.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test5.com")));
+
+ // Upgrade the application and verify that the permissions are still not there
+ restartManager("2");
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test1.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test2.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test3.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test4.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test5.com")));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js b/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js
new file mode 100644
index 000000000..ae1373214
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that xpinstall.[whitelist|blacklist].add preferences are emptied when
+// converted into permissions.
+
+const PREF_XPI_WHITELIST_PERMISSIONS = "xpinstall.whitelist.add";
+const PREF_XPI_BLACKLIST_PERMISSIONS = "xpinstall.blacklist.add";
+
+function newPrincipal(uri) {
+ return Services.scriptSecurityManager.getNoAppCodebasePrincipal(NetUtil.newURI(uri));
+}
+
+function do_check_permission_prefs(preferences) {
+ // Check preferences were emptied
+ for (let pref of preferences) {
+ try {
+ do_check_eq(Services.prefs.getCharPref(pref), "");
+ }
+ catch (e) {
+ // Successfully emptied
+ }
+ }
+}
+
+function clear_imported_preferences_cache() {
+ let scope = Components.utils.import("resource://gre/modules/PermissionsUtils.jsm", {});
+ scope.gImportedPrefBranches.clear();
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ // Create own preferences to test
+ Services.prefs.setCharPref("xpinstall.whitelist.add.EMPTY", "");
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST", "whitelist.example.com");
+ Services.prefs.setCharPref("xpinstall.blacklist.add.EMPTY", "");
+ Services.prefs.setCharPref("xpinstall.blacklist.add.TEST", "blacklist.example.com");
+
+ // Get list of preferences to check
+ var whitelistPreferences = Services.prefs.getChildList(PREF_XPI_WHITELIST_PERMISSIONS, {});
+ var blacklistPreferences = Services.prefs.getChildList(PREF_XPI_BLACKLIST_PERMISSIONS, {});
+ var preferences = whitelistPreferences.concat(blacklistPreferences);
+
+ startupManager();
+
+ // Permissions are imported lazily - act as thought we're checking an install,
+ // to trigger on-deman importing of the permissions.
+ AddonManager.isInstallAllowed("application/x-xpinstall", newPrincipal("http://example.com/file.xpi"));
+ do_check_permission_prefs(preferences);
+
+
+ // Import can also be triggerred by an observer notification by any other area
+ // of code, such as a permissions management UI.
+
+ // First, request to flush all permissions
+ clear_imported_preferences_cache();
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST2", "whitelist2.example.com");
+ Services.obs.notifyObservers(null, "flush-pending-permissions", "install");
+ do_check_permission_prefs(preferences);
+
+ // Then, request to flush just install permissions
+ clear_imported_preferences_cache();
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST3", "whitelist3.example.com");
+ Services.obs.notifyObservers(null, "flush-pending-permissions", "");
+ do_check_permission_prefs(preferences);
+
+ // And a request to flush some other permissions sholdn't flush install permissions
+ clear_imported_preferences_cache();
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST4", "whitelist4.example.com");
+ Services.obs.notifyObservers(null, "flush-pending-permissions", "lolcats");
+ do_check_eq(Services.prefs.getCharPref("xpinstall.whitelist.add.TEST4"), "whitelist4.example.com");
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_pluginBlocklistCtp.js b/toolkit/mozapps/extensions/test/xpcshell/test_pluginBlocklistCtp.js
new file mode 100644
index 000000000..8d7e944e2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_pluginBlocklistCtp.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/. */
+
+const nsIBLS = Components.interfaces.nsIBlocklistService;
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gBlocklistService = null;
+var gNotifier = null;
+var gNextTest = null;
+var gPluginHost = null;
+
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+mapFile("/data/test_pluginBlocklistCtp.xml", gServer);
+mapFile("/data/test_pluginBlocklistCtpUndo.xml", gServer);
+
+var PLUGINS = [{
+ // severity=0, vulnerabilitystatus=0 -> outdated
+ name: "test_plugin_0",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // severity=0, vulnerabilitystatus=1 -> update available
+ name: "test_plugin_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // severity=0, vulnerabilitystatus=2 -> no update
+ name: "test_plugin_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // no severity field -> severity=3 by default -> hardblock
+ name: "test_plugin_3",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // severity=1, vulnerabilitystatus=2 -> softblock
+ name: "test_plugin_4",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // not in the blocklist -> not blocked
+ name: "test_plugin_5",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}];
+
+function test_basic() {
+ var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_VULNERABLE_UPDATE_AVAILABLE);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_VULNERABLE_NO_UPDATE);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[4], "1", "1.9") == nsIBLS.STATE_SOFTBLOCKED);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[5], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+ gNextTest = test_is_not_clicktoplay;
+ do_execute_soon(gNextTest);
+}
+
+function get_test_plugin() {
+ var pluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
+ for (var plugin of pluginHost.getPluginTags()) {
+ if (plugin.name == "Test Plug-in")
+ return plugin;
+ }
+ do_check_true(false);
+ return null;
+}
+
+// At this time, the blocklist does not have an entry for the test plugin,
+// so it shouldn't be click-to-play.
+function test_is_not_clicktoplay() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtpUndo.xml");
+ gNextTest = test_is_clicktoplay;
+ gNotifier.notify(null);
+}
+
+// Here, we've updated the blocklist to have a block for the test plugin,
+// so it should be click-to-play.
+function test_is_clicktoplay() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_eq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtp.xml");
+ gNextTest = test_is_not_clicktoplay2;
+ gNotifier.notify(null);
+}
+
+// But now we've removed that entry from the blocklist (really we've gone back
+// to the old one), so the plugin shouldn't be click-to-play any more.
+function test_is_not_clicktoplay2() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtpUndo.xml");
+ gNextTest = test_disable_blocklist;
+ gNotifier.notify(null);
+}
+
+// Test that disabling the blocklist when a plugin is ctp-blocklisted will
+// result in the plugin not being click-to-play.
+function test_disable_blocklist() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_eq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ gNextTest = null;
+ Services.prefs.setBoolPref("extensions.blocklist.enabled", false);
+ blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+
+ // it should still be possible to make a plugin click-to-play via the pref
+ // and setting that plugin's enabled state to click-to-play
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+ let previousEnabledState = plugin.enabledState;
+ plugin.enabledState = Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY;
+ do_check_eq(gPluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY);
+ // clean up plugin state
+ plugin.enabledState = previousEnabledState;
+
+ gServer.stop(do_test_finished);
+}
+
+// Observe "blocklist-updated" so we know when to advance to the next test
+function observer() {
+ if (gNextTest)
+ do_execute_soon(gNextTest);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtp.xml");
+ startupManager();
+
+ gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
+ gBlocklistService = Components.classes["@mozilla.org/extensions/blocklist;1"].getService(Components.interfaces.nsIBlocklistService);
+ gNotifier = Components.classes["@mozilla.org/extensions/blocklist;1"].getService(Components.interfaces.nsITimerCallback);
+ Services.obs.addObserver(observer, "blocklist-updated", false);
+
+ do_register_cleanup(function() {
+ Services.prefs.clearUserPref("extensions.blocklist.url");
+ Services.prefs.clearUserPref("extensions.blocklist.enabled");
+ Services.prefs.clearUserPref("plugins.click_to_play");
+ Services.obs.removeObserver(observer, "blocklist-updated");
+ });
+
+ gNextTest = test_basic;
+ do_test_pending();
+ gNotifier.notify(null);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_pluginInfoURL.js b/toolkit/mozapps/extensions/test/xpcshell/test_pluginInfoURL.js
new file mode 100644
index 000000000..e140f021a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_pluginInfoURL.js
@@ -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/.
+ */
+
+const Ci = Components.interfaces;
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+/**
+ * MockPlugin mimics the behaviour of a plugin.
+ */
+function MockPlugin(name, version, enabledState) {
+ this.name = name;
+ this.version = version;
+ this.enabledState = enabledState;
+}
+
+MockPlugin.prototype = {
+ get blocklisted() {
+ let bls = Services.blocklist;
+ return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
+ },
+
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ }
+};
+
+// The mocked blocked plugin used to test the blocklist.
+const PLUGINS = [
+ new MockPlugin('test_with_infoURL', '5', Ci.nsIPluginTag.STATE_ENABLED),
+ new MockPlugin('test_with_altInfoURL', '5', Ci.nsIPluginTag.STATE_ENABLED),
+ new MockPlugin('test_no_infoURL', '5', Ci.nsIPluginTag.STATE_ENABLED)
+];
+
+/**
+ * The entry point of the unit tests, which is also responsible of
+ * copying the blocklist file to the profile folder.
+ */
+function run_test() {
+ copyBlocklistToProfile(do_get_file('data/pluginInfoURL_block.xml'));
+
+ createAppInfo('xpcshell@tests.mozilla.org', 'XPCShell', '3', '8');
+ startupManager();
+
+ run_next_test();
+}
+
+/**
+ * Test that the blocklist service correctly loads and returns the infoURL for
+ * a plugin that matches the first entry in the blocklist.
+ */
+add_task(function* test_infoURL() {
+ // The testInfoURL must match the value within the
+ // <infoURL> tag in pluginInfoURL_block.xml.
+ let testInfoURL = 'http://test.url.com/';
+
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[0]),
+ testInfoURL, 'Should be the provided url when an infoURL tag is available');
+});
+
+/**
+ * Test that the blocklist service correctly loads and returns the infoURL for
+ * a plugin that partially matches an earlier entry in the blocklist.
+ */
+add_task(function* test_altInfoURL() {
+ let altTestInfoURL = 'http://alt.test.url.com/';
+
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[1]),
+ altTestInfoURL, 'Should be the alternative infoURL');
+});
+
+/**
+ * Test that the blocklist service correctly returns null
+ * if the infoURL tag is missing in the blocklist.xml file.
+ */
+add_task(function* test_infoURL_missing() {
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[2]), null,
+ 'Should be null when no infoURL tag is available.');
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_pluginchange.js b/toolkit/mozapps/extensions/test/xpcshell/test_pluginchange.js
new file mode 100644
index 000000000..d3e33dac3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_pluginchange.js
@@ -0,0 +1,292 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const LIST_UPDATED_TOPIC = "plugins-list-updated";
+
+// We need to use the same algorithm for generating IDs for plugins
+var { getIDHashForString } = Components.utils.import("resource://gre/modules/addons/PluginProvider.jsm");
+
+function PluginTag(name, description) {
+ this.name = name;
+ this.description = description;
+}
+
+PluginTag.prototype = {
+ name: null,
+ description: null,
+ version: "1.0",
+ filename: null,
+ fullpath: null,
+ disabled: false,
+ blocklisted: false,
+ clicktoplay: false,
+
+ mimeTypes: [],
+
+ getMimeTypes: function(count) {
+ count.value = this.mimeTypes.length;
+ return this.mimeTypes;
+ }
+};
+
+PLUGINS = [
+ // A standalone plugin
+ new PluginTag("Java", "A mock Java plugin"),
+
+ // A plugin made up of two plugin files
+ new PluginTag("Flash", "A mock Flash plugin"),
+ new PluginTag("Flash", "A mock Flash plugin")
+];
+
+gPluginHost = {
+ // nsIPluginHost
+ getPluginTags: function(count) {
+ count.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIPluginHost])
+};
+
+var PluginHostFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return gPluginHost.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{aa6f9fef-cbe2-4d55-a2fa-dcf5482068b9}"), "PluginHost",
+ "@mozilla.org/plugin/host;1", PluginHostFactory);
+
+// This verifies that when the list of plugins changes the add-ons manager
+// correctly updates
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setBoolPref("media.gmp-provider.enabled", false);
+
+ startupManager();
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ run_test_1();
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+function sortAddons(addons) {
+ addons.sort(function(a, b) {
+ return a.name.localeCompare(b.name);
+ });
+}
+
+// Basic check that the mock object works
+function run_test_1() {
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Java");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_2();
+ });
+}
+
+// No change to the list should not trigger any events or changes in the API
+function run_test_2() {
+ // Reorder the list a bit
+ let tag = PLUGINS[0];
+ PLUGINS[0] = PLUGINS[2];
+ PLUGINS[2] = PLUGINS[1];
+ PLUGINS[1] = tag;
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Java");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_3();
+ });
+}
+
+// Tests that a newly detected plugin shows up in the API and sends out events
+function run_test_3() {
+ let tag = new PluginTag("Quicktime", "A mock Quicktime plugin");
+ PLUGINS.push(tag);
+ let id = getIDHashForString(tag.name + tag.description);
+
+ let test_params = {};
+ test_params[id] = [
+ ["onInstalling", false],
+ "onInstalled"
+ ];
+
+ prepare_test(test_params, [
+ "onExternalInstall"
+ ]);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 3);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Java");
+ do_check_false(addons[1].userDisabled);
+ do_check_eq(addons[2].name, "Quicktime");
+ do_check_false(addons[2].userDisabled);
+
+ run_test_4();
+ });
+}
+
+// Tests that a removed plugin disappears from in the API and sends out events
+function run_test_4() {
+ let tag = PLUGINS.splice(1, 1)[0];
+ let id = getIDHashForString(tag.name + tag.description);
+
+ let test_params = {};
+ test_params[id] = [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ];
+
+ prepare_test(test_params);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_5();
+ });
+}
+
+// Removing part of the flash plugin should have no effect
+function run_test_5() {
+ PLUGINS.splice(0, 1);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_6();
+ });
+}
+
+// Replacing flash should be detected
+function run_test_6() {
+ let oldTag = PLUGINS.splice(0, 1)[0];
+ let newTag = new PluginTag("Flash 2", "A new crash-free Flash!");
+ newTag.disabled = true;
+ PLUGINS.push(newTag);
+
+ let test_params = {};
+ test_params[getIDHashForString(oldTag.name + oldTag.description)] = [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ];
+ test_params[getIDHashForString(newTag.name + newTag.description)] = [
+ ["onInstalling", false],
+ "onInstalled"
+ ];
+
+ prepare_test(test_params, [
+ "onExternalInstall"
+ ]);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash 2");
+ do_check_true(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_7();
+ });
+}
+
+// If new tags are detected and the disabled state changes then we should send
+// out appropriate notifications
+function run_test_7() {
+ PLUGINS[0] = new PluginTag("Quicktime", "A mock Quicktime plugin");
+ PLUGINS[0].disabled = true;
+ PLUGINS[1] = new PluginTag("Flash 2", "A new crash-free Flash!");
+
+ let test_params = {};
+ test_params[getIDHashForString(PLUGINS[0].name + PLUGINS[0].description)] = [
+ ["onDisabling", false],
+ "onDisabled"
+ ];
+ test_params[getIDHashForString(PLUGINS[1].name + PLUGINS[1].description)] = [
+ ["onEnabling", false],
+ "onEnabled"
+ ];
+
+ prepare_test(test_params);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash 2");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_true(addons[1].userDisabled);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_plugins.js b/toolkit/mozapps/extensions/test/xpcshell/test_plugins.js
new file mode 100644
index 000000000..5541bc946
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_plugins.js
@@ -0,0 +1,210 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that plugins exist and can be enabled and disabled.
+var gID = null;
+
+function setTestPluginState(state) {
+ let tags = AM_Cc["@mozilla.org/plugin/host;1"].getService(AM_Ci.nsIPluginHost)
+ .getPluginTags();
+ for (let tag of tags) {
+ if (tag.name == "Test Plug-in") {
+ tag.enabledState = state;
+ return;
+ }
+ }
+ throw Error("No plugin tag found for the test plugin");
+}
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+
+ setTestPluginState(AM_Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+
+ startupManager();
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ run_test_1();
+}
+
+// Finds the test plugin library
+function get_test_plugin() {
+ var pluginEnum = Services.dirsvc.get("APluginsDL", AM_Ci.nsISimpleEnumerator);
+ while (pluginEnum.hasMoreElements()) {
+ let dir = pluginEnum.getNext().QueryInterface(AM_Ci.nsILocalFile);
+ let plugin = dir.clone();
+ // OSX plugin
+ plugin.append("Test.plugin");
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ plugin = dir.clone();
+ // *nix plugin
+ plugin.append("libnptest.so");
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ // Windows plugin
+ plugin = dir.clone();
+ plugin.append("nptest.dll");
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ }
+ return null;
+}
+
+function getFileSize(aFile) {
+ if (!aFile.isDirectory())
+ return aFile.fileSize;
+
+ let size = 0;
+ let entries = aFile.directoryEntries.QueryInterface(AM_Ci.nsIDirectoryEnumerator);
+ let entry;
+ while (entry = entries.nextFile)
+ size += getFileSize(entry);
+ entries.close();
+ return size;
+}
+
+function getPluginLastModifiedTime(aPluginFile) {
+ // On OS X we use the bundle contents last modified time as using
+ // the package directories modified date may be outdated.
+ // See bug 313700.
+ try {
+ let localFileMac = aPluginFile.QueryInterface(AM_Ci.nsILocalFileMac);
+ if (localFileMac) {
+ return localFileMac.bundleContentsLastModifiedTime;
+ }
+ } catch (e) {
+ }
+
+ return aPluginFile.lastModifiedTime;
+}
+
+// Tests that the test plugin exists
+function run_test_1() {
+ var testPlugin = get_test_plugin();
+ do_check_neq(testPlugin, null);
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ do_check_true(addons.length > 0);
+
+ addons.forEach(function(p) {
+ if (p.name == "Test Plug-in")
+ gID = p.id;
+ });
+
+ do_check_neq(gID, null);
+
+ AddonManager.getAddonByID(gID, function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Test Plug-in");
+ do_check_eq(p.description,
+ "Plug-in for testing purposes.\u2122 " +
+ "(\u0939\u093f\u0928\u094d\u0926\u0940 " +
+ "\u4e2d\u6587 " +
+ "\u0627\u0644\u0639\u0631\u0628\u064a\u0629)");
+ do_check_eq(p.creator, null);
+ do_check_eq(p.version, "1.0.0.0");
+ do_check_eq(p.type, "plugin");
+ do_check_eq(p.userDisabled, "askToActivate");
+ do_check_false(p.appDisabled);
+ do_check_true(p.isActive);
+ do_check_true(p.isCompatible);
+ do_check_true(p.providesUpdatesSecurely);
+ do_check_eq(p.blocklistState, 0);
+ do_check_eq(p.permissions, AddonManager.PERM_CAN_DISABLE | AddonManager.PERM_CAN_ENABLE);
+ do_check_eq(p.pendingOperations, 0);
+ do_check_true(p.size > 0);
+ do_check_eq(p.size, getFileSize(testPlugin));
+ do_check_true(p.updateDate > 0);
+ do_check_true("isCompatibleWith" in p);
+ do_check_true("findUpdates" in p);
+
+ let lastModifiedTime = getPluginLastModifiedTime(testPlugin);
+ do_check_eq(p.updateDate.getTime(), lastModifiedTime);
+ do_check_eq(p.installDate.getTime(), lastModifiedTime);
+
+ run_test_2(p);
+ });
+ });
+}
+
+// Tests that disabling a plugin works
+function run_test_2(p) {
+ let test = {};
+ test[gID] = [
+ ["onDisabling", false],
+ "onDisabled",
+ ["onPropertyChanged", ["userDisabled"]]
+ ];
+ prepare_test(test);
+
+ p.userDisabled = true;
+
+ ensure_test_completed();
+
+ do_check_true(p.userDisabled);
+ do_check_false(p.appDisabled);
+ do_check_false(p.isActive);
+
+ AddonManager.getAddonByID(gID, function(p) {
+ do_check_neq(p, null);
+ do_check_true(p.userDisabled);
+ do_check_false(p.appDisabled);
+ do_check_false(p.isActive);
+ do_check_eq(p.name, "Test Plug-in");
+
+ run_test_3(p);
+ });
+}
+
+// Tests that enabling a plugin works
+function run_test_3(p) {
+ let test = {};
+ test[gID] = [
+ ["onEnabling", false],
+ "onEnabled"
+ ];
+ prepare_test(test);
+
+ p.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(p.userDisabled);
+ do_check_false(p.appDisabled);
+ do_check_true(p.isActive);
+
+ AddonManager.getAddonByID(gID, function(p) {
+ do_check_neq(p, null);
+ do_check_false(p.userDisabled);
+ do_check_false(p.appDisabled);
+ do_check_true(p.isActive);
+ do_check_eq(p.name, "Test Plug-in");
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Verify that after a restart the test plugin has the same ID
+function run_test_4() {
+ restartManager();
+
+ AddonManager.getAddonByID(gID, function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Test Plug-in");
+
+ Services.prefs.clearUserPref("plugins.click_to_play");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_pref_properties.js b/toolkit/mozapps/extensions/test/xpcshell/test_pref_properties.js
new file mode 100644
index 000000000..9abffaab0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_pref_properties.js
@@ -0,0 +1,206 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the preference-related properties of AddonManager
+// eg: AddonManager.checkCompatibility, AddonManager.updateEnabled, etc
+
+var gManagerEventsListener = {
+ seenEvents: [],
+ init: function() {
+ let events = ["onCompatibilityModeChanged", "onCheckUpdateSecurityChanged",
+ "onUpdateModeChanged"];
+ events.forEach(function(aEvent) {
+ this[aEvent] = function() {
+ do_print("Saw event " + aEvent);
+ this.seenEvents.push(aEvent);
+ }
+ }, this);
+ AddonManager.addManagerListener(this);
+ // Try to add twice, to test that the second time silently fails.
+ AddonManager.addManagerListener(this);
+ },
+ shutdown: function() {
+ AddonManager.removeManagerListener(this);
+ },
+ expect: function(aEvents) {
+ this.expectedEvents = aEvents;
+ },
+ checkExpected: function() {
+ do_print("Checking expected events...");
+ while (this.expectedEvents.length > 0) {
+ let event = this.expectedEvents.pop();
+ do_print("Looking for expected event " + event);
+ let matchingEvents = this.seenEvents.filter(function(aSeenEvent) {
+ return aSeenEvent == event;
+ });
+ do_check_eq(matchingEvents.length, 1);
+ }
+ this.seenEvents = [];
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setBoolPref("extensions.update.enabled", true);
+ Services.prefs.setBoolPref("extensions.update.autoUpdateDefault", true);
+ Services.prefs.setBoolPref("extensions.strictCompatibility", true);
+ Services.prefs.setBoolPref("extensions.checkUpdatesecurity", true);
+
+ startupManager();
+ gManagerEventsListener.init();
+
+
+ // AddonManager.updateEnabled
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.updateEnabled = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.updateEnabled);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.updateEnabled = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.updateEnabled);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.updateEnabled = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.updateEnabled);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.updateEnabled = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.updateEnabled);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ // AddonManager.autoUpdateDefault
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.autoUpdateDefault = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.autoUpdateDefault);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.autoUpdateDefault = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.autoUpdateDefault);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.autoUpdateDefault = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.autoUpdateDefault);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.autoUpdateDefault = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.autoUpdateDefault);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ // AddonManager.strictCompatibility
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.strictCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.strictCompatibility);
+ do_check_false(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.strictCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.strictCompatibility);
+ do_check_false(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.strictCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.strictCompatibility);
+ do_check_true(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.strictCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.strictCompatibility);
+ do_check_true(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+
+ // AddonManager.checkCompatibility
+ if (isNightlyChannel()) {
+ var version = "nightly";
+ } else {
+ version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1");
+ }
+ const COMPATIBILITY_PREF = "extensions.checkCompatibility." + version;
+
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.checkCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.getBoolPref(COMPATIBILITY_PREF));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.getBoolPref(COMPATIBILITY_PREF));
+
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.checkCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.prefHasUserValue(COMPATIBILITY_PREF));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.prefHasUserValue(COMPATIBILITY_PREF));
+
+
+ // AddonManager.checkUpdateSecurity
+ gManagerEventsListener.expect(["onCheckUpdateSecurityChanged"]);
+ AddonManager.checkUpdateSecurity = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkUpdateSecurity);
+ if (AddonManager.checkUpdateSecurityDefault)
+ do_check_false(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkUpdateSecurity = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkUpdateSecurity);
+ if (AddonManager.checkUpdateSecurityDefault)
+ do_check_false(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.expect(["onCheckUpdateSecurityChanged"]);
+ AddonManager.checkUpdateSecurity = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkUpdateSecurity);
+ if (!AddonManager.checkUpdateSecurityDefault)
+ do_check_true(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkUpdateSecurity = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkUpdateSecurity);
+ if (!AddonManager.checkUpdateSecurityDefault)
+ do_check_true(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.shutdown();
+
+ // After removing the listener, ensure we get no further events.
+ gManagerEventsListener.expect([]);
+ AddonManager.updateEnabled = false;
+ gManagerEventsListener.checkExpected();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_provider_markSafe.js b/toolkit/mozapps/extensions/test/xpcshell/test_provider_markSafe.js
new file mode 100644
index 000000000..55d503f2c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_markSafe.js
@@ -0,0 +1,47 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+let startupOrder = [];
+
+function mockAddonProvider(name) {
+ let mockProvider = {
+ markSafe: false,
+ apiAccessed: false,
+
+ startup() {
+ if (this.markSafe)
+ AddonManagerPrivate.markProviderSafe(this);
+
+ let uri = Services.io.newURI("beard://long", null, null);
+ AddonManager.isInstallEnabled("made-up-mimetype");
+ },
+ supportsMimetype(mimetype) {
+ this.apiAccessed = true;
+ return false;
+ },
+
+ get name() name,
+ };
+
+ return mockProvider;
+};
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* testMarkSafe() {
+ do_print("Starting with provider normally");
+ let provider = mockAddonProvider("Mock1");
+ AddonManagerPrivate.registerProvider(provider);
+ startupManager();
+ ok(!provider.apiAccessed, "Provider API should not have been accessed");
+ AddonManagerPrivate.unregisterProvider(provider);
+ yield promiseShutdownManager();
+
+ do_print("Starting with provider that marks itself safe");
+ provider.apiAccessed = false;
+ provider.markSafe = true;
+ AddonManagerPrivate.registerProvider(provider);
+ startupManager();
+ ok(provider.apiAccessed, "Provider API should have been accessed");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_provider_shutdown.js b/toolkit/mozapps/extensions/test/xpcshell/test_provider_shutdown.js
new file mode 100644
index 000000000..f6de26241
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_shutdown.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Verify that we report shutdown status for Addon Manager providers
+// and AddonRepository correctly.
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Make a mock AddonRepository that just lets us hang shutdown.
+// Needs two promises - one to let us know that AM has called shutdown,
+// and one for us to let AM know that shutdown is done.
+function mockAddonProvider(aName) {
+ let mockProvider = {
+ donePromise: null,
+ doneResolve: null,
+ doneReject: null,
+ shutdownPromise: null,
+ shutdownResolve: null,
+
+ get name() aName,
+
+ shutdown() {
+ this.shutdownResolve();
+ return this.donePromise;
+ },
+ };
+ mockProvider.donePromise = new Promise((resolve, reject) => {
+ mockProvider.doneResolve = resolve;
+ mockProvider.doneReject = reject;
+ });
+ mockProvider.shutdownPromise = new Promise((resolve, reject) => {
+ mockProvider.shutdownResolve = resolve;
+ });
+ return mockProvider;
+};
+
+function run_test() {
+ run_next_test();
+}
+
+// Helper to find a particular shutdown blocker's status in the JSON blob
+function findInStatus(aStatus, aName) {
+ for (let {name, state} of aStatus.state) {
+ if (name == aName) {
+ return state;
+ }
+ }
+ return null;
+}
+
+/*
+ * Make sure we report correctly when an add-on provider or AddonRepository block shutdown
+ */
+add_task(function* blockRepoShutdown() {
+ // Reach into the AddonManager scope and inject our mock AddonRepository
+ let realAddonRepo = AMscope.AddonRepository;
+ // the mock provider behaves enough like AddonRepository for the purpose of this test
+ let mockRepo = mockAddonProvider("Mock repo");
+ AMscope.AddonRepository = mockRepo;
+
+ let mockProvider = mockAddonProvider("Mock provider");
+
+ startupManager();
+ AddonManagerPrivate.registerProvider(mockProvider);
+
+ // Start shutting the manager down
+ let managerDown = promiseShutdownManager();
+
+ // Wait for manager to call provider shutdown.
+ yield mockProvider.shutdownPromise;
+ // check AsyncShutdown state
+ let status = MockAsyncShutdown.status();
+ equal(findInStatus(status[0], "Mock provider"), "(none)");
+ equal(status[1].name, "AddonRepository: async shutdown");
+ equal(status[1].state, "pending");
+ // let the provider finish
+ mockProvider.doneResolve();
+
+ // Wait for manager to call repo shutdown and start waiting for it
+ yield mockRepo.shutdownPromise;
+ // Check the shutdown state
+ status = MockAsyncShutdown.status();
+ equal(status[0].name, "AddonManager: Waiting for providers to shut down.");
+ equal(status[0].state, "Complete");
+ equal(status[1].name, "AddonRepository: async shutdown");
+ equal(status[1].state, "in progress");
+
+ // Now finish our shutdown, and wait for the manager to wrap up
+ mockRepo.doneResolve();
+ yield managerDown;
+
+ // Check the shutdown state again
+ status = MockAsyncShutdown.status();
+ equal(status[0].name, "AddonRepository: async shutdown");
+ equal(status[0].state, "done");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_shutdown.js b/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_shutdown.js
new file mode 100644
index 000000000..df717f5a5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_shutdown.js
@@ -0,0 +1,61 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+let shutdownOrder = [];
+
+function mockAddonProvider(name) {
+ let mockProvider = {
+ hasShutdown: false,
+ unsafeAccess: false,
+
+ shutdownCallback: null,
+
+ startup() { },
+ shutdown() {
+ this.hasShutdown = true;
+ shutdownOrder.push(this.name);
+ if (this.shutdownCallback)
+ return this.shutdownCallback();
+ },
+ getAddonByID(id, callback) {
+ if (this.hasShutdown) {
+ unsafeAccess = true;
+ }
+ callback(null);
+ },
+
+ get name() name,
+ };
+
+ return mockProvider;
+};
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* unsafeProviderShutdown() {
+ let firstProvider = mockAddonProvider("Mock1");
+ AddonManagerPrivate.registerProvider(firstProvider);
+ let secondProvider = mockAddonProvider("Mock2");
+ AddonManagerPrivate.registerProvider(secondProvider);
+
+ startupManager();
+
+ let shutdownPromise = null;
+ yield new Promise(resolve => {
+ secondProvider.shutdownCallback = function() {
+ return new Promise(shutdownResolve => {
+ AddonManager.getAddonByID("does-not-exist", () => {
+ shutdownResolve();
+ resolve();
+ });
+ });
+ };
+
+ shutdownPromise = promiseShutdownManager();
+ });
+ yield shutdownPromise;
+
+ equal(shutdownOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have shutdown in expected order");
+ ok(!firstProvider.unsafeAccess, "First registered mock provider should not have been accessed unsafely");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_startup.js b/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_startup.js
new file mode 100644
index 000000000..867dc9673
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_startup.js
@@ -0,0 +1,53 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+let startupOrder = [];
+
+function mockAddonProvider(name) {
+ let mockProvider = {
+ hasStarted: false,
+ unsafeAccess: false,
+
+ startupCallback: null,
+
+ startup() {
+ this.hasStarted = true;
+ startupOrder.push(this.name);
+ if (this.startupCallback)
+ this.startupCallback();
+ },
+ getAddonByID(id, callback) {
+ if (!this.hasStarted) {
+ unsafeAccess = true;
+ }
+ callback(null);
+ },
+
+ get name() name,
+ };
+
+ return mockProvider;
+};
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* unsafeProviderStartup() {
+ let secondProvider = null;
+
+ yield new Promise(resolve => {
+ let firstProvider = mockAddonProvider("Mock1");
+ firstProvider.startupCallback = function() {
+ AddonManager.getAddonByID("does-not-exist", resolve);
+ };
+ AddonManagerPrivate.registerProvider(firstProvider);
+
+ secondProvider = mockAddonProvider("Mock2");
+ AddonManagerPrivate.registerProvider(secondProvider);
+
+ startupManager();
+ });
+
+ equal(startupOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have hasStarted in expected order");
+ ok(!secondProvider.unsafeAccess, "Second registered mock provider should not have been accessed unsafely");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_registry.js b/toolkit/mozapps/extensions/test/xpcshell/test_registry.js
new file mode 100644
index 000000000..010250457
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_registry.js
@@ -0,0 +1,151 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that extensions installed through the registry work as expected
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const addon1Dir = writeInstallRDFForExtension(addon1, gProfD, "addon1");
+const addon2Dir = writeInstallRDFForExtension(addon2, gProfD, "addon2");
+
+function run_test() {
+ // This test only works where there is a registry.
+ if (!("nsIWindowsRegKey" in AM_Ci))
+ return;
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+// Tests whether basic registry install works
+function run_test_1() {
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon1Dir.path);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon2Dir.path);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests whether uninstalling from the registry works
+function run_test_2() {
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", null);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", null);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Checks that the ID in the registry must match that in the install manifest
+function run_test_3() {
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon2Dir.path);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon1Dir.path);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Tests whether an extension's ID can change without its directory changing
+function run_test_4() {
+ // Restarting with bad items in the registry should not force an EM restart
+ restartManager();
+
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", null);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", null);
+
+ restartManager();
+
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon1Dir.path);
+ restartManager();
+
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", null);
+ MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon1Dir.path);
+ writeInstallRDFForExtension(addon2, gProfD, "addon1");
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_safemode.js b/toolkit/mozapps/extensions/test/xpcshell/test_safemode.js
new file mode 100644
index 000000000..05647f807
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_safemode.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// Tests that extensions behave correctly in safe mode
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ optionsURL: "chrome://foo/content/options.xul",
+ aboutURL: "chrome://foo/content/about.xul",
+ iconURL: "chrome://foo/content/icon.png",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gIconURL = null;
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ gAppInfo.inSafeMode = true;
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_eq(a1, null);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ writeInstallRDFForExtension(addon1, profileDir, addon1.id, "icon.png");
+ gIconURL = do_get_addon_root_uri(profileDir.clone(), addon1.id) + "icon.png";
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_false(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, null);
+ do_check_eq(newa1.optionsURL, null);
+ do_check_eq(newa1.iconURL, gIconURL);
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_1();
+ });
+ }));
+}
+
+// Disabling an add-on should work
+function run_test_1() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_false(hasFlag(a1.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_DISABLE));
+ a1.userDisabled = true;
+ do_check_false(a1.isActive);
+ do_check_eq(a1.aboutURL, null);
+ do_check_eq(a1.optionsURL, null);
+ do_check_eq(a1.iconURL, gIconURL);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ run_test_2();
+ });
+}
+
+// Enabling an add-on should happen without restart but not become active.
+function run_test_2() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ a1.userDisabled = false;
+ do_check_false(a1.isActive);
+ do_check_eq(a1.aboutURL, null);
+ do_check_eq(a1.optionsURL, null);
+ do_check_eq(a1.iconURL, gIconURL);
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js b/toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
new file mode 100644
index 000000000..a865824f0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Verify that API functions fail if the Add-ons Manager isn't initialised.
+
+const IGNORE = ["escapeAddonURI", "shouldAutoUpdate", "getStartupChanges",
+ "addTypeListener", "removeTypeListener",
+ "addAddonListener", "removeAddonListener",
+ "addInstallListener", "removeInstallListener",
+ "addManagerListener", "removeManagerListener",
+ "mapURIToAddonID", "shutdown"];
+
+const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
+ "AddonScreenshot", "AddonType", "startup", "shutdown",
+ "registerProvider", "unregisterProvider",
+ "addStartupChange", "removeStartupChange",
+ "recordTimestamp", "recordSimpleMeasure",
+ "recordException", "getSimpleMeasures", "simpleTimer",
+ "setTelemetryDetails", "getTelemetryDetails",
+ "callNoUpdateListeners", "backgroundUpdateTimerHandler"];
+
+function test_functions() {
+ for (let prop in AddonManager) {
+ if (IGNORE.indexOf(prop) != -1)
+ continue;
+ if (typeof AddonManager[prop] != "function")
+ continue;
+
+ try {
+ do_print("AddonManager." + prop);
+ AddonManager[prop]();
+ do_throw(prop + " did not throw an exception");
+ }
+ catch (e) {
+ if (e.result != Components.results.NS_ERROR_NOT_INITIALIZED)
+ do_throw(prop + " threw an unexpected exception: " + e);
+ }
+ }
+
+ for (let prop in AddonManagerPrivate) {
+ if (typeof AddonManagerPrivate[prop] != "function")
+ continue;
+ if (IGNORE_PRIVATE.indexOf(prop) != -1)
+ continue;
+
+ try {
+ do_print("AddonManagerPrivate." + prop);
+ AddonManagerPrivate[prop]();
+ do_throw(prop + " did not throw an exception");
+ }
+ catch (e) {
+ if (e.result != Components.results.NS_ERROR_NOT_INITIALIZED)
+ do_throw(prop + " threw an unexpected exception: " + e);
+ }
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ test_functions();
+ startupManager();
+ shutdownManager();
+ test_functions();
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_sourceURI.js b/toolkit/mozapps/extensions/test/xpcshell/test_sourceURI.js
new file mode 100644
index 000000000..e78bb5074
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_sourceURI.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+const PORT = gServer.identity.primaryPort;
+const BASE_URL = "http://localhost:" + PORT;
+const DEFAULT_URL = "about:blank";
+
+var addon = {
+ id: "addon@tests.mozilla.org",
+ version: "1.0",
+ name: "Test",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function backgroundUpdate(aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+ aCallback();
+ }, "addons-background-update-complete", false);
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function run_test() {
+ do_test_pending();
+
+ mapUrlToFile("/cache.xml", do_get_file("data/test_sourceURI.xml"), gServer);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, BASE_URL + "/cache.xml");
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, BASE_URL + "/cache.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ writeInstallRDFForExtension(addon, profileDir);
+ startupManager();
+
+ AddonManager.getAddonByID("addon@tests.mozilla.org", function(a) {
+ do_check_neq(a, null);
+ do_check_eq(a.sourceURI, null);
+
+ backgroundUpdate(function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon@tests.mozilla.org", function(a) {
+ do_check_neq(a, null);
+ do_check_neq(a.sourceURI, null);
+ do_check_eq(a.sourceURI.spec, "http://www.example.com/testaddon.xpi");
+
+ do_test_finished();
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
new file mode 100644
index 000000000..181f8ee62
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -0,0 +1,917 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies startup detection of added/removed/changed items and install
+// location priorities
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }, { // Repeated target application entries should be ignored
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{ // Bad target application entries should be ignored
+ minVersion: "3",
+ maxVersion: "4"
+ }, {
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "3.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.2.*"
+ }]
+};
+
+// Should be ignored because it has no ID
+var addon4 = {
+ version: "4.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Should be ignored because it has no version
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Should be ignored because it has an invalid type
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "3.0",
+ name: "Test 6",
+ type: 5,
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.2.*"
+ }]
+};
+
+// Should be ignored because it has an invalid type
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "3.0",
+ name: "Test 3",
+ type: "extension",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.2.*"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const globalDir = gProfD.clone();
+globalDir.append("extensions2");
+globalDir.append(gAppInfo.ID);
+registerDirectory("XRESysSExtPD", globalDir.parent);
+const userDir = gProfD.clone();
+userDir.append("extensions3");
+userDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userDir.parent);
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gCachePurged = false;
+
+// Set up the profile
+function run_test() {
+ do_test_pending("test_startup main");
+
+ let obs = AM_Cc["@mozilla.org/observer-service;1"].
+ getService(AM_Ci.nsIObserverService);
+ obs.addObserver({
+ observe: function(aSubject, aTopic, aData) {
+ gCachePurged = true;
+ }
+ }, "startupcache-invalidate", false);
+
+ startupManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+
+ do_check_false(gExtensionsJSON.exists());
+
+ do_check_false(gExtensionsINI.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+
+ do_check_eq(a1, null);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+ do_check_eq(a2, null);
+ do_check_not_in_crash_annotation(addon2.id, addon2.version);
+ do_check_eq(a3, null);
+ do_check_not_in_crash_annotation(addon3.id, addon3.version);
+ do_check_eq(a4, null);
+ do_check_eq(a5, null);
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function end_test() {
+ do_test_finished("test_startup main");
+}
+
+// Try to install all the items into the profile
+function run_test_1() {
+ writeInstallRDFForExtension(addon1, profileDir);
+ var dest = writeInstallRDFForExtension(addon2, profileDir);
+ // Attempt to make this look like it was added some time in the past so
+ // the change in run_test_2 makes the last modified time change.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir, "addon4@tests.mozilla.org");
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ do_print("Checking for " + gExtensionsINI.path);
+ do_check_true(gExtensionsINI.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_neq(a1.syncGUID, null);
+ do_check_true(a1.syncGUID.length >= 9);
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.name, "Test 1");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_eq(a1.sourceURI, null);
+ do_check_true(a1.foreignInstall);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_neq(a2.syncGUID, null);
+ do_check_true(a2.syncGUID.length >= 9);
+ do_check_eq(a2.version, "2.0");
+ do_check_eq(a2.name, "Test 2");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, addon2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+ do_check_eq(a2.sourceURI, null);
+ do_check_true(a2.foreignInstall);
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.id, "addon3@tests.mozilla.org");
+ do_check_neq(a3.syncGUID, null);
+ do_check_true(a3.syncGUID.length >= 9);
+ do_check_eq(a3.version, "3.0");
+ do_check_eq(a3.name, "Test 3");
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(hasFlag(a3.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a3.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon3.id, addon3.version);
+ do_check_eq(a3.scope, AddonManager.SCOPE_PROFILE);
+ do_check_eq(a3.sourceURI, null);
+ do_check_true(a3.foreignInstall);
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon5@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_check_eq(a6, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon6@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon6@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_check_eq(a7, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon7@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon7@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ AddonManager.getAddonsByTypes(["extension"], function(extensionAddons) {
+ do_check_eq(extensionAddons.length, 3);
+
+ do_execute_soon(run_test_2);
+ });
+ });
+}
+
+// Test that modified items are detected and items in other install locations
+// are ignored
+function run_test_2() {
+ addon1.version = "1.1";
+ writeInstallRDFForExtension(addon1, userDir);
+ addon2.version="2.1";
+ writeInstallRDFForExtension(addon2, profileDir);
+ addon2.version="2.2";
+ writeInstallRDFForExtension(addon2, globalDir);
+ addon2.version="2.3";
+ writeInstallRDFForExtension(addon2, userDir);
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
+ dest.remove(true);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon3@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ do_check_true(gExtensionsINI.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.0");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true(a1.foreignInstall);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.1");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true(a2.foreignInstall);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon3.id, addon3.version);
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Check that removing items from the profile reveals their hidden versions.
+function run_test_3() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ writeInstallRDFForExtension(addon3, profileDir, "addon4@tests.mozilla.org");
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.1");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Test that disabling an install location works
+function run_test_4() {
+ Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_SYSTEM);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon1@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_eq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon1@tests.mozilla.org"));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.2");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_true(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Switching disabled locations works
+function run_test_5() {
+ Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_USER);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon1@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.1");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Resetting the pref makes everything visible again
+function run_test_6() {
+ Services.prefs.clearUserPref("extensions.enabledScopes");
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.1");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Check that items in the profile hide the others again.
+function run_test_7() {
+ addon1.version = "1.2";
+ writeInstallRDFForExtension(addon1, profileDir);
+ var dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.2");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.2");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_true(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Disabling all locations still leaves the profile working
+function run_test_8() {
+ Services.prefs.setIntPref("extensions.enabledScopes", 0);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.2");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_eq(a2, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon2@tests.mozilla.org"));
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// More hiding and revealing
+function run_test_9() {
+ Services.prefs.clearUserPref("extensions.enabledScopes", 0);
+
+ var dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = globalDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ addon2.version = "2.4";
+ writeInstallRDFForExtension(addon2, profileDir);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.2");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.4");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_10);
+ });
+}
+
+// Checks that a removal from one location and an addition in another location
+// for the same item is handled
+function run_test_10() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ addon1.version = "1.3";
+ writeInstallRDFForExtension(addon1, userDir);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.4");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_11);
+ });
+}
+
+// This should remove any remaining items
+function run_test_11() {
+ var dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+ do_check_eq(a3, null);
+ do_check_eq(a4, null);
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon3@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon4@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon5@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon3@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon4@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon5@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+ do_check_not_in_crash_annotation(addon2.id, addon2.version);
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Test that auto-disabling for specific scopes works
+function run_test_12() {
+ Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_USER);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, userDir);
+ writeInstallRDFForExtension(addon3, globalDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ do_check_neq(a3, null);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ dest = globalDir.clone();
+ dest.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+
+ Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_SYSTEM);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, userDir);
+ writeInstallRDFForExtension(addon3, globalDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.isActive);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ dest = globalDir.clone();
+ dest.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+
+ Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_USER + AddonManager.SCOPE_SYSTEM);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, userDir);
+ writeInstallRDFForExtension(addon3, globalDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.isActive);
+
+ do_execute_soon(end_test);
+ });
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js b/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js
new file mode 100644
index 000000000..788e1ef79
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests AddonManager.strictCompatibility and it's related preference,
+// extensions.strictCompatibility, and the strictCompatibility option in
+// install.rdf
+
+
+// Always compatible
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Incompatible in strict compatibility mode
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.7",
+ maxVersion: "0.8"
+ }]
+};
+
+// Theme - always uses strict compatibility, so is always incompatible
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ internalName: "test-theme-3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.8",
+ maxVersion: "0.9"
+ }]
+};
+
+// Opt-in to strict compatibility - always incompatible
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ strictCompatibility: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.8",
+ maxVersion: "0.9"
+ }]
+};
+
+// Addon from the future - would be marked as compatibile-by-default,
+// but minVersion is higher than the app version
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "3",
+ maxVersion: "5"
+ }]
+};
+
+// Extremely old addon - maxVersion is less than the mimimum compat version
+// set in extensions.minCompatibleVersion
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }]
+};
+
+// Dictionary - incompatible in strict compatibility mode
+var addon7= {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ type: "64",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.8",
+ maxVersion: "0.9"
+ }]
+};
+
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function do_check_compat_status(aStrict, aAddonCompat, aCallback) {
+ do_check_eq(AddonManager.strictCompatibility, aStrict);
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.isCompatible, aAddonCompat[0]);
+ do_check_eq(a1.appDisabled, !aAddonCompat[0]);
+ do_check_false(a1.strictCompatibility);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.isCompatible, aAddonCompat[1]);
+ do_check_eq(a2.appDisabled, !aAddonCompat[1]);
+ do_check_false(a2.strictCompatibility);
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.isCompatible, aAddonCompat[2]);
+ do_check_eq(a3.appDisabled, !aAddonCompat[2]);
+ do_check_true(a3.strictCompatibility);
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.isCompatible, aAddonCompat[3]);
+ do_check_eq(a4.appDisabled, !aAddonCompat[3]);
+ do_check_true(a4.strictCompatibility);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.isCompatible, aAddonCompat[4]);
+ do_check_eq(a5.appDisabled, !aAddonCompat[4]);
+ do_check_false(a5.strictCompatibility);
+
+ do_check_neq(a6, null);
+ do_check_eq(a6.isCompatible, aAddonCompat[5]);
+ do_check_eq(a6.appDisabled, !aAddonCompat[5]);
+ do_check_false(a6.strictCompatibility);
+
+ do_check_neq(a7, null);
+ do_check_eq(a7.isCompatible, aAddonCompat[6]);
+ do_check_eq(a7.appDisabled, !aAddonCompat[6]);
+ do_check_false(a7.strictCompatibility);
+
+ do_execute_soon(aCallback);
+ });
+}
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+
+ Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.1");
+
+ startupManager();
+
+ // Should default to enabling strict compat.
+ do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_1);
+}
+
+function run_test_1() {
+ do_print("Test 1");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_2);
+}
+
+function run_test_2() {
+ do_print("Test 2");
+ restartManager();
+ do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_3);
+}
+
+function run_test_3() {
+ do_print("Test 3");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+ do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_4);
+}
+
+function run_test_4() {
+ do_print("Test 4");
+ restartManager();
+ do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_5);
+}
+
+function run_test_5() {
+ do_print("Test 5");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.4");
+ do_check_compat_status(false, [true, true, false, false, false, false, true], do_test_finished);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_syncGUID.js b/toolkit/mozapps/extensions/test/xpcshell/test_syncGUID.js
new file mode 100644
index 000000000..f1d6e0914
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_syncGUID.js
@@ -0,0 +1,154 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// restartManager() mucks with XPIProvider.jsm importing, so we hack around.
+this.__defineGetter__("XPIProvider", function () {
+ let scope = {};
+ return Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", scope)
+ .XPIProvider;
+});
+
+const addonId = "addon1@tests.mozilla.org";
+
+function run_test() {
+ Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ run_next_test();
+}
+
+add_test(function test_getter_and_setter() {
+ // Our test add-on requires a restart.
+ let listener = {
+ onInstallEnded: function onInstallEnded() {
+ AddonManager.removeInstallListener(listener);
+ // never restart directly inside an onInstallEnded handler!
+ do_execute_soon(function getter_setter_install_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID(addonId, function(addon) {
+
+ do_check_neq(addon, null);
+ do_check_neq(addon.syncGUID, null);
+ do_check_true(addon.syncGUID.length >= 9);
+
+ let oldGUID = addon.SyncGUID;
+ let newGUID = "foo";
+
+ addon.syncGUID = newGUID;
+ do_check_eq(newGUID, addon.syncGUID);
+
+ // Verify change made it to DB.
+ AddonManager.getAddonByID(addonId, function(newAddon) {
+ do_check_neq(newAddon, null);
+ do_check_eq(newGUID, newAddon.syncGUID);
+ });
+
+ run_next_test();
+ });
+ });
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install1"),
+ function(install) {
+ install.install();
+ });
+});
+
+add_test(function test_fetch_by_guid_unknown_guid() {
+ XPIProvider.getAddonBySyncGUID("XXXX", function(addon) {
+ do_check_eq(null, addon);
+ run_next_test();
+ });
+});
+
+// Ensure setting an extension to an existing syncGUID results in error.
+add_test(function test_error_on_duplicate_syncguid_insert() {
+ const installNames = ["test_install1", "test_install2_1"];
+ const installIDs = ["addon1@tests.mozilla.org", "addon2@tests.mozilla.org"];
+
+ let installCount = 0;
+
+ let listener = {
+ onInstallEnded: function onInstallEnded() {
+ installCount++;
+
+ if (installCount == installNames.length) {
+ AddonManager.removeInstallListener(listener);
+ do_execute_soon(function duplicate_syncguid_install_ended() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(installIDs, callback_soon(function(addons) {
+ let initialGUID = addons[1].syncGUID;
+
+ try {
+ addons[1].syncGUID = addons[0].syncGUID;
+ do_throw("Should not get here.");
+ }
+ catch (e) {
+ do_check_true(e.message.startsWith("Addon sync GUID conflict"));
+ restartManager();
+
+ AddonManager.getAddonByID(installIDs[1], function(addon) {
+ do_check_eq(initialGUID, addon.syncGUID);
+ run_next_test();
+ });
+ }
+ }));
+ });
+ }
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+ let getInstallCB = function(install) { install.install(); };
+
+ for each (let name in installNames) {
+ AddonManager.getInstallForFile(do_get_addon(name), getInstallCB);
+ }
+});
+
+add_test(function test_fetch_by_guid_known_guid() {
+ AddonManager.getAddonByID(addonId, function(addon) {
+ do_check_neq(null, addon);
+ do_check_neq(null, addon.syncGUID);
+
+ let syncGUID = addon.syncGUID;
+
+ XPIProvider.getAddonBySyncGUID(syncGUID, function(newAddon) {
+ do_check_neq(null, newAddon);
+ do_check_eq(syncGUID, newAddon.syncGUID);
+
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_addon_manager_get_by_sync_guid() {
+ AddonManager.getAddonByID(addonId, function(addon) {
+ do_check_neq(null, addon.syncGUID);
+
+ let syncGUID = addon.syncGUID;
+
+ AddonManager.getAddonBySyncGUID(syncGUID, function(newAddon) {
+ do_check_neq(null, newAddon);
+ do_check_eq(addon.id, newAddon.id);
+ do_check_eq(syncGUID, newAddon.syncGUID);
+
+ AddonManager.getAddonBySyncGUID("DOES_NOT_EXIST", function(missing) {
+ do_check_eq(undefined, missing);
+
+ run_next_test();
+ });
+ });
+ });
+});
+
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_targetPlatforms.js b/toolkit/mozapps/extensions/test/xpcshell/test_targetPlatforms.js
new file mode 100644
index 000000000..ef4f2aee5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_targetPlatforms.js
@@ -0,0 +1,146 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the targetPlatform entries are checked when deciding
+// if an add-on is incompatible.
+
+// No targetPlatforms so should be compatible
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Matches the OS
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetPlatforms: [
+ "XPCShell",
+ "WINNT_x86",
+ "XPCShell"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Matches the OS and ABI
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetPlatforms: [
+ "WINNT",
+ "XPCShell_noarch-spidermonkey"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Doesn't match
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetPlatforms: [
+ "WINNT_noarch-spidermonkey",
+ "Darwin",
+ "WINNT_noarch-spidermonkey"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Matches the OS but since a different entry specifies ABI this doesn't match.
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetPlatforms: [
+ "XPCShell",
+ "XPCShell_foo"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Set up the profile
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isPlatformCompatible);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isPlatformCompatible);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_in_crash_annotation(addon2.id, addon2.version);
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_true(a3.isPlatformCompatible);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_in_crash_annotation(addon3.id, addon3.version);
+
+ do_check_neq(a4, null);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isPlatformCompatible);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_not_in_crash_annotation(addon4.id, addon4.version);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isPlatformCompatible);
+ do_check_false(a5.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+ do_check_not_in_crash_annotation(addon5.id, addon5.version);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_theme.js b/toolkit/mozapps/extensions/test/xpcshell/test_theme.js
new file mode 100644
index 000000000..f201c776d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_theme.js
@@ -0,0 +1,1092 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// The maximum allowable time since install. If an add-on claims to have been
+// installed longer ago than this the the test will fail.
+const MAX_INSTALL_TIME = 10000;
+
+// This verifies that themes behave as expected
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Observer to ensure a "lightweight-theme-styling-update" notification is sent
+// when expected
+var gLWThemeChanged = false;
+var LightweightThemeObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != "lightweight-theme-styling-update")
+ return;
+
+ gLWThemeChanged = true;
+ }
+};
+
+AM_Cc["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .addObserver(LightweightThemeObserver, "lightweight-theme-styling-update", false);
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+ writeInstallRDFForExtension({
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ skinnable: true,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ skinnable: false,
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ // We need a default theme for some of these things to work but we have hidden
+ // the one in the application directory.
+ writeInstallRDFForExtension({
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ function([d, t1, t2]) {
+ do_check_neq(d, null);
+ do_check_false(d.skinnable);
+ do_check_false(d.foreignInstall);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+ do_check_true(t1.skinnable);
+ do_check_true(t1.foreignInstall);
+ do_check_eq(t1.screenshots, null);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL |
+ AddonManager.OP_NEEDS_RESTART_DISABLE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_false(t2.skinnable);
+ do_check_true(t2.foreignInstall);
+ do_check_eq(t2.screenshots, null);
+ do_check_false(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Checks enabling one theme disables the others
+function run_test_1() {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onDisabling"
+ ],
+ "theme2@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(t1.userDisabled);
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(check_test_1);
+ });
+}
+
+function check_test_1() {
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme2/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+
+ do_check_neq(t2, null);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_true(t2.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL |
+ AddonManager.OP_NEEDS_RESTART_DISABLE);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Removing the active theme should fall back to the default (not ideal in this
+// case since we don't have the default theme installed)
+function run_test_2() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("theme2@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(t2, null);
+ do_check_false(isThemeInAddonsList(profileDir, "theme2@tests.mozilla.org"));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Installing a lightweight theme should happen instantly and disable the default theme
+function run_test_3() {
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(null, p1);
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.type, "theme");
+ do_check_eq(p1.description, "A test theme");
+ do_check_eq(p1.creator, "Mozilla");
+ do_check_eq(p1.homepageURL, "http://localhost/data/index.html");
+ do_check_eq(p1.iconURL, "http://localhost/data/icon.png");
+ do_check_eq(p1.screenshots.length, 1);
+ do_check_eq(p1.screenshots[0], "http://localhost/data/preview.png");
+ do_check_false(p1.appDisabled);
+ do_check_false(p1.userDisabled);
+ do_check_true(p1.isCompatible);
+ do_check_true(p1.providesUpdatesSecurely);
+ do_check_eq(p1.blocklistState, 0);
+ do_check_true(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+ do_check_eq(p1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true("isCompatibleWith" in p1);
+ do_check_true("findUpdates" in p1);
+ do_check_eq(p1.installDate.getTime(), p1.updateDate.getTime());
+
+ // Should have been installed sometime in the last few seconds.
+ let difference = Date.now() - p1.installDate.getTime();
+ if (difference > MAX_INSTALL_TIME)
+ do_throw("Add-on was installed " + difference + "ms ago");
+ else if (difference < 0)
+ do_throw("Add-on was installed " + difference + "ms in the future");
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "1@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_4);
+ });
+ });
+}
+
+// Installing a second lightweight theme should disable the first with no restart
+function run_test_4() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ],
+ "2@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "2",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A second test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByIDs(["1@personas.mozilla.org",
+ "2@personas.mozilla.org"], function([p1, p2]) {
+ do_check_neq(null, p2);
+ do_check_false(p2.appDisabled);
+ do_check_false(p2.userDisabled);
+ do_check_true(p2.isActive);
+ do_check_eq(p2.pendingOperations, 0);
+ do_check_eq(p2.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+ do_check_eq(p2.installDate.getTime(), p2.updateDate.getTime());
+
+ // Should have been installed sometime in the last few seconds.
+ let difference = Date.now() - p2.installDate.getTime();
+ if (difference > MAX_INSTALL_TIME)
+ do_throw("Add-on was installed " + difference + "ms ago");
+ else if (difference < 0)
+ do_throw("Add-on was installed " + difference + "ms in the future");
+
+ do_check_neq(null, p1);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.userDisabled);
+ do_check_false(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_ENABLE);
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "2@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Switching to a custom theme should disable the lightweight theme and require
+// a restart. Cancelling that should also be possible.
+function run_test_5() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onDisabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onOperationCancelled",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onDisabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_true(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+ do_check_true(hasFlag(AddonManager.PERM_CAN_ENABLE, p2.permissions));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(check_test_5);
+ });
+}
+
+function check_test_5() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_false(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Switching from a custom theme to a lightweight theme should require a restart
+function run_test_6() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onOperationCancelled",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_true(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(check_test_6);
+ });
+}
+
+function check_test_6() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_false(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Uninstalling a lightweight theme should not require a restart
+function run_test_7() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ p1.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 1);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Uninstalling a lightweight theme in use should not require a restart and it
+// should reactivate the default theme
+// Also, uninstalling a lightweight theme in use should send a
+// "lightweight-theme-styling-update" notification through the observer service
+function run_test_8() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("2@personas.mozilla.org", function(p2) {
+ p2.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 0);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Uninstalling a theme not in use should not require a restart
+function run_test_9() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ t1.uninstall();
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(newt1) {
+ do_check_eq(newt1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_10);
+ });
+ });
+}
+
+// Uninstalling a custom theme in use should require a restart
+function run_test_10() {
+ AddonManager.getAddonByID("theme2@tests.mozilla.org", callback_soon(function(oldt2) {
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ "onEnabling",
+ ],
+ "default@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ oldt2.userDisabled = false;
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([d, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ "onUninstalling",
+ ],
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ t2.uninstall();
+
+ ensure_test_completed();
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_11);
+ });
+ }));
+}
+
+// Installing a custom theme not in use should not require a restart
+function run_test_11() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.skinnable, true);
+ do_check_false(hasFlag(install.addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_11);
+ install.install();
+ });
+}
+
+function check_test_11() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ var previewSpec = do_get_addon_root_uri(profileDir, "theme1@tests.mozilla.org") + "preview.png";
+ do_check_eq(t1.screenshots.length, 1);
+ do_check_eq(t1.screenshots[0], previewSpec);
+ do_check_true(t1.skinnable);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Updating a custom theme not in use should not require a restart
+function run_test_12() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_false(hasFlag(install.addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_12);
+ install.install();
+ });
+}
+
+function check_test_12() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+// Updating a custom theme in use should require a restart
+function run_test_13() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onEnabling",
+ ],
+ "default@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ t1.userDisabled = false;
+ ensure_test_completed();
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onInstalling",
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_13));
+ install.install();
+ });
+ }));
+}
+
+function check_test_13() {
+ restartManager();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(gLWThemeChanged);
+ t1.uninstall();
+ restartManager();
+
+ do_execute_soon(run_test_14);
+ }));
+}
+
+// Switching from a lightweight theme to the default theme should not require
+// a restart
+function run_test_14() {
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ };
+
+ AddonManager.getAddonByID("default@tests.mozilla.org", function(d) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.isActive);
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ d.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_15);
+ });
+}
+
+// Upgrading the application with a custom theme in use should not disable it
+function run_test_15() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_theme")], function() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ t1.userDisabled = false;
+
+ restartManager();
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"],
+ callback_soon(function([d, t1]) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+
+ restartManager("2");
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d, t1]) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+
+ do_execute_soon(run_test_16);
+ });
+ }));
+ }));
+ });
+}
+
+// Upgrading the application with a custom theme in use should disable it if it
+// is no longer compatible
+function run_test_16() {
+ restartManager("3");
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d, t1]) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(t1.userDisabled);
+ do_check_true(t1.appDisabled);
+ do_check_false(t1.isActive);
+
+ do_execute_soon(run_test_17);
+ });
+}
+
+// Verifies that if the selected theme pref is changed by a different version
+// of the application that we correctly reset it when it points to an
+// incompatible theme
+function run_test_17() {
+ restartManager("2");
+ shutdownManager();
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+
+ restartManager("3");
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d, t1]) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(t1.userDisabled);
+ do_check_true(t1.appDisabled);
+ do_check_false(t1.isActive);
+
+ do_execute_soon(run_test_18);
+ });
+}
+
+// Disabling the active theme should switch back to the default theme
+function run_test_18() {
+ restartManager(2);
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ t1.userDisabled = false;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"],
+ callback_soon(function([d, t1]) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onDisabling",
+ ],
+ "default@tests.mozilla.org": [
+ "onEnabling",
+ ]
+ });
+ t1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d, t1]) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+
+ do_execute_soon(run_test_19);
+ });
+ }));
+ }));
+}
+
+// Disabling the active persona should switch back to the default theme
+function run_test_19() {
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "1@personas.mozilla.org"], function([d, p1]) {
+ p1.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_false(p1.userDisabled);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.isActive);
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+ p1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(p1.userDisabled);
+ do_check_false(p1.appDisabled);
+ do_check_false(p1.isActive);
+
+ do_execute_soon(run_test_20);
+ });
+}
+
+// Tests that you cannot disable the default theme
+function run_test_20() {
+ AddonManager.getAddonByID("default@tests.mozilla.org", function(d) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ try {
+ d.userDisabled = true;
+ do_throw("Disabling the default theme should throw an exception");
+ }
+ catch (e) {
+ }
+
+ do_execute_soon(run_test_21);
+ });
+}
+
+// Tests that cached copies of a lightweight theme have the right permissions
+// and pendingOperations during the onEnabling event
+function run_test_21() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ // Switch to a custom theme so we can test pendingOperations properly.
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "default@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ t1.userDisabled = false;
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ AddonManager.addAddonListener({
+ onEnabling: function(aAddon) {
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+ do_check_eq(aAddon.permissions, p1.permissions);
+ do_check_eq(aAddon.pendingOperations, p1.pendingOperations);
+ }
+ });
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ p1.userDisabled = false;
+ ensure_test_completed();
+
+ end_test();
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_types.js b/toolkit/mozapps/extensions/test/xpcshell/test_types.js
new file mode 100644
index 000000000..679f4808c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_types.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that custom types can be defined and undefined
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+ startupManager();
+
+ do_check_false("test" in AddonManager.addonTypes);
+ let types = AddonManager.addonTypes;
+
+ // The dumbest provider possible
+ var provider = {
+ };
+
+ var expectedAdd = "test";
+ var expectedRemove = null;
+
+ AddonManager.addTypeListener({
+ onTypeAdded: function(aType) {
+ do_check_eq(aType.id, expectedAdd);
+ expectedAdd = null;
+ },
+
+ onTypeRemoved: function(aType) {
+ do_check_eq(aType.id, expectedRemove);
+ expectedRemove = null;
+ }
+ });
+
+ AddonManagerPrivate.registerProvider(provider, [{
+ id: "test",
+ name: "Test",
+ uiPriority: 1
+ }, {
+ id: "t$e%st",
+ name: "Test",
+ uiPriority: 1
+ }]);
+
+ do_check_eq(expectedAdd, null);
+
+ do_check_true("test" in types);
+ do_check_eq(types["test"].name, "Test");
+ do_check_false("t$e%st" in types);
+
+ delete types["test"];
+ do_check_true("test" in types);
+
+ types["foo"] = "bar";
+ do_check_false("foo" in types);
+
+ expectedRemove = "test";
+
+ AddonManagerPrivate.unregisterProvider(provider);
+
+ do_check_eq(expectedRemove, null);
+
+ do_check_false("test" in AddonManager.addonTypes);
+ // The cached reference to addonTypes is live
+ do_check_false("test" in types);
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_undothemeuninstall.js b/toolkit/mozapps/extensions/test/xpcshell/test_undothemeuninstall.js
new file mode 100644
index 000000000..c804b3bd6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_undothemeuninstall.js
@@ -0,0 +1,421 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that forcing undo for uninstall works for themes
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+var defaultTheme = {
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function dummyLWTheme(id) {
+ return {
+ id: id || Math.random().toString(),
+ name: Math.random().toString(),
+ headerURL: "http://lwttest.invalid/a.png",
+ footerURL: "http://lwttest.invalid/b.png",
+ textcolor: Math.random().toString(),
+ accentcolor: Math.random().toString()
+ };
+}
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ do_register_cleanup(promiseShutdownManager);
+
+ run_next_test();
+}
+
+add_task(function* checkDefault() {
+ writeInstallRDFForExtension(defaultTheme, profileDir);
+ yield promiseRestartManager();
+
+ let d = yield promiseAddonByID("default@tests.mozilla.org");
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
+
+// Tests that uninstalling an enabled theme offers the option to undo
+add_task(function* uninstallEnabledOffersUndo() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let t1 = yield promiseAddonByID("theme1@tests.mozilla.org");
+
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+
+ t1.userDisabled = false;
+
+ yield promiseRestartManager();
+
+ let d = null;
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
+
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(t1, null);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
+
+//Tests that uninstalling an enabled theme can be undone
+add_task(function* canUndoUninstallEnabled() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let t1 = yield promiseAddonByID("theme1@tests.mozilla.org");
+
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+
+ t1.userDisabled = false;
+
+ yield promiseRestartManager();
+
+ let d = null;
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
+
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onOperationCancelled"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ t1.cancelUninstall();
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ t1.uninstall();
+ yield promiseRestartManager();
+});
+
+//Tests that uninstalling a disabled theme offers the option to undo
+add_task(function* uninstallDisabledOffersUndo() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(t1, null);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
+
+//Tests that uninstalling a disabled theme can be undone
+add_task(function* canUndoUninstallDisabled() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ t1.cancelUninstall();
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ t1.uninstall();
+ yield promiseRestartManager();
+});
+
+//Tests that uninstalling an enabled lightweight theme offers the option to undo
+add_task(function* uninstallLWTOffersUndo() {
+ // skipped since lightweight themes don't support undoable uninstall yet
+ return;
+ LightweightThemeManager.currentTheme = dummyLWTheme("theme1");
+
+ let [ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@personas.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
+
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(t1, null);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js b/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
new file mode 100644
index 000000000..a589361b6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
@@ -0,0 +1,792 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that forcing undo for uninstall works
+
+const APP_STARTUP = 1;
+const APP_SHUTDOWN = 2;
+const ADDON_ENABLE = 3;
+const ADDON_DISABLE = 4;
+const ADDON_INSTALL = 5;
+const ADDON_UNINSTALL = 6;
+const ADDON_UPGRADE = 7;
+const ADDON_DOWNGRADE = 8;
+
+const ID = "undouninstall1@tests.mozilla.org";
+const INCOMPAT_ID = "incompatible@tests.mozilla.org";
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+BootstrapMonitor.init();
+
+function getStartupReason(id) {
+ let info = BootstrapMonitor.started.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getShutdownReason(id) {
+ let info = BootstrapMonitor.stopped.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getInstallReason(id) {
+ let info = BootstrapMonitor.installed.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getUninstallReason(id) {
+ let info = BootstrapMonitor.uninstalled.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getStartupOldVersion(id) {
+ let info = BootstrapMonitor.started.get(id);
+ return info ? info.data.oldVersion : undefined;
+}
+
+function getShutdownNewVersion(id) {
+ let info = BootstrapMonitor.stopped.get(id);
+ return info ? info.data.newVersion : undefined;
+}
+
+function getInstallOldVersion(id) {
+ let info = BootstrapMonitor.installed.get(id);
+ return info ? info.data.oldVersion : undefined;
+}
+
+function getUninstallNewVersion(id) {
+ let info = BootstrapMonitor.uninstalled.get(id);
+ return info ? info.data.newVersion : undefined;
+}
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ do_register_cleanup(promiseShutdownManager);
+
+ run_next_test();
+}
+
+add_task(function* installAddon() {
+ let olda1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_eq(olda1, null);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ yield promiseRestartManager();
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+});
+
+// Uninstalling an add-on should work.
+add_task(function* uninstallAddon() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_neq(a1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ a1.uninstall(true);
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ let list = yield promiseAddonsWithOperationsByTypes(null);
+
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_eq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ do_check_false(dest.exists());
+ writeInstallRDFForExtension(addon1, profileDir);
+ yield promiseRestartManager();
+});
+
+// Cancelling the uninstall should send onOperationCancelled
+add_task(function* cancelUninstall() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ a1.uninstall(true);
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ do_check_eq(a1.pendingOperations, 0);
+
+ ensure_test_completed();
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+});
+
+// Uninstalling an item pending disable should still require a restart
+add_task(function* pendingDisableRequestRestart() {
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+ a1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+ do_check_true(a1.isActive);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+
+ yield promiseRestartManager();
+});
+
+// Test that uninstalling an inactive item should still allow cancelling
+add_task(function* uninstallInactiveIsCancellable() {
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ yield promiseRestartManager();
+});
+
+//Test that an inactive item can be uninstalled
+add_task(function* uninstallInactive() {
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ [ "onUninstalling", false ],
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_eq(a1, null);
+});
+
+// Tests that an enabled restartless add-on can be uninstalled and goes away
+// when the uninstall is committed
+add_task(function* uninstallRestartless() {
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+ ensure_test_completed();
+
+ let a1 = yield promiseAddonByID(ID);
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(ID);
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ // complete the uinstall
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(ID);
+
+ do_check_eq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+});
+
+//Tests that an enabled restartless add-on can be uninstalled and then cancelled
+add_task(function* cancelUninstallOfRestartless() {
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(ID);
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ shutdownManager();
+
+ do_check_eq(getShutdownReason(ID), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(ID), undefined);
+
+ startupManager(false);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getStartupReason(ID), APP_STARTUP);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+// Tests that reinstalling an enabled restartless add-on waiting to be
+// uninstalled aborts the uninstall and leaves the add-on enabled
+add_task(function* reinstallAddonAwaitingUninstall() {
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getUninstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(getInstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(getStartupReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ shutdownManager();
+
+ do_check_eq(getShutdownReason(ID), APP_SHUTDOWN);
+
+ startupManager(false);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getStartupReason(ID), APP_STARTUP);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+// Tests that a disabled restartless add-on can be uninstalled and goes away
+// when the uninstall is committed
+add_task(function* uninstallDisabledRestartless() {
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ // commit the uninstall
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_eq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ do_check_eq(getUninstallReason(ID), ADDON_UNINSTALL);
+});
+
+//Tests that a disabled restartless add-on can be uninstalled and then cancelled
+add_task(function* cancelUninstallDisabledRestartless() {
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+ ensure_test_completed();
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+ a1.userDisabled = true;
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+//Tests that reinstalling a disabled restartless add-on waiting to be
+//uninstalled aborts the uninstall and leaves the add-on disabled
+add_task(function* reinstallDisabledAddonAwaitingUninstall() {
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+ do_check_eq(getUninstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(getInstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+
+// Test that uninstalling a temporary addon can be canceled
+add_task(function* cancelUninstallTemporary() {
+ yield AddonManager.installTemporaryAddon(do_get_addon("test_undouninstall1"));
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_ENABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(a1.pendingOperations, 0);
+
+ yield promiseRestartManager();
+});
+
+// Tests that cancelling the uninstall of an incompatible restartless addon
+// does not start the addon
+add_task(function* cancelUninstallIncompatibleRestartless() {
+ yield promiseInstallAllFiles([do_get_addon("test_undoincompatible")]);
+
+ let a1 = yield promiseAddonByID(INCOMPAT_ID);
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
+ do_check_false(a1.isActive);
+
+ prepare_test({
+ "incompatible@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(INCOMPAT_ID);
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+
+ prepare_test({
+ "incompatible@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(INCOMPAT_ID);
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_false(a1.isActive);
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_uninstall.js b/toolkit/mozapps/extensions/test/xpcshell/test_uninstall.js
new file mode 100644
index 000000000..6b12489f2
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_uninstall.js
@@ -0,0 +1,216 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons can be uninstalled.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ do_test_pending();
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(olda1) {
+ do_check_eq(olda1, null);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ do_execute_soon(run_test_1);
+ });
+ }));
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Uninstalling an add-on should work.
+function run_test_1() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_neq(a1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ a1.uninstall();
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ do_execute_soon(check_test_1);
+ });
+ });
+}
+
+function check_test_1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ do_check_false(dest.exists());
+ writeInstallRDFForExtension(addon1, profileDir);
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Cancelling the uninstall should send onOperationCancelled
+function run_test_2() {
+ restartManager();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ a1.uninstall();
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ do_check_eq(a1.pendingOperations, 0);
+
+ ensure_test_completed();
+
+ do_execute_soon(check_test_2);
+ });
+}
+
+function check_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ run_test_3();
+ });
+}
+
+// Uninstalling an item pending disable should still require a restart
+function run_test_3() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+ a1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+ do_check_true(a1.isActive);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall();
+
+ check_test_3();
+ });
+}
+
+function check_test_3() {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Test that uninstalling an inactive item should happen without a restart
+function run_test_4() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ check_test_4();
+ });
+}
+
+function check_test_4() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_update.js b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
new file mode 100644
index 000000000..b7e32d59f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -0,0 +1,1310 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+// This test requires lightweight themes update to be enabled even if the app
+// doesn't support lightweight themes.
+Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const PARAMS = "?%REQ_VERSION%/%ITEM_ID%/%ITEM_VERSION%/%ITEM_MAXAPPVERSION%/" +
+ "%ITEM_STATUS%/%APP_ID%/%APP_VERSION%/%CURRENT_APP_VERSION%/" +
+ "%APP_OS%/%APP_ABI%/%APP_LOCALE%/%UPDATE_TYPE%";
+
+var gInstallDate;
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_update.rdf", testserver);
+mapFile("/data/test_update.xml", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+let originalSyncGUID;
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "5",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Verify that an update is available and can be installed.
+function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_eq(a1.releaseNotesURI, null);
+ do_check_true(a1.foreignInstall);
+ do_check_neq(a1.syncGUID, null);
+
+ originalSyncGUID = a1.syncGUID;
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["applyBackgroundUpdates"]]
+ ]
+ });
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ check_test_completed();
+
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ prepare_test({}, [
+ "onNewInstall",
+ ]);
+
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ do_check_eq(addon, a1);
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, addon);
+ do_check_eq(install.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+
+ // Verify that another update check returns the same AddonInstall
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(newAddon, newInstall) {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+ do_check_eq(newAddon, addon);
+ do_check_eq(newInstall, install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_1);
+ install.install();
+ });
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_1(install) {
+ ensure_test_completed();
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ run_test_2(install);
+ return false;
+}
+
+// Continue installing the update.
+function run_test_2(install) {
+ // Verify that another update check returns no new update
+ install.existingAddon.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should find no available update when one is already downloading");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_2);
+ install.install();
+ });
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+}
+
+function check_test_2() {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(olda1) {
+ do_check_neq(olda1, null);
+ do_check_eq(olda1.version, "1.0");
+ do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+ shutdownManager();
+
+ startupManager();
+
+ do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ do_check_true(a1.foreignInstall);
+ do_check_neq(a1.syncGUID, null);
+ do_check_eq(originalSyncGUID, a1.syncGUID);
+
+ a1.uninstall();
+ do_execute_soon(run_test_3);
+ });
+ }));
+}
+
+
+// Check that an update check finds compatibility updates and applies them
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isCompatibleWith("0"));
+
+ a2.findUpdates({
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_eq(addon, a2);
+ do_execute_soon(check_test_3);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_3() {
+ restartManager();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ a2.uninstall();
+
+ run_test_4();
+ });
+}
+
+// Checks that we see no compatibility information when there is none.
+function run_test_4() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5"));
+ do_check_false(a3.isCompatibleWith("2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen compatibility information");
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ this.sawUpdate = true;
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ run_test_5();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Checks that compatibility info for future apps are detected but don't make
+// the item compatibile.
+function run_test_5() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5"));
+ do_check_false(a3.isCompatibleWith("2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ this.sawUpdate = true;
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should have seen some compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ do_execute_soon(check_test_5);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED, "3.0");
+ });
+}
+
+function check_test_5() {
+ restartManager();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+
+ a3.uninstall();
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Test that background update checks work
+function run_test_6() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({}, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], continue_test_6);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function continue_test_6(install) {
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon1@tests.mozilla.org");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_6));
+}
+
+function check_test_6(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ a1.uninstall();
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Test that background update checks work for lightweight themes
+function run_test_7() {
+ restartManager();
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ };
+
+ // XXX The lightweight theme manager strips non-https updateURLs so hack it
+ // back in.
+ let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
+ do_check_eq(themes.length, 1);
+ themes[0].updateURL = "http://localhost:" + gPort + "/data/lwtheme.js";
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+
+ testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
+ // Server will specify an expiry in one year.
+ let expiry = new Date();
+ expiry.setFullYear(expiry.getFullYear() + 1);
+ response.setHeader("Expires", expiry.toUTCString(), false);
+ response.write(JSON.stringify({
+ id: "1",
+ version: "2",
+ name: "Updated Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index2.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon2.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ }));
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_true(p1.isActive);
+ do_check_eq(p1.installDate.getTime(), p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.installDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall"
+ ], check_test_7);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_7() {
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "2");
+ do_check_eq(p1.name, "Updated Theme");
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.updateDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ run_test_7_cache();
+ });
+}
+
+// Test that background update checks for lightweight themes do not use the cache
+// The update body from test 7 shouldn't be used since the cache should be bypassed.
+function run_test_7_cache() {
+ // XXX The lightweight theme manager strips non-https updateURLs so hack it
+ // back in.
+ let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
+ do_check_eq(themes.length, 1);
+ themes[0].updateURL = "http://localhost:" + gPort + "/data/lwtheme.js";
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+
+ testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
+ response.write(JSON.stringify({
+ id: "1",
+ version: "3",
+ name: "Updated Theme v.3",
+ description: "A test theme v.3",
+ author: "John Smith",
+ homepageURL: "http://localhost:" + gPort + "/data/index3.html?v=3",
+ headerURL: "http://localhost:" + gPort + "/data/header.png?v=3",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png?v=3",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png?v=3",
+ iconURL: "http://localhost:" + gPort + "/data/icon2.png?v=3",
+ updateURL: "https://localhost:" + gPort + "/data/lwtheme.js?v=3"
+ }));
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "2");
+ do_check_eq(p1.name, "Updated Theme");
+ do_check_true(p1.isActive);
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall"
+ ], check_test_7_cache);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_7_cache() {
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ let currentTheme = LightweightThemeManager.currentTheme;
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "3");
+ do_check_eq(p1.name, "Updated Theme v.3");
+ do_check_eq(p1.description, "A test theme v.3");
+ do_print(JSON.stringify(p1));
+ do_check_eq(p1.creator.name, "John Smith");
+ do_check_eq(p1.homepageURL, "http://localhost:" + gPort + "/data/index3.html?v=3");
+ do_check_eq(p1.screenshots[0].url, "http://localhost:" + gPort + "/data/preview.png?v=3");
+ do_check_eq(p1.iconURL, "http://localhost:" + gPort + "/data/icon2.png?v=3");
+ do_check_eq(currentTheme.headerURL, "http://localhost:" + gPort + "/data/header.png?v=3");
+ do_check_eq(currentTheme.footerURL, "http://localhost:" + gPort + "/data/footer.png?v=3");
+ do_check_eq(currentTheme.updateURL, "https://localhost:" + gPort + "/data/lwtheme.js?v=3");
+
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Verify the parameter escaping in update urls.
+function run_test_8() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "67.0.5b1",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.3+",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }, {
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "0.5ab6",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 4",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 5",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 6",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(a2) {
+ a2.userDisabled = true;
+ restartManager();
+
+ testserver.registerPathHandler("/data/param_test.rdf", function(request, response) {
+ do_check_neq(request.queryString, "");
+ let [req_version, item_id, item_version,
+ item_maxappversion, item_status,
+ app_id, app_version, current_app_version,
+ app_os, app_abi, app_locale, update_type] =
+ [decodeURIComponent(a) for each (a in request.queryString.split("/"))];
+
+ do_check_eq(req_version, "2");
+
+ switch(item_id) {
+ case "addon1@tests.mozilla.org":
+ do_check_eq(item_version, "5.0");
+ do_check_eq(item_maxappversion, "2");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "97");
+ break;
+ case "addon2@tests.mozilla.org":
+ do_check_eq(item_version, "67.0.5b1");
+ do_check_eq(item_maxappversion, "3");
+ do_check_eq(item_status, "userDisabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "49");
+ break;
+ case "addon3@tests.mozilla.org":
+ do_check_eq(item_version, "1.3+");
+ do_check_eq(item_maxappversion, "0");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "112");
+ break;
+ case "addon4@tests.mozilla.org":
+ do_check_eq(item_version, "0.5ab6");
+ do_check_eq(item_maxappversion, "5");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "2");
+ do_check_eq(update_type, "98");
+ break;
+ case "addon5@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "35");
+ break;
+ case "addon6@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "99");
+ break;
+ default:
+ do_throw("Update request for unexpected add-on " + item_id);
+ }
+
+ do_check_eq(app_id, "xpcshell@tests.mozilla.org");
+ do_check_eq(current_app_version, "1");
+ do_check_eq(app_os, "XPCShell");
+ do_check_eq(app_abi, "noarch-spidermonkey");
+ do_check_eq(app_locale, "fr-FR");
+
+ request.setStatusLine(null, 500, "Server Error");
+ });
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6]) {
+ let count = 6;
+
+ function run_next_test() {
+ a1.uninstall();
+ a2.uninstall();
+ a3.uninstall();
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+
+ restartManager();
+ run_test_9();
+ }
+
+ let compatListener = {
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(run_next_test);
+ }
+ };
+
+ let updateListener = {
+ onUpdateAvailable: function(addon, update) {
+ // Dummy so the update checker knows we care about new versions
+ },
+
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(run_next_test);
+ }
+ };
+
+ a1.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ a2.findUpdates(compatListener, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
+ a3.findUpdates(updateListener, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ a4.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "2");
+ a5.findUpdates(compatListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ a6.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ }));
+}
+
+// Tests that if an install.rdf claims compatibility then the add-on will be
+// seen as compatible regardless of what the update.rdf says.
+function run_test_9() {
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_true(a4.isActive);
+ do_check_true(a4.isCompatible);
+
+ run_test_10();
+ });
+}
+
+// Tests that a normal update check won't decrease a targetApplication's
+// maxVersion.
+function run_test_10() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+
+ run_test_11();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+// Tests that an update check for a new application will decrease a
+// targetApplication's maxVersion.
+function run_test_11() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+
+ do_execute_soon(run_test_12);
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+}
+
+// Check that the decreased maxVersion applied and disables the add-on
+function run_test_12() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_true(a4.isActive);
+ do_check_true(a4.isCompatible);
+
+ a4.uninstall();
+ do_execute_soon(run_test_13);
+ });
+}
+
+// Tests that a compatibility update is passed to the listener when there is
+// compatibility info for the current version of the app but not for the
+// version of the app that the caller requested an update check for, when
+// strict compatibility checking is disabled.
+function run_test_13() {
+ restartManager();
+
+ // Not initially compatible but the update check will make it compatible
+ writeInstallRDFForExtension({
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 7",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_true(a7.isActive);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isCompatibleWith("0"));
+
+ a7.findUpdates({
+ sawUpdate: false,
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should have seen compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+ do_execute_soon(check_test_13);
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "3.0");
+ });
+}
+
+function check_test_13() {
+ restartManager();
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_true(a7.isActive);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+
+ a7.uninstall();
+ do_execute_soon(run_test_14);
+ });
+}
+
+// Test that background update checks doesn't update an add-on that isn't
+// allowed to update automatically.
+function run_test_14() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon1@tests.mozilla.org" &&
+ aInstall.existingAddon.id != "addon8@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ do_throw("Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ do_throw("Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_check_eq(aInstall.existingAddon.pendingUpgrade.install, aInstall);
+
+ do_execute_soon(check_test_14);
+ },
+
+ onInstallFailed: function(aInstall) {
+ do_throw("Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ do_throw("Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_14() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_neq(a8, null);
+ do_check_eq(a8.version, "1.0");
+ a8.uninstall();
+
+ do_execute_soon(run_test_15);
+ });
+}
+
+// Test that background update checks doesn't update an add-on that is
+// pending uninstall
+function run_test_15() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.uninstall();
+ do_check_false(hasFlag(a8.permissions, AddonManager.PERM_CAN_UPGRADE));
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon1@tests.mozilla.org" &&
+ aInstall.existingAddon.id != "addon8@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ do_throw("Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ do_throw("Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_execute_soon(check_test_15);
+ },
+
+ onInstallFailed: function(aInstall) {
+ do_throw("Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ do_throw("Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_15() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_eq(a8, null);
+
+ do_execute_soon(run_test_16);
+ });
+}
+
+function run_test_16() {
+ restartManager();
+
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_1_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a1) {
+ do_check_neq(a1.syncGUID, null);
+ let oldGUID = a1.syncGUID;
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_2_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2.syncGUID, null);
+ do_check_eq(oldGUID, a2.syncGUID);
+
+ a2.uninstall();
+ do_execute_soon(run_test_17);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Test that the update check correctly observes the
+// extensions.strictCompatibility pref and compatibility overrides.
+function run_test_17() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 9",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ do_check_eq(aInstall.version, "3.0");
+ },
+ onDownloadFailed: function(aInstall) {
+ AddonManager.getAddonByID("addon9@tests.mozilla.org", function(a9) {
+ a9.uninstall();
+ do_execute_soon(run_test_18);
+ });
+ }
+ });
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+// Tests that compatibility updates are applied to addons when the updated
+// compatibility data wouldn't match with strict compatibility enabled.
+function run_test_18() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon10@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 10",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon10@tests.mozilla.org", function(a10) {
+ do_check_neq(a10, null);
+
+ a10.findUpdates({
+ onNoCompatibilityUpdateAvailable: function() {
+ do_throw("Should have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ a10.uninstall();
+ do_execute_soon(run_test_19);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_19() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 11",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+ do_check_neq(a11, null);
+
+ a11.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ a11.uninstall();
+ do_execute_soon(run_test_20);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Test that the update succeeds when the update.rdf URN contains a type prefix
+// different from the add-on type
+function run_test_20() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon12@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 12",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({}, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], continue_test_20);
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function continue_test_20(install) {
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon12@tests.mozilla.org");
+
+ prepare_test({
+ "addon12@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_20));
+}
+
+function check_test_20(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("addon12@tests.mozilla.org", function(a12) {
+ do_check_neq(a12, null);
+ do_check_eq(a12.version, "2.0");
+ do_check_eq(a12.type, "extension");
+ a12.uninstall();
+
+ do_execute_soon(() => {
+ restartManager();
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js b/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js
new file mode 100644
index 000000000..d513f4adf
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js
@@ -0,0 +1,142 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test cancelling add-on update checks while in progress (bug 925389)
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Set up an HTTP server to respond to update requests
+Components.utils.import("resource://testing-common/httpd.js");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ // Kick off the task-based tests...
+ run_next_test();
+}
+
+// Install one extension
+// Start download of update check (but delay HTTP response)
+// Cancel update check
+// - ensure we get cancel notification
+// complete HTTP response
+// - ensure no callbacks after cancel
+// - ensure update is gone
+
+// Create an addon update listener containing a promise
+// that resolves when the update is cancelled
+function makeCancelListener() {
+ let updated = Promise.defer();
+ return {
+ onUpdateAvailable: function(addon, install) {
+ updated.reject("Should not have seen onUpdateAvailable notification");
+ },
+
+ onUpdateFinished: function(aAddon, aError) {
+ do_print("onUpdateCheckFinished: " + aAddon.id + " " + aError);
+ updated.resolve(aError);
+ },
+ promise: updated.promise
+ };
+}
+
+// Set up the HTTP server so that we can control when it responds
+let httpReceived = Promise.defer();
+function dataHandler(aRequest, aResponse) {
+ asyncResponse = aResponse;
+ aResponse.processAsync();
+ httpReceived.resolve([aRequest, aResponse]);
+}
+var testserver = new HttpServer();
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+testserver.registerPathHandler("/data/test_update.rdf", dataHandler);
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// Set up an add-on for update check
+writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+}, profileDir);
+
+add_task(function cancel_during_check() {
+ startupManager();
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_neq(a1, null);
+
+ let listener = makeCancelListener();
+ a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+
+ // Wait for the http request to arrive
+ let [request, response] = yield httpReceived.promise;
+
+ // cancelUpdate returns true if there is an update check in progress
+ do_check_true(a1.cancelUpdate());
+
+ let updateResult = yield listener.promise;
+ do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult);
+
+ // Now complete the HTTP request
+ let file = do_get_cwd();
+ file.append("data");
+ file.append("test_update.rdf");
+ let data = loadFile(file);
+ response.write(data);
+ response.finish();
+
+ // trying to cancel again should return false, i.e. nothing to cancel
+ do_check_false(a1.cancelUpdate());
+
+ yield true;
+});
+
+// Test that update check is cancelled if the XPI provider shuts down while
+// the update check is in progress
+add_task(function shutdown_during_check() {
+ // Reset our HTTP listener
+ httpReceived = Promise.defer();
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_neq(a1, null);
+
+ let listener = makeCancelListener();
+ a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+
+ // Wait for the http request to arrive
+ let [request, response] = yield httpReceived.promise;
+
+ shutdownManager();
+
+ let updateResult = yield listener.promise;
+ do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult);
+
+ // Now complete the HTTP request
+ let file = do_get_cwd();
+ file.append("data");
+ file.append("test_update.rdf");
+ let data = loadFile(file);
+ response.write(data);
+ response.finish();
+
+ // trying to cancel again should return false, i.e. nothing to cancel
+ do_check_false(a1.cancelUpdate());
+
+ yield testserver.stop(Promise.defer().resolve);
+});
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js b/toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js
new file mode 100644
index 000000000..6043b1792
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js
@@ -0,0 +1,184 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update check correctly fills in the
+// %COMPATIBILITY_MODE% token in the update URL.
+
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_updatecompatmode_ignore.rdf", testserver);
+mapFile("/data/test_updatecompatmode_normal.rdf", testserver);
+mapFile("/data/test_updatecompatmode_strict.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "compatmode-normal@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - normal"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "compatmode-strict@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - strict"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "compatmode-strict-optin@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - strict opt-in",
+ strictCompatibility: true
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "compatmode-ignore@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - ignore",
+ }, profileDir);
+
+ startupManager();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+
+// Strict compatibility checking disabled.
+function run_test_1() {
+ do_print("Testing with strict compatibility checking disabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ AddonManager.getAddonByID("compatmode-normal@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_check_eq(install.version, "2.0")
+ },
+
+ onUpdateFinished: function() {
+ run_test_2();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Strict compatibility checking enabled.
+function run_test_2() {
+ do_print("Testing with strict compatibility checking enabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+ AddonManager.getAddonByID("compatmode-strict@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_check_eq(install.version, "2.0")
+ },
+
+ onUpdateFinished: function() {
+ run_test_3();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Strict compatibility checking opt-in.
+function run_test_3() {
+ do_print("Testing with strict compatibility disabled, but addon opt-in");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ AddonManager.getAddonByID("compatmode-strict-optin@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ run_test_4();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Compatibility checking disabled.
+function run_test_4() {
+ do_print("Testing with all compatibility checking disabled");
+ AddonManager.checkCompatibility = false;
+ AddonManager.getAddonByID("compatmode-ignore@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_check_eq(install.version, "2.0")
+ },
+
+ onUpdateFinished: function() {
+ end_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
new file mode 100644
index 000000000..672594088
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work correctly when compatibility
+// check is disabled.
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_update.rdf", testserver);
+mapFile("/data/test_update.xml", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ run_test_1();
+}
+
+// Test that the update check correctly observes the
+// extensions.strictCompatibility pref and compatibility overrides.
+function run_test_1() {
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 9",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ do_check_eq(aInstall.version, "4.0");
+ },
+ onDownloadFailed: function(aInstall) {
+ do_execute_soon(run_test_2);
+ }
+ });
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_2() {
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 11",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+ do_check_neq(a11, null);
+
+ a11.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ end_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
new file mode 100644
index 000000000..0474535f1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
@@ -0,0 +1,1085 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// This test requires lightweight themes update to be enabled even if the app
+// doesn't support lightweight themes.
+Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const PARAMS = "?%REQ_VERSION%/%ITEM_ID%/%ITEM_VERSION%/%ITEM_MAXAPPVERSION%/" +
+ "%ITEM_STATUS%/%APP_ID%/%APP_VERSION%/%CURRENT_APP_VERSION%/" +
+ "%APP_OS%/%APP_ABI%/%APP_LOCALE%/%UPDATE_TYPE%";
+
+var gInstallDate;
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_update.rdf", testserver);
+mapFile("/data/test_update.xml", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "5",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ Services.prefs.clearUserPref(PREF_EM_STRICT_COMPATIBILITY);
+
+ testserver.stop(do_test_finished);
+}
+
+// Verify that an update is available and can be installed.
+function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_eq(a1.releaseNotesURI, null);
+
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["applyBackgroundUpdates"]]
+ ]
+ });
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ check_test_completed();
+
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ prepare_test({}, [
+ "onNewInstall",
+ ]);
+
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ do_check_eq(addon, a1);
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, addon);
+ do_check_eq(install.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+
+ // Verify that another update check returns the same AddonInstall
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(newAddon, newInstall) {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+ do_check_eq(newAddon, addon);
+ do_check_eq(newInstall, install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_1);
+ install.install();
+ });
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_1(install) {
+ ensure_test_completed();
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ run_test_2(install);
+ return false;
+}
+
+// Continue installing the update.
+function run_test_2(install) {
+ // Verify that another update check returns no new update
+ install.existingAddon.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should find no available update when one is already downloading");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_2);
+ install.install();
+ });
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+}
+
+function check_test_2() {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(olda1) {
+ do_check_neq(olda1, null);
+ do_check_eq(olda1.version, "1.0");
+ do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+ shutdownManager();
+
+ startupManager();
+
+ do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+
+ a1.uninstall();
+ do_execute_soon(run_test_3);
+ });
+ }));
+}
+
+
+// Check that an update check finds compatibility updates and applies them
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+ do_check_true(a2.appDisabled);
+ do_check_true(a2.isCompatibleWith("0"));
+
+ a2.findUpdates({
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_eq(addon, a2);
+ do_execute_soon(check_test_3);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_3() {
+ restartManager();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ a2.uninstall();
+
+ run_test_4();
+ });
+}
+
+// Checks that we see no compatibility information when there is none.
+function run_test_4() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5"));
+ do_check_false(a3.isCompatibleWith("2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen compatibility information");
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ this.sawUpdate = true;
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ run_test_5();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Checks that compatibility info for future apps are detected but don't make
+// the item compatibile.
+function run_test_5() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5"));
+ do_check_false(a3.isCompatibleWith("2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ this.sawUpdate = true;
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should have seen some compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ do_execute_soon(check_test_5);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED, "3.0");
+ });
+}
+
+function check_test_5() {
+ restartManager();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+
+ a3.uninstall();
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Test that background update checks work
+function run_test_6() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({}, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], continue_test_6);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function continue_test_6(install) {
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon1@tests.mozilla.org");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_6));
+}
+
+function check_test_6(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ a1.uninstall();
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Test that background update checks work for lightweight themes
+function run_test_7() {
+ restartManager();
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ };
+
+ // XXX The lightweight theme manager strips non-https updateURLs so hack it
+ // back in.
+ let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
+ do_check_eq(themes.length, 1);
+ themes[0].updateURL = "http://localhost:" + gPort + "/data/lwtheme.js";
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+
+ testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
+ response.write(JSON.stringify({
+ id: "1",
+ version: "2",
+ name: "Updated Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index2.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon2.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ }));
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_true(p1.isActive);
+ do_check_eq(p1.installDate.getTime(), p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.installDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall"
+ ], check_test_7);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_7() {
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "2");
+ do_check_eq(p1.name, "Updated Theme");
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.updateDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Verify the parameter escaping in update urls.
+function run_test_8() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "67.0.5b1",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.3+",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }, {
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "0.5ab6",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 4",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 5",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 6",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(a2) {
+ a2.userDisabled = true;
+ restartManager();
+
+ testserver.registerPathHandler("/data/param_test.rdf", function(request, response) {
+ do_check_neq(request.queryString, "");
+ let [req_version, item_id, item_version,
+ item_maxappversion, item_status,
+ app_id, app_version, current_app_version,
+ app_os, app_abi, app_locale, update_type] =
+ [decodeURIComponent(a) for each (a in request.queryString.split("/"))];
+
+ do_check_eq(req_version, "2");
+
+ switch(item_id) {
+ case "addon1@tests.mozilla.org":
+ do_check_eq(item_version, "5.0");
+ do_check_eq(item_maxappversion, "2");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "97");
+ break;
+ case "addon2@tests.mozilla.org":
+ do_check_eq(item_version, "67.0.5b1");
+ do_check_eq(item_maxappversion, "3");
+ do_check_eq(item_status, "userDisabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "49");
+ break;
+ case "addon3@tests.mozilla.org":
+ do_check_eq(item_version, "1.3+");
+ do_check_eq(item_maxappversion, "0");
+ do_check_eq(item_status, "userEnabled,incompatible");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "112");
+ break;
+ case "addon4@tests.mozilla.org":
+ do_check_eq(item_version, "0.5ab6");
+ do_check_eq(item_maxappversion, "5");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "2");
+ do_check_eq(update_type, "98");
+ break;
+ case "addon5@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "35");
+ break;
+ case "addon6@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "99");
+ break;
+ default:
+ do_throw("Update request for unexpected add-on " + item_id);
+ }
+
+ do_check_eq(app_id, "xpcshell@tests.mozilla.org");
+ do_check_eq(current_app_version, "1");
+ do_check_eq(app_os, "XPCShell");
+ do_check_eq(app_abi, "noarch-spidermonkey");
+ do_check_eq(app_locale, "fr-FR");
+
+ request.setStatusLine(null, 500, "Server Error");
+ });
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6]) {
+ let count = 6;
+
+ function run_next_test() {
+ a1.uninstall();
+ a2.uninstall();
+ a3.uninstall();
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+
+ restartManager();
+ run_test_9();
+ }
+
+ let compatListener = {
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(run_next_test);
+ }
+ };
+
+ let updateListener = {
+ onUpdateAvailable: function(addon, update) {
+ // Dummy so the update checker knows we care about new versions
+ },
+
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(run_next_test);
+ }
+ };
+
+ a1.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ a2.findUpdates(compatListener, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
+ a3.findUpdates(updateListener, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ a4.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "2");
+ a5.findUpdates(compatListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ a6.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ }));
+}
+
+// Tests that if an install.rdf claims compatibility then the add-on will be
+// seen as compatible regardless of what the update.rdf says.
+function run_test_9() {
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_true(a4.isActive);
+ do_check_true(a4.isCompatible);
+
+ run_test_10();
+ });
+}
+
+// Tests that a normal update check won't decrease a targetApplication's
+// maxVersion.
+function run_test_10() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+
+ run_test_11();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+// Tests that an update check for a new application will decrease a
+// targetApplication's maxVersion.
+function run_test_11() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_false(addon.isCompatible);
+
+ do_execute_soon(run_test_12);
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+}
+
+// Check that the decreased maxVersion applied and disables the add-on
+function run_test_12() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_false(a4.isActive);
+ do_check_false(a4.isCompatible);
+
+ a4.uninstall();
+ do_execute_soon(run_test_13);
+ });
+}
+
+// Tests that no compatibility update is passed to the listener when there is
+// compatibility info for the current version of the app but not for the
+// version of the app that the caller requested an update check for.
+function run_test_13() {
+ restartManager();
+
+ // Not initially compatible but the update check will make it compatible
+ writeInstallRDFForExtension({
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 7",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_false(a7.isCompatible);
+ do_check_true(a7.appDisabled);
+ do_check_true(a7.isCompatibleWith("0"));
+
+ a7.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+ do_execute_soon(check_test_13);
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "3.0");
+ });
+}
+
+function check_test_13() {
+ restartManager();
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_true(a7.isActive);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+
+ a7.uninstall();
+ do_execute_soon(run_test_14);
+ });
+}
+
+// Test that background update checks doesn't update an add-on that isn't
+// allowed to update automatically.
+function run_test_14() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon1@tests.mozilla.org" &&
+ aInstall.existingAddon.id != "addon8@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ do_throw("Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ do_throw("Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_check_eq(aInstall.existingAddon.pendingUpgrade.install, aInstall);
+ do_execute_soon(check_test_14);
+ },
+
+ onInstallFailed: function(aInstall) {
+ do_throw("Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ do_throw("Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_14() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_neq(a8, null);
+ do_check_eq(a8.version, "1.0");
+ a8.uninstall();
+
+ do_execute_soon(run_test_15);
+ });
+}
+
+// Test that background update checks doesn't update an add-on that is
+// pending uninstall
+function run_test_15() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.uninstall();
+ do_check_false(hasFlag(a8.permissions, AddonManager.PERM_CAN_UPGRADE));
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon1@tests.mozilla.org" &&
+ aInstall.existingAddon.id != "addon8@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ do_throw("Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ do_throw("Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_execute_soon(check_test_15);
+ },
+
+ onInstallFailed: function(aInstall) {
+ do_throw("Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ do_throw("Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+}
+
+function check_test_15() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_eq(a8, null);
+
+ do_execute_soon(run_test_16);
+ });
+}
+
+// Test that the update check correctly observes the
+// extensions.strictCompatibility pref and compatibility overrides.
+function run_test_16() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 9",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ do_check_eq(aInstall.version, "2.0");
+ },
+ onDownloadFailed: function(aInstall) {
+ do_execute_soon(run_test_17);
+ }
+ });
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_17() {
+
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 11",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+ do_check_neq(a11, null);
+
+ a11.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ do_execute_soon(end_test);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js b/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
new file mode 100644
index 000000000..d2e15103b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
@@ -0,0 +1,312 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that AddonUpdateChecker works correctly
+
+Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.start(4444);
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Test that a basic update check returns the expected available updates
+function run_test_1() {
+ AddonUpdateChecker.checkForUpdates("updatecheck1@tests.mozilla.org", null,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ check_test_1(updates);
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function check_test_1(updates) {
+ do_check_eq(updates.length, 5);
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates);
+ do_check_neq(update, null);
+ do_check_eq(update.version, 3);
+ update = AddonUpdateChecker.getCompatibilityUpdate(updates, "2");
+ do_check_neq(update, null);
+ do_check_eq(update.version, 2);
+ do_check_eq(update.targetApplications[0].minVersion, 1);
+ do_check_eq(update.targetApplications[0].maxVersion, 2);
+
+ run_test_2();
+}
+
+/*
+ * Tests that the security checks are applied correctly
+ *
+ * Test signature updateHash updateLink expected
+ *--------------------------------------------------------
+ * 2 absent absent http fail
+ * 3 broken absent http fail
+ * 4 correct absent http no update
+ * 5 correct sha1 http update
+ * 6 corrent absent https update
+ * 7 corrent sha1 https update
+ * 8 corrent md2 http no update
+ * 9 corrent md2 https update
+ */
+
+let updateKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK426erD/H3XtsjvaB5+PJqbhj" +
+ "Zc9EDI5OCJS8R3FIObJ9ZHJK1TXeaE7JWqt9WUmBWTEFvwS+FI9vWu8058N9CHhD" +
+ "NyeP6i4LuUYjTURnn7Yw/IgzyIJ2oKsYa32RuxAyteqAWqPT/J63wBixIeCxmysf" +
+ "awB/zH4KaPiY3vnrzQIDAQAB";
+
+function run_test_2() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_5@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_throw("Expected the update check to fail");
+ },
+
+ onUpdateCheckError: function(status) {
+ run_test_3();
+ }
+ });
+}
+
+function run_test_3() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_7@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_throw("Expected the update check to fail");
+ },
+
+ onUpdateCheckError: function(status) {
+ run_test_4();
+ }
+ });
+}
+
+function run_test_4() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_8@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ do_check_false("updateURL" in updates[0]);
+ run_test_5();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_5() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_9@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ do_check_eq(updates[0].version, "2.0");
+ do_check_true("updateURL" in updates[0]);
+ run_test_6();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_6() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_10@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ do_check_eq(updates[0].version, "2.0");
+ do_check_true("updateURL" in updates[0]);
+ run_test_7();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_7() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_11@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ do_check_eq(updates[0].version, "2.0");
+ do_check_true("updateURL" in updates[0]);
+ run_test_8();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_8() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_12@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ do_check_false("updateURL" in updates[0]);
+ run_test_9();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_9() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_13@tests.mozilla.org",
+ updateKey,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ do_check_eq(updates[0].version, "2.0");
+ do_check_true("updateURL" in updates[0]);
+ run_test_10();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_10() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_14@tests.mozilla.org",
+ null,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 0);
+ run_test_11();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_11() {
+ AddonUpdateChecker.checkForUpdates("test_bug378216_15@tests.mozilla.org",
+ null,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_throw("Update check should have failed");
+ },
+
+ onUpdateCheckError: function(status) {
+ do_check_eq(status, AddonUpdateChecker.ERROR_PARSE_ERROR);
+ run_test_12();
+ }
+ });
+}
+
+function run_test_12() {
+ AddonUpdateChecker.checkForUpdates("ignore-compat@tests.mozilla.org",
+ null,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 3);
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+ null,
+ null,
+ true);
+ do_check_neq(update, null);
+ do_check_eq(update.version, 2);
+ run_test_13();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_13() {
+ AddonUpdateChecker.checkForUpdates("compat-override@tests.mozilla.org",
+ null,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 3);
+ let overrides = [{
+ type: "incompatible",
+ minVersion: 1,
+ maxVersion: 2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 0.1,
+ appMaxVersion: 0.2
+ }, {
+ type: "incompatible",
+ minVersion: 2,
+ maxVersion: 2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 1,
+ appMaxVersion: 2
+ }];
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+ null,
+ null,
+ true,
+ false,
+ overrides);
+ do_check_neq(update, null);
+ do_check_eq(update.version, 1);
+ run_test_14();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
+
+function run_test_14() {
+ AddonUpdateChecker.checkForUpdates("compat-strict-optin@tests.mozilla.org",
+ null,
+ "http://localhost:4444/data/test_updatecheck.rdf", {
+ onUpdateCheckComplete: function(updates) {
+ do_check_eq(updates.length, 1);
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+ null,
+ null,
+ true,
+ false);
+ do_check_eq(update, null);
+ end_test();
+ },
+
+ onUpdateCheckError: function(status) {
+ do_throw("Update check failed with status " + status);
+ }
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_updateid.js b/toolkit/mozapps/extensions/test/xpcshell/test_updateid.js
new file mode 100644
index 000000000..e8aea0301
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updateid.js
@@ -0,0 +1,422 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that updating an add-on to a new ID works
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function resetPrefs() {
+ Services.prefs.setIntPref("bootstraptest.active_version", -1);
+ Services.prefs.setIntPref("bootstraptest.installed_version", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.install_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function getInstalledVersion() {
+ return Services.prefs.getIntPref("bootstraptest.installed_version");
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.start(4444);
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function installUpdate(aInstall, aCallback) {
+ aInstall.addListener({
+ onInstallEnded: function(aInstall) {
+ // give the startup time to run
+ do_execute_soon(function() {
+ aCallback(aInstall);
+ });
+ }
+ });
+
+ aInstall.install();
+}
+
+// Verify that an update to an add-on with a new ID uninstalls the old add-on
+function run_test_1() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:4444/data/test_updateid.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ a1.findUpdates({
+ onUpdateAvailable: function(addon, install) {
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, a1);
+
+ installUpdate(install, check_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_1(install) {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ // Existing add-on should have a pending upgrade
+ do_check_neq(a1.pendingUpgrade, null);
+ do_check_eq(a1.pendingUpgrade.id, "addon2@tests.mozilla.org");
+ do_check_eq(a1.pendingUpgrade.install.existingAddon, a1);
+ do_check_neq(a1.syncGUID);
+
+ let a1SyncGUID = a1.syncGUID;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ // Should have uninstalled the old and installed the new
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+ do_check_neq(a2.syncGUID, null);
+
+ // The Sync GUID should change when the ID changes
+ do_check_neq(a1SyncGUID, a2.syncGUID);
+
+ a2.uninstall();
+
+ do_execute_soon(run_test_2);
+ });
+ }));
+}
+
+// Test that when the new add-on already exists we just upgrade that
+function run_test_2() {
+ restartManager();
+ shutdownManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:4444/data/test_updateid.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ a1.findUpdates({
+ onUpdateAvailable: function(addon, install) {
+ installUpdate(install, check_test_2);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_2(install) {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ callback_soon(function([a1, a2]) {
+ do_check_eq(a1.pendingUpgrade, null);
+ // Existing add-on should have a pending upgrade
+ do_check_neq(a2.pendingUpgrade, null);
+ do_check_eq(a2.pendingUpgrade.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.pendingUpgrade.install.existingAddon, a2);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ // Should have uninstalled the old and installed the new
+ do_check_neq(a1, null);
+ do_check_neq(a2, null);
+
+ a1.uninstall();
+ a2.uninstall();
+
+ do_execute_soon(run_test_3);
+ });
+ }));
+}
+
+// Test that we rollback correctly when removing the old add-on fails
+function run_test_3() {
+ restartManager();
+ shutdownManager();
+
+ // This test only works on Windows
+ if (!("nsIWindowsRegKey" in AM_Ci)) {
+ run_test_4();
+ return;
+ }
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:4444/data/test_updateid.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ a1.findUpdates({
+ onUpdateAvailable: function(addon, install) {
+ installUpdate(install, check_test_3);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_3(install) {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ // Existing add-on should have a pending upgrade
+ do_check_neq(a1.pendingUpgrade, null);
+ do_check_eq(a1.pendingUpgrade.id, "addon2@tests.mozilla.org");
+ do_check_eq(a1.pendingUpgrade.install.existingAddon, a1);
+
+ // Lock the old add-on open so it can't be uninstalled
+ var file = profileDir.clone();
+ file.append("addon1@tests.mozilla.org");
+ if (!file.exists())
+ file.leafName += ".xpi";
+ else
+ file.append("install.rdf");
+
+ var fstream = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fstream.init(file, FileUtils.MODE_APPEND | FileUtils.MODE_WRONLY, FileUtils.PERMS_FILE, 0);
+
+ restartManager();
+
+ fstream.close();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ callback_soon(function([a1, a2]) {
+ // Should not have installed the new add-on but it should still be
+ // pending install
+ do_check_neq(a1, null);
+ do_check_eq(a2, null);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ // Should have installed the new add-on
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+
+ a2.uninstall();
+
+ do_execute_soon(run_test_4);
+ });
+ }));
+ }));
+}
+
+// Tests that upgrading to a bootstrapped add-on works but requires a restart
+function run_test_4() {
+ restartManager();
+ shutdownManager();
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ updateURL: "http://localhost:4444/data/test_updateid.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ startupManager();
+
+ resetPrefs();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_neq(a2.syncGUID, null);
+ do_check_eq(a2.version, "2.0");
+
+ a2.findUpdates({
+ onUpdateAvailable: function(addon, install) {
+ installUpdate(install, check_test_4);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_4() {
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"],
+ callback_soon(function([a2, a3]) {
+ // Should still be pending install even though the new add-on is restartless
+ do_check_neq(a2, null);
+ do_check_eq(a3, null);
+
+ do_check_neq(a2.pendingUpgrade, null);
+ do_check_eq(a2.pendingUpgrade.id, "addon3@tests.mozilla.org");
+
+ do_check_eq(getInstalledVersion(), -1);
+ do_check_eq(getActiveVersion(), -1);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a2, a3]) {
+ // Should have updated
+ do_check_eq(a2, null);
+ do_check_neq(a3, null);
+
+ do_check_eq(getInstalledVersion(), 3);
+ do_check_eq(getActiveVersion(), 3);
+
+ do_execute_soon(run_test_5);
+ });
+ }));
+}
+
+// Tests that upgrading to another bootstrapped add-on works without a restart
+function run_test_5() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "3.0");
+
+ a3.findUpdates({
+ onUpdateAvailable: function(addon, install) {
+ installUpdate(install, check_test_5);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_5() {
+ AddonManager.getAddonsByIDs(["addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ callback_soon(function([a3, a4]) {
+ // Should have updated
+ do_check_eq(a3, null);
+ do_check_neq(a4, null);
+
+ do_check_eq(getInstalledVersion(), 4);
+ do_check_eq(getActiveVersion(), 4);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"], function([a3, a4]) {
+ // Should still be gone
+ do_check_eq(a3, null);
+ do_check_neq(a4, null);
+
+ do_check_eq(getInstalledVersion(), 4);
+ do_check_eq(getActiveVersion(), 4);
+
+ run_test_6();
+ });
+ }));
+}
+
+// Tests that upgrading to a non-bootstrapped add-on works but requires a restart
+function run_test_6() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "4.0");
+
+ a4.findUpdates({
+ onUpdateAvailable: function(addon, install) {
+ installUpdate(install, check_test_6);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+function check_test_6() {
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ callback_soon(function([a4, a2]) {
+ // Should still be pending install even though the old add-on is restartless
+ do_check_neq(a4, null);
+ do_check_eq(a2, null);
+
+ do_check_neq(a4.pendingUpgrade, null);
+ do_check_eq(a4.pendingUpgrade.id, "addon2@tests.mozilla.org");
+
+ do_check_eq(getInstalledVersion(), 4);
+ do_check_eq(getActiveVersion(), 4);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a4, a2]) {
+ // Should have updated
+ do_check_eq(a4, null);
+ do_check_neq(a2, null);
+
+ do_check_eq(getInstalledVersion(), 0);
+ do_check_eq(getActiveVersion(), 0);
+
+ end_test();
+ });
+ }));
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js b/toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js
new file mode 100644
index 000000000..f79789b68
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js
@@ -0,0 +1,206 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that app upgrades produce the expected behaviours,
+// with strict compatibility checking disabled.
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+// Enable loading extensions from the application scope
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE +
+ AddonManager.SCOPE_APPLICATION);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+const globalDir = Services.dirsvc.get("XCurProcD", AM_Ci.nsIFile);
+globalDir.append("extensions");
+
+var gGlobalExisted = globalDir.exists();
+var gInstallTime = Date.now();
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Will be compatible in the first version and incompatible in subsequent versions
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ targetPlatforms: [
+ "XPCShell",
+ "WINNT_x86",
+ ]
+ }, profileDir);
+
+ // Works in all tested versions
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 2",
+ targetPlatforms: [
+ "XPCShell_noarch-spidermonkey"
+ ]
+ }, profileDir);
+
+ // Will be disabled in the first version and enabled in the second.
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ // Will be compatible in both versions but will change version in between
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+function end_test() {
+ if (!gGlobalExisted) {
+ globalDir.remove(true);
+ }
+ else {
+ globalDir.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ globalDir.remove(true);
+ }
+ do_execute_soon(do_test_finished);
+}
+
+// Test that the test extensions are all installed
+function run_test_1() {
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "1.0");
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Test that upgrading the application doesn't disable now incompatible add-ons
+function run_test_2() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ restartManager("2");
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that nothing changes when only the build ID changes.
+function run_test_3() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "3.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "3",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ // Simulates a simple Build ID change, the platform deletes extensions.ini
+ // whenever the application is changed.
+ gExtensionsINI.remove(true);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js
new file mode 100644
index 000000000..69383166e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js
@@ -0,0 +1,209 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that app upgrades produce the expected behaviours,
+// with strict compatibility checking enabled.
+
+// Enable loading extensions from the application scope
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE +
+ AddonManager.SCOPE_APPLICATION);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+const globalDir = Services.dirsvc.get("XCurProcD", AM_Ci.nsIFile);
+globalDir.append("extensions");
+
+var gGlobalExisted = globalDir.exists();
+var gInstallTime = Date.now();
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Will be enabled in the first version and disabled in subsequent versions
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ targetPlatforms: [
+ "XPCShell",
+ "WINNT_x86",
+ ]
+ }, profileDir);
+
+ // Works in all tested versions
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 2",
+ targetPlatforms: [
+ "XPCShell_noarch-spidermonkey"
+ ]
+ }, profileDir);
+
+ // Will be disabled in the first version and enabled in the second.
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ // Will be enabled in both versions but will change version in between
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ do_test_pending();
+
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ run_test_1();
+}
+
+function end_test() {
+ if (!gGlobalExisted) {
+ globalDir.remove(true);
+ }
+ else {
+ globalDir.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ globalDir.remove(true);
+ }
+
+ Services.prefs.clearUserPref(PREF_EM_STRICT_COMPATIBILITY);
+
+ do_execute_soon(do_test_finished);
+}
+
+// Test that the test extensions are all installed
+function run_test_1() {
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "1.0");
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Test that upgrading the application disables now incompatible add-ons
+function run_test_2() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ restartManager("2");
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that nothing changes when only the build ID changes.
+function run_test_3() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "3.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "3",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ // Simulates a simple Build ID change, the platform deletes extensions.ini
+ // whenever the application is changed.
+ gExtensionsINI.remove(true);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
new file mode 100644
index 000000000..bab072e83
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -0,0 +1,281 @@
+# The file is shared between the two main xpcshell manifest files.
+[DEFAULT]
+skip-if = toolkit == 'android' || toolkit == 'gonk'
+
+[test_AddonRepository.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_AddonRepository_cache.js]
+# Bug 676992: test consistently hangs on Android
+# Bug 1026805: frequent hangs on OSX 10.8
+skip-if = os == "android" || os == "mac"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_AddonRepository_compatmode.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_LightweightThemeManager.js]
+[test_backgroundupdate.js]
+[test_bad_json.js]
+[test_badschema.js]
+[test_blocklistchange.js]
+# Times out during parallel runs on desktop
+requesttimeoutfactor = 2
+[test_blocklist_prefs.js]
+[test_blocklist_metadata_filters.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_blocklist_regexp.js]
+skip-if = os == "android"
+[test_bootstrap.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bootstrap_resource.js]
+[test_bug299716.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug299716_2.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Hardcoded port in install.rdf.
+[test_bug324121.js]
+# Bug 676992: test consistently hangs on Android
+# Bug 1026805: frequent hangs on OSX 10.8
+skip-if = os == "android" || os == "mac"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug335238.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug371495.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug384052.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug393285.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug394300.js]
+# Bug 676992: test consistently hangs on Android
+# Bug 1026805: frequent hangs on OSX 10.8
+skip-if = os == "android" || os == "mac"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug397778.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug406118.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug424262.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug425657.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug430120.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug449027.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug455906.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug465190.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug468528.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_1.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_1_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_2.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_3.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_3_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_4.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug514327_1.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug514327_2.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = buildapp == "mulet" || os == "android"
+[test_bug514327_3.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug521905.js]
+[test_bug526598.js]
+[test_bug541420.js]
+[test_bug542391.js]
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug554133.js]
+[test_bug559800.js]
+[test_bug563256.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_bug564030.js]
+[test_bug566626.js]
+[test_bug567184.js]
+[test_bug569138.js]
+[test_bug570173.js]
+[test_bug576735.js]
+[test_bug587088.js]
+[test_bug594058.js]
+[test_bug595081.js]
+[test_bug595573.js]
+[test_bug596343.js]
+[test_bug596607.js]
+[test_bug616841.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_bug619730.js]
+[test_bug620837.js]
+[test_bug655254.js]
+[test_bug659772.js]
+[test_bug675371.js]
+[test_bug740612.js]
+[test_bug753900.js]
+[test_bug757663.js]
+[test_bug953156.js]
+[test_checkcompatibility.js]
+[test_checkCompatibility_themeOverride.js]
+[test_childprocess.js]
+[test_ChromeManifestParser.js]
+[test_compatoverrides.js]
+[test_corrupt.js]
+[test_corrupt_strictcompat.js]
+[test_corruptfile.js]
+[test_dataDirectory.js]
+[test_default_providers_pref.js]
+[test_dictionary.js]
+[test_langpack.js]
+[test_disable.js]
+[test_distribution.js]
+[test_dss.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_duplicateplugins.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_error.js]
+[test_experiment.js]
+[test_filepointer.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_fuel.js]
+[test_general.js]
+[test_getresource.js]
+[test_gfxBlacklist_Device.js]
+[test_gfxBlacklist_DriverNew.js]
+[test_gfxBlacklist_Equal_DriverNew.js]
+[test_gfxBlacklist_Equal_DriverOld.js]
+[test_gfxBlacklist_Equal_OK.js]
+[test_gfxBlacklist_GTE_DriverOld.js]
+[test_gfxBlacklist_GTE_OK.js]
+[test_gfxBlacklist_No_Comparison.js]
+[test_gfxBlacklist_OK.js]
+[test_gfxBlacklist_OS.js]
+[test_gfxBlacklist_OSVersion_match.js]
+[test_gfxBlacklist_OSVersion_mismatch_OSVersion.js]
+[test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js]
+[test_gfxBlacklist_Vendor.js]
+[test_gfxBlacklist_prefs.js]
+[test_hasbinarycomponents.js]
+[test_install.js]
+[test_install_icons.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_install_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_isDebuggable.js]
+[test_locale.js]
+[test_locked.js]
+[test_locked2.js]
+[test_locked_strictcompat.js]
+[test_manifest.js]
+[test_mapURIToAddonID.js]
+# Same as test_bootstrap.js
+skip-if = os == "android"
+[test_migrate1.js]
+[test_migrate2.js]
+[test_migrate3.js]
+[test_migrate4.js]
+# Times out during parallel runs on desktop
+requesttimeoutfactor = 2
+[test_migrate5.js]
+[test_migrateAddonRepository.js]
+[test_migrate_max_version.js]
+[test_multiprocessCompatible.js]
+[test_no_addons.js]
+[test_onPropertyChanged_appDisabled.js]
+[test_permissions.js]
+[test_permissions_prefs.js]
+[test_plugins.js]
+skip-if = buildapp == "mulet"
+[test_pluginchange.js]
+# PluginProvider.jsm is not shipped on Android
+skip-if = os == "android"
+[test_pluginBlocklistCtp.js]
+# Bug 676992: test consistently fails on Android
+fail-if = buildapp == "mulet" || os == "android"
+[test_pref_properties.js]
+[test_registry.js]
+[test_safemode.js]
+[test_startup.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_syncGUID.js]
+[test_strictcompatibility.js]
+[test_targetPlatforms.js]
+[test_theme.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_types.js]
+[test_undothemeuninstall.js]
+[test_undouninstall.js]
+[test_uninstall.js]
+[test_update.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_updateCancel.js]
+[test_update_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_update_ignorecompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_updatecheck.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_updateid.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_update_compatmode.js]
+[test_upgrade.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses global XCurProcD dir.
+[test_upgrade_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses global XCurProcD dir.
+[test_overrideblocklist.js]
+run-sequentially = Uses global XCurProcD dir.
+[test_sourceURI.js]
+[test_bootstrap_globals.js]
diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
new file mode 100644
index 000000000..51520f888
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
@@ -0,0 +1,8 @@
+ [DEFAULT]
+head = head_addons.js head_unpack.js
+tail =
+firefox-appdir = browser
+skip-if = toolkit == 'android' || toolkit == 'gonk'
+dupe-manifest =
+
+[include:xpcshell-shared.ini]
diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
new file mode 100644
index 000000000..83ab77c74
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -0,0 +1,28 @@
+[DEFAULT]
+skip-if = buildapp == 'mulet' || toolkit == 'android' || toolkit == 'gonk'
+head = head_addons.js
+tail =
+firefox-appdir = browser
+dupe-manifest =
+support-files =
+ data/**
+ xpcshell-shared.ini
+
+[test_addon_path_service.js]
+[test_asyncBlocklistLoad.js]
+[test_cacheflush.js]
+[test_DeferredSave.js]
+[test_gmpProvider.js]
+run-if = appname == "firefox"
+[test_isReady.js]
+[test_metadata_update.js]
+[test_pluginInfoURL.js]
+[test_provider_markSafe.js]
+[test_provider_shutdown.js]
+[test_provider_unsafe_access_shutdown.js]
+[test_provider_unsafe_access_startup.js]
+[test_shutdown.js]
+[test_XPIcancel.js]
+[test_XPIStates.js]
+
+[include:xpcshell-shared.ini]
diff --git a/toolkit/mozapps/extensions/test/xpinstall/authRedirect.sjs b/toolkit/mozapps/extensions/test/xpinstall/authRedirect.sjs
new file mode 100644
index 000000000..85d448e2b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/authRedirect.sjs
@@ -0,0 +1,21 @@
+// Simple script redirects to the query part of the uri if the browser
+// authenticates with username "testuser" password "testpass"
+
+function handleRequest(request, response) {
+ if (request.hasHeader("Authorization")) {
+ if (request.getHeader("Authorization") == "Basic dGVzdHVzZXI6dGVzdHBhc3M=") {
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ response.setHeader("Location", request.queryString);
+ response.write("See " + request.queryString);
+ }
+ else {
+ response.setStatusLine(request.httpVersion, 403, "Forbidden");
+ response.write("Invalid credentials");
+ }
+ }
+ else {
+ response.setStatusLine(request.httpVersion, 401, "Authentication required");
+ response.setHeader("WWW-Authenticate", "basic realm=\"XPInstall\"", false);
+ response.write("Unauthenticated request");
+ }
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser.ini b/toolkit/mozapps/extensions/test/xpinstall/browser.ini
new file mode 100644
index 000000000..d6392cdc8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser.ini
@@ -0,0 +1,103 @@
+[DEFAULT]
+support-files =
+ authRedirect.sjs
+ bug540558.html
+ bug638292.html
+ bug645699.html
+ concurrent_installs.html
+ cookieRedirect.sjs
+ corrupt.xpi
+ empty.xpi
+ enabled.html
+ hashRedirect.sjs
+ head.js
+ incompatible.xpi
+ installchrome.html
+ installtrigger.html
+ installtrigger_frame.html
+ multipackage.xpi
+ navigate.html
+ redirect.sjs
+ restartless.xpi
+ signed-no-cn.xpi
+ signed-no-o.xpi
+ signed-tampered.xpi
+ signed-untrusted.xpi
+ signed.xpi
+ signed2.xpi
+ slowinstall.sjs
+ startsoftwareupdate.html
+ theme.xpi
+ triggerredirect.html
+ unsigned.xpi
+
+[browser_auth.js]
+[browser_auth2.js]
+[browser_auth3.js]
+[browser_auth4.js]
+[browser_badargs.js]
+[browser_badargs2.js]
+[browser_badhash.js]
+[browser_badhashtype.js]
+[browser_bug540558.js]
+[browser_bug611242.js]
+[browser_bug638292.js]
+skip-if = e10s # Bug 1083269
+[browser_bug645699.js]
+# [browser_bug672485.js]
+# disabled due to a leak. See bug 682410.
+[browser_cancel.js]
+[browser_concurrent_installs.js]
+[browser_cookies.js]
+[browser_cookies2.js]
+[browser_cookies3.js]
+[browser_cookies4.js]
+skip-if = true # Bug 1084646
+[browser_corrupt.js]
+[browser_datauri.js]
+[browser_empty.js]
+[browser_enabled.js]
+[browser_enabled2.js]
+[browser_enabled3.js]
+[browser_hash.js]
+[browser_httphash.js]
+[browser_httphash2.js]
+[browser_httphash3.js]
+[browser_httphash4.js]
+[browser_httphash5.js]
+[browser_httphash6.js]
+[browser_installchrome.js]
+[browser_localfile.js]
+[browser_localfile2.js]
+[browser_localfile3.js]
+[browser_localfile4.js]
+[browser_multipackage.js]
+[browser_navigateaway.js]
+[browser_navigateaway2.js]
+[browser_navigateaway3.js]
+[browser_navigateaway4.js]
+[browser_offline.js]
+[browser_relative.js]
+[browser_signed_multiple.js]
+[browser_signed_naming.js]
+[browser_signed_tampered.js]
+[browser_signed_trigger.js]
+[browser_signed_untrusted.js]
+[browser_signed_url.js]
+[browser_softwareupdate.js]
+[browser_switchtab.js]
+[browser_trigger_redirect.js]
+[browser_unsigned_trigger.js]
+[browser_unsigned_trigger_iframe.js]
+skip-if = buildapp == "mulet"
+[browser_unsigned_trigger_xorigin.js]
+skip-if = buildapp == "mulet"
+[browser_unsigned_url.js]
+[browser_whitelist.js]
+[browser_whitelist2.js]
+[browser_whitelist3.js]
+[browser_whitelist4.js]
+[browser_whitelist5.js]
+[browser_whitelist6.js]
+[browser_whitelist7.js]
+skip-if = (os == 'win' || os == 'mac') && debug # bug 986458 - leaked 1 docshell until shutdown on chunked debug bc
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js
new file mode 100644
index 000000000..ee2913827
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js
@@ -0,0 +1,43 @@
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds when authentication is required
+// This verifies bug 312473
+function test() {
+ Harness.authenticationCallback = get_auth_info;
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+ return [ "testuser", "testpass" ];
+}
+
+function download_failed(install) {
+ ok(false, "Install should not have failed");
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+ .getService(Components.interfaces.nsIHttpAuthManager);
+ authMgr.clearAll();
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_auth2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_auth2.js
new file mode 100644
index 000000000..d50ce941d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth2.js
@@ -0,0 +1,46 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when authentication is required and bad
+// credentials are given
+// This verifies bug 312473
+function test() {
+ requestLongerTimeout(2);
+ Harness.authenticationCallback = get_auth_info;
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+ return [ "baduser", "badpass" ];
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should have failed");
+}
+
+function install_ended(install, addon) {
+ ok(false, "Add-on should not have installed");
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+ .getService(Components.interfaces.nsIHttpAuthManager);
+ authMgr.clearAll();
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_auth3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_auth3.js
new file mode 100644
index 000000000..f06e97fa3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth3.js
@@ -0,0 +1,54 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when authentication is required and it is
+// canceled
+// This verifies bug 312473
+
+///////////////////
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
+
+function test() {
+ Harness.authenticationCallback = get_auth_info;
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+ return null;
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should have failed");
+}
+
+function install_ended(install, addon) {
+ ok(false, "Add-on should not have installed");
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+ .getService(Components.interfaces.nsIHttpAuthManager);
+ authMgr.clearAll();
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_auth4.js b/toolkit/mozapps/extensions/test/xpinstall/browser_auth4.js
new file mode 100644
index 000000000..abbc161f7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth4.js
@@ -0,0 +1,53 @@
+///////////////////
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
+
+// ----------------------------------------------------------------------------
+// Test whether a request for auth for an XPI switches to the appropriate tab
+var gNewTab;
+
+function test() {
+ Harness.authenticationCallback = get_auth_info;
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gNewTab = gBrowser.addTab();
+ gBrowser.getBrowserForTab(gNewTab).loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+ is(gBrowser.selectedTab, gNewTab, "Should have focused the tab loading the XPI");
+ return [ "testuser", "testpass" ];
+}
+
+function download_failed(install) {
+ ok(false, "Install should not have failed");
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+ .getService(Components.interfaces.nsIHttpAuthManager);
+ authMgr.clearAll();
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeTab(gNewTab);
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_badargs.js b/toolkit/mozapps/extensions/test/xpinstall/browser_badargs.js
new file mode 100644
index 000000000..fb9c3d10c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_badargs.js
@@ -0,0 +1,37 @@
+// ----------------------------------------------------------------------------
+// Test whether passing a simple string to InstallTrigger.install throws an
+// exception
+function test() {
+ waitForExplicitFinish();
+
+ var triggers = encodeURIComponent(JSON.stringify(TESTROOT + "unsigned.xpi"));
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ gBrowser.contentWindow.addEventListener("InstallTriggered", page_loaded, false);
+ }
+
+ gBrowser.selectedBrowser.addEventListener("load", loadListener, true);
+
+ // In non-e10s the exception in the content page would trigger a test failure
+ if (!gMultiProcessBrowser)
+ expectUncaughtException();
+
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function page_loaded() {
+ gBrowser.contentWindow.removeEventListener("InstallTriggered", page_loaded, false);
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "exception", "installTrigger should have failed");
+
+ // In non-e10s the exception from the page is thrown after the event so we
+ // have to spin the event loop to make sure it arrives so expectUncaughtException
+ // sees it.
+ executeSoon(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_badargs2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_badargs2.js
new file mode 100644
index 000000000..25e1586c8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_badargs2.js
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------
+// Test whether passing an undefined url InstallTrigger.install throws an
+// exception
+function test() {
+ waitForExplicitFinish();
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: undefined
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ gBrowser.contentWindow.addEventListener("InstallTriggered", page_loaded, false);
+ }
+
+ gBrowser.selectedBrowser.addEventListener("load", loadListener, true);
+
+ // In non-e10s the exception in the content page would trigger a test failure
+ if (!gMultiProcessBrowser)
+ expectUncaughtException();
+
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function page_loaded() {
+ gBrowser.contentWindow.removeEventListener("InstallTriggered", page_loaded, false);
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "exception", "installTrigger should have failed");
+
+ // In non-e10s the exception from the page is thrown after the event so we
+ // have to spin the event loop to make sure it arrives so expectUncaughtException
+ // sees it.
+ executeSoon(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_badhash.js b/toolkit/mozapps/extensions/test/xpinstall/browser_badhash.js
new file mode 100644
index 000000000..d7bcedd90
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_badhash.js
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when an invalid hash is included
+// This verifies bug 302284
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ Hash: "sha1:643b08418599ddbd1ea8a511c90696578fb844b9",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_INCORRECT_HASH, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_badhashtype.js b/toolkit/mozapps/extensions/test/xpinstall/browser_badhashtype.js
new file mode 100644
index 000000000..105ab681e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_badhashtype.js
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when an unknown hash type is included
+// This verifies bug 302284
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ Hash: "foo:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_INCORRECT_HASH, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_bug540558.js b/toolkit/mozapps/extensions/test/xpinstall/browser_bug540558.js
new file mode 100644
index 000000000..6a425c61a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug540558.js
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------------
+// Tests that calling InstallTrigger.installChrome works
+function test() {
+ Harness.installEndedCallback = check_xpi_install;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "bug540558.html");
+}
+
+function check_xpi_install(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_bug611242.js b/toolkit/mozapps/extensions/test/xpinstall/browser_bug611242.js
new file mode 100644
index 000000000..08af331bd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug611242.js
@@ -0,0 +1,34 @@
+// ----------------------------------------------------------------------------
+// Test whether setting a new property in InstallTrigger then persists to other
+// page loads
+function loadURI(aUri, aCallback) {
+ gBrowser.selectedBrowser.addEventListener("load", function() {
+ if (gBrowser.selectedBrowser.currentURI.spec != aUri)
+ return;
+
+ gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+ aCallback();
+ }, true);
+
+ gBrowser.loadURI(aUri);
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ loadURI(TESTROOT + "enabled.html", function() {
+ window.content.wrappedJSObject.InstallTrigger.enabled.k = function() { };
+
+ loadURI(TESTROOT2 + "enabled.html", function() {
+ is(window.content.wrappedJSObject.InstallTrigger.enabled.k, undefined, "Property should not be defined");
+
+ gBrowser.removeTab(gBrowser.selectedTab);
+
+ finish();
+ });
+ });
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_bug638292.js b/toolkit/mozapps/extensions/test/xpinstall/browser_bug638292.js
new file mode 100644
index 000000000..d5d590a3f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug638292.js
@@ -0,0 +1,63 @@
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.enabled is working
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function() {
+ gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+ waitForFocus(page_loaded, gBrowser.contentWindow);
+ }, true);
+ gBrowser.loadURI(TESTROOT + "bug638292.html");
+}
+
+function check_load(aCallback) {
+ gBrowser.addEventListener("load", function(aEvent) {
+ if (!gBrowser.browsers[2] ||
+ aEvent.target != gBrowser.browsers[2].contentDocument) {
+ // SeaMonkey tabbrowser needs to deal with additional loads.
+ if (navigator.userAgent.match(/ SeaMonkey\//))
+ info("Ignoring unrelated load on SeaMonkey. (Expected 2-3 times.)");
+ else
+ ok(false, "Ignoring unrelated load on Firefox. (Should never happen!)");
+ return;
+ }
+
+ gBrowser.removeEventListener("load", arguments.callee, true);
+
+ // Let the load handler complete
+ executeSoon(function() {
+ var doc = gBrowser.browsers[2].contentDocument;
+ is(doc.getElementById("enabled").textContent, "true", "installTrigger should have been enabled");
+
+ // Focus the old tab
+ gBrowser.selectedTab = gBrowser.tabs[1];
+ waitForFocus(function() {
+ // Close the new tab
+ gBrowser.removeTab(gBrowser.tabs[2]);
+ aCallback();
+ }, gBrowser.contentWindow);
+ });
+ }, true);
+}
+
+function page_loaded() {
+ var doc = gBrowser.contentDocument;
+ info("Clicking link 1");
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("link1"), { }, gBrowser.contentWindow);
+
+ check_load(function() {
+ info("Clicking link 2");
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("link2"), { }, gBrowser.contentWindow);
+
+ check_load(function() {
+ info("Clicking link 3");
+ EventUtils.synthesizeMouseAtCenter(doc.getElementById("link3"), { button: 1 }, gBrowser.contentWindow);
+
+ check_load(function() {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_bug645699.js b/toolkit/mozapps/extensions/test/xpinstall/browser_bug645699.js
new file mode 100644
index 000000000..a5c188c03
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug645699.js
@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 645699
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "bug645699.html");
+}
+
+function allow_blocked(installInfo) {
+ is(installInfo.browser, gBrowser.selectedBrowser, "Install should have been triggered by the right browser");
+ is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
+ return false;
+}
+
+function confirm_install(window) {
+ ok(false, "Should not see the install dialog");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "0 Add-ons should have been successfully installed");
+ Services.perms.remove("addons.mozilla.org", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_bug672485.js b/toolkit/mozapps/extensions/test/xpinstall/browser_bug672485.js
new file mode 100644
index 000000000..36e9c5b3c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug672485.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+gWindowWatcher = null;
+
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installCancelledCallback = cancelled_install;
+ Harness.installEndedCallback = complete_install;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ gWindowWatcher = Services.ww;
+ delete Services.ww;
+ is(Services.ww, undefined, "Services.ww should now be undefined");
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ ok(false, "Should not see the install dialog");
+ return false;
+}
+
+function cancelled_install() {
+ ok(true, "Install should b cancelled");
+}
+
+function complete_install() {
+ ok(false, "Install should not have completed");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "0 Add-ons should have been successfully installed");
+
+ gBrowser.removeCurrentTab();
+
+ Services.ww = gWindowWatcher;
+
+ Services.perms.remove("example.com", "install");
+
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js b/toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
new file mode 100644
index 000000000..8fb6efcb8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// ----------------------------------------------------------------------------
+// Tests that cancelling multiple installs doesn't fail
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Signed XPI": TESTROOT + "signed.xpi",
+ "Signed XPI 2": TESTROOT + "signed2.xpi",
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_item(items, url) {
+ for (let item of items) {
+ if (item.url == url)
+ return item;
+ }
+ ok(false, "Item for " + url + " was not listed");
+ return null;
+}
+
+function confirm_install(window) {
+ let items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 2, "Should be 2 items listed in the confirmation dialog");
+ let item = get_item(items, TESTROOT + "signed.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ item = get_item(items, TESTROOT + "signed2.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ return false;
+}
+
+function install_ended(install, addon) {
+ ok(false, "Should not have seen installs complete");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_concurrent_installs.js b/toolkit/mozapps/extensions/test/xpinstall/browser_concurrent_installs.js
new file mode 100644
index 000000000..3613e95b0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_concurrent_installs.js
@@ -0,0 +1,128 @@
+// Test that having two frames that request installs at the same time doesn't
+// cause callback ID conflicts (discussed in bug 926712)
+
+let {Promise} = Cu.import("resource://gre/modules/Promise.jsm");
+
+let gConcurrentTabs = [];
+let gQueuedForInstall = [];
+let gResults = [];
+
+function frame_script() {
+ addMessageListener("Test:StartInstall", () => {
+ content.document.getElementById("installnow").click()
+ });
+
+ addEventListener("load", () => {
+ sendAsyncMessage("Test:Loaded");
+
+ content.addEventListener("InstallComplete", (e) => {
+ sendAsyncMessage("Test:InstallComplete", e.detail);
+ }, true);
+ }, true);
+}
+
+let gAddonAndWindowListener = {
+ onOpenWindow: function(win) {
+ var window = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+ info("Window opened");
+
+ waitForFocus(function() {
+ info("Focused!");
+ // Initially the accept button is disabled on a countdown timer
+ let button = window.document.documentElement.getButton("accept");
+ button.disabled = false;
+ if (gQueuedForInstall.length > 0) {
+ // Start downloading the next add-on while we accept this dialog:
+ installNext();
+ }
+ window.document.documentElement.acceptDialog();
+ }, window);
+ },
+ onCloseWindow: function(win) { },
+ onInstallEnded: function(install) {
+ install.cancel();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowMediatorListener])
+};
+
+function installNext() {
+ let tab = gQueuedForInstall.shift();
+ tab.linkedBrowser.messageManager.sendAsyncMessage("Test:StartInstall");
+}
+
+function winForTab(t) {
+ return t.linkedBrowser.contentWindow;
+}
+
+function createTab(url) {
+ let tab = gBrowser.addTab(url);
+ tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
+
+ tab.linkedBrowser.messageManager.addMessageListener("Test:InstallComplete", ({data}) => {
+ gResults.push(data);
+ if (gResults.length == 2) {
+ executeSoon(endThisTest);
+ }
+ });
+
+ return tab;
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIRESECUREORIGIN, false);
+ Services.wm.addListener(gAddonAndWindowListener);
+ AddonManager.addInstallListener(gAddonAndWindowListener);
+ registerCleanupFunction(function() {
+ Services.wm.removeListener(gAddonAndWindowListener);
+ AddonManager.removeInstallListener(gAddonAndWindowListener);
+ Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIRESECUREORIGIN);
+
+ Services.perms.remove("example.com", "install");
+ Services.perms.remove("example.org", "install");
+
+ while (gConcurrentTabs.length) {
+ gBrowser.removeTab(gConcurrentTabs.shift());
+ }
+ });
+
+ let pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+ pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
+
+ gConcurrentTabs.push(createTab(TESTROOT + "concurrent_installs.html"));
+ gConcurrentTabs.push(createTab(TESTROOT2 + "concurrent_installs.html"));
+
+ let promises = gConcurrentTabs.map((t) => {
+ return new Promise(resolve => {
+ t.linkedBrowser.messageManager.addMessageListener("Test:Loaded", resolve);
+ });
+ });
+
+ Promise.all(promises).then(() => {
+ gQueuedForInstall = [...gConcurrentTabs];
+ installNext();
+ });
+}
+
+function endThisTest() {
+ is(gResults.length, 2, "Should have two urls");
+ isnot(gResults[0].loc, gResults[1].loc, "Should not have results from the same page.");
+ isnot(gResults[0].xpi, gResults[1].xpi, "Should not have the same XPIs.");
+ for (let i = 0; i < 2; i++) {
+ let {loc, xpi} = gResults[i];
+ if (loc.includes("example.org")) {
+ ok(xpi.includes("example.org"), "Should get .org XPI for .org loc");
+ } else if (loc.includes("example.com")) {
+ ok(xpi.includes("example.com"), "Should get .com XPI for .com loc");
+ } else {
+ ok(false, "Should never get anything that isn't from example.org or example.com");
+ }
+ }
+
+ finish();
+}
+
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_cookies.js b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies.js
new file mode 100644
index 000000000..c0e7c11b5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies.js
@@ -0,0 +1,30 @@
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent fails when no cookies
+// are set
+// This verifies bug 462739
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_cookies2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies2.js
new file mode 100644
index 000000000..02ea8ff21
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies2.js
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent succeeds when cookies
+// are set
+// This verifies bug 462739
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager2);
+ cm.add("example.com", "/browser/" + RELATIVE_DIR, "xpinstall", "true", false,
+ false, true, (Date.now() / 1000) + 60);
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager2);
+ cm.remove("example.com", "xpinstall", "/browser/" + RELATIVE_DIR, false);
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_cookies3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies3.js
new file mode 100644
index 000000000..c23778dd0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies3.js
@@ -0,0 +1,44 @@
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent succeeds when cookies
+// are set and third party cookies are disabled.
+// This verifies bug 462739
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager2);
+ cm.add("example.com", "/browser/" + RELATIVE_DIR, "xpinstall", "true", false,
+ false, true, (Date.now() / 1000) + 60);
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager2);
+ cm.remove("example.com", "xpinstall", "/browser/" + RELATIVE_DIR, false);
+
+ Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_cookies4.js b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies4.js
new file mode 100644
index 000000000..33347cdb7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_cookies4.js
@@ -0,0 +1,43 @@
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent fails when cookies
+// are set and third party cookies are disabled and the request is to a third
+// party.
+// This verifies bug 462739
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager2);
+ cm.add("example.org", "/browser/" + RELATIVE_DIR, "xpinstall", "true", false,
+ false, true, (Date.now() / 1000) + 60);
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Cookie check": TESTROOT2 + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager2);
+ cm.remove("example.org", "xpinstall", "/browser/" + RELATIVE_DIR, false);
+
+ Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_corrupt.js b/toolkit/mozapps/extensions/test/xpinstall/browser_corrupt.js
new file mode 100644
index 000000000..8f0c3c66a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_corrupt.js
@@ -0,0 +1,32 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when the xpi is corrupt.
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.finalContentEvent = "InstallComplete";
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Corrupt XPI": TESTROOT + "corrupt.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("status").textContent, "-207", "Callback should have seen the failure");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_datauri.js b/toolkit/mozapps/extensions/test/xpinstall/browser_datauri.js
new file mode 100644
index 000000000..917f2465d
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_datauri.js
@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------------------
+// Checks that a chained redirect through a data URI and javascript is blocked
+
+function setup_redirect(aSettings) {
+ var url = TESTROOT + "redirect.sjs?mode=setup";
+ for (var name in aSettings) {
+ url += "&" + name + "=" + encodeURIComponent(aSettings[name]);
+ }
+
+ var req = new XMLHttpRequest();
+ req.open("GET", url, false);
+ req.send(null);
+}
+
+function test() {
+ Harness.installOriginBlockedCallback = install_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ setup_redirect({
+ "Location": "data:text/html,<script>window.location.href='" + TESTROOT + "unsigned.xpi'</script>"
+ });
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "redirect.sjs?mode=redirect");
+}
+
+function install_blocked(installInfo) {
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_empty.js b/toolkit/mozapps/extensions/test/xpinstall/browser_empty.js
new file mode 100644
index 000000000..cbf9e48a7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_empty.js
@@ -0,0 +1,28 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when there is no install script present.
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Empty XPI": TESTROOT + "empty.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_enabled.js b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled.js
new file mode 100644
index 000000000..56118c4e1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled.js
@@ -0,0 +1,24 @@
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.enabled is working
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ gBrowser.contentWindow.addEventListener("PageLoaded", page_loaded, false);
+ }
+
+ gBrowser.selectedBrowser.addEventListener("load", loadListener, true);
+ gBrowser.loadURI(TESTROOT + "enabled.html");
+}
+
+function page_loaded() {
+ gBrowser.contentWindow.removeEventListener("PageLoaded", page_loaded, false);
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("enabled").textContent, "true", "installTrigger should have been enabled");
+ gBrowser.removeCurrentTab();
+ finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_enabled2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled2.js
new file mode 100644
index 000000000..290987bda
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled2.js
@@ -0,0 +1,27 @@
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.enabled is working
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setBoolPref("xpinstall.enabled", false);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ gBrowser.contentWindow.addEventListener("PageLoaded", page_loaded, false);
+ }
+
+ gBrowser.selectedBrowser.addEventListener("load", loadListener, true);
+ gBrowser.loadURI(TESTROOT + "enabled.html");
+}
+
+function page_loaded() {
+ gBrowser.contentWindow.removeEventListener("PageLoaded", page_loaded, false);
+ Services.prefs.clearUserPref("xpinstall.enabled");
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("enabled").textContent, "false", "installTrigger should have not been enabled");
+ gBrowser.removeCurrentTab();
+ finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_enabled3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled3.js
new file mode 100644
index 000000000..ea3eba530
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled3.js
@@ -0,0 +1,48 @@
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.install call fails when xpinstall is disabled
+function test() {
+ Harness.installDisabledCallback = install_disabled;
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installConfirmCallback = confirm_install;
+ Harness.setup();
+
+ Services.prefs.setBoolPref("xpinstall.enabled", false);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ gBrowser.contentWindow.addEventListener("InstallTriggered", page_loaded, false);
+ }
+
+ gBrowser.selectedBrowser.addEventListener("load", loadListener, true);
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_disabled(installInfo) {
+ ok(true, "Saw installation disabled");
+}
+
+function allow_blocked(installInfo) {
+ ok(false, "Should never see the blocked install notification");
+ return false;
+}
+
+function confirm_install(window) {
+ ok(false, "Should never see an install confirmation dialog");
+ return false;
+}
+
+function page_loaded() {
+ gBrowser.contentWindow.removeEventListener("InstallTriggered", page_loaded, false);
+ Services.prefs.clearUserPref("xpinstall.enabled");
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "false", "installTrigger should have not been enabled");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_hash.js b/toolkit/mozapps/extensions/test/xpinstall/browser_hash.js
new file mode 100644
index 000000000..0af71fcb0
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_hash.js
@@ -0,0 +1,34 @@
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds when a valid hash is included
+// This verifies bug 302284
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ Hash: "sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_hash2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_hash2.js
new file mode 100644
index 000000000..8d83e3cd3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_hash2.js
@@ -0,0 +1,34 @@
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds using case-insensitive hashes
+// This verifies bug 603021
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ Hash: "sha1:3D0DC22E1F394E159B08AAF5F0F97DE4D5C65F4F",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash.js b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash.js
new file mode 100644
index 000000000..b072db1f3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash.js
@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds when a valid hash is included in the HTTPS
+// request
+// This verifies bug 591070
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ var url = "https://example.com/browser/" + RELATIVE_DIR + "hashRedirect.sjs";
+ url += "?sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f|" + TESTROOT + "unsigned.xpi";
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: url,
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash2.js
new file mode 100644
index 000000000..93326af9f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash2.js
@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails when a invalid hash is included in the HTTPS
+// request
+// This verifies bug 591070
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ var url = "https://example.com/browser/" + RELATIVE_DIR + "hashRedirect.sjs";
+ url += "?sha1:foobar|" + TESTROOT + "unsigned.xpi";
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: url,
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_INCORRECT_HASH, "Download should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "0 Add-ons should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash3.js
new file mode 100644
index 000000000..76a6283fb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash3.js
@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------
+// Tests that the HTTPS hash is ignored when InstallTrigger is passed a hash.
+// This verifies bug 591070
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ var url = "https://example.com/browser/" + RELATIVE_DIR + "hashRedirect.sjs";
+ url += "?sha1:foobar|" + TESTROOT + "unsigned.xpi";
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: url,
+ Hash: "sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash4.js b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash4.js
new file mode 100644
index 000000000..97dc64d1f
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash4.js
@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------------------
+// Test that hashes are ignored in the headers of HTTP requests
+// This verifies bug 591070
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var url = "http://example.com/browser/" + RELATIVE_DIR + "hashRedirect.sjs";
+ url += "?sha1:foobar|" + TESTROOT + "unsigned.xpi";
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: url,
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash5.js b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash5.js
new file mode 100644
index 000000000..1237989f6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash5.js
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------
+// Test that only the first HTTPS hash is used
+// This verifies bug 591070
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ var url = "https://example.com/browser/" + RELATIVE_DIR + "hashRedirect.sjs";
+ url += "?sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f|";
+ url += "https://example.com/browser/" + RELATIVE_DIR + "hashRedirect.sjs";
+ url += "?sha1:foobar|" + TESTROOT + "unsigned.xpi";
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: url,
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js
new file mode 100644
index 000000000..9ab490235
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js
@@ -0,0 +1,83 @@
+// ----------------------------------------------------------------------------
+// Tests that a new hash is accepted when restarting a failed download
+// This verifies bug 593535
+function setup_redirect(aSettings) {
+ var url = "https://example.com/browser/" + RELATIVE_DIR + "redirect.sjs?mode=setup";
+ for (var name in aSettings) {
+ url += "&" + name + "=" + aSettings[name];
+ }
+
+ var req = new XMLHttpRequest();
+ req.open("GET", url, false);
+ req.send(null);
+}
+
+var gInstall = null;
+
+function test() {
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_failed_download;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+
+ // Set up the redirect to give a bad hash
+ setup_redirect({
+ "X-Target-Digest": "sha1:foo",
+ "Location": "http://example.com/browser/" + RELATIVE_DIR + "unsigned.xpi"
+ });
+
+ var url = "https://example.com/browser/" + RELATIVE_DIR + "redirect.sjs?mode=redirect";
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: url,
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_INCORRECT_HASH, "Should have seen a hash failure");
+ // Stash the failed download while the harness cleans itself up
+ gInstall = install;
+}
+
+function finish_failed_download() {
+ // Setup to track the successful re-download
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ // Give it the right hash this time
+ setup_redirect({
+ "X-Target-Digest": "sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
+ "Location": "http://example.com/browser/" + RELATIVE_DIR + "unsigned.xpi"
+ });
+
+ // The harness expects onNewInstall events for all installs that are about to start
+ Harness.onNewInstall(gInstall);
+
+ // Restart the install as a regular webpage install so the harness tracks it
+ AddonManager.installAddonsFromWebpage("application/x-xpinstall",
+ gBrowser.selectedBrowser,
+ gBrowser.contentPrincipal, [gInstall]);
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_installchrome.js b/toolkit/mozapps/extensions/test/xpinstall/browser_installchrome.js
new file mode 100644
index 000000000..c3be10ec9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_installchrome.js
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------------
+// Tests that calling InstallTrigger.installChrome works
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_localfile.js b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile.js
new file mode 100644
index 000000000..0e70e8177
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile.js
@@ -0,0 +1,34 @@
+// ----------------------------------------------------------------------------
+// Tests installing an local file works when loading the url
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Components.interfaces.nsIChromeRegistry);
+
+ var chromeroot = extractChromeRoot(gTestPath);
+ try {
+ var xpipath = cr.convertChromeURL(makeURI(chromeroot + "unsigned.xpi")).spec;
+ } catch (ex) {
+ var xpipath = chromeroot + "unsigned.xpi"; //scenario where we are running from a .jar and already extracted
+ }
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+ gBrowser.loadURI(xpipath);
+ });
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_localfile2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile2.js
new file mode 100644
index 000000000..253ed15b9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile2.js
@@ -0,0 +1,49 @@
+// ----------------------------------------------------------------------------
+// Test whether an install fails if the url is a local file when requested from
+// web content
+function test() {
+ waitForExplicitFinish();
+
+ var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Components.interfaces.nsIChromeRegistry);
+
+ var chromeroot = getChromeRoot(gTestPath);
+ try {
+ var xpipath = cr.convertChromeURL(makeURI(chromeroot + "unsigned.xpi")).spec;
+ } catch (ex) {
+ var xpipath = chromeroot + "unsigned.xpi"; //scenario where we are running from a .jar and already extracted
+ }
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": xpipath
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ gBrowser.contentWindow.addEventListener("InstallTriggered", page_loaded, false);
+ }
+
+ gBrowser.selectedBrowser.addEventListener("load", loadListener, true);
+
+ // In non-e10s the exception in the content page would trigger a test failure
+ if (!gMultiProcessBrowser)
+ expectUncaughtException();
+
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function page_loaded() {
+ gBrowser.contentWindow.removeEventListener("InstallTriggered", page_loaded, false);
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "exception", "installTrigger should have failed");
+
+ // In non-e10s the exception from the page is thrown after the event so we
+ // have to spin the event loop to make sure it arrives so expectUncaughtException
+ // sees it.
+ executeSoon(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_localfile3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile3.js
new file mode 100644
index 000000000..f24f41cd6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile3.js
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------
+// Tests installing an add-on from a local file with whitelisting disabled.
+// This should be blocked by the whitelist check.
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ // Disable direct request whitelisting, installing from file should be blocked.
+ Services.prefs.setBoolPref("xpinstall.whitelist.directRequest", false);
+
+ var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Components.interfaces.nsIChromeRegistry);
+
+ var chromeroot = extractChromeRoot(gTestPath);
+ try {
+ var xpipath = cr.convertChromeURL(makeURI(chromeroot + "unsigned.xpi")).spec;
+ } catch (ex) {
+ var xpipath = chromeroot + "unsigned.xpi"; //scenario where we are running from a .jar and already extracted
+ }
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+ gBrowser.loadURI(xpipath);
+ });
+}
+
+function allow_blocked(installInfo) {
+ ok(true, "Seen blocked");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+
+ Services.prefs.clearUserPref("xpinstall.whitelist.directRequest");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_localfile4.js b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile4.js
new file mode 100644
index 000000000..2e8263f19
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile4.js
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------
+// Tests installing an add-on from a local file with whitelisting disabled.
+// This should be blocked by the whitelist check.
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ // Disable file request whitelisting, installing by file referrer should be blocked.
+ Services.prefs.setBoolPref("xpinstall.whitelist.fileRequest", false);
+
+ var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Components.interfaces.nsIChromeRegistry);
+
+ var chromeroot = extractChromeRoot(gTestPath);
+ try {
+ var xpipath = cr.convertChromeURL(makeURI(chromeroot)).spec;
+ } catch (ex) {
+ var xpipath = chromeroot; //scenario where we are running from a .jar and already extracted
+ }
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(xpipath + "installtrigger.html?" + triggers);
+}
+
+function allow_blocked(installInfo) {
+ ok(true, "Seen blocked");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+
+ Services.prefs.clearUserPref("xpinstall.whitelist.fileRequest");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_multipackage.js b/toolkit/mozapps/extensions/test/xpinstall/browser_multipackage.js
new file mode 100644
index 000000000..c5e00008c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_multipackage.js
@@ -0,0 +1,53 @@
+// ----------------------------------------------------------------------------
+// Tests installing an signed add-on by navigating directly to the url
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+ gBrowser.loadURI(TESTROOT + "multipackage.xpi");
+ });
+}
+
+function get_item(items, name) {
+ for (let item of items) {
+ if (item.name == name)
+ return item;
+ }
+ ok(false, "Item for " + name + " was not listed");
+ return null;
+}
+
+function confirm_install(window) {
+ let items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 2, "Should be 2 items listed in the confirmation dialog");
+
+ let item = get_item(items, "XPI Test");
+ if (item) {
+ is(item.signed, "false", "Should not have listed the item as signed");
+ is(item.icon, "", "Should have listed no icon for the item");
+ }
+
+ item = get_item(items, "Signed XPI Test");
+ if (item) {
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ is(item.icon, "", "Should have listed no icon for the item");
+ }
+
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 2, "2 Add-ons should have been successfully installed");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway.js b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway.js
new file mode 100644
index 000000000..a2ba85d53
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway.js
@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------------------
+// Tests that navigating away from the initiating page during the install
+// doesn't break the install.
+// This verifies bug 473060
+function test() {
+ Harness.downloadProgressCallback = download_progress;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+ gBrowser.loadURI(TESTROOT + "enabled.html");
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway2.js
new file mode 100644
index 000000000..46d4ffe1e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway2.js
@@ -0,0 +1,34 @@
+// ----------------------------------------------------------------------------
+// Tests that closing the initiating page during the install cancels the install
+// to avoid spoofing the user.
+function test() {
+ Harness.downloadProgressCallback = download_progress;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+ gBrowser.removeCurrentTab();
+}
+
+function install_ended(install, addon) {
+ ok(false, "Should not have seen installs complete");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway3.js
new file mode 100644
index 000000000..7f55a65fe
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway3.js
@@ -0,0 +1,75 @@
+// ----------------------------------------------------------------------------
+// Tests that navigating to a new origin cancels ongoing installs.
+
+// Block the modal install UI from showing.
+let InstallPrompt = {
+ confirm: function(aBrowser, aUri, aInstalls, aCount) {
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallPrompt]),
+
+ classID: Components.ID("{405f3c55-241f-40df-97f1-a6e60e250ec5}"),
+
+ factory: {
+ registrar: Components.manager.QueryInterface(Ci.nsIComponentRegistrar),
+
+ register: function() {
+ this.registrar.registerFactory(InstallPrompt.classID, "InstallPrompt",
+ "@mozilla.org/addons/web-install-prompt;1",
+ this);
+ },
+
+ unregister: function() {
+ this.registrar.unregisterFactory(InstallPrompt.classID, this);
+ },
+
+ // nsIFactory
+ createInstance: function(aOuter, aIID) {
+ if (aOuter) {
+ throw Components.Exception("Class does not allow aggregation",
+ Components.results.NS_ERROR_NO_AGGREGATION);
+ }
+ return InstallPrompt.QueryInterface(aIID);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+ }
+};
+
+function test() {
+ InstallPrompt.factory.register();
+ registerCleanupFunction(() => {
+ InstallPrompt.factory.unregister();
+ });
+
+ Harness.downloadProgressCallback = download_progress;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+ gBrowser.loadURI(TESTROOT2 + "enabled.html");
+}
+
+function install_ended(install, addon) {
+ ok(false, "Should not have seen installs complete");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been successfully installed");
+
+ Services.perms.remove("http://example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway4.js b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway4.js
new file mode 100644
index 000000000..61fc7d3ac
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_navigateaway4.js
@@ -0,0 +1,44 @@
+// ----------------------------------------------------------------------------
+// Tests that navigating to a new origin cancels ongoing installs and closes
+// the install UI.
+let sawUnload = null;
+
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ sawUnload = BrowserTestUtils.waitForEvent(window, "unload");
+
+ gBrowser.loadURI(TESTROOT2 + "enabled.html");
+
+ return Harness.leaveOpen;
+}
+
+function install_ended(install, addon) {
+ ok(false, "Should not have seen installs complete");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been successfully installed");
+
+ Services.perms.remove("http://example.com", "install");
+
+ sawUnload.then(() => {
+ ok(true, "The install UI should have closed itself.");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+ });
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_offline.js b/toolkit/mozapps/extensions/test/xpinstall/browser_offline.js
new file mode 100644
index 000000000..28f3497d1
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_offline.js
@@ -0,0 +1,61 @@
+let proxyPrefValue;
+
+// ----------------------------------------------------------------------------
+// Tests that going offline cancels an in progress download.
+function test() {
+ Harness.downloadProgressCallback = download_progress;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+ try {
+ // 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);
+ Services.io.manageOfflineStatus = false;
+ Services.io.offline = true;
+ } catch (ex) {
+ }
+}
+
+function finish_test(count) {
+ function wait_for_online() {
+ info("Checking if the browser is still offline...");
+
+ let tab = gBrowser.selectedTab;
+ tab.linkedBrowser.addEventListener("DOMContentLoaded", function errorLoad() {
+ tab.linkedBrowser.removeEventListener("DOMContentLoaded", errorLoad, true);
+ let url = tab.linkedBrowser.contentDocument.documentURI;
+ info("loaded: " + url);
+ if (/^about:neterror\?e=netOffline/.test(url)) {
+ wait_for_online();
+ } else {
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+ }
+ }, true);
+ tab.linkedBrowser.loadURI("http://example.com/");
+ }
+
+ is(count, 0, "No add-ons should have been installed");
+ try {
+ Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
+ Services.io.offline = false;
+ } catch (ex) {
+ }
+
+ Services.perms.remove("example.com", "install");
+
+ wait_for_online();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_relative.js b/toolkit/mozapps/extensions/test/xpinstall/browser_relative.js
new file mode 100644
index 000000000..dfd5a0898
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_relative.js
@@ -0,0 +1,49 @@
+// ----------------------------------------------------------------------------
+// Tests that InstallTrigger deals with relative urls correctly.
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.finalContentEvent = "InstallComplete";
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: "unsigned.xpi",
+ IconURL: "icon.png",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have seen the name");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "true", "installTrigger should have claimed success");
+ is(doc.getElementById("status").textContent, "0", "Callback should have seen a success");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js
new file mode 100644
index 000000000..e6efe5468
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js
@@ -0,0 +1,72 @@
+// ----------------------------------------------------------------------------
+// Tests installing two signed add-ons in the same trigger works.
+// This verifies bug 453545
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Signed XPI": TESTROOT + "signed.xpi",
+ "Signed XPI 2": TESTROOT + "signed2.xpi",
+ "Signed XPI 3": TESTROOT + "signed-no-o.xpi",
+ "Signed XPI 4": TESTROOT + "signed-no-cn.xpi",
+ "Signed XPI 5": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_item(items, url) {
+ for (let item of items) {
+ if (item.url == url)
+ return item;
+ }
+ ok(false, "Item for " + url + " was not listed");
+ return null;
+}
+
+function confirm_install(window) {
+
+ var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"].
+ getService(Components.interfaces.nsIStringBundleService);
+ var bundle = sbs.createBundle("chrome://mozapps/locale/xpinstall/xpinstallConfirm.properties");
+
+ var expectedIntroString = bundle.formatStringFromName("itemWarnIntroMultiple", ["5"], 1);
+
+ var introStringNode = window.document.getElementById("itemWarningIntro");
+ is(introStringNode.textContent, expectedIntroString, "Should have the correct intro string");
+
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 5, "Should be 5 items listed in the confirmation dialog");
+ let item = get_item(items, TESTROOT + "signed.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ item = get_item(items, TESTROOT + "signed2.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 5, "5 Add-ons should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js
new file mode 100644
index 000000000..c57ddb200
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js
@@ -0,0 +1,67 @@
+// ----------------------------------------------------------------------------
+// Tests that the correct signer is presented for combinations of O and CN present.
+// The signed files have (when present) O=Mozilla Testing, CN=Object Signer
+// This verifies bug 372980
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Signed XPI (O and CN)": TESTROOT + "signed.xpi",
+ "Signed XPI (CN)": TESTROOT + "signed-no-o.xpi",
+ "Signed XPI (O)": TESTROOT + "signed-no-cn.xpi",
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_item(items, url) {
+ for (let item of items) {
+ if (item.url == url)
+ return item;
+ }
+ ok(false, "Item for " + url + " was not listed");
+ return null;
+}
+
+function confirm_install(window) {
+ let items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 3, "Should be 3 items listed in the confirmation dialog");
+ let item = get_item(items, TESTROOT + "signed.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ item = get_item(items, TESTROOT + "signed-no-o.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test (No Org)", "Should have seen the name from the trigger list");
+ is(item.cert, "(Object Signer)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ item = get_item(items, TESTROOT + "signed-no-cn.xpi");
+ if (item) {
+ is(item.name, "Signed XPI Test (No Common Name)", "Should have seen the name from the trigger list");
+ is(item.cert, "(Mozilla Testing)", "Should have seen the signer");
+ is(item.signed, "true", "Should have listed the item as signed");
+ }
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 3, "3 Add-ons should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_tampered.js b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_tampered.js
new file mode 100644
index 000000000..a3dc454d9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_tampered.js
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------
+// Tests installing a signed add-on that has been tampered with after signing.
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.downloadFailedCallback = download_failed;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Tampered Signed XPI": TESTROOT + "signed-tampered.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ ok(false, "Should not offer to install");
+}
+
+function download_failed(install) {
+ is(install.error, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_trigger.js b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_trigger.js
new file mode 100644
index 000000000..cefce2a4c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_trigger.js
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------
+// Tests installing an signed add-on through an InstallTrigger call in web
+// content.
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Signed XPI": TESTROOT + "signed.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "Signed XPI Test", "Should have seen the name from the trigger list");
+ is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
+ is(items[0].cert, "(Object Signer)", "Should have seen the signer");
+ is(items[0].signed, "true", "Should have listed the item as signed");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_untrusted.js b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_untrusted.js
new file mode 100644
index 000000000..afb34d8a3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_untrusted.js
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------
+// Tests installing an add-on signed by an untrusted certificate through an
+// InstallTrigger call in web content.
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Untrusted Signed XPI": TESTROOT + "signed-untrusted.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "Signed XPI Test", "Should have had the filename for the item name");
+ is(items[0].url, TESTROOT + "signed-untrusted.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, "", "Should have listed no icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_url.js b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_url.js
new file mode 100644
index 000000000..33cda6e4c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_url.js
@@ -0,0 +1,34 @@
+// ----------------------------------------------------------------------------
+// Tests installing an signed add-on by navigating directly to the url
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+ gBrowser.loadURI(TESTROOT + "signed.xpi");
+ });
+}
+
+function confirm_install(window) {
+ let items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "Signed XPI Test", "Should have had the name");
+ is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
+ is(items[0].cert, "(Object Signer)", "Should have seen the signer");
+ is(items[0].signed, "true", "Should have listed the item as signed");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_softwareupdate.js b/toolkit/mozapps/extensions/test/xpinstall/browser_softwareupdate.js
new file mode 100644
index 000000000..4c3dc768e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_softwareupdate.js
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------------
+// Tests that calling InstallTrigger.startSoftwareUpdate works
+function test() {
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_switchtab.js b/toolkit/mozapps/extensions/test/xpinstall/browser_switchtab.js
new file mode 100644
index 000000000..9672de9bd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_switchtab.js
@@ -0,0 +1,49 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content.
+let expectedTab = null;
+
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ IconURL: TESTROOT + "icon.png",
+ toString: function() { return this.URL; }
+ }
+ }));
+ expectedTab = gBrowser.addTab();
+ expectedTab.linkedBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have seen the name");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+
+ is(gBrowser.selectedTab, expectedTab, "Should have switched to the installing tab.");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeTab(expectedTab);
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_trigger_redirect.js b/toolkit/mozapps/extensions/test/xpinstall/browser_trigger_redirect.js
new file mode 100644
index 000000000..a5448b616
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_trigger_redirect.js
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------
+// Tests that the InstallTrigger callback can redirect to a relative url.
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.finalContentEvent = "InstallComplete";
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "triggerredirect.html");
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have seen the name");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ var doc = gBrowser.contentDocument;
+ is(gBrowser.currentURI.spec, TESTROOT + "triggerredirect.html#foo", "Should have redirected");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger.js b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger.js
new file mode 100644
index 000000000..ab0f238aa
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger.js
@@ -0,0 +1,50 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content.
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.finalContentEvent = "InstallComplete";
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ IconURL: TESTROOT + "icon.png",
+ toString: function() { return this.URL; }
+ }
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have seen the name");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "true", "installTrigger should have claimed success");
+ is(doc.getElementById("status").textContent, "0", "Callback should have seen a success");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_iframe.js b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_iframe.js
new file mode 100644
index 000000000..658bbfe1c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_iframe.js
@@ -0,0 +1,51 @@
+// ----------------------------------------------------------------------------
+// Test for bug 589598 - Ensure that installing through InstallTrigger
+// works in an iframe in web content.
+
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.finalContentEvent = "InstallComplete";
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var inner_url = encodeURIComponent(TESTROOT + "installtrigger.html?" + encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ IconURL: TESTROOT + "icon.png",
+ toString: function() { return this.URL; }
+ }
+ })));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger_frame.html?" + inner_url);
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have seen the name");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ Services.perms.remove("example.com", "install");
+
+ var doc = gBrowser.contentWindow.frames[0].document; // Document of iframe
+ is(doc.getElementById("return").textContent, "true", "installTrigger in iframe should have claimed success");
+ is(doc.getElementById("status").textContent, "0", "Callback in iframe should have seen a success");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_xorigin.js b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_xorigin.js
new file mode 100644
index 000000000..07947a135
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_trigger_xorigin.js
@@ -0,0 +1,38 @@
+// ----------------------------------------------------------------------------
+// Ensure that an inner frame from a different origin can't initiate an install
+
+let wasOriginBlocked = false;
+
+function test() {
+ Harness.installOriginBlockedCallback = install_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.finalContentEvent = "InstallComplete";
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ var inner_url = encodeURIComponent(TESTROOT + "installtrigger.html?" + encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": {
+ URL: TESTROOT + "unsigned.xpi",
+ IconURL: TESTROOT + "icon.png",
+ toString: function() { return this.URL; }
+ }
+ })));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT2 + "installtrigger_frame.html?" + inner_url);
+}
+
+function install_blocked(installInfo) {
+ wasOriginBlocked = true;
+}
+
+function finish_test(count) {
+ ok(wasOriginBlocked, "Should have been blocked due to the cross origin request.");
+
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("http://example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_url.js b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_url.js
new file mode 100644
index 000000000..e103dffd3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_unsigned_url.js
@@ -0,0 +1,35 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on by navigating directly to the url
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+ gBrowser.loadURI(TESTROOT + "unsigned.xpi");
+ });
+}
+
+function confirm_install(window) {
+ let items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have had the filename for the item name");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].icon, "", "Should have listed no icon for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist.js
new file mode 100644
index 000000000..448ce3a5c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist.js
@@ -0,0 +1,46 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 252830
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installEndedCallback = install_ended;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function allow_blocked(installInfo) {
+ is(installInfo.browser, gBrowser.selectedBrowser, "Install should have been triggered by the right browser");
+ is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
+ return true;
+}
+
+function confirm_install(window) {
+ var items = window.document.getElementById("itemList").childNodes;
+ is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+ is(items[0].name, "XPI Test", "Should have seen the name from the trigger list");
+ is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+ is(items[0].signed, "false", "Should have listed the item as unsigned");
+ return true;
+}
+
+function install_ended(install, addon) {
+ install.cancel();
+}
+
+function finish_test(count) {
+ is(count, 1, "1 Add-on should have been successfully installed");
+
+ var doc = gBrowser.contentDocument;
+ is(doc.getElementById("return").textContent, "false", "installTrigger should seen a failure");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist2.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist2.js
new file mode 100644
index 000000000..168a31ef3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist2.js
@@ -0,0 +1,31 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content. This should be blocked by the whitelist check because the source
+// is not whitelisted, even though the target is.
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
+
+ var triggers = encodeURIComponent(JSON.stringify({
+ "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
+ }));
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function allow_blocked(installInfo) {
+ is(installInfo.browser, gBrowser.selectedBrowser, "Install should have been triggered by the right browser");
+ is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
+ return false;
+}
+
+function finish_test() {
+ Services.perms.remove("example.org", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist3.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist3.js
new file mode 100644
index 000000000..888e1bd5a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist3.js
@@ -0,0 +1,28 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a navigation. Should not be
+// blocked since the referer is whitelisted.
+let URL = TESTROOT2 + "navigate.html?" + encodeURIComponent(TESTROOT + "unsigned.xpi");
+
+function test() {
+ Harness.installConfirmCallback = confirm_install;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(URL);
+}
+
+function confirm_install(window) {
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.org", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist4.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist4.js
new file mode 100644
index 000000000..14a961742
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist4.js
@@ -0,0 +1,30 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a navigation. Should be
+// blocked since the referer is not whitelisted even though the target is.
+let URL = TESTROOT2 + "navigate.html?" + encodeURIComponent(TESTROOT + "unsigned.xpi");
+
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ var pm = Services.perms;
+ pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(URL);
+}
+
+function allow_blocked(installInfo) {
+ is(installInfo.browser, gBrowser.selectedBrowser, "Install should have been triggered by the right browser");
+ is(installInfo.originatingURI.spec, URL, "Install should have been triggered by the right uri");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ Services.perms.remove("example.com", "install");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist5.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist5.js
new file mode 100644
index 000000000..97448d803
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist5.js
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a startSoftwareUpdate call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 252830
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function allow_blocked(installInfo) {
+ is(installInfo.browser, gBrowser.selectedBrowser, "Install should have been triggered by the right browser");
+ is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist6.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist6.js
new file mode 100644
index 000000000..f2f3641e4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist6.js
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an installChrome call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 252830
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function allow_blocked(installInfo) {
+ is(installInfo.browser, gBrowser.selectedBrowser, "Install should have been triggered by the right browser");
+ is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
+// ----------------------------------------------------------------------------
diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist7.js b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist7.js
new file mode 100644
index 000000000..b36617ab5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist7.js
@@ -0,0 +1,32 @@
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a direct install request from
+// web content. This should be blocked by the whitelist check because we disable
+// direct request whitelisting, even though the target URI is whitelisted.
+function test() {
+ Harness.installBlockedCallback = allow_blocked;
+ Harness.installsCompletedCallback = finish_test;
+ Harness.setup();
+
+ // Disable direct request whitelisting, installing should be blocked.
+ Services.prefs.setBoolPref("xpinstall.whitelist.directRequest", false);
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+ gBrowser.loadURI(TESTROOT + "unsigned.xpi");
+ });
+}
+
+function allow_blocked(installInfo) {
+ ok(true, "Seen blocked");
+ return false;
+}
+
+function finish_test(count) {
+ is(count, 0, "No add-ons should have been installed");
+
+ Services.perms.remove("example.org", "install");
+ Services.prefs.clearUserPref("xpinstall.whitelist.directRequest");
+
+ gBrowser.removeCurrentTab();
+ Harness.finish();
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/bug540558.html b/toolkit/mozapps/extensions/test/xpinstall/bug540558.html
new file mode 100644
index 000000000..666a56437
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/bug540558.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page tests that window.InstallTrigger.install works -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function startInstall() {
+ window.InstallTrigger.install({
+ "Unsigned XPI": "unsigned.xpi"
+ });
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+<p id="return"></p>
+<p id="status"></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/bug638292.html b/toolkit/mozapps/extensions/test/xpinstall/bug638292.html
new file mode 100644
index 000000000..198207d4b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/bug638292.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page tests InstallTrigger is defined in a new window -->
+
+<head>
+<title>InstallTrigger tests</title>
+</head>
+<body>
+<p>InstallTrigger tests</p>
+<p><a id="link1" target="_blank" href="enabled.html">Open window with target</a></p>
+<p><a id="link2" onclick="window.open(this.href); return false" href="enabled.html">Open window with JS</a></p>
+<p><a id="link3" href="enabled.html">Open window with middle-click</a></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/bug645699.html b/toolkit/mozapps/extensions/test/xpinstall/bug645699.html
new file mode 100644
index 000000000..9a65720ae
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/bug645699.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function startInstall() {
+ var whiteUrl = "https://example.org/";
+
+ try {
+ Object.defineProperty(window, "location", { value : { href : whiteUrl } });
+ throw new Error("Object.defineProperty(window, 'location', ...) should have thrown");
+ } catch (exc) {
+ if (!(exc instanceof TypeError))
+ throw exc;
+ }
+ Object.defineProperty(document, "documentURIObject", { spec : { href : whiteUrl } });
+
+ InstallTrigger.install({
+ "Unsigned XPI": "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi"
+ });
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/concurrent_installs.html b/toolkit/mozapps/extensions/test/xpinstall/concurrent_installs.html
new file mode 100644
index 000000000..192bbf2bc
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/concurrent_installs.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<head>
+ <meta charset="utf-8">
+<title>Concurrent InstallTrigger tests</title>
+<script type="text/javascript">
+function installCallback(url, status) {
+ document.getElementById("status").textContent = status;
+
+ dump("Sending InstallComplete\n");
+ var event = new CustomEvent("InstallComplete", {detail: {loc: location.href, xpi: url}});
+ window.dispatchEvent(event);
+}
+
+function startInstall() {
+ var root = location.href.replace("concurrent_installs.html", "");
+ var triggers = {
+ "Unsigned XPI": root + "unsigned.xpi"
+ };
+ try {
+ document.getElementById("return").textContent = InstallTrigger.install(triggers, installCallback);
+ }
+ catch (e) {
+ document.getElementById("return").textContent = "exception";
+ throw e;
+ }
+}
+</script>
+</head>
+<body>
+<p>InstallTrigger tests</p>
+<button id="installnow" onclick="startInstall()">Click to install</button>
+<p id="return"></p>
+<p id="status"></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/cookieRedirect.sjs b/toolkit/mozapps/extensions/test/xpinstall/cookieRedirect.sjs
new file mode 100644
index 000000000..92bccd9ec
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/cookieRedirect.sjs
@@ -0,0 +1,24 @@
+// Simple script redirects to the query part of the uri if the cookie "xpinstall"
+// has the value "true", otherwise gives a 500 error.
+
+function handleRequest(request, response)
+{
+ let cookie = null;
+ if (request.hasHeader("Cookie")) {
+ let cookies = request.getHeader("Cookie").split(";");
+ for (let i = 0; i < cookies.length; i++) {
+ if (cookies[i].substring(0, 10) == "xpinstall=")
+ cookie = cookies[i].substring(10);
+ }
+ }
+
+ if (cookie == "true") {
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ response.setHeader("Location", request.queryString);
+ response.write("See " + request.queryString);
+ }
+ else {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ response.write("Invalid request");
+ }
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi b/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
new file mode 100644
index 000000000..35d7bd5e5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
@@ -0,0 +1 @@
+This is a corrupt zip file
diff --git a/toolkit/mozapps/extensions/test/xpinstall/empty.xpi b/toolkit/mozapps/extensions/test/xpinstall/empty.xpi
new file mode 100644
index 000000000..74ed2b817
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/empty.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/enabled.html b/toolkit/mozapps/extensions/test/xpinstall/enabled.html
new file mode 100644
index 000000000..ec8513edc
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/enabled.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will test if InstallTrigger seems to be enabled -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function init() {
+ document.getElementById("enabled").textContent = InstallTrigger.enabled() ? "true" : "false";
+ dump("Sending PageLoaded\n");
+ var event = new CustomEvent("PageLoaded");
+ window.dispatchEvent(event);
+}
+</script>
+</head>
+<body onload="init()">
+<p>InstallTrigger tests</p>
+<p id="enabled"></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/hashRedirect.sjs b/toolkit/mozapps/extensions/test/xpinstall/hashRedirect.sjs
new file mode 100644
index 000000000..324a092a3
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/hashRedirect.sjs
@@ -0,0 +1,15 @@
+// Simple script redirects takes the query part of te request and splits it on
+// the | character. Anything before is included as the X-Target-Digest header
+// the latter part is used as the url to redirect to
+
+function handleRequest(request, response)
+{
+ let pos = request.queryString.indexOf("|");
+ let header = request.queryString.substring(0, pos);
+ let url = request.queryString.substring(pos + 1);
+
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ response.setHeader("X-Target-Digest", header);
+ response.setHeader("Location", url);
+ response.write("See " + url);
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/head.js b/toolkit/mozapps/extensions/test/xpinstall/head.js
new file mode 100644
index 000000000..90db29924
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/head.js
@@ -0,0 +1,424 @@
+const RELATIVE_DIR = "toolkit/mozapps/extensions/test/xpinstall/";
+
+const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
+const TESTROOT2 = "http://example.org/browser/" + RELATIVE_DIR;
+const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
+const PROMPT_URL = "chrome://global/content/commonDialog.xul";
+const ADDONS_URL = "chrome://mozapps/content/extensions/extensions.xul";
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
+const PREF_INSTALL_REQUIRESECUREORIGIN = "extensions.install.requireSecureOrigin";
+const CHROME_NAME = "mochikit";
+
+function getChromeRoot(path) {
+ if (path === undefined) {
+ return "chrome://" + CHROME_NAME + "/content/browser/" + RELATIVE_DIR
+ }
+ return getRootDirectory(path);
+}
+
+function extractChromeRoot(path) {
+ var chromeRootPath = getChromeRoot(path);
+ var jar = getJar(chromeRootPath);
+ if (jar) {
+ var tmpdir = extractJarToTmp(jar);
+ return "file://" + tmpdir.path + "/";
+ }
+ return chromeRootPath;
+}
+
+/**
+ * This is a test harness designed to handle responding to UI during the process
+ * of installing an XPI. A test can set callbacks to hear about specific parts
+ * of the sequence.
+ * Before use setup must be called and finish must be called afterwards.
+ */
+var Harness = {
+ // If set then the callback is called when an install is attempted and
+ // software installation is disabled.
+ installDisabledCallback: null,
+ // If set then the callback is called when an install is attempted and
+ // then canceled.
+ installCancelledCallback: null,
+ // If set then the callback will be called when an install's origin is blocked.
+ installOriginBlockedCallback: null,
+ // If set then the callback will be called when an install is blocked by the
+ // whitelist. The callback should return true to continue with the install
+ // anyway.
+ installBlockedCallback: null,
+ // If set will be called in the event of authentication being needed to get
+ // the xpi. Should return a 2 element array of username and password, or
+ // null to not authenticate.
+ authenticationCallback: null,
+ // If set this will be called to allow checking the contents of the xpinstall
+ // confirmation dialog. The callback should return true to continue the install.
+ installConfirmCallback: null,
+ // If set will be called when downloading of an item has begun.
+ downloadStartedCallback: null,
+ // If set will be called during the download of an item.
+ downloadProgressCallback: null,
+ // If set will be called when an xpi fails to download.
+ downloadFailedCallback: null,
+ // If set will be called when an xpi download is cancelled.
+ downloadCancelledCallback: null,
+ // If set will be called when downloading of an item has ended.
+ downloadEndedCallback: null,
+ // If set will be called when installation by the extension manager of an xpi
+ // item starts
+ installStartedCallback: null,
+ // If set will be called when an xpi fails to install.
+ installFailedCallback: null,
+ // If set will be called when each xpi item to be installed completes
+ // installation.
+ installEndedCallback: null,
+ // If set will be called when all triggered items are installed or the install
+ // is canceled.
+ installsCompletedCallback: null,
+ // If set the harness will wait for this DOM event before calling
+ // installsCompletedCallback
+ finalContentEvent: null,
+
+ waitingForEvent: false,
+ pendingCount: null,
+ installCount: null,
+ runningInstalls: null,
+
+ waitingForFinish: false,
+
+ // A unique value to return from the installConfirmCallback to indicate that
+ // the install UI shouldn't be closed automatically
+ leaveOpen: {},
+
+ // Setup and tear down functions
+ setup: function() {
+ if (!this.waitingForFinish) {
+ waitForExplicitFinish();
+ this.waitingForFinish = true;
+
+ Services.prefs.setBoolPref(PREF_INSTALL_REQUIRESECUREORIGIN, false);
+
+ Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
+ Services.obs.addObserver(this, "addon-install-started", false);
+ Services.obs.addObserver(this, "addon-install-disabled", false);
+ Services.obs.addObserver(this, "addon-install-origin-blocked", false);
+ Services.obs.addObserver(this, "addon-install-blocked", false);
+ Services.obs.addObserver(this, "addon-install-failed", false);
+ Services.obs.addObserver(this, "addon-install-complete", false);
+
+ AddonManager.addInstallListener(this);
+
+ Services.wm.addListener(this);
+
+ var self = this;
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
+ Services.prefs.clearUserPref(PREF_INSTALL_REQUIRESECUREORIGIN);
+ Services.obs.removeObserver(self, "addon-install-started");
+ Services.obs.removeObserver(self, "addon-install-disabled");
+ Services.obs.removeObserver(self, "addon-install-origin-blocked");
+ Services.obs.removeObserver(self, "addon-install-blocked");
+ Services.obs.removeObserver(self, "addon-install-failed");
+ Services.obs.removeObserver(self, "addon-install-complete");
+
+ AddonManager.removeInstallListener(self);
+
+ Services.wm.removeListener(self);
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ is(aInstalls.length, 0, "Should be no active installs at the end of the test");
+ aInstalls.forEach(function(aInstall) {
+ info("Install for " + aInstall.sourceURI + " is in state " + aInstall.state);
+ aInstall.cancel();
+ });
+ });
+ });
+ }
+
+ this.installCount = 0;
+ this.pendingCount = 0;
+ this.runningInstalls = [];
+ },
+
+ finish: function() {
+ finish();
+ },
+
+ endTest: function() {
+ let callback = this.installsCompletedCallback;
+ let count = this.installCount;
+
+ is(this.runningInstalls.length, 0, "Should be no running installs left");
+ this.runningInstalls.forEach(function(aInstall) {
+ info("Install for " + aInstall.sourceURI + " is in state " + aInstall.state);
+ });
+
+ this.installOriginBlockedCallback = null;
+ this.installBlockedCallback = null;
+ this.authenticationCallback = null;
+ this.installConfirmCallback = null;
+ this.downloadStartedCallback = null;
+ this.downloadProgressCallback = null;
+ this.downloadCancelledCallback = null;
+ this.downloadFailedCallback = null;
+ this.downloadEndedCallback = null;
+ this.installStartedCallback = null;
+ this.installFailedCallback = null;
+ this.installEndedCallback = null;
+ this.installsCompletedCallback = null;
+ this.runningInstalls = null;
+
+ if (callback)
+ callback(count);
+ },
+
+ // Window open handling
+ windowReady: function(window) {
+ if (window.document.location.href == XPINSTALL_URL) {
+ if (this.installBlockedCallback)
+ ok(false, "Should have been blocked by the whitelist");
+ this.pendingCount = window.document.getElementById("itemList").childNodes.length;
+
+ // If there is a confirm callback then its return status determines whether
+ // to install the items or not. If not the test is over.
+ let result = true;
+ if (this.installConfirmCallback) {
+ result = this.installConfirmCallback(window);
+ if (result === this.leaveOpen)
+ return;
+ }
+
+ if (!result) {
+ window.document.documentElement.cancelDialog();
+ }
+ else {
+ // Initially the accept button is disabled on a countdown timer
+ var button = window.document.documentElement.getButton("accept");
+ button.disabled = false;
+ window.document.documentElement.acceptDialog();
+ }
+ }
+ else if (window.document.location.href == PROMPT_URL) {
+ var promptType = window.args.promptType;
+ switch (promptType) {
+ case "alert":
+ case "alertCheck":
+ case "confirmCheck":
+ case "confirm":
+ case "confirmEx":
+ window.document.documentElement.acceptDialog();
+ break;
+ case "promptUserAndPass":
+ // This is a login dialog, hopefully an authentication prompt
+ // for the xpi.
+ if (this.authenticationCallback) {
+ var auth = this.authenticationCallback();
+ if (auth && auth.length == 2) {
+ window.document.getElementById("loginTextbox").value = auth[0];
+ window.document.getElementById("password1Textbox").value = auth[1];
+ window.document.documentElement.acceptDialog();
+ }
+ else {
+ window.document.documentElement.cancelDialog();
+ }
+ }
+ else {
+ window.document.documentElement.cancelDialog();
+ }
+ break;
+ default:
+ ok(false, "prompt type " + promptType + " not handled in test.");
+ break;
+ }
+ }
+ },
+
+ // Install blocked handling
+
+ installDisabled: function(installInfo) {
+ ok(!!this.installDisabledCallback, "Installation shouldn't have been disabled");
+ if (this.installDisabledCallback)
+ this.installDisabledCallback(installInfo);
+ this.endTest();
+ },
+
+ installCancelled: function(installInfo) {
+ if (this.expectingCancelled)
+ return;
+
+ ok(!!this.installCancelledCallback, "Installation shouldn't have been cancelled");
+ if (this.installCancelledCallback)
+ this.installCancelledCallback(installInfo);
+ this.endTest();
+ },
+
+ installOriginBlocked: function(installInfo) {
+ ok(!!this.installOriginBlockedCallback, "Shouldn't have been blocked");
+ if (this.installOriginBlockedCallback)
+ this.installOriginBlockedCallback(installInfo);
+ this.endTest();
+ },
+
+ installBlocked: function(installInfo) {
+ ok(!!this.installBlockedCallback, "Shouldn't have been blocked by the whitelist");
+ if (this.installBlockedCallback && this.installBlockedCallback(installInfo)) {
+ this.installBlockedCallback = null;
+ installInfo.install();
+ }
+ else {
+ this.expectingCancelled = true;
+ installInfo.installs.forEach(function(install) {
+ install.cancel();
+ });
+ this.expectingCancelled = false;
+ this.endTest();
+ }
+ },
+
+ // nsIWindowMediatorListener
+
+ onWindowTitleChange: function(window, title) {
+ },
+
+ onOpenWindow: function(window) {
+ var domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindow);
+ var self = this;
+ waitForFocus(function() {
+ self.windowReady(domwindow);
+ }, domwindow);
+ },
+
+ onCloseWindow: function(window) {
+ },
+
+ // Addon Install Listener
+
+ onNewInstall: function(install) {
+ this.runningInstalls.push(install);
+
+ if (this.finalContentEvent && !this.waitingForEvent) {
+ this.waitingForEvent = true;
+ info("Waiting for " + this.finalContentEvent);
+ let win = gBrowser.contentWindow;
+ let listener = () => {
+ info("Saw " + this.finalContentEvent);
+ win.removeEventListener(this.finalContentEvent, listener, false);
+ this.waitingForEvent = false;
+ if (this.pendingCount == 0)
+ this.endTest();
+ }
+ win.addEventListener(this.finalContentEvent, listener, false);
+ }
+ },
+
+ onDownloadStarted: function(install) {
+ this.pendingCount++;
+ if (this.downloadStartedCallback)
+ this.downloadStartedCallback(install);
+ },
+
+ onDownloadProgress: function(install) {
+ if (this.downloadProgressCallback)
+ this.downloadProgressCallback(install);
+ },
+
+ onDownloadEnded: function(install) {
+ if (this.downloadEndedCallback)
+ this.downloadEndedCallback(install);
+ },
+
+ onDownloadCancelled: function(install) {
+ isnot(this.runningInstalls.indexOf(install), -1,
+ "Should only see cancelations for started installs");
+ this.runningInstalls.splice(this.runningInstalls.indexOf(install), 1);
+
+ if (this.downloadCancelledCallback)
+ this.downloadCancelledCallback(install);
+ this.checkTestEnded();
+ },
+
+ onDownloadFailed: function(install) {
+ if (this.downloadFailedCallback)
+ this.downloadFailedCallback(install);
+ this.checkTestEnded();
+ },
+
+ onInstallStarted: function(install) {
+ if (this.installStartedCallback)
+ this.installStartedCallback(install);
+ },
+
+ onInstallEnded: function(install, addon) {
+ if (this.installEndedCallback)
+ this.installEndedCallback(install, addon);
+ this.installCount++;
+ this.checkTestEnded();
+ },
+
+ onInstallFailed: function(install) {
+ if (this.installFailedCallback)
+ this.installFailedCallback(install);
+ this.checkTestEnded();
+ },
+
+ checkTestEnded: function() {
+ if (--this.pendingCount == 0 && !this.waitingForEvent)
+ this.endTest();
+ },
+
+ // nsIObserver
+
+ observe: function(subject, topic, data) {
+ var installInfo = subject.QueryInterface(Components.interfaces.amIWebInstallInfo);
+ switch (topic) {
+ case "addon-install-started":
+ is(this.runningInstalls.length, installInfo.installs.length,
+ "Should have seen the expected number of installs started");
+ break;
+ case "addon-install-disabled":
+ this.installDisabled(installInfo);
+ break;
+ case "addon-install-cancelled":
+ this.installCancelled(installInfo);
+ break;
+ case "addon-install-origin-blocked":
+ this.installOriginBlocked(installInfo);
+ break;
+ case "addon-install-blocked":
+ this.installBlocked(installInfo);
+ break;
+ case "addon-install-failed":
+ installInfo.installs.forEach(function(aInstall) {
+ isnot(this.runningInstalls.indexOf(aInstall), -1,
+ "Should only see failures for started installs");
+
+ ok(aInstall.error != 0 || aInstall.addon.appDisabled,
+ "Failed installs should have an error or be appDisabled");
+
+ this.runningInstalls.splice(this.runningInstalls.indexOf(aInstall), 1);
+ }, this);
+ break;
+ case "addon-install-complete":
+ installInfo.installs.forEach(function(aInstall) {
+ isnot(this.runningInstalls.indexOf(aInstall), -1,
+ "Should only see completed events for started installs");
+
+ is(aInstall.error, 0, "Completed installs should have no error");
+ ok(!aInstall.appDisabled, "Completed installs should not be appDisabled");
+
+ // Complete installs are either in the INSTALLED or CANCELLED state
+ // since the test may cancel installs the moment they complete.
+ ok(aInstall.state == AddonManager.STATE_INSTALLED ||
+ aInstall.state == AddonManager.STATE_CANCELLED,
+ "Completed installs should be in the right state");
+
+ this.runningInstalls.splice(this.runningInstalls.indexOf(aInstall), 1);
+ }, this);
+ break;
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIWindowMediatorListener,
+ Ci.nsISupports])
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi b/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
new file mode 100644
index 000000000..cc40f43c9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/installchrome.html b/toolkit/mozapps/extensions/test/xpinstall/installchrome.html
new file mode 100644
index 000000000..71c072d3a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/installchrome.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept a url as the uri query and pass it to InstallTrigger.installChrome -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function startInstall() {
+ InstallTrigger.installChrome(InstallTrigger.SKIN,
+ decodeURIComponent(document.location.search.substring(1)),
+ "test");
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html b/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
new file mode 100644
index 000000000..cd7618cc5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept some json as the uri query and pass it to InstallTrigger.install -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function installCallback(url, status) {
+ document.getElementById("status").textContent = status;
+
+ dump("Sending InstallComplete\n");
+ var event = new CustomEvent("InstallComplete");
+ var target = window.parent ? window.parent : window;
+ target.dispatchEvent(event);
+}
+
+function startInstall() {
+ var event = new CustomEvent("InstallTriggered");
+ var text = decodeURIComponent(document.location.search.substring(1));
+ var triggers = JSON.parse(text);
+ try {
+ document.getElementById("return").textContent = InstallTrigger.install(triggers, installCallback);
+ dump("Sending InstallTriggered\n");
+ window.dispatchEvent(event);
+ }
+ catch (e) {
+ document.getElementById("return").textContent = "exception";
+ dump("Sending InstallTriggered\n");
+ window.dispatchEvent(event);
+ throw e;
+ }
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+<p id="return"></p>
+<p id="status"></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/installtrigger_frame.html b/toolkit/mozapps/extensions/test/xpinstall/installtrigger_frame.html
new file mode 100644
index 000000000..2b302642e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/installtrigger_frame.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept some url as the uri query and load it in
+ an inner iframe, which will run InstallTrigger.install -->
+
+<head>
+<title>InstallTrigger frame tests</title>
+<script type="text/javascript">
+function prepChild() {
+ // Pass our parameters over to the child
+ var child = window.frames[0];
+ var url = decodeURIComponent(document.location.search.substr(1));
+ child.location = url;
+}
+</script>
+</head>
+<body onload="prepChild()">
+
+<iframe src="about:blank">
+</iframe>
+
+<p>InstallTrigger tests</p>
+<p id="return"></p>
+<p id="status"></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/multipackage.xpi b/toolkit/mozapps/extensions/test/xpinstall/multipackage.xpi
new file mode 100644
index 000000000..11fbe1861
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/multipackage.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/navigate.html b/toolkit/mozapps/extensions/test/xpinstall/navigate.html
new file mode 100644
index 000000000..5a6903eb9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/navigate.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept some url as the uri query and navigate to it by
+ clicking a link -->
+
+<head>
+<title>Navigation tests</title>
+<script type="text/javascript">
+function navigate() {
+ // Pass our parameters over to the child
+ var child = window.frames[0];
+ var url = decodeURIComponent(document.location.search.substr(1));
+ var link = document.getElementById("link");
+ link.href = url;
+ link.click();
+}
+</script>
+</head>
+<body onload="navigate()">
+
+<p><a id="link">Test Link</a></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs b/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs
new file mode 100644
index 000000000..d248bfbc7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs
@@ -0,0 +1,45 @@
+// Script has two modes based on the query string. If the mode is "setup" then
+// parameters from the query string configure the redirection. If the mode is
+// "redirect" then a redirect is returned
+
+function handleRequest(request, response)
+{
+ let parts = request.queryString.split("&");
+ let settings = {};
+
+ parts.forEach(function(aString) {
+ let [k, v] = aString.split("=");
+ settings[k] = decodeURIComponent(v);
+ })
+
+ if (settings.mode == "setup") {
+ delete settings.mode;
+
+ // Object states must be an nsISupports
+ var state = {
+ settings: settings,
+ QueryInterface: function(aIid) {
+ if (aIid.equals(Components.interfaces.nsISupports))
+ return settings;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ }
+ state.wrappedJSObject = state;
+
+ setObjectState("xpinstall-redirect-settings", state);
+ response.setStatusLine(request.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/plain");
+ response.write("Setup complete");
+ }
+ else if (settings.mode == "redirect") {
+ getObjectState("xpinstall-redirect-settings", function(aObject) {
+ settings = aObject.wrappedJSObject.settings;
+ });
+
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ for (var name in settings) {
+ response.setHeader(name, settings[name]);
+ }
+ response.write("Done");
+ }
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi b/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
new file mode 100644
index 000000000..973bc00cb
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/signed-no-cn.xpi b/toolkit/mozapps/extensions/test/xpinstall/signed-no-cn.xpi
new file mode 100644
index 000000000..90d3a3ce6
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/signed-no-cn.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/signed-no-o.xpi b/toolkit/mozapps/extensions/test/xpinstall/signed-no-o.xpi
new file mode 100644
index 000000000..19b754038
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/signed-no-o.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/signed-tampered.xpi b/toolkit/mozapps/extensions/test/xpinstall/signed-tampered.xpi
new file mode 100644
index 000000000..8c951881e
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/signed-tampered.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/signed-untrusted.xpi b/toolkit/mozapps/extensions/test/xpinstall/signed-untrusted.xpi
new file mode 100644
index 000000000..09789d189
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/signed-untrusted.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/signed.xpi b/toolkit/mozapps/extensions/test/xpinstall/signed.xpi
new file mode 100644
index 000000000..bd7f78b7c
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/signed.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/signed2.xpi b/toolkit/mozapps/extensions/test/xpinstall/signed2.xpi
new file mode 100644
index 000000000..085efbbf7
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/signed2.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs b/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs
new file mode 100644
index 000000000..5f767a8f4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs
@@ -0,0 +1,101 @@
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const RELATIVE_PATH = "browser/toolkit/mozapps/extensions/test/xpinstall"
+const NOTIFICATION_TOPIC = "slowinstall-complete";
+
+/**
+ * Helper function to create a JS object representing the url parameters from
+ * the request's queryString.
+ *
+ * @param aQueryString
+ * The request's query string.
+ * @return A JS object representing the url parameters from the request's
+ * queryString.
+ */
+function parseQueryString(aQueryString) {
+ var paramArray = aQueryString.split("&");
+ var regex = /^([^=]+)=(.*)$/;
+ var params = {};
+ for (var i = 0, sz = paramArray.length; i < sz; i++) {
+ var match = regex.exec(paramArray[i]);
+ if (!match)
+ throw "Bad parameter in queryString! '" + paramArray[i] + "'";
+ params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
+ }
+
+ return params;
+}
+
+function handleRequest(aRequest, aResponse) {
+ let id = +getState("ID");
+ setState("ID", "" + (id + 1));
+
+ function LOG(str) {
+ dump("slowinstall.sjs[" + id + "]: " + str + "\n");
+ }
+
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+
+ var params = { };
+ if (aRequest.queryString)
+ params = parseQueryString(aRequest.queryString);
+
+ if (params.file) {
+ let xpiFile = "";
+
+ function complete_download() {
+ LOG("Completing download");
+ downloadPaused = false;
+
+ try {
+ // Doesn't seem to be a sane way to read using OS.File and write to an
+ // nsIOutputStream so here we are.
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(xpiFile);
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ stream.init(file, -1, -1, stream.DEFER_OPEN + stream.CLOSE_ON_EOF);
+
+ NetUtil.asyncCopy(stream, aResponse.bodyOutputStream, () => {
+ LOG("Download complete");
+ aResponse.finish();
+ });
+ }
+ catch (e) {
+ LOG("Exception " + e);
+ }
+ }
+
+ let waitForComplete = new Promise(resolve => {
+ function complete() {
+ Services.obs.removeObserver(complete, NOTIFICATION_TOPIC);
+ resolve();
+ }
+
+ Services.obs.addObserver(complete, NOTIFICATION_TOPIC, false);
+ });
+
+ aResponse.processAsync();
+
+ OS.File.getCurrentDirectory().then(dir => {
+ xpiFile = OS.Path.join(dir, ...RELATIVE_PATH.split("/"), params.file);
+ LOG("Starting slow download of " + xpiFile);
+
+ OS.File.stat(xpiFile).then(info => {
+ aResponse.setHeader("Content-Type", "binary/octet-stream");
+ aResponse.setHeader("Content-Length", info.size.toString());
+
+ LOG("Download paused");
+ waitForComplete.then(complete_download);
+ });
+ });
+ }
+ else if (params.continue) {
+ dump("slowinstall.sjs: Received signal to complete all current downloads.\n");
+ Services.obs.notifyObservers(null, NOTIFICATION_TOPIC, null);
+ }
+}
diff --git a/toolkit/mozapps/extensions/test/xpinstall/startsoftwareupdate.html b/toolkit/mozapps/extensions/test/xpinstall/startsoftwareupdate.html
new file mode 100644
index 000000000..4845a504a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/startsoftwareupdate.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept a url as the uri query and pass it to InstallTrigger.startSoftwareUpdate -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function startInstall() {
+ InstallTrigger.startSoftwareUpdate(decodeURIComponent(document.location.search.substring(1)));
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/theme.xpi b/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
new file mode 100644
index 000000000..0c94a280b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
Binary files differ
diff --git a/toolkit/mozapps/extensions/test/xpinstall/triggerredirect.html b/toolkit/mozapps/extensions/test/xpinstall/triggerredirect.html
new file mode 100644
index 000000000..49fda7dc8
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/triggerredirect.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will attempt an install and then try to load a new page in the tab -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function installCallback(url, status) {
+ document.location = "#foo";
+
+ dump("Sending InstallComplete\n");
+ var event = new CustomEvent("InstallComplete");
+ window.dispatchEvent(event);
+}
+
+function startInstall() {
+ InstallTrigger.install({
+ "Unsigned XPI": {
+ URL: "unsigned.xpi",
+ IconURL: "icon.png",
+ toString: function() { return this.URL; }
+ }
+ }, installCallback);
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+<p id="return"></p>
+<p id="status"></p>
+</body>
+</html>
diff --git a/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi b/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi
new file mode 100644
index 000000000..51b00475a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi
Binary files differ
diff --git a/toolkit/mozapps/installer/package-name.mk b/toolkit/mozapps/installer/package-name.mk
index b1ac9a588..a2caff914 100644
--- a/toolkit/mozapps/installer/package-name.mk
+++ b/toolkit/mozapps/installer/package-name.mk
@@ -12,8 +12,12 @@ ifndef PACKAGE_NAME_MK_INCLUDED
PACKAGE_NAME_MK_INCLUDED := 1
ifndef MOZ_PKG_VERSION
+ifdef MC_BASILISK
+MOZ_PKG_VERSION = $(BUILDID)
+else
MOZ_PKG_VERSION = $(MOZ_APP_VERSION)
endif
+endif
ifndef MOZ_PKG_PLATFORM
MOZ_PKG_PLATFORM := $(TARGET_OS)-$(TARGET_CPU)
@@ -61,17 +65,17 @@ endif
ifdef MOZ_SIMPLE_PACKAGE_NAME
PKG_BASENAME := $(MOZ_SIMPLE_PACKAGE_NAME)
else
-PKG_BASENAME = $(MOZ_PKG_APPNAME)-$(MOZ_PKG_VERSION).$(AB_CD).$(MOZ_PKG_PLATFORM)
+PKG_BASENAME = $(MOZ_PKG_APPNAME)-$(MOZ_PKG_VERSION).$(MOZ_PKG_PLATFORM)
endif
PKG_PATH =
SDK_PATH =
PKG_INST_BASENAME = $(PKG_BASENAME).installer
PKG_STUB_BASENAME = $(PKG_BASENAME).installer-stub
-PKG_INST_PATH = install/sea/
+PKG_INST_PATH = $(PKG_PATH)
PKG_UPDATE_BASENAME = $(PKG_BASENAME)
CHECKSUMS_FILE_BASENAME = $(PKG_BASENAME)
MOZ_INFO_BASENAME = $(PKG_BASENAME)
-PKG_UPDATE_PATH = update/
+PKG_UPDATE_PATH = $(PKG_PATH)
COMPLETE_MAR = $(PKG_UPDATE_PATH)$(PKG_UPDATE_BASENAME).complete.mar
# PARTIAL_MAR needs to be processed by $(wildcard) before you use it.
PARTIAL_MAR = $(PKG_UPDATE_PATH)$(PKG_UPDATE_BASENAME).partial.*.mar
@@ -132,9 +136,6 @@ SYMBOL_ARCHIVE_BASENAME = $(PKG_BASENAME).crashreporter-symbols
# Code coverage package naming
CODE_COVERAGE_ARCHIVE_BASENAME = $(PKG_BASENAME).code-coverage-gcno
-# Mozharness naming
-MOZHARNESS_PACKAGE = mozharness.zip
-
# Test package naming
TEST_PACKAGE = $(PKG_BASENAME).common.tests.zip
CPP_TEST_PACKAGE = $(PKG_BASENAME).cppunittest.tests.zip
@@ -153,8 +154,6 @@ endif
MOZ_SOURCESTAMP_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).txt
MOZ_BUILDINFO_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).json
-MOZ_BUILDID_INFO_TXT_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME)_info.txt
-MOZ_MOZINFO_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).mozinfo.json
MOZ_TEST_PACKAGES_FILE = $(DIST)/$(PKG_PATH)/$(PKG_BASENAME).test_packages.json
# JavaScript Shell
diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk
index 80e87a1ec..68247e7df 100644
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -55,12 +55,6 @@ stage-package: $(MOZ_PKG_MANIFEST) $(MOZ_PKG_MANIFEST_DEPS)
$(MOZ_PKG_MANIFEST) $(DIST) $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)$(if $(MOZ_PKG_MANIFEST),,$(_BINPATH)) \
$(if $(filter omni,$(MOZ_PACKAGER_FORMAT)),$(if $(NON_OMNIJAR_FILES),--non-resource $(NON_OMNIJAR_FILES)))
$(PYTHON) $(MOZILLA_DIR)/toolkit/mozapps/installer/find-dupes.py $(DEFINES) $(ACDEFINES) $(MOZ_PKG_DUPEFLAGS) $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)
-ifndef MOZ_THUNDERBIRD
- # Package mozharness
- $(call py_action,test_archive, \
- mozharness \
- $(ABS_DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE))
-endif # MOZ_THUNDERBIRD
ifdef MOZ_PACKAGE_JSSHELL
# Package JavaScript Shell
@echo 'Packaging JavaScript Shell...'
@@ -89,7 +83,7 @@ endif # Darwin
prepare-package: stage-package
-make-package-internal: prepare-package make-sourcestamp-file make-buildinfo-file make-mozinfo-file
+make-package-internal: prepare-package make-sourcestamp-file make-buildinfo-file
@echo 'Compressing...'
cd $(DIST) && $(MAKE_PACKAGE)
@@ -114,11 +108,6 @@ make-buildinfo-file:
$(addprefix MOZ_SOURCE_REPO=,MOZ_SOURCE_REPO=$(shell awk '$$2 == "MOZ_SOURCE_REPO" {print $$3}' $(DEPTH)/source-repo.h)) \
MOZ_SOURCE_STAMP=$(shell awk '$$2 == "MOZ_SOURCE_STAMP" {print $$3}' $(DEPTH)/source-repo.h) \
MOZ_PKG_PLATFORM=$(MOZ_PKG_PLATFORM)
- echo "buildID=$(BUILDID)" > $(MOZ_BUILDID_INFO_TXT_FILE)
-
-.PHONY: make-mozinfo-file
-make-mozinfo-file:
- cp $(DEPTH)/mozinfo.json $(MOZ_MOZINFO_FILE)
# The install target will install the application to prefix/lib/appname-version
# In addition if INSTALL_SDK is set, it will install the development headers,
diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk
index 516331782..9abfd855a 100644
--- a/toolkit/mozapps/installer/upload-files.mk
+++ b/toolkit/mozapps/installer/upload-files.mk
@@ -444,7 +444,6 @@ UPLOAD_FILES= \
$(call QUOTED_WILDCARD,$(DIST)/$(COMPLETE_MAR)) \
$(call QUOTED_WILDCARD,$(DIST)/$(LANGPACK)) \
$(call QUOTED_WILDCARD,$(wildcard $(DIST)/$(PARTIAL_MAR))) \
- $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE)) \
$(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(TEST_PACKAGE)) \
$(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(CPP_TEST_PACKAGE)) \
$(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(XPC_TEST_PACKAGE)) \
@@ -458,8 +457,6 @@ UPLOAD_FILES= \
$(call QUOTED_WILDCARD,$(DIST)/$(SDK).asc) \
$(call QUOTED_WILDCARD,$(MOZ_SOURCESTAMP_FILE)) \
$(call QUOTED_WILDCARD,$(MOZ_BUILDINFO_FILE)) \
- $(call QUOTED_WILDCARD,$(MOZ_BUILDID_INFO_TXT_FILE)) \
- $(call QUOTED_WILDCARD,$(MOZ_MOZINFO_FILE)) \
$(call QUOTED_WILDCARD,$(MOZ_TEST_PACKAGES_FILE)) \
$(call QUOTED_WILDCARD,$(PKG_JSSHELL)) \
$(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip) \
@@ -490,10 +487,6 @@ ifdef MOZ_SIGN_CMD
UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PACKAGE).asc)
endif
-ifdef MOZ_STUB_INSTALLER
- UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe)
-endif
-
ifndef MOZ_PKG_SRCDIR
MOZ_PKG_SRCDIR = $(topsrcdir)
endif
diff --git a/toolkit/mozapps/installer/windows/nsis/common.nsh b/toolkit/mozapps/installer/windows/nsis/common.nsh
index ac7607449..846718dab 100755
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/common.nsh
@@ -5107,46 +5107,15 @@
Quit
${EndIf}
- !ifdef HAVE_64BIT_BUILD
- ${Unless} ${RunningX64}
- ${OrUnless} ${AtLeastWin7}
- MessageBox MB_OK|MB_ICONSTOP "$R9"
- ; Nothing initialized so no need to call OnEndCommon
- Quit
- ${EndUnless}
+ ; Windows NT 6.0 (Vista/Server 2008) and lower are not supported.
+ ${Unless} ${AtLeastWin7}
+ MessageBox MB_OK|MB_ICONSTOP "$R9"
+ ; Nothing initialized so no need to call OnEndCommon
+ Quit
+ ${EndUnless}
+ !ifdef HAVE_64BIT_BUILD
SetRegView 64
- !else
- StrCpy $R8 "0"
- ${If} ${AtMostWin2000}
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} ${IsWinXP}
- ${AndIf} ${AtMostServicePack} 1
- StrCpy $R8 "1"
- ${EndIf}
-
- ${If} $R8 == "1"
- ; XXX-rstrong - some systems failed the AtLeastWin2000 test that we
- ; used to use for an unknown reason and likely fail the AtMostWin2000
- ; and possibly the IsWinXP test as well. To work around this also
- ; check if the Windows NT registry Key exists and if it does if the
- ; first char in CurrentVersion is equal to 3 (Windows NT 3.5 and
- ; 3.5.1), to 4 (Windows NT 4) or 5 (Windows 2000 and Windows XP).
- StrCpy $R8 ""
- ClearErrors
- ReadRegStr $R8 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
- StrCpy $R8 "$R8" 1
- ${If} ${Errors}
- ${OrIf} "$R8" == "3"
- ${OrIf} "$R8" == "4"
- ${OrIf} "$R8" == "5"
- MessageBox MB_OK|MB_ICONSTOP "$R9"
- ; Nothing initialized so no need to call OnEndCommon
- Quit
- ${EndIf}
- ${EndUnless}
!endif
${GetParameters} $R8
diff --git a/toolkit/mozapps/installer/windows/nsis/makensis.mk b/toolkit/mozapps/installer/windows/nsis/makensis.mk
index 97608e0ce..aff6d29e9 100644
--- a/toolkit/mozapps/installer/windows/nsis/makensis.mk
+++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk
@@ -45,18 +45,6 @@ $(CONFIG_DIR)/setup.exe::
$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/,$(CUSTOM_UI)) $(CONFIG_DIR)
cd $(CONFIG_DIR) && $(MAKENSISU) installer.nsi
-ifdef MOZ_STUB_INSTALLER
- cd $(CONFIG_DIR) && $(MAKENSISU) stub.nsi
-ifdef MOZ_EXTERNAL_SIGNING_FORMAT
- $(MOZ_SIGN_CMD) $(foreach f,$(MOZ_EXTERNAL_SIGNING_FORMAT),-f $(f)) $(CONFIG_DIR)/setup-stub.exe
-endif
- $(MAKE) $(CONFIG_DIR)/7zSD.sfx
- cd $(CONFIG_DIR) && $(CYGWIN_WRAPPER) 7z a -t7z $(ABS_CONFIG_DIR)/stub.7z setup-stub.exe -mx -m0=BCJ2 -m1=LZMA:d21 -m2=LZMA:d17 -m3=LZMA:d17 -mb0:1 -mb0s1:2 -mb0s2:3
- cat $(CONFIG_DIR)/7zSD.sfx $(CONFIG_DIR)/stub.tag $(CONFIG_DIR)/stub.7z > "$(CONFIG_DIR)/stub.exe"
-ifdef MOZ_EXTERNAL_SIGNING_FORMAT_STUB
- $(MOZ_SIGN_CMD) $(foreach f,$(MOZ_EXTERNAL_SIGNING_FORMAT_STUB),-f $(f)) $(CONFIG_DIR)/stub.exe
-endif
-endif
# Support for building the uninstaller when repackaging locales
ifeq ($(CONFIG_DIR),l10ngen)
cd $(CONFIG_DIR) && $(MAKENSISU) uninstaller.nsi
@@ -75,10 +63,6 @@ installer::
$(NSINSTALL) -D $(DIST)/$(PKG_INST_PATH)
cat $(CONFIG_DIR)/7zSD.sfx $(CONFIG_DIR)/app.tag $(CONFIG_DIR)/app.7z > "$(DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe"
chmod 0755 "$(DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe"
-ifdef MOZ_STUB_INSTALLER
- cp $(CONFIG_DIR)/stub.exe "$(DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe"
- chmod 0755 "$(DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe"
-endif
ifdef MOZ_EXTERNAL_SIGNING_FORMAT
$(MOZ_SIGN_CMD) $(foreach f,$(MOZ_EXTERNAL_SIGNING_FORMAT),-f $(f)) "$(DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe"
endif
diff --git a/toolkit/mozapps/update/content/history.js b/toolkit/mozapps/update/content/history.js
index 32a098de5..c5bbccefc 100644
--- a/toolkit/mozapps/update/content/history.js
+++ b/toolkit/mozapps/update/content/history.js
@@ -37,7 +37,7 @@ var gUpdateHistory = {
var element = document.createElementNS(NS_XUL, "update");
this._view.appendChild(element);
element.name = bundle.getFormattedString("updateFullName",
- [update.name, update.buildID]);
+ [update.name, (update.buildID ? update.buildID : "-")]);
element.type = bundle.getString("updateType_" + update.type);
element.installDate = this._formatDate(update.installDate);
if (update.detailsURL)
diff --git a/toolkit/mozapps/update/updater/updater.exe.comctl32.manifest b/toolkit/mozapps/update/updater/updater.exe.comctl32.manifest
index 9a6cdb565..6bb850199 100644
--- a/toolkit/mozapps/update/updater/updater.exe.comctl32.manifest
+++ b/toolkit/mozapps/update/updater/updater.exe.comctl32.manifest
@@ -32,7 +32,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/toolkit/mozapps/update/updater/updater.exe.manifest b/toolkit/mozapps/update/updater/updater.exe.manifest
index cd229c954..619b4abfe 100644
--- a/toolkit/mozapps/update/updater/updater.exe.manifest
+++ b/toolkit/mozapps/update/updater/updater.exe.manifest
@@ -20,7 +20,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
diff --git a/toolkit/mozapps/webextensions/content/extensions.js b/toolkit/mozapps/webextensions/content/extensions.js
index 56158d9c6..5e428fe17 100644
--- a/toolkit/mozapps/webextensions/content/extensions.js
+++ b/toolkit/mozapps/webextensions/content/extensions.js
@@ -2744,12 +2744,8 @@ var gListView = {
document.getElementById("signing-dev-info").hidden = true;
}
- if (Preferences.get("plugin.load_flash_only", true)) {
- document.getElementById("plugindeprecation-learnmore-link")
- .setAttribute("href", Services.urlFormatter.formatURLPref("app.support.baseURL") + "npapi");
- } else {
- document.getElementById("plugindeprecation-notice").hidden = true;
- }
+ // To-Do: remove deprecation notice content.
+ document.getElementById("plugindeprecation-notice").hidden = true;
},
show: function(aType, aRequest) {
diff --git a/toolkit/mozapps/webextensions/jar.mn b/toolkit/mozapps/webextensions/jar.mn
index ecf64b7ef..77cbf32eb 100644
--- a/toolkit/mozapps/webextensions/jar.mn
+++ b/toolkit/mozapps/webextensions/jar.mn
@@ -7,7 +7,7 @@ toolkit.jar:
% content mozapps %content/mozapps/
* content/mozapps/extensions/extensions.xul (content/extensions.xul)
content/mozapps/extensions/extensions.css (content/extensions.css)
- content/mozapps/extensions/extensions.js (content/extensions.js)
+* content/mozapps/extensions/extensions.js (content/extensions.js)
* content/mozapps/extensions/extensions.xml (content/extensions.xml)
content/mozapps/extensions/updateinfo.xsl (content/updateinfo.xsl)
content/mozapps/extensions/about.xul (content/about.xul)
diff --git a/toolkit/mozapps/webextensions/test/browser/browser-window.ini b/toolkit/mozapps/webextensions/test/browser/browser-window.ini
index fcda90fc6..ca9353d21 100644
--- a/toolkit/mozapps/webextensions/test/browser/browser-window.ini
+++ b/toolkit/mozapps/webextensions/test/browser/browser-window.ini
@@ -41,12 +41,12 @@ support-files =
webapi_checkchromeframe.xul
webapi_checkframed.html
webapi_checknavigatedwindow.html
- !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
- !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/corrupt.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/incompatible.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/installtrigger.html
+ !/toolkit/mozapps/webextensions/test/xpinstall/restartless.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/theme.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/unsigned.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/amosigned.xpi
[include:browser-common.ini]
diff --git a/toolkit/mozapps/webextensions/test/browser/browser.ini b/toolkit/mozapps/webextensions/test/browser/browser.ini
index a23841d33..8a144baba 100644
--- a/toolkit/mozapps/webextensions/test/browser/browser.ini
+++ b/toolkit/mozapps/webextensions/test/browser/browser.ini
@@ -41,13 +41,13 @@ support-files =
webapi_checkchromeframe.xul
webapi_checkframed.html
webapi_checknavigatedwindow.html
- !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
- !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi
- !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/corrupt.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/incompatible.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/installtrigger.html
+ !/toolkit/mozapps/webextensions/test/xpinstall/restartless.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/theme.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/unsigned.xpi
+ !/toolkit/mozapps/webextensions/test/xpinstall/amosigned.xpi
[browser_addonrepository_performance.js]
[browser_bug557956.js]
diff --git a/toolkit/themes/linux/mozapps/extensions/category-available.png b/toolkit/themes/linux/mozapps/extensions/category-available.png
new file mode 100644
index 000000000..689d526c9
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-available.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-dictionaries.png b/toolkit/themes/linux/mozapps/extensions/category-dictionaries.png
new file mode 100644
index 000000000..a1e0d5359
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-dictionaries.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-discover.png b/toolkit/themes/linux/mozapps/extensions/category-discover.png
new file mode 100644
index 000000000..ccea27524
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-discover.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-experiments.png b/toolkit/themes/linux/mozapps/extensions/category-experiments.png
new file mode 100644
index 000000000..a9d00545e
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-experiments.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-plugins.png b/toolkit/themes/linux/mozapps/extensions/category-plugins.png
new file mode 100644
index 000000000..b253dd08f
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-plugins.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-recent.png b/toolkit/themes/linux/mozapps/extensions/category-recent.png
new file mode 100644
index 000000000..9039b27aa
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-recent.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-search.png b/toolkit/themes/linux/mozapps/extensions/category-search.png
new file mode 100644
index 000000000..52e91a7ce
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-search.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/category-service.png b/toolkit/themes/linux/mozapps/extensions/category-service.png
new file mode 100644
index 000000000..997c8541c
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/category-service.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/dictionaryGeneric-16.png b/toolkit/themes/linux/mozapps/extensions/dictionaryGeneric-16.png
new file mode 100644
index 000000000..08a0447a4
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/dictionaryGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/dictionaryGeneric.png b/toolkit/themes/linux/mozapps/extensions/dictionaryGeneric.png
new file mode 100644
index 000000000..a1e0d5359
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/dictionaryGeneric.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/experimentGeneric.png b/toolkit/themes/linux/mozapps/extensions/experimentGeneric.png
new file mode 100644
index 000000000..a9d00545e
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/experimentGeneric.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/extensionGeneric-16.png b/toolkit/themes/linux/mozapps/extensions/extensionGeneric-16.png
new file mode 100644
index 000000000..b1a2f3652
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/extensionGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/extensionGeneric.png b/toolkit/themes/linux/mozapps/extensions/extensionGeneric.png
new file mode 100644
index 000000000..2ae95a5b2
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/extensionGeneric.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/extensions.css b/toolkit/themes/linux/mozapps/extensions/extensions.css
new file mode 100644
index 000000000..5c642fbbf
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/extensions.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://global/skin/inContentUI.css");
+
+
+/*** global warnings ***/
+
+.global-warning-container {
+ overflow-x: hidden;
+}
+
+.global-warning {
+ -moz-box-align: center;
+ padding: 0 8px;
+ font-weight: bold;
+}
+
+.global-warning-text {
+ color: -moz-FieldText;
+}
+
+#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 ThreeDShadow;
+ min-height: 41px;
+}
+
+@media (max-width: 600px) {
+ .global-warning-text {
+ display: none;
+ }
+}
+
+/* Plugins aren't yet disabled by safemode (bug 342333),
+ so don't show that warning when viewing plugins. */
+#addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
+#addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning-container {
+ background-color: inherit;
+ background-image: none;
+}
+
+
+/*** notification icons ***/
+
+.warning-icon {
+ list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=menu");
+ width: 16px;
+ height: 16px;
+ margin: 3px 0;
+}
+
+.error-icon {
+ list-style-image: url("moz-icon://stock/gtk-dialog-error?size=menu");
+ width: 16px;
+ height: 16px;
+ margin: 3px 0;
+}
+
+.pending-icon,
+.info-icon {
+ list-style-image: url("moz-icon://stock/gtk-dialog-info?size=menu");
+ width: 16px;
+ height: 16px;
+ 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;
+ font-size: 12px;
+ border: 1px solid ThreeDShadow;
+ border-radius: 8px;
+ color: WindowText;
+ background-color: Window;
+ background-clip: padding-box;
+}
+
+.alert .alert-title {
+ font-weight: bold;
+ font-size: 200%;
+ margin-bottom: 15px;
+}
+
+.alert button {
+ margin: 1em 2em;
+}
+
+.loading {
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+/*** category selector ***/
+
+#categories {
+ -moz-appearance: none;
+ border: none;
+ -moz-margin-end: -1px;
+ background-color: transparent;
+ position: relative;
+ margin-top: 41px;
+}
+
+.category {
+ -moz-appearance: none;
+ border-width: 1px;
+ -moz-border-end-width: 0;
+ border-style: solid;
+ border-color: transparent;
+ padding: 10px 4px;
+ -moz-box-align: center;
+ overflow: hidden;
+ min-height: 0;
+ color: WindowText;
+}
+
+.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: -moz-Field;
+ color: -moz-FieldText;
+ border-color: ThreeDShadow;
+}
+
+.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: Highlight;
+ padding: 2px 8px;
+ margin: 6px 0;
+ border-radius: 10000px;
+ color: HighlightText;
+ font-weight: bold;
+ text-align: center;
+}
+
+.category-badge[value="0"] {
+ visibility: hidden;
+}
+
+.category-icon {
+ width: 32px;
+ height: 32px;
+ -moz-margin-start: 6px;
+}
+
+#category-search > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-search.png");
+}
+#category-discover > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-discover.png");
+}
+#category-locale > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-languages.png");
+}
+#category-searchengine > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-searchengines.png");
+}
+#category-extension > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-extensions.png");
+}
+#category-service > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-service.png");
+}
+#category-theme > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-themes.png");
+}
+#category-plugin > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
+}
+#category-dictionary > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-dictionaries.png");
+}
+#category-experiment > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-experiments.png");
+}
+#category-availableUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
+}
+#category-recentUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
+}
+
+
+/*** header ***/
+
+#header {
+ margin-bottom: 18px;
+}
+
+.nav-button {
+ min-width: 0;
+}
+
+#back-btn:-moz-locale-dir(ltr) {
+ list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar");
+}
+
+#forward-btn:-moz-locale-dir(ltr) {
+ list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
+}
+
+#back-btn:-moz-locale-dir(rtl) {
+ list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
+}
+
+#forward-btn:-moz-locale-dir(rtl) {
+ list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
+}
+
+#back-btn[disabled="true"]:-moz-locale-dir(ltr) {
+ list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled");
+}
+
+#forward-btn[disabled="true"]:-moz-locale-dir(ltr) {
+ list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
+}
+
+#back-btn[disabled="true"]:-moz-locale-dir(rtl) {
+ list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
+}
+
+#forward-btn[disabled="true"]:-moz-locale-dir(rtl) {
+ list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
+}
+
+#header-utils-btn {
+ min-width: 4.5em;
+}
+
+#header-utils-btn .toolbarbutton-icon {
+ list-style-image: url("moz-icon://stock/gtk-preferences?size=toolbar");
+}
+
+#header-utils-btn:-moz-focusring > .button-box {
+ border: none;
+}
+
+#header-search {
+ margin: 0;
+}
+
+@media (max-width: 600px) {
+ #header-search {
+ width: 12em;
+ }
+}
+
+.view-header {
+ padding: 4px;
+ margin: 0;
+ min-height: 41px;
+ background-color: ThreeDHighlight;
+ border-bottom: 1px solid ThreeDShadow;
+}
+
+
+/*** sorters ***/
+
+.sort-controls {
+ -moz-appearance: none;
+}
+
+.sorter[checkState="1"] .button-icon {
+ display: -moz-box;
+ list-style-image: url("moz-icon://stock/gtk-sort-descending?size=16");
+}
+
+.sorter[checkState="2"] .button-icon {
+ display: -moz-box;
+ list-style-image: url("moz-icon://stock/gtk-sort-ascending?size=16");
+}
+
+
+/*** 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");
+ -moz-margin-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 {
+ border-bottom: 1px solid ThreeDLightShadow;
+ padding: 5px;
+}
+
+.addon[selected] .text-link,
+.addon[selected] .button-link {
+ color: inherit;
+}
+
+.details {
+ cursor: pointer;
+ margin: 0;
+ -moz-margin-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: grayscale(1);
+}
+
+.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");
+}
+
+.addon-view[type="experiment"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/experimentGeneric.png");
+}
+
+.name-container {
+ font-size: 150%;
+ margin-bottom: 0;
+ font-weight: bold;
+ -moz-box-align: end;
+ -moz-box-flex: 1;
+}
+
+.creator {
+ font-weight: bold;
+}
+
+.addon-view[active="false"]:not([selected]) {
+ color: GrayText;
+}
+
+.description-container {
+ -moz-margin-start: 6px;
+ -moz-box-align: center;
+}
+
+.description {
+ margin: 0;
+}
+
+.warning,
+.pending,
+.error {
+ -moz-margin-start: 48px;
+ -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:not([selected]) .warning {
+ color: #90792E;
+}
+
+.addon-view:not([selected]) .error {
+ color: #7C322B;
+}
+
+.addon-view:not([selected]) .pending {
+ color: #4F7939;
+}
+
+.addon[active="false"] {
+ background-image: linear-gradient(rgba(135, 135, 135, 0.2),
+ rgba(135, 135, 135, 0.1));
+}
+
+.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 .relnotes-container {
+ -moz-box-align: start;
+ 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;
+ cursor: pointer;
+ list-style-image: url("moz-icon://stock/gtk-go-down?size=16");
+}
+
+.addon .relnotes-toggle > .button-box > .button-icon {
+ display: -moz-box;
+}
+
+.addon[show-relnotes] .relnotes-toggle {
+ list-style-image: url("moz-icon://stock/gtk-go-up?size=16");
+}
+
+
+/*** search view ***/
+
+#search-filter {
+ padding: 5px 20px;
+ font-size: 120%;
+ overflow-x: hidden;
+ border-bottom: 1px solid ThreeDShadow;
+}
+
+#search-filter-label {
+ font-weight: bold;
+}
+
+#search-allresults-link {
+ margin-top: 1em;
+ margin-bottom: 2em;
+}
+
+/*** detail view ***/
+
+#detail-view[active="false"] .fade {
+ opacity: 0.6;
+}
+
+#detail-view .loading {
+ opacity: 0;
+}
+
+#detail-view[loading-extended] .loading {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ 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 {
+ -moz-margin-start: 0;
+}
+
+#detail-icon-container {
+ width: 64px;
+ -moz-margin-end: 10px;
+}
+
+#detail-icon {
+ max-width: 64px;
+ max-height: 64px;
+}
+
+#detail-summary {
+ margin-bottom: 2em;
+}
+
+#detail-name-container {
+ font-size: 200%;
+}
+
+#detail-screenshot {
+ -moz-margin-end: 2em;
+ max-width: 300px;
+ max-height: 300px;
+}
+
+#detail-screenshot[loading] {
+ background-image: url("chrome://global/skin/icons/loading_16.png");
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ border: 1px threedshadow solid;
+ border-radius: 5px;
+ box-sizing: border-box;
+}
+
+#detail-screenshot[loading="error"] {
+ background-image: url("chrome://global/skin/media/error.png");
+}
+
+#detail-desc-container {
+ margin-bottom: 2em;
+}
+
+#detail-desc, #detail-fulldesc {
+ -moz-margin-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 ThreeDShadow;
+ margin-bottom: 2em;
+ padding: 1em;
+ background: ThreeDHighlight;
+}
+
+#detail-contrib-description {
+ font-style: italic;
+ margin-bottom: 1em;
+}
+
+#detail-contrib-suggested {
+ color: GrayText;
+}
+
+#detail-grid {
+ margin-bottom: 2em;
+}
+
+#detail-grid > columns > column:first-child {
+ min-width: 15em;
+ max-width: 25em;
+}
+
+.detail-row[first-row="true"],
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
+ border-top: none;
+}
+
+.detail-row,
+.detail-row-complex,
+setting {
+ border-top: 1px solid ThreeDShadow;
+ -moz-box-align: center;
+ min-height: 32px;
+}
+
+#detail-controls {
+ margin-bottom: 1em;
+}
+
+#detail-view[active="false"]:not([pending]):not([notification]) {
+ background-image: linear-gradient(rgba(135, 135, 135, 0.1),
+ rgba(135, 135, 135, 0));
+}
+
+setting[first-row="true"] {
+ margin-top: 2em;
+}
+
+setting {
+ -moz-box-align: start;
+}
+
+.preferences-alignment {
+ min-height: 32px;
+ -moz-box-align: center;
+}
+
+.preferences-description {
+ font-size: 90.9%;
+ color: graytext;
+ margin-top: -2px;
+ -moz-margin-start: 2em;
+ white-space: pre-wrap;
+}
+
+.preferences-description:empty {
+ display: none;
+}
+
+menulist { /* Fixes some styling inconsistencies */
+ font-size: 100%;
+ margin: 1px 5px 2px 5px;
+}
+
+colorpicker[type="button"] { /* Fixes some styling inconsistencies */
+ height: 29px;
+ margin: 1px 5px 2px 5px;
+}
+
+setting[type="radio"] > radiogroup {
+ -moz-box-orient: horizontal;
+}
+
+/*** creator ***/
+
+.creator > label {
+ -moz-margin-start: 0;
+ -moz-margin-end: 0;
+}
+
+.creator > .text-link {
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+
+/*** rating ***/
+
+.meta-rating {
+ -moz-margin-end: 0;
+ vertical-align: text-top;
+}
+
+.meta-rating[showrating="average"] > .star {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-not-won.png");
+ padding: 0 1px;
+}
+
+.meta-rating[showrating="user"] > .star {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-unrated.png");
+ padding: 2px 3px;
+}
+
+.meta-rating > .star[on="true"],
+.meta-rating[showrating="user"] > .star[hover] {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-won.png");
+ padding: 0 1px;
+}
+
+
+/*** download progress ***/
+
+.download-progress {
+ width: 200px;
+}
+
+.download-progress .start-cap,
+.download-progress .end-cap {
+ display: none;
+}
+
+.download-progress .progress {
+ padding: 0;
+ margin: 0;
+ border: none;
+}
+
+.download-progress .cancel {
+ -moz-appearance: none;
+ background-color: ButtonFace;
+ padding-bottom: 1px;
+ -moz-padding-start: 2px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+ border-radius: 10000px;
+ min-width: 16px;
+ width: 16px;
+ height: 16px;
+ margin: 3px;
+}
+
+.download-progress .cancel:hover {
+ background-color: -moz-ButtonHoverFace;
+}
+
+.download-progress .cancel {
+ list-style-image: url('chrome://mozapps/skin/extensions/cancel.png');
+}
+
+.download-progress .status-container {
+ -moz-box-align: center;
+}
+
+
+/*** install status ***/
+
+.install-status {
+ -moz-box-align: center;
+}
+
+
+/*** check for updates ***/
+
+#updates-container {
+ -moz-box-align: center;
+}
+
+#updates-installed,
+#updates-downloaded {
+ font-weight: bold;
+}
+
+#update-selected {
+ margin: 12px;
+}
+
+
+/*** buttons ***/
+
+.addon-control[disabled="true"]:not(.no-auto-hide) {
+ display: none;
+}
+
+.no-auto-hide .addon-control {
+ display: block !important;
+}
+
+.addon-control.enable {
+ list-style-image: url("moz-icon://stock/gtk-yes?size=button");
+}
+
+.addon-control.disable {
+ list-style-image: url("moz-icon://stock/gtk-no?size=button");
+}
+
+.addon-control.remove {
+ list-style-image: url("moz-icon://stock/gtk-remove?size=button");
+}
+
+.addon-control.preferences {
+ list-style-image: url("moz-icon://stock/gtk-preferences?size=button");
+}
+
+.addon-control.install,
+.addon-control.update {
+ list-style-image: url("moz-icon://stock/gtk-save?size=button");
+}
+
+.button-link {
+ -moz-appearance: none;
+ background: transparent;
+ border: none;
+ text-decoration: underline;
+ color: -moz-nativehyperlinktext;
+ cursor: pointer;
+ min-width: 0;
+ margin: 0 6px;
+}
+
+.button-link:active {
+ color: -moz-activehyperlinktext;
+}
+
+.header-button .toolbarbutton-text {
+ display: none;
+}
+
+/*** telemetry experiments ***/
+
+#detail-experiment-container {
+ font-size: 80%;
+ margin-bottom: 1em;
+}
+
+#detail-experiment-bullet-container,
+#detail-experiment-state,
+#detail-experiment-time,
+.experiment-bullet-container,
+.experiment-state,
+.experiment-time {
+ vertical-align: middle;
+ display: inline-block;
+}
+
+.addon .experiment-bullet,
+#detail-experiment-bullet {
+ fill: rgb(158, 158, 158);
+}
+
+.addon[active="true"] .experiment-bullet,
+#detail-view[active="true"] #detail-experiment-bullet {
+ fill: rgb(106, 201, 20);
+}
diff --git a/toolkit/themes/linux/mozapps/extensions/localeGeneric.png b/toolkit/themes/linux/mozapps/extensions/localeGeneric.png
new file mode 100644
index 000000000..c72115906
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/localeGeneric.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/newaddon.css b/toolkit/themes/linux/mozapps/extensions/newaddon.css
new file mode 100644
index 000000000..2e5e25210
--- /dev/null
+++ b/toolkit/themes/linux/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/. */
+
+@import url("chrome://global/skin/inContentUI.css");
+
+#addon-page {
+ 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 {
+ overflow: visible;
+ max-width: 600px;
+ margin: 20px;
+ padding: 30px 90px;
+}
+
+#addon-info {
+ -moz-box-align: start;
+ margin: 25px 10px;
+}
+
+#icon {
+ -moz-margin-end: 10px;
+ max-width: 64px;
+ max-height: 64px;
+ 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 {
+ color: GrayText;
+}
+
+#location {
+ color: GrayText;
+}
+
+#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;
+ -moz-margin-end: 5px;
+}
+
+#allow {
+ -moz-margin-start: 84px;
+ margin-bottom: 20px;
+}
+
+#continuePanel,
+#restartPanel {
+ margin-top: 25px;
+ -moz-box-pack: end;
+ -moz-box-align: end;
+}
+
+#continuePanel {
+ -moz-box-pack: end;
+}
+
+#restartMessage {
+ text-align: right;
+}
+
+#restartSpacer {
+ -moz-box-flex: 1;
+}
+
+#later {
+ color: GrayText;
+}
diff --git a/toolkit/themes/linux/mozapps/extensions/selectAddons.css b/toolkit/themes/linux/mozapps/extensions/selectAddons.css
new file mode 100644
index 000000000..8a94a6c18
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/selectAddons.css
@@ -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/. */
+
+#view-deck {
+ background: Window;
+}
+
+.heading {
+ font-size: 270%;
+ text-align: center;
+ margin: 0 100px;
+}
+
+.progress {
+ margin: 10px 128px;
+}
+
+.progress-label,
+#errors-description {
+ text-align: center;
+ margin: 0 10px;
+}
+
+#checking-heading,
+#update-heading,
+#errors-heading {
+ margin-top: 90px;
+}
+
+#select-heading,
+#confirm-heading {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+#select-description,
+#confirm-description {
+ margin: 10px;
+}
+
+#select-list {
+ border-top: 1px solid WindowFrame;
+ background-color: Window;
+}
+
+#select-grid column {
+ -moz-box-align: center;
+}
+
+#select-grid row {
+ -moz-box-align: stretch;
+}
+
+#select-grid row:nth-of-type(odd) {
+ background-color: -moz-oddtreerow;
+}
+
+#select-grid label,
+#select-grid checkbox {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.select-cell {
+ -moz-box-align: center;
+ -moz-box-pack: start;
+}
+
+#select-header .select-cell {
+ -moz-appearance: treeheadercell;
+ box-sizing: border-box;
+}
+
+.select-keep {
+ -moz-box-pack: center;
+}
+
+.select-keep .checkbox-label-box {
+ display: none;
+}
+
+.select-keep .addon-keep-checkbox:-moz-focusring {
+ outline: 1px dotted ThreeDDarkShadow;
+}
+
+.select-icon {
+ width: 20px;
+}
+
+#select-grid separator {
+ display: none;
+}
+
+.addon-name,
+.addon-action-message,
+.addon-action-update {
+ box-sizing: border-box;
+ margin: 0;
+ padding-top: 1px;
+ padding-bottom: 2px;
+ -moz-padding-start: 6px;
+ -moz-padding-end: 5px;
+}
+
+.addon:not([active]) .addon-name,
+.addon:not([active]) .addon-action-message,
+.addon:not([active]) .addon-action-update {
+ color: GrayText;
+}
+
+.addon-icon {
+ height: 16px;
+ width: 16px;
+ margin: 2px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.addon-icon[type="theme"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric-16.png");
+}
+
+.addon-icon[type="plugin"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.addon-icon[type="dictionary"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric-16.png");
+}
+
+.action-list {
+ margin-top: 10px;
+ -moz-margin-start: 5em;
+}
+
+.action-header {
+ margin-bottom: 10px;
+}
+
+#confirm .addon {
+ -moz-margin-start: 3em;
+ -moz-box-align: center;
+}
+
+.addon:not([active]) .addon-icon,
+#disable-list .addon-icon,
+#incompatible-list .addon-icon {
+ filter: grayscale(1);
+}
+
+#footer {
+ padding: 15px 12px;
+ border-top: 2px solid;
+ -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow;
+}
+
+.progress-label,
+#footer-label {
+ font-style: italic;
+ color: GrayText;
+}
diff --git a/toolkit/themes/linux/mozapps/extensions/themeGeneric-16.png b/toolkit/themes/linux/mozapps/extensions/themeGeneric-16.png
new file mode 100644
index 000000000..019886fea
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/themeGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/extensions/themeGeneric.png b/toolkit/themes/linux/mozapps/extensions/themeGeneric.png
new file mode 100644
index 000000000..cde1c7834
--- /dev/null
+++ b/toolkit/themes/linux/mozapps/extensions/themeGeneric.png
Binary files differ
diff --git a/toolkit/themes/linux/mozapps/jar.mn b/toolkit/themes/linux/mozapps/jar.mn
index 0965662bd..d4997d36c 100644
--- a/toolkit/themes/linux/mozapps/jar.mn
+++ b/toolkit/themes/linux/mozapps/jar.mn
@@ -22,6 +22,30 @@ toolkit.jar:
skin/classic/mozapps/extensions/localeGeneric.png (webextensions/localeGeneric.png)
* skin/classic/mozapps/extensions/newaddon.css (webextensions/newaddon.css)
skin/classic/mozapps/extensions/heart.png (webextensions/heart.png)
+#else
++ skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
++ skin/classic/mozapps/extensions/category-search.png (extensions/category-search.png)
++ skin/classic/mozapps/extensions/category-discover.png (extensions/category-discover.png)
++ skin/classic/mozapps/extensions/category-languages.png (extensions/localeGeneric.png)
++ skin/classic/mozapps/extensions/category-extensions.png (extensions/extensionGeneric.png)
++ skin/classic/mozapps/extensions/category-themes.png (extensions/themeGeneric.png)
++ skin/classic/mozapps/extensions/category-plugins.png (extensions/category-plugins.png)
++ skin/classic/mozapps/extensions/category-service.png (extensions/category-service.png)
++ skin/classic/mozapps/extensions/category-dictionaries.png (extensions/category-dictionaries.png)
++ skin/classic/mozapps/extensions/category-experiments.png (extensions/category-experiments.png)
++ skin/classic/mozapps/extensions/category-recent.png (extensions/category-recent.png)
++ skin/classic/mozapps/extensions/category-available.png (extensions/category-available.png)
++ skin/classic/mozapps/extensions/extensionGeneric.png (extensions/extensionGeneric.png)
++ skin/classic/mozapps/extensions/extensionGeneric-16.png (extensions/extensionGeneric-16.png)
++ skin/classic/mozapps/extensions/dictionaryGeneric.png (extensions/dictionaryGeneric.png)
++ skin/classic/mozapps/extensions/dictionaryGeneric-16.png (extensions/dictionaryGeneric-16.png)
++ skin/classic/mozapps/extensions/experimentGeneric.png (extensions/experimentGeneric.png)
++ skin/classic/mozapps/extensions/themeGeneric.png (extensions/themeGeneric.png)
++ skin/classic/mozapps/extensions/themeGeneric-16.png (extensions/themeGeneric-16.png)
++ skin/classic/mozapps/extensions/localeGeneric.png (extensions/localeGeneric.png)
++ skin/classic/mozapps/extensions/newaddon.css (extensions/newaddon.css)
++ skin/classic/mozapps/extensions/selectAddons.css (extensions/selectAddons.css)
++ skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png (extensions/extensionGeneric.png)
#endif
skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png)
skin/classic/mozapps/plugins/pluginBlocked.png (plugins/pluginBlocked.png)
diff --git a/toolkit/themes/osx/mozapps/extensions/about.css b/toolkit/themes/osx/mozapps/extensions/about.css
new file mode 100644
index 000000000..cfabd47db
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/about.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/. */
+
+#genericAbout {
+ padding: 0px;
+ min-height: 200px;
+ max-height: 400px;
+ width: 30em;
+}
+
+#clientBox {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.basic-info {
+ padding: 10px;
+}
+
+#extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 64px;
+ max-height: 64px;
+ -moz-margin-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 {
+ -moz-margin-start: 18px;
+}
+
+#extensionCreator, .contributor {
+ margin: 0px;
+}
+
+.sectionTitle {
+ padding: 2px 0px 3px 0px;
+ margin-top: 3px;
+ font-weight: bold;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-error.png b/toolkit/themes/osx/mozapps/extensions/alerticon-error.png
new file mode 100644
index 000000000..8740e4911
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-error.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png b/toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png
new file mode 100644
index 000000000..2c5f628ab
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png b/toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png
new file mode 100644
index 000000000..a186c6b7a
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-warning.png b/toolkit/themes/osx/mozapps/extensions/alerticon-warning.png
new file mode 100644
index 000000000..75ea826f9
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-warning.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/blocklist.css b/toolkit/themes/osx/mozapps/extensions/blocklist.css
new file mode 100644
index 000000000..b241c9446
--- /dev/null
+++ b/toolkit/themes/osx/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;
+ -moz-padding-start: 7px;
+ -moz-padding-end: 7px;
+ border-bottom: 1px solid #C0C0C0;
+}
+
+.addon-name-version {
+ font-size: 110%;
+}
+
+.blockedLabel {
+ font-weight: bold;
+ font-style: italic;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/cancel.png b/toolkit/themes/osx/mozapps/extensions/cancel.png
new file mode 100644
index 000000000..0d98ab235
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/cancel.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/category-available-XP.png b/toolkit/themes/osx/mozapps/extensions/category-available.png
index d1b737ab0..d1b737ab0 100644
--- a/toolkit/themes/windows/mozapps/webextensions/category-available-XP.png
+++ b/toolkit/themes/osx/mozapps/extensions/category-available.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-dictionaries.png b/toolkit/themes/osx/mozapps/extensions/category-dictionaries.png
new file mode 100644
index 000000000..54ae4f93f
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-dictionaries.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/category-discover-XP.png b/toolkit/themes/osx/mozapps/extensions/category-discover.png
index a6f5b49b3..a6f5b49b3 100644
--- a/toolkit/themes/windows/mozapps/webextensions/category-discover-XP.png
+++ b/toolkit/themes/osx/mozapps/extensions/category-discover.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-experiments.png b/toolkit/themes/osx/mozapps/extensions/category-experiments.png
new file mode 100644
index 000000000..a9d00545e
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-experiments.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/category-plugins-XP.png b/toolkit/themes/osx/mozapps/extensions/category-plugins.png
index 5c4d8bf47..5c4d8bf47 100644
--- a/toolkit/themes/windows/mozapps/webextensions/category-plugins-XP.png
+++ b/toolkit/themes/osx/mozapps/extensions/category-plugins.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/category-recent-XP.png b/toolkit/themes/osx/mozapps/extensions/category-recent.png
index 7ecfc7d4c..7ecfc7d4c 100644
--- a/toolkit/themes/windows/mozapps/webextensions/category-recent-XP.png
+++ b/toolkit/themes/osx/mozapps/extensions/category-recent.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-search.png b/toolkit/themes/osx/mozapps/extensions/category-search.png
new file mode 100644
index 000000000..52e91a7ce
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-search.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-searchengines.png b/toolkit/themes/osx/mozapps/extensions/category-searchengines.png
new file mode 100644
index 000000000..b893cb48a
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-searchengines.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-service.png b/toolkit/themes/osx/mozapps/extensions/category-service.png
new file mode 100644
index 000000000..997c8541c
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-service.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png
new file mode 100644
index 000000000..4ad1a1a82
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.png b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.png
new file mode 100644
index 000000000..54ae4f93f
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/discover-logo.png b/toolkit/themes/osx/mozapps/extensions/discover-logo.png
new file mode 100644
index 000000000..cd50735a8
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/discover-logo.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/eula.css b/toolkit/themes/osx/mozapps/extensions/eula.css
new file mode 100644
index 000000000..5fb2c52df
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/eula.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/. */
+
+#icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 48px;
+ max-height: 48px;
+ -moz-margin-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: -moz-FieldText;
+ background-color: -moz-Field;
+ margin: 1em;
+ border: 1px solid;
+ -moz-border-top-colors: ActiveBorder;
+ -moz-border-right-colors: ActiveBorder;
+ -moz-border-bottom-colors: ActiveBorder;
+ -moz-border-left-colors: ActiveBorder;
+}
+
diff --git a/toolkit/themes/osx/mozapps/extensions/experimentGeneric.png b/toolkit/themes/osx/mozapps/extensions/experimentGeneric.png
new file mode 100644
index 000000000..a9d00545e
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/experimentGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.png b/toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.png
new file mode 100644
index 000000000..fc6c8a258
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/extensionGeneric.png b/toolkit/themes/osx/mozapps/extensions/extensionGeneric.png
new file mode 100644
index 000000000..6a76774c7
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/extensionGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/extensions.css b/toolkit/themes/osx/mozapps/extensions/extensions.css
new file mode 100644
index 000000000..9614967a4
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/extensions.css
@@ -0,0 +1,1199 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/inContentUI.css");
+
+%include ../../global/shared.inc
+
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+
+/*** global warnings ***/
+
+.global-warning-container {
+ overflow-x: hidden;
+}
+
+.global-warning {
+ -moz-box-align: center;
+ padding: 0 8px;
+ color: #916D15;
+ font-weight: bold;
+}
+
+.global-warning,
+.global-warning .button-link {
+ text-shadow: @loweredShadow@;
+}
+
+#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;
+ min-height: 31px;
+ border-bottom: 1px solid rgba(50, 65, 92, 0.4);
+}
+
+@media (max-width: 600px) {
+ .global-warning-text {
+ display: none;
+ }
+
+ .global-warning .warning-icon {
+ background-color: rgba(255, 255, 255, 0.7);
+ box-shadow: 0px 0px 2px 4px rgba(255, 255, 255, 0.7);
+ border-radius: 10px;
+ }
+}
+
+/*** global informations ***/
+#addons-page .global-info-container {
+ background-color: #e3e6eb;
+ border-top-right-radius: 5px;
+ border-top-left-radius: 5px;
+}
+
+/* Plugins aren't yet disabled by safemode (bug 342333),
+ so don't show that warning when viewing plugins. */
+#addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
+#addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning-container {
+ background-color: inherit;
+ background-image: none;
+}
+
+
+/*** 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: #373D48;
+ font-size: 12px;
+ border: 1px solid #A8B8D1;
+ border-radius: 8px;
+ background-image: linear-gradient(rgba(255, 255, 255, 0.7), rgba(236, 241, 247, 0.7));
+ background-clip: padding-box;
+ box-shadow: 0 -3px 0 rgba(58, 78, 103, 0.05) inset,
+ 0 3px 0 rgba(175, 195, 220, 0.3);
+}
+
+.alert .alert-title {
+ font-weight: bold;
+ font-size: 200%;
+ margin-bottom: 15px;
+}
+
+.alert button {
+ margin: 1em 2em;
+}
+
+.loading {
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+
+
+/*** category selector ***/
+
+#categories {
+ -moz-appearance: none;
+ border: none;
+ -moz-margin-end: -1px;
+ background-color: transparent;
+ position: relative;
+ margin-top: 31px;
+}
+
+.category {
+ -moz-appearance: none;
+ color: #252F3B;
+ border-width: 1px;
+ border-style: solid;
+ border-color: transparent;
+ padding: 10px 4px;
+ -moz-box-align: center;
+ overflow: hidden;
+ min-height: 0;
+}
+
+.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: rgba(255, 255, 255, 0.35);
+ color: -moz-dialogtext;
+ border-color: rgba(50, 65, 92, 0.4);
+ -moz-border-end-color: #C9CFD7;
+}
+
+.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;
+ -moz-margin-start: 6px;
+}
+
+#category-search > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-search.png");
+}
+#category-discover > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-discover.png");
+}
+#category-locale > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-languages.png");
+}
+#category-searchengine > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-searchengines.png");
+}
+#category-extension > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-extensions.png");
+}
+#category-service > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-service.png");
+}
+#category-theme > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-themes.png");
+}
+#category-plugin > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
+}
+#category-dictionary > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-dictionaries.png");
+}
+#category-experiment > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-experiments.png");
+}
+#category-availableUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
+}
+#category-recentUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
+}
+
+
+/*** header ***/
+
+#header {
+ margin-bottom: 18px;
+}
+
+.nav-button {
+ list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
+}
+
+#back-btn:-moz-locale-dir(ltr),
+#forward-btn:-moz-locale-dir(rtl) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-right: none;
+ -moz-image-region: rect(0, 20px, 20px, 0);
+ padding-right: 3px;
+}
+
+#back-btn:-moz-locale-dir(rtl),
+#forward-btn:-moz-locale-dir(ltr) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-image-region: rect(0, 40px, 20px, 20px);
+ padding-left: 3px;
+}
+
+#header-utils-btn {
+ list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg#utilities");
+ -moz-margin-end: 18px;
+}
+
+#header-utils-btn > .toolbarbutton-menu-dropmarker {
+ list-style-image: url("chrome://mozapps/skin/extensions/toolbarbutton-dropmarker.png");
+ padding: 0;
+ -moz-margin-start: 2px;
+}
+
+#header-search {
+ margin: 0;
+ -moz-appearance: none;
+ padding: 3px 5px 2px;
+ border: 1px solid rgba(60,73,97,0.5);
+ border-radius: 10000px;
+ box-shadow: inset 0 1px 1px rgba(0,0,0,0.15), 0 1px rgba(255,255,255,0.25);
+ background: linear-gradient(rgba(255,255,255,0.2), rgba(255,255,255,0.3));
+ background-clip: padding-box;
+}
+
+@media (max-width: 600px) {
+ #header-search {
+ width: 12em;
+ }
+}
+
+#header-search[focused] {
+ box-shadow: @focusRingShadow@, inset 0 1px 1px rgba(0,0,0,0.15);
+ border-color: -moz-mac-focusring;
+}
+
+#header-search > .textbox-input-box {
+ -moz-padding-start: 15px;
+ background: url("chrome://mozapps/skin/extensions/search.png") left no-repeat;
+}
+
+#header-search > .textbox-input-box:-moz-locale-dir(rtl) {
+ background-position: right;
+}
+
+#header-search > .textbox-input-box > html|*.textbox-input::-moz-placeholder {
+ color: #5C6470;
+ opacity: 1.0;
+}
+
+.view-header {
+ padding: 4px;
+ margin: 0;
+ min-height: 31px;
+ border-bottom: 1px solid rgba(50, 65, 92, 0.4);
+ background-image: linear-gradient(rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05));
+}
+
+
+/*** sorters ***/
+
+.sort-controls {
+ -moz-appearance: none;
+}
+
+.sorter {
+ -moz-appearance: none;
+ border: none;
+ color: #41434B;
+ background-color: transparent;
+ border-radius: 10000px;
+ padding: 0 6px;
+ margin: 0 6px;
+ min-width: 12px !important;
+ -moz-box-direction: reverse;
+}
+
+.sorter[checkState="1"],
+.sorter[checkState="2"],
+.sorter:active:hover {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+ box-shadow: inset #A3A6AC 0 1px 1px, @loweredShadow@;
+}
+
+.sorter:hover {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+}
+
+.sorter[checkState="1"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.sorter[checkState="2"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.sorter .button-icon {
+ -moz-margin-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");
+ -moz-margin-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 {
+ border-bottom: 1px solid #B6B1B9;
+ padding: 5px;
+ color: #373D48;
+}
+
+.details {
+ cursor: pointer;
+ margin: 0;
+ -moz-margin-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: grayscale(1);
+}
+
+.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");
+}
+
+.addon-view[type="experiment"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/experimentGeneric.png");
+}
+
+.name-container {
+ font-size: 150%;
+ margin-bottom: 0;
+ font-weight: bold;
+ color: #000;
+ text-shadow: @loweredShadow@;
+ -moz-box-align: end;
+ -moz-box-flex: 1;
+}
+
+.creator {
+ font-weight: bold;
+}
+
+.creator .text-link {
+ color: #0066CC;
+}
+
+.description-container {
+ margin-top: 8px;
+ -moz-margin-start: 6px;
+ -moz-box-align: center;
+}
+
+.description {
+ margin: 0;
+}
+
+.warning,
+.pending,
+.error {
+ -moz-margin-start: 48px;
+ font-weight: bold;
+ text-shadow: @loweredShadow@;
+ -moz-box-align: center;
+}
+
+.content-container,
+.basicinfo-container {
+ -moz-box-align: start;
+}
+
+.addon[status="installing"] > .content-container {
+ -moz-box-align: stretch;
+}
+
+.update-info-container {
+ -moz-box-align: center;
+}
+
+.advancedinfo-container,
+.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-view[notification="warning"] {
+ background-image: linear-gradient(rgba(255, 255, 0, 0.2), rgba(255, 255, 0, 0.1));
+}
+
+.addon-view[notification="error"] {
+ background-image: linear-gradient(rgba(255, 0, 0, 0.2), rgba(255, 0, 0, 0.1));
+}
+
+.addon-view[notification="info"] {
+ background-image: linear-gradient(rgba(0, 0, 255, 0.2), rgba(0, 0, 255, 0.1));
+}
+
+.addon-view[pending="enable"],
+.addon-view[pending="upgrade"],
+.addon-view[pending="install"] {
+ background-image: linear-gradient(rgba(0, 255, 0, 0.2), rgba(0, 255, 0, 0.1));
+}
+
+.addon-view[pending="disable"],
+.addon-view[pending="uninstall"] {
+ background-image: linear-gradient(rgba(128, 128, 128, 0.2), rgba(128, 128, 128, 0.1));
+}
+
+.addon .relnotes-container {
+ -moz-box-align: start;
+ 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.gif");
+}
+
+.addon .relnotes-toggle > .button-box > .button-icon {
+ -moz-padding-start: 4px;
+}
+
+.addon[show-relnotes] .relnotes-toggle {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.addon[active="false"] {
+ background-color: rgba(135, 135, 135, 0.1);
+ background-image: linear-gradient(rgba(135, 135, 135, 0),
+ rgba(135, 135, 135, 0.1));
+}
+
+.addon-view[active="false"],
+.addon-view[active="false"] .name-container {
+ color: #686A6B;
+}
+
+.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(105, 125, 149, 0.39);
+ color: black;
+}
+
+.addon[selected] .name-container {
+ text-shadow: @loweredShadow@;
+}
+
+.addon[active="false"][selected] .name-container {
+ color: #3F3F3F;
+}
+
+
+/*** search view ***/
+
+#search-filter {
+ padding: 5px 20px;
+ font-size: 120%;
+ overflow-x: hidden;
+ border-bottom: 1px solid rgba(50, 65, 92, 0.4);
+}
+
+#search-filter-label {
+ font-weight: bold;
+ color: #666;
+}
+
+.search-filter-radio {
+ -moz-appearance: none;
+ padding: 0 10px;
+ margin: 0 3px;
+ border-radius: 10000px;
+}
+
+.search-filter-radio[selected] {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+ box-shadow: inset #A3A6AC 0 1px 1px, @loweredShadow@;
+}
+
+.search-filter-radio:hover {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+}
+
+.search-filter-radio .radio-check {
+ 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 {
+ -moz-margin-start: 0;
+}
+
+#detail-icon-container {
+ width: 64px;
+ -moz-margin-end: 10px;
+ margin-top: 6px;
+}
+
+#detail-icon {
+ max-width: 64px;
+ max-height: 64px;
+}
+
+#detail-summary {
+ margin-bottom: 2em;
+}
+
+#detail-name-container {
+ font-size: 200%;
+}
+
+#detail-screenshot {
+ -moz-margin-end: 2em;
+ max-width: 300px;
+ max-height: 300px;
+}
+
+#detail-screenshot[loading] {
+ background-image: url("chrome://global/skin/icons/loading_16.png"),
+ linear-gradient(rgba(255, 255, 255, 0.5), transparent);
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ border-radius: 3px;
+}
+
+#detail-screenshot[loading="error"] {
+ background-image: url("chrome://global/skin/media/error.png"),
+ linear-gradient(rgba(255, 255, 255, 0.5), transparent);
+}
+
+#detail-desc-container {
+ margin-bottom: 2em;
+}
+
+#detail-desc, #detail-fulldesc {
+ -moz-margin-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 rgba(50, 65, 92, 0.3);
+ margin-bottom: 2em;
+ padding: 1em;
+ background-color: rgba(255, 255, 255, 0.35);
+}
+
+#detail-contrib-description {
+ font-style: italic;
+ margin-bottom: 1em;
+ color: #373D48;
+}
+
+#detail-contrib-suggested {
+ color: grey;
+ font-weight: bold;
+}
+
+#detail-contrib-btn {
+ -moz-appearance: none;
+ 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 {
+ -moz-margin-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;
+}
+
+#detail-grid {
+ margin-bottom: 2em;
+}
+
+#detail-grid > columns > column:first-child {
+ min-width: 15em;
+ max-width: 25em;
+}
+
+.detail-row[first-row="true"],
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
+ border-top: none;
+}
+
+.detail-row,
+.detail-row-complex,
+setting {
+ border-top: 2px solid;
+ -moz-border-top-colors: rgba(28, 31, 37, 0.2) rgba(255, 255, 255, 0.2);
+ -moz-box-align: center;
+ min-height: 30px;
+}
+
+#detail-controls {
+ margin-bottom: 1em;
+}
+
+#detail-view[active="false"]:not([pending]):not([notification]) {
+ background-image: linear-gradient(rgba(135, 135, 135, 0.1),
+ rgba(135, 135, 135, 0));
+}
+
+setting[first-row="true"] {
+ margin-top: 2em;
+}
+
+setting {
+ -moz-box-align: start;
+}
+
+.preferences-alignment {
+ min-height: 30px;
+ -moz-box-align: center;
+}
+
+.preferences-description {
+ font-size: 90.9%;
+ color: graytext;
+ margin-top: -2px;
+ -moz-margin-start: 2em;
+ white-space: pre-wrap;
+}
+
+.preferences-description:empty {
+ display: none;
+}
+
+setting[type="radio"] > radiogroup {
+ -moz-box-orient: horizontal;
+}
+
+
+/*** creator ***/
+
+.creator > label {
+ -moz-margin-start: 0;
+ -moz-margin-end: 0;
+}
+
+.creator > .text-link {
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+
+/*** rating ***/
+
+.meta-rating {
+ -moz-margin-end: 0;
+ margin-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-image: linear-gradient(#DCDEE3, #CBCED6);
+ border: 1px solid #858898;
+ border-radius: 3px;
+ box-shadow: inset #E3E8EC 0 1px 1px, @loweredShadow@;
+ width: 200px;
+ height: 21px;
+ margin: 0 8px;
+}
+
+.download-progress[mode="undetermined"] .progress {
+ -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter-undetermined");
+}
+
+.download-progress[mode="undetermined"] {
+ border-color: #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(#6AC47E, #4FAC6A);
+ margin-top: -1px;
+ margin-bottom: -1px;
+ border: 1px solid #2E773A;
+}
+
+.download-progress .start-cap {
+ -moz-margin-start: -1px;
+ -moz-border-end-width: 0;
+}
+
+.download-progress .end-cap {
+ -moz-margin-end: -1px;
+ -moz-border-start-width: 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.15);
+ border: 1px solid rgba(0, 0, 0, 0.4);
+ padding: 3px;
+ border-radius: 3px;
+ min-width: 0;
+ margin: 3px;
+}
+
+.download-progress .cancel .button-text {
+ display: none;
+}
+
+.download-progress .cancel .button-icon {
+ -moz-margin-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: @loweredShadow@;
+}
+
+
+/*** install status ***/
+
+.install-status {
+ -moz-box-align: center;
+}
+
+
+/*** check for updates ***/
+
+#updates-container {
+ -moz-box-align: center;
+}
+
+#updates-installed,
+#updates-downloaded {
+ color: #3C735C;
+ font-weight: bold;
+}
+
+#update-selected {
+ margin: 12px;
+}
+
+
+/*** buttons ***/
+
+.addon-control[disabled="true"]:not(.no-auto-hide) {
+ display: none;
+}
+
+.no-auto-hide .addon-control {
+ display: block !important;
+}
+
+.no-auto-hide > .menulist-dropmarker {
+ -moz-padding-start: 0px !important;
+}
+
+button.button-link {
+ -moz-appearance: none;
+ background: transparent;
+ border: none;
+ box-shadow: none;
+ text-decoration: underline;
+ color: #0066CC;
+ cursor: pointer;
+ min-width: 0;
+ margin: 0 6px;
+}
+
+.text-link {
+ color: #3386D5;
+}
+
+.button-link:hover,
+.text-link:hover {
+ color: #3DA1FF;
+}
+
+/* Needed to override normal button style from inContent.css */
+button.button-link:not([disabled="true"]):active:hover {
+ background: transparent;
+ border: none;
+ box-shadow: none;
+}
+
+.header-button {
+ -moz-appearance: none;
+ padding: 0 4px;
+ margin: 0;
+ height: 22px;
+ border: 1px solid rgba(60,73,97,0.5);
+ border-radius: @toolbarbuttonCornerRadius@;
+ box-shadow: inset 0 1px rgba(255,255,255,0.25), 0 1px rgba(255,255,255,0.25);
+ background: linear-gradient(rgba(255,255,255,0.45), transparent);
+ background-clip: padding-box;
+}
+
+.header-button .toolbarbutton-text {
+ display: none;
+}
+
+.header-button[disabled="true"] .toolbarbutton-icon {
+ opacity: 0.4;
+}
+
+.header-button:not([disabled="true"]):active:hover,
+.header-button[open="true"] {
+ border-color: rgba(45,54,71,0.7);
+ box-shadow: inset 0 0 4px rgb(45,54,71), 0 1px rgba(255,255,255,0.25);
+ background-image: linear-gradient(rgba(45,54,71,0.6), transparent);
+}
+
+/*** telemetry experiments ***/
+
+#detail-experiment-container {
+ font-size: 80%;
+ margin-bottom: 1em;
+}
+
+#detail-experiment-bullet-container,
+#detail-experiment-state,
+#detail-experiment-time,
+.experiment-bullet-container,
+.experiment-state,
+.experiment-time {
+ vertical-align: middle;
+ display: inline-block;
+}
+
+.addon .experiment-bullet,
+#detail-experiment-bullet {
+ fill: rgb(158, 158, 158);
+}
+
+.addon[active="true"] .experiment-bullet,
+#detail-view[active="true"] #detail-experiment-bullet {
+ fill: rgb(106, 201, 20);
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/heart.png b/toolkit/themes/osx/mozapps/extensions/heart.png
new file mode 100644
index 000000000..655f4c4be
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/heart.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/localeGeneric-XP.png b/toolkit/themes/osx/mozapps/extensions/localeGeneric.png
index 4d9ac5ad8..4d9ac5ad8 100644
--- a/toolkit/themes/windows/mozapps/webextensions/localeGeneric-XP.png
+++ b/toolkit/themes/osx/mozapps/extensions/localeGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/navigation.png b/toolkit/themes/osx/mozapps/extensions/navigation.png
new file mode 100644
index 000000000..ffc40d7e5
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/navigation.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/newaddon.css b/toolkit/themes/osx/mozapps/extensions/newaddon.css
new file mode 100644
index 000000000..5bf04fab1
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/newaddon.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/. */
+
+%include ../../global/shared.inc
+
+@import url("chrome://global/skin/inContentUI.css");
+
+#addon-page {
+ 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 {
+ overflow: visible;
+ max-width: 600px;
+ margin: 20px;
+ padding: 30px 90px;
+}
+
+#addon-info {
+ -moz-box-align: start;
+ margin: 25px 10px;
+}
+
+#icon {
+ -moz-margin-end: 10px;
+ max-width: 64px;
+ max-height: 64px;
+ 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 {
+ color: GrayText;
+}
+
+#location {
+ color: GrayText;
+}
+
+#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;
+ -moz-margin-end: 5px;
+}
+
+#allow {
+ -moz-margin-start: 84px;
+ margin-bottom: 20px;
+}
+
+#continuePanel,
+#restartPanel {
+ margin-top: 25px;
+ -moz-box-align: center;
+ -moz-box-pack: end;
+}
+
+#continuePanel {
+ -moz-box-pack: end;
+}
+
+#restartMessage {
+ text-align: right;
+}
+
+#restartSpacer {
+ -moz-box-flex: 1;
+}
+
+#later {
+ color: GrayText;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/rating-not-won.png b/toolkit/themes/osx/mozapps/extensions/rating-not-won.png
new file mode 100644
index 000000000..2761f1925
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/rating-not-won.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/rating-won.png b/toolkit/themes/osx/mozapps/extensions/rating-won.png
new file mode 100644
index 000000000..336dd8f6e
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/rating-won.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/search.png b/toolkit/themes/osx/mozapps/extensions/search.png
new file mode 100644
index 000000000..93196dbbf
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/search.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/selectAddons.css b/toolkit/themes/osx/mozapps/extensions/selectAddons.css
new file mode 100644
index 000000000..8682b04b5
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/selectAddons.css
@@ -0,0 +1,163 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 ../../global/shared.inc
+
+.heading {
+ font-size: 270%;
+ text-align: center;
+ margin: 0 120px;
+}
+
+.progress {
+ margin: 10px 128px;
+}
+
+.progress-label,
+#errors-description {
+ text-align: center;
+ margin: 0 10px;
+}
+
+#checking-heading,
+#update-heading,
+#errors-heading {
+ margin-top: 90px;
+}
+
+#select-heading,
+#confirm-heading {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+#select-description,
+#confirm-description {
+ margin: 10px;
+}
+
+#select-list {
+ border: 1px solid WindowFrame;
+ background-color: Window;
+ margin: 10px;
+}
+
+#select-grid column {
+ -moz-box-align: center;
+}
+
+#select-grid row {
+ -moz-box-align: stretch;
+}
+
+#select-grid row:nth-of-type(odd) {
+ background-color: -moz-oddtreerow;
+}
+
+#select-grid label,
+#select-grid checkbox {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.select-cell {
+ -moz-box-align: center;
+ -moz-box-pack: start;
+ box-sizing: border-box;
+}
+
+#select-header {
+ background-color: Window !important;
+}
+
+#select-header .select-cell {
+ -moz-appearance: treeheadercell;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.select-keep {
+ -moz-box-pack: center;
+}
+
+.select-icon {
+ width: 20px;
+}
+
+#select-grid separator {
+ display: none;
+}
+
+.addon-name,
+.addon-action-message,
+.addon-action-update {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 2px 6px;
+}
+
+.addon:not([active]) .addon-name,
+.addon:not([active]) .addon-action-message,
+.addon:not([active]) .addon-action-update {
+ color: GrayText;
+}
+
+.addon-icon {
+ height: 16px;
+ width: 16px;
+ margin: 2px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.addon-icon[type="theme"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric-16.png");
+}
+
+.addon-icon[type="plugin"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.addon-icon[type="dictionary"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric-16.png");
+}
+
+.action-list {
+ margin-top: 10px;
+ -moz-margin-start: 5em;
+}
+
+.action-header {
+ margin-bottom: 10px;
+}
+
+#confirm .addon {
+ -moz-margin-start: 3em;
+ -moz-box-align: center;
+}
+
+.addon:not([active]) .addon-icon,
+#disable-list .addon-icon,
+#incompatible-list .addon-icon {
+ filter: grayscale(1);
+}
+
+#footer {
+ padding: 15px 12px;
+ -moz-appearance: statusbar;
+ -moz-window-dragging: drag;
+}
+
+button {
+ -moz-appearance: toolbarbutton;
+ min-height: 22px;
+ margin: 0 6px;
+ padding: 0;
+ text-shadow: @loweredShadow@;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-error.png b/toolkit/themes/osx/mozapps/extensions/stripes-error.png
new file mode 100644
index 000000000..1dc2d8504
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-error.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png b/toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png
new file mode 100644
index 000000000..901ab1ec2
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png b/toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png
new file mode 100644
index 000000000..370ceec0f
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-warning.png b/toolkit/themes/osx/mozapps/extensions/stripes-warning.png
new file mode 100644
index 000000000..69463fb1a
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-warning.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png b/toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png
new file mode 100644
index 000000000..190bb30d7
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/themeGeneric-XP.png b/toolkit/themes/osx/mozapps/extensions/themeGeneric.png
index be645f76d..be645f76d 100644
--- a/toolkit/themes/windows/mozapps/webextensions/themeGeneric-XP.png
+++ b/toolkit/themes/osx/mozapps/extensions/themeGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png b/toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png
new file mode 100644
index 000000000..e7674c62a
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/update.css b/toolkit/themes/osx/mozapps/extensions/update.css
new file mode 100644
index 000000000..8e9255691
--- /dev/null
+++ b/toolkit/themes/osx/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/warning.gif");
+}
+
+.throbber {
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+ width: 16px;
+ height: 16px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ -moz-margin-start: 5px;
+ -moz-margin-end: 2px;
+}
+
+.alertBox {
+ background-color: InfoBackground;
+ color: InfoText;
+ border: 1px outset InfoBackground;
+ margin-left: 3px;
+ margin-right: 3px;
+ padding: 5px;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css b/toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css
new file mode 100644
index 000000000..0a1a84b24
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.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/. */
+
+#xpinstallheader {
+ margin-bottom: 2em;
+}
+
+.alert-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/warning-large.png");
+ margin-top: 0 !important;
+ margin-bottom: 6px !important;
+ -moz-margin-start: 6px !important;
+ -moz-margin-end: 20px !important;
+}
+
+#itemList {
+ -moz-appearance: listbox;
+ margin: 3px 4px 10px 4px;
+ height: 14em;
+}
+
+#itemWarningIntro {
+ -moz-margin-start: 8px;
+}
+
+#dialogContentBox {
+ padding: 5px;
+}
+
+installitem {
+ padding: 5px 0 5px 5px;
+ border-bottom: 1px dotted #C0C0C0;
+ margin-bottom: 5px;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 1.25em;
+ margin-bottom: 1em;
+}
+
+.xpinstallIconContainer {
+ width: 32px;
+ height: 32px;
+ -moz-margin-end: 5px;
+}
+
+.xpinstallItemName {
+ font-weight: bold;
+}
+
+.xpinstallItemSigned {
+ font-style: italic;
+ font-size: 0.9em;
+}
+
+.xpinstallItemURL {
+ -moz-appearance: none;
+ border: none;
+ background-color: Window;
+ margin-top: 2px;
+ margin-bottom: 1px;
+ -moz-margin-start: 6px;
+ -moz-margin-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/toolkit/themes/osx/mozapps/jar.mn b/toolkit/themes/osx/mozapps/jar.mn
index 8da83da16..35927755b 100644
--- a/toolkit/themes/osx/mozapps/jar.mn
+++ b/toolkit/themes/osx/mozapps/jar.mn
@@ -37,6 +37,51 @@ toolkit.jar:
skin/classic/mozapps/extensions/eula.css (webextensions/eula.css)
skin/classic/mozapps/extensions/blocklist.css (webextensions/blocklist.css)
* skin/classic/mozapps/extensions/newaddon.css (webextensions/newaddon.css)
+#else
+ skin/classic/mozapps/extensions/category-search.png (extensions/category-search.png)
+ skin/classic/mozapps/extensions/category-discover.png (extensions/category-discover.png)
+ skin/classic/mozapps/extensions/category-languages.png (extensions/localeGeneric.png)
+ skin/classic/mozapps/extensions/category-searchengines.png (extensions/category-searchengines.png)
+ skin/classic/mozapps/extensions/category-extensions.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/extensions/category-themes.png (extensions/themeGeneric.png)
+ skin/classic/mozapps/extensions/category-plugins.png (extensions/category-plugins.png)
+ skin/classic/mozapps/extensions/category-service.png (extensions/category-service.png)
+ skin/classic/mozapps/extensions/category-dictionaries.png (extensions/category-dictionaries.png)
+ skin/classic/mozapps/extensions/category-experiments.png (extensions/category-experiments.png)
+ skin/classic/mozapps/extensions/category-recent.png (extensions/category-recent.png)
+ skin/classic/mozapps/extensions/category-available.png (extensions/category-available.png)
+ skin/classic/mozapps/extensions/discover-logo.png (extensions/discover-logo.png)
+ skin/classic/mozapps/extensions/extensionGeneric.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/extensions/extensionGeneric-16.png (extensions/extensionGeneric-16.png)
+ skin/classic/mozapps/extensions/themeGeneric.png (extensions/themeGeneric.png)
+ skin/classic/mozapps/extensions/themeGeneric-16.png (extensions/themeGeneric-16.png)
+ skin/classic/mozapps/extensions/dictionaryGeneric.png (extensions/dictionaryGeneric.png)
+ skin/classic/mozapps/extensions/dictionaryGeneric-16.png (extensions/dictionaryGeneric-16.png)
+ skin/classic/mozapps/extensions/experimentGeneric.png (extensions/experimentGeneric.png)
+ skin/classic/mozapps/extensions/localeGeneric.png (extensions/localeGeneric.png)
+ skin/classic/mozapps/extensions/rating-won.png (extensions/rating-won.png)
+ skin/classic/mozapps/extensions/rating-not-won.png (extensions/rating-not-won.png)
+ skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
+ skin/classic/mozapps/extensions/utilities.svg (../../shared/extensions/utilities.svg)
+ skin/classic/mozapps/extensions/toolbarbutton-dropmarker.png (extensions/toolbarbutton-dropmarker.png)
+ skin/classic/mozapps/extensions/heart.png (extensions/heart.png)
+ skin/classic/mozapps/extensions/navigation.png (extensions/navigation.png)
+ skin/classic/mozapps/extensions/stripes-warning.png (extensions/stripes-warning.png)
+ skin/classic/mozapps/extensions/stripes-error.png (extensions/stripes-error.png)
+ skin/classic/mozapps/extensions/stripes-info-positive.png (extensions/stripes-info-positive.png)
+ skin/classic/mozapps/extensions/stripes-info-negative.png (extensions/stripes-info-negative.png)
+ skin/classic/mozapps/extensions/alerticon-warning.png (extensions/alerticon-warning.png)
+ skin/classic/mozapps/extensions/alerticon-error.png (extensions/alerticon-error.png)
+ skin/classic/mozapps/extensions/alerticon-info-positive.png (extensions/alerticon-info-positive.png)
+ skin/classic/mozapps/extensions/alerticon-info-negative.png (extensions/alerticon-info-negative.png)
+ skin/classic/mozapps/extensions/search.png (extensions/search.png)
+ skin/classic/mozapps/extensions/about.css (extensions/about.css)
+* skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
+* skin/classic/mozapps/extensions/selectAddons.css (extensions/selectAddons.css)
+ skin/classic/mozapps/extensions/update.css (extensions/update.css)
+ skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
+ skin/classic/mozapps/extensions/blocklist.css (extensions/blocklist.css)
+* skin/classic/mozapps/extensions/newaddon.css (extensions/newaddon.css)
#endif
skin/classic/mozapps/plugins/notifyPluginGeneric.png (plugins/notifyPluginGeneric.png)
skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png)
@@ -52,6 +97,9 @@ toolkit.jar:
skin/classic/mozapps/viewsource/viewsource.css (viewsource/viewsource.css)
#ifdef MOZ_WEBEXTENSIONS
skin/classic/mozapps/xpinstall/xpinstallConfirm.css (webextensions/xpinstallConfirm.css)
+#else
+ skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/xpinstall/xpinstallConfirm.css (extensions/xpinstallConfirm.css)
#endif
skin/classic/mozapps/handling/handling.css (handling/handling.css)
diff --git a/toolkit/themes/shared/extensions/utilities.svg b/toolkit/themes/shared/extensions/utilities.svg
new file mode 100644
index 000000000..fd911001b
--- /dev/null
+++ b/toolkit/themes/shared/extensions/utilities.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0"
+ y="0"
+ width="16"
+ height="16"
+ viewBox="0 0 16 16">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #424f5a;
+ }
+ use[id$="-native"] {
+ fill: GrayText;
+ }
+ </style>
+ <defs style="display: none;">
+ <path id="utilities-shape" d="m11.5,13.9l-.6-1.5c.3-.2 .5-.4 .8-.6 .2-.2 .4-.5 .6-.7l1.5,.6c.3,.1 .6,0 .7-.3l.4-1c.1-.3 0-.6-.3-.7l-1.5-.6c.1-.6 .1-1.3 0-2l1.5-.6c.3-.1 .4-.4 .3-.7l-.4-1c-.1-.3-.4-.4-.7-.3l-1.5,.6c-.2-.3-.4-.5-.6-.8-.2-.1-.5-.3-.7-.5l.6-1.5c.1-.3 0-.6-.3-.7l-.9-.4c-.3-.1-.6,0-.7,.3l-.6,1.5c-.6-.1-1.3-.1-2,0l-.6-1.5c-.1-.3-.4-.4-.7-.3l-1,.4c-.2,.1-.3,.4-.2,.6l.6,1.5c-.3,.3-.5,.5-.8,.7-.2,.3-.4,.5-.6,.8l-1.5-.7c-.3-.1-.6,0-.7,.3l-.4,.9c-.1,.3 0,.6 .3,.7l1.5,.7c-.1,.6-.1,1.3 0,1.9l-1.5,.6c-.3,.1-.4,.4-.3,.7l.4,1c.1,.3 .4,.4 .7,.3l1.5-.6c.2,.3 .4,.5 .6,.8 .2,.2 .5,.4 .7,.6l-.6,1.5c-.1,.3 0,.6 .3,.7l1,.4c.3,.1 .6,0 .7-.3l.6-1.5c.6,.1 1.3,.1 2,0l.6,1.5c.1,.3 .4,.4 .7,.3l1-.4c.1-.1 .3-.4 .1-.7zm-5.1-4.2c-.9-.9-.9-2.4 0-3.3 .9-.9 2.4-.9 3.3,0 .9,.9 .9,2.4 0,3.3-.9,.9-2.4,.9-3.3,0z"/>
+ </defs>
+ <use id="utilities" xlink:href="#utilities-shape"/>
+ <use id="utilities-native" xlink:href="#utilities-shape"/>
+</svg>
diff --git a/toolkit/themes/windows/global/dirListing/folder-XP.png b/toolkit/themes/windows/global/dirListing/folder-XP.png
deleted file mode 100644
index 102de5196..000000000
--- a/toolkit/themes/windows/global/dirListing/folder-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/dirListing/local-XP.png b/toolkit/themes/windows/global/dirListing/local-XP.png
deleted file mode 100644
index 99191f3a5..000000000
--- a/toolkit/themes/windows/global/dirListing/local-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/dirListing/remote-XP.png b/toolkit/themes/windows/global/dirListing/remote-XP.png
deleted file mode 100644
index 4febb764b..000000000
--- a/toolkit/themes/windows/global/dirListing/remote-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/dirListing/up-XP.png b/toolkit/themes/windows/global/dirListing/up-XP.png
deleted file mode 100644
index 851502d47..000000000
--- a/toolkit/themes/windows/global/dirListing/up-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/global.css b/toolkit/themes/windows/global/global.css
index 20a190684..aaddef882 100644
--- a/toolkit/themes/windows/global/global.css
+++ b/toolkit/themes/windows/global/global.css
@@ -350,9 +350,7 @@ popupnotificationcontent {
width: 20px;
}
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
.close-icon {
-moz-image-region: rect(0, 16px, 16px, 0);
}
@@ -386,9 +384,7 @@ popupnotificationcontent {
-moz-image-region: rect(0, 120px, 40px, 80px);
}
- @media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+ @media (-moz-os-version: windows-win7) {
.close-icon {
-moz-image-region: rect(0, 32px, 32px, 0);
}
diff --git a/toolkit/themes/windows/global/icons/Landscape-XP.png b/toolkit/themes/windows/global/icons/Landscape-XP.png
deleted file mode 100644
index cc43abdf3..000000000
--- a/toolkit/themes/windows/global/icons/Landscape-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/Portrait-XP.png b/toolkit/themes/windows/global/icons/Portrait-XP.png
deleted file mode 100644
index ce36e8413..000000000
--- a/toolkit/themes/windows/global/icons/Portrait-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/Print-preview-XP.png b/toolkit/themes/windows/global/icons/Print-preview-XP.png
deleted file mode 100644
index 6f4736737..000000000
--- a/toolkit/themes/windows/global/icons/Print-preview-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/Question-XP.png b/toolkit/themes/windows/global/icons/Question-XP.png
deleted file mode 100644
index c10004078..000000000
--- a/toolkit/themes/windows/global/icons/Question-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/Search-close-XP.png b/toolkit/themes/windows/global/icons/Search-close-XP.png
deleted file mode 100644
index 39d800e1a..000000000
--- a/toolkit/themes/windows/global/icons/Search-close-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/Search-glass-XP.png b/toolkit/themes/windows/global/icons/Search-glass-XP.png
deleted file mode 100644
index 9eb0e259f..000000000
--- a/toolkit/themes/windows/global/icons/Search-glass-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/Warning-XP.png b/toolkit/themes/windows/global/icons/Warning-XP.png
deleted file mode 100644
index c0ec9b519..000000000
--- a/toolkit/themes/windows/global/icons/Warning-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/autoscroll-XP.png b/toolkit/themes/windows/global/icons/autoscroll-XP.png
deleted file mode 100644
index 6aac3d98e..000000000
--- a/toolkit/themes/windows/global/icons/autoscroll-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/blacklist_favicon-XP.png b/toolkit/themes/windows/global/icons/blacklist_favicon-XP.png
deleted file mode 100644
index 74af56f57..000000000
--- a/toolkit/themes/windows/global/icons/blacklist_favicon-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/blacklist_large-XP.png b/toolkit/themes/windows/global/icons/blacklist_large-XP.png
deleted file mode 100644
index 96ff341c0..000000000
--- a/toolkit/themes/windows/global/icons/blacklist_large-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/close-inverted-XPVista7.png b/toolkit/themes/windows/global/icons/close-inverted-win7.png
index 68596a307..68596a307 100644
--- a/toolkit/themes/windows/global/icons/close-inverted-XPVista7.png
+++ b/toolkit/themes/windows/global/icons/close-inverted-win7.png
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/close-inverted-XPVista7@2x.png b/toolkit/themes/windows/global/icons/close-inverted-win7@2x.png
index eec52e6d1..eec52e6d1 100644
--- a/toolkit/themes/windows/global/icons/close-inverted-XPVista7@2x.png
+++ b/toolkit/themes/windows/global/icons/close-inverted-win7@2x.png
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/close-XPVista7.png b/toolkit/themes/windows/global/icons/close-win7.png
index ea6ada49e..ea6ada49e 100644
--- a/toolkit/themes/windows/global/icons/close-XPVista7.png
+++ b/toolkit/themes/windows/global/icons/close-win7.png
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/close-XPVista7@2x.png b/toolkit/themes/windows/global/icons/close-win7@2x.png
index c25a64a4b..c25a64a4b 100644
--- a/toolkit/themes/windows/global/icons/close-XPVista7@2x.png
+++ b/toolkit/themes/windows/global/icons/close-win7@2x.png
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/error-16-XP.png b/toolkit/themes/windows/global/icons/error-16-XP.png
deleted file mode 100644
index 936d2abeb..000000000
--- a/toolkit/themes/windows/global/icons/error-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/error-64-XP.png b/toolkit/themes/windows/global/icons/error-64-XP.png
deleted file mode 100644
index 75a00c490..000000000
--- a/toolkit/themes/windows/global/icons/error-64-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/folder-item-XP.png b/toolkit/themes/windows/global/icons/folder-item-XP.png
deleted file mode 100644
index 26cda98c9..000000000
--- a/toolkit/themes/windows/global/icons/folder-item-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/information-16-XP.png b/toolkit/themes/windows/global/icons/information-16-XP.png
deleted file mode 100644
index aa8ce6e88..000000000
--- a/toolkit/themes/windows/global/icons/information-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/information-24-XP.png b/toolkit/themes/windows/global/icons/information-24-XP.png
deleted file mode 100644
index 7fa782d58..000000000
--- a/toolkit/themes/windows/global/icons/information-24-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/information-32-XP.png b/toolkit/themes/windows/global/icons/information-32-XP.png
deleted file mode 100644
index 5120db685..000000000
--- a/toolkit/themes/windows/global/icons/information-32-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/question-16-XP.png b/toolkit/themes/windows/global/icons/question-16-XP.png
deleted file mode 100644
index 0c8eafda0..000000000
--- a/toolkit/themes/windows/global/icons/question-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/question-64-XP.png b/toolkit/themes/windows/global/icons/question-64-XP.png
deleted file mode 100644
index a1a51b8d8..000000000
--- a/toolkit/themes/windows/global/icons/question-64-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/sslWarning-XP.png b/toolkit/themes/windows/global/icons/sslWarning-XP.png
deleted file mode 100644
index 09946986f..000000000
--- a/toolkit/themes/windows/global/icons/sslWarning-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/warning-16-XP.png b/toolkit/themes/windows/global/icons/warning-16-XP.png
deleted file mode 100644
index a21574370..000000000
--- a/toolkit/themes/windows/global/icons/warning-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/warning-64-XP.png b/toolkit/themes/windows/global/icons/warning-64-XP.png
deleted file mode 100644
index 832f34886..000000000
--- a/toolkit/themes/windows/global/icons/warning-64-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/warning-large-XP.png b/toolkit/themes/windows/global/icons/warning-large-XP.png
deleted file mode 100644
index 0ec8e79f6..000000000
--- a/toolkit/themes/windows/global/icons/warning-large-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/icons/windowControls-XP.png b/toolkit/themes/windows/global/icons/windowControls-XP.png
deleted file mode 100644
index d90efb05b..000000000
--- a/toolkit/themes/windows/global/icons/windowControls-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/jar.mn b/toolkit/themes/windows/global/jar.mn
index 7f0771020..7f2f29942 100644
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -44,102 +44,36 @@ toolkit.jar:
skin/classic/global/icons/autocomplete-search.svg (icons/autocomplete-search.svg)
skin/classic/global/icons/blacklist_favicon.png (icons/blacklist_favicon.png)
skin/classic/global/icons/blacklist_large.png (icons/blacklist_large.png)
- skin/classic/global/icons/close-XPVista7.png (icons/close-XPVista7.png)
- skin/classic/global/icons/close-XPVista7@2x.png (icons/close-XPVista7@2x.png)
- skin/classic/global/icons/close-inverted-XPVista7.png (icons/close-inverted-XPVista7.png)
- skin/classic/global/icons/close-inverted-XPVista7@2x.png (icons/close-inverted-XPVista7@2x.png)
+ skin/classic/global/icons/close-win7.png (icons/close-win7.png)
+ skin/classic/global/icons/close-win7@2x.png (icons/close-win7@2x.png)
+ skin/classic/global/icons/close-inverted-win7.png (icons/close-inverted-win7.png)
+ skin/classic/global/icons/close-inverted-win7@2x.png (icons/close-inverted-win7@2x.png)
skin/classic/global/icons/resizer.png (icons/resizer.png)
skin/classic/global/icons/sslWarning.png (icons/sslWarning.png)
* skin/classic/global/in-content/common.css (in-content/common.css)
* skin/classic/global/in-content/info-pages.css (in-content/info-pages.css)
skin/classic/global/toolbar/spring.png (toolbar/spring.png)
- skin/classic/global/dirListing/folder-XP.png (dirListing/folder-XP.png)
- skin/classic/global/dirListing/local-XP.png (dirListing/local-XP.png)
- skin/classic/global/dirListing/remote-XP.png (dirListing/remote-XP.png)
- skin/classic/global/dirListing/up-XP.png (dirListing/up-XP.png)
- skin/classic/global/icons/autoscroll.png (icons/autoscroll.png)
- skin/classic/global/icons/autoscroll-XP.png (icons/autoscroll-XP.png)
- skin/classic/global/icons/blacklist_favicon-XP.png (icons/blacklist_favicon-XP.png)
- skin/classic/global/icons/blacklist_large-XP.png (icons/blacklist_large-XP.png)
- skin/classic/global/icons/Error-XP.png (icons/Error-XP.png)
- skin/classic/global/icons/error-16-XP.png (icons/error-16-XP.png)
- skin/classic/global/icons/error-64-XP.png (icons/error-64-XP.png)
- skin/classic/global/icons/folder-item-XP.png (icons/folder-item-XP.png)
- skin/classic/global/icons/information-16-XP.png (icons/information-16-XP.png)
- skin/classic/global/icons/information-24-XP.png (icons/information-24-XP.png)
- skin/classic/global/icons/information-32-XP.png (icons/information-32-XP.png)
- skin/classic/global/icons/Print-preview-XP.png (icons/Print-preview-XP.png)
- skin/classic/global/icons/Portrait-XP.png (icons/Portrait-XP.png)
- skin/classic/global/icons/Landscape-XP.png (icons/Landscape-XP.png)
- skin/classic/global/icons/Question-XP.png (icons/Question-XP.png)
- skin/classic/global/icons/question-16-XP.png (icons/question-16-XP.png)
- skin/classic/global/icons/question-64-XP.png (icons/question-64-XP.png)
- skin/classic/global/icons/Search-close-XP.png (icons/Search-close-XP.png)
- skin/classic/global/icons/Search-glass-XP.png (icons/Search-glass-XP.png)
- skin/classic/global/icons/sslWarning-XP.png (icons/sslWarning-XP.png)
- skin/classic/global/icons/Warning-XP.png (icons/Warning-XP.png)
- skin/classic/global/icons/warning-large-XP.png (icons/warning-large-XP.png)
- skin/classic/global/icons/warning-16-XP.png (icons/warning-16-XP.png)
- skin/classic/global/icons/warning-64-XP.png (icons/warning-64-XP.png)
- skin/classic/global/icons/windowControls-XP.png (icons/windowControls-XP.png)
- skin/classic/global/toolbar/spring-XP.png (toolbar/spring-XP.png)
- skin/classic/global/tree/sort-asc-XP.png (tree/sort-asc-XP.png)
- skin/classic/global/tree/sort-dsc-XP.png (tree/sort-dsc-XP.png)
- skin/classic/global/tree/twisty.svg (tree/twisty.svg)
- skin/classic/global/tree/twisty-XP.svg (tree/twisty-XP.svg)
- skin/classic/global/tree/twisty-Vista78.svg (tree/twisty-Vista78.svg)
+ skin/classic/global/icons/autoscroll.png (icons/autoscroll.png)
+ skin/classic/global/tree/twisty.svg (tree/twisty.svg)
+ skin/classic/global/tree/twisty-preWin10.svg (tree/twisty-preWin10.svg)
#ifdef MOZ_PHOENIX
[browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
#elif MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
#endif
-% override chrome://global/skin/dirListing/folder.png chrome://global/skin/dirListing/folder-XP.png osversion<6
-% override chrome://global/skin/dirListing/local.png chrome://global/skin/dirListing/local-XP.png osversion<6
-% override chrome://global/skin/dirListing/remote.png chrome://global/skin/dirListing/remote-XP.png osversion<6
-% override chrome://global/skin/dirListing/up.png chrome://global/skin/dirListing/up-XP.png osversion<6
-% override chrome://global/skin/icons/autoscroll.png chrome://global/skin/icons/autoscroll-XP.png osversion<6
-% override chrome://global/skin/icons/blacklist_favicon.png chrome://global/skin/icons/blacklist_favicon-XP.png osversion<6
-% override chrome://global/skin/icons/blacklist_large.png chrome://global/skin/icons/blacklist_large-XP.png osversion<6
-% override chrome://global/skin/icons/Error.png chrome://global/skin/icons/Error-XP.png osversion<6
-% override chrome://global/skin/icons/error-16.png chrome://global/skin/icons/error-16-XP.png osversion<6
-% override chrome://global/skin/icons/error-64.png chrome://global/skin/icons/error-64-XP.png osversion<6
-% override chrome://global/skin/icons/folder-item.png chrome://global/skin/icons/folder-item-XP.png osversion<6
-% override chrome://global/skin/icons/information-16.png chrome://global/skin/icons/information-16-XP.png osversion<6
-% override chrome://global/skin/icons/information-24.png chrome://global/skin/icons/information-24-XP.png osversion<6
-% override chrome://global/skin/icons/information-32.png chrome://global/skin/icons/information-32-XP.png osversion<6
-% override chrome://global/skin/icons/Print-preview.png chrome://global/skin/icons/Print-preview-XP.png osversion<6
-% override chrome://global/skin/icons/Portrait.png chrome://global/skin/icons/Portrait-XP.png osversion<6
-% override chrome://global/skin/icons/Landscape.png chrome://global/skin/icons/Landscape-XP.png osversion<6
-% override chrome://global/skin/icons/Question.png chrome://global/skin/icons/Question-XP.png osversion<6
-% override chrome://global/skin/icons/question-16.png chrome://global/skin/icons/question-16-XP.png osversion<6
-% override chrome://global/skin/icons/question-64.png chrome://global/skin/icons/question-64-XP.png osversion<6
-% override chrome://global/skin/icons/Search-close.png chrome://global/skin/icons/Search-close-XP.png osversion<6
-% override chrome://global/skin/icons/Search-glass.png chrome://global/skin/icons/Search-glass-XP.png osversion<6
-% override chrome://global/skin/icons/sslWarning.png chrome://global/skin/icons/sslWarning-XP.png osversion<6
-% override chrome://global/skin/icons/Warning.png chrome://global/skin/icons/Warning-XP.png osversion<6
-% override chrome://global/skin/icons/warning-large.png chrome://global/skin/icons/warning-large-XP.png osversion<6
-% override chrome://global/skin/icons/warning-16.png chrome://global/skin/icons/warning-16-XP.png osversion<6
-% override chrome://global/skin/icons/warning-64.png chrome://global/skin/icons/warning-64-XP.png osversion<6
-% override chrome://global/skin/icons/windowControls.png chrome://global/skin/icons/windowControls-XP.png osversion<6
-% override chrome://global/skin/toolbar/spring.png chrome://global/skin/toolbar/spring-XP.png osversion<6
-% override chrome://global/skin/tree/sort-asc.png chrome://global/skin/tree/sort-asc-XP.png osversion<6
-% override chrome://global/skin/tree/sort-dsc.png chrome://global/skin/tree/sort-dsc-XP.png osversion<6
-% override chrome://global/skin/icons/close.png chrome://global/skin/icons/close-XPVista7.png osversion<=6.1
-% override chrome://global/skin/icons/close@2x.png chrome://global/skin/icons/close-XPVista7@2x.png osversion<=6.1
-% override chrome://global/skin/icons/close-inverted.png chrome://global/skin/icons/close-inverted-XPVista7.png osversion<=6.1
-% override chrome://global/skin/icons/close-inverted@2x.png chrome://global/skin/icons/close-inverted-XPVista7@2x.png osversion<=6.1
+% override chrome://global/skin/icons/close.png chrome://global/skin/icons/close-win7.png osversion<=6.1
+% override chrome://global/skin/icons/close@2x.png chrome://global/skin/icons/close-win7@2x.png osversion<=6.1
+% override chrome://global/skin/icons/close-inverted.png chrome://global/skin/icons/close-inverted-win7.png osversion<=6.1
+% override chrome://global/skin/icons/close-inverted@2x.png chrome://global/skin/icons/close-inverted-win7@2x.png osversion<=6.1
-% override chrome://global/skin/tree/twisty.svg#clsd chrome://global/skin/tree/twisty-Vista78.svg#clsd osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#clsd-rtl chrome://global/skin/tree/twisty-Vista78.svg#clsd-rtl osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#clsd-hover chrome://global/skin/tree/twisty-Vista78.svg#clsd-hover osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#clsd-hover-rtl chrome://global/skin/tree/twisty-Vista78.svg#clsd-hover-rtl osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#open chrome://global/skin/tree/twisty-Vista78.svg#open osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#open-rtl chrome://global/skin/tree/twisty-Vista78.svg#open-rtl osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#open-hover chrome://global/skin/tree/twisty-Vista78.svg#open-hover osversion<=6.3
-% override chrome://global/skin/tree/twisty.svg#open-hover-rtl chrome://global/skin/tree/twisty-Vista78.svg#open-hover-rtl osversion<=6.3
-# to be sure osversion<6 has always higher precedence than osversion<=6.3 we override twisty-Vista78.svg instead of twisty.svg
-% override chrome://global/skin/tree/twisty-Vista78.svg#clsd chrome://global/skin/tree/twisty-XP.svg#clsd osversion<6
-% override chrome://global/skin/tree/twisty-Vista78.svg#open chrome://global/skin/tree/twisty-XP.svg#open osversion<6
+% override chrome://global/skin/tree/twisty.svg#clsd chrome://global/skin/tree/twisty-preWin10.svg#clsd osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#clsd-rtl chrome://global/skin/tree/twisty-preWin10.svg#clsd-rtl osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#clsd-hover chrome://global/skin/tree/twisty-preWin10.svg#clsd-hover osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#clsd-hover-rtl chrome://global/skin/tree/twisty-preWin10.svg#clsd-hover-rtl osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#open chrome://global/skin/tree/twisty-preWin10.svg#open osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#open-rtl chrome://global/skin/tree/twisty-preWin10.svg#open-rtl osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#open-hover chrome://global/skin/tree/twisty-preWin10.svg#open-hover osversion<=6.3
+% override chrome://global/skin/tree/twisty.svg#open-hover-rtl chrome://global/skin/tree/twisty-preWin10.svg#open-hover-rtl osversion<=6.3
diff --git a/toolkit/themes/windows/global/listbox.css b/toolkit/themes/windows/global/listbox.css
index fd60b1693..ddea3f8fa 100644
--- a/toolkit/themes/windows/global/listbox.css
+++ b/toolkit/themes/windows/global/listbox.css
@@ -144,70 +144,67 @@ listheader[sortable="true"]:hover:active {
}
@media (-moz-windows-default-theme) {
- @media not all and (-moz-os-version: windows-xp) {
- listitem {
- --listitem-selectedColor: rgb(217,217,217);
- --listitem-selectedBorder: var(--listitem-selectedColor);
- --listitem-selectedBottomBorder: rgb(204,204,204);
- --listitem-selectedBackground: var(--listitem-selectedColor);
- --listitem-selectedImage: none;
- --listitem-selectedCurrentBorder: rgb(123,195,255);
- --listitem-selectedFocusColor: rgb(205,232,255);
- --listitem-selectedFocusBorder: var(--listitem-selectedFocusColor);
- --listitem-selectedFocusBottomBorder: rgb(165,214,255);
- --listitem-selectedFocusBackground: var(--listitem-selectedFocusColor);
- --listitem-selectedFocusImage: none;
- --listitem-selectedFocusCurrentBorder: var(--listitem-selectedFocusColor);
- --listitem-selectedFocusCurrentBottomBorder: var(--listitem-selectedFocusBottomBorder);
- --listitem-selectedFocusCurrentBackground: var(--listitem-selectedFocusColor);
-
- color: -moz-FieldText;
- margin-inline-start: 1px;
- margin-inline-end: 1px;
- padding-top: 1px;
- padding-bottom: 1px;
- border-width: 1px;
- background-repeat: no-repeat;
- background-size: 100% 100%;
- }
+ listitem {
+ --listitem-selectedColor: rgb(217,217,217);
+ --listitem-selectedBorder: var(--listitem-selectedColor);
+ --listitem-selectedBottomBorder: rgb(204,204,204);
+ --listitem-selectedBackground: var(--listitem-selectedColor);
+ --listitem-selectedImage: none;
+ --listitem-selectedCurrentBorder: rgb(123,195,255);
+ --listitem-selectedFocusColor: rgb(205,232,255);
+ --listitem-selectedFocusBorder: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusBottomBorder: rgb(165,214,255);
+ --listitem-selectedFocusBackground: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusImage: none;
+ --listitem-selectedFocusCurrentBorder: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusCurrentBottomBorder: var(--listitem-selectedFocusBottomBorder);
+ --listitem-selectedFocusCurrentBackground: var(--listitem-selectedFocusColor);
+
+ color: -moz-FieldText;
+ margin-inline-start: 1px;
+ margin-inline-end: 1px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ border-width: 1px;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ }
- listitem[selected="true"] {
- border-top-color: var(--listitem-selectedBorder);
- border-right-color: var(--listitem-selectedBorder);
- border-left-color: var(--listitem-selectedBorder);
- border-bottom-color: var(--listitem-selectedBottomBorder);
- background-image: var(--listitem-selectedImage);
- background-color: var(--listitem-selectedBackground);
- color: -moz-DialogText;
- }
+ listitem[selected="true"] {
+ border-top-color: var(--listitem-selectedBorder);
+ border-right-color: var(--listitem-selectedBorder);
+ border-left-color: var(--listitem-selectedBorder);
+ border-bottom-color: var(--listitem-selectedBottomBorder);
+ background-image: var(--listitem-selectedImage);
+ background-color: var(--listitem-selectedBackground);
+ color: -moz-DialogText;
+ }
- listbox:focus > listitem[selected="true"] {
- border-top-color: var(--listitem-selectedFocusBorder);
- border-right-color: var(--listitem-selectedFocusBorder);
- border-left-color: var(--listitem-selectedFocusBorder);
- border-bottom-color: var(--listitem-selectedFocusBottomBorder);
- background-image: var(--listitem-selectedFocusImage);
- background-color: var(--listitem-selectedFocusBackground);
- color: -moz-DialogText;
- }
+ listbox:focus > listitem[selected="true"] {
+ border-top-color: var(--listitem-selectedFocusBorder);
+ border-right-color: var(--listitem-selectedFocusBorder);
+ border-left-color: var(--listitem-selectedFocusBorder);
+ border-bottom-color: var(--listitem-selectedFocusBottomBorder);
+ background-image: var(--listitem-selectedFocusImage);
+ background-color: var(--listitem-selectedFocusBackground);
+ color: -moz-DialogText;
+ }
- listbox:focus > listitem[current="true"] {
- border-color: var(--listitem-selectedCurrentBorder);
- outline: none;
- }
+ listbox:focus > listitem[current="true"] {
+ border-color: var(--listitem-selectedCurrentBorder);
+ outline: none;
+ }
- listbox:focus > listitem[selected="true"][current="true"] {
- border-top-color: var(--listitem-selectedFocusCurrentBorder);
- border-right-color: var(--listitem-selectedFocusCurrentBorder);
- border-left-color: var(--listitem-selectedFocusCurrentBorder);
- border-bottom-color: var(--listitem-selectedFocusCurrentBottomBorder);
- background-color: var(--listitem-selectedFocusCurrentBackground);
- outline: none;
- }
+ listbox:focus > listitem[selected="true"][current="true"] {
+ border-top-color: var(--listitem-selectedFocusCurrentBorder);
+ border-right-color: var(--listitem-selectedFocusCurrentBorder);
+ border-left-color: var(--listitem-selectedFocusCurrentBorder);
+ border-bottom-color: var(--listitem-selectedFocusCurrentBottomBorder);
+ background-color: var(--listitem-selectedFocusCurrentBackground);
+ outline: none;
}
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+ @media (-moz-os-version: windows-win7) {
listitem {
--listitem-selectedBottomBorder: var(--listitem-selectedColor);
--listitem-selectedBackground: rgba(190,190,190,.15);
diff --git a/toolkit/themes/windows/global/menu.css b/toolkit/themes/windows/global/menu.css
index 2a228251d..c22aedd7a 100644
--- a/toolkit/themes/windows/global/menu.css
+++ b/toolkit/themes/windows/global/menu.css
@@ -148,15 +148,13 @@ menubar > menu:-moz-lwtheme[_moz-menuactive="true"]:not([disabled="true"]) {
}
@media (-moz-windows-default-theme) {
- @media not all and (-moz-os-version: windows-xp) {
- menubar > menu:-moz-lwtheme {
- -moz-appearance: menuitem;
- }
-
- menubar > menu:-moz-lwtheme[_moz-menuactive="true"]:not([disabled="true"]) {
- color: inherit !important;
- text-shadow: inherit;
- }
+ menubar > menu:-moz-lwtheme {
+ -moz-appearance: menuitem;
+ }
+
+ menubar > menu:-moz-lwtheme[_moz-menuactive="true"]:not([disabled="true"]) {
+ color: inherit !important;
+ text-shadow: inherit;
}
}
diff --git a/toolkit/themes/windows/global/menulist.css b/toolkit/themes/windows/global/menulist.css
index 9480bffda..de24dbdde 100644
--- a/toolkit/themes/windows/global/menulist.css
+++ b/toolkit/themes/windows/global/menulist.css
@@ -82,12 +82,6 @@ menulist:-moz-focusring:not([open="true"]) > .menulist-label-box {
border: 1px dotted ThreeDDarkShadow;
}
-@media (-moz-os-version: windows-xp) {
- menulist:-moz-focusring:not([open="true"]) > .menulist-label-box {
- border: 1px dotted #F5DB95;
- }
-}
-
/* ..... disabled state ..... */
menulist[disabled="true"] {
@@ -113,31 +107,29 @@ html|*.menulist-editable-input {
}
@media (-moz-windows-default-theme) {
- @media not all and (-moz-os-version: windows-xp) {
- .menulist-label-box {
- background-color: transparent !important;
- color: inherit !important;
- }
-
- .menulist-label {
- margin-top: -1px !important;
- margin-bottom: -1px !important;
- margin-inline-start: 0 !important;
- }
-
- .menulist-description {
- margin-inline-start: 1ex !important;
- }
-
- menulist:not([editable="true"]) > .menulist-dropmarker {
- margin-top: -2px;
- margin-inline-start: 3px;
- margin-inline-end: -3px;
- }
-
- .menulist-icon {
- margin-top: -1px;
- margin-bottom: -1px;
- }
+ .menulist-label-box {
+ background-color: transparent !important;
+ color: inherit !important;
+ }
+
+ .menulist-label {
+ margin-top: -1px !important;
+ margin-bottom: -1px !important;
+ margin-inline-start: 0 !important;
+ }
+
+ .menulist-description {
+ margin-inline-start: 1ex !important;
+ }
+
+ menulist:not([editable="true"]) > .menulist-dropmarker {
+ margin-top: -2px;
+ margin-inline-start: 3px;
+ margin-inline-end: -3px;
+ }
+
+ .menulist-icon {
+ margin-top: -1px;
+ margin-bottom: -1px;
}
}
diff --git a/toolkit/themes/windows/global/popup.css b/toolkit/themes/windows/global/popup.css
index 010e85a93..e5c3c4500 100644
--- a/toolkit/themes/windows/global/popup.css
+++ b/toolkit/themes/windows/global/popup.css
@@ -58,9 +58,7 @@ panel[type="arrow"][side="right"] {
}
%ifdef XP_WIN
-@media (-moz-os-version: windows-xp),
- (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
+@media (-moz-os-version: windows-win7) {
%endif
.panel-arrowcontent {
border-radius: 4px;
diff --git a/toolkit/themes/windows/global/textbox.css b/toolkit/themes/windows/global/textbox.css
index 78ca7f320..7b136a0ea 100644
--- a/toolkit/themes/windows/global/textbox.css
+++ b/toolkit/themes/windows/global/textbox.css
@@ -37,10 +37,8 @@ html|*.textbox-textarea {
}
@media (-moz-windows-default-theme) {
- @media not all and (-moz-os-version: windows-xp) {
- textbox html|*.textbox-input::placeholder {
- font-style: italic;
- }
+ textbox html|*.textbox-input::placeholder {
+ font-style: italic;
}
}
diff --git a/toolkit/themes/windows/global/toolbar/spring-XP.png b/toolkit/themes/windows/global/toolbar/spring-XP.png
deleted file mode 100644
index e20f602c2..000000000
--- a/toolkit/themes/windows/global/toolbar/spring-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/toolbarbutton.css b/toolkit/themes/windows/global/toolbarbutton.css
index e96e529cf..ac3c7ba4a 100644
--- a/toolkit/themes/windows/global/toolbarbutton.css
+++ b/toolkit/themes/windows/global/toolbarbutton.css
@@ -84,17 +84,11 @@ toolbarbutton[checked="true"]:not([disabled="true"]) {
text-shadow: none;
}
- toolbarbutton:-moz-lwtheme:not(:hover):not([checked="true"]):not([open="true"]):not([disabled="true"]) {
+ toolbarbutton:-moz-lwtheme:not([disabled="true"]) {
color: inherit;
text-shadow: inherit;
}
- @media not all and (-moz-os-version: windows-xp) {
- toolbarbutton:-moz-lwtheme:not([disabled="true"]) {
- color: inherit;
- text-shadow: inherit;
- }
- }
}
@media not all and (-moz-windows-default-theme) {
diff --git a/toolkit/themes/windows/global/tree.css b/toolkit/themes/windows/global/tree.css
index 130096824..2b4957c6c 100644
--- a/toolkit/themes/windows/global/tree.css
+++ b/toolkit/themes/windows/global/tree.css
@@ -310,7 +310,7 @@ treecol:not([hideheader="true"]) > .treecol-sortdirection[sortDirection="descend
/* ::::: twisty ::::: */
treechildren::-moz-tree-twisty {
- padding-inline-end: 4px;
+ padding-inline-end: 1px;
padding-top: 1px;
width: 9px; /* The image's width is 9 pixels */
list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd");
@@ -320,8 +320,32 @@ treechildren::-moz-tree-twisty(open) {
list-style-image: url("chrome://global/skin/tree/twisty.svg#open");
}
+treechildren::-moz-tree-twisty(hover) {
+ list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd-hover");
+}
+
+treechildren::-moz-tree-twisty(hover, open) {
+ list-style-image: url("chrome://global/skin/tree/twisty.svg#open-hover");
+}
+
+treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty {
+ list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd-rtl");
+}
+
+treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open) {
+ list-style-image: url("chrome://global/skin/tree/twisty.svg#open-rtl");
+}
+
+treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(hover) {
+ list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd-hover-rtl");
+}
+
+treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(hover, open) {
+ list-style-image: url("chrome://global/skin/tree/twisty.svg#open-hover-rtl");
+}
+
treechildren::-moz-tree-indentation {
- width: 18px;
+ width: 12px;
}
/* ::::: gridline style ::::: */
@@ -370,233 +394,196 @@ treechildren::-moz-tree-cell-text(active, selected, editing) {
}
%ifdef XP_WIN
-@media not all and (-moz-os-version: windows-xp) {
- /* ::::: twisty ::::: */
-
- treechildren::-moz-tree-indentation {
- width: 12px;
+@media (-moz-windows-default-theme) {
+ treechildren {
+ --treechildren-outline: none;
+ --treechildren-2ndBorderColor: rgba(255,255,255,.4);
+ --treechildren-selectedColor: rgb(217,217,217);
+ --treechildren-focusColor: rgb(123,195,255);
+ --treechildren-selectedFocusColor: rgb(205,232,255);
+ --treechildren-currentColor: rgb(125,162,206);
+ --treechildren-hoverColor: rgb(229,243,255);
+ --treechildren-selectedBorder: var(--treechildren-selectedColor);
+ --treechildren-selectedBottomBorder: rgb(204,204,204);
+ --treechildren-selectedImage: linear-gradient(rgb(217,217,217), rgb(217,217,217));
+ --treechildren-selectedBackground: transparent;
+ --treechildren-currentFocusBorder: var(--treechildren-focusColor);
+ --treechildren-currentFocusBottomBorder: var(--treechildren-focusColor);
+ --treechildren-selectedFocusBorder: var(--treechildren-selectedFocusColor);
+ --treechildren-selectedFocusBottomBorder: rgb(165,214,255);
+ --treechildren-selectedFocusImage: none;
+ --treechildren-selectedFocusBackground: var(--treechildren-selectedFocusColor);
+ --treechildren-selectedFocusCurrentBorder: var(--treechildren-focusColor);
+ --treechildren-selectedFocusCurrentBottomBorder: var(--treechildren-focusColor);
+ --treechildren-selectedFocusCurrentImage: linear-gradient(rgb(205,232,255), rgb(205,232,255));
+ --treechildren-hoverBorder: var(--treechildren-hoverColor);
+ --treechildren-hoverBottomBorder: var(--treechildren-hoverColor);
+ --treechildren-hoverImage: linear-gradient(rgb(229,243,255), rgb(229,243,255));
+ --treechildren-hoverCurrentBorder: var(--treechildren-currentColor);
+ --treechildren-hoverCurrentBottomBorder: var(--treechildren-currentColor);
+ --treechildren-hoverCurrentImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.16));
+ --treechildren-hoverSelectedBorder: var(--treechildren-focusColor);
+ --treechildren-hoverSelectedBottomBorder: var(--treechildren-focusColor);
+ --treechildren-hoverSelectedImage: linear-gradient(rgb(205,232,255), rgb(205,232,255));
}
- treechildren::-moz-tree-twisty {
- padding-inline-end: 1px;
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row {
+ height: 1.8em;
+ color: -moz-FieldText;
+ margin-inline-start: 1px;
+ margin-inline-end: 1px;
+ border-width: 1px;
+ border-color: transparent;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
}
- treechildren::-moz-tree-twisty(hover) {
- list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd-hover");
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected) {
+ -moz-border-top-colors: var(--treechildren-selectedBorder);
+ -moz-border-right-colors: var(--treechildren-selectedBorder);
+ -moz-border-left-colors: var(--treechildren-selectedBorder);
+ -moz-border-bottom-colors: var(--treechildren-selectedBottomBorder);
+ background-image: var(--treechildren-selectedImage);
+ background-color: var(--treechildren-selectedBackground);
+ outline: var(--treechildren-outline);
}
- treechildren::-moz-tree-twisty(hover, open) {
- list-style-image: url("chrome://global/skin/tree/twisty.svg#open-hover");
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(current, focus) {
+ border-style: solid;
+ -moz-border-top-colors: var(--treechildren-currentFocusBorder);
+ -moz-border-right-colors: var(--treechildren-currentFocusBorder);
+ -moz-border-left-colors: var(--treechildren-currentFocusBorder);
+ -moz-border-bottom-colors: var(--treechildren-currentFocusBottomBorder);
+ outline: var(--treechildren-outline);
}
- treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty {
- list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd-rtl");
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected, focus),
+ treechildren::-moz-tree-row(dropOn) {
+ -moz-border-top-colors: var(--treechildren-selectedFocusBorder);
+ -moz-border-right-colors: var(--treechildren-selectedFocusBorder);
+ -moz-border-left-colors: var(--treechildren-selectedFocusBorder);
+ -moz-border-bottom-colors: var(--treechildren-selectedFocusBottomBorder);
+ background-image: var(--treechildren-selectedFocusImage);
+ background-color: var(--treechildren-selectedFocusBackground);
}
- treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open) {
- list-style-image: url("chrome://global/skin/tree/twisty.svg#open-rtl");
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected, current, focus) {
+ border-style: solid;
+ -moz-border-top-colors: var(--treechildren-selectedFocusCurrentBorder);
+ -moz-border-right-colors: var(--treechildren-selectedFocusCurrentBorder);
+ -moz-border-left-colors: var(--treechildren-selectedFocusCurrentBorder);
+ -moz-border-bottom-colors: var(--treechildren-selectedFocusCurrentBottomBorder);
+ background-image: var(--treechildren-selectedFocusCurrentImage);
}
- treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(hover) {
- list-style-image: url("chrome://global/skin/tree/twisty.svg#clsd-hover-rtl");
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(hover) {
+ -moz-border-top-colors: var(--treechildren-hoverBorder);
+ -moz-border-right-colors: var(--treechildren-hoverBorder);
+ -moz-border-left-colors: var(--treechildren-hoverBorder);
+ -moz-border-bottom-colors: var(--treechildren-hoverBottomBorder);
+ background-image: var(--treechildren-hoverImage);
+ outline: var(--treechildren-outline);
}
- treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(hover, open) {
- list-style-image: url("chrome://global/skin/tree/twisty.svg#open-hover-rtl");
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(hover, current) {
+ -moz-border-top-colors: var(--treechildren-hoverCurrentBorder);
+ -moz-border-right-colors: var(--treechildren-hoverCurrentBorder);
+ -moz-border-left-colors: var(--treechildren-hoverCurrentBorder);
+ -moz-border-bottom-colors: var(--treechildren-hoverCurrentBottomBorder);
+ background-image: var(--treechildren-hoverCurrentImage);
}
- @media (-moz-windows-default-theme) {
- treechildren {
- --treechildren-outline: none;
- --treechildren-2ndBorderColor: rgba(255,255,255,.4);
- --treechildren-selectedColor: rgb(217,217,217);
- --treechildren-focusColor: rgb(123,195,255);
- --treechildren-selectedFocusColor: rgb(205,232,255);
- --treechildren-currentColor: rgb(125,162,206);
- --treechildren-hoverColor: rgb(229,243,255);
- --treechildren-selectedBorder: var(--treechildren-selectedColor);
- --treechildren-selectedBottomBorder: rgb(204,204,204);
- --treechildren-selectedImage: linear-gradient(rgb(217,217,217), rgb(217,217,217));
- --treechildren-selectedBackground: transparent;
- --treechildren-currentFocusBorder: var(--treechildren-focusColor);
- --treechildren-currentFocusBottomBorder: var(--treechildren-focusColor);
- --treechildren-selectedFocusBorder: var(--treechildren-selectedFocusColor);
- --treechildren-selectedFocusBottomBorder: rgb(165,214,255);
- --treechildren-selectedFocusImage: none;
- --treechildren-selectedFocusBackground: var(--treechildren-selectedFocusColor);
- --treechildren-selectedFocusCurrentBorder: var(--treechildren-focusColor);
- --treechildren-selectedFocusCurrentBottomBorder: var(--treechildren-focusColor);
- --treechildren-selectedFocusCurrentImage: linear-gradient(rgb(205,232,255), rgb(205,232,255));
- --treechildren-hoverBorder: var(--treechildren-hoverColor);
- --treechildren-hoverBottomBorder: var(--treechildren-hoverColor);
- --treechildren-hoverImage: linear-gradient(rgb(229,243,255), rgb(229,243,255));
- --treechildren-hoverCurrentBorder: var(--treechildren-currentColor);
- --treechildren-hoverCurrentBottomBorder: var(--treechildren-currentColor);
- --treechildren-hoverCurrentImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.16));
- --treechildren-hoverSelectedBorder: var(--treechildren-focusColor);
- --treechildren-hoverSelectedBottomBorder: var(--treechildren-focusColor);
- --treechildren-hoverSelectedImage: linear-gradient(rgb(205,232,255), rgb(205,232,255));
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row {
- height: 1.8em;
- color: -moz-FieldText;
- margin-inline-start: 1px;
- margin-inline-end: 1px;
- border-width: 1px;
- border-color: transparent;
- background-repeat: no-repeat;
- background-size: 100% 100%;
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected) {
- -moz-border-top-colors: var(--treechildren-selectedBorder);
- -moz-border-right-colors: var(--treechildren-selectedBorder);
- -moz-border-left-colors: var(--treechildren-selectedBorder);
- -moz-border-bottom-colors: var(--treechildren-selectedBottomBorder);
- background-image: var(--treechildren-selectedImage);
- background-color: var(--treechildren-selectedBackground);
- outline: var(--treechildren-outline);
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(current, focus) {
- border-style: solid;
- -moz-border-top-colors: var(--treechildren-currentFocusBorder);
- -moz-border-right-colors: var(--treechildren-currentFocusBorder);
- -moz-border-left-colors: var(--treechildren-currentFocusBorder);
- -moz-border-bottom-colors: var(--treechildren-currentFocusBottomBorder);
- outline: var(--treechildren-outline);
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected, focus),
- treechildren::-moz-tree-row(dropOn) {
- -moz-border-top-colors: var(--treechildren-selectedFocusBorder);
- -moz-border-right-colors: var(--treechildren-selectedFocusBorder);
- -moz-border-left-colors: var(--treechildren-selectedFocusBorder);
- -moz-border-bottom-colors: var(--treechildren-selectedFocusBottomBorder);
- background-image: var(--treechildren-selectedFocusImage);
- background-color: var(--treechildren-selectedFocusBackground);
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected, current, focus) {
- border-style: solid;
- -moz-border-top-colors: var(--treechildren-selectedFocusCurrentBorder);
- -moz-border-right-colors: var(--treechildren-selectedFocusCurrentBorder);
- -moz-border-left-colors: var(--treechildren-selectedFocusCurrentBorder);
- -moz-border-bottom-colors: var(--treechildren-selectedFocusCurrentBottomBorder);
- background-image: var(--treechildren-selectedFocusCurrentImage);
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(hover) {
- -moz-border-top-colors: var(--treechildren-hoverBorder);
- -moz-border-right-colors: var(--treechildren-hoverBorder);
- -moz-border-left-colors: var(--treechildren-hoverBorder);
- -moz-border-bottom-colors: var(--treechildren-hoverBottomBorder);
- background-image: var(--treechildren-hoverImage);
- outline: var(--treechildren-outline);
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(hover, current) {
- -moz-border-top-colors: var(--treechildren-hoverCurrentBorder);
- -moz-border-right-colors: var(--treechildren-hoverCurrentBorder);
- -moz-border-left-colors: var(--treechildren-hoverCurrentBorder);
- -moz-border-bottom-colors: var(--treechildren-hoverCurrentBottomBorder);
- background-image: var(--treechildren-hoverCurrentImage);
- }
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row(hover, selected) {
+ -moz-border-top-colors: var(--treechildren-hoverSelectedBorder);
+ -moz-border-right-colors: var(--treechildren-hoverSelectedBorder);
+ -moz-border-left-colors: var(--treechildren-hoverSelectedBorder);
+ -moz-border-bottom-colors: var(--treechildren-hoverSelectedBottomBorder);
+ background-image: var(--treechildren-hoverSelectedImage);
+ }
- treechildren:not(.autocomplete-treebody)::-moz-tree-row(hover, selected) {
- -moz-border-top-colors: var(--treechildren-hoverSelectedBorder);
- -moz-border-right-colors: var(--treechildren-hoverSelectedBorder);
- -moz-border-left-colors: var(--treechildren-hoverSelectedBorder);
- -moz-border-bottom-colors: var(--treechildren-hoverSelectedBottomBorder);
- background-image: var(--treechildren-hoverSelectedImage);
- }
+ tree[disabled="true"] > treechildren::-moz-tree-row {
+ background: none;
+ -moz-border-top-colors: transparent;
+ -moz-border-right-colors: transparent;
+ -moz-border-left-colors: transparent;
+ -moz-border-bottom-colors: transparent;
+ }
- tree[disabled="true"] > treechildren::-moz-tree-row {
- background: none;
- -moz-border-top-colors: transparent;
- -moz-border-right-colors: transparent;
- -moz-border-left-colors: transparent;
- -moz-border-bottom-colors: transparent;
- }
+ treechildren::-moz-tree-cell(dropOn) {
+ background-image: none;
+ background-color: transparent;
+ border-radius: 0;
+ }
- treechildren::-moz-tree-cell(dropOn) {
- background-image: none;
- background-color: transparent;
- border-radius: 0;
- }
+ treechildren::-moz-tree-cell-text(primary, dropOn) {
+ color: -moz-FieldText;
+ }
- treechildren::-moz-tree-cell-text(primary, dropOn) {
- color: -moz-FieldText;
- }
+ treechildren:not(.autocomplete-treebody)::-moz-tree-cell-text {
+ padding-bottom: initial;
+ border-color: transparent;
+ background-color: transparent;
+ }
- treechildren:not(.autocomplete-treebody)::-moz-tree-cell-text {
- padding-bottom: initial;
- border-color: transparent;
- background-color: transparent;
- }
+ treechildren:not(.autocomplete-treebody)::-moz-tree-cell-text(selected, focus) {
+ color: -moz-DialogText;
+ }
- treechildren:not(.autocomplete-treebody)::-moz-tree-cell-text(selected, focus) {
- color: -moz-DialogText;
+ @media (-moz-os-version: windows-win7) {
+ treechildren {
+ --treechildren-outline: 1px solid var(--treechildren-2ndBorderColor);
+ --treechildren-2ndBottomBorderColor: rgba(255,255,255,.6);
+ --treechildren-selectedBorder: var(--treechildren-selectedColor) var(--treechildren-2ndBorderColor);
+ --treechildren-selectedBottomBorder: var(--treechildren-selectedColor) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-selectedImage: linear-gradient(rgba(190,190,190,.1), rgba(190,190,190,.4));
+ --treechildren-currentFocusBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
+ --treechildren-currentFocusBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-selectedFocusBorder: rgb(132,172,221) var(--treechildren-2ndBorderColor);
+ --treechildren-selectedFocusBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-selectedFocusImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.375));
+ --treechildren-selectedFocusBackground: transparent;
+ --treechildren-selectedFocusCurrentBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
+ --treechildren-selectedFocusCurrentBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-selectedFocusCurrentImage: linear-gradient(rgba(131,183,249,.28), rgba(131,183,249,.5));
+ --treechildren-hoverBorder: rgb(184,214,251) var(--treechildren-2ndBorderColor);
+ --treechildren-hoverBottomBorder: rgb(184,214,251) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-hoverImage: linear-gradient(rgba(131,183,249,.05), rgba(131,183,249,.16));
+ --treechildren-hoverCurrentBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
+ --treechildren-hoverCurrentBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-hoverCurrentImage: linear-gradient(rgba(131,183,249,.05), rgba(131,183,249,.16));
+ --treechildren-hoverSelectedBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
+ --treechildren-hoverSelectedBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
+ --treechildren-hoverSelectedImage: linear-gradient(rgba(131,183,249,.28), rgba(131,183,249,.5));
}
- @media (-moz-os-version: windows-vista),
- (-moz-os-version: windows-win7) {
- treechildren {
- --treechildren-outline: 1px solid var(--treechildren-2ndBorderColor);
- --treechildren-2ndBottomBorderColor: rgba(255,255,255,.6);
- --treechildren-selectedBorder: var(--treechildren-selectedColor) var(--treechildren-2ndBorderColor);
- --treechildren-selectedBottomBorder: var(--treechildren-selectedColor) var(--treechildren-2ndBottomBorderColor);
- --treechildren-selectedImage: linear-gradient(rgba(190,190,190,.1), rgba(190,190,190,.4));
- --treechildren-currentFocusBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
- --treechildren-currentFocusBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
- --treechildren-selectedFocusBorder: rgb(132,172,221) var(--treechildren-2ndBorderColor);
- --treechildren-selectedFocusBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
- --treechildren-selectedFocusImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.375));
- --treechildren-selectedFocusBackground: transparent;
- --treechildren-selectedFocusCurrentBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
- --treechildren-selectedFocusCurrentBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
- --treechildren-selectedFocusCurrentImage: linear-gradient(rgba(131,183,249,.28), rgba(131,183,249,.5));
- --treechildren-hoverBorder: rgb(184,214,251) var(--treechildren-2ndBorderColor);
- --treechildren-hoverBottomBorder: rgb(184,214,251) var(--treechildren-2ndBottomBorderColor);
- --treechildren-hoverImage: linear-gradient(rgba(131,183,249,.05), rgba(131,183,249,.16));
- --treechildren-hoverCurrentBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
- --treechildren-hoverCurrentBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
- --treechildren-hoverCurrentImage: linear-gradient(rgba(131,183,249,.05), rgba(131,183,249,.16));
- --treechildren-hoverSelectedBorder: var(--treechildren-currentColor) var(--treechildren-2ndBorderColor);
- --treechildren-hoverSelectedBottomBorder: var(--treechildren-currentColor) var(--treechildren-2ndBottomBorderColor);
- --treechildren-hoverSelectedImage: linear-gradient(rgba(131,183,249,.28), rgba(131,183,249,.5));
- }
-
- treechildren:not(.autocomplete-treebody)::-moz-tree-row {
- border-width: 2px;
- border-radius: 3px;
- -moz-outline-radius: 3px;
- }
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row {
+ border-width: 2px;
+ border-radius: 3px;
+ -moz-outline-radius: 3px;
}
+ }
- @media (-moz-os-version: windows-win8) {
- treechildren {
- --treechildren-outline: 1px solid var(--treechildren-2ndBorderColor);
- --treechildren-selectedBorder: var(--treechildren-selectedColor);
- --treechildren-selectedBottomBorder: var(--treechildren-selectedColor);
- --treechildren-selectedImage: linear-gradient(rgba(190,190,190,.4), rgba(190,190,190,.4));
- --treechildren-currentFocusBorder: var(--treechildren-currentColor);
- --treechildren-currentFocusBottomBorder: var(--treechildren-currentColor);
- --treechildren-selectedFocusBorder: rgb(132,172,221) var(--treechildren-2ndBorderColor);
- --treechildren-selectedFocusBottomBorder: var(--treechildren-currentColor);
- --treechildren-selectedFocusImage: linear-gradient(rgba(131,183,249,.375), rgba(131,183,249,.375));
- --treechildren-selectedFocusBackground: transparent;
- --treechildren-selectedFocusCurrentBorder: var(--treechildren-currentColor);
- --treechildren-selectedFocusCurrentBottomBorder: var(--treechildren-currentColor);
- --treechildren-selectedFocusCurrentImage: linear-gradient(rgba(131,183,249,.5), rgba(131,183,249,.5));
- --treechildren-hoverBorder: rgb(184,214,251);
- --treechildren-hoverBottomBorder: rgb(184,214,251);
- --treechildren-hoverImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.16));
- --treechildren-hoverSelectedBorder: var(--treechildren-currentColor);
- --treechildren-hoverSelectedBottomBorder: var(--treechildren-currentColor);
- --treechildren-hoverSelectedImage: linear-gradient(rgba(131,183,249,.5), rgba(131,183,249,.5));
- }
+ @media (-moz-os-version: windows-win8) {
+ treechildren {
+ --treechildren-outline: 1px solid var(--treechildren-2ndBorderColor);
+ --treechildren-selectedBorder: var(--treechildren-selectedColor);
+ --treechildren-selectedBottomBorder: var(--treechildren-selectedColor);
+ --treechildren-selectedImage: linear-gradient(rgba(190,190,190,.4), rgba(190,190,190,.4));
+ --treechildren-currentFocusBorder: var(--treechildren-currentColor);
+ --treechildren-currentFocusBottomBorder: var(--treechildren-currentColor);
+ --treechildren-selectedFocusBorder: rgb(132,172,221) var(--treechildren-2ndBorderColor);
+ --treechildren-selectedFocusBottomBorder: var(--treechildren-currentColor);
+ --treechildren-selectedFocusImage: linear-gradient(rgba(131,183,249,.375), rgba(131,183,249,.375));
+ --treechildren-selectedFocusBackground: transparent;
+ --treechildren-selectedFocusCurrentBorder: var(--treechildren-currentColor);
+ --treechildren-selectedFocusCurrentBottomBorder: var(--treechildren-currentColor);
+ --treechildren-selectedFocusCurrentImage: linear-gradient(rgba(131,183,249,.5), rgba(131,183,249,.5));
+ --treechildren-hoverBorder: rgb(184,214,251);
+ --treechildren-hoverBottomBorder: rgb(184,214,251);
+ --treechildren-hoverImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.16));
+ --treechildren-hoverSelectedBorder: var(--treechildren-currentColor);
+ --treechildren-hoverSelectedBottomBorder: var(--treechildren-currentColor);
+ --treechildren-hoverSelectedImage: linear-gradient(rgba(131,183,249,.5), rgba(131,183,249,.5));
}
}
}
diff --git a/toolkit/themes/windows/global/tree/sort-asc-XP.png b/toolkit/themes/windows/global/tree/sort-asc-XP.png
deleted file mode 100644
index 64d077a8f..000000000
--- a/toolkit/themes/windows/global/tree/sort-asc-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/tree/sort-dsc-XP.png b/toolkit/themes/windows/global/tree/sort-dsc-XP.png
deleted file mode 100644
index 9c4a82587..000000000
--- a/toolkit/themes/windows/global/tree/sort-dsc-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/global/tree/twisty-XP.svg b/toolkit/themes/windows/global/tree/twisty-XP.svg
deleted file mode 100644
index 66219d513..000000000
--- a/toolkit/themes/windows/global/tree/twisty-XP.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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" xmlns:xlink="http://www.w3.org/1999/xlink" width="9" height="9">
- <style>
- use:not(:target) {
- display: none;
- }
- use {
- stroke: #000000;
- stroke-width: 1;
- }
- </style>
- <defs>
- <linearGradient id="linearGradient1">
- <stop style="stop-color: #ffffff;" offset="0"/>
- <stop style="stop-color: #e0e0e0;" offset="1"/>
- </linearGradient>
- <linearGradient id="linearGradient2">
- <stop style="stop-color: #c9c9c9;" offset="0"/>
- <stop style="stop-color: #f8f8f8;" offset="1"/>
- </linearGradient>
- <linearGradient id="gradient1" xlink:href="#linearGradient1" gradientUnits="userSpaceOnUse" x1="4.5" y1="2" x2="4.5" y2="7"/>
- <linearGradient id="gradient2" xlink:href="#linearGradient2" gradientUnits="userSpaceOnUse" x1="4.5" y1="6" x2="4.5" y2="3"/>
- <path id="clsd-shape" d="m 2,4.5 5,0 M 4.5,2 l 0,5"/>
- <path id="open-shape" d="m 2,4.5 5,0"/>
- </defs>
- <rect style="fill: url(#gradient1); stroke: #5d5cc2; stroke-linejoin: round; stroke-opacity: 0.8" width="8" height="8" x="0.5" y="0.5"/>
- <rect style="fill: url(#gradient2);" width="5" height="5" x="2" y="2"/>
- <use id="clsd" xlink:href="#clsd-shape"/>
- <use id="open" xlink:href="#open-shape"/>
-</svg>
diff --git a/toolkit/themes/windows/global/tree/twisty-Vista78.svg b/toolkit/themes/windows/global/tree/twisty-preWin10.svg
index 0a6908634..0a6908634 100644
--- a/toolkit/themes/windows/global/tree/twisty-Vista78.svg
+++ b/toolkit/themes/windows/global/tree/twisty-preWin10.svg
diff --git a/toolkit/themes/windows/mozapps/downloads/downloadButtons-XP.png b/toolkit/themes/windows/mozapps/downloads/downloadButtons-XP.png
deleted file mode 100644
index d36385ce5..000000000
--- a/toolkit/themes/windows/mozapps/downloads/downloadButtons-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/downloads/downloadIcon-XP.png b/toolkit/themes/windows/mozapps/downloads/downloadIcon-XP.png
deleted file mode 100644
index 8225b6c2d..000000000
--- a/toolkit/themes/windows/mozapps/downloads/downloadIcon-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/about.css b/toolkit/themes/windows/mozapps/extensions/about.css
new file mode 100644
index 000000000..7188c11bf
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/about.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/. */
+
+#genericAbout {
+ padding: 0px;
+ min-height: 200px;
+ max-height: 400px;
+ width: 30em;
+}
+
+#clientBox {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+@media (-moz-windows-compositor) {
+ #genericAbout {
+ -moz-appearance: -moz-win-glass;
+ background: transparent;
+ }
+
+ #clientBox {
+ -moz-appearance: -moz-win-exclude-glass;
+ }
+}
+
+
+.basic-info {
+ padding: 10px;
+}
+
+#extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 64px;
+ max-height: 64px;
+ -moz-margin-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 {
+ -moz-margin-start: 18px;
+}
+
+#extensionCreator, .contributor {
+ margin: 0px;
+}
+
+.sectionTitle {
+ padding: 2px 0px 3px 0px;
+ margin-top: 3px;
+ font-weight: bold;
+}
+
diff --git a/toolkit/themes/windows/mozapps/extensions/alerticon-error.png b/toolkit/themes/windows/mozapps/extensions/alerticon-error.png
new file mode 100644
index 000000000..8740e4911
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/alerticon-error.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/alerticon-info-negative.png b/toolkit/themes/windows/mozapps/extensions/alerticon-info-negative.png
new file mode 100644
index 000000000..2c5f628ab
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/alerticon-info-negative.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/alerticon-info-positive.png b/toolkit/themes/windows/mozapps/extensions/alerticon-info-positive.png
new file mode 100644
index 000000000..a186c6b7a
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/alerticon-info-positive.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/alerticon-warning.png b/toolkit/themes/windows/mozapps/extensions/alerticon-warning.png
new file mode 100644
index 000000000..75ea826f9
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/alerticon-warning.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/blocklist.css b/toolkit/themes/windows/mozapps/extensions/blocklist.css
new file mode 100644
index 000000000..da92102d0
--- /dev/null
+++ b/toolkit/themes/windows/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;
+ -moz-padding-start: 7px;
+ -moz-padding-end: 7px;
+ border-bottom: 1px solid #C0C0C0;
+}
+
+.addonName {
+ font-weight: bold;
+}
+
+.blockedLabel {
+ font-weight: bold;
+ font-style: italic;
+}
diff --git a/toolkit/themes/windows/mozapps/extensions/cancel.png b/toolkit/themes/windows/mozapps/extensions/cancel.png
new file mode 100644
index 000000000..0d98ab235
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/cancel.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-available.png b/toolkit/themes/windows/mozapps/extensions/category-available.png
new file mode 100644
index 000000000..9341f2aa7
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-available.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-dictionaries.png b/toolkit/themes/windows/mozapps/extensions/category-dictionaries.png
new file mode 100644
index 000000000..b26bb7100
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-dictionaries.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-discover.png b/toolkit/themes/windows/mozapps/extensions/category-discover.png
new file mode 100644
index 000000000..af954a613
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-discover.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-experiments.png b/toolkit/themes/windows/mozapps/extensions/category-experiments.png
new file mode 100644
index 000000000..a9d00545e
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-experiments.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-plugins.png b/toolkit/themes/windows/mozapps/extensions/category-plugins.png
new file mode 100644
index 000000000..1fcbf154e
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-plugins.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-recent.png b/toolkit/themes/windows/mozapps/extensions/category-recent.png
new file mode 100644
index 000000000..d65158646
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-recent.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-search.png b/toolkit/themes/windows/mozapps/extensions/category-search.png
new file mode 100644
index 000000000..52e91a7ce
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-search.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-searchengines.png b/toolkit/themes/windows/mozapps/extensions/category-searchengines.png
new file mode 100644
index 000000000..b893cb48a
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-searchengines.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/category-service.png b/toolkit/themes/windows/mozapps/extensions/category-service.png
new file mode 100644
index 000000000..997c8541c
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/category-service.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-16.png b/toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-16.png
new file mode 100644
index 000000000..37e2a5e4c
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/dictionaryGeneric.png b/toolkit/themes/windows/mozapps/extensions/dictionaryGeneric.png
new file mode 100644
index 000000000..b26bb7100
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/dictionaryGeneric.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/discover-logo.png b/toolkit/themes/windows/mozapps/extensions/discover-logo.png
new file mode 100644
index 000000000..cd50735a8
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/discover-logo.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/eula.css b/toolkit/themes/windows/mozapps/extensions/eula.css
new file mode 100644
index 000000000..5fb2c52df
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/eula.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/. */
+
+#icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 48px;
+ max-height: 48px;
+ -moz-margin-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: -moz-FieldText;
+ background-color: -moz-Field;
+ margin: 1em;
+ border: 1px solid;
+ -moz-border-top-colors: ActiveBorder;
+ -moz-border-right-colors: ActiveBorder;
+ -moz-border-bottom-colors: ActiveBorder;
+ -moz-border-left-colors: ActiveBorder;
+}
+
diff --git a/toolkit/themes/windows/mozapps/extensions/experimentGeneric.png b/toolkit/themes/windows/mozapps/extensions/experimentGeneric.png
new file mode 100644
index 000000000..a9d00545e
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/experimentGeneric.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/extensionGeneric-16.png b/toolkit/themes/windows/mozapps/extensions/extensionGeneric-16.png
new file mode 100644
index 000000000..0ddee4c9b
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/extensionGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/extensionGeneric-48.png b/toolkit/themes/windows/mozapps/extensions/extensionGeneric-48.png
new file mode 100644
index 000000000..27418fba8
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/extensionGeneric-48.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/extensionGeneric.png b/toolkit/themes/windows/mozapps/extensions/extensionGeneric.png
new file mode 100644
index 000000000..5a3162363
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/extensionGeneric.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/extensions.css b/toolkit/themes/windows/mozapps/extensions/extensions.css
new file mode 100644
index 000000000..f350f7ca6
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/extensions.css
@@ -0,0 +1,1234 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/inContentUI.css");
+
+
+.nav-button {
+ list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
+}
+
+#forward-btn {
+ -moz-border-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;
+ }
+}
+
+/*** global informations ***/
+#addons-page .global-info-container {
+ background-color: #f3f7fb;
+ border-top-right-radius: 5px;
+ border-top-left-radius: 5px;
+}
+
+/* Plugins aren't yet disabled by safemode (bug 342333),
+ so don't show that warning when viewing plugins. */
+#addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
+#addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning-container {
+ background-color: inherit;
+ background-image: none;
+}
+
+
+/*** 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: #373D48;
+ border: 1px solid #A8B8D1;
+ border-radius: 8px;
+ background-image: linear-gradient(#FFF, #ECF1F7);
+ background-clip: padding-box;
+ box-shadow: 2px 2px 4px #999;
+}
+
+.alert .alert-title {
+ font-weight: bold;
+ font-size: 200%;
+ margin-bottom: 15px;
+}
+
+.alert button {
+ margin: 1em 2em;
+}
+
+.loading {
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+
+/*** category selector ***/
+
+#categories {
+ -moz-appearance: none;
+ border: none;
+ -moz-margin-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;
+}
+
+%ifdef XP_WIN
+@media (-moz-os-version: windows-vista),
+ (-moz-os-version: windows-win7) {
+%endif
+ .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;
+ }
+%ifdef XP_WIN
+}
+%endif
+
+.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: rgba(255, 255, 255, 0.4);
+ color: #252F3B;
+ border-color: #C3CEDF;
+ -moz-border-end-color: #E2E9F2;
+}
+
+.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;
+ -moz-margin-start: 6px;
+}
+
+#category-search > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-search.png");
+}
+#category-discover > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-discover.png");
+}
+#category-locale > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-languages.png");
+}
+#category-searchengine > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-searchengines.png");
+}
+#category-extension > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-extensions.png");
+}
+#category-service > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-service.png");
+}
+#category-theme > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-themes.png");
+}
+#category-plugin > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
+}
+#category-dictionary > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-dictionaries.png");
+}
+#category-experiment > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-experiments.png");
+}
+#category-availableUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
+}
+#category-recentUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
+}
+
+
+/*** header ***/
+
+#header {
+ margin-bottom: 18px;
+}
+
+#header-search {
+ margin: 0;
+}
+
+@media (max-width: 600px) {
+ #header-search {
+ width: 12em;
+ }
+}
+
+@media (-moz-windows-default-theme) {
+ #header-search {
+ -moz-appearance: none;
+ border: 1px solid rgba(0, 0, 0, 0.32);
+ padding-bottom: 2px;
+ background-color: rgba(255, 255, 255, 0.4);
+ }
+
+%ifdef XP_WIN
+@media (-moz-os-version: windows-vista),
+ (-moz-os-version: windows-win7) {
+%endif
+ #header-search {
+ border-radius: 2.5px;
+ }
+%ifdef XP_WIN
+}
+%endif
+
+ #header-search:hover {
+ background-color: rgba(255, 255, 255, .75);
+ }
+
+ #header-search[focused] {
+ background-color: white;
+ }
+}
+
+#header-utils-btn {
+ list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg#utilities");
+ -moz-margin-end: 16px;
+}
+
+@media not all and (-moz-windows-default-theme) {
+ #header-utils-btn {
+ list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg#utilities-native");
+ }
+}
+
+.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.gif");
+}
+
+.sorter[checkState="2"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.sorter .button-icon {
+ -moz-margin-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");
+ -moz-margin-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: 2px solid;
+ -moz-border-top-colors: rgba(0, 0, 0, 0.1) rgba(255, 255, 255, 0.1);
+ border-bottom: 1px solid;
+ -moz-border-bottom-colors: 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;
+ -moz-border-top-colors: rgba(255, 255, 255, 0.1);
+}
+
+.view-pane:not(#search-view) .addon:last-of-type,
+#search-view .addon[last] {
+ border-bottom-width: 2px;
+ -moz-border-bottom-colors: rgba(0, 0, 0, 0.1) rgba(255, 255, 255, 0.1);
+}
+
+.details {
+ cursor: pointer;
+ margin: 0;
+ -moz-margin-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: grayscale(1);
+}
+
+
+.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");
+}
+
+.addon-view[type="experiment"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/experimentGeneric.png");
+}
+
+.name-container {
+ font-size: 150%;
+ font-weight: bold;
+ color: #3F3F3F;
+ margin-bottom: 0;
+ -moz-box-align: end;
+ -moz-box-flex: 1;
+}
+
+.creator {
+ font-weight: bold;
+}
+
+.creator .text-link {
+ color: #0066CC;
+}
+
+.description-container {
+ -moz-margin-start: 6px;
+ -moz-box-align: center;
+}
+
+.description {
+ margin: 0;
+}
+
+.warning,
+.pending,
+.error {
+ -moz-margin-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;
+ -moz-margin-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.gif");
+}
+
+.addon .relnotes-toggle > .button-box > .button-icon {
+ -moz-padding-start: 4px;
+}
+
+.addon[show-relnotes] .relnotes-toggle {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.addon[active="false"] {
+ background-color: rgba(135, 135, 135, 0.1);
+ background-image: linear-gradient(rgba(135, 135, 135, 0),
+ rgba(135, 135, 135, 0.1));
+}
+
+.addon-view[active="false"],
+.addon-view[active="false"] .name-container {
+ color: #888A8B;
+}
+
+.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: #3F3F3F;
+}
+
+
+/*** 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 {
+ -moz-margin-start: 0;
+}
+
+#detail-icon-container {
+ width: 64px;
+ -moz-margin-end: 10px;
+ margin-top: 6px;
+}
+
+#detail-icon {
+ max-width: 64px;
+ max-height: 64px;
+}
+
+#detail-summary {
+ margin-bottom: 2em;
+}
+
+#detail-name-container {
+ font-size: 200%;
+}
+
+#detail-screenshot {
+ -moz-margin-end: 2em;
+ max-width: 300px;
+ max-height: 300px;
+}
+
+#detail-screenshot[loading] {
+ background-image: url("chrome://global/skin/icons/loading_16.png"),
+ linear-gradient(rgba(255, 255, 255, 0.5), transparent);
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ border-radius: 3px;
+}
+
+#detail-screenshot[loading="error"] {
+ background-image: url("chrome://global/skin/media/error.png"),
+ linear-gradient(rgba(255, 255, 255, 0.5), transparent);
+}
+
+#detail-desc-container {
+ margin-bottom: 2em;
+}
+
+#detail-desc, #detail-fulldesc {
+ -moz-margin-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 {
+ -moz-appearance: none;
+ 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 {
+ -moz-margin-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-grid > columns > column:first-child {
+ min-width: 15em;
+ max-width: 25em;
+}
+
+.detail-row[first-row="true"],
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
+ border-top: none;
+}
+
+.detail-row,
+.detail-row-complex,
+setting {
+ border-top: 2px solid;
+ -moz-border-top-colors: rgba(28, 31, 37, 0.1) rgba(255, 255, 255, 0.1);
+ -moz-box-align: center;
+ min-height: 30px;
+}
+
+#detail-controls {
+ margin-bottom: 1em;
+}
+
+#detail-view[active="false"]:not([pending]):not([notification]) {
+ background-image: linear-gradient(rgba(135, 135, 135, 0.1),
+ rgba(135, 135, 135, 0));
+}
+
+setting[first-row="true"] {
+ margin-top: 2em;
+}
+
+setting {
+ -moz-box-align: start;
+}
+
+.preferences-alignment {
+ min-height: 30px;
+ -moz-box-align: center;
+}
+
+.preferences-description {
+ font-size: 90.9%;
+ color: graytext;
+ margin-top: -2px;
+ -moz-margin-start: 2em;
+ white-space: pre-wrap;
+}
+
+.preferences-description:empty {
+ display: none;
+}
+
+setting[type="radio"] > radiogroup {
+ -moz-box-orient: horizontal;
+}
+
+menulist { /* Fixes some styling inconsistencies */
+ margin: 1px 5px 2px 5px;
+}
+
+/*** creator ***/
+
+.creator > label {
+ -moz-margin-start: 0;
+ -moz-margin-end: 0;
+}
+
+.creator > .text-link {
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+
+/*** rating ***/
+
+.meta-rating {
+ -moz-margin-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(#92DDA0, #6FC483 49%, #5EB272 51%, #80CE91);
+ margin-top: -1px;
+ margin-bottom: -1px;
+ border: 1px solid;
+ border-color: #358942 #317F3D #2E773A;
+}
+
+.download-progress .start-cap {
+ -moz-margin-start: -1px;
+ -moz-border-end-width: 0;
+}
+
+.download-progress .end-cap {
+ -moz-margin-end: -1px;
+ -moz-border-start-width: 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 {
+ -moz-margin-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"]:not(.no-auto-hide) {
+ display: none;
+}
+
+.no-auto-hide .addon-control {
+ display: block !important;
+}
+
+button.button-link {
+ -moz-appearance: none;
+ background: transparent;
+ border: none;
+ box-shadow: none;
+ text-decoration: underline;
+ color: #0066CC;
+ cursor: pointer;
+ min-width: 0;
+ margin: 0 6px;
+}
+
+.text-link {
+ color: #3386D5;
+}
+
+.button-link:hover,
+.text-link:hover {
+ color: #3DA1FF;
+}
+
+/* Needed to override normal button style from inContent.css */
+button.button-link:not([disabled="true"]):active:hover {
+ background: transparent;
+ border: none;
+ box-shadow: none;
+}
+
+.header-button {
+ -moz-appearance: none;
+ padding: 1px 3px;
+ color: #444;
+ text-shadow: 0 0 3px white;
+ background: linear-gradient(rgba(251, 252, 253, 0.95), transparent 49%,
+ rgba(211, 212, 213, 0.45) 51%, rgba(225, 226, 229, 0.3));
+ background-clip: padding-box;
+ border: 1px solid rgba(31, 64, 100, 0.4);
+ border-top-color: rgba(31, 64, 100, 0.3);
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25) inset,
+ 0 0 2px 1px rgba(255, 255, 255, 0.25) inset;
+}
+
+%ifdef XP_WIN
+@media (-moz-os-version: windows-vista),
+ (-moz-os-version: windows-win7) {
+%endif
+ .header-button {
+ border-radius: 2.5px;
+ }
+%ifdef XP_WIN
+}
+%endif
+
+.header-button[disabled="true"] {
+ opacity: 0.8;
+}
+
+.header-button[disabled="true"] > .toolbarbutton-icon {
+ opacity: 0.4;
+}
+
+.header-button:not([disabled="true"]):active:hover,
+.header-button[open="true"] {
+ background-color: rgba(61, 76, 92, 0.2);
+ border-color: rgba(39, 53, 68, 0.5);
+ box-shadow: 0 0 3px 1px rgba(39, 53, 68, 0.2) inset;
+}
+
+.header-button > .toolbarbutton-text {
+ display: none;
+}
+
+/*** telemetry experiments ***/
+
+#detail-experiment-container {
+ font-size: 80%;
+ margin-bottom: 1em;
+}
+
+#detail-experiment-bullet-container,
+#detail-experiment-state,
+#detail-experiment-time,
+.experiment-bullet-container,
+.experiment-state,
+.experiment-time {
+ vertical-align: middle;
+ display: inline-block;
+}
+
+.addon .experiment-bullet,
+#detail-experiment-bullet {
+ fill: rgb(158, 158, 158);
+}
+
+.addon[active="true"] .experiment-bullet,
+#detail-view[active="true"] #detail-experiment-bullet {
+ fill: rgb(106, 201, 20);
+}
diff --git a/toolkit/themes/windows/mozapps/extensions/heart.png b/toolkit/themes/windows/mozapps/extensions/heart.png
new file mode 100644
index 000000000..655f4c4be
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/heart.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/localeGeneric.png b/toolkit/themes/windows/mozapps/extensions/localeGeneric.png
new file mode 100644
index 000000000..623ba3a6a
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/localeGeneric.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/navigation.png b/toolkit/themes/windows/mozapps/extensions/navigation.png
new file mode 100644
index 000000000..8ff639127
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/navigation.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/newaddon.css b/toolkit/themes/windows/mozapps/extensions/newaddon.css
new file mode 100644
index 000000000..b3c429ec7
--- /dev/null
+++ b/toolkit/themes/windows/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/. */
+
+@import url("chrome://global/skin/inContentUI.css");
+
+#addon-page {
+ 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 {
+ overflow: visible;
+ max-width: 600px;
+ margin: 20px;
+ padding: 30px 90px;
+}
+
+#addon-info {
+ -moz-box-align: start;
+ margin: 25px 10px;
+}
+
+#icon {
+ -moz-margin-end: 10px;
+ max-width: 64px;
+ max-height: 64px;
+ 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 {
+ color: GrayText;
+}
+
+#location {
+ color: GrayText;
+}
+
+#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;
+ -moz-margin-end: 5px;
+}
+
+#allow {
+ -moz-margin-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 {
+ color: GrayText;
+}
diff --git a/toolkit/themes/windows/mozapps/extensions/rating-not-won.png b/toolkit/themes/windows/mozapps/extensions/rating-not-won.png
new file mode 100644
index 000000000..2761f1925
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/rating-not-won.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/rating-won.png b/toolkit/themes/windows/mozapps/extensions/rating-won.png
new file mode 100644
index 000000000..336dd8f6e
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/rating-won.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/selectAddons.css b/toolkit/themes/windows/mozapps/extensions/selectAddons.css
new file mode 100644
index 000000000..e15ca9c72
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/selectAddons.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/. */
+
+#view-deck {
+ background: Window;
+}
+
+.heading {
+ font-size: 270%;
+ text-align: center;
+ margin: 0 120px;
+}
+
+.progress {
+ margin: 10px 128px;
+}
+
+.progress-label,
+#errors-description {
+ text-align: center;
+ margin: 0 10px;
+}
+
+#checking-heading,
+#update-heading,
+#errors-heading {
+ margin-top: 90px;
+}
+
+#select-heading,
+#confirm-heading {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+#select-description,
+#confirm-description {
+ margin: 10px;
+}
+
+#select-list {
+ border-top: 1px solid #D6E5F5;
+ background-color: Window;
+}
+
+#select-grid column {
+ -moz-box-align: center;
+}
+
+#select-grid row {
+ -moz-box-align: stretch;
+}
+
+#select-grid label {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.select-cell {
+ -moz-box-align: center;
+ -moz-box-pack: start;
+}
+
+#select-header .select-cell {
+ box-sizing: border-box;
+}
+
+#select-header .select-keep,
+#select-header .select-icon,
+#select-header .select-name,
+#select-header .select-action {
+ background-image: linear-gradient(#D6E5F5 0%, Window 100%);
+ background-size: 1px 100%;
+ background-position: right;
+ background-repeat: no-repeat;
+}
+
+.select-keep {
+ -moz-box-pack: center;
+}
+
+.select-icon {
+ width: 20px;
+}
+
+.select-keep .addon-keep-checkbox {
+ margin: 0;
+ padding: 0;
+ width: 13px;
+}
+
+.select-keep .addon-keep-checkbox:-moz-focusring {
+ outline: 1px dotted ThreeDDarkShadow;
+}
+
+.select-keep .checkbox-label-box {
+ display: none;
+}
+
+.addon-name,
+.addon-action-message,
+.addon-action-update {
+ box-sizing: border-box;
+ margin: 0;
+ padding-top: 1px;
+ padding-bottom: 2px;
+ -moz-padding-start: 6px;
+ -moz-padding-end: 5px;
+}
+
+#select-grid separator {
+ border-top: 1px solid #D6E5F5;
+ height: 0;
+ margin-top: 0.4em;
+ margin-bottom: 0.4em;
+}
+
+.addon:not([active]) .addon-name,
+.addon:not([active]) .addon-action-message,
+.addon:not([active]) .addon-action-update {
+ color: GrayText;
+}
+
+.addon-icon {
+ height: 16px;
+ width: 16px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.addon-icon[type="theme"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric-16.png");
+}
+
+.addon-icon[type="plugin"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.addon-icon[type="dictionary"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric-16.png");
+}
+
+.action-list {
+ margin-top: 10px;
+ -moz-margin-start: 5em;
+}
+
+.action-header {
+ margin-bottom: 10px;
+}
+
+#confirm .addon {
+ -moz-margin-start: 3em;
+ -moz-box-align: center;
+}
+
+.addon:not([active]) .addon-icon,
+#disable-list .addon-icon,
+#incompatible-list .addon-icon {
+ filter: grayscale(1);
+}
+
+#footer {
+ padding: 15px 12px;
+}
+
+.progress-label,
+#footer-label {
+ color: GrayText;
+}
+
+%ifdef XP_WIN
+.progress-label,
+#footer-label {
+ font-style: italic;
+}
+
+@media (-moz-windows-default-theme) {
+ #footer {
+ background-color: #f1f5fb;
+ box-shadow: 0px 1px 2px rgb(204,214,234) inset;
+ }
+}
+%endif
diff --git a/toolkit/themes/windows/mozapps/extensions/stripes-error.png b/toolkit/themes/windows/mozapps/extensions/stripes-error.png
new file mode 100644
index 000000000..1dc2d8504
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/stripes-error.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/stripes-info-negative.png b/toolkit/themes/windows/mozapps/extensions/stripes-info-negative.png
new file mode 100644
index 000000000..901ab1ec2
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/stripes-info-negative.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/stripes-info-positive.png b/toolkit/themes/windows/mozapps/extensions/stripes-info-positive.png
new file mode 100644
index 000000000..370ceec0f
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/stripes-info-positive.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/stripes-warning.png b/toolkit/themes/windows/mozapps/extensions/stripes-warning.png
new file mode 100644
index 000000000..69463fb1a
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/stripes-warning.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/themeGeneric-16.png b/toolkit/themes/windows/mozapps/extensions/themeGeneric-16.png
new file mode 100644
index 000000000..ff13ce37f
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/themeGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/themeGeneric.png b/toolkit/themes/windows/mozapps/extensions/themeGeneric.png
new file mode 100644
index 000000000..9cea549dd
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/themeGeneric.png
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/extensions/update.css b/toolkit/themes/windows/mozapps/extensions/update.css
new file mode 100644
index 000000000..d35438ac9
--- /dev/null
+++ b/toolkit/themes/windows/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://global/skin/icons/loading_16.png");
+ width: 16px;
+ height: 16px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ -moz-margin-start: 5px;
+ -moz-margin-end: 2px;
+}
+
+.alertBox {
+ background-color: InfoBackground;
+ color: InfoText;
+ border: 1px outset InfoBackground;
+ margin-left: 3px;
+ margin-right: 3px;
+ padding: 5px;
+}
diff --git a/toolkit/themes/windows/mozapps/extensions/xpinstallConfirm.css b/toolkit/themes/windows/mozapps/extensions/xpinstallConfirm.css
new file mode 100644
index 000000000..d038e1abf
--- /dev/null
+++ b/toolkit/themes/windows/mozapps/extensions/xpinstallConfirm.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/. */
+
+#xpinstallheader {
+ margin-bottom: 2em;
+}
+
+#itemList {
+ -moz-appearance: listbox;
+ margin: 3px 4px 10px 4px;
+ height: 14em;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
+ -moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+#itemWarningIntro {
+ -moz-margin-start: 8px;
+}
+
+#dialogContentBox {
+ padding: 5px;
+}
+
+installitem {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ -moz-padding-start: 5px;
+ -moz-padding-end: 0;
+ border-bottom: 1px dotted #C0C0C0;
+ margin-bottom: 5px;
+}
+
+.alert-icon {
+%ifdef XP_WIN
+ list-style-image: url("chrome://global/skin/icons/warning-large.png");
+ width: 48px;
+ height: 48px;
+%endif
+ -moz-margin-end: 20px;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 1.25em;
+ margin-bottom: 1em;
+}
+
+.xpinstallIconContainer {
+ width: 32px;
+ height: 32px;
+ -moz-margin-end: 5px;
+}
+
+.xpinstallItemName {
+ font-weight: bold;
+}
+
+.xpinstallItemSigned {
+ font-style: italic;
+ font-size: 0.9em;
+}
+
+.xpinstallItemURL {
+ -moz-appearance: none;
+ border: none;
+ padding: 0;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ -moz-margin-start: 6px;
+ -moz-margin-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/toolkit/themes/windows/mozapps/jar.mn b/toolkit/themes/windows/mozapps/jar.mn
index b5907d016..29203811f 100644
--- a/toolkit/themes/windows/mozapps/jar.mn
+++ b/toolkit/themes/windows/mozapps/jar.mn
@@ -22,6 +22,51 @@ toolkit.jar:
skin/classic/mozapps/extensions/localeGeneric.png (webextensions/localeGeneric.png)
skin/classic/mozapps/extensions/heart.png (webextensions/heart.png)
* skin/classic/mozapps/extensions/newaddon.css (webextensions/newaddon.css)
+#else
+ skin/classic/mozapps/extensions/about.css (extensions/about.css)
+ skin/classic/mozapps/extensions/blocklist.css (extensions/blocklist.css)
+* skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
+* skin/classic/mozapps/extensions/selectAddons.css (extensions/selectAddons.css)
+ skin/classic/mozapps/extensions/update.css (extensions/update.css)
+ skin/classic/mozapps/extensions/category-search.png (extensions/category-search.png)
+ skin/classic/mozapps/extensions/category-discover.png (extensions/category-discover.png)
+ skin/classic/mozapps/extensions/category-languages.png (extensions/localeGeneric.png)
+ skin/classic/mozapps/extensions/category-searchengines.png (extensions/category-searchengines.png)
+ skin/classic/mozapps/extensions/category-extensions.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/extensions/category-themes.png (extensions/themeGeneric.png)
+ skin/classic/mozapps/extensions/category-plugins.png (extensions/category-plugins.png)
+ skin/classic/mozapps/extensions/category-service.png (extensions/category-service.png)
+ skin/classic/mozapps/extensions/category-dictionaries.png (extensions/dictionaryGeneric.png)
+ skin/classic/mozapps/extensions/category-recent.png (extensions/category-recent.png)
+ skin/classic/mozapps/extensions/category-available.png (extensions/category-available.png)
+ skin/classic/mozapps/extensions/discover-logo.png (extensions/discover-logo.png)
+ skin/classic/mozapps/extensions/extensionGeneric.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/extensions/extensionGeneric-16.png (extensions/extensionGeneric-16.png)
+ skin/classic/mozapps/extensions/extensionGeneric-48.png (extensions/extensionGeneric-48.png)
+ skin/classic/mozapps/extensions/themeGeneric.png (extensions/themeGeneric.png)
+ skin/classic/mozapps/extensions/themeGeneric-16.png (extensions/themeGeneric-16.png)
+ skin/classic/mozapps/extensions/dictionaryGeneric.png (extensions/dictionaryGeneric.png)
+ skin/classic/mozapps/extensions/dictionaryGeneric-16.png (extensions/dictionaryGeneric-16.png)
+ skin/classic/mozapps/extensions/experimentGeneric.png (extensions/experimentGeneric.png)
+ skin/classic/mozapps/extensions/localeGeneric.png (extensions/localeGeneric.png)
+ skin/classic/mozapps/extensions/rating-won.png (extensions/rating-won.png)
+ skin/classic/mozapps/extensions/rating-not-won.png (extensions/rating-not-won.png)
+ skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
+ skin/classic/mozapps/extensions/utilities.svg (../../shared/extensions/utilities.svg)
+ skin/classic/mozapps/extensions/heart.png (extensions/heart.png)
+ skin/classic/mozapps/extensions/navigation.png (extensions/navigation.png)
+ skin/classic/mozapps/extensions/stripes-warning.png (extensions/stripes-warning.png)
+ skin/classic/mozapps/extensions/stripes-error.png (extensions/stripes-error.png)
+ skin/classic/mozapps/extensions/stripes-info-positive.png (extensions/stripes-info-positive.png)
+ skin/classic/mozapps/extensions/stripes-info-negative.png (extensions/stripes-info-negative.png)
+ skin/classic/mozapps/extensions/alerticon-warning.png (extensions/alerticon-warning.png)
+ skin/classic/mozapps/extensions/alerticon-error.png (extensions/alerticon-error.png)
+ skin/classic/mozapps/extensions/alerticon-info-positive.png (extensions/alerticon-info-positive.png)
+ skin/classic/mozapps/extensions/alerticon-info-negative.png (extensions/alerticon-info-negative.png)
+ skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
+ skin/classic/mozapps/extensions/newaddon.css (extensions/newaddon.css)
+* skin/classic/mozapps/xpinstall/xpinstallConfirm.css (extensions/xpinstallConfirm.css)
+ skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png (extensions/extensionGeneric.png)
#endif
skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png)
skin/classic/mozapps/plugins/pluginBlocked.png (plugins/pluginBlocked.png)
@@ -29,43 +74,9 @@ toolkit.jar:
skin/classic/mozapps/profile/profileicon.png (profile/profileicon.png)
skin/classic/mozapps/update/updates.css (update/updates.css)
skin/classic/mozapps/viewsource/viewsource.css (viewsource/viewsource.css)
- skin/classic/mozapps/downloads/downloadButtons-XP.png (downloads/downloadButtons-XP.png)
- skin/classic/mozapps/downloads/downloadIcon-XP.png (downloads/downloadIcon-XP.png)
-#ifdef MOZ_WEBEXTENSIONS
- skin/classic/mozapps/extensions/category-discover-XP.png (webextensions/category-discover-XP.png)
- skin/classic/mozapps/extensions/category-plugins-XP.png (webextensions/category-plugins-XP.png)
- skin/classic/mozapps/extensions/category-recent-XP.png (webextensions/category-recent-XP.png)
- skin/classic/mozapps/extensions/category-available-XP.png (webextensions/category-available-XP.png)
- skin/classic/mozapps/extensions/extensionGeneric-16-XP.png (webextensions/extensionGeneric-16-XP.png)
- skin/classic/mozapps/extensions/themeGeneric-XP.png (webextensions/themeGeneric-XP.png)
- skin/classic/mozapps/extensions/themeGeneric-16-XP.png (webextensions/themeGeneric-16-XP.png)
- skin/classic/mozapps/extensions/localeGeneric-XP.png (webextensions/localeGeneric-XP.png)
-#endif
- skin/classic/mozapps/plugins/pluginGeneric-XP.png (plugins/pluginGeneric-XP.png)
- skin/classic/mozapps/plugins/pluginBlocked-XP.png (plugins/pluginBlocked-XP.png)
- skin/classic/mozapps/plugins/pluginGeneric-16-XP.png (plugins/pluginGeneric-16-XP.png)
- skin/classic/mozapps/profile/profileicon-XP.png (profile/profileicon-XP.png)
- skin/classic/mozapps/update/downloadButtons-XP.png (update/downloadButtons-XP.png)
#ifdef MOZ_PHOENIX
[browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
#elif MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
#endif
-% override chrome://mozapps/skin/downloads/downloadButtons.png chrome://mozapps/skin/downloads/downloadButtons-XP.png osversion<6
-% override chrome://mozapps/skin/downloads/downloadIcon.png chrome://mozapps/skin/downloads/downloadIcon-XP.png osversion<6
-#ifdef MOZ_WEBEXTENSIONS
-% override chrome://mozapps/skin/extensions/category-discover.png chrome://mozapps/skin/extensions/category-discover-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/category-plugins.png chrome://mozapps/skin/extensions/category-plugins-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/category-recent.png chrome://mozapps/skin/extensions/category-recent-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/category-available.png chrome://mozapps/skin/extensions/category-available-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/extensionGeneric-16.png chrome://mozapps/skin/extensions/extensionGeneric-16-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/themeGeneric.png chrome://mozapps/skin/extensions/themeGeneric-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/themeGeneric-16.png chrome://mozapps/skin/extensions/themeGeneric-16-XP.png osversion<6
-% override chrome://mozapps/skin/extensions/localeGeneric.png chrome://mozapps/skin/extensions/localeGeneric-XP.png osversion<6
-#endif
-% override chrome://mozapps/skin/plugins/pluginGeneric.png chrome://mozapps/skin/plugins/pluginGeneric-XP.png osversion<6
-% override chrome://mozapps/skin/plugins/pluginBlocked.png chrome://mozapps/skin/plugins/pluginBlocked-XP.png osversion<6
-% override chrome://mozapps/skin/plugins/pluginGeneric-16.png chrome://mozapps/skin/plugins/pluginGeneric-16-XP.png osversion<6
-% override chrome://mozapps/skin/profile/profileicon.png chrome://mozapps/skin/profile/profileicon-XP.png osversion<6
-% override chrome://mozapps/skin/update/downloadButtons.png chrome://mozapps/skin/update/downloadButtons-XP.png osversion<6
diff --git a/toolkit/themes/windows/mozapps/plugins/pluginBlocked-XP.png b/toolkit/themes/windows/mozapps/plugins/pluginBlocked-XP.png
deleted file mode 100644
index 954036c1f..000000000
--- a/toolkit/themes/windows/mozapps/plugins/pluginBlocked-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/plugins/pluginGeneric-16-XP.png b/toolkit/themes/windows/mozapps/plugins/pluginGeneric-16-XP.png
deleted file mode 100644
index d6dffb29b..000000000
--- a/toolkit/themes/windows/mozapps/plugins/pluginGeneric-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/plugins/pluginGeneric-XP.png b/toolkit/themes/windows/mozapps/plugins/pluginGeneric-XP.png
deleted file mode 100644
index 3e6d43c9a..000000000
--- a/toolkit/themes/windows/mozapps/plugins/pluginGeneric-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/profile/profileicon-XP.png b/toolkit/themes/windows/mozapps/profile/profileicon-XP.png
deleted file mode 100644
index 0854ee177..000000000
--- a/toolkit/themes/windows/mozapps/profile/profileicon-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/update/downloadButtons-XP.png b/toolkit/themes/windows/mozapps/update/downloadButtons-XP.png
deleted file mode 100644
index d36385ce5..000000000
--- a/toolkit/themes/windows/mozapps/update/downloadButtons-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/extensionGeneric-16-XP.png b/toolkit/themes/windows/mozapps/webextensions/extensionGeneric-16-XP.png
deleted file mode 100644
index 36e7689a3..000000000
--- a/toolkit/themes/windows/mozapps/webextensions/extensionGeneric-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/themes/windows/mozapps/webextensions/themeGeneric-16-XP.png b/toolkit/themes/windows/mozapps/webextensions/themeGeneric-16-XP.png
deleted file mode 100644
index 16d77a4a2..000000000
--- a/toolkit/themes/windows/mozapps/webextensions/themeGeneric-16-XP.png
+++ /dev/null
Binary files differ
diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
index 439282525..d94f1240a 100644
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -132,8 +132,10 @@ DIRS += [
if CONFIG['MOZ_PREF_EXTENSIONS']:
DIRS += ['/extensions/pref']
+if CONFIG['MOZ_DEVTOOLS_SERVER']:
+ DIRS += ['/devtools']
+
DIRS += [
- '/devtools',
'/services',
'/startupcache',
'/js/ductwork/debugger',
diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
index 4979e1652..4a1d3046f 100644
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -98,7 +98,6 @@
#include <intrin.h>
#include <math.h>
#include "cairo/cairo-features.h"
-#include "mozilla/WindowsVersion.h"
#include "mozilla/mscom/MainThreadRuntime.h"
#include "mozilla/widget/AudioSession.h"
@@ -3106,10 +3105,7 @@ XREMain::XRE_mainInit(bool* aExitFlag)
// manual font file I/O on _all_ system fonts. To avoid this, load the
// dwrite library and create a factory as early as possible so that the
// FntCache service is ready by the time it's needed.
-
- if (IsVistaOrLater()) {
- CreateThread(nullptr, 0, &InitDwriteBG, nullptr, 0, nullptr);
- }
+ CreateThread(nullptr, 0, &InitDwriteBG, nullptr, 0, nullptr);
}
#endif
@@ -4835,7 +4831,7 @@ enum {
kE10sDisabledForAddons = 7,
kE10sForceDisabled = 8,
// kE10sDisabledForXPAcceleration = 9, removed in bug 1296353
- kE10sDisabledForOperatingSystem = 10,
+ // kE10sDisabledForOperatingSystem = 10, removed due to xp-eol
};
const char* kAccessibilityLastRunDatePref = "accessibility.lastLoadDate";
@@ -4912,19 +4908,6 @@ MultiprocessBlockPolicy() {
}
#endif
- /**
- * Avoids enabling e10s for Windows XP users on the release channel.
- */
-#if defined(XP_WIN)
- if (!IsVistaOrLater()) {
- nsAdoptingCString channelName = Preferences::GetDefaultCString("app.update.channel");
- if (channelName.EqualsLiteral("release") || channelName.EqualsLiteral("esr")) {
- gMultiprocessBlockPolicy = kE10sDisabledForOperatingSystem;
- return gMultiprocessBlockPolicy;
- }
- }
-#endif // XP_WIN
-
/*
* None of the blocking policies matched, so e10s is allowed to run.
* Cache the information and return 0, indicating success.
diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp
index 0e85532b9..1e67ea7ce 100644
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -24,6 +24,7 @@
#ifdef XP_WIN
#include <process.h>
+#include <shobjidl.h>
#include "mozilla/ipc/WindowsMessageLoop.h"
#endif
@@ -119,10 +120,6 @@ using mozilla::startup::sChildProcessType;
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
-#ifdef XP_WIN
-static const wchar_t kShellLibraryName[] = L"shell32.dll";
-#endif
-
nsresult
XRE_LockProfileDirectory(nsIFile* aDirectory,
nsISupports* *aLockObject)
@@ -270,26 +267,9 @@ XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/)
void
SetTaskbarGroupId(const nsString& aId)
{
- typedef HRESULT (WINAPI * SetCurrentProcessExplicitAppUserModelIDPtr)(PCWSTR AppID);
-
- SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr;
-
- HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
-
- funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr)
- GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID");
-
- if (!funcAppUserModelID) {
- ::FreeLibrary(hDLL);
- return;
- }
-
- if (FAILED(funcAppUserModelID(aId.get()))) {
+ if (FAILED(SetCurrentProcessExplicitAppUserModelID(aId.get()))) {
NS_WARNING("SetCurrentProcessExplicitAppUserModelID failed for child process.");
}
-
- if (hDLL)
- ::FreeLibrary(hDLL);
}
#endif
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
index 2fbd324b7..09168319f 100644
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -48,7 +48,6 @@
#ifdef XP_WIN
#include <windows.h>
#include <shlobj.h>
-#include "mozilla/WindowsVersion.h"
#endif
#ifdef XP_MACOSX
#include "nsILocalFileMac.h"
@@ -762,15 +761,12 @@ nsXREDirProvider::LoadContentProcessTempDir()
static bool
IsContentSandboxDisabled()
{
+ bool isSandboxDisabled = false;
if (!BrowserTabsRemoteAutostart()) {
return false;
}
-#if defined(XP_WIN)
- const bool isSandboxDisabled = !mozilla::IsVistaOrLater() ||
- (Preferences::GetInt("security.sandbox.content.level") < 1);
-#elif defined(XP_MACOSX)
- const bool isSandboxDisabled =
- Preferences::GetInt("security.sandbox.content.level") < 1;
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ isSandboxDisabled = Preferences::GetInt("security.sandbox.content.level") < 1;
#endif
return isSandboxDisabled;
}
diff --git a/toolkit/xre/test/win/TestDllInterceptor.cpp b/toolkit/xre/test/win/TestDllInterceptor.cpp
index dff71817f..57d68ea77 100644
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -2,11 +2,6 @@
* License, v. 2.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 _WIN32_WINNT < 0x0600
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0600
-#endif
-
#include <shlobj.h>
#include <stdio.h>
diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp
index e53db69c5..5c8693957 100644
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -245,13 +245,7 @@ RemovePrefForDriverVersion()
static OperatingSystem
BlacklistOSToOperatingSystem(const nsAString& os)
{
- if (os.EqualsLiteral("WINNT 5.1"))
- return OperatingSystem::WindowsXP;
- else if (os.EqualsLiteral("WINNT 5.2"))
- return OperatingSystem::WindowsServer2003;
- else if (os.EqualsLiteral("WINNT 6.0"))
- return OperatingSystem::WindowsVista;
- else if (os.EqualsLiteral("WINNT 6.1"))
+ if (os.EqualsLiteral("WINNT 6.1"))
return OperatingSystem::Windows7;
else if (os.EqualsLiteral("WINNT 6.2"))
return OperatingSystem::Windows8;
diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h
index e2502c559..cf84b3308 100644
--- a/widget/LookAndFeel.h
+++ b/widget/LookAndFeel.h
@@ -440,9 +440,7 @@ public:
* Operating system versions.
*/
enum OperatingSystemVersion {
- eOperatingSystemVersion_WindowsXP = 0,
- eOperatingSystemVersion_WindowsVista,
- eOperatingSystemVersion_Windows7,
+ eOperatingSystemVersion_Windows7 = 2,
eOperatingSystemVersion_Windows8,
eOperatingSystemVersion_Windows10,
eOperatingSystemVersion_Unknown
diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c
index d9fb9385d..d0b87613b 100644
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -9,7 +9,6 @@ STUB(gdk_atom_name)
STUB(gdk_beep)
STUB(gdk_cairo_create)
STUB(gdk_color_free)
-STUB(gdk_color_parse)
STUB(gdk_cursor_new_for_display)
STUB(gdk_cursor_new_from_name)
STUB(gdk_cursor_new_from_pixbuf)
diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp
index 0cbd323de..bfea41851 100644
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -241,9 +241,6 @@ ParseIDFromDeviceID(const nsAString &key, const char *prefix, int length)
// based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
enum {
kWindowsUnknown = 0,
- kWindowsXP = 0x50001,
- kWindowsServer2003 = 0x50002,
- kWindowsVista = 0x60000,
kWindows7 = 0x60001,
kWindows8 = 0x60002,
kWindows8_1 = 0x60003,
@@ -826,12 +823,6 @@ static OperatingSystem
WindowsVersionToOperatingSystem(int32_t aWindowsVersion)
{
switch(aWindowsVersion) {
- case kWindowsXP:
- return OperatingSystem::WindowsXP;
- case kWindowsServer2003:
- return OperatingSystem::WindowsServer2003;
- case kWindowsVista:
- return OperatingSystem::WindowsVista;
case kWindows7:
return OperatingSystem::Windows7;
case kWindows8:
@@ -860,11 +851,6 @@ GfxInfo::GetGfxDriverInfo()
/*
* NVIDIA entries
*/
- APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP,
- (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
- GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
- DRIVER_LESS_THAN_OR_EQUAL, V(6,14,11,8745), "FEATURE_FAILURE_NV_XP", "nVidia driver > 187.45" );
-
/*
* The last 5 digit of the NVIDIA driver version maps to the version that
* NVIDIA uses. The minor version (15, 16, 17) corresponds roughtly to the
@@ -875,33 +861,17 @@ GfxInfo::GetGfxDriverInfo()
* 187.45 (late October 2009) and earlier contain a bug which can cause us
* to crash on shutdown.
*/
- APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista,
- (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
- GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
- DRIVER_LESS_THAN_OR_EQUAL, V(8,15,11,8745),
- "FEATURE_FAILURE_NV_VISTA_15", "nVidia driver > 187.45" );
APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Windows7,
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
DRIVER_LESS_THAN_OR_EQUAL, V(8,15,11,8745),
"FEATURE_FAILURE_NV_W7_15", "nVidia driver > 187.45" );
- APPEND_TO_DRIVER_BLOCKLIST_RANGE(OperatingSystem::WindowsVista,
- (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
- GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
- DRIVER_BETWEEN_INCLUSIVE_START, V(8,16,10,0000), V(8,16,11,8745),
- "FEATURE_FAILURE_NV_VISTA_16", "nVidia driver > 187.45" );
APPEND_TO_DRIVER_BLOCKLIST_RANGE(OperatingSystem::Windows7,
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
DRIVER_BETWEEN_INCLUSIVE_START, V(8,16,10,0000), V(8,16,11,8745),
"FEATURE_FAILURE_NV_W7_16", "nVidia driver > 187.45" );
// Telemetry doesn't show any driver in this range so it might not even be required.
- APPEND_TO_DRIVER_BLOCKLIST_RANGE(OperatingSystem::WindowsVista,
- (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
- GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
- DRIVER_BETWEEN_INCLUSIVE_START, V(8,17,10,0000), V(8,17,11,8745),
- "FEATURE_FAILURE_NV_VISTA_17", "nVidia driver > 187.45" );
- // Telemetry doesn't show any driver in this range so it might not even be required.
APPEND_TO_DRIVER_BLOCKLIST_RANGE(OperatingSystem::Windows7,
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
@@ -999,13 +969,6 @@ GfxInfo::GetGfxDriverInfo()
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, \
DRIVER_BUILD_ID_LESS_THAN, driverVer, ruleId )
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::WindowsVista, IntelGMA500, 1006, "FEATURE_FAILURE_594877_1");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::WindowsVista, IntelGMA900, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_594877_2");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::WindowsVista, IntelGMA950, 1504, "FEATURE_FAILURE_594877_3");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::WindowsVista, IntelGMA3150, 2124, "FEATURE_FAILURE_594877_4");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::WindowsVista, IntelGMAX3000, 1666, "FEATURE_FAILURE_594877_5");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::WindowsVista, IntelHDGraphicsToSandyBridge, 2202, "FEATURE_FAILURE_594877_6");
-
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::Windows7, IntelGMA500, 2026, "FEATURE_FAILURE_594877_7");
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::Windows7, IntelGMA900, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_594877_8");
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(OperatingSystem::Windows7, IntelGMA950, 1930, "FEATURE_FAILURE_594877_9");
@@ -1022,31 +985,6 @@ GfxInfo::GetGfxDriverInfo()
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_1180379");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelGMA500, V(3,0,20,3200), "FEATURE_FAILURE_INTEL_1");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelGMA900, V(6,14,10,4764), "FEATURE_FAILURE_INTEL_2");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelGMA950, V(6,14,10,4926), "FEATURE_FAILURE_INTEL_3");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelGMA3150, V(6,14,10,5134), "FEATURE_FAILURE_INTEL_4");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelGMAX3000, V(6,14,10,5218), "FEATURE_FAILURE_INTEL_5");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelGMAX4500HD, V(6,14,10,4969), "FEATURE_FAILURE_INTEL_6");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsXP, IntelHDGraphicsToSandyBridge, V(6,14,10,4969), "FEATURE_FAILURE_INTEL_7");
-
- // StrechRect seems to suffer from precision issues which leads to artifacting
- // during content drawing starting with at least version 6.14.10.5082
- // and going until 6.14.10.5218. See bug 919454 and bug 949275 for more info.
- APPEND_TO_DRIVER_BLOCKLIST_RANGE(OperatingSystem::WindowsXP,
- const_cast<nsAString&>(GfxDriverInfo::GetDeviceVendor(VendorIntel)),
- const_cast<GfxDeviceFamily*>(GfxDriverInfo::GetDeviceFamily(IntelGMAX4500HD)),
- GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
- DRIVER_BETWEEN_EXCLUSIVE, V(6,14,10,5076), V(6,14,10,5218), "FEATURE_FAILURE_INTEL_8", "6.14.10.5218");
-
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelGMA500, V(3,0,20,3200), "FEATURE_FAILURE_INTEL_9");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelGMA900, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_INTEL_10");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelGMA950, V(7,14,10,1504), "FEATURE_FAILURE_INTEL_11");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelGMA3150, V(7,14,10,1910), "FEATURE_FAILURE_INTEL_12");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelGMAX3000, V(7,15,10,1666), "FEATURE_FAILURE_INTEL_13");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelGMAX4500HD, V(7,15,10,1666), "FEATURE_FAILURE_INTEL_14");
- IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::WindowsVista, IntelHDGraphicsToSandyBridge, V(7,15,10,1666), "FEATURE_FAILURE_INTEL_15");
-
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::Windows7, IntelGMA500, V(5,0,0,2026), "FEATURE_FAILURE_INTEL_16");
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::Windows7, IntelGMA900, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_INTEL_17");
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(OperatingSystem::Windows7, IntelGMA950, V(8,15,10,1930), "FEATURE_FAILURE_INTEL_18");
@@ -1300,23 +1238,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
return NS_OK;
}
- // special-case the WinXP test slaves: they have out-of-date drivers, but we still want to
- // whitelist them, actually we do know that this combination of device and driver version
- // works well.
- if (mWindowsVersion == kWindowsXP &&
- adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), nsCaseInsensitiveStringComparator()) &&
- adapterDeviceID.LowerCaseEqualsLiteral("0x0861") && // GeForce 9400
- driverVersion == V(6,14,11,7756))
- {
- *aStatus = FEATURE_STATUS_OK;
- return NS_OK;
- }
-
- // Windows Server 2003 should be just like Windows XP for present purpose, but still has a different version number.
- // OTOH Windows Server 2008 R1 and R2 already have the same version numbers as Vista and Seven respectively
- if (os == OperatingSystem::WindowsServer2003)
- os = OperatingSystem::WindowsXP;
-
if (mHasDriverVersionMismatch) {
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION;
return NS_OK;
diff --git a/widget/windows/KeyboardLayout.cpp b/widget/windows/KeyboardLayout.cpp
index 11f657874..341a40c18 100644
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -1363,34 +1363,6 @@ NativeKey::InitWithKeyChar()
break;
}
- if (!CanComputeVirtualKeyCodeFromScanCode()) {
- // The right control key and the right alt key are extended keys.
- // Therefore, we never get VK_RCONTRL and VK_RMENU for the result of
- // MapVirtualKeyEx() on WinXP or WinServer2003.
- //
- // If VK_SHIFT, VK_CONTROL or VK_MENU key message is caused by well
- // known scan code, we should decide it as Right key. Otherwise,
- // decide it as Left key.
- switch (mOriginalVirtualKeyCode) {
- case VK_CONTROL:
- mVirtualKeyCode =
- mIsExtended && mScanCode == 0x1D ? VK_RCONTROL : VK_LCONTROL;
- break;
- case VK_MENU:
- mVirtualKeyCode =
- mIsExtended && mScanCode == 0x38 ? VK_RMENU : VK_LMENU;
- break;
- case VK_SHIFT:
- // Neither left shift nor right shift is an extended key,
- // let's use VK_LSHIFT for unknown mapping.
- mVirtualKeyCode = VK_LSHIFT;
- break;
- default:
- MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
- }
- break;
- }
-
NS_ASSERTION(!mVirtualKeyCode,
"mVirtualKeyCode has been computed already");
@@ -1447,11 +1419,6 @@ NativeKey::InitWithKeyChar()
// scancode, we cannot compute virtual keycode. I.e., with such
// applications, we cannot generate proper KeyboardEvent.code value.
- // We cannot compute the virtual key code from WM_CHAR message on WinXP
- // if it's caused by an extended key.
- if (!CanComputeVirtualKeyCodeFromScanCode()) {
- break;
- }
mVirtualKeyCode = mOriginalVirtualKeyCode =
ComputeVirtualKeyCodeFromScanCodeEx();
NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode");
@@ -1869,18 +1836,6 @@ NativeKey::GetKeyLocation() const
}
}
-bool
-NativeKey::CanComputeVirtualKeyCodeFromScanCode() const
-{
- // Vista or later supports ScanCodeEx.
- if (IsVistaOrLater()) {
- return true;
- }
- // Otherwise, MapVirtualKeyEx() can compute virtual keycode only with
- // non-extended key.
- return !mIsExtended;
-}
-
uint8_t
NativeKey::ComputeVirtualKeyCodeFromScanCode() const
{
@@ -1894,12 +1849,6 @@ NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const
// MapVirtualKeyEx() has been improved for supporting extended keys since
// Vista. When we call it for mapping a scancode of an extended key and
// a virtual keycode, we need to add 0xE000 to the scancode.
- // On the other hand, neither WinXP nor WinServer2003 doesn't support 0xE000.
- // Therefore, we have no way to get virtual keycode from scan code of
- // extended keys.
- if (NS_WARN_IF(!CanComputeVirtualKeyCodeFromScanCode())) {
- return 0;
- }
return static_cast<uint8_t>(
::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX,
mKeyboardLayout));
@@ -1910,8 +1859,7 @@ NativeKey::ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const
{
return static_cast<uint16_t>(
::MapVirtualKeyEx(aVirtualKeyCode,
- IsVistaOrLater() ? MAPVK_VK_TO_VSC_EX :
- MAPVK_VK_TO_VSC,
+ MAPVK_VK_TO_VSC_EX,
mKeyboardLayout));
}
@@ -4345,8 +4293,7 @@ KeyboardLayout::LoadLayout(HKL aLayout)
if (MOZ_LOG_TEST(sKeyboardLayoutLogger, LogLevel::Verbose)) {
static const UINT kExtendedScanCode[] = { 0x0000, 0xE000 };
- static const UINT kMapType =
- IsVistaOrLater() ? MAPVK_VSC_TO_VK_EX : MAPVK_VSC_TO_VK;
+ static const UINT kMapType = MAPVK_VSC_TO_VK_EX;
MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Verbose,
("Logging virtual keycode values for scancode (0x%p)...",
mKeyboardLayout));
@@ -4358,11 +4305,6 @@ KeyboardLayout::LoadLayout(HKL aLayout)
MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Verbose,
("0x%04X, %s", scanCode, kVirtualKeyName[virtualKeyCode]));
}
- // XP and Server 2003 don't support 0xE0 prefix of the scancode.
- // Therefore, we don't need to continue on them.
- if (!IsVistaOrLater()) {
- break;
- }
}
}
}
diff --git a/widget/windows/KeyboardLayout.h b/widget/windows/KeyboardLayout.h
index dd2ac0bfc..70aacc80e 100644
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -560,11 +560,6 @@ private:
bool GetFollowingCharMessage(MSG& aCharMsg);
/**
- * Whether the key event can compute virtual keycode from the scancode value.
- */
- bool CanComputeVirtualKeyCodeFromScanCode() const;
-
- /**
* Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK.
*/
uint8_t ComputeVirtualKeyCodeFromScanCode() const;
diff --git a/widget/windows/LSPAnnotator.cpp b/widget/windows/LSPAnnotator.cpp
index de4a40d2a..97f6e5b50 100644
--- a/widget/windows/LSPAnnotator.cpp
+++ b/widget/windows/LSPAnnotator.cpp
@@ -11,11 +11,6 @@
* on machines with several LSPs.
*/
-#if _WIN32_WINNT < 0x0600
-// Redefining _WIN32_WINNT for some Vista APIs that we call
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0600
-#endif
#include "nsICrashReporter.h"
#include "nsISupportsImpl.h"
#include "nsServiceManagerUtils.h"
diff --git a/widget/windows/TSFTextStore.cpp b/widget/windows/TSFTextStore.cpp
index fb0505aa3..7224126b8 100644
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -5762,8 +5762,7 @@ TSFTextStore::Initialize()
return;
}
- bool enableTsf =
- IsVistaOrLater() && Preferences::GetBool(kPrefNameEnableTSF, false);
+ bool enableTsf = Preferences::GetBool(kPrefNameEnableTSF, false);
MOZ_LOG(sTextStoreLog, LogLevel::Info,
(" TSFTextStore::Initialize(), TSF is %s",
enableTsf ? "enabled" : "disabled"));
diff --git a/widget/windows/TaskbarPreview.cpp b/widget/windows/TaskbarPreview.cpp
index c897af021..6c15df2e0 100644
--- a/widget/windows/TaskbarPreview.cpp
+++ b/widget/windows/TaskbarPreview.cpp
@@ -28,7 +28,9 @@
#include "mozilla/Telemetry.h"
// Defined in dwmapi in a header that needs a higher numbered _WINNT #define
+#ifndef DWM_SIT_DISPLAYFRAME
#define DWM_SIT_DISPLAYFRAME 0x1
+#endif
namespace mozilla {
namespace widget {
diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp
index 10937ba51..e157b1be2 100644
--- a/widget/windows/WinMouseScrollHandler.cpp
+++ b/widget/windows/WinMouseScrollHandler.cpp
@@ -992,10 +992,7 @@ MouseScrollHandler::SystemSettings::InitScrollChars()
&mScrollChars, 0)) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::SystemSettings::InitScrollChars(): ::SystemParametersInfo("
- "SPI_GETWHEELSCROLLCHARS) failed, %s",
- IsVistaOrLater() ?
- "this is unexpected on Vista or later" :
- "but on XP or earlier, this is not a problem"));
+ "SPI_GETWHEELSCROLLCHARS) failed, this is unexpected on Vista or later"));
// XXX Should we use DefaultScrollChars()?
mScrollChars = 1;
}
@@ -1081,7 +1078,7 @@ bool
MouseScrollHandler::SystemSettings::IsOverridingSystemScrollSpeedAllowed()
{
return mScrollLines == DefaultScrollLines() &&
- (!IsVistaOrLater() || mScrollChars == DefaultScrollChars());
+ mScrollChars == DefaultScrollChars();
}
/******************************************************************************
diff --git a/widget/windows/WinTaskbar.cpp b/widget/windows/WinTaskbar.cpp
index 698b7ec0e..530cfd5b9 100644
--- a/widget/windows/WinTaskbar.cpp
+++ b/widget/windows/WinTaskbar.cpp
@@ -34,8 +34,6 @@
#include <propkey.h>
#include <shellapi.h>
-const wchar_t kShellLibraryName[] = L"shell32.dll";
-
static NS_DEFINE_CID(kJumpListBuilderCID, NS_WIN_JUMPLISTBUILDER_CID);
namespace {
@@ -77,30 +75,14 @@ SetWindowAppUserModelProp(mozIDOMWindow *aParent,
if (!toplevelHWND)
return NS_ERROR_INVALID_ARG;
- typedef HRESULT (WINAPI * SHGetPropertyStoreForWindowPtr)
- (HWND hwnd, REFIID riid, void** ppv);
- SHGetPropertyStoreForWindowPtr funcGetProStore = nullptr;
-
- HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
- funcGetProStore = (SHGetPropertyStoreForWindowPtr)
- GetProcAddress(hDLL, "SHGetPropertyStoreForWindow");
-
- if (!funcGetProStore) {
- FreeLibrary(hDLL);
- return NS_ERROR_NO_INTERFACE;
- }
-
- IPropertyStore* pPropStore;
- if (FAILED(funcGetProStore(toplevelHWND,
- IID_PPV_ARGS(&pPropStore)))) {
- FreeLibrary(hDLL);
+ RefPtr<IPropertyStore> pPropStore;
+ if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND, IID_IPropertyStore,
+ getter_AddRefs(pPropStore)))) {
return NS_ERROR_INVALID_ARG;
}
PROPVARIANT pv;
if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
- pPropStore->Release();
- FreeLibrary(hDLL);
return NS_ERROR_UNEXPECTED;
}
@@ -111,8 +93,6 @@ SetWindowAppUserModelProp(mozIDOMWindow *aParent,
}
PropVariantClear(&pv);
- pPropStore->Release();
- FreeLibrary(hDLL);
return rv;
}
@@ -339,40 +319,18 @@ WinTaskbar::GetDefaultGroupId(nsAString & aDefaultGroupId) {
// (static) Called from AppShell
bool
WinTaskbar::RegisterAppUserModelID() {
- if (!IsWin7OrLater())
- return false;
-
- SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr;
- bool retVal = false;
-
nsAutoString uid;
if (!GetAppUserModelID(uid))
return false;
- HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
-
- funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr)
- GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID");
-
- if (!funcAppUserModelID) {
- ::FreeLibrary(hDLL);
- return false;
- }
-
- if (SUCCEEDED(funcAppUserModelID(uid.get())))
- retVal = true;
-
- if (hDLL)
- ::FreeLibrary(hDLL);
-
- return retVal;
+ return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid.get()));
}
NS_IMETHODIMP
WinTaskbar::GetAvailable(bool *aAvailable) {
// ITaskbarList4::HrInit() may fail with shell extensions like blackbox
// installed. Initialize early to return available=false in those cases.
- *aAvailable = IsWin7OrLater() && Initialize();
+ *aAvailable = Initialize();
return NS_OK;
}
diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp
index 149513b2f..0a57ad439 100644
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -462,7 +462,7 @@ static NtTestAlertPtr sNtTestAlert = nullptr;
void
WinUtils::Initialize()
{
- if (!sDwmDll && IsVistaOrLater()) {
+ if (!sDwmDll) {
sDwmDll = ::LoadLibraryW(kDwmLibraryName);
if (sDwmDll) {
@@ -601,7 +601,7 @@ WinUtils::SystemScaleFactor()
return systemScale;
}
-#ifndef WM_DPICHANGED
+#if WINVER < 0x603
typedef enum {
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
@@ -628,16 +628,14 @@ GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness;
static bool
SlowIsPerMonitorDPIAware()
{
- if (IsVistaOrLater()) {
- // Intentionally leak the handle.
- HMODULE shcore =
- LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
- if (shcore) {
- sGetDpiForMonitor =
- (GETDPIFORMONITORPROC) GetProcAddress(shcore, "GetDpiForMonitor");
- sGetProcessDpiAwareness =
- (GETPROCESSDPIAWARENESSPROC) GetProcAddress(shcore, "GetProcessDpiAwareness");
- }
+ // Intentionally leak the handle.
+ HMODULE shcore =
+ LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (shcore) {
+ sGetDpiForMonitor =
+ (GETDPIFORMONITORPROC) GetProcAddress(shcore, "GetDpiForMonitor");
+ sGetProcessDpiAwareness =
+ (GETPROCESSDPIAWARENESSPROC) GetProcAddress(shcore, "GetProcessDpiAwareness");
}
PROCESS_DPI_AWARENESS dpiAwareness;
return sGetDpiForMonitor && sGetProcessDpiAwareness &&
@@ -760,7 +758,7 @@ static DWORD
GetWaitFlags()
{
DWORD result = MWMO_INPUTAVAILABLE;
- if (IsVistaOrLater() && XRE_IsContentProcess()) {
+ if (XRE_IsContentProcess()) {
result |= MWMO_ALERTABLE;
}
return result;
@@ -1878,7 +1876,7 @@ WinUtils::IsTouchDeviceSupportPresent()
uint32_t
WinUtils::GetMaxTouchPoints()
{
- if (IsWin7OrLater() && IsTouchDeviceSupportPresent()) {
+ if (IsTouchDeviceSupportPresent()) {
return GetSystemMetrics(SM_MAXIMUMTOUCHES);
}
return 0;
@@ -1893,11 +1891,6 @@ typedef DWORD (WINAPI * GetFinalPathNameByHandlePtr)(HANDLE hFile,
bool
WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath)
{
- // Users folder was introduced with Vista.
- if (!IsVistaOrLater()) {
- return true;
- }
-
wchar_t path[MAX_PATH] = { 0 };
nsAutoHandle handle(
@@ -2025,21 +2018,19 @@ WinUtils::GetAppInitDLLs(nsAString& aOutput)
}
nsAutoRegKey key(hkey);
LONG status;
- if (IsVistaOrLater()) {
- const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
- DWORD loadAppInitDLLs = 0;
- DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
- status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr,
- nullptr, (LPBYTE)&loadAppInitDLLs,
- &loadAppInitDLLsLen);
- if (status != ERROR_SUCCESS) {
- return false;
- }
- if (!loadAppInitDLLs) {
- // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
- // In this case we'll return true along with an empty output string.
- return true;
- }
+ const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
+ DWORD loadAppInitDLLs = 0;
+ DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
+ status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr,
+ nullptr, (LPBYTE)&loadAppInitDLLs,
+ &loadAppInitDLLsLen);
+ if (status != ERROR_SUCCESS) {
+ return false;
+ }
+ if (!loadAppInitDLLs) {
+ // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
+ // In this case we'll return true along with an empty output string.
+ return true;
}
DWORD numBytes = 0;
const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
diff --git a/widget/windows/WindowsUIUtils.cpp b/widget/windows/WindowsUIUtils.cpp
index 223511859..1c270b5ec 100644
--- a/widget/windows/WindowsUIUtils.cpp
+++ b/widget/windows/WindowsUIUtils.cpp
@@ -5,7 +5,8 @@
#include <windows.h>
#include <winsdkver.h>
-#include "mozwrlbase.h"
+#include <wrl.h>
+
#include "nsServiceManagerUtils.h"
#include "WindowsUIUtils.h"
@@ -179,4 +180,3 @@ WindowsUIUtils::UpdateTabletModeState()
return NS_OK;
}
-
diff --git a/widget/windows/mozwrlbase.h b/widget/windows/mozwrlbase.h
deleted file mode 100644
index d82be8f04..000000000
--- a/widget/windows/mozwrlbase.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- 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/. */
-
-#pragma once
-
-/*
- * Includes <wrl.h> and it's children. Defines imports needed by
- * corewrappers.h in the case where windows.h has already been
- * included w/WINVER < 0x600. Also ups WINVER/_WIN32_WINNT prior
- * to including wrl.h. Mozilla's build currently has WINVER set to
- * 0x502 for XP support.
- */
-
-#if _WIN32_WINNT < 0x600
-
-#include <windows.h>
-
-VOID
-WINAPI
-ReleaseSRWLockExclusive(
- _Inout_ PSRWLOCK SRWLock
- );
-
-VOID
-WINAPI
-ReleaseSRWLockShared(
- _Inout_ PSRWLOCK SRWLock
- );
-
-BOOL
-WINAPI
-InitializeCriticalSectionEx(
- _Out_ LPCRITICAL_SECTION lpCriticalSection,
- _In_ DWORD dwSpinCount,
- _In_ DWORD Flags
- );
-
-VOID
-WINAPI
-InitializeSRWLock(
- _Out_ PSRWLOCK SRWLock
- );
-
-VOID
-WINAPI
-AcquireSRWLockExclusive(
- _Inout_ PSRWLOCK SRWLock
- );
-
-BOOLEAN
-WINAPI
-TryAcquireSRWLockExclusive(
- _Inout_ PSRWLOCK SRWLock
- );
-
-BOOLEAN
-WINAPI
-TryAcquireSRWLockShared(
- _Inout_ PSRWLOCK SRWLock
- );
-
-VOID
-WINAPI
-AcquireSRWLockShared(
- _Inout_ PSRWLOCK SRWLock
- );
-
-#undef WINVER
-#undef _WIN32_WINNT
-#define WINVER 0x600
-#define _WIN32_WINNT 0x600
-
-#endif // _WIN32_WINNT < 0x600
-
-#include <wrl.h>
diff --git a/widget/windows/nsDataObj.cpp b/widget/windows/nsDataObj.cpp
index fc45968ae..02ec3b2fe 100644
--- a/widget/windows/nsDataObj.cpp
+++ b/widget/windows/nsDataObj.cpp
@@ -1148,8 +1148,7 @@ nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
const char *shortcutFormatStr;
int totalLen;
nsCString path;
- if (!Preferences::GetBool(kShellIconPref, true) ||
- !IsVistaOrLater()) {
+ if (!Preferences::GetBool(kShellIconPref, true)) {
shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s
totalLen = formatLen + asciiUrl.Length(); // don't include null character
diff --git a/widget/windows/nsFilePicker.cpp b/widget/windows/nsFilePicker.cpp
index 53857cf5e..4e942968a 100644
--- a/widget/windows/nsFilePicker.cpp
+++ b/widget/windows/nsFilePicker.cpp
@@ -28,7 +28,6 @@
#include "nsPIDOMWindow.h"
#include "GeckoProfiler.h"
-using mozilla::IsVistaOrLater;
using mozilla::IsWin8OrLater;
using mozilla::MakeUnique;
using mozilla::mscom::EnsureMTA;
@@ -219,191 +218,6 @@ STDMETHODIMP nsFilePicker::QueryInterface(REFIID refiid, void** ppvResult)
return E_NOINTERFACE;
}
-/*
- * XP picker callbacks
- */
-
-// Show - Display the file dialog
-int CALLBACK
-BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
-{
- if (uMsg == BFFM_INITIALIZED)
- {
- char16_t * filePath = (char16_t *) lpData;
- if (filePath)
- ::SendMessageW(hwnd, BFFM_SETSELECTIONW,
- TRUE /* true because lpData is a path string */,
- lpData);
- }
- return 0;
-}
-
-static void
-EnsureWindowVisible(HWND hwnd)
-{
- // Obtain the monitor which has the largest area of intersection
- // with the window, or nullptr if there is no intersection.
- HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
- if (!monitor) {
- // The window is not visible, we should reposition it to the same place as its parent
- HWND parentHwnd = GetParent(hwnd);
- RECT parentRect;
- GetWindowRect(parentHwnd, &parentRect);
- SetWindowPos(hwnd, nullptr, parentRect.left, parentRect.top, 0, 0,
- SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
- }
-}
-
-// Callback hook which will ensure that the window is visible. Currently
-// only in use on os <= XP.
-UINT_PTR CALLBACK
-nsFilePicker::FilePickerHook(HWND hwnd,
- UINT msg,
- WPARAM wParam,
- LPARAM lParam)
-{
- switch(msg) {
- case WM_NOTIFY:
- {
- LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
- if (!lpofn || !lpofn->lpOFN) {
- return 0;
- }
-
- if (CDN_INITDONE == lpofn->hdr.code) {
- // The Window will be automatically moved to the last position after
- // CDN_INITDONE. We post a message to ensure the window will be visible
- // so it will be done after the automatic last position window move.
- PostMessage(hwnd, MOZ_WM_ENSUREVISIBLE, 0, 0);
- }
- }
- break;
- case MOZ_WM_ENSUREVISIBLE:
- EnsureWindowVisible(GetParent(hwnd));
- break;
- case WM_INITDIALOG:
- {
- OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
- SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
- nsFilePicker* picker = reinterpret_cast<nsFilePicker*>(pofn->lCustData);
- if (picker) {
- picker->SetDialogHandle(hwnd);
- SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, nullptr);
- }
- }
- break;
- case WM_TIMER:
- {
- // Check to see if our parent has been torn down, if so, we close too.
- if (wParam == kDialogTimerID) {
- nsFilePicker* picker =
- reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
- if (picker && picker->ClosePickerIfNeeded(true)) {
- KillTimer(hwnd, kDialogTimerID);
- }
- }
- }
- break;
- }
- return 0;
-}
-
-
-// Callback hook which will dynamically allocate a buffer large enough
-// for the file picker dialog. Currently only in use on os <= XP.
-UINT_PTR CALLBACK
-nsFilePicker::MultiFilePickerHook(HWND hwnd,
- UINT msg,
- WPARAM wParam,
- LPARAM lParam)
-{
- switch (msg) {
- case WM_INITDIALOG:
- {
- // Finds the child drop down of a File Picker dialog and sets the
- // maximum amount of text it can hold when typed in manually.
- // A wParam of 0 mean 0x7FFFFFFE characters.
- HWND comboBox = FindWindowEx(GetParent(hwnd), nullptr,
- L"ComboBoxEx32", nullptr );
- if(comboBox)
- SendMessage(comboBox, CB_LIMITTEXT, 0, 0);
- // Store our nsFilePicker ptr for future use
- OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
- SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
- nsFilePicker* picker =
- reinterpret_cast<nsFilePicker*>(pofn->lCustData);
- if (picker) {
- picker->SetDialogHandle(hwnd);
- SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, nullptr);
- }
- }
- break;
- case WM_NOTIFY:
- {
- LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
- if (!lpofn || !lpofn->lpOFN) {
- return 0;
- }
- // CDN_SELCHANGE is sent when the selection in the list box of the file
- // selection dialog changes
- if (lpofn->hdr.code == CDN_SELCHANGE) {
- HWND parentHWND = GetParent(hwnd);
-
- // Get the required size for the selected files buffer
- UINT newBufLength = 0;
- int requiredBufLength = CommDlg_OpenSave_GetSpecW(parentHWND,
- nullptr, 0);
- if(requiredBufLength >= 0)
- newBufLength += requiredBufLength;
- else
- newBufLength += MAX_PATH;
-
- // If the user selects multiple files, the buffer contains the
- // current directory followed by the file names of the selected
- // files. So make room for the directory path. If the user
- // selects a single file, it is no harm to add extra space.
- requiredBufLength = CommDlg_OpenSave_GetFolderPathW(parentHWND,
- nullptr, 0);
- if(requiredBufLength >= 0)
- newBufLength += requiredBufLength;
- else
- newBufLength += MAX_PATH;
-
- // Check if lpstrFile and nMaxFile are large enough
- if (newBufLength > lpofn->lpOFN->nMaxFile) {
- if (lpofn->lpOFN->lpstrFile)
- delete[] lpofn->lpOFN->lpstrFile;
-
- // We allocate FILE_BUFFER_SIZE more bytes than is needed so that
- // if the user selects a file and holds down shift and down to
- // select additional items, we will not continuously reallocate
- newBufLength += FILE_BUFFER_SIZE;
-
- wchar_t* filesBuffer = new wchar_t[newBufLength];
- ZeroMemory(filesBuffer, newBufLength * sizeof(wchar_t));
-
- lpofn->lpOFN->lpstrFile = filesBuffer;
- lpofn->lpOFN->nMaxFile = newBufLength;
- }
- }
- }
- break;
- case WM_TIMER:
- {
- // Check to see if our parent has been torn down, if so, we close too.
- if (wParam == kDialogTimerID) {
- nsFilePicker* picker =
- reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
- if (picker && picker->ClosePickerIfNeeded(true)) {
- KillTimer(hwnd, kDialogTimerID);
- }
- }
- }
- break;
- }
-
- return FilePickerHook(hwnd, msg, wParam, lParam);
-}
/*
* Vista+ callbacks
@@ -476,25 +290,18 @@ nsFilePicker::OnOverwrite(IFileDialog *pfd,
*/
bool
-nsFilePicker::ClosePickerIfNeeded(bool aIsXPDialog)
+nsFilePicker::ClosePickerIfNeeded()
{
if (!mParentWidget || !mDlgWnd)
return false;
nsWindow *win = static_cast<nsWindow *>(mParentWidget.get());
- // Note, the xp callbacks hand us an inner window, so we have to step up
- // one to get the actual dialog.
- HWND dlgWnd;
- if (aIsXPDialog)
- dlgWnd = GetParent(mDlgWnd);
- else
- dlgWnd = mDlgWnd;
- if (IsWindow(dlgWnd) && IsWindowVisible(dlgWnd) && win->DestroyCalled()) {
+ if (IsWindow(mDlgWnd) && IsWindowVisible(mDlgWnd) && win->DestroyCalled()) {
wchar_t className[64];
// Make sure we have the right window
- if (GetClassNameW(dlgWnd, className, mozilla::ArrayLength(className)) &&
+ if (GetClassNameW(mDlgWnd, className, mozilla::ArrayLength(className)) &&
!wcscmp(className, L"#32770") &&
- DestroyWindow(dlgWnd)) {
+ DestroyWindow(mDlgWnd)) {
mDlgWnd = nullptr;
return true;
}
@@ -506,7 +313,7 @@ void
nsFilePicker::PickerCallbackTimerFunc(nsITimer *aTimer, void *aCtx)
{
nsFilePicker* picker = (nsFilePicker*)aCtx;
- if (picker->ClosePickerIfNeeded(false)) {
+ if (picker->ClosePickerIfNeeded()) {
aTimer->Cancel();
}
}
@@ -523,62 +330,15 @@ nsFilePicker::SetDialogHandle(HWND aWnd)
* Folder picker invocation
*/
-// Open the older XP style folder picker dialog. We end up in this call
-// on XP systems or when platform is built without the longhorn SDK.
-bool
-nsFilePicker::ShowXPFolderPicker(const nsString& aInitialDir)
-{
- bool result = false;
-
- auto dirBuffer = MakeUnique<wchar_t[]>(FILE_BUFFER_SIZE);
- wcsncpy(dirBuffer.get(), aInitialDir.get(), FILE_BUFFER_SIZE);
- dirBuffer[FILE_BUFFER_SIZE-1] = '\0';
-
- AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
- mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
-
- BROWSEINFOW browserInfo = {0};
- browserInfo.pidlRoot = nullptr;
- browserInfo.pszDisplayName = dirBuffer.get();
- browserInfo.lpszTitle = mTitle.get();
- browserInfo.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
- browserInfo.hwndOwner = adtw.get();
- browserInfo.iImage = 0;
- browserInfo.lParam = reinterpret_cast<LPARAM>(this);
-
- if (!aInitialDir.IsEmpty()) {
- // the dialog is modal so that |initialDir.get()| will be valid in
- // BrowserCallbackProc. Thus, we don't need to clone it.
- browserInfo.lParam = (LPARAM) aInitialDir.get();
- browserInfo.lpfn = &BrowseCallbackProc;
- } else {
- browserInfo.lParam = 0;
- browserInfo.lpfn = nullptr;
- }
-
- LPITEMIDLIST list = ::SHBrowseForFolderW(&browserInfo);
- if (list) {
- result = ::SHGetPathFromIDListW(list, dirBuffer.get());
- if (result)
- mUnicodeFile.Assign(static_cast<const wchar_t*>(dirBuffer.get()));
- // free PIDL
- CoTaskMemFree(list);
- }
-
- return result;
-}
-
/*
- * Show a folder picker post Windows XP
- *
+ * Show a folder picker.
+ *
* @param aInitialDir The initial directory, the last used directory will be
* used if left blank.
- * @param aWasInitError Out parameter will hold true if there was an error
- * before the folder picker is shown.
* @return true if a file was selected successfully.
*/
bool
-nsFilePicker::ShowFolderPicker(const nsString& aInitialDir, bool &aWasInitError)
+nsFilePicker::ShowFolderPicker(const nsString& aInitialDir)
{
if (!IsWin8OrLater()) {
// Some Windows 7 users are experiencing a race condition when some dlls
@@ -593,10 +353,8 @@ nsFilePicker::ShowFolderPicker(const nsString& aInitialDir, bool &aWasInitError)
if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,
IID_IFileOpenDialog,
getter_AddRefs(dialog)))) {
- aWasInitError = true;
return false;
}
- aWasInitError = false;
// hook up event callbacks
dialog->Advise(this, &mFDECookie);
@@ -658,233 +416,15 @@ nsFilePicker::ShowFolderPicker(const nsString& aInitialDir, bool &aWasInitError)
* File open and save picker invocation
*/
-/* static */ bool
-nsFilePicker::GetFileNameWrapper(OPENFILENAMEW* ofn, PickerType aType)
-{
- MOZ_SEH_TRY {
- if (aType == PICKER_TYPE_OPEN)
- return ::GetOpenFileNameW(ofn);
- else if (aType == PICKER_TYPE_SAVE)
- return ::GetSaveFileNameW(ofn);
- } MOZ_SEH_EXCEPT(true) {
- NS_ERROR("nsFilePicker GetFileName win32 call generated an exception! This is bad!");
- }
- return false;
-}
-
-bool
-nsFilePicker::FilePickerWrapper(OPENFILENAMEW* ofn, PickerType aType)
-{
- if (!ofn)
- return false;
- AutoWidgetPickerState awps(mParentWidget);
- return GetFileNameWrapper(ofn, aType);
-}
-
-bool
-nsFilePicker::ShowXPFilePicker(const nsString& aInitialDir)
-{
- OPENFILENAMEW ofn = {0};
- ofn.lStructSize = sizeof(ofn);
- nsString filterBuffer = mFilterList;
-
- auto fileBuffer = MakeUnique<wchar_t[]>(FILE_BUFFER_SIZE);
- wcsncpy(fileBuffer.get(), mDefaultFilePath.get(), FILE_BUFFER_SIZE);
- fileBuffer[FILE_BUFFER_SIZE-1] = '\0'; // null terminate in case copy truncated
-
- if (!aInitialDir.IsEmpty()) {
- ofn.lpstrInitialDir = aInitialDir.get();
- }
-
- AutoDestroyTmpWindow adtw((HWND) (mParentWidget.get() ?
- mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
-
- ofn.lpstrTitle = (LPCWSTR)mTitle.get();
- ofn.lpstrFilter = (LPCWSTR)filterBuffer.get();
- ofn.nFilterIndex = mSelectedType;
- ofn.lpstrFile = fileBuffer.get();
- ofn.nMaxFile = FILE_BUFFER_SIZE;
- ofn.hwndOwner = adtw.get();
- ofn.lCustData = reinterpret_cast<LPARAM>(this);
- ofn.Flags = OFN_SHAREAWARE | OFN_LONGNAMES | OFN_OVERWRITEPROMPT |
- OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_ENABLESIZING |
- OFN_EXPLORER;
-
- // Windows Vista and up won't allow you to use the new looking dialogs with
- // a hook procedure. The hook procedure fixes a problem on XP dialogs for
- // file picker visibility. Vista and up automatically ensures the file
- // picker is always visible.
- if (!IsVistaOrLater()) {
- ofn.lpfnHook = FilePickerHook;
- ofn.Flags |= OFN_ENABLEHOOK;
- }
-
- // Handle add to recent docs settings
- if (IsPrivacyModeEnabled() || !mAddToRecentDocs) {
- ofn.Flags |= OFN_DONTADDTORECENT;
- }
-
- NS_NAMED_LITERAL_STRING(htmExt, "html");
-
- if (!mDefaultExtension.IsEmpty()) {
- ofn.lpstrDefExt = mDefaultExtension.get();
- } else if (IsDefaultPathHtml()) {
- // Get file extension from suggested filename to detect if we are
- // saving an html file.
- // This is supposed to append ".htm" if user doesn't supply an
- // extension but the behavior is sort of weird:
- // - Often appends ".html" even if you have an extension
- // - It obeys your extension if you put quotes around name
- ofn.lpstrDefExt = htmExt.get();
- }
-
- // When possible, instead of using OFN_NOCHANGEDIR to ensure the current
- // working directory will not change from this call, we will retrieve the
- // current working directory before the call and restore it after the
- // call. This flag causes problems on Windows XP for paths that are
- // selected like C:test.txt where the user is currently at C:\somepath
- // In which case expected result should be C:\somepath\test.txt
- AutoRestoreWorkingPath restoreWorkingPath;
- // If we can't get the current working directory, the best case is to
- // use the OFN_NOCHANGEDIR flag
- if (!restoreWorkingPath.HasWorkingPath()) {
- ofn.Flags |= OFN_NOCHANGEDIR;
- }
-
- bool result = false;
-
- switch(mMode) {
- case modeOpen:
- // FILE MUST EXIST!
- ofn.Flags |= OFN_FILEMUSTEXIST;
- result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
- break;
-
- case modeOpenMultiple:
- ofn.Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT;
-
- // The hook set here ensures that the buffer returned will always be
- // large enough to hold all selected files. The hook may modify the
- // value of ofn.lpstrFile and deallocate the old buffer that it pointed
- // to (fileBuffer). The hook assumes that the passed in value is heap
- // allocated and that the returned value should be freed by the caller.
- // If the hook changes the buffer, it will deallocate the old buffer.
- // This fix would be nice to have in Vista and up, but it would force
- // the file picker to use the old style dialogs because hooks are not
- // allowed in the new file picker UI. We need to eventually move to
- // the new Common File Dialogs for Vista and up.
- if (!IsVistaOrLater()) {
- ofn.lpfnHook = MultiFilePickerHook;
- fileBuffer.release();
- result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
- fileBuffer.reset(ofn.lpstrFile);
- } else {
- result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
- }
- break;
-
- case modeSave:
- {
- ofn.Flags |= OFN_NOREADONLYRETURN;
-
- // Don't follow shortcuts when saving a shortcut, this can be used
- // to trick users (bug 271732)
- if (IsDefaultPathLink())
- ofn.Flags |= OFN_NODEREFERENCELINKS;
-
- result = FilePickerWrapper(&ofn, PICKER_TYPE_SAVE);
- if (!result) {
- // Error, find out what kind.
- if (GetLastError() == ERROR_INVALID_PARAMETER ||
- CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
- // Probably the default file name is too long or contains illegal
- // characters. Try again, without a starting file name.
- ofn.lpstrFile[0] = L'\0';
- result = FilePickerWrapper(&ofn, PICKER_TYPE_SAVE);
- }
- }
- }
- break;
-
- default:
- NS_NOTREACHED("unsupported file picker mode");
- return false;
- }
-
- if (!result)
- return false;
-
- // Remember what filter type the user selected
- mSelectedType = (int16_t)ofn.nFilterIndex;
-
- // Single file selection, we're done
- if (mMode != modeOpenMultiple) {
- GetQualifiedPath(fileBuffer.get(), mUnicodeFile);
- return true;
- }
-
- // Set user-selected location of file or directory. From msdn's "Open and
- // Save As Dialog Boxes" section:
- // If you specify OFN_EXPLORER, the directory and file name strings are '\0'
- // separated, with an extra '\0' character after the last file name. This
- // format enables the Explorer-style dialog boxes to return long file names
- // that include spaces.
- wchar_t *current = fileBuffer.get();
-
- nsAutoString dirName(current);
- // Sometimes dirName contains a trailing slash and sometimes it doesn't:
- if (current[dirName.Length() - 1] != '\\')
- dirName.Append((char16_t)'\\');
-
- while (current && *current && *(current + wcslen(current) + 1)) {
- current = current + wcslen(current) + 1;
-
- nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
- NS_ENSURE_TRUE(file, false);
-
- // Only prepend the directory if the path specified is a relative path
- nsAutoString path;
- if (PathIsRelativeW(current)) {
- path = dirName + nsDependentString(current);
- } else {
- path = current;
- }
-
- nsAutoString canonicalizedPath;
- GetQualifiedPath(path.get(), canonicalizedPath);
- if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
- !mFiles.AppendObject(file))
- return false;
- }
-
- // Handle the case where the user selected just one file. From msdn: If you
- // specify OFN_ALLOWMULTISELECT and the user selects only one file the
- // lpstrFile string does not have a separator between the path and file name.
- if (current && *current && (current == fileBuffer.get())) {
- nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
- NS_ENSURE_TRUE(file, false);
-
- nsAutoString canonicalizedPath;
- GetQualifiedPath(current, canonicalizedPath);
- if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
- !mFiles.AppendObject(file))
- return false;
- }
-
- return true;
-}
-
/*
- * Show a file picker post Windows XP
- *
+ * Show a file picker.
+ *
* @param aInitialDir The initial directory, the last used directory will be
* used if left blank.
- * @param aWasInitError Out parameter will hold true if there was an error
- * before the file picker is shown.
* @return true if a file was selected successfully.
*/
bool
-nsFilePicker::ShowFilePicker(const nsString& aInitialDir, bool &aWasInitError)
+nsFilePicker::ShowFilePicker(const nsString& aInitialDir)
{
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
@@ -902,18 +442,15 @@ nsFilePicker::ShowFilePicker(const nsString& aInitialDir, bool &aWasInitError)
if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,
IID_IFileOpenDialog,
getter_AddRefs(dialog)))) {
- aWasInitError = true;
return false;
}
} else {
if (FAILED(CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC,
IID_IFileSaveDialog,
getter_AddRefs(dialog)))) {
- aWasInitError = true;
return false;
}
}
- aWasInitError = false;
// hook up event callbacks
dialog->Advise(this, &mFDECookie);
@@ -1080,22 +617,11 @@ nsFilePicker::ShowW(int16_t *aReturnVal)
// with our context set temporarily to system-dpi-aware
WinUtils::AutoSystemDpiAware dpiAwareness;
- // Launch the XP file/folder picker on XP and as a fallback on Vista+.
- // The CoCreateInstance call to CLSID_FileOpenDialog fails with "(0x80040111)
- // ClassFactory cannot supply requested class" when the checkbox for
- // Disable Visual Themes is on in the compatability tab within the shortcut
- // properties.
- bool result = false, wasInitError = true;
+ bool result = false;
if (mMode == modeGetFolder) {
- if (IsVistaOrLater())
- result = ShowFolderPicker(initialDir, wasInitError);
- if (!result && wasInitError)
- result = ShowXPFolderPicker(initialDir);
+ result = ShowFolderPicker(initialDir);
} else {
- if (IsVistaOrLater())
- result = ShowFilePicker(initialDir, wasInitError);
- if (!result && wasInitError)
- result = ShowXPFilePicker(initialDir);
+ result = ShowFilePicker(initialDir);
}
// exit, and return returnCancel in aReturnVal
@@ -1247,48 +773,10 @@ nsFilePicker::InitNative(nsIWidget *aParent,
mTitle.Assign(aTitle);
}
-void
-nsFilePicker::GetQualifiedPath(const wchar_t *aInPath, nsString &aOutPath)
-{
- // Prefer a qualified path over a non qualified path.
- // Things like c:file.txt would be accepted in Win XP but would later
- // fail to open from the download manager.
- wchar_t qualifiedFileBuffer[MAX_PATH];
- if (PathSearchAndQualifyW(aInPath, qualifiedFileBuffer, MAX_PATH)) {
- aOutPath.Assign(qualifiedFileBuffer);
- } else {
- aOutPath.Assign(aInPath);
- }
-}
-
-void
-nsFilePicker::AppendXPFilter(const nsAString& aTitle, const nsAString& aFilter)
-{
- mFilterList.Append(aTitle);
- mFilterList.Append(char16_t('\0'));
-
- if (aFilter.EqualsLiteral("..apps"))
- mFilterList.AppendLiteral("*.exe;*.com");
- else
- {
- nsAutoString filter(aFilter);
- filter.StripWhitespace();
- if (filter.EqualsLiteral("*"))
- filter.AppendLiteral(".*");
- mFilterList.Append(filter);
- }
-
- mFilterList.Append(char16_t('\0'));
-}
-
NS_IMETHODIMP
nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
{
- if (IsVistaOrLater()) {
- mComFilterList.Append(aTitle, aFilter);
- } else {
- AppendXPFilter(aTitle, aFilter);
- }
+ mComFilterList.Append(aTitle, aFilter);
return NS_OK;
}
diff --git a/widget/windows/nsFilePicker.h b/widget/windows/nsFilePicker.h
index 90d8c15bc..740f07a7d 100644
--- a/widget/windows/nsFilePicker.h
+++ b/widget/windows/nsFilePicker.h
@@ -9,17 +9,6 @@
#include <windows.h>
-// For Vista IFileDialog interfaces which aren't exposed
-// unless _WIN32_WINNT >= _WIN32_WINNT_LONGHORN.
-#if _WIN32_WINNT < _WIN32_WINNT_LONGHORN
-#define _WIN32_WINNT_bak _WIN32_WINNT
-#undef _WIN32_WINNT
-#define _WIN32_WINNT _WIN32_WINNT_LONGHORN
-#define _WIN32_IE_bak _WIN32_IE
-#undef _WIN32_IE
-#define _WIN32_IE _WIN32_IE_IE70
-#endif
-
#include "nsIFile.h"
#include "nsITimer.h"
#include "nsISimpleEnumerator.h"
@@ -87,32 +76,19 @@ public:
HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse);
protected:
- enum PickerType {
- PICKER_TYPE_OPEN,
- PICKER_TYPE_SAVE,
- };
-
/* method from nsBaseFilePicker */
virtual void InitNative(nsIWidget *aParent,
const nsAString& aTitle);
- static void GetQualifiedPath(const wchar_t *aInPath, nsString &aOutPath);
void GetFilterListArray(nsString& aFilterList);
- static bool GetFileNameWrapper(OPENFILENAMEW* ofn, PickerType aType);
- bool FilePickerWrapper(OPENFILENAMEW* ofn, PickerType aType);
- bool ShowXPFolderPicker(const nsString& aInitialDir);
- bool ShowXPFilePicker(const nsString& aInitialDir);
- bool ShowFolderPicker(const nsString& aInitialDir, bool &aWasInitError);
- bool ShowFilePicker(const nsString& aInitialDir, bool &aWasInitError);
- void AppendXPFilter(const nsAString& aTitle, const nsAString& aFilter);
+ bool ShowFolderPicker(const nsString& aInitialDir);
+ bool ShowFilePicker(const nsString& aInitialDir);
void RememberLastUsedDirectory();
bool IsPrivacyModeEnabled();
bool IsDefaultPathLink();
bool IsDefaultPathHtml();
void SetDialogHandle(HWND aWnd);
- bool ClosePickerIfNeeded(bool aIsXPDialog);
+ bool ClosePickerIfNeeded();
static void PickerCallbackTimerFunc(nsITimer *aTimer, void *aPicker);
- static UINT_PTR CALLBACK MultiFilePickerHook(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
- static UINT_PTR CALLBACK FilePickerHook(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
nsCOMPtr<nsILoadContext> mLoadContext;
nsCOMPtr<nsIWidget> mParentWidget;
@@ -154,11 +130,4 @@ protected:
DWORD mFDECookie;
};
-#if defined(_WIN32_WINNT_bak)
-#undef _WIN32_WINNT
-#define _WIN32_WINNT _WIN32_WINNT_bak
-#undef _WIN32_IE
-#define _WIN32_IE _WIN32_IE_bak
-#endif
-
#endif // nsFilePicker_h__
diff --git a/widget/windows/nsLookAndFeel.cpp b/widget/windows/nsLookAndFeel.cpp
index e649802b1..06eee3771 100644
--- a/widget/windows/nsLookAndFeel.cpp
+++ b/widget/windows/nsLookAndFeel.cpp
@@ -32,12 +32,8 @@ nsLookAndFeel::GetOperatingSystemVersion()
version = eOperatingSystemVersion_Windows10;
} else if (IsWin8OrLater()) {
version = eOperatingSystemVersion_Windows8;
- } else if (IsWin7OrLater()) {
- version = eOperatingSystemVersion_Windows7;
- } else if (IsVistaOrLater()) {
- version = eOperatingSystemVersion_WindowsVista;
} else {
- version = eOperatingSystemVersion_WindowsXP;
+ version = eOperatingSystemVersion_Windows7;
}
return version;
@@ -183,8 +179,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
idx = COLOR_HIGHLIGHT;
break;
case eColorID__moz_menubarhovertext:
- if (!IsVistaOrLater() || !IsAppThemed())
- {
+ if (!IsAppThemed()) {
idx = nsUXThemeData::sFlatMenus ?
COLOR_HIGHLIGHTTEXT :
COLOR_MENUTEXT;
@@ -192,8 +187,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
}
// Fall through
case eColorID__moz_menuhovertext:
- if (IsVistaOrLater() && IsAppThemed())
- {
+ if (IsAppThemed()) {
res = ::GetColorFromTheme(eUXMenu,
MENU_POPUPITEM, MPI_HOT, TMT_TEXTCOLOR, aColor);
if (NS_SUCCEEDED(res))
@@ -285,7 +279,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
aColor = NS_RGB(0, 0, 0);
return NS_OK;
case eColorID__moz_win_mediatext:
- if (IsVistaOrLater() && IsAppThemed()) {
+ if (IsAppThemed()) {
res = ::GetColorFromTheme(eUXMediaToolbar,
TP_BUTTON, TS_NORMAL, TMT_TEXTCOLOR, aColor);
if (NS_SUCCEEDED(res))
@@ -295,8 +289,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
idx = COLOR_WINDOWTEXT;
break;
case eColorID__moz_win_communicationstext:
- if (IsVistaOrLater() && IsAppThemed())
- {
+ if (IsAppThemed()) {
res = ::GetColorFromTheme(eUXCommunicationsToolbar,
TP_BUTTON, TS_NORMAL, TMT_TEXTCOLOR, aColor);
if (NS_SUCCEEDED(res))
diff --git a/widget/windows/nsLookAndFeel.h b/widget/windows/nsLookAndFeel.h
index 6200541f5..a77808322 100644
--- a/widget/windows/nsLookAndFeel.h
+++ b/widget/windows/nsLookAndFeel.h
@@ -5,6 +5,7 @@
#ifndef __nsLookAndFeel
#define __nsLookAndFeel
+
#include "nsXPLookAndFeel.h"
#include "nsIWindowsRegKey.h"
@@ -28,18 +29,6 @@
#ifndef SM_SYSTEMDOCKED
#define SM_CONVERTIBLESLATEMODE 0x00002003
#define SM_SYSTEMDOCKED 0x00002004
-typedef enum _AR_STATE
-{
- AR_ENABLED = 0x0,
- AR_DISABLED = 0x1,
- AR_SUPPRESSED = 0x2,
- AR_REMOTESESSION = 0x4,
- AR_MULTIMON = 0x8,
- AR_NOSENSOR = 0x10,
- AR_NOT_SUPPORTED = 0x20,
- AR_DOCKED = 0x40,
- AR_LAPTOP = 0x80
-} AR_STATE, *PAR_STATE;
#endif
class nsLookAndFeel: public nsXPLookAndFeel {
diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp
index 4ff6b0af9..8950dcf90 100644
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -39,7 +39,6 @@
#include "nsUXThemeConstants.h"
#include <algorithm>
-using mozilla::IsVistaOrLater;
using namespace mozilla;
using namespace mozilla::widget;
@@ -301,10 +300,6 @@ DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState,
* aero basic max 0 2 1 2
* aero basic close 1 2 1 2
*
- * xp theme min 0 2 0 2
- * xp theme max 0 2 1 2
- * xp theme close 1 2 2 2
- *
* 'cold' button padding - generic button padding, should
* be handled in css.
* left top right bottom
@@ -315,16 +310,11 @@ DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState,
* aero basic min 0 0 1 0
* aero basic max 1 0 0 0
* aero basic close 0 0 0 0
- *
- * xp theme min 0 0 1 0
- * xp theme max 1 0 0 0
- * xp theme close 0 0 0 0
*/
enum CaptionDesktopTheme {
CAPTION_CLASSIC = 0,
CAPTION_BASIC,
- CAPTION_XPTHEME,
};
enum CaptionButton {
@@ -358,8 +348,6 @@ AddPaddingRect(LayoutDeviceIntSize* aSize, CaptionButton button) {
RECT offset;
if (!IsAppThemed())
offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
- else if (!IsVistaOrLater())
- offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
else
offset = buttonData[CAPTION_BASIC].hotPadding[button];
aSize->width += offset.left + offset.right;
@@ -373,8 +361,6 @@ OffsetBackgroundRect(RECT& rect, CaptionButton button) {
RECT offset;
if (!IsAppThemed())
offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
- else if (!IsVistaOrLater())
- offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
else
offset = buttonData[CAPTION_BASIC].hotPadding[button];
rect.left += offset.left;
@@ -419,9 +405,7 @@ OffsetBackgroundRect(RECT& rect, CaptionButton button) {
static const double kProgressDeterminateTimeSpan = 3.0;
static const double kProgressIndeterminateTimeSpan = 5.0;
// The width of the overlay used to animate the horizontal progress bar (Vista and later).
-static const int32_t kProgressHorizontalVistaOverlaySize = 120;
-// The width of the overlay used for the horizontal indeterminate progress bars on XP.
-static const int32_t kProgressHorizontalXPOverlaySize = 55;
+static const int32_t kProgressHorizontalOverlaySize = 120;
// The height of the overlay used to animate the vertical progress bar (Vista and later).
static const int32_t kProgressVerticalOverlaySize = 45;
// The height of the overlay used for the vertical indeterminate progress bar (Vista and later).
@@ -435,18 +419,8 @@ static const int32_t kProgressClassicOverlaySize = 40;
*/
static int32_t
GetProgressOverlayStyle(bool aIsVertical)
-{
- if (aIsVertical) {
- if (IsVistaOrLater()) {
- return PP_MOVEOVERLAYVERT;
- }
- return PP_CHUNKVERT;
- } else {
- if (IsVistaOrLater()) {
- return PP_MOVEOVERLAY;
- }
- return PP_CHUNK;
- }
+{
+ return aIsVertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
}
/*
@@ -457,14 +431,11 @@ GetProgressOverlayStyle(bool aIsVertical)
static int32_t
GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate)
{
- if (IsVistaOrLater()) {
- if (aIsVertical) {
- return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
- : kProgressVerticalOverlaySize;
- }
- return kProgressHorizontalVistaOverlaySize;
+ if (aIsVertical) {
+ return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
+ : kProgressVerticalOverlaySize;
}
- return kProgressHorizontalXPOverlaySize;
+ return kProgressHorizontalOverlaySize;
}
/*
@@ -554,87 +525,6 @@ nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame,
}
/*
- * DrawChunkProgressMeter - renders an xp style chunked progress meter. Called
- * by DrawProgressMeter.
- *
- * @param aTheme progress theme handle
- * @param aHdc hdc returned by gfxWindowsNativeDrawing
- * @param aPart the PP_X progress part
- * @param aState the theme state
- * @param aFrame the elements frame
- * @param aWidgetRect bounding rect for the widget
- * @param aClipRect dirty rect that needs drawing.
- * @param aAppUnits app units per device pixel
- * @param aIsIndeterm is an indeterminate progress?
- * @param aIsVertical render a vertical progress?
- * @param aIsRtl direction is rtl
- */
-static void
-DrawChunkProgressMeter(HTHEME aTheme, HDC aHdc, int aPart,
- int aState, nsIFrame* aFrame, RECT* aWidgetRect,
- RECT* aClipRect, gfxFloat aAppUnits, bool aIsIndeterm,
- bool aIsVertical, bool aIsRtl)
-{
- NS_ASSERTION(aTheme, "Bad theme.");
- NS_ASSERTION(aHdc, "Bad hdc.");
- NS_ASSERTION(aWidgetRect, "Bad rect.");
- NS_ASSERTION(aClipRect, "Bad clip rect.");
- NS_ASSERTION(aFrame, "Bad frame.");
-
- // For horizontal meters, the theme lib paints the right graphic but doesn't
- // paint the chunks, so we do that manually. For vertical meters, the theme
- // library draws everything correctly.
- if (aIsVertical) {
- DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
- return;
- }
-
- // query for the proper chunk metrics
- int chunkSize, spaceSize;
- if (FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
- TMT_PROGRESSCHUNKSIZE, &chunkSize)) ||
- FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
- TMT_PROGRESSSPACESIZE, &spaceSize))) {
- DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
- return;
- }
-
- // render chunks
- if (!aIsRtl || aIsIndeterm) {
- for (int chunk = aWidgetRect->left; chunk <= aWidgetRect->right;
- chunk += (chunkSize+spaceSize)) {
- if (!aIsIndeterm && ((chunk + chunkSize) > aWidgetRect->right)) {
- // aWidgetRect->right represents the end of the meter. Partial blocks
- // don't get rendered with one exception, so exit here if we don't have
- // a full chunk to draw.
- // The above is true *except* when the meter is at 100% fill, in which
- // case Windows renders any remaining partial block. Query the parent
- // frame to find out if we're at 100%.
- if (!IsProgressMeterFilled(aFrame)) {
- break;
- }
- }
- RECT bounds =
- { chunk, aWidgetRect->top, chunk + chunkSize, aWidgetRect->bottom };
- DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
- }
- } else {
- // rtl needs to grow in the opposite direction to look right.
- for (int chunk = aWidgetRect->right; chunk >= aWidgetRect->left;
- chunk -= (chunkSize+spaceSize)) {
- if ((chunk - chunkSize) < aWidgetRect->left) {
- if (!IsProgressMeterFilled(aFrame)) {
- break;
- }
- }
- RECT bounds =
- { chunk - chunkSize, aWidgetRect->top, chunk, aWidgetRect->bottom };
- DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
- }
- }
-}
-
-/*
* DrawProgressMeter - render an appropriate progress meter based on progress
* meter style, orientation, and os. Note, this does not render the underlying
* progress track.
@@ -653,8 +543,7 @@ void
nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
HANDLE aTheme, HDC aHdc,
int aPart, int aState,
- RECT* aWidgetRect, RECT* aClipRect,
- gfxFloat aAppUnits)
+ RECT* aWidgetRect, RECT* aClipRect)
{
if (!aFrame || !aTheme || !aHdc)
return;
@@ -665,12 +554,6 @@ nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
RECT adjWidgetRect, adjClipRect;
adjWidgetRect = *aWidgetRect;
adjClipRect = *aClipRect;
- if (!IsVistaOrLater()) {
- // Adjust clipping out by one pixel. XP progress meters are inset,
- // Vista+ are not.
- InflateRect(&adjWidgetRect, 1, 1);
- InflateRect(&adjClipRect, 1, 1);
- }
nsIFrame* parentFrame = aFrame->GetParent();
if (!parentFrame) {
@@ -685,20 +568,13 @@ nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates);
bool animate = indeterminate;
- if (IsVistaOrLater()) {
- // Vista and up progress meter is fill style, rendered here. We render
- // the pulse overlay in the follow up section below.
- DrawThemeBackground(aTheme, aHdc, aPart, aState,
- &adjWidgetRect, &adjClipRect);
- if (!IsProgressMeterFilled(aFrame)) {
- animate = true;
- }
- } else if (!indeterminate) {
- // XP progress meters are 'chunk' style.
- DrawChunkProgressMeter(aTheme, aHdc, aPart, aState, aFrame,
- &adjWidgetRect, &adjClipRect, aAppUnits,
- indeterminate, vertical, IsFrameRTL(aFrame));
- }
+ // Vista and up progress meter is fill style, rendered here. We render
+ // the pulse overlay in the follow up section below.
+ DrawThemeBackground(aTheme, aHdc, aPart, aState,
+ &adjWidgetRect, &adjClipRect);
+ if (!IsProgressMeterFilled(aFrame)) {
+ animate = true;
+ }
if (animate) {
// Indeterminate rendering
@@ -706,14 +582,8 @@ nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
RECT overlayRect =
CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical,
indeterminate, false);
- if (IsVistaOrLater()) {
- DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
- &adjClipRect);
- } else {
- DrawChunkProgressMeter(aTheme, aHdc, overlayPart, aState, aFrame,
- &overlayRect, &adjClipRect, aAppUnits,
- indeterminate, vertical, IsFrameRTL(aFrame));
- }
+ DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
+ &adjClipRect);
if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
NS_WARNING("unable to animate progress widget!");
@@ -724,15 +594,6 @@ nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
HANDLE
nsNativeThemeWin::GetTheme(uint8_t aWidgetType)
{
- if (!IsVistaOrLater()) {
- // On XP or earlier, render dropdowns as textfields;
- // doing it the right way works fine with the MS themes,
- // but breaks on a lot of custom themes (presumably because MS
- // apps do the textfield border business as well).
- if (aWidgetType == NS_THEME_MENULIST)
- aWidgetType = NS_THEME_TEXTFIELD;
- }
-
switch (aWidgetType) {
case NS_THEME_BUTTON:
case NS_THEME_RADIO:
@@ -745,9 +606,7 @@ nsNativeThemeWin::GetTheme(uint8_t aWidgetType)
case NS_THEME_FOCUS_OUTLINE:
return nsUXThemeData::GetTheme(eUXEdit);
case NS_THEME_TOOLTIP:
- // XP/2K3 should force a classic treatment of tooltips
- return !IsVistaOrLater() ?
- nullptr : nsUXThemeData::GetTheme(eUXTooltip);
+ return nsUXThemeData::GetTheme(eUXTooltip);
case NS_THEME_TOOLBOX:
return nsUXThemeData::GetTheme(eUXRebar);
case NS_THEME_WIN_MEDIA_TOOLBOX:
@@ -874,12 +733,6 @@ nsresult
nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
int32_t& aPart, int32_t& aState)
{
- if (!IsVistaOrLater()) {
- // See GetTheme
- if (aWidgetType == NS_THEME_MENULIST)
- aWidgetType = NS_THEME_TEXTFIELD;
- }
-
switch (aWidgetType) {
case NS_THEME_BUTTON: {
aPart = BP_BUTTON;
@@ -952,63 +805,43 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
case NS_THEME_TEXTFIELD_MULTILINE: {
EventStates eventState = GetContentState(aFrame, aWidgetType);
- if (IsVistaOrLater()) {
- /* Note: the NOSCROLL type has a rounded corner in each
- * corner. The more specific HSCROLL, VSCROLL, HVSCROLL types
- * have side and/or top/bottom edges rendered as straight
- * horizontal lines with sharp corners to accommodate a
- * scrollbar. However, the scrollbar gets rendered on top of
- * this for us, so we don't care, and can just use NOSCROLL
- * here.
- */
- aPart = TFP_EDITBORDER_NOSCROLL;
-
- if (!aFrame) {
- aState = TFS_EDITBORDER_NORMAL;
- } else if (IsDisabled(aFrame, eventState)) {
- aState = TFS_EDITBORDER_DISABLED;
- } else if (IsReadOnly(aFrame)) {
- /* no special read-only state */
- aState = TFS_EDITBORDER_NORMAL;
- } else {
- nsIContent* content = aFrame->GetContent();
+ /* Note: the NOSCROLL type has a rounded corner in each corner. The more
+ * specific HSCROLL, VSCROLL, HVSCROLL types have side and/or top/bottom
+ * edges rendered as straight horizontal lines with sharp corners to
+ * accommodate a scrollbar. However, the scrollbar gets rendered on top
+ * of this for us, so we don't care, and can just use NOSCROLL here.
+ */
+ aPart = TFP_EDITBORDER_NOSCROLL;
- /* XUL textboxes don't get focused themselves, because they have child
- * html:input.. but we can check the XUL focused attributes on them
- */
- if (content && content->IsXULElement() && IsFocused(aFrame))
- aState = TFS_EDITBORDER_FOCUSED;
- else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS))
- aState = TFS_EDITBORDER_FOCUSED;
- else if (eventState.HasState(NS_EVENT_STATE_HOVER))
- aState = TFS_EDITBORDER_HOVER;
- else
- aState = TFS_EDITBORDER_NORMAL;
- }
+ if (!aFrame) {
+ aState = TFS_EDITBORDER_NORMAL;
+ } else if (IsDisabled(aFrame, eventState)) {
+ aState = TFS_EDITBORDER_DISABLED;
+ } else if (IsReadOnly(aFrame)) {
+ /* no special read-only state */
+ aState = TFS_EDITBORDER_NORMAL;
} else {
- aPart = TFP_TEXTFIELD;
-
- if (!aFrame)
- aState = TS_NORMAL;
- else if (IsDisabled(aFrame, eventState))
- aState = TS_DISABLED;
- else if (IsReadOnly(aFrame))
- aState = TFS_READONLY;
+ nsIContent* content = aFrame->GetContent();
+
+ /* XUL textboxes don't get focused themselves, because they have child
+ * html:input.. but we can check the XUL focused attributes on them
+ */
+ if (content && content->IsXULElement() && IsFocused(aFrame))
+ aState = TFS_EDITBORDER_FOCUSED;
+ else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS))
+ aState = TFS_EDITBORDER_FOCUSED;
+ else if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ aState = TFS_EDITBORDER_HOVER;
else
- aState = StandardGetState(aFrame, aWidgetType, true);
+ aState = TFS_EDITBORDER_NORMAL;
}
return NS_OK;
}
case NS_THEME_FOCUS_OUTLINE: {
- if (IsVistaOrLater()) {
- // XXX the EDITBORDER values don't respect DTBG_OMITCONTENT
- aPart = TFP_TEXTFIELD; //TFP_EDITBORDER_NOSCROLL;
- aState = TS_FOCUSED; //TFS_EDITBORDER_FOCUSED;
- } else {
- aPart = TFP_TEXTFIELD;
- aState = TS_FOCUSED;
- }
+ // XXX the EDITBORDER values don't respect DTBG_OMITCONTENT
+ aPart = TFP_TEXTFIELD; //TFP_EDITBORDER_NOSCROLL;
+ aState = TS_FOCUSED; //TFS_EDITBORDER_FOCUSED;
return NS_OK;
}
case NS_THEME_TOOLTIP: {
@@ -1032,11 +865,9 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
nsIFrame* parentFrame = aFrame->GetParent();
if (aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL ||
IsVerticalProgress(parentFrame)) {
- aPart = IsVistaOrLater() ?
- PP_FILLVERT : PP_CHUNKVERT;
+ aPart = PP_FILLVERT;
} else {
- aPart = IsVistaOrLater() ?
- PP_FILL : PP_CHUNK;
+ aPart = PP_FILL;
}
aState = PBBVS_NORMAL;
@@ -1100,8 +931,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
aState += TS_ACTIVE;
else if (eventState.HasState(NS_EVENT_STATE_HOVER))
aState += TS_HOVER;
- else if (IsVistaOrLater() &&
- parentState.HasState(NS_EVENT_STATE_HOVER))
+ else if (parentState.HasState(NS_EVENT_STATE_HOVER))
aState = (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE;
else
aState += TS_NORMAL;
@@ -1204,14 +1034,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
case NS_THEME_SCROLLBAR:
case NS_THEME_SCROLLBAR_SMALL: {
aState = 0;
- if (IsVistaOrLater()) {
- // On vista, they have a part
- aPart = RP_BACKGROUND;
- } else {
- // Otherwise, they don't. (But I bet
- // RP_BACKGROUND would work here, too);
- aPart = 0;
- }
+ aPart = RP_BACKGROUND;
return NS_OK;
}
case NS_THEME_TOOLBAR: {
@@ -1336,8 +1159,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
aFrame = parentFrame;
EventStates eventState = GetContentState(aFrame, aWidgetType);
- aPart = IsVistaOrLater() ?
- CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
+ aPart = CBP_DROPMARKER_VISTA;
// For HTML controls with author styling, we should fall
// back to the old dropmarker style to avoid clashes with
@@ -1357,28 +1179,25 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
else
isOpen = IsOpenButton(aFrame);
- if (IsVistaOrLater()) {
- if (isHTML || IsMenuListEditable(aFrame)) {
- if (isOpen) {
- /* Hover is propagated, but we need to know whether we're
- * hovering just the combobox frame, not the dropdown frame.
- * But, we can't get that information, since hover is on the
- * content node, and they share the same content node. So,
- * instead, we cheat -- if the dropdown is open, we always
- * show the hover state. This looks fine in practice.
- */
- aState = TS_HOVER;
- return NS_OK;
- }
- } else {
- /* On Vista, the dropdown indicator on a menulist button in
- * chrome is not given a hover effect. When the frame isn't
- * isn't HTML content, we cheat and force the dropdown state
- * to be normal. (Bug 430434)
+ if (isHTML || IsMenuListEditable(aFrame)) {
+ if (isOpen) {
+ /* Hover is propagated, but we need to know whether we're hovering
+ * just the combobox frame, not the dropdown frame. But, we can't get
+ * that information, since hover is on the content node, and they
+ * share the same content node. So, instead, we cheat -- if the
+ * dropdown is open, we always show the hover state. This looks fine
+ * in practice.
*/
- aState = TS_NORMAL;
+ aState = TS_HOVER;
return NS_OK;
}
+ } else {
+ /* The dropdown indicator on a menulist button in chrome is not given a
+ * hover effect. When the frame isn't isn't HTML content, we cheat and
+ * force the dropdown state to be normal. (Bug 430434)
+ */
+ aState = TS_NORMAL;
+ return NS_OK;
}
aState = TS_NORMAL;
@@ -1879,7 +1698,7 @@ RENDER_AGAIN:
else if (aWidgetType == NS_THEME_PROGRESSCHUNK ||
aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL) {
DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state,
- &widgetRect, &clipRect, p2a);
+ &widgetRect, &clipRect);
}
else if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
// Inflate 'widgetRect' with the focus outline size.
@@ -2145,41 +1964,39 @@ nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
return ok;
}
- if (IsVistaOrLater()) {
- if (aWidgetType == NS_THEME_NUMBER_INPUT ||
- aWidgetType == NS_THEME_TEXTFIELD ||
- aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
- aWidgetType == NS_THEME_MENULIST)
- {
- /* If we have author-specified padding for these elements, don't do the fixups below */
- if (aFrame->PresContext()->HasAuthorSpecifiedRules(aFrame, NS_AUTHOR_SPECIFIED_PADDING))
- return false;
- }
+ if (aWidgetType == NS_THEME_NUMBER_INPUT ||
+ aWidgetType == NS_THEME_TEXTFIELD ||
+ aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
+ aWidgetType == NS_THEME_MENULIST)
+ {
+ // If we have author-specified padding for these elements, don't do the
+ // fixups below.
+ if (aFrame->PresContext()->HasAuthorSpecifiedRules(aFrame, NS_AUTHOR_SPECIFIED_PADDING))
+ return false;
+ }
- /* textfields need extra pixels on all sides, otherwise they
- * wrap their content too tightly. The actual border is drawn 1px
- * inside the specified rectangle, so Gecko will end up making the
- * contents look too small. Instead, we add 2px padding for the
- * contents and fix this. (Used to be 1px added, see bug 430212)
+ /* textfields need extra pixels on all sides, otherwise they wrap their
+ * content too tightly. The actual border is drawn 1px inside the specified
+ * rectangle, so Gecko will end up making the contents look too small.
+ * Instead, we add 2px padding for the contents and fix this. (Used to be 1px
+ * added, see bug 430212)
+ */
+ if (aWidgetType == NS_THEME_NUMBER_INPUT ||
+ aWidgetType == NS_THEME_TEXTFIELD ||
+ aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
+ aResult->top = aResult->bottom = 2;
+ aResult->left = aResult->right = 2;
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
+ } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_MENULIST) {
+ /* For content menulist controls, we need an extra pixel so that we have
+ * room to draw our focus rectangle stuff. Otherwise, the focus rect might
+ * overlap the control's border.
*/
- if (aWidgetType == NS_THEME_NUMBER_INPUT ||
- aWidgetType == NS_THEME_TEXTFIELD ||
- aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
- aResult->top = aResult->bottom = 2;
- aResult->left = aResult->right = 2;
- ScaleForFrameDPI(aResult, aFrame);
- return ok;
- } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_MENULIST) {
- /* For content menulist controls, we need an extra pixel so
- * that we have room to draw our focus rectangle stuff.
- * Otherwise, the focus rect might overlap the control's
- * border.
- */
- aResult->top = aResult->bottom = 1;
- aResult->left = aResult->right = 1;
- ScaleForFrameDPI(aResult, aFrame);
- return ok;
- }
+ aResult->top = aResult->bottom = 1;
+ aResult->left = aResult->right = 1;
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
}
int32_t right, left, top, bottom;
@@ -2244,23 +2061,21 @@ nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
* a border only shows up if the widget is being hovered.
*/
#if 0
- if (IsVistaOrLater()) {
- /* We explicitly draw dropdown buttons in HTML content 1px bigger
- * up, right, and bottom so that they overlap the dropdown's border
- * like they're supposed to.
- */
- if (aWidgetType == NS_THEME_MENULIST_BUTTON &&
- IsHTMLContent(aFrame) &&
- !IsWidgetStyled(aFrame->GetParent()->PresContext(),
- aFrame->GetParent(),
- NS_THEME_MENULIST))
- {
- int32_t p2a = aContext->AppUnitsPerDevPixel();
- /* Note: no overflow on the left */
- nsMargin m(p2a, p2a, p2a, 0);
- aOverflowRect->Inflate (m);
- return true;
- }
+ /* We explicitly draw dropdown buttons in HTML content 1px bigger up, right,
+ * and bottom so that they overlap the dropdown's border like they're
+ * supposed to.
+ */
+ if (aWidgetType == NS_THEME_MENULIST_BUTTON &&
+ IsHTMLContent(aFrame) &&
+ !IsWidgetStyled(aFrame->GetParent()->PresContext(),
+ aFrame->GetParent(),
+ NS_THEME_MENULIST))
+ {
+ int32_t p2a = aContext->AppUnitsPerDevPixel();
+ /* Note: no overflow on the left */
+ nsMargin m(p2a, p2a, p2a, 0);
+ aOverflowRect->Inflate (m);
+ return true;
}
#endif
@@ -2384,22 +2199,18 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsPresContext* aPresContext, nsIFrame* aF
case NS_THEME_SCALETHUMB_VERTICAL:
{
*aIsOverridable = false;
- // on Vista, GetThemePartAndState returns odd values for
+ // On Vista, GetThemePartAndState returns odd values for
// scale thumbs, so use a hardcoded size instead.
- if (IsVistaOrLater()) {
- if (aWidgetType == NS_THEME_SCALETHUMB_HORIZONTAL ||
- (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
- aResult->width = 12;
- aResult->height = 20;
- }
- else {
- aResult->width = 20;
- aResult->height = 12;
- }
- ScaleForFrameDPI(aResult, aFrame);
- return rv;
+ if (aWidgetType == NS_THEME_SCALETHUMB_HORIZONTAL ||
+ (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
+ aResult->width = 12;
+ aResult->height = 20;
+ } else {
+ aResult->width = 20;
+ aResult->height = 12;
}
- break;
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
}
case NS_THEME_SCROLLBAR:
@@ -2438,11 +2249,6 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsPresContext* aPresContext, nsIFrame* aF
// stores that info in nsUXThemeData.
aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cx;
aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cy;
- // For XP, subtract 4 from system metrics dimensions.
- if (!IsVistaOrLater()) {
- aResult->width -= 4;
- aResult->height -= 4;
- }
AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
*aIsOverridable = false;
return rv;
@@ -2450,10 +2256,6 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsPresContext* aPresContext, nsIFrame* aF
case NS_THEME_WINDOW_BUTTON_MINIMIZE:
aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cx;
aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cy;
- if (!IsVistaOrLater()) {
- aResult->width -= 4;
- aResult->height -= 4;
- }
AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
*aIsOverridable = false;
return rv;
@@ -2461,10 +2263,6 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsPresContext* aPresContext, nsIFrame* aF
case NS_THEME_WINDOW_BUTTON_CLOSE:
aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cx;
aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cy;
- if (!IsVistaOrLater()) {
- aResult->width -= 4;
- aResult->height -= 4;
- }
AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
*aIsOverridable = false;
return rv;
@@ -2594,18 +2392,9 @@ nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
return NS_OK;
}
- // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover
- if (!IsVistaOrLater() &&
- (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ||
- aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL)) {
- *aShouldRepaint = false;
- return NS_OK;
- }
-
// We need to repaint the dropdown arrow in vista HTML combobox controls when
// the control is closed to get rid of the hover effect.
- if (IsVistaOrLater() &&
- (aWidgetType == NS_THEME_MENULIST || aWidgetType == NS_THEME_MENULIST_BUTTON) &&
+ if ((aWidgetType == NS_THEME_MENULIST || aWidgetType == NS_THEME_MENULIST_BUTTON) &&
IsHTMLContent(aFrame))
{
*aShouldRepaint = true;
diff --git a/widget/windows/nsNativeThemeWin.h b/widget/windows/nsNativeThemeWin.h
index f20649444..32b82b1e1 100644
--- a/widget/windows/nsNativeThemeWin.h
+++ b/widget/windows/nsNativeThemeWin.h
@@ -117,8 +117,7 @@ protected:
void DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
HANDLE aTheme, HDC aHdc,
int aPart, int aState,
- RECT* aWidgetRect, RECT* aClipRect,
- gfxFloat aAppUnits);
+ RECT* aWidgetRect, RECT* aClipRect);
private:
TimeStamp mProgressDeterminateTimeStamp;
diff --git a/widget/windows/nsUXThemeConstants.h b/widget/windows/nsUXThemeConstants.h
index 731dcedf2..ba7afde82 100644
--- a/widget/windows/nsUXThemeConstants.h
+++ b/widget/windows/nsUXThemeConstants.h
@@ -67,7 +67,7 @@
#define SP_GRIPPERHOR 8
#define SP_GRIPPERVERT 9
-// Vista only; implict hover state.
+// Implicit hover state.
// BASE + 0 = UP, + 1 = DOWN, etc.
#define SP_BUTTON_IMPLICIT_HOVER_BASE 17
diff --git a/widget/windows/nsUXThemeData.cpp b/widget/windows/nsUXThemeData.cpp
index bcbd32484..4d9ac9570 100644
--- a/widget/windows/nsUXThemeData.cpp
+++ b/widget/windows/nsUXThemeData.cpp
@@ -143,10 +143,8 @@ nsUXThemeData::InitTitlebarInfo()
sCommandButtons[3].cx = sCommandButtons[0].cx * 3;
sCommandButtons[3].cy = sCommandButtons[0].cy;
- // Use system metrics for pre-vista, otherwise trigger a
- // refresh on the next layout.
- sTitlebarInfoPopulatedAero = sTitlebarInfoPopulatedThemed =
- !IsVistaOrLater();
+ // Trigger a refresh on the next layout.
+ sTitlebarInfoPopulatedAero = sTitlebarInfoPopulatedThemed = false;
}
// static
@@ -306,7 +304,7 @@ void
nsUXThemeData::UpdateNativeThemeInfo()
{
// Trigger a refresh of themed button metrics if needed
- sTitlebarInfoPopulatedThemed = !IsVistaOrLater();
+ sTitlebarInfoPopulatedThemed = false;
sIsDefaultWindowsTheme = false;
sThemeId = LookAndFeel::eWindowsTheme_Generic;
diff --git a/widget/windows/nsWinGesture.h b/widget/windows/nsWinGesture.h
index 24c1f6b2d..fdd1588b6 100644
--- a/widget/windows/nsWinGesture.h
+++ b/widget/windows/nsWinGesture.h
@@ -17,155 +17,6 @@
#include "mozilla/EventForwards.h"
#include "mozilla/TouchEvents.h"
-// Desktop builds target apis for 502. Win8 Metro builds target 602.
-#if WINVER < 0x0602
-
-DECLARE_HANDLE(HGESTUREINFO);
-
-/*
- * Gesture flags - GESTUREINFO.dwFlags
- */
-#define GF_BEGIN 0x00000001
-#define GF_INERTIA 0x00000002
-#define GF_END 0x00000004
-
-/*
- * Gesture configuration structure
- * - Used in SetGestureConfig and GetGestureConfig
- * - Note that any setting not included in either GESTURECONFIG.dwWant or
- * GESTURECONFIG.dwBlock will use the parent window's preferences or
- * system defaults.
- */
-typedef struct tagGESTURECONFIG {
- DWORD dwID; // gesture ID
- DWORD dwWant; // settings related to gesture ID that are to be turned on
- DWORD dwBlock; // settings related to gesture ID that are to be turned off
-} GESTURECONFIG, *PGESTURECONFIG;
-
-/*
- * Gesture information structure
- * - Pass the HGESTUREINFO received in the WM_GESTURE message lParam into the
- * GetGestureInfo function to retrieve this information.
- * - If cbExtraArgs is non-zero, pass the HGESTUREINFO received in the WM_GESTURE
- * message lParam into the GetGestureExtraArgs function to retrieve extended
- * argument information.
- */
-typedef struct tagGESTUREINFO {
- UINT cbSize; // size, in bytes, of this structure (including variable length Args field)
- DWORD dwFlags; // see GF_* flags
- DWORD dwID; // gesture ID, see GID_* defines
- HWND hwndTarget; // handle to window targeted by this gesture
- POINTS ptsLocation; // current location of this gesture
- DWORD dwInstanceID; // internally used
- DWORD dwSequenceID; // internally used
- ULONGLONG ullArguments; // arguments for gestures whose arguments fit in 8 BYTES
- UINT cbExtraArgs; // size, in bytes, of extra arguments, if any, that accompany this gesture
-} GESTUREINFO, *PGESTUREINFO;
-typedef GESTUREINFO const * PCGESTUREINFO;
-
-/*
- * Gesture notification structure
- * - The WM_GESTURENOTIFY message lParam contains a pointer to this structure.
- * - The WM_GESTURENOTIFY message notifies a window that gesture recognition is
- * in progress and a gesture will be generated if one is recognized under the
- * current gesture settings.
- */
-typedef struct tagGESTURENOTIFYSTRUCT {
- UINT cbSize; // size, in bytes, of this structure
- DWORD dwFlags; // unused
- HWND hwndTarget; // handle to window targeted by the gesture
- POINTS ptsLocation; // starting location
- DWORD dwInstanceID; // internally used
-} GESTURENOTIFYSTRUCT, *PGESTURENOTIFYSTRUCT;
-
-
-/*
- * Gesture argument helpers
- * - Angle should be a double in the range of -2pi to +2pi
- * - Argument should be an unsigned 16-bit value
- */
-#define GID_ROTATE_ANGLE_TO_ARGUMENT(_arg_) ((USHORT)((((_arg_) + 2.0 * 3.14159265) / (4.0 * 3.14159265)) * 65535.0))
-#define GID_ROTATE_ANGLE_FROM_ARGUMENT(_arg_) ((((double)(_arg_) / 65535.0) * 4.0 * 3.14159265) - 2.0 * 3.14159265)
-
-/*
- * Gesture configuration flags
- */
-#define GC_ALLGESTURES 0x00000001
-
-#define GC_ZOOM 0x00000001
-
-#define GC_PAN 0x00000001
-#define GC_PAN_WITH_SINGLE_FINGER_VERTICALLY 0x00000002
-#define GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY 0x00000004
-#define GC_PAN_WITH_GUTTER 0x00000008
-#define GC_PAN_WITH_INERTIA 0x00000010
-
-#define GC_ROTATE 0x00000001
-
-#define GC_TWOFINGERTAP 0x00000001
-
-#define GC_PRESSANDTAP 0x00000001
-
-/*
- * Gesture IDs
- */
-#define GID_BEGIN 1
-#define GID_END 2
-#define GID_ZOOM 3
-#define GID_PAN 4
-#define GID_ROTATE 5
-#define GID_TWOFINGERTAP 6
-#define GID_PRESSANDTAP 7
-
-// Maximum number of gestures that can be included
-// in a single call to SetGestureConfig / GetGestureConfig
-#define GESTURECONFIGMAXCOUNT 256
-
-// If specified, GetGestureConfig returns consolidated configuration
-// for the specified window and it's parent window chain
-#define GCF_INCLUDE_ANCESTORS 0x00000001
-
-// Window events we need to respond to or receive
-#define WM_GESTURE 0x0119
-#define WM_GESTURENOTIFY 0x011A
-
-typedef struct _TOUCHINPUT {
- LONG x;
- LONG y;
- HANDLE hSource;
- DWORD dwID;
- DWORD dwFlags;
- DWORD dwMask;
- DWORD dwTime;
- ULONG_PTR dwExtraInfo;
- DWORD cxContact;
- DWORD cyContact;
-} TOUCHINPUT, *PTOUCHINPUT;
-
-typedef HANDLE HTOUCHINPUT;
-
-#define WM_TOUCH 0x0240
-
-#define TOUCHEVENTF_MOVE 0x0001
-#define TOUCHEVENTF_DOWN 0x0002
-#define TOUCHEVENTF_UP 0x0004
-#define TOUCHEVENTF_INRANGE 0x0008
-#define TOUCHEVENTF_PRIMARY 0x0010
-#define TOUCHEVENTF_NOCOALESCE 0x0020
-#define TOUCHEVENTF_PEN 0x0040
-#define TOUCHEVENTF_PALM 0x0080
-
-#define TOUCHINPUTMASKF_TIMEFROMSYSTEM 0x0001
-#define TOUCHINPUTMASKF_EXTRAINFO 0x0002
-#define TOUCHINPUTMASKF_CONTACTAREA 0x0004
-
-#define TOUCH_COORD_TO_PIXEL(C) (C/100)
-
-#define TWF_FINETOUCH 0x0001
-#define TWF_WANTPALM 0x0002
-
-#endif // WINVER < 0x0602
-
// WM_TABLET_QUERYSYSTEMGESTURESTATUS return values
#define TABLET_ROTATE_GESTURE_ENABLE 0x02000000
@@ -205,7 +56,7 @@ public:
bool GetTouchInputInfo(HTOUCHINPUT hTouchInput, uint32_t cInputs, PTOUCHINPUT pInputs);
bool CloseTouchInputHandle(HTOUCHINPUT hTouchInput);
bool IsAvailable();
-
+
// Simple gesture process
bool ProcessGestureMessage(HWND hWnd, WPARAM wParam, LPARAM lParam, mozilla::WidgetSimpleGestureEvent& evt);
@@ -216,7 +67,7 @@ public:
void UpdatePanFeedbackX(HWND hWnd, int32_t scrollOverflow, bool& endFeedback);
void UpdatePanFeedbackY(HWND hWnd, int32_t scrollOverflow, bool& endFeedback);
void PanFeedbackFinalize(HWND hWnd, bool endFeedback);
-
+
public:
// Helpers
bool GetGestureInfo(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo);
@@ -284,5 +135,3 @@ private:
};
#endif /* WinGesture_h__ */
-
-
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp
index 85321a189..b2bb59bd3 100644
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -266,8 +266,7 @@ LONG nsWindow::sLastMouseDownTime = 0L;
LONG nsWindow::sLastClickCount = 0L;
BYTE nsWindow::sLastMouseButton = 0;
-// Trim heap on minimize. (initialized, but still true.)
-int nsWindow::sTrimOnMinimize = 2;
+bool nsWindow::sHaveInitializedPrefs = false;
TriStateBool nsWindow::sHasBogusPopupsDropShadowOnMultiMonitor = TRI_UNKNOWN;
@@ -783,7 +782,7 @@ nsWindow::Create(nsIWidget* aParent,
parent = nullptr;
}
- if (IsVistaOrLater() && !IsWin8OrLater() &&
+ if (!IsWin8OrLater() &&
HasBogusPopupsDropShadowOnMultiMonitor()) {
extendedStyle |= WS_EX_COMPOSITED;
}
@@ -908,20 +907,13 @@ nsWindow::Create(nsIWidget* aParent,
mDefaultIMC.Init(this);
IMEHandler::InitInputContext(this, mInputContext);
- // If the internal variable set by the config.trim_on_minimize pref has not
- // been initialized, and if this is the hidden window (conveniently created
- // before any visible windows, and after the profile has been initialized),
- // do some initialization work.
- if (sTrimOnMinimize == 2 && mWindowType == eWindowType_invisible) {
- // Our internal trim prevention logic is effective on 2K/XP at maintaining
- // the working set when windows are minimized, but on Vista and up it has
- // little to no effect. Since this feature has been the source of numerous
- // bugs over the years, disable it (sTrimOnMinimize=1) on Vista and up.
- sTrimOnMinimize =
- Preferences::GetBool("config.trim_on_minimize",
- IsVistaOrLater() ? 1 : 0);
+ // Do some initialization work, but only if (a) it hasn't already been done,
+ // and (b) this is the hidden window (which is conveniently created before
+ // any visible windows but after the profile has been initialized).
+ if (!sHaveInitializedPrefs && mWindowType == eWindowType_invisible) {
sSwitchKeyboardLayout =
Preferences::GetBool("intl.keyboard.per_window_layout", false);
+ sHaveInitializedPrefs = true;
}
// Query for command button metric data for rendering the titlebar. We
@@ -1647,9 +1639,10 @@ bool nsWindow::IsVisible() const
// XP and Vista visual styles sometimes require window clipping regions to be applied for proper
// transparency. These routines are called on size and move operations.
+// XXX this is apparently still needed in Windows 7 and later
void nsWindow::ClearThemeRegion()
{
- if (IsVistaOrLater() && !HasGlass() &&
+ if (!HasGlass() &&
(mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
(mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
SetWindowRgn(mWnd, nullptr, false);
@@ -1663,7 +1656,7 @@ void nsWindow::SetThemeRegion()
// so default constants are used for part and state. At some point we might need part and
// state values from nsNativeThemeWin's GetThemePartAndState, but currently windows that
// change shape based on state haven't come up.
- if (IsVistaOrLater() && !HasGlass() &&
+ if (!HasGlass() &&
(mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
(mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
HRGN hRgn = nullptr;
@@ -2080,13 +2073,7 @@ nsWindow::SetSizeMode(nsSizeMode aMode)
break;
case nsSizeMode_Minimized :
- // Using SW_SHOWMINIMIZED prevents the working set from being trimmed but
- // keeps the window active in the tray. So after the window is minimized,
- // windows will fire WM_WINDOWPOSCHANGED (OnWindowPosChanged) at which point
- // we will do some additional processing to get the active window set right.
- // If sTrimOnMinimize is set, we let windows handle minimization normally
- // using SW_MINIMIZE.
- mode = sTrimOnMinimize ? SW_MINIMIZE : SW_SHOWMINIMIZED;
+ mode = SW_MINIMIZE;
break;
default :
@@ -5589,12 +5576,13 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
DispatchPendingEvents();
break;
- // Windows doesn't provide to customize the behavior of 4th nor 5th button
- // of mouse. If 5-button mouse works with standard mouse deriver of
- // Windows, users cannot disable 4th button (browser back) nor 5th button
- // (browser forward). We should allow to do it with our prefs since we can
- // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
- // messages are not sent to DefWindowProc.
+ // Windows doesn't provide a way to customize the behavior of 4th or 5th
+ // button of a mouse. If a 5-button mouse works with the standard mouse
+ // driver of Windows, users cannot disable the 4th button (browser back)
+ // nor the 5th button (browser forward).
+ // We can disable it here with our prefs since we can prevent Windows
+ // from generating WM_APPCOMMAND messages if WM_XBUTTONUP messages are
+ // not sent to DefWindowProc.
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
case WM_NCXBUTTONDOWN:
@@ -5602,10 +5590,10 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
*aRetValue = TRUE;
switch (GET_XBUTTON_WPARAM(wParam)) {
case XBUTTON1:
- result = !Preferences::GetBool("mousebutton.4th.enabled", true);
+ result = !Preferences::GetBool("mouse.button4.enabled", true);
break;
case XBUTTON2:
- result = !Preferences::GetBool("mousebutton.5th.enabled", true);
+ result = !Preferences::GetBool("mouse.button5.enabled", true);
break;
default:
break;
@@ -5827,12 +5815,6 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
case WM_SYSCOMMAND:
{
WPARAM filteredWParam = (wParam &0xFFF0);
- // prevent Windows from trimming the working set. bug 76831
- if (!sTrimOnMinimize && filteredWParam == SC_MINIMIZE) {
- ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
- result = true;
- }
-
if (mSizeMode == nsSizeMode_Fullscreen &&
filteredWParam == SC_RESTORE &&
GetCurrentShowCmd(mWnd) != SW_SHOWMINIMIZED) {
@@ -6450,14 +6432,6 @@ void nsWindow::OnWindowPosChanged(WINDOWPOS* wp)
else
mSizeMode = nsSizeMode_Normal;
- // If !sTrimOnMinimize, we minimize windows using SW_SHOWMINIMIZED (See
- // SetSizeMode for internal calls, and WM_SYSCOMMAND for external). This
- // prevents the working set from being trimmed but keeps the window active.
- // After the window is minimized, we need to do some touch up work on the
- // active window. (bugs 76831 & 499816)
- if (!sTrimOnMinimize && nsSizeMode_Minimized == mSizeMode)
- ActivateOtherWindowHelper(mWnd);
-
#ifdef WINSTATE_DEBUG_OUTPUT
switch (mSizeMode) {
case nsSizeMode_Normal:
@@ -6578,31 +6552,6 @@ void nsWindow::OnWindowPosChanged(WINDOWPOS* wp)
}
}
-// static
-void nsWindow::ActivateOtherWindowHelper(HWND aWnd)
-{
- // Find the next window that is enabled, visible, and not minimized.
- HWND hwndBelow = ::GetNextWindow(aWnd, GW_HWNDNEXT);
- while (hwndBelow && (!::IsWindowEnabled(hwndBelow) || !::IsWindowVisible(hwndBelow) ||
- ::IsIconic(hwndBelow))) {
- hwndBelow = ::GetNextWindow(hwndBelow, GW_HWNDNEXT);
- }
-
- // Push ourselves to the bottom of the stack, then activate the
- // next window.
- ::SetWindowPos(aWnd, HWND_BOTTOM, 0, 0, 0, 0,
- SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
- if (hwndBelow)
- ::SetForegroundWindow(hwndBelow);
-
- // Play the minimize sound while we're here, since that is also
- // forgotten when we use SW_SHOWMINIMIZED.
- nsCOMPtr<nsISound> sound(do_CreateInstance("@mozilla.org/sound;1"));
- if (sound) {
- sound->PlaySystemSound(NS_LITERAL_STRING("Minimize"));
- }
-}
-
void nsWindow::OnWindowPosChanging(LPWINDOWPOS& info)
{
// Update non-client margins if the frame size is changing, and let the
@@ -7140,8 +7089,10 @@ nsWindow::OnSysColorChanged()
NotifySysColorChanged();
// On Windows 10 only, we trigger a theme change to pick up changed media
// queries that are needed for accent color changes.
+ // We also set a temp pref to notify the FE that the colors have changed.
if (IsWin10OrLater()) {
NotifyThemeChanged();
+ Preferences::SetBool("ui.colorChanged", true);
}
}
}
diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h
index 248978bd7..199500e9c 100644
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -486,7 +486,6 @@ protected:
bool aIntersectWithExisting) override;
nsIntRegion GetRegionToPaint(bool aForceFullRepaint,
PAINTSTRUCT ps, HDC aDC);
- static void ActivateOtherWindowHelper(HWND aWnd);
void ClearCachedResources();
nsIWidgetListener* GetPaintListener();
@@ -552,7 +551,7 @@ protected:
static bool sJustGotDeactivate;
static bool sJustGotActivate;
static bool sIsInMouseCapture;
- static int sTrimOnMinimize;
+ static bool sHaveInitializedPrefs;
// Always use the helper method to read this property. See bug 603793.
static TriStateBool sHasBogusPopupsDropShadowOnMultiMonitor;
diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp
index c72ea48d7..6ead5cdc7 100644
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -111,7 +111,6 @@ extern nsresult nsStringInputStreamConstructor(nsISupports*, REFNSIID, void**);
#include "SpecialSystemDirectory.h"
#if defined(XP_WIN)
-#include "mozilla/WindowsVersion.h"
#include "nsWindowsRegKey.h"
#endif
@@ -584,8 +583,6 @@ NS_InitXPCOM2(nsIServiceManager** aResult,
NS_StartupLocalFile();
- StartupSpecialSystemDirectory();
-
nsDirectoryService::RealInit();
bool value;
diff --git a/xpcom/glue/nsThreadUtils.cpp b/xpcom/glue/nsThreadUtils.cpp
index 287ada7be..2f2383fd8 100644
--- a/xpcom/glue/nsThreadUtils.cpp
+++ b/xpcom/glue/nsThreadUtils.cpp
@@ -20,15 +20,10 @@
#ifdef XP_WIN
#include <windows.h>
-#include "mozilla/WindowsVersion.h"
-using mozilla::IsVistaOrLater;
#elif defined(XP_MACOSX)
#include <sys/resource.h>
#endif
-#include <pratom.h>
-#include <prthread.h>
-
using namespace mozilla;
#ifndef XPCOM_GLUE_AVOID_NSPR
@@ -443,8 +438,7 @@ nsThreadPoolNaming::SetThreadPoolName(const nsACString& aPoolName,
nsAutoLowPriorityIO::nsAutoLowPriorityIO()
{
#if defined(XP_WIN)
- lowIOPrioritySet = IsVistaOrLater() &&
- SetThreadPriority(GetCurrentThread(),
+ lowIOPrioritySet = SetThreadPriority(GetCurrentThread(),
THREAD_MODE_BACKGROUND_BEGIN);
#elif defined(XP_MACOSX)
oldPriority = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD);
diff --git a/xpcom/io/SpecialSystemDirectory.cpp b/xpcom/io/SpecialSystemDirectory.cpp
index 9ce8eb85b..ab65d38f9 100644
--- a/xpcom/io/SpecialSystemDirectory.cpp
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -12,7 +12,6 @@
#if defined(XP_WIN)
#include <windows.h>
-#include <shlobj.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -20,9 +19,6 @@
#include <shlobj.h>
#include <knownfolders.h>
#include <guiddef.h>
-#include "mozilla/WindowsVersion.h"
-
-using mozilla::IsWin7OrLater;
#elif defined(XP_UNIX)
@@ -51,40 +47,17 @@ using mozilla::IsWin7OrLater;
#endif
#endif
-#ifdef XP_WIN
-typedef HRESULT (WINAPI* nsGetKnownFolderPath)(GUID& rfid,
- DWORD dwFlags,
- HANDLE hToken,
- PWSTR* ppszPath);
-
-static nsGetKnownFolderPath gGetKnownFolderPath = nullptr;
-#endif
-
-void
-StartupSpecialSystemDirectory()
-{
-#if defined (XP_WIN)
- // SHGetKnownFolderPath is only available on Windows Vista
- // so that we need to use GetProcAddress to get the pointer.
- HMODULE hShell32DLLInst = GetModuleHandleW(L"shell32.dll");
- if (hShell32DLLInst) {
- gGetKnownFolderPath = (nsGetKnownFolderPath)
- GetProcAddress(hShell32DLLInst, "SHGetKnownFolderPath");
- }
-#endif
-}
-
#if defined (XP_WIN)
static nsresult
GetKnownFolder(GUID* aGuid, nsIFile** aFile)
{
- if (!aGuid || !gGetKnownFolderPath) {
+ if (!aGuid) {
return NS_ERROR_FAILURE;
}
PWSTR path = nullptr;
- gGetKnownFolderPath(*aGuid, 0, nullptr, &path);
+ SHGetKnownFolderPath(*aGuid, 0, nullptr, &path);
if (!path) {
return NS_ERROR_FAILURE;
@@ -139,19 +112,13 @@ SHLoadLibraryFromKnownFolder(REFKNOWNFOLDERID aFolderId, DWORD aMode,
}
/*
- * Check to see if we're on Win7 and up, and if so, returns the default
- * save-to location for the Windows Library passed in through aFolderId.
- * Otherwise falls back on pre-win7 GetWindowsFolder.
+ * Return the default save-to location for the Windows Library passed in
+ * through aFolderId.
*/
static nsresult
GetLibrarySaveToPath(int aFallbackFolderId, REFKNOWNFOLDERID aFolderId,
nsIFile** aFile)
{
- // Skip off checking for library support if the os is Vista or lower.
- if (!IsWin7OrLater()) {
- return GetWindowsFolder(aFallbackFolderId, aFile);
- }
-
RefPtr<IShellLibrary> shellLib;
RefPtr<IShellItem> savePath;
HRESULT hr =
@@ -740,8 +707,6 @@ GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
}
#if defined(MOZ_CONTENT_SANDBOX)
case Win_LocalAppdataLow: {
- // This should only really fail on versions pre-Vista, in which case this
- // shouldn't have been used in the first place.
GUID localAppDataLowGuid = FOLDERID_LocalAppDataLow;
return GetKnownFolder(&localAppDataLowGuid, aFile);
}
diff --git a/xpcom/io/SpecialSystemDirectory.h b/xpcom/io/SpecialSystemDirectory.h
index dd3d88379..7c7f8fa42 100644
--- a/xpcom/io/SpecialSystemDirectory.h
+++ b/xpcom/io/SpecialSystemDirectory.h
@@ -16,9 +16,6 @@
#include "prenv.h"
#endif
-extern void StartupSpecialSystemDirectory();
-
-
enum SystemDirectories {
OS_DriveDirectory = 1,
OS_TemporaryDirectory = 2,
diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp
index 3a7e570f5..66e267807 100644
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -7,7 +7,6 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/UniquePtrExtensions.h"
-#include "mozilla/WindowsVersion.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
@@ -1970,13 +1969,11 @@ nsLocalFile::CopySingleFile(nsIFile* aSourceFile, nsIFile* aDestParent,
// So we only use COPY_FILE_NO_BUFFERING when we have a remote drive.
int copyOK;
DWORD dwCopyFlags = COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
- if (IsVistaOrLater()) {
- bool path1Remote, path2Remote;
- if (!IsRemoteFilePath(filePath.get(), path1Remote) ||
- !IsRemoteFilePath(destPath.get(), path2Remote) ||
- path1Remote || path2Remote) {
- dwCopyFlags |= COPY_FILE_NO_BUFFERING;
- }
+ bool path1Remote, path2Remote;
+ if (!IsRemoteFilePath(filePath.get(), path1Remote) ||
+ !IsRemoteFilePath(destPath.get(), path2Remote) ||
+ path1Remote || path2Remote) {
+ dwCopyFlags |= COPY_FILE_NO_BUFFERING;
}
if (!move) {