diff --git a/build.js b/build.js index c3b739050..517c53445 100755 --- a/build.js +++ b/build.js @@ -10,6 +10,7 @@ const DEFAULT = 'manifest_default.json'; const BROWSERS = { 'Firefox': 'manifest_firefox.json', 'Chromium': 'manifest_chromium.json', + 'Safari': 'manifest_safari.json' }; const getVersion = async () => { @@ -54,6 +55,10 @@ const createZipFile = async (fileName, path) => { const version = await getVersion(); for (const browser in BROWSERS) { + if (params.includes('--safari-web-extension') && browser !== 'Safari') { + continue; + } + console.log(`KeePassXC-Browser: Creating extension package for ${browser}`); const fileName = await getDestinationFilename(BROWSERS[browser], version); @@ -64,7 +69,19 @@ const createZipFile = async (fileName, path) => { await fs.rm(fileName); } - await createZipFile(fileName, DEST); + if (params.includes('--safari-web-extension') && browser === 'Safari' && params.includes('-o')) { + let outputPath = params[params.indexOf('-o') + 1] + + if (!outputPath) { + console.error('No ouput path specified!'); + return; + } + + await fs.cpSync(DEST, outputPath, { recursive: true }) + } else { + await createZipFile(fileName, DEST); + } + console.log('Done'); } diff --git a/dist/manifest_chromium.json b/dist/manifest_chromium.json index 25990f375..8971ff40d 100755 --- a/dist/manifest_chromium.json +++ b/dist/manifest_chromium.json @@ -59,7 +59,8 @@ "content/pwgen.js", "content/totp-autocomplete.js", "content/totp-field.js", - "content/username-field.js" + "content/username-field.js", + "content/theme.js" ], "run_at": "document_idle", "all_frames": true diff --git a/dist/manifest_firefox.json b/dist/manifest_firefox.json index 9e90477b9..a690115e7 100644 --- a/dist/manifest_firefox.json +++ b/dist/manifest_firefox.json @@ -72,7 +72,8 @@ "content/pwgen.js", "content/totp-autocomplete.js", "content/totp-field.js", - "content/username-field.js" + "content/username-field.js", + "content/theme.js" ], "run_at": "document_idle", "all_frames": true diff --git a/dist/manifest_safari.json b/dist/manifest_safari.json new file mode 100644 index 000000000..b15abf315 --- /dev/null +++ b/dist/manifest_safari.json @@ -0,0 +1,190 @@ +{ + "manifest_version": 3, + "name": "KeePassXC-Browser", + "version": "1.9.8", + "version_name": "1.9.8", + "minimum_chrome_version": "93", + "description": "__MSG_extensionDescription__", + "author": "KeePassXC Team", + "action": { + "default_icon": { + "16": "icons/keepassxc_16x16.png", + "18": "icons/keepassxc_18x18.png", + "19": "icons/keepassxc_19x19.png", + "32": "icons/keepassxc_32x32.png", + "36": "icons/keepassxc_36x36.png", + "38": "icons/keepassxc_38x38.png", + "64": "icons/keepassxc_64x64.png" + }, + "default_title": "KeePassXC-Browser", + "default_popup": "popups/popup.html" + }, + "options_ui": { + "page": "options/options.html", + "open_in_tab": true + }, + "background": { + "scripts": [ + "common/browser-polyfill.min.js", + "common/global.js", + "common/sites.js", + "background/nacl.min.js", + "background/nacl-util.min.js", + "background/client.js", + "background/keepass.js", + "background/httpauth.js", + "background/offscreen.js", + "background/browserAction.js", + "background/page.js", + "background/event.js", + "background/init.js" + ] + }, + "content_scripts": [ + { + "matches": [ + "" + ], + "exclude_matches": [ + "*://*/*.xml*", + "file:///*.xml*" + ], + "js": [ + "common/browser-polyfill.min.js", + "common/global.js", + "common/sites.js", + "content/ui.js", + "content/banner.js", + "content/autocomplete.js", + "content/credential-autocomplete.js", + "content/custom-fields-banner.js", + "content/fields.js", + "content/fill.js", + "content/form.js", + "content/icons.js", + "content/keepassxc-browser.js", + "content/observer-helper.js", + "content/pwgen.js", + "content/totp-autocomplete.js", + "content/totp-field.js", + "content/username-field.js", + "content/theme.js" + ], + "run_at": "document_idle", + "all_frames": true + }, + { + "matches": [ + "" + ], + "exclude_matches": [ + "*://*/*.xml*", + "file:///*.xml*" + ], + "js": [ + "content/passkeys-inject.js", + "content/passkeys-utils.js" + ], + "run_at": "document_start", + "all_frames": true + } + ], + "commands": { + "fill_username_password": { + "description": "__MSG_contextMenuFillUsernameAndPassword__", + "suggested_key": { + "default": "Alt+Shift+U", + "mac": "MacCtrl+Shift+U" + } + }, + "fill_password": { + "description": "__MSG_contextMenuFillPassword__", + "suggested_key": { + "default": "Alt+Shift+P", + "mac": "MacCtrl+Shift+P" + } + }, + "fill_totp": { + "description": "__MSG_contextMenuFillTOTP__", + "suggested_key": { + "default": "Alt+Shift+O", + "mac": "MacCtrl+Shift+O" + } + }, + "show_password_generator": { + "description": "__MSG_contextMenuShowPasswordGenerator__", + "suggested_key": { + "default": "Alt+Shift+G", + "mac": "MacCtrl+Shift+G" + } + }, + "save_credentials": { + "description": "__MSG_contextMenuSaveCredentials__" + }, + "redetect_fields": { + "description": "__MSG_popupRedetectButton__" + }, + "choose_credential_fields": { + "description": "__MSG_popupChooseCredentialsText__" + }, + "retrive_credentials_forced": { + "description": "__MSG_popupReopenButton__" + }, + "request_autotype": { + "description": "__MSG_contextMenuRequestGlobalAutoType__" + }, + "reload_extension": { + "description": "__MSG_popupReloadButton__" + } + }, + "web_accessible_resources": [ + { + "resources": [ + "icons/disconnected.svg", + "icons/help.svg", + "icons/keepassxc.svg", + "icons/key.svg", + "icons/locked.svg", + "icons/otp.svg", + "css/autocomplete.css", + "css/banner.css", + "css/button.css", + "css/colors.css", + "css/define.css", + "css/notification.css", + "css/pwgen.css", + "css/username.css", + "css/totp.css", + "content/passkeys.js" + ], + "matches": [ + "https://*/*", + "http://*/*" + ] + } + ], + "permissions": [ + "activeTab", + "clipboardWrite", + "contextMenus", + "cookies", + "nativeMessaging", + "notifications", + "offscreen", + "storage", + "tabs", + "webNavigation" + ], + "content_security_policy": { + "extension_pages": "script-src 'self'" + }, + "host_permissions": [ + "", + "https://*/*", + "http://*/*" + ], + "storage": { + "managed_schema": "managed_storage.json" + }, + "default_locale": "en" +} \ No newline at end of file diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index b6f96c259..b91c8419a 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -1377,5 +1377,13 @@ "lockDatabase": { "message": "Lock database", "description": "Lock database button title text." + }, + "optionsUnsupportedKeyboardShortcutsSafari": { + "message": "Changing keyboard shortcuts is not supported in Safari.", + "description": "Info message about unsupported ability to change keyboard shortcuts in Safari." + }, + "optionsUnsupportedAutofillHTTPAuthDialogsSafari": { + "message": "Autofill HTTP Auth dialogs is not supported in Safari.", + "description": "Info message about autofilling HTTP Auth dialogs is unsupported in Safari." } } diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index 1a28354b7..3c0e460f5 100755 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -3,13 +3,19 @@ const browserActionWrapper = browser.action || browser.browserAction; const browserAction = {}; -browserAction.show = async function(tab, popupData) { +browserAction.updateIcon = async function(popupData) { popupData ??= page.popupData; - page.popupData = popupData; browserActionWrapper.setIcon({ path: await browserAction.generateIconName(popupData.iconType) }); +} + +browserAction.show = async function(tab, popupData) { + popupData ??= page.popupData; + page.popupData = popupData; + + browserAction.updateIcon(popupData); if (popupData.popup && tab?.id) { browserActionWrapper.setPopup({ @@ -83,7 +89,7 @@ browserAction.generateIconName = async function(iconType) { style = page.settings.colorTheme; } } - const filetype = (isFirefox() ? 'svg' : 'png'); + const filetype = ((isFirefox() || isSafari()) ? 'svg' : 'png'); return `/icons/toolbar/${style}/${name}.${filetype}`; }; diff --git a/keepassxc-browser/background/client.js b/keepassxc-browser/background/client.js index c98b01dec..9965a04a1 100644 --- a/keepassxc-browser/background/client.js +++ b/keepassxc-browser/background/client.js @@ -404,6 +404,11 @@ function onDisconnected() { } keepassClient.onNativeMessage = function(response) { + // Due to limitiations on SFSafariApplication.dispatchMessage this is needed + if (response?.name === 'proxy_message') { + response = response.userInfo + } + // Handle database lock/unlock status if (response.action === kpActions.DATABASE_LOCKED || response.action === kpActions.DATABASE_UNLOCKED) { keepass.updateDatabase(); diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index a6d30555a..4c7a3fb72 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -164,6 +164,10 @@ kpxcEvent.onLoginPopup = async function(tab, logins) { } }; +kpxcEvent.themeChanged = async function () { + browserAction.updateIcon(); +}; + kpxcEvent.initHttpAuth = async function() { httpAuth.init(); }; @@ -303,5 +307,6 @@ kpxcEvent.messageHandlers = { 'save_settings': kpxcEvent.onSaveSettings, 'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC, 'update_context_menu': page.updateContextMenu, - 'update_popup': page.updatePopup + 'update_popup': page.updatePopup, + 'theme_changed': kpxcEvent.themeChanged }; diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 4b5e61c82..71c08c29f 100755 --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -6,6 +6,10 @@ httpAuth.requests = []; httpAuth.pendingCallbacks = []; httpAuth.init = function() { + if (isSafari()) { + return; + } + let handleReq = httpAuth.handleRequestPromise; let reqType = 'blocking'; diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index ae6ec86ae..bd5c5c04e 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -16,6 +16,7 @@ keepass.cacheTimeout = 30 * 1000; // Milliseconds keepass.databaseHash = ''; keepass.previousDatabaseHash = ''; keepass.reconnectLoop = null; +keepass.isSafari = isSafari(); const kpActions = { SET_LOGIN: 'set-login', diff --git a/keepassxc-browser/background/offscreen.js b/keepassxc-browser/background/offscreen.js index 921816a7b..cb4e98d1a 100644 --- a/keepassxc-browser/background/offscreen.js +++ b/keepassxc-browser/background/offscreen.js @@ -1,6 +1,12 @@ 'use strict'; async function retrieveColorScheme() { + if (isSafari()) { + const tab = await getCurrentTab(); + const theme = tab?.id && await browser.tabs.sendMessage(tab.id, { action: 'get_theme' }); + return theme ?? 'light'; + } + if (typeof window !== 'undefined') { // Firefox does not support the offscreen API but its background script still has a window (so far) return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; @@ -23,9 +29,11 @@ async function retrieveColorScheme() { target: 'offscreen', action: 'retrieve_color_scheme', }); + if (!style) { // if messaging fails then use "light" icon return 'light'; } + return style; } diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 55de510e7..6f227eb68 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -34,6 +34,15 @@ const isEdge = function() { return navigator.userAgent.indexOf('Edg') !== -1; }; +let cachedIsSafari = null; + +function isSafari() { + if (cachedIsSafari === null) { + cachedIsSafari = browser.runtime.getURL('').startsWith('safari'); + } + return cachedIsSafari; +} + const showNotification = function(message) { browser.notifications.create({ 'type': 'basic', diff --git a/keepassxc-browser/content/banner.js b/keepassxc-browser/content/banner.js index eb732a322..115d3dd5b 100644 --- a/keepassxc-browser/content/banner.js +++ b/keepassxc-browser/content/banner.js @@ -62,7 +62,7 @@ kpxcBanner.create = async function(credentials = {}) { const bannerInfo = kpxcUI.createElement('div', 'banner-info'); const bannerButtons = kpxcUI.createElement('div', 'banner-buttons'); - const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'); + const className = (isSafari() ? 'kpxc-banner-icon-safari' : (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon')); const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' }); const infoText = kpxcUI.createElement('span', '', {}, tr('rememberInfoText')); diff --git a/keepassxc-browser/content/custom-fields-banner.js b/keepassxc-browser/content/custom-fields-banner.js index 299899c56..efa6c8350 100644 --- a/keepassxc-browser/content/custom-fields-banner.js +++ b/keepassxc-browser/content/custom-fields-banner.js @@ -90,7 +90,7 @@ kpxcCustomLoginFieldsBanner.create = async function() { const bannerInfo = kpxcUI.createElement('div', 'banner-info'); const bannerButtons = kpxcUI.createElement('div', 'banner-buttons'); - const iconClassName = isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; + const iconClassName = isSafari() ? 'kpxc-banner-icon-safari' : (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'); const icon = kpxcUI.createElement('span', iconClassName); const infoText = kpxcUI.createElement('span', '', {}, tr('defineChooseCustomLoginFieldText')); const separator = kpxcUI.createElement('div', 'kpxc-separator'); diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index b51d8e863..19f7a8be3 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -1004,6 +1004,8 @@ browser.runtime.onMessage.addListener(async function(req, sender) { kpxcPasswordGenerator.showPasswordGenerator(); } else if (req.action === 'request_autotype') { sendMessage('request_autotype', [ window.location.hostname ]); + } else if (req.action === 'get_theme') { + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } } }); diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js index c9a3bf571..57894462e 100644 --- a/keepassxc-browser/content/pwgen.js +++ b/keepassxc-browser/content/pwgen.js @@ -49,7 +49,7 @@ PasswordIcon.prototype.initField = function(field) { }; PasswordIcon.prototype.createIcon = function(field) { - const className = (isFirefox() ? 'key-moz' : 'key'); + const className = isSafari() ? 'key-safari' : (isFirefox() ? 'key-moz' : 'key'); const size = (field.offsetHeight > 28) ? 24 : 16; const offset = kpxcUI.calculateIconOffset(field, size); diff --git a/keepassxc-browser/content/theme.js b/keepassxc-browser/content/theme.js new file mode 100644 index 000000000..672fc37be --- /dev/null +++ b/keepassxc-browser/content/theme.js @@ -0,0 +1,3 @@ +window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + sendMessage('theme_changed'); +}); diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index f80cd0673..32bf26dce 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -139,7 +139,7 @@ TOTPFieldIcon.prototype.initField = async function(field, segmented) { }; TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) { - const className = (isFirefox() ? 'moz' : 'default'); + const className = (isSafari() ? 'safari' : (isFirefox() ? 'moz' : 'default')); // Size the icon dynamically, but not greater than 24 or smaller than 14 const size = Math.max(Math.min(24, field.offsetHeight - 4), 14); diff --git a/keepassxc-browser/content/ui.js b/keepassxc-browser/content/ui.js index 49379ce8a..db67e0b08 100644 --- a/keepassxc-browser/content/ui.js +++ b/keepassxc-browser/content/ui.js @@ -353,7 +353,7 @@ kpxcUI.createNotification = function(type, message) { const notification = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {}); type = type.charAt(0).toUpperCase() + type.slice(1) + '!'; - const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'); + const className = (isSafari() ? 'kpxc-banner-icon-safari' : (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon')); const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' }); const label = kpxcUI.createElement('span', 'kpxc-label', {}, type); const msg = kpxcUI.createElement('span', '', {}, message); diff --git a/keepassxc-browser/content/username-field.js b/keepassxc-browser/content/username-field.js index 6f3afb19b..4f7b28fae 100644 --- a/keepassxc-browser/content/username-field.js +++ b/keepassxc-browser/content/username-field.js @@ -44,7 +44,7 @@ class UsernameFieldIcon extends Icon { this.observer.disconnect(); } - this.icon.classList.remove('lock', 'lock-moz', 'unlock', 'unlock-moz', 'disconnected', 'disconnected-moz'); + this.icon.classList.remove('lock', 'lock-moz', 'lock-safari', 'unlock', 'unlock-moz', 'unlock-safari', 'disconnected', 'disconnected-moz', 'disconnected-safari'); this.icon.classList.add(getIconClassName(state)); this.icon.title = getIconText(state); @@ -139,12 +139,12 @@ const iconClicked = async function(field, icon) { const getIconClassName = function(state = DatabaseState.UNLOCKED) { if (state === DatabaseState.LOCKED) { - return (isFirefox() ? 'lock-moz' : 'lock'); + return (isSafari() ? 'lock-safari' : isFirefox() ? 'lock-moz' : 'lock'); } else if (state === DatabaseState.DISCONNECTED) { - return (isFirefox() ? 'disconnected-moz' : 'disconnected'); + return (isSafari() ? 'disconnected-safari' : isFirefox() ? 'disconnected-moz' : 'disconnected'); } - return (isFirefox() ? 'unlock-moz' : 'unlock'); + return (isSafari() ? 'unlock-safari' : isFirefox() ? 'unlock-moz' : 'unlock'); }; const getIconText = function(state) { diff --git a/keepassxc-browser/css/banner.css b/keepassxc-browser/css/banner.css index 797952d79..f78889c15 100644 --- a/keepassxc-browser/css/banner.css +++ b/keepassxc-browser/css/banner.css @@ -77,6 +77,16 @@ div.kpxc-banner .kpxc-banner-icon-moz { background-size: contain; } +div.kpxc-banner .kpxc-banner-icon-safari { + width: 24px; + height: 24px; + overflow: hidden; + + + background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; + background-size: contain; +} + div.kpxc-banner .kpxc-help-icon { width: 24px; height: 24px; @@ -93,6 +103,14 @@ div.kpxc-banner .kpxc-help-icon-moz { background-size: contain; } +div.kpxc-banner .kpxc-help-icon-safari { + width: 24px; + height: 24px; + overflow: hidden; + background: url('safari-web-extension://__MSG_@@extension_id__/icons/help.svg') right no-repeat; + background-size: contain; +} + .kpxc-separator { border-left: 1px solid #ccc; height: 100% !important; diff --git a/keepassxc-browser/css/notification.css b/keepassxc-browser/css/notification.css index 1bb815f33..7cebdeaf0 100644 --- a/keepassxc-browser/css/notification.css +++ b/keepassxc-browser/css/notification.css @@ -43,6 +43,16 @@ background-size: contain; } +.kpxc-notification .kpxc-banner-icon-safari { + width: 24px; + height: 24px; + padding: 10px; + margin-right: 4px; + overflow: hidden; + background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; + background-size: contain; +} + .kpxc-notification .kpxc-label { font-weight: bold; } diff --git a/keepassxc-browser/css/pwgen.css b/keepassxc-browser/css/pwgen.css index 64b574dac..6e148849c 100644 --- a/keepassxc-browser/css/pwgen.css +++ b/keepassxc-browser/css/pwgen.css @@ -12,3 +12,8 @@ background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat; background-size: contain; } + +.kpxc-pwgen-icon.key-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat; + background-size: contain; +} diff --git a/keepassxc-browser/css/totp.css b/keepassxc-browser/css/totp.css index 828113267..380815c3f 100644 --- a/keepassxc-browser/css/totp.css +++ b/keepassxc-browser/css/totp.css @@ -11,4 +11,9 @@ .kpxc-totp-icon.moz { background: url('moz-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat; background-size: contain; +} + +.kpxc-totp-icon.safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat; + background-size: contain; } \ No newline at end of file diff --git a/keepassxc-browser/css/username.css b/keepassxc-browser/css/username.css index 6b2e32af6..69cfad25d 100644 --- a/keepassxc-browser/css/username.css +++ b/keepassxc-browser/css/username.css @@ -13,6 +13,11 @@ background-size: contain; } +.kpxc-username-icon.disconnected-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/disconnected.svg') right no-repeat; + background-size: contain; +} + .kpxc-username-icon.lock { background: url('chrome-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat; background-size: contain; @@ -23,6 +28,13 @@ background-size: contain; } +.kpxc-username-icon.lock-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat; + + + background-size: contain; +} + .kpxc-username-icon.unlock { background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; background-size: contain; @@ -32,3 +44,9 @@ background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; background-size: contain; } + + +.kpxc-username-icon.unlock-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; + background-size: contain; +} \ No newline at end of file diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index 25990f375..8971ff40d 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -59,7 +59,8 @@ "content/pwgen.js", "content/totp-autocomplete.js", "content/totp-field.js", - "content/username-field.js" + "content/username-field.js", + "content/theme.js" ], "run_at": "document_idle", "all_frames": true diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 28e851d2f..42f6ced9b 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -125,7 +125,7 @@

-
+
@@ -166,12 +166,17 @@

: error
: error

-

+

+ +
@@ -285,6 +290,11 @@

+ +
@@ -404,7 +414,7 @@

-
+
@@ -873,6 +883,7 @@


Lukas Schulze
Stefan Sundin
dynobo +
Sebastian Livoni

diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index c06a53074..aebf7ffc5 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -98,6 +98,11 @@ options.initGeneralSettings = async function() { $('#defaultGroupButtonReset').disabled = true; } + // Autofill HTTP Auth dialogs is not supported in Safari Web Extensions. + if (checkbox.name === 'autoFillAndSend' && isSafari()) { + checkbox.disabled = true; + } + checkbox.addEventListener('click', changeCheckboxValue); } @@ -323,6 +328,9 @@ options.initGeneralSettings = async function() { siteListing.append(document.createElement('br')); } } + + // Show and hide Safari specific features + options.showHideSafariSelectors() }; // Also hides/disables any options with KeePassXC versions that are too old @@ -799,6 +807,12 @@ options.createWarning = function(elem, text) { }, 5000); }; +options.showHideSafariSelectors = function() { + let selector = isSafari() ? '.hide-safari' : '.show-safari' + + document.querySelectorAll(selector).forEach((elem) => elem.hide()) +} + const getBrowserId = function() { if (navigator.userAgent.indexOf('Firefox') > -1) { return 'Mozilla Firefox ' + navigator.userAgent.substr(navigator.userAgent.lastIndexOf('/') + 1); @@ -812,6 +826,12 @@ const getBrowserId = function() { startPos = navigator.userAgent.indexOf('/', startPos) + 1; const version = navigator.userAgent.substring(startPos, navigator.userAgent.indexOf('Safari')); return 'Chrome/Chromium ' + version; + } else if (navigator.userAgent.indexOf('Safari') > -1) { + let startPos = navigator.userAgent.indexOf('Version'); + + startPos = navigator.userAgent.indexOf('/', startPos) + 1; + const version = navigator.userAgent.substring(startPos, navigator.userAgent.indexOf('Safari')); + return 'Safari ' + version; } return 'Other/Unknown'; diff --git a/keepassxc-browser/popups/popup.css b/keepassxc-browser/popups/popup.css index 3f0929fea..8e38231a7 100644 --- a/keepassxc-browser/popups/popup.css +++ b/keepassxc-browser/popups/popup.css @@ -153,6 +153,15 @@ code { width: 2.5rem; } +#choose-custom-login-fields-button-safari { + background-image: url('safari-web-extension://__MSG_@@extension_id__/icons/custom_login_fields.svg'); + background-position: center; + background-repeat: no-repeat; + background-size: 70%; + height: 31px; + width: 2.5rem; +} + #lock-database-button { display: none; width: 2.5rem; diff --git a/keepassxc-browser/popups/popup_functions.js b/keepassxc-browser/popups/popup_functions.js index 82ad4543a..6cf135658 100644 --- a/keepassxc-browser/popups/popup_functions.js +++ b/keepassxc-browser/popups/popup_functions.js @@ -18,6 +18,8 @@ async function initSettings() { const customLoginFieldsButton = document.body.querySelector('#settings #choose-custom-login-fields-button'); if (isFirefox()) { customLoginFieldsButton.id = 'choose-custom-login-fields-button-moz'; + } else if (isSafari()) { + customLoginFieldsButton.id = 'choose-custom-login-fields-button-safari'; } customLoginFieldsButton.addEventListener('click', async () => {