You can turn on KeePassXC-Browser’s extension in Safari Extensions preferences.
+KeePassXC-Browser’s extension is currently on. You can turn it off in Safari Extensions preferences.
+KeePassXC-Browser’s extension is currently off. You can turn it on in Safari Extensions preferences.
+ + + diff --git a/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Icon.png b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Icon.png new file mode 100644 index 000000000..69b0fe24e Binary files /dev/null and b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Icon.png differ diff --git a/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Script.js b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Script.js new file mode 100644 index 000000000..47b8c9a4e --- /dev/null +++ b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Script.js @@ -0,0 +1,22 @@ +function show(enabled, useSettingsInsteadOfPreferences) { + if (useSettingsInsteadOfPreferences) { + document.getElementsByClassName('state-on')[0].innerText = "KeePassXC-Browser’s extension is currently on. You can turn it off in the Extensions section of Safari Settings."; + document.getElementsByClassName('state-off')[0].innerText = "KeePassXC-Browser’s extension is currently off. You can turn it on in the Extensions section of Safari Settings."; + document.getElementsByClassName('state-unknown')[0].innerText = "You can turn on KeePassXC-Browser’s extension in the Extensions section of Safari Settings."; + document.getElementsByClassName('open-preferences')[0].innerText = "Quit and Open Safari Settings…"; + } + + if (typeof enabled === "boolean") { + document.body.classList.toggle(`state-on`, enabled); + document.body.classList.toggle(`state-off`, !enabled); + } else { + document.body.classList.remove(`state-on`); + document.body.classList.remove(`state-off`); + } +} + +function openPreferences() { + webkit.messageHandlers.controller.postMessage("open-preferences"); +} + +document.querySelector("button.open-preferences").addEventListener("click", openPreferences); diff --git a/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Style.css b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Style.css new file mode 100644 index 000000000..cbde9e697 --- /dev/null +++ b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/Resources/Style.css @@ -0,0 +1,45 @@ +* { + -webkit-user-select: none; + -webkit-user-drag: none; + cursor: default; +} + +:root { + color-scheme: light dark; + + --spacing: 20px; +} + +html { + height: 100%; +} + +body { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + gap: var(--spacing); + margin: 0 calc(var(--spacing) * 2); + height: 100%; + + font: -apple-system-short-body; + text-align: center; +} + +body:not(.state-on, .state-off) :is(.state-on, .state-off) { + display: none; +} + +body.state-on :is(.state-off, .state-unknown) { + display: none; +} + +body.state-off :is(.state-on, .state-unknown) { + display: none; +} + +button { + font-size: 1em; +} diff --git a/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/ViewController.swift b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/ViewController.swift new file mode 100644 index 000000000..b8553909a --- /dev/null +++ b/keepassxc-browser-safari/KeePassXC-Browser/KeePassXC-Browser/ViewController.swift @@ -0,0 +1,57 @@ +// +// ViewController.swift +// KeePassXC-Browser +// +// Created by varjolintu on 12.12.2025. +// + +import Cocoa +import SafariServices +import WebKit + +let extensionBundleIdentifier = "org.keepassxc.KeePassXC-Browser.Extension" + +class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHandler { + + @IBOutlet var webView: WKWebView! + + override func viewDidLoad() { + super.viewDidLoad() + + self.webView.navigationDelegate = self + + self.webView.configuration.userContentController.add(self, name: "controller") + + self.webView.loadFileURL(Bundle.main.url(forResource: "Main", withExtension: "html")!, allowingReadAccessTo: Bundle.main.resourceURL!) + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + SFSafariExtensionManager.getStateOfSafariExtension(withIdentifier: extensionBundleIdentifier) { (state, error) in + guard let state = state, error == nil else { + // Insert code to inform the user that something went wrong. + return + } + + DispatchQueue.main.async { + if #available(macOS 13, *) { + webView.evaluateJavaScript("show(\(state.isEnabled), true)") + } else { + webView.evaluateJavaScript("show(\(state.isEnabled), false)") + } + } + } + } + + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + if (message.body as! String != "open-preferences") { + return; + } + + SFSafariApplication.showPreferencesForExtension(withIdentifier: extensionBundleIdentifier) { error in + DispatchQueue.main.async { + NSApplication.shared.terminate(nil) + } + } + } + +} diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index 41a9d5209..59d6c474b 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -870,6 +870,22 @@ "message": "Allow filling HTTP Basic Auth credentials", "description": "Allow filling HTTP Basic Auth credentials checkbox text." }, + "optionsConnectionMethod": { + "message": "Connection method", + "description": "Connection method selection text." + }, + "optionsConnectionMethodHelpText": { + "message": "If native messaging is blocked in your browser, you can switch to a direct WebSocket sonnection. The feature must be enabled from KeePassXC side to work. Browser restart is required.", + "description": "Connection method help text." + }, + "optionsConnectionMethodNativeMessaging": { + "message": "Native messaging", + "description": "Native messaging option for Connection method." + }, + "optionsConnectionMethodWebSocket": { + "message": "WebSocket", + "description": "WebSocket option for Connection method." + }, "optionsDebugLogging": { "message": "Debug logging", "description": "Debug logging checkbox text." diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index 0b6095d5a..91d0e599f 100755 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -83,7 +83,7 @@ browserAction.generateIconName = async function(iconType) { style = page.settings.colorTheme; } } - const filetype = page.isFirefox ? 'svg' : 'png'; + const filetype = (page.isFirefox || page.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..71e210460 100644 --- a/keepassxc-browser/background/client.js +++ b/keepassxc-browser/background/client.js @@ -5,6 +5,7 @@ keepassClient.keySize = 24; keepassClient.messageTimeout = 500; // Milliseconds keepassClient.nativeHostName = 'org.keepassxc.keepassxc_browser'; keepassClient.nativePort = null; +keepassClient.webSocket = null; const kpErrors = { UNKNOWN_ERROR: 0, @@ -157,7 +158,10 @@ class Message { //-------------------------------------------------------------------------- keepassClient.sendNativeMessage = async function(request, enableTimeout = false, timeoutValue) { - if (!keepassClient.nativePort) { + if (page?.settings?.connectionMethod === ConnectionMethod.WEBSOCKET && !keepassClient.webSocket) { + logError('No WebSocket defined.'); + return; + } else if (page?.settings?.connectionMethod === ConnectionMethod.NATIVE_MESSAGING && !keepassClient.nativePort) { logError('No native messaging port defined.'); return; } @@ -167,7 +171,11 @@ keepassClient.sendNativeMessage = async function(request, enableTimeout = false, messageBuffer.addMessage(message); }); - keepassClient.nativePort.postMessage(request); + if (page.isSafari || page?.settings?.connectionMethod === ConnectionMethod.WEBSOCKET) { + keepassClient.webSocket.send(JSON.stringify(request)); + } else { + keepassClient.nativePort.postMessage(request); + } const response = await message.promise; @@ -400,7 +408,7 @@ function onDisconnected() { page.clearAllLogins(); keepass.updatePopup('cross'); keepass.updateDatabaseHashToContent(); - logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)}`); + logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError?.message)}`); } keepassClient.onNativeMessage = function(response) { @@ -413,3 +421,42 @@ keepassClient.onNativeMessage = function(response) { // Generic response handling keepassClient.handleNativeMessage(response); }; + +//-------------------------------------------------------------------------- +// WebSocket related +//-------------------------------------------------------------------------- + +keepassClient.connectToWebSocket = async function() { + return new Promise((resolve, reject) => { + if (keepassClient.webSocket) { + keepassClient.webSocket.close(); + } + + console.log(`${EXTENSION_NAME}: Connecting to WebSocket`); + + try { + keepassClient.webSocket = new WebSocket('ws://localhost:7580'); + keepassClient.webSocket.addEventListener('close', (event) => { + logError('Close WebSocket:', event); + onDisconnected(); + reject(); + }); + keepassClient.webSocket.addEventListener('error', (event) => { + logError('WebSocket error:', event); + onDisconnected(); + reject(); + }); + keepassClient.webSocket.addEventListener('message', (event) => { + keepassClient.onNativeMessage(JSON.parse(event?.data)); + }); + keepassClient.webSocket.addEventListener('open', (event) => { + console.log(`${EXTENSION_NAME}: WebSocket connected`); + keepass.isConnected = true; + resolve(); + }); + } catch (e) { + keepassClient.webSocket = null; + onDisconnected(); + } + }); +}; diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 64cc278f6..a3dbfde3c 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 (page.isSafari) { + return; + } + let handleReq = httpAuth.handleRequestPromise; let reqType = 'blocking'; diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index c1d136706..2e8a50277 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -805,7 +805,12 @@ keepass.disableAutomaticReconnect = function() { }; keepass.reconnect = async function(tab = null, connectionTimeout = 1500) { - keepassClient.connectToNative(); + if (page?.settings?.connectionMethod === ConnectionMethod.WEBSOCKET) { + await keepassClient.connectToWebSocket(); + } else { + keepassClient.connectToNative(); + } + keepass.generateNewKeyPair(); const keyChangeResult = await keepass.changePublicKeys(tab, !!connectionTimeout, connectionTimeout).catch(() => false); diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 1476a8b04..83f50475b 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -1,5 +1,10 @@ 'use strict'; +const ConnectionMethod = { + NATIVE_MESSAGING: 'nativemessaging', + WEBSOCKET: 'websocket', +}; + const defaultSettings = { afterFillSorting: SORT_BY_MATCHING_CREDENTIALS_SETTING, afterFillSortingTotp: SORT_BY_RELEVANT_ENTRY, @@ -15,6 +20,7 @@ const defaultSettings = { checkUpdateKeePassXC: CHECK_UPDATE_NEVER, clearCredentialsTimeout: 10, colorTheme: 'system', + connectionMethod: isSafari() ? ConnectionMethod.WEBSOCKET : ConnectionMethod.NATIVE_MESSAGING, credentialSorting: SORT_BY_GROUP_AND_TITLE, debugLogging: false, defaultGroup: '', @@ -50,6 +56,7 @@ page.clearCredentialsTimeout = null; page.currentRequest = {}; page.currentTabId = -1; page.isFirefox = false; +page.isSafari = false; page.manualFill = ManualFill.NONE; page.menuContexts = [ 'editable' ]; page.passwordFilled = false; @@ -68,6 +75,7 @@ page.initBrowser = async function() { navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1 || typeof browser.runtime.getBrowserInfo === 'function'; + page.isSafari = isSafari(); }; page.initSettings = async function() { @@ -75,25 +83,27 @@ page.initSettings = async function() { const item = await browser.storage.local.get({ 'settings': {} }); // Load managed settings if found - if (page.isFirefox && typeof(browser.storage.managed) === 'object') { - try { - const managedSettings = await browser.storage.managed.get('settings'); - if (managedSettings?.settings) { - debugLogMessage('Managed settings found.'); - item.settings = managedSettings.settings; + if (!page.isSafari) { + if (page.isFirefox && typeof(browser.storage.managed) === 'object') { + try { + const managedSettings = await browser.storage.managed.get('settings'); + if (managedSettings?.settings) { + debugLogMessage('Managed settings found.'); + item.settings = managedSettings.settings; + } + } catch (err) { + debugLogMessage('page.initSettings: ' + err); } - } catch (err) { - debugLogMessage('page.initSettings: ' + err); + } else if (typeof chrome.storage.managed === 'object') { + chrome.storage.managed.get('settings').then((managedSettings) => { + if (managedSettings?.settings) { + debugLogMessage('Managed settings found.'); + item.settings = managedSettings.settings; + } + }).catch((err) => { + debugLogMessage('page.initSettings: ' + err); + }); } - } else if (typeof chrome.storage.managed === 'object') { - chrome.storage.managed.get('settings').then((managedSettings) => { - if (managedSettings?.settings) { - debugLogMessage('Managed settings found.'); - item.settings = managedSettings.settings; - } - }).catch((err) => { - debugLogMessage('page.initSettings: ' + err); - }); } page.settings = item.settings; diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 10f58f60f..7310550b6 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -36,6 +36,12 @@ const isEdge = function() { return navigator.userAgent.indexOf('Edg') !== -1; }; +const isSafari = function() { + return navigator.userAgent.indexOf('Safari') !== -1 + && navigator.userAgent.indexOf('Chrome') === -1 + && navigator.userAgent.indexOf('Chromium') === -1;; +}; + const showNotification = function(message) { browser.notifications.create({ 'type': 'basic', diff --git a/keepassxc-browser/content/banner.js b/keepassxc-browser/content/banner.js index 7e968b869..8db141d9d 100644 --- a/keepassxc-browser/content/banner.js +++ b/keepassxc-browser/content/banner.js @@ -62,7 +62,9 @@ kpxcBanner.create = async function(credentials = {}) { const bannerInfo = kpxcUI.createElement('div', 'banner-info'); const bannerButtons = kpxcUI.createElement('div', 'banner-buttons'); - const className = kpxc.isFirefox ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; + const className = isSafari() + ? 'kpxc-banner-icon-safari' + : kpxc.isFirefox ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' }); const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('rememberInfoText')); diff --git a/keepassxc-browser/content/custom-fields-banner.js b/keepassxc-browser/content/custom-fields-banner.js index 2b83d6a75..70816db55 100644 --- a/keepassxc-browser/content/custom-fields-banner.js +++ b/keepassxc-browser/content/custom-fields-banner.js @@ -93,7 +93,9 @@ kpxcCustomLoginFieldsBanner.create = async function() { const bannerInfo = kpxcUI.createElement('div', 'banner-info'); const bannerButtons = kpxcUI.createElement('div', 'banner-buttons'); - const iconClassName = kpxc.isFirefox ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; + const iconClassName = isSafari() + ? 'kpxc-banner-icon-safari' + : kpxc.isFirefox ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; const icon = kpxcUI.createElement('span', iconClassName); const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('defineChooseCustomLoginFieldText')); const separator = kpxcUI.createElement('div', 'kpxc-separator'); diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js index 522f073e5..cde438726 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 = kpxc.isFirefox ? 'key-moz' : 'key'; + const className = isSafari() ? 'key-safari' : kpxc.isFirefox ? 'key-moz' : 'key'; const size = this.calculateIconSize(field); const icon = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-icon ' + className, diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index f471d2ceb..e75e9f740 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 = kpxc.isFirefox ? 'moz' : 'default'; + const className = isSafari() ? 'safari' : kpxc.isFirefox ? 'moz' : 'default'; const size = this.calculateIconSize(field); const icon = kpxcUI.createElement('div', 'kpxc kpxc-totp-icon ' + className, diff --git a/keepassxc-browser/content/ui.js b/keepassxc-browser/content/ui.js index be2d43c35..f253cf4a7 100644 --- a/keepassxc-browser/content/ui.js +++ b/keepassxc-browser/content/ui.js @@ -369,7 +369,9 @@ kpxcUI.createNotification = async function(type, message) { const notification = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {}); type = type.charAt(0).toUpperCase() + type.slice(1) + '!'; - const className = kpxc.isFirefox ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; + const className = isSafari() + ? 'kpxc-banner-icon-safari' + : kpxc.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 3316bf66b..36be854a7 100644 --- a/keepassxc-browser/content/username-field.js +++ b/keepassxc-browser/content/username-field.js @@ -44,7 +44,8 @@ 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); @@ -147,12 +148,12 @@ const iconClicked = async function(field, icon) { const getIconClassName = function(state = DatabaseState.UNLOCKED) { if (state === DatabaseState.LOCKED) { - return kpxc.isFirefox ? 'lock-moz' : 'lock'; + return isSafari() ? 'lock-safari' : kpxc.isFirefox ? 'lock-moz' : 'lock'; } else if (state === DatabaseState.DISCONNECTED) { - return kpxc.isFirefox ? 'disconnected-moz' : 'disconnected'; + return isSafari() ? 'disconnected-safari' : kpxc.isFirefox ? 'disconnected-moz' : 'disconnected'; } - return kpxc.isFirefox ? 'unlock-moz' : 'unlock'; + return isSafari() ? 'unlock-safari' : kpxc.isFirefox ? 'unlock-moz' : 'unlock'; }; const getIconText = function(state) { diff --git a/keepassxc-browser/css/banner.css b/keepassxc-browser/css/banner.css index 31ee59086..b77ec088a 100644 --- a/keepassxc-browser/css/banner.css +++ b/keepassxc-browser/css/banner.css @@ -81,6 +81,14 @@ 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; @@ -97,6 +105,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 4368d25fb..065b48246 100644 --- a/keepassxc-browser/css/pwgen.css +++ b/keepassxc-browser/css/pwgen.css @@ -14,3 +14,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 a34ed7cf3..955ff5c4a 100644 --- a/keepassxc-browser/css/totp.css +++ b/keepassxc-browser/css/totp.css @@ -13,4 +13,9 @@ .kpxc-totp-icon.moz { background: url('moz-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat; background-size: contain; -} \ No newline at end of file +} + +.kpxc-totp-icon.safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat; + background-size: contain; +} diff --git a/keepassxc-browser/css/username.css b/keepassxc-browser/css/username.css index 42d438bd7..7b9686b52 100644 --- a/keepassxc-browser/css/username.css +++ b/keepassxc-browser/css/username.css @@ -15,6 +15,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; @@ -25,6 +30,11 @@ 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; @@ -34,3 +44,8 @@ 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; +} diff --git a/keepassxc-browser/options/options.css b/keepassxc-browser/options/options.css index e7827d38b..8f91eae05 100644 --- a/keepassxc-browser/options/options.css +++ b/keepassxc-browser/options/options.css @@ -63,6 +63,10 @@ input:disabled { opacity: .3!important; } +select:disabled { + opacity: .3!important; +} + footer { display: none; bottom: 1rem; diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index a9e6997cc..159cbde4d 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -154,7 +154,7 @@ -