Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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);
Expand All @@ -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');
}

Expand Down
188 changes: 188 additions & 0 deletions dist/manifest_safari.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
{
"manifest_version": 3,
"name": "KeePassXC-Browser",
"version": "1.9.7",
"version_name": "1.9.7",
"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": [
"<all_urls>"
],
"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"
],
"run_at": "document_idle",
"all_frames": true
},
{
"matches": [
"<all_urls>"
],
"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"
],
"content_security_policy": {
"extension_pages": "script-src 'self'"
},
"host_permissions": [
"<all_urls>",
"https://*/*",
"http://*/*"
],
"storage": {
"managed_schema": "managed_storage.json"
},
"default_locale": "en"
}
8 changes: 8 additions & 0 deletions keepassxc-browser/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
7 changes: 7 additions & 0 deletions keepassxc-browser/background/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ keepassClient.sendNativeMessage = async function(request, enableTimeout = false,
};

keepassClient.handleNativeMessage = async function(response) {
if (response?.name === 'proxy_message') return // Ignoring KeePassXC App Broadcast Message

// Parse through the message buffer to find the corresponding Promise.
await navigator.locks.request('messageBuffer', async (lock) => {
const message = messageBuffer.getMessage(response);
Expand Down Expand Up @@ -404,6 +406,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();
Expand Down
6 changes: 6 additions & 0 deletions keepassxc-browser/background/httpauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ httpAuth.requests = [];
httpAuth.pendingCallbacks = [];

httpAuth.init = function() {
if (isSafari()) {
// browser.webRequest is not supported in Safari Web Extensions.
logError('httpAuth.init error: Autofill HTTP Auth dialogs is not supported in Safari');
return;
}

let handleReq = httpAuth.handleRequestPromise;
let reqType = 'blocking';

Expand Down
1 change: 1 addition & 0 deletions keepassxc-browser/background/keepass.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
9 changes: 9 additions & 0 deletions keepassxc-browser/common/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion keepassxc-browser/content/banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand Down
2 changes: 1 addition & 1 deletion keepassxc-browser/content/custom-fields-banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion keepassxc-browser/content/pwgen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion keepassxc-browser/content/totp-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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);
Expand Down
2 changes: 1 addition & 1 deletion keepassxc-browser/content/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions keepassxc-browser/content/username-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand Down
Loading