From 27ca7cc739ecd0622d87b07f9089b141641b877c Mon Sep 17 00:00:00 2001 From: Hinton Date: Mon, 7 Dec 2020 20:00:49 +0100 Subject: [PATCH] WIP Safari web extension --- src/background/main.background.ts | 42 +- src/browser/browserApi.ts | 124 +--- src/browser/safariApp.ts | 36 +- src/popup/app-routing.animations.ts | 6 +- src/safari/desktop.xcodeproj/project.pbxproj | 357 ++++----- .../contents.xcworkspacedata | 2 +- .../xcshareddata/xcschemes/desktop.xcscheme | 78 ++ src/safari/desktop/AppDelegate.swift | 12 +- .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 36 +- .../AppIcon.appiconset/icon128.png | Bin 0 -> 5763 bytes .../AppIcon.appiconset/icon16.png | Bin 0 -> 3245 bytes .../AppIcon.appiconset/icon32.png | Bin 0 -> 3653 bytes .../desktop/Assets.xcassets/Contents.json | 6 +- src/safari/desktop/Base.lproj/Main.storyboard | 684 ++---------------- src/safari/desktop/Info.plist | 6 +- src/safari/desktop/ViewController.swift | 40 +- .../SafariExtensionViewController.xib | 20 - src/safari/safari/Info.plist | 64 +- .../safari/SafariExtensionHandler.swift | 101 --- .../SafariExtensionViewController.swift | 422 ----------- .../safari/SafariWebExtensionHandler.swift | 109 +++ src/safari/safari/ToolbarItemIcon.pdf | Bin 46066 -> 0 bytes src/safari/safari/app/popup/index.html | 29 - src/services/browserMessaging.service.ts | 10 +- src/services/browserPlatformUtils.service.ts | 10 +- src/services/browserStorage.service.ts | 58 +- src/services/i18n.service.ts | 16 +- 28 files changed, 607 insertions(+), 1672 deletions(-) create mode 100644 src/safari/desktop.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme create mode 100644 src/safari/desktop/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon128.png create mode 100644 src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon16.png create mode 100644 src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon32.png delete mode 100644 src/safari/safari/Base.lproj/SafariExtensionViewController.xib delete mode 100644 src/safari/safari/SafariExtensionHandler.swift delete mode 100644 src/safari/safari/SafariExtensionViewController.swift create mode 100644 src/safari/safari/SafariWebExtensionHandler.swift delete mode 100644 src/safari/safari/ToolbarItemIcon.pdf delete mode 100644 src/safari/safari/app/popup/index.html diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 6de29fa43a..d2138dd96a 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -165,8 +165,8 @@ export default class MainBackground { return promise.then((result) => result.response === 'unlocked'); } }); - this.storageService = new BrowserStorageService(this.platformUtilsService); - this.secureStorageService = new BrowserStorageService(this.platformUtilsService); + this.storageService = new BrowserStorageService(); + this.secureStorageService = new BrowserStorageService(); this.i18nService = new I18nService(BrowserApi.getUILanguage(window)); this.cryptoFunctionService = new WebCryptoFunctionService(window, this.platformUtilsService); this.cryptoService = new CryptoService(this.storageService, this.secureStorageService, @@ -249,17 +249,15 @@ export default class MainBackground { this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService, this.platformUtilsService, this.analytics, this.vaultTimeoutService); - if (!this.isSafari) { - this.tabsBackground = new TabsBackground(this); - this.contextMenusBackground = new ContextMenusBackground(this, this.cipherService, - this.passwordGenerationService, this.analytics, this.platformUtilsService, this.vaultTimeoutService, - this.eventService, this.totpService); - this.idleBackground = new IdleBackground(this.vaultTimeoutService, this.storageService, - this.notificationsService); - this.webRequestBackground = new WebRequestBackground(this.platformUtilsService, this.cipherService, - this.vaultTimeoutService); - this.windowsBackground = new WindowsBackground(this); - } + this.tabsBackground = new TabsBackground(this); + this.contextMenusBackground = new ContextMenusBackground(this, this.cipherService, + this.passwordGenerationService, this.analytics, this.platformUtilsService, this.vaultTimeoutService, + this.eventService, this.totpService); + this.idleBackground = new IdleBackground(this.vaultTimeoutService, this.storageService, + this.notificationsService); + this.webRequestBackground = new WebRequestBackground(this.platformUtilsService, this.cipherService, + this.vaultTimeoutService); + this.windowsBackground = new WindowsBackground(this); } async bootstrap() { @@ -273,13 +271,11 @@ export default class MainBackground { await this.runtimeBackground.init(); await this.commandsBackground.init(); - if (!this.isSafari) { - await this.tabsBackground.init(); - await this.contextMenusBackground.init(); - await this.idleBackground.init(); - await this.webRequestBackground.init(); - await this.windowsBackground.init(); - } + await this.tabsBackground.init(); + await this.contextMenusBackground.init(); + await this.idleBackground.init(); + await this.webRequestBackground.init(); + await this.windowsBackground.init(); return new Promise((resolve) => { setTimeout(async () => { @@ -294,7 +290,7 @@ export default class MainBackground { } async setIcon() { - if (this.isSafari || (!chrome.browserAction && !this.sidebarAction)) { + if ((!chrome.browserAction && !this.sidebarAction)) { return; } @@ -313,7 +309,7 @@ export default class MainBackground { } async refreshBadgeAndMenu(forLocked: boolean = false) { - if (this.isSafari || !chrome.windows || !chrome.contextMenus) { + if (!chrome.windows || !chrome.contextMenus) { return; } @@ -444,7 +440,7 @@ export default class MainBackground { } private async buildContextMenu() { - if (this.isSafari || !chrome.contextMenus || this.buildingContextMenu) { + if (!chrome.contextMenus || this.buildingContextMenu) { return; } diff --git a/src/browser/browserApi.ts b/src/browser/browserApi.ts index 1ee54a45b1..c19f0059e3 100644 --- a/src/browser/browserApi.ts +++ b/src/browser/browserApi.ts @@ -4,20 +4,16 @@ import { Utils } from 'jslib/misc/utils'; export class BrowserApi { static isWebExtensionsApi: boolean = (typeof browser !== 'undefined'); - static isSafariApi: boolean = (window as any).safariAppExtension === true; + static isSafariApi: boolean = true; static isChromeApi: boolean = !BrowserApi.isSafariApi && (typeof chrome !== 'undefined'); static isFirefoxOnAndroid: boolean = navigator.userAgent.indexOf('Firefox/') !== -1 && navigator.userAgent.indexOf('Android') !== -1; static async getTabFromCurrentWindowId(): Promise { - if (BrowserApi.isChromeApi) { - return await BrowserApi.tabsQueryFirst({ - active: true, - windowId: chrome.windows.WINDOW_ID_CURRENT, - }); - } else if (BrowserApi.isSafariApi) { - return await BrowserApi.getTabFromCurrentWindow(); - } + return await BrowserApi.tabsQueryFirst({ + active: true, + windowId: chrome.windows.WINDOW_ID_CURRENT, + }); } static async getTabFromCurrentWindow(): Promise { @@ -34,16 +30,11 @@ export class BrowserApi { } static async tabsQuery(options: any): Promise { - if (BrowserApi.isChromeApi) { - return new Promise((resolve) => { - chrome.tabs.query(options, (tabs: any[]) => { - resolve(tabs); - }); + return new Promise((resolve) => { + chrome.tabs.query(options, (tabs: any[]) => { + resolve(tabs); }); - } else if (BrowserApi.isSafariApi) { - const tabs = await SafariApp.sendMessageToApp('tabs_query', JSON.stringify(options)); - return tabs != null ? JSON.parse(tabs) : null; - } + }); } static async tabsQueryFirst(options: any): Promise { @@ -72,81 +63,36 @@ export class BrowserApi { return; } - if (BrowserApi.isChromeApi) { - return new Promise((resolve) => { - chrome.tabs.sendMessage(tab.id, obj, options, () => { - if (chrome.runtime.lastError) { - // Some error happened - } - resolve(); - }); + return new Promise((resolve) => { + chrome.tabs.sendMessage(tab.id, obj, options, () => { + if (chrome.runtime.lastError) { + // Some error happened + } + resolve(); }); - } else if (BrowserApi.isSafariApi) { - if (options != null && options.frameId != null && obj.bitwardenFrameId == null) { - obj.bitwardenFrameId = options.frameId; - } - await SafariApp.sendMessageToApp('tabs_message', JSON.stringify({ - tab: tab, - obj: JSON.stringify(obj), - options: options, - }), true); - } + }); } static getBackgroundPage(): any { - if (BrowserApi.isChromeApi) { - return chrome.extension.getBackgroundPage(); - } else if (BrowserApi.isSafariApi) { - return window; - } else { - return null; - } + return chrome.extension.getBackgroundPage(); } static getApplicationVersion(): string { - if (BrowserApi.isChromeApi) { - return chrome.runtime.getManifest().version; - } else if (BrowserApi.isSafariApi) { - return (window as any).bitwardenApplicationVersion; - } else { - return null; - } + return chrome.runtime.getManifest().version; } static async isPopupOpen(): Promise { - if (BrowserApi.isChromeApi) { - return Promise.resolve(chrome.extension.getViews({ type: 'popup' }).length > 0); - } else if (BrowserApi.isSafariApi) { - const open = await SafariApp.sendMessageToApp('isPopoverOpen'); - return open === 'true'; - } else { - return Promise.resolve(false); - } + return Promise.resolve(chrome.extension.getViews({ type: 'popup' }).length > 0); } static createNewTab(url: string, extensionPage: boolean = false) { - if (BrowserApi.isChromeApi) { - chrome.tabs.create({ url: url }); - } else if (BrowserApi.isSafariApi) { - SafariApp.sendMessageToApp('createNewTab', url, true); - } + chrome.tabs.create({ url: url }); } static messageListener(name: string, callback: (message: any, sender: any, response: any) => void) { - if (BrowserApi.isChromeApi) { - chrome.runtime.onMessage.addListener((msg: any, sender: any, response: any) => { - callback(msg, sender, response); - }); - } else if (BrowserApi.isSafariApi) { - SafariApp.addMessageListener(name, (message: any, sender: any, response: any) => { - if (message.bitwardenFrameId != null) { - if (sender != null && typeof (sender) === 'object' && sender.frameId == null) { - sender.frameId = message.bitwardenFrameId; - } - } - callback(message, sender, response); - }); - } + chrome.runtime.onMessage.addListener((msg: any, sender: any, response: any) => { + callback(msg, sender, response); + }); } static closePopup(win: Window) { @@ -155,10 +101,8 @@ export class BrowserApi { // condition is only called if the popup wasn't already dismissed (future proofing). // ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1433604 browser.tabs.update({ active: true }).finally(win.close); - } else if (BrowserApi.isWebExtensionsApi || BrowserApi.isChromeApi) { + } else { win.close(); - } else if (BrowserApi.isSafariApi) { - SafariApp.sendMessageToApp('hidePopover'); } } @@ -196,30 +140,22 @@ export class BrowserApi { } static getUILanguage(win: Window) { - if (BrowserApi.isSafariApi) { - return win.navigator.language; - } else { - return chrome.i18n.getUILanguage(); - } + return chrome.i18n.getUILanguage(); } static reloadExtension(win: Window) { if (win != null) { return win.location.reload(true); - } else if (BrowserApi.isSafariApi) { - SafariApp.sendMessageToApp('reloadExtension'); - } else if (!BrowserApi.isSafariApi) { + } else { return chrome.runtime.reload(); } } static reloadOpenWindows() { - if (!BrowserApi.isSafariApi) { - const views = chrome.extension.getViews() as Window[]; - views.filter((w) => w.location.href != null).forEach((w) => { - w.location.reload(); - }); - } + const views = chrome.extension.getViews() as Window[]; + views.filter((w) => w.location.href != null).forEach((w) => { + w.location.reload(); + }); } static connectNative(application: string): browser.runtime.Port | chrome.runtime.Port { diff --git a/src/browser/safariApp.ts b/src/browser/safariApp.ts index 5593b069b8..3c9b887a1d 100644 --- a/src/browser/safariApp.ts +++ b/src/browser/safariApp.ts @@ -6,6 +6,7 @@ export class SafariApp { return; } (window as any).bitwardenSafariAppInited = true; + if (BrowserApi.isSafariApi) { (window as any).bitwardenSafariAppRequests = new Map void, timeoutDate: Date }>(); @@ -14,31 +15,25 @@ export class SafariApp { (window as any).bitwardenSafariAppMessageReceiver = (message: any) => { SafariApp.receiveMessageFromApp(message); }; - setInterval(() => SafariApp.cleanupOldRequests(), 5 * 60000); // every 5 mins } } static sendMessageToApp(command: string, data: any = null, resolveNow = false): Promise { + debugger; if (!BrowserApi.isSafariApi) { return Promise.resolve(null); } return new Promise((resolve) => { const now = new Date(); const messageId = now.getTime().toString() + '_' + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); - (window as any).webkit.messageHandlers.bitwardenApp.postMessage(JSON.stringify({ + (browser as any).runtime.sendNativeMessage("com.bitwarden.desktop", { id: messageId, command: command, data: data, responseData: null, - })); - if (resolveNow) { - resolve(); - } else { - (window as any).bitwardenSafariAppRequests.set(messageId, { - resolve: resolve, - timeoutDate: new Date(now.getTime() + 5 * 60000), - }); - } + }, (response: any) => { + resolve(response); + }); }); } @@ -56,30 +51,13 @@ export class SafariApp { } if ((message.id == null || message.id === '') && message.command === 'app_message') { try { + debugger; const msg = JSON.parse(message.data); SafariApp.sendMessageToListeners(msg, { id: 'app_message', tab: message.senderTab, }, null); } catch { } - } else if (message.id != null && (window as any).bitwardenSafariAppRequests.has(message.id)) { - const p = (window as any).bitwardenSafariAppRequests.get(message.id); - p.resolve(message.responseData); - (window as any).bitwardenSafariAppRequests.delete(message.id); } } - - private static cleanupOldRequests() { - const removeIds: string[] = []; - ((window as any).bitwardenSafariAppRequests as - Map void, timeoutDate: Date }>) - .forEach((v, key) => { - if (v.timeoutDate < new Date()) { - removeIds.push(key); - } - }); - removeIds.forEach((id) => { - (window as any).bitwardenSafariAppRequests.delete(id); - }); - } } diff --git a/src/popup/app-routing.animations.ts b/src/popup/app-routing.animations.ts index a852922b0c..05e3197605 100644 --- a/src/popup/app-routing.animations.ts +++ b/src/popup/app-routing.animations.ts @@ -185,8 +185,6 @@ export const routerTransition = trigger('routerTransition', [ transition('tabs => premium', inSlideLeft), transition('premium => tabs', outSlideRight), -]); -if (!BrowserApi.isSafariApi) { - routerTransition.definitions.push(transition('tabs => lock', inSlideDown)); -} + transition('tabs => lock', inSlideDown), +]); diff --git a/src/safari/desktop.xcodeproj/project.pbxproj b/src/safari/desktop.xcodeproj/project.pbxproj index b563d9d34e..7c38c7084d 100644 --- a/src/safari/desktop.xcodeproj/project.pbxproj +++ b/src/safari/desktop.xcodeproj/project.pbxproj @@ -7,37 +7,42 @@ objects = { /* Begin PBXBuildFile section */ - 27E5E98D22F3D5B2005EA1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E5E98C22F3D5B2005EA1D9 /* AppDelegate.swift */; }; - 27E5E98F22F3D5B2005EA1D9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E5E98E22F3D5B2005EA1D9 /* ViewController.swift */; }; - 27E5E99122F3D5B4005EA1D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 27E5E99022F3D5B4005EA1D9 /* Assets.xcassets */; }; - 27E5E99422F3D5B4005EA1D9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 27E5E99222F3D5B4005EA1D9 /* Main.storyboard */; }; - 27E5E9A322F3D5FE005EA1D9 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27E5E9A222F3D5FE005EA1D9 /* Cocoa.framework */; }; - 27E5E9A622F3D5FE005EA1D9 /* SafariExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E5E9A522F3D5FE005EA1D9 /* SafariExtensionHandler.swift */; }; - 27E5E9A822F3D5FE005EA1D9 /* SafariExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E5E9A722F3D5FE005EA1D9 /* SafariExtensionViewController.swift */; }; - 27E5E9AB22F3D5FE005EA1D9 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 27E5E9A922F3D5FE005EA1D9 /* SafariExtensionViewController.xib */; }; - 27E5E9B022F3D5FE005EA1D9 /* ToolbarItemIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 27E5E9AF22F3D5FE005EA1D9 /* ToolbarItemIcon.pdf */; }; - 27E5E9B422F3D5FE005EA1D9 /* safari.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 27E5E9A022F3D5FE005EA1D9 /* safari.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 27E5E9BC22F4B9D5005EA1D9 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 27E5E9BB22F4B9D5005EA1D9 /* app */; }; + 55E0374D2577FA6B00979016 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E0374C2577FA6B00979016 /* AppDelegate.swift */; }; + 55E037502577FA6B00979016 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55E0374E2577FA6B00979016 /* Main.storyboard */; }; + 55E037522577FA6B00979016 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E037512577FA6B00979016 /* ViewController.swift */; }; + 55E037542577FA6E00979016 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 55E037532577FA6E00979016 /* Assets.xcassets */; }; + 55E0375B2577FA6F00979016 /* safari.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 55E0375A2577FA6F00979016 /* safari.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 55E037602577FA6F00979016 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E0375F2577FA6F00979016 /* Cocoa.framework */; }; + 55E037632577FA6F00979016 /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E037622577FA6F00979016 /* SafariWebExtensionHandler.swift */; }; + 55E037792577FA6F00979016 /* popup in Resources */ = {isa = PBXBuildFile; fileRef = 55E037702577FA6F00979016 /* popup */; }; + 55E0377A2577FA6F00979016 /* background.js in Resources */ = {isa = PBXBuildFile; fileRef = 55E037712577FA6F00979016 /* background.js */; }; + 55E0377B2577FA6F00979016 /* images in Resources */ = {isa = PBXBuildFile; fileRef = 55E037722577FA6F00979016 /* images */; }; + 55E0377C2577FA6F00979016 /* notification in Resources */ = {isa = PBXBuildFile; fileRef = 55E037732577FA6F00979016 /* notification */; }; + 55E0377D2577FA6F00979016 /* content in Resources */ = {isa = PBXBuildFile; fileRef = 55E037742577FA6F00979016 /* content */; }; + 55E0377E2577FA6F00979016 /* vendor.js in Resources */ = {isa = PBXBuildFile; fileRef = 55E037752577FA6F00979016 /* vendor.js */; }; + 55E0377F2577FA6F00979016 /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 55E037762577FA6F00979016 /* manifest.json */; }; + 55E037802577FA6F00979016 /* background.html in Resources */ = {isa = PBXBuildFile; fileRef = 55E037772577FA6F00979016 /* background.html */; }; + 55E037812577FA6F00979016 /* _locales in Resources */ = {isa = PBXBuildFile; fileRef = 55E037782577FA6F00979016 /* _locales */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 27E5E9B222F3D5FE005EA1D9 /* PBXContainerItemProxy */ = { + 55E0375C2577FA6F00979016 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 27E5E98122F3D5B2005EA1D9 /* Project object */; + containerPortal = 55E037402577FA6B00979016 /* Project object */; proxyType = 1; - remoteGlobalIDString = 27E5E99F22F3D5FD005EA1D9; - remoteInfo = safari; + remoteGlobalIDString = 55E037592577FA6F00979016; + remoteInfo = "safari"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 27E5E9B822F3D5FE005EA1D9 /* Embed App Extensions */ = { + 55E0376B2577FA6F00979016 /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( - 27E5E9B422F3D5FE005EA1D9 /* safari.appex in Embed App Extensions */, + 55E0375B2577FA6F00979016 /* safari.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -45,160 +50,173 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 27E5E98922F3D5B2005EA1D9 /* desktop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktop.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 27E5E98C22F3D5B2005EA1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 27E5E98E22F3D5B2005EA1D9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 27E5E99022F3D5B4005EA1D9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 27E5E99322F3D5B4005EA1D9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 27E5E99522F3D5B4005EA1D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 27E5E99622F3D5B4005EA1D9 /* desktop.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = desktop.entitlements; sourceTree = ""; }; - 27E5E9A022F3D5FE005EA1D9 /* safari.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = safari.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 27E5E9A222F3D5FE005EA1D9 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; - 27E5E9A522F3D5FE005EA1D9 /* SafariExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionHandler.swift; sourceTree = ""; }; - 27E5E9A722F3D5FE005EA1D9 /* SafariExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionViewController.swift; sourceTree = ""; }; - 27E5E9AA22F3D5FE005EA1D9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SafariExtensionViewController.xib; sourceTree = ""; }; - 27E5E9AC22F3D5FE005EA1D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 27E5E9AF22F3D5FE005EA1D9 /* ToolbarItemIcon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ToolbarItemIcon.pdf; sourceTree = ""; }; - 27E5E9B122F3D5FE005EA1D9 /* safari.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = safari.entitlements; sourceTree = ""; }; - 27E5E9BB22F4B9D5005EA1D9 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; + 55E037482577FA6B00979016 /* desktop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktop.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 55E0374B2577FA6B00979016 /* desktop.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = desktop.entitlements; sourceTree = ""; }; + 55E0374C2577FA6B00979016 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 55E0374F2577FA6B00979016 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 55E037512577FA6B00979016 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 55E037532577FA6E00979016 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 55E037552577FA6E00979016 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 55E0375A2577FA6F00979016 /* safari.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = safari.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 55E0375F2577FA6F00979016 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 55E037622577FA6F00979016 /* SafariWebExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebExtensionHandler.swift; sourceTree = ""; }; + 55E037642577FA6F00979016 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 55E037652577FA6F00979016 /* safari.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = safari.entitlements; sourceTree = ""; }; + 55E037702577FA6F00979016 /* popup */ = {isa = PBXFileReference; lastKnownFileType = folder; name = popup; path = ../../../build/popup; sourceTree = ""; }; + 55E037712577FA6F00979016 /* background.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = background.js; path = ../../../build/background.js; sourceTree = ""; }; + 55E037722577FA6F00979016 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ../../../build/images; sourceTree = ""; }; + 55E037732577FA6F00979016 /* notification */ = {isa = PBXFileReference; lastKnownFileType = folder; name = notification; path = ../../../build/notification; sourceTree = ""; }; + 55E037742577FA6F00979016 /* content */ = {isa = PBXFileReference; lastKnownFileType = folder; name = content; path = ../../../build/content; sourceTree = ""; }; + 55E037752577FA6F00979016 /* vendor.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = vendor.js; path = ../../../build/vendor.js; sourceTree = ""; }; + 55E037762577FA6F00979016 /* manifest.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = manifest.json; path = ../../../build/manifest.json; sourceTree = ""; }; + 55E037772577FA6F00979016 /* background.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = background.html; path = ../../../build/background.html; sourceTree = ""; }; + 55E037782577FA6F00979016 /* _locales */ = {isa = PBXFileReference; lastKnownFileType = folder; name = _locales; path = ../../../build/_locales; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 27E5E98622F3D5B2005EA1D9 /* Frameworks */ = { + 55E037452577FA6B00979016 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 27E5E99D22F3D5FD005EA1D9 /* Frameworks */ = { + 55E037572577FA6F00979016 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 27E5E9A322F3D5FE005EA1D9 /* Cocoa.framework in Frameworks */, + 55E037602577FA6F00979016 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 27E5E98022F3D5B2005EA1D9 = { + 55E0373F2577FA6B00979016 = { isa = PBXGroup; children = ( - 27E5E98B22F3D5B2005EA1D9 /* desktop */, - 27E5E9A422F3D5FE005EA1D9 /* safari */, - 27E5E9A122F3D5FE005EA1D9 /* Frameworks */, - 27E5E98A22F3D5B2005EA1D9 /* Products */, + 55E0374A2577FA6B00979016 /* desktop */, + 55E037612577FA6F00979016 /* safari */, + 55E0375E2577FA6F00979016 /* Frameworks */, + 55E037492577FA6B00979016 /* Products */, ); sourceTree = ""; }; - 27E5E98A22F3D5B2005EA1D9 /* Products */ = { + 55E037492577FA6B00979016 /* Products */ = { isa = PBXGroup; children = ( - 27E5E98922F3D5B2005EA1D9 /* desktop.app */, - 27E5E9A022F3D5FE005EA1D9 /* safari.appex */, + 55E037482577FA6B00979016 /* desktop.app */, + 55E0375A2577FA6F00979016 /* safari.appex */, ); name = Products; sourceTree = ""; }; - 27E5E98B22F3D5B2005EA1D9 /* desktop */ = { + 55E0374A2577FA6B00979016 /* desktop */ = { isa = PBXGroup; children = ( - 27E5E98C22F3D5B2005EA1D9 /* AppDelegate.swift */, - 27E5E98E22F3D5B2005EA1D9 /* ViewController.swift */, - 27E5E99022F3D5B4005EA1D9 /* Assets.xcassets */, - 27E5E99222F3D5B4005EA1D9 /* Main.storyboard */, - 27E5E99522F3D5B4005EA1D9 /* Info.plist */, - 27E5E99622F3D5B4005EA1D9 /* desktop.entitlements */, + 55E0374B2577FA6B00979016 /* desktop.entitlements */, + 55E0374C2577FA6B00979016 /* AppDelegate.swift */, + 55E0374E2577FA6B00979016 /* Main.storyboard */, + 55E037512577FA6B00979016 /* ViewController.swift */, + 55E037532577FA6E00979016 /* Assets.xcassets */, + 55E037552577FA6E00979016 /* Info.plist */, ); path = desktop; sourceTree = ""; }; - 27E5E9A122F3D5FE005EA1D9 /* Frameworks */ = { + 55E0375E2577FA6F00979016 /* Frameworks */ = { isa = PBXGroup; children = ( - 27E5E9A222F3D5FE005EA1D9 /* Cocoa.framework */, + 55E0375F2577FA6F00979016 /* Cocoa.framework */, ); name = Frameworks; sourceTree = ""; }; - 27E5E9A422F3D5FE005EA1D9 /* safari */ = { + 55E037612577FA6F00979016 /* safari */ = { isa = PBXGroup; children = ( - 27E5E9BB22F4B9D5005EA1D9 /* app */, - 27E5E9A522F3D5FE005EA1D9 /* SafariExtensionHandler.swift */, - 27E5E9A722F3D5FE005EA1D9 /* SafariExtensionViewController.swift */, - 27E5E9A922F3D5FE005EA1D9 /* SafariExtensionViewController.xib */, - 27E5E9AC22F3D5FE005EA1D9 /* Info.plist */, - 27E5E9AF22F3D5FE005EA1D9 /* ToolbarItemIcon.pdf */, - 27E5E9B122F3D5FE005EA1D9 /* safari.entitlements */, + 55E0376F2577FA6F00979016 /* Resources */, + 55E037622577FA6F00979016 /* SafariWebExtensionHandler.swift */, + 55E037642577FA6F00979016 /* Info.plist */, + 55E037652577FA6F00979016 /* safari.entitlements */, ); path = safari; sourceTree = ""; }; + 55E0376F2577FA6F00979016 /* Resources */ = { + isa = PBXGroup; + children = ( + 55E037702577FA6F00979016 /* popup */, + 55E037712577FA6F00979016 /* background.js */, + 55E037722577FA6F00979016 /* images */, + 55E037732577FA6F00979016 /* notification */, + 55E037742577FA6F00979016 /* content */, + 55E037752577FA6F00979016 /* vendor.js */, + 55E037762577FA6F00979016 /* manifest.json */, + 55E037772577FA6F00979016 /* background.html */, + 55E037782577FA6F00979016 /* _locales */, + ); + name = Resources; + path = "safari"; + sourceTree = SOURCE_ROOT; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 27E5E98822F3D5B2005EA1D9 /* desktop */ = { + 55E037472577FA6B00979016 /* desktop */ = { isa = PBXNativeTarget; - buildConfigurationList = 27E5E99922F3D5B4005EA1D9 /* Build configuration list for PBXNativeTarget "desktop" */; + buildConfigurationList = 55E0376C2577FA6F00979016 /* Build configuration list for PBXNativeTarget "desktop" */; buildPhases = ( - 27E5E98522F3D5B2005EA1D9 /* Sources */, - 27E5E98622F3D5B2005EA1D9 /* Frameworks */, - 27E5E98722F3D5B2005EA1D9 /* Resources */, - 27E5E9B822F3D5FE005EA1D9 /* Embed App Extensions */, + 55E037442577FA6B00979016 /* Sources */, + 55E037452577FA6B00979016 /* Frameworks */, + 55E037462577FA6B00979016 /* Resources */, + 55E0376B2577FA6F00979016 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( - 27E5E9B322F3D5FE005EA1D9 /* PBXTargetDependency */, + 55E0375D2577FA6F00979016 /* PBXTargetDependency */, ); name = desktop; productName = desktop; - productReference = 27E5E98922F3D5B2005EA1D9 /* desktop.app */; + productReference = 55E037482577FA6B00979016 /* desktop.app */; productType = "com.apple.product-type.application"; }; - 27E5E99F22F3D5FD005EA1D9 /* safari */ = { + 55E037592577FA6F00979016 /* safari */ = { isa = PBXNativeTarget; - buildConfigurationList = 27E5E9B522F3D5FE005EA1D9 /* Build configuration list for PBXNativeTarget "safari" */; + buildConfigurationList = 55E037682577FA6F00979016 /* Build configuration list for PBXNativeTarget "safari" */; buildPhases = ( - 27E5E99C22F3D5FD005EA1D9 /* Sources */, - 27E5E99D22F3D5FD005EA1D9 /* Frameworks */, - 27E5E99E22F3D5FD005EA1D9 /* Resources */, + 55E037562577FA6F00979016 /* Sources */, + 55E037572577FA6F00979016 /* Frameworks */, + 55E037582577FA6F00979016 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = safari; - productName = safari; - productReference = 27E5E9A022F3D5FE005EA1D9 /* safari.appex */; + productName = "safari"; + productReference = 55E0375A2577FA6F00979016 /* safari.appex */; productType = "com.apple.product-type.app-extension"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 27E5E98122F3D5B2005EA1D9 /* Project object */ = { + 55E037402577FA6B00979016 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1030; - LastUpgradeCheck = 1030; - ORGANIZATIONNAME = "8bit Solutions LLC"; + LastSwiftUpdateCheck = 1220; + LastUpgradeCheck = 1220; TargetAttributes = { - 27E5E98822F3D5B2005EA1D9 = { - CreatedOnToolsVersion = 10.3; + 55E037472577FA6B00979016 = { + CreatedOnToolsVersion = 12.2; }; - 27E5E99F22F3D5FD005EA1D9 = { - CreatedOnToolsVersion = 10.3; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; + 55E037592577FA6F00979016 = { + CreatedOnToolsVersion = 12.2; }; }; }; - buildConfigurationList = 27E5E98422F3D5B2005EA1D9 /* Build configuration list for PBXProject "desktop" */; + buildConfigurationList = 55E037432577FA6B00979016 /* Build configuration list for PBXProject "desktop" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; @@ -206,92 +224,88 @@ en, Base, ); - mainGroup = 27E5E98022F3D5B2005EA1D9; - productRefGroup = 27E5E98A22F3D5B2005EA1D9 /* Products */; + mainGroup = 55E0373F2577FA6B00979016; + productRefGroup = 55E037492577FA6B00979016 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 27E5E98822F3D5B2005EA1D9 /* desktop */, - 27E5E99F22F3D5FD005EA1D9 /* safari */, + 55E037472577FA6B00979016 /* desktop */, + 55E037592577FA6F00979016 /* safari */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 27E5E98722F3D5B2005EA1D9 /* Resources */ = { + 55E037462577FA6B00979016 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 27E5E99122F3D5B4005EA1D9 /* Assets.xcassets in Resources */, - 27E5E99422F3D5B4005EA1D9 /* Main.storyboard in Resources */, + 55E037542577FA6E00979016 /* Assets.xcassets in Resources */, + 55E037502577FA6B00979016 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 27E5E99E22F3D5FD005EA1D9 /* Resources */ = { + 55E037582577FA6F00979016 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 27E5E9BC22F4B9D5005EA1D9 /* app in Resources */, - 27E5E9B022F3D5FE005EA1D9 /* ToolbarItemIcon.pdf in Resources */, - 27E5E9AB22F3D5FE005EA1D9 /* SafariExtensionViewController.xib in Resources */, + 55E037812577FA6F00979016 /* _locales in Resources */, + 55E0377B2577FA6F00979016 /* images in Resources */, + 55E0377F2577FA6F00979016 /* manifest.json in Resources */, + 55E037802577FA6F00979016 /* background.html in Resources */, + 55E0377A2577FA6F00979016 /* background.js in Resources */, + 55E037792577FA6F00979016 /* popup in Resources */, + 55E0377C2577FA6F00979016 /* notification in Resources */, + 55E0377E2577FA6F00979016 /* vendor.js in Resources */, + 55E0377D2577FA6F00979016 /* content in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 27E5E98522F3D5B2005EA1D9 /* Sources */ = { + 55E037442577FA6B00979016 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 27E5E98F22F3D5B2005EA1D9 /* ViewController.swift in Sources */, - 27E5E98D22F3D5B2005EA1D9 /* AppDelegate.swift in Sources */, + 55E037522577FA6B00979016 /* ViewController.swift in Sources */, + 55E0374D2577FA6B00979016 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 27E5E99C22F3D5FD005EA1D9 /* Sources */ = { + 55E037562577FA6F00979016 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 27E5E9A822F3D5FE005EA1D9 /* SafariExtensionViewController.swift in Sources */, - 27E5E9A622F3D5FE005EA1D9 /* SafariExtensionHandler.swift in Sources */, + 55E037632577FA6F00979016 /* SafariWebExtensionHandler.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 27E5E9B322F3D5FE005EA1D9 /* PBXTargetDependency */ = { + 55E0375D2577FA6F00979016 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 27E5E99F22F3D5FD005EA1D9 /* safari */; - targetProxy = 27E5E9B222F3D5FE005EA1D9 /* PBXContainerItemProxy */; + target = 55E037592577FA6F00979016 /* safari */; + targetProxy = 55E0375C2577FA6F00979016 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - 27E5E99222F3D5B4005EA1D9 /* Main.storyboard */ = { + 55E0374E2577FA6B00979016 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( - 27E5E99322F3D5B4005EA1D9 /* Base */, + 55E0374F2577FA6B00979016 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; - 27E5E9A922F3D5FE005EA1D9 /* SafariExtensionViewController.xib */ = { - isa = PBXVariantGroup; - children = ( - 27E5E9AA22F3D5FE005EA1D9 /* Base */, - ); - name = SafariExtensionViewController.xib; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 27E5E99722F3D5B4005EA1D9 /* Debug */ = { + 55E037662577FA6F00979016 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -315,13 +329,13 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -340,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -350,10 +364,9 @@ }; name = Debug; }; - 27E5E99822F3D5B4005EA1D9 /* Release */ = { + 55E037672577FA6F00979016 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -377,13 +390,13 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -396,7 +409,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; @@ -405,90 +418,82 @@ }; name = Release; }; - 27E5E99A22F3D5B4005EA1D9 /* Debug */ = { + 55E037692577FA6F00979016 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_ENTITLEMENTS = desktop/desktop.entitlements; + CODE_SIGN_ENTITLEMENTS = "safari/safari.entitlements"; CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = LTZ2PFU5D6; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = desktop/Info.plist; + INFOPLIST_FILE = "safari/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", + "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop.safari; PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; }; name = Debug; }; - 27E5E99B22F3D5B4005EA1D9 /* Release */ = { + 55E0376A2577FA6F00979016 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_ENTITLEMENTS = desktop/desktop.entitlements; + CODE_SIGN_ENTITLEMENTS = "safari/safari.entitlements"; CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = LTZ2PFU5D6; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = desktop/Info.plist; + INFOPLIST_FILE = "safari/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", + "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop.safari; PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; }; name = Release; }; - 27E5E9B622F3D5FE005EA1D9 /* Debug */ = { + 55E0376D2577FA6F00979016 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_ENTITLEMENTS = safari/safari.entitlements; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = desktop/desktop.entitlements; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LTZ2PFU5D6; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = safari/Info.plist; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = desktop/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop.safari; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop; PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; }; name = Debug; }; - 27E5E9B722F3D5FE005EA1D9 /* Release */ = { + 55E0376E2577FA6F00979016 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_ENTITLEMENTS = safari/safari.entitlements; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = desktop/desktop.entitlements; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LTZ2PFU5D6; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = safari/Info.plist; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = desktop/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop.safari; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = com.bitwarden.desktop; PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; }; name = Release; @@ -496,34 +501,34 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 27E5E98422F3D5B2005EA1D9 /* Build configuration list for PBXProject "desktop" */ = { + 55E037432577FA6B00979016 /* Build configuration list for PBXProject "desktop" */ = { isa = XCConfigurationList; buildConfigurations = ( - 27E5E99722F3D5B4005EA1D9 /* Debug */, - 27E5E99822F3D5B4005EA1D9 /* Release */, + 55E037662577FA6F00979016 /* Debug */, + 55E037672577FA6F00979016 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 27E5E99922F3D5B4005EA1D9 /* Build configuration list for PBXNativeTarget "desktop" */ = { + 55E037682577FA6F00979016 /* Build configuration list for PBXNativeTarget "safari" */ = { isa = XCConfigurationList; buildConfigurations = ( - 27E5E99A22F3D5B4005EA1D9 /* Debug */, - 27E5E99B22F3D5B4005EA1D9 /* Release */, + 55E037692577FA6F00979016 /* Debug */, + 55E0376A2577FA6F00979016 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 27E5E9B522F3D5FE005EA1D9 /* Build configuration list for PBXNativeTarget "safari" */ = { + 55E0376C2577FA6F00979016 /* Build configuration list for PBXNativeTarget "desktop" */ = { isa = XCConfigurationList; buildConfigurations = ( - 27E5E9B622F3D5FE005EA1D9 /* Debug */, - 27E5E9B722F3D5FE005EA1D9 /* Release */, + 55E0376D2577FA6F00979016 /* Debug */, + 55E0376E2577FA6F00979016 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = 27E5E98122F3D5B2005EA1D9 /* Project object */; + rootObject = 55E037402577FA6B00979016 /* Project object */; } diff --git a/src/safari/desktop.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/safari/desktop.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 699699887b..919434a625 100644 --- a/src/safari/desktop.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/src/safari/desktop.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/src/safari/desktop.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme b/src/safari/desktop.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme new file mode 100644 index 0000000000..7dd13c8dc8 --- /dev/null +++ b/src/safari/desktop.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/safari/desktop/AppDelegate.swift b/src/safari/desktop/AppDelegate.swift index 9ef5111723..6222982a6e 100644 --- a/src/safari/desktop/AppDelegate.swift +++ b/src/safari/desktop/AppDelegate.swift @@ -1,12 +1,18 @@ import Cocoa -@NSApplicationMain +@main class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_: Notification) { + + func applicationDidFinishLaunching(_ notification: Notification) { // Insert code here to initialize your application } - func applicationWillTerminate(_: Notification) { + func applicationWillTerminate(_ notification: Notification) { // Insert code here to tear down your application } + + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + } diff --git a/src/safari/desktop/Assets.xcassets/AccentColor.colorset/Contents.json b/src/safari/desktop/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..eb87897008 --- /dev/null +++ b/src/safari/desktop/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/Contents.json b/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/Contents.json index 2db2b1c7c6..6bf9a9b330 100644 --- a/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,54 +1,58 @@ { "images" : [ { - "idiom" : "mac", "size" : "16x16", + "idiom" : "mac", + "filename" : "icon16.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "16x16", + "idiom" : "mac", + "filename" : "icon32.png", "scale" : "2x" }, { - "idiom" : "mac", "size" : "32x32", + "idiom" : "mac", + "filename" : "icon32.png", "scale" : "1x" }, { "idiom" : "mac", - "size" : "32x32", - "scale" : "2x" + "scale" : "2x", + "size" : "32x32" }, { - "idiom" : "mac", "size" : "128x128", + "idiom" : "mac", + "filename" : "icon128.png", "scale" : "1x" }, { "idiom" : "mac", - "size" : "128x128", - "scale" : "2x" + "scale" : "2x", + "size" : "128x128" }, { "idiom" : "mac", - "size" : "256x256", - "scale" : "1x" + "scale" : "1x", + "size" : "256x256" }, { "idiom" : "mac", - "size" : "256x256", - "scale" : "2x" + "scale" : "2x", + "size" : "256x256" }, { "idiom" : "mac", - "size" : "512x512", - "scale" : "1x" + "scale" : "1x", + "size" : "512x512" }, { "idiom" : "mac", - "size" : "512x512", - "scale" : "2x" + "scale" : "2x", + "size" : "512x512" } ], "info" : { diff --git a/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon128.png b/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..914b3e38998ea3c163dff3427ba171193722cc56 GIT binary patch literal 5763 zcmd5=3s_8B-=CrjAt8lm-f_!iH+#?Qx$L3RO+_V2>LBgedrwVjW}2C5RE`Ou6NN-Y z?x&pG>bMrFBPj|Ucj+KPid+iiQtz5>I`7BJ^S$r$J>NXf%-(yg|Mj(W7FpwE;L=Ct?5m*B3jyY%w*8pMU;!J7`7URbRRbl zy1i6R(K(h}h(J&dorhT>TsDeflj$rN;WJ@A6JbLLSAfC-7K`5bVE}J(QY4t;Ful_k zc(P&yDwQ$;lNk{aVHv@;l*$8`2!>%yn8jqVARqxLmPnMi3X&*BcUf?t6ogzXQ;MY$ zy2c_dl!hs-7$DM)6hdU(W+jSFm;hi*6)s~UmarzJc0rQpmXn3agWH{x1d|G;LMVw+ z0c4SGSy`Y|DOChYzhSz2`fm;Zv~F(QKE4`DNJzH}h0-w`q|ph;SEUtROJo#t4yBNW z$qC9a9E3T#D;tG!4)q1kA5aJ4-O5U_=o`A4nZG4Psl?w9)y#B@$^`au3Rg@{*j0)I%2<8c3xI@lO zN{U5GddhJi7Rw7^3pjiM%z1!E}8A^Ba_X352%OW?sht!knofkleTXJZ^3 z1JHB05K0g*gd>;;!i8)EhcOn)M?q8?VhDg45E8B_MJonKwSCY>L(t%`jzJy^1|KG( z`R$zd{m%TC>A4aySPK?|uAx>3{GOo80hocW_Aw8!iVF5|5Cdcs9YL@_j$fvH|78j^ zp)Y`SC2tFsMFFU)=xkATctGb}u-Li_6&9P#2dVQR9HVdu#f21vao8jzV)1x5PI5&g z$(hR3^dq3u(dv3=qkJ~SVT(i%#^wnjlq*CbKFr4;4vq+U2vFwpMc>xu;;@LzW+4!d z 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 DJHyES literal 0 HcmV?d00001 diff --git a/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon16.png b/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..3acbfd1435b4361e54a5520c148db6deb5a4a804 GIT binary patch literal 3245 zcmcgve{9rL9KQ*uFb5-QTn1=Y{*W2pwSCw2u9e$hcY{qlSjJ?(jPkC1-8I~`UE6K9 zB_sxo0fV^2U1A3eyWc zhg4Inj*6HzrUbtT6pk0DpycPo053_8Q;_9C3>#v&0N_NKXJrVf+(i>_wyMeX(M365 z9mUoyS2qpHvxwqm zha09zq}dcwR^F`Pud9Jd}1 z<2ixpL_I&*)wMC^dXig`!fAaBRZo&vwd9D6k!#uwrkNb#PTdfSR3zeYMWW`#kfIwN zwB=qRxmh@hTpXq$3t7NI0Qm-%mjfZd24$Ab>cvb|*SbgQ2>`+dE+BK9ED%+3Se5Wt zMJ^gwSyg1sv{Q(zf}TQcn6ZpDn#!-O95IuoO&pWZ0wc$3Bau4W)bu0~IQ5IGsM_j? z$Vs9Ipr6Zx8;i-chT|eb!L`vaO%nC%x++6~m5>sT1C15qfM+ELB!T3Kg8~<4d0t>~ zXlQ=aR60Cfdh@d-s+tP1F&v(#A^|A@3^Xo|fCzaF5aR*}u|Wk3iWXv35_X1^@-kZ| zT>>RXTJ@q*iA7D|c^H5qkg$XS&m#fEG0y=-6tIRQF$6V{raWSJW=KfLE5$HPQq2rD zc?3;%WCw$gB^Seazd3pHIPzif(}qsg0@9R6tt|Y@NtYuq6JMFJIz5e(O;Md7<79&% zp$$u#Zz#Rq(PR2?Q0Kpc#9;*rD2{{={FxeWJ+sG&+@thV^AP6dK5b!F801`w6#5o*?tQ1hCU~u$| zMrl+3M@GuHTo`BhJ%;4zGB$E5w)GKg7|FPN2vml9cgEki+?!aJ{bzxWx}n_Rm14XD zOt$|pe{W6M?`RgzMP9LH_IP=;CNxntl1*PG`^Gps3E6hUAmk)~I9>rz@j-?d~LAQ^~+E;#@a0L=KDIYyOlWY>*1J$tia8sePaMd<9jt z(S;46S1!EPzvQm=l8-CDn{$5k!SxI7Tl3aSeFw_lKC_~%xMfahsl8S_e&hKTntgBJ zgRRHP*hKBx^LF`;fiJdIxA(HAo_nXL!)`kv{gFIi(-o_N=U*;X8ix?yd0Q z(go9N%8!=!mVA|{nj#i77yP6hOdnZ$wPu5{{cEfro?P7MJ9Mr@J5@RB=Pm0iUag+I zZ!f#EwY_Ke)tf<+;ivDuKjqtgarVh|!j@QDVd#^y zmo}7q7%ba%`6LXKJ8$eRnlbNE?8}yG%WBfcKDu^e_L<%%TXy}vBX~XB$nHF}H#zrc zPqg>kRCsygyw{)j{GJCYHihdSeb4}{QxD+pPyG5=@%|M{Hco%2tK-jS*In9wVj!}4 z_jbsbmyc~3c&7hi-;WpjF1+ya%*3R|1;5OiWVf6x{H^sx?+-<7_0s5`s+DX00$4sg A6951J literal 0 HcmV?d00001 diff --git a/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon32.png b/src/safari/desktop/Assets.xcassets/AppIcon.appiconset/icon32.png new file mode 100644 index 0000000000000000000000000000000000000000..35008195d94578abd47e2326e089cddcd75f6399 GIT binary patch literal 3653 zcmc&%4R91!9lxY$1NopTO+<&*oefx8+1u~E-7VLU+y!!#3u!o-WCYB9yiK;byFGU| zxy!}jqbM~4j8wGHkXjsCN|UxghfzL~%w*53f1i1VFs2mG~?$s6!7GgjMdxm?Aw zoo}A2w{H#{d`}NG*-gP^qGCkcl4{7noruJsw9B=qG7*!Mc3>kiXw{=$^xCnLD59%g zw2=)G!I%%U>9t7{tVq^{lw`XisA%Pb$fAS@6-0n7A&E#hYKaLin$j!6u``XMNJ_+R z_o7vfL8K{IkN6A|Agr6i6p~>PkKiUbnh}JB2t|-QPVhKMV+6C@pwG$j?-?#Y{f}I5O9LRDGGxUnAH`vr34nWDzX~2`MG0Dml4WhZ#;8R}~zDK?Fo?3(Ar? z*;t!l8&;b!iR#?)Ee0U8!C=nDguX-~ITMy$(+R`K05YMp73zusya8B7hpB*?PUvPu zHX6%r0AqMgV>=YjZMJo7lD*E#EkS{VK8dKak`s-IJ`+f`VTKGNoaIh^7DdG8bGRZP z3xblOM;&PIa{|fi0)EK`UX&sz0wXAlVnPHX(mY0ZM1n}m1r1f#y2i_~7)6ChT4X#T z&0+*MCJK41O13nmu&RifVMZjF1wA6Q0z4LNMUh-?MV}EiOlTOoMvtEl_;(jo&D(&RSgB|7;_J5@+h)QV6YT9jHek7CIOnpSdWL}Jv2=c zfX{lf+|*&0kiz4&I$o(zBg4~xr8Nx`Xpf9BoXlW6!3!8Gk+O&6G?nKy6mf_lLN(AO z$tguI3SCW2HaP?hccdph6ahat>HKEao2OVGTV5H}VJ;{Xaj2Dse+BGv5N2pAHCL}E zK)A`TLu9O!6LeNOwbFd+3T#1RaAiZUf=RI;QLRjas+L+aqp<#9788wS7>cGiOaKDF z7)FAb0t|^M91Ao_;CM>o7U51ef}4=^D6I(p_ZFc6NRC?F@t`YXcLn%L&I2UX7DP!& z!>Kq$1ynRMtxrIz>7_5NHr@tc;wG!Jw2WnFc3d(+RVz&O^eP#j)O{n`3CwW;#3WOK z=L|3{FRGbF1j!X1VjGAQi-mP1MK8QFs%Cn54p4MbJC@Wr1xOq$4Ve)zUJzix5E_rk zum~|mk!cUjvYMbtS&+wr#9|~JB6*Q!M1syXng1B4Ocs?kDcTC)QH_6$hzQC<$`T=C zEFf6OP))&jjU+Htpecq045taOY5i;3Cv2Pli-?pdxiCfg9fsuSGC6W8F!gb47|*zz z3rLE4{~dc%a&LNF|NoPzR0~V@AN-$NQ~KSXhI5Hitf@PPI8hUtE*s$`M1<$q6ek<3 zRElFI*myaH#{`1mFo}Z2>rn`x09jKwNpZOCXfrubb6b$3;nb|3Ld3A=lTQhvg z-Mc{)R47t z=Q@DLzp);BW0QI6!y9is+pzg$Vf?SzGA#om&gTf_^;)x6B69d}Yy(O-Kioo8lwvWty^PML@vvbvbMQh)GrRU;~ zW!=vde!daT4d3g!{PdSwUz>fth;^SbqK8n~OIN zZfq}4;&c7K1g8#_`1Ut;UENyVzx2S-{3Gl7=33u>Zo|P{OWT-%?$KHI{=WO**`ZUL z_SSuJhF-RF^_#m-tn2A{YR(HU4nK5%>1>*Odt?XmqYc}Bo;bR{jDMlybX~)5w^wd> z- - + + - + + @@ -21,13 +22,6 @@ - - - - - - - @@ -54,607 +48,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -677,15 +70,15 @@ - + - - - - + + + + @@ -705,13 +98,70 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/safari/desktop/Info.plist b/src/safari/desktop/Info.plist index 3c9b847a73..201669d037 100644 --- a/src/safari/desktop/Info.plist +++ b/src/safari/desktop/Info.plist @@ -11,13 +11,13 @@ CFBundleIconFile CFBundleIdentifier - com.bitwarden.desktop + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - Bitwarden + $(PRODUCT_NAME) CFBundlePackageType - APPL + $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion diff --git a/src/safari/desktop/ViewController.swift b/src/safari/desktop/ViewController.swift index 5af73ffee0..fbda5decf8 100644 --- a/src/safari/desktop/ViewController.swift +++ b/src/safari/desktop/ViewController.swift @@ -1,14 +1,44 @@ import Cocoa +import SafariServices.SFSafariApplication +import SafariServices.SFSafariExtensionManager + +let appName = "desktop" +let extensionBundleIdentifier = "com.bitwarden.desktop.Extension" class ViewController: NSViewController { + + @IBOutlet var appNameLabel: NSTextField! + override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. - } + self.appNameLabel.stringValue = appName + 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 + } - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. + DispatchQueue.main.async { + if (state.isEnabled) { + self.appNameLabel.stringValue = "\(appName)'s extension is currently on." + } else { + self.appNameLabel.stringValue = "\(appName)'s extension is currently off. You can turn it on in Safari Extensions preferences." + } + } } } + + @IBAction func openSafariExtensionPreferences(_ sender: AnyObject?) { + SFSafariApplication.showPreferencesForExtension(withIdentifier: extensionBundleIdentifier) { error in + guard error == nil else { + // Insert code to inform the user that something went wrong. + return + } + + DispatchQueue.main.async { + NSApplication.shared.terminate(nil) + } + } + } + } diff --git a/src/safari/safari/Base.lproj/SafariExtensionViewController.xib b/src/safari/safari/Base.lproj/SafariExtensionViewController.xib deleted file mode 100644 index 3716df7567..0000000000 --- a/src/safari/safari/Base.lproj/SafariExtensionViewController.xib +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/safari/safari/Info.plist b/src/safari/safari/Info.plist index 82f785e9c6..eaec1ff8e0 100644 --- a/src/safari/safari/Info.plist +++ b/src/safari/safari/Info.plist @@ -9,75 +9,25 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.bitwarden.desktop.safari + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - Bitwarden + $(PRODUCT_NAME) CFBundlePackageType - XPC! + $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 0.0.1 + 1.0 CFBundleVersion - 0.0.2 + 1 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSExtension NSExtensionPointIdentifier - com.apple.Safari.extension + com.apple.Safari.web-extension NSExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).SafariExtensionHandler - SFSafariStyleSheet - - - Style Sheet - app/content/autofill.css - - - SFSafariContentScript - - - Script - app/content/autofill.js - - - Script - app/content/autofiller.js - - - Script - app/content/notificationBar.js - - - Script - app/content/shortcuts.js - - - Script - app/content/sso.js - - - SFSafariToolbarItem - - Action - Popover - Identifier - Button - Image - ToolbarItemIcon.pdf - Label - Bitwarden - - SFSafariWebsiteAccess - - Level - All - - SFSafariExtensionBundleIdentifiersToUninstall - - com.bitwarden.safari - + $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler NSHumanReadableCopyright Copyright © 2020 Bitwarden Inc. All rights reserved. diff --git a/src/safari/safari/SafariExtensionHandler.swift b/src/safari/safari/SafariExtensionHandler.swift deleted file mode 100644 index 3d8711f401..0000000000 --- a/src/safari/safari/SafariExtensionHandler.swift +++ /dev/null @@ -1,101 +0,0 @@ -import SafariServices - -class SafariExtensionHandler: SFSafariExtensionHandler { - override init() { - super.init() - SafariExtensionViewController.shared.initWebView() - } - - override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String: Any]?) { - // This method will be called when a content script provided by your extension - // calls safari.extension.dispatchMessage("message"). - if messageName == "bitwarden" { - page.getPropertiesWithCompletionHandler { properties in - DispatchQueue.main.async { - makeSenderTabObject(page: page, props: properties, complete: { senderTab in - DispatchQueue.main.async { - self.sendMessage(msg: userInfo, sender: senderTab) - } - }) - } - } - } - } - - override func toolbarItemClicked(in _: SFSafariWindow) { - // This method will be called when your toolbar item is clicked. - } - - override func validateToolbarItem(in _: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) { - // This is called when Safari's state changed in some way that would require the extension's - // toolbar item to be validated again. - validationHandler(true, "") - } - - override func popoverViewController() -> SFSafariExtensionViewController { - return SafariExtensionViewController.shared - } - - override func popoverWillShow(in _: SFSafariWindow) { - SafariExtensionViewController.shared.popoverOpenCount += 1 - DispatchQueue.main.async { - self.sendMessage(msg: ["command": "reloadPopup"], sender: nil) - } - } - - override func popoverDidClose(in _: SFSafariWindow) { - SafariExtensionViewController.shared.popoverOpenCount -= 1 - } - - func sendMessage(msg: [String: Any]?, sender: Tab? = nil) { - if SafariExtensionViewController.shared.webView == nil { - return - } - let newMsg = AppMessage() - newMsg.command = "app_message" - newMsg.senderTab = sender - do { - let jsonData = try JSONSerialization.data(withJSONObject: msg as Any, options: []) - newMsg.data = String(data: jsonData, encoding: .utf8) - } catch let error { - print("error converting to json: \(error)") - } - SafariExtensionViewController.shared.replyMessage(message: newMsg) - } -} - -func makeSenderTabObject(page: SFSafariPage, props: SFSafariPageProperties?, complete: @escaping (Tab) -> Void) { - let t = Tab() - t.title = props?.title - t.url = props?.url?.absoluteString - page.getContainingTab { tab in - tab.getContainingWindow(completionHandler: { win in - guard let window = win else { - t.active = false; - t.windowId = -100 - SFSafariApplication.getAllWindows(completionHandler: { allWins in - if (allWins.count == 0) { - return - } - allWins[0].getAllTabs { allWinTabs in - t.index = allWinTabs.firstIndex(of: tab) ?? -1 - t.id = "\(t.windowId)_\(t.index)" - complete(t) - } - }) - return - } - window.getActiveTab(completionHandler: { activeTab in - t.active = activeTab != nil && tab == activeTab - SFSafariApplication.getAllWindows(completionHandler: { allWins in - t.windowId = allWins.firstIndex(of: window) ?? -100 - window.getAllTabs { allWinTabs in - t.index = allWinTabs.firstIndex(of: tab) ?? -1 - t.id = "\(t.windowId)_\(t.index)" - complete(t) - } - }) - }) - }) - } -} diff --git a/src/safari/safari/SafariExtensionViewController.swift b/src/safari/safari/SafariExtensionViewController.swift deleted file mode 100644 index 0f2ac13eaa..0000000000 --- a/src/safari/safari/SafariExtensionViewController.swift +++ /dev/null @@ -1,422 +0,0 @@ -import SafariServices -import WebKit - -class SafariExtensionViewController: SFSafariExtensionViewController, WKScriptMessageHandler, WKNavigationDelegate { - var webView: WKWebView! - var initedWebView: Bool = false - var popoverOpenCount: Int = 0 - - static let shared: SafariExtensionViewController = { - let shared = SafariExtensionViewController() - shared.preferredContentSize = NSSize(width: 375, height: 600) - return shared - }() - - func initWebView() { - if initedWebView { - return - } - let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String - initedWebView = true - let parentHeight = SafariExtensionViewController.shared.preferredContentSize.height - let parentWidth = SafariExtensionViewController.shared.preferredContentSize.width - let webViewConfig = WKWebViewConfiguration() - let bundleURL = Bundle.main.resourceURL!.absoluteURL - let html = bundleURL.appendingPathComponent("app/popup/index.html") - let url = URL(string: "\(html.absoluteString)?appVersion=\(version!)") - webViewConfig.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs") - webViewConfig.preferences.setValue(true, forKey: "developerExtrasEnabled") - webViewConfig.userContentController.add(self, name: "bitwardenApp") - webView = WKWebView(frame: CGRect(x: 0, y: 0, width: parentWidth, height: parentHeight), - configuration: webViewConfig) - webView.navigationDelegate = self - webView.allowsLinkPreview = false - webView.loadFileURL(url!, allowingReadAccessTo: bundleURL) - webView.alphaValue = 0.0 - webView.uiDelegate = self - view.addSubview(webView) - } - - func webView(_ webView: WKWebView, didFinish _: WKNavigation!) { - if #available(OSXApplicationExtension 10.12, *) { - NSAnimationContext.runAnimationGroup({ _ in - NSAnimationContext.current.duration = 0.35 - webView.animator().alphaValue = 1.0 - }) - } else { - // Fallback on earlier versions - } - } - - override func viewDidLoad() { - super.viewDidLoad() - let backgroundColor = NSColor(red: (39 / 255.0), green: (42 / 255.0), blue: (46 / 255.0), alpha: 1.0) - view.setValue(backgroundColor, forKey: "backgroundColor") - initWebView() - } - - func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { - if message.name != "bitwardenApp" { - return - } - guard let messageBody = message.body as? String else { - return - } - guard let m: AppMessage = jsonDeserialize(json: messageBody) else { - return - } - let command = m.command - NSLog("Command: \(command)") - if command == "storage_get" { - if let data = m.data { - let obj = UserDefaults.standard.string(forKey: data) - m.responseData = obj - replyMessage(message: m) - } - } else if command == "storage_save" { - guard let data: StorageData = jsonDeserialize(json: m.data) else { - return - } - if let obj = data.obj { - UserDefaults.standard.set(obj, forKey: data.key) - } else { - UserDefaults.standard.removeObject(forKey: data.key) - } - replyMessage(message: m) - } else if command == "storage_remove" { - if let data = m.data { - UserDefaults.standard.removeObject(forKey: data) - replyMessage(message: m) - } - } else if command == "getLocaleStrings" { - let language = m.data ?? "en" - guard let bundleUrl = Bundle.main.resourceURL?.absoluteURL else { - return - } - let messagesUrl = bundleUrl.appendingPathComponent("app/_locales/\(language)/messages.json") - do { - let json = try String(contentsOf: messagesUrl, encoding: .utf8) - webView.evaluateJavaScript("window.bitwardenLocaleStrings = \(json);", completionHandler: {(result, error) in - guard let err = error else { - return; - } - NSLog("evaluateJavaScript error : %@", err.localizedDescription); - }) - } catch { - NSLog("ERROR on getLocaleStrings, \(error)") - } - replyMessage(message: m) - } else if command == "tabs_query" { - guard let options: TabQueryOptions = jsonDeserialize(json: m.data) else { - return - } - if options.currentWindow ?? false { - SFSafariApplication.getActiveWindow { win in - if win != nil { - processWindowsForTabs(wins: [win!], options: options, complete: { tabs in - m.responseData = jsonSerialize(obj: tabs) - self.replyMessage(message: m) - }) - } else { - SFSafariApplication.getAllWindows { wins in - processWindowsForTabs(wins: wins, options: options, complete: { tabs in - m.responseData = jsonSerialize(obj: tabs) - self.replyMessage(message: m) - }) - } - } - } - } else { - SFSafariApplication.getAllWindows { wins in - processWindowsForTabs(wins: wins, options: options, complete: { tabs in - m.responseData = jsonSerialize(obj: tabs) - self.replyMessage(message: m) - }) - } - } - } else if command == "tabs_message" { - guard let tabMsg: TabMessage = jsonDeserialize(json: m.data) else { - return - } - SFSafariApplication.getAllWindows { wins in - var theWin: SFSafariWindow? - var winIndex = 0 - for win in wins { - if tabMsg.tab.windowId == winIndex { - theWin = win - break - } - winIndex = winIndex + 1 - } - var theTab: SFSafariTab? - theWin?.getAllTabs { tabs in - var tabIndex = 0 - for tab in tabs { - if tabMsg.tab.index == tabIndex { - theTab = tab - break - } - tabIndex = tabIndex + 1 - } - theTab?.getActivePage { activePage in - activePage?.dispatchMessageToScript(withName: "bitwarden", userInfo: ["msg": tabMsg.obj]) - } - } - } - } else if command == "hidePopover" { - dismissPopover() - replyMessage(message: m) - } else if command == "showPopover" { - if popoverOpenCount <= 0 { - SFSafariApplication.getActiveWindow { win in - win?.getToolbarItem(completionHandler: { item in - item?.showPopover() - }) - } - } - } else if command == "isPopoverOpen" { - m.responseData = popoverOpenCount > 0 ? "true" : "false" - replyMessage(message: m) - } else if command == "createNewTab" { - if let data = m.data, let url = URL(string: data) { - SFSafariApplication.getActiveWindow { win in - win?.openTab(with: url, makeActiveIfPossible: true, completionHandler: { _ in - // Tab opened - }) - } - } - } else if command == "reloadExtension" { - webView?.reload() - replyMessage(message: m) - } else if command == "copyToClipboard" { - let pasteboard = NSPasteboard.general - pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) - pasteboard.setString(m.data ?? "", forType: NSPasteboard.PasteboardType.string) - replyMessage(message: m) - } else if command == "readFromClipboard" { - let pasteboard = NSPasteboard.general - m.responseData = pasteboard.pasteboardItems?.first?.string(forType: .string) - replyMessage(message: m) - } else if command == "downloadFile" { - guard let jsonData = m.data else { - return - } - guard let dlMsg: DownloadFileMessage = jsonDeserialize(json: jsonData) else { - return - } - var blobData: Data? - if dlMsg.blobOptions?.type == "text/plain" { - blobData = dlMsg.blobData?.data(using: .utf8) - } else if let blob = dlMsg.blobData { - blobData = Data(base64Encoded: blob) - } - guard let data = blobData else { - return - } - let panel = NSSavePanel() - panel.canCreateDirectories = true - panel.nameFieldStringValue = dlMsg.fileName - panel.begin { response in - if response == NSApplication.ModalResponse.OK { - if let url = panel.url { - do { - let fileManager = FileManager.default - if !fileManager.fileExists(atPath: url.absoluteString) { - fileManager.createFile(atPath: url.absoluteString, contents: Data(), - attributes: nil) - } - try data.write(to: url) - } catch { - print(error) - NSLog("ERROR in downloadFile, \(error)") - } - } - } - } - } - } - - func replyMessage(message: AppMessage) { - if webView == nil { - return - } - let json = (jsonSerialize(obj: message) ?? "null") - webView.evaluateJavaScript("window.bitwardenSafariAppMessageReceiver(\(json));", completionHandler: {(result, error) in - guard let err = error else { - return; - } - NSLog("evaluateJavaScript error : %@", err.localizedDescription); - }) - } -} - -extension SafariExtensionViewController: WKUIDelegate { - @available(OSXApplicationExtension 10.12, *) - func webView(_: WKWebView, runOpenPanelWith _: WKOpenPanelParameters, initiatedByFrame _: WKFrameInfo, - completionHandler: @escaping ([URL]?) -> Void) { - let openPanel = NSOpenPanel() - openPanel.canChooseFiles = true - openPanel.begin { result in - if result == NSApplication.ModalResponse.OK && openPanel.url != nil { - completionHandler([openPanel.url!]) - } else { - completionHandler(nil) - } - } - } -} - -func processWindowsForTabs(wins: [SFSafariWindow], options: TabQueryOptions?, complete: @escaping ([Tab]) -> Void) { - if wins.count == 0 { - complete([]) - return - } - var newTabs: [Tab] = [] - let winGroup = DispatchGroup() - for win in wins { - winGroup.enter() - win.getActiveTab { activeTab in - win.getAllTabs { allTabs in - let tabGroup = DispatchGroup() - for tab in allTabs { - tabGroup.enter() - if options?.active ?? false { - if activeTab != nil && activeTab == tab { - let windowIndex = wins.firstIndex(of: win) ?? -100 - let tabIndex = allTabs.firstIndex(of: tab) ?? -1 - makeTabObject(tab: tab, activeTab: activeTab, windowIndex: windowIndex, - tabIndex: tabIndex, complete: { t in - newTabs.append(t) - tabGroup.leave() - }) - } else { - tabGroup.leave() - } - } else { - let windowIndex = wins.firstIndex(of: win) ?? -100 - let tabIndex = allTabs.firstIndex(of: tab) ?? -1 - makeTabObject(tab: tab, activeTab: activeTab, windowIndex: windowIndex, - tabIndex: tabIndex, complete: { t in - newTabs.append(t) - tabGroup.leave() - }) - } - } - tabGroup.notify(queue: .main) { - winGroup.leave() - } - } - } - } - winGroup.notify(queue: .main) { - complete(newTabs) - } -} - -func makeTabObject(tab: SFSafariTab, activeTab: SFSafariTab?, windowIndex: Int, tabIndex: Int, - complete: @escaping (Tab) -> Void) { - let t = Tab() - t.active = activeTab != nil && tab == activeTab - t.windowId = windowIndex - t.index = tabIndex - t.id = "\(windowIndex)_\(tabIndex)" - tab.getActivePage { page in - guard let activePage = page else { - complete(t) - return - } - activePage.getPropertiesWithCompletionHandler({ props in - t.title = props?.title - t.url = props?.url?.absoluteString - complete(t) - }) - } -} - -func jsonSerialize(obj: T?) -> String? { - let encoder = JSONEncoder() - do { - let data = try encoder.encode(obj) - return String(data: data, encoding: .utf8) ?? "null" - } catch _ { - return "null" - } -} - -func jsonDeserialize(json: String?) -> T? { - if json == nil { - return nil - } - let decoder = JSONDecoder() - do { - let obj = try decoder.decode(T.self, from: json!.data(using: .utf8)!) - return obj - } catch _ { - return nil - } -} - -class AppMessage: Decodable, Encodable { - init() { - id = "" - command = "" - data = nil - responseData = nil - responseError = nil - } - - var id: String - var command: String - var data: String? - var responseData: String? - var responseError: Bool? - var senderTab: Tab? -} - -class StorageData: Decodable, Encodable { - var key: String - var obj: String? -} - -class TabQueryOptions: Decodable, Encodable { - var currentWindow: Bool? - var active: Bool? -} - -class Tab: Decodable, Encodable { - init() { - id = "" - index = -1 - windowId = -100 - title = "" - active = false - url = "" - } - - var id: String - var index: Int - var windowId: Int - var title: String? - var active: Bool - var url: String? -} - -class TabMessage: Decodable, Encodable { - var tab: Tab - var obj: String - var options: TabMessageOptions? -} - -class TabMessageOptions: Decodable, Encodable { - var frameId: Int? -} - -class DownloadFileMessage: Decodable, Encodable { - var fileName: String - var blobData: String? - var blobOptions: DownloadFileMessageBlobOptions? -} - -class DownloadFileMessageBlobOptions: Decodable, Encodable { - var type: String? -} diff --git a/src/safari/safari/SafariWebExtensionHandler.swift b/src/safari/safari/SafariWebExtensionHandler.swift new file mode 100644 index 0000000000..001d388e6e --- /dev/null +++ b/src/safari/safari/SafariWebExtensionHandler.swift @@ -0,0 +1,109 @@ +import SafariServices +import os.log + +let SFExtensionMessageKey = "message" + +class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { + + func beginRequest(with context: NSExtensionContext) { + let item = context.inputItems[0] as! NSExtensionItem + let message = item.userInfo?[SFExtensionMessageKey] as AnyObject? + os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", message as! CVarArg) + + let response = NSExtensionItem() + + guard let command = message?["command"] as? String else { + return + } + + switch (command) { + case "readFromClipboard": + let pasteboard = NSPasteboard.general + response.userInfo = [ SFExtensionMessageKey: pasteboard.pasteboardItems?.first?.string(forType: .string) as Any ] + break + case "showPopover": + SFSafariApplication.getActiveWindow { win in + win?.getToolbarItem(completionHandler: { item in + item?.showPopover() + }) + } + break + case "downloadFile": + guard let jsonData = message?["data"] as? String else { + return + } + guard let dlMsg: DownloadFileMessage = jsonDeserialize(json: jsonData) else { + return + } + var blobData: Data? + if dlMsg.blobOptions?.type == "text/plain" { + blobData = dlMsg.blobData?.data(using: .utf8) + } else if let blob = dlMsg.blobData { + blobData = Data(base64Encoded: blob) + } + guard let data = blobData else { + return + } + let panel = NSSavePanel() + panel.canCreateDirectories = true + panel.nameFieldStringValue = dlMsg.fileName + panel.begin { response in + if response == NSApplication.ModalResponse.OK { + if let url = panel.url { + do { + let fileManager = FileManager.default + if !fileManager.fileExists(atPath: url.absoluteString) { + fileManager.createFile(atPath: url.absoluteString, contents: Data(), + attributes: nil) + } + try data.write(to: url) + } catch { + print(error) + NSLog("ERROR in downloadFile, \(error)") + } + } + } + } + break + + default: + return + } + + context.completeRequest(returningItems: [response], completionHandler: nil) + } + +} + +func jsonSerialize(obj: T?) -> String? { + let encoder = JSONEncoder() + do { + let data = try encoder.encode(obj) + return String(data: data, encoding: .utf8) ?? "null" + } catch _ { + return "null" + } +} + +func jsonDeserialize(json: String?) -> T? { + if json == nil { + return nil + } + let decoder = JSONDecoder() + do { + let obj = try decoder.decode(T.self, from: json!.data(using: .utf8)!) + return obj + } catch _ { + return nil + } +} + +class DownloadFileMessage: Decodable, Encodable { + var fileName: String + var blobData: String? + var blobOptions: DownloadFileMessageBlobOptions? +} + +class DownloadFileMessageBlobOptions: Decodable, Encodable { + var type: String? +} diff --git a/src/safari/safari/ToolbarItemIcon.pdf b/src/safari/safari/ToolbarItemIcon.pdf deleted file mode 100644 index dbf98ce90e57cfe02aca29db06dc6f2678a6586d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46066 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 - - - - - - Bitwarden - - - - - - -
-
- - - - - - - - - diff --git a/src/services/browserMessaging.service.ts b/src/services/browserMessaging.service.ts index 01f94269fb..e5bd9b1e98 100644 --- a/src/services/browserMessaging.service.ts +++ b/src/services/browserMessaging.service.ts @@ -1,16 +1,8 @@ -import { BrowserApi } from '../browser/browserApi'; -import { SafariApp } from '../browser/safariApp'; - import { MessagingService } from 'jslib/abstractions/messaging.service'; export default class BrowserMessagingService implements MessagingService { send(subscriber: string, arg: any = {}) { const message = Object.assign({}, { command: subscriber }, arg); - if (BrowserApi.isSafariApi) { - SafariApp.sendMessageToApp(subscriber, arg); - SafariApp.sendMessageToListeners(message, 'BrowserMessagingService', null); - } else { - chrome.runtime.sendMessage(message); - } + chrome.runtime.sendMessage(message); } } diff --git a/src/services/browserPlatformUtils.service.ts b/src/services/browserPlatformUtils.service.ts index b9946937f4..02d2f5698d 100644 --- a/src/services/browserPlatformUtils.service.ts +++ b/src/services/browserPlatformUtils.service.ts @@ -189,13 +189,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService } const clearing = options ? !!options.clearing : false; const clearMs: number = options && options.clearMs ? options.clearMs : null; - if (this.isSafariExtension()) { - SafariApp.sendMessageToApp('copyToClipboard', text).then(() => { - if (!clearing && this.clipboardWriteCallback != null) { - this.clipboardWriteCallback(text, clearMs); - } - }); - } else if (this.isFirefox() && (win as any).navigator.clipboard && (win as any).navigator.clipboard.writeText) { + if (this.isFirefox() && (win as any).navigator.clipboard && (win as any).navigator.clipboard.writeText) { (win as any).navigator.clipboard.writeText(text).then(() => { if (!clearing && this.clipboardWriteCallback != null) { this.clipboardWriteCallback(text, clearMs); @@ -311,6 +305,6 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService } private isSafariExtension(): boolean { - return (window as any).safariAppExtension === true; + return navigator.userAgent.indexOf(' Safari/') !== -1; } } diff --git a/src/services/browserStorage.service.ts b/src/services/browserStorage.service.ts index 7510327d7d..8f8cf60792 100644 --- a/src/services/browserStorage.service.ts +++ b/src/services/browserStorage.service.ts @@ -3,61 +3,39 @@ import { StorageService, } from 'jslib/abstractions'; -import { SafariApp } from '../browser/safariApp'; - export default class BrowserStorageService implements StorageService { private chromeStorageApi: any; - private isSafari: boolean; - constructor(platformUtilsService: PlatformUtilsService) { - this.isSafari = platformUtilsService.isSafari(); - if (!this.isSafari) { - this.chromeStorageApi = chrome.storage.local; - } + constructor() { + this.chromeStorageApi = chrome.storage.local; } async get(key: string): Promise { - if (this.isSafari) { - const obj = await SafariApp.sendMessageToApp('storage_get', key); - return obj == null ? null : JSON.parse(obj) as T; - } else { - return new Promise((resolve) => { - this.chromeStorageApi.get(key, (obj: any) => { - if (obj != null && obj[key] != null) { - resolve(obj[key] as T); - return; - } - resolve(null); - }); + return new Promise((resolve) => { + this.chromeStorageApi.get(key, (obj: any) => { + if (obj != null && obj[key] != null) { + resolve(obj[key] as T); + return; + } + resolve(null); }); - } + }); } async save(key: string, obj: any): Promise { const keyedObj = { [key]: obj }; - if (this.isSafari) { - await SafariApp.sendMessageToApp('storage_save', JSON.stringify({ - key: key, - obj: JSON.stringify(obj), - })); - } else { - return new Promise((resolve) => { - this.chromeStorageApi.set(keyedObj, () => { - resolve(); - }); + return new Promise((resolve) => { + this.chromeStorageApi.set(keyedObj, () => { + resolve(); }); - } + }); } async remove(key: string): Promise { - if (this.isSafari) { - await SafariApp.sendMessageToApp('storage_remove', key); - } else { - return new Promise((resolve) => { - this.chromeStorageApi.remove(key, () => { - resolve(); - }); + return new Promise((resolve) => { + this.chromeStorageApi.remove(key, () => { + resolve(); }); - } + }); } } diff --git a/src/services/i18n.service.ts b/src/services/i18n.service.ts index 4eb880424d..542dbb6f35 100644 --- a/src/services/i18n.service.ts +++ b/src/services/i18n.service.ts @@ -1,19 +1,11 @@ import { I18nService as BaseI18nService } from 'jslib/services/i18n.service'; -import { BrowserApi } from '../browser/browserApi'; -import { SafariApp } from '../browser/safariApp'; - export default class I18nService extends BaseI18nService { constructor(systemLanguage: string) { - super(systemLanguage, BrowserApi.isSafariApi ? 'safari' : null, async (formattedLocale: string) => { - if (BrowserApi.isSafariApi) { - await SafariApp.sendMessageToApp('getLocaleStrings', formattedLocale); - return (window as any).bitwardenLocaleStrings; - } else { - // Deprecated - const file = await fetch(this.localesDirectory + formattedLocale + '/messages.json'); - return await file.json(); - } + super(systemLanguage, null, async (formattedLocale: string) => { + // Deprecated + const file = await fetch(this.localesDirectory + formattedLocale + '/messages.json'); + return await file.json(); }); this.supportedTranslationLocales = [