@@ -6,29 +6,51 @@ enum ActionType: String {
66 case tabForward
77 case tabBackward
88 case closeTab
9+ case updateSettings
10+ }
11+
12+ enum InputAction : String {
13+ case openSettings
14+ case resetSettings
915}
1016
1117enum TabDirection : String {
1218 case forward
1319 case backward
1420}
1521
16- func mod( _ a: Int , _ n: Int ) -> Int {
17- // https://stackoverflow.com/questions/41180292/negative-number-modulo-in-swift
18- precondition ( n > 0 , " modulus must be positive " )
19- let r = a % n
20- return r >= 0 ? r : r + n
21- }
22-
2322class SafariExtensionHandler : SFSafariExtensionHandler {
24- override func messageReceived( withName messageName: String , from page: SFSafariPage , userInfo: [ String : Any ] ? ) {
25- guard let action = ActionType ( rawValue: messageName) else {
26- NSLog ( " Received message with unsupported type: \( messageName) " )
27- return
23+
24+ private enum Constant {
25+ static let mainAppName = " Vimari "
26+ static let newTabPageURL = " https://duckduckgo.com " //Try it :D
27+ }
28+
29+ let configuration : ConfigurationModelProtocol = ConfigurationModel ( )
30+
31+ //MARK: Overrides
32+
33+ // This method handles messages from the Vimari App (located /Vimari in the repository)
34+ override func messageReceivedFromContainingApp( withName messageName: String , userInfo: [ String : Any ] ? = nil ) {
35+ do {
36+ switch InputAction ( rawValue: messageName) {
37+ case . openSettings:
38+ try configuration. editConfigFile ( )
39+ case . resetSettings:
40+ try configuration. resetConfigFile ( )
41+ case . none:
42+ NSLog ( " Input not supported " + messageName)
43+ }
44+ } catch {
45+ NSLog ( error. localizedDescription)
2846 }
2947
48+ }
49+
50+ // This method handles messages from the extension (in the browser page)
51+ override func messageReceived( withName messageName: String , from page: SFSafariPage , userInfo: [ String : Any ] ? ) {
3052 NSLog ( " Received message: \( messageName) " )
31- switch action {
53+ switch ActionType ( rawValue : messageName ) {
3254 case . openLinkInTab:
3355 let url = URL ( string: userInfo ? [ " url " ] as! String )
3456 openInNewTab ( url: url!)
@@ -40,66 +62,156 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
4062 changeTab ( withDirection: . backward, from: page)
4163 case . closeTab:
4264 closeTab ( from: page)
65+ case . updateSettings:
66+ updateSettings ( page: page)
67+ case . none:
68+ NSLog ( " Received message with unsupported type: \( messageName) " )
4369 }
4470 }
4571
46- func openInNewTab( url: URL ) {
72+ override func toolbarItemClicked( in _: SFSafariWindow ) {
73+ // This method will be called when your toolbar item is clicked.
74+ NSLog ( " The extension's toolbar item was clicked " )
75+ NSWorkspace . shared. launchApplication ( Constant . mainAppName)
76+ }
77+
78+ override func validateToolbarItem( in _: SFSafariWindow , validationHandler: @escaping ( ( Bool , String ) -> Void ) ) {
79+ // This is called when Safari's state changed in some way that would require the extension's toolbar item to be validated again.
80+ validationHandler ( true , " " )
81+ }
82+
83+ override func popoverViewController( ) -> SFSafariExtensionViewController {
84+ return SafariExtensionViewController . shared
85+ }
86+
87+ // MARK: Tabs Methods
88+
89+ private func openInNewTab( url: URL ) {
4790 SFSafariApplication . getActiveWindow { activeWindow in
4891 activeWindow? . openTab ( with: url, makeActiveIfPossible: false , completionHandler: { _ in
4992 // Perform some action here after the page loads
5093 } )
5194 }
5295 }
5396
54- func openNewTab( ) {
55- // Ideally this URL would be something that represents an empty tab better than localhost
56- let url = URL ( string: " http://localhost " ) !
97+ private func openNewTab( ) {
98+ var newPageUrl : String ? = getSetting ( " openTabUrl " ) as? String
99+ if newPageUrl == nil || newPageUrl!. isEmpty {
100+ newPageUrl = Constant . newTabPageURL
101+ }
102+ let url = URL ( string: newPageUrl!) !
57103 SFSafariApplication . getActiveWindow { activeWindow in
58104 activeWindow? . openTab ( with: url, makeActiveIfPossible: true , completionHandler: { _ in
59105 // Perform some action here after the page loads
60106 } )
61107 }
62108 }
63109
64- func changeTab( withDirection direction: TabDirection , from page: SFSafariPage , completionHandler: ( ( ) -> Void ) ? = nil ) {
65- page. getContainingTab ( completionHandler: { currentTab in
66- currentTab. getContainingWindow ( completionHandler: { window in
67- window? . getAllTabs ( completionHandler: { tabs in
110+ private func changeTab( withDirection direction: TabDirection , from page: SFSafariPage , completionHandler: ( ( ) -> Void ) ? = nil ) {
111+ page. getContainingTab ( ) { currentTab in
112+ // Using .currentWindow instead of .containingWindow, this prevents the window being nil in the case of a pinned tab.
113+ self . currentWindow ( from: page) { window in
114+ window? . getAllTabs ( ) { tabs in
115+ tabs. forEach { tab in NSLog ( tab. description) }
68116 if let currentIndex = tabs. firstIndex ( of: currentTab) {
69117 let indexStep = direction == TabDirection . forward ? 1 : - 1
70118
71119 // Wrap around the ends with a modulus operator.
72120 // % calculates the remainder, not the modulus, so we need a
73121 // custom function.
74122 let newIndex = mod ( currentIndex + indexStep, tabs. count)
75-
123+
76124 tabs [ newIndex] . activate ( completionHandler: completionHandler ?? { } )
77-
125+
78126 }
79- } )
80- } )
81- } )
127+ }
128+ }
129+ }
130+ }
131+
132+ /**
133+ Returns the containing window of a SFSafariPage, if not available default to the current active window.
134+ */
135+ private func currentWindow( from page: SFSafariPage , completionHandler: @escaping ( ( SFSafariWindow ? ) -> Void ) ) {
136+ page. getContainingTab ( ) { $0. getContainingWindow ( ) { window in
137+ if window != nil {
138+ return completionHandler ( window)
139+ } else {
140+ SFSafariApplication . getActiveWindow ( ) { window in
141+ return completionHandler ( window)
142+ }
143+ }
144+ } }
82145 }
83146
84- func closeTab( from page: SFSafariPage ) {
147+ private func closeTab( from page: SFSafariPage ) {
85148 page. getContainingTab {
86149 tab in
87150 tab. close ( )
88151 }
89152 }
153+
154+ // MARK: Settings
90155
91- override func toolbarItemClicked( in _: SFSafariWindow ) {
92- // This method will be called when your toolbar item is clicked.
93- NSLog ( " The extension's toolbar item was clicked " )
94- NSWorkspace . shared. launchApplication ( " Vimari " )
156+ private func getSetting( _ settingKey: String ) -> Any ? {
157+ do {
158+ let settings = try configuration. getUserSettings ( )
159+ return settings [ settingKey]
160+ } catch {
161+ NSLog ( " Was not able to retrieve the user settings \n \( error. localizedDescription) " )
162+ return nil
163+ }
164+ }
165+
166+ private func updateSettings( page: SFSafariPage ) {
167+ do {
168+ let settings : [ String : Any ]
169+ if let userSettings = try ? configuration. getUserSettings ( ) {
170+ settings = userSettings
171+ } else {
172+ settings = try configuration. getDefaultSettings ( )
173+ }
174+ page. dispatch ( settings: settings)
175+ } catch {
176+ NSLog ( error. localizedDescription)
177+ }
178+ }
179+
180+ private func fallbackSettings( page: SFSafariPage ) {
181+ do {
182+ let settings = try configuration. getUserSettings ( )
183+ page. dispatch ( settings: settings)
184+ } catch {
185+ NSLog ( error. localizedDescription)
186+ }
95187 }
188+ }
96189
97- override func validateToolbarItem( in _: SFSafariWindow , validationHandler: @escaping ( ( Bool , String ) -> Void ) ) {
98- // This is called when Safari's state changed in some way that would require the extension's toolbar item to be validated again.
99- validationHandler ( true , " " )
190+ // MARK: Helpers
191+
192+ private func mod( _ a: Int , _ n: Int ) -> Int {
193+ // https://stackoverflow.com/questions/41180292/negative-number-modulo-in-swift
194+ precondition ( n > 0 , " modulus must be positive " )
195+ let r = a % n
196+ return r >= 0 ? r : r + n
197+ }
198+
199+ private extension SFSafariPage {
200+ func dispatch( settings: [ String : Any ] ) {
201+ self . dispatchMessageToScript (
202+ withName: " updateSettingsEvent " ,
203+ userInfo: settings
204+ )
100205 }
206+ }
101207
102- override func popoverViewController( ) -> SFSafariExtensionViewController {
103- return SafariExtensionViewController . shared
208+ private extension SFSafariApplication {
209+ static func getActivePage( completionHandler: @escaping ( SFSafariPage ? ) -> Void ) {
210+ SFSafariApplication . getActiveWindow {
211+ $0? . getActiveTab {
212+ $0? . getActivePage ( completionHandler: completionHandler)
213+ }
214+ }
104215 }
105216}
217+
0 commit comments