diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 5166d45..6b4e9e0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -62,6 +62,7 @@ body: - Arc - Brave - Vivaldi + - Dia - Firefox (experimental) validations: diff --git a/README.md b/README.md index 810e73c..5a66e67 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ SimplyTrack requires several macOS permissions to function properly: 1. **Automation Permission**: To track browser activity - System Preferences → Privacy & Security → Automation - - Enable SimplyTrack for your browsers (Safari, Chrome, Edge, Arc, Brave, Vivaldi, Firefox) + - Enable SimplyTrack for your browsers (Safari, Chrome, Edge, Arc, Brave, Vivaldi, Dia, Firefox) 2. **System Events Permission**: For Safari and Firefox browser integration - System Preferences → Privacy & Security → Automation diff --git a/SimplyTrack/App/AppDelegate.swift b/SimplyTrack/App/AppDelegate.swift index ef21007..261b7e6 100644 --- a/SimplyTrack/App/AppDelegate.swift +++ b/SimplyTrack/App/AppDelegate.swift @@ -50,6 +50,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { // Initialize all services with proper dependency injection initializeServices() + // Prompt for accessibility permission if not already granted + PermissionManager.shared.checkAccessibilityPermission() + // Start services in correct order menuBarManager?.setupMenuBar() trackingService?.startTracking() diff --git a/SimplyTrack/Managers/PermissionManager.swift b/SimplyTrack/Managers/PermissionManager.swift index 0e8b388..912118d 100644 --- a/SimplyTrack/Managers/PermissionManager.swift +++ b/SimplyTrack/Managers/PermissionManager.swift @@ -8,6 +8,7 @@ import AppKit import ApplicationServices import Foundation +import os.log /// Status of macOS system permissions required for app functionality. /// Used to track automation permissions needed for browser integration. @@ -27,6 +28,8 @@ class PermissionManager: ObservableObject { /// Shared singleton instance for permission management static let shared = PermissionManager() + private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PermissionManager") + /// Current status of automation permissions for browser AppleScript access @Published var automationPermissionStatus: PermissionStatus = .notDetermined /// Current status of System Events automation permissions (needed for Safari private browsing detection) @@ -43,6 +46,7 @@ class PermissionManager: ObservableObject { "company.thebrowser.Browser", "com.brave.Browser", "com.vivaldi.Vivaldi", + "company.thebrowser.dia", "org.mozilla.firefox", ] @@ -89,6 +93,24 @@ class PermissionManager: ObservableObject { } } + /// Checks if accessibility permissions are granted and prompts if not. + /// Should be called at app startup to trigger the macOS permission dialog. + /// Does not set `.denied` on failure — the browser error paths handle that + /// once the user has had a chance to respond to the system prompt. + func checkAccessibilityPermission() { + let trusted = AXIsProcessTrustedWithOptions( + [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary + ) + if trusted { + logger.info("Accessibility permission already granted") + Task { @MainActor in + self.accessibilityPermissionStatus = .granted + } + } else { + logger.warning("Accessibility permission not granted — user prompted") + } + } + /// Opens System Preferences to the Automation privacy settings. /// Allows users to grant AppleScript permissions for browser automation and System Events access. func openSystemPreferences() { diff --git a/SimplyTrack/Services/Browsers/DiaBrowser.swift b/SimplyTrack/Services/Browsers/DiaBrowser.swift new file mode 100644 index 0000000..e069010 --- /dev/null +++ b/SimplyTrack/Services/Browsers/DiaBrowser.swift @@ -0,0 +1,71 @@ +// +// DiaBrowser.swift +// SimplyTrack +// + +import Foundation +import os.log + +/// Dia-specific implementation of browser interface. +/// Handles URL detection for the Dia browser by The Browser Company. +/// Dia exposes a custom AppleScript interface with tab and URL support. +class DiaBrowser: BaseBrowser { + private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "DiaBrowser") + + init() { + super.init(bundleId: "company.thebrowser.dia", displayName: "Dia") + } + + /// Dia-specific AppleScript for URL retrieval + override var currentURLScript: String { + return """ + tell application "Dia" + if (count of windows) > 0 then + return URL of active tab of window 1 + end if + end tell + """ + } + + /// Checks if Dia is currently in incognito mode. + /// Dia doesn't expose a private browsing property in its AppleScript dictionary, + /// so we use the accessibility API to check the window's AXIdentifier which + /// contains "bigIncognitoBrowserWindow" for private windows. + override func isInPrivateBrowsingMode() -> Bool { + let script = """ + tell application "System Events" + tell process "Dia" + if (count of windows) > 0 then + return value of attribute "AXIdentifier" of front window + end if + end tell + end tell + """ + + let scriptResult = executeAppleScript(script) + + if let error = scriptResult.error { + if scriptResult.errorCode == -1719 { + logger.debug("Dia System Events transient error (invalid index): \(error.description)") + } else if scriptResult.errorCode == -1743 || scriptResult.errorCode == -1744 { + PermissionManager.shared.handleSystemEventsPermissionResult(success: false) + } else if scriptResult.errorCode == -25211 { + PermissionManager.shared.handleAccessibilityPermissionResult(success: false) + } else { + logger.error("Dia System Events AppleScript error: \(error.description)") + } + return false + } + + if scriptResult.result != nil { + PermissionManager.shared.handleSystemEventsPermissionResult(success: true) + PermissionManager.shared.handleAccessibilityPermissionResult(success: true) + } + + guard let identifier = scriptResult.result else { + return false + } + + return identifier.contains("Incognito") + } +} diff --git a/SimplyTrack/Services/Browsers/FirefoxBrowser.swift b/SimplyTrack/Services/Browsers/FirefoxBrowser.swift index 1f01a1f..df82365 100644 --- a/SimplyTrack/Services/Browsers/FirefoxBrowser.swift +++ b/SimplyTrack/Services/Browsers/FirefoxBrowser.swift @@ -93,6 +93,8 @@ class FirefoxBrowser: BaseBrowser { ) } else if scriptResult.errorCode == -1743 || scriptResult.errorCode == -1744 { PermissionManager.shared.handleSystemEventsPermissionResult(success: false) + } else if scriptResult.errorCode == -25211 { + PermissionManager.shared.handleAccessibilityPermissionResult(success: false) } else if scriptResult.errorCode == -1719 || scriptResult.errorCode == -1728 { // Firefox's accessibility hierarchy can shift during navigation or between versions. logger.debug("Firefox address bar unavailable in current accessibility tree: \(error.description)") diff --git a/SimplyTrack/Services/Browsers/SafariBrowser.swift b/SimplyTrack/Services/Browsers/SafariBrowser.swift index 14305ce..0391eb9 100644 --- a/SimplyTrack/Services/Browsers/SafariBrowser.swift +++ b/SimplyTrack/Services/Browsers/SafariBrowser.swift @@ -57,6 +57,8 @@ class SafariBrowser: BaseBrowser { } else if scriptResult.errorCode == -1743 || scriptResult.errorCode == -1744 { // System Events permission errors PermissionManager.shared.handleSystemEventsPermissionResult(success: false) + } else if scriptResult.errorCode == -25211 { + PermissionManager.shared.handleAccessibilityPermissionResult(success: false) } else { // Log non-permission System Events errors logger.error("Safari System Events AppleScript error: \(error.description)") diff --git a/SimplyTrack/Services/WebTrackingService.swift b/SimplyTrack/Services/WebTrackingService.swift index 19418ef..7badf99 100644 --- a/SimplyTrack/Services/WebTrackingService.swift +++ b/SimplyTrack/Services/WebTrackingService.swift @@ -3,7 +3,7 @@ // SimplyTrack // // Handles browser integration, AppleScript execution, favicon fetching, and website detection -// Supports Safari, Chrome, Edge, Arc, Brave, Vivaldi, and Firefox through AppleScript communication for website tracking +// Supports Safari, Chrome, Edge, Arc, Brave, Vivaldi, Dia, and Firefox through AppleScript communication for website tracking // import AppKit @@ -63,6 +63,7 @@ class WebTrackingService { ArcBrowser(), BraveBrowser(), VivaldiBrowser(), + DiaBrowser(), FirefoxBrowser(), ].reduce(into: [:]) { result, browser in result[browser.bundleId] = browser diff --git a/SimplyTrack/SimplyTrack.entitlements b/SimplyTrack/SimplyTrack.entitlements index f0da4f8..0c13b67 100644 --- a/SimplyTrack/SimplyTrack.entitlements +++ b/SimplyTrack/SimplyTrack.entitlements @@ -12,6 +12,7 @@ company.thebrowser.Browser com.brave.Browser com.vivaldi.Vivaldi + company.thebrowser.dia org.mozilla.firefox com.apple.systemevents diff --git a/SimplyTrack/Views/ContentView.swift b/SimplyTrack/Views/ContentView.swift index db4465d..ef52a67 100644 --- a/SimplyTrack/Views/ContentView.swift +++ b/SimplyTrack/Views/ContentView.swift @@ -122,7 +122,7 @@ struct ContentView: View { if permissionManager.systemEventsPermissionStatus == .denied { PermissionBannerView( title: "System Events Permission Required", - message: "SimplyTrack needs System Events access to detect Safari private browsing. Enable it in System Preferences.", + message: "SimplyTrack needs System Events access to detect Safari and Dia private browsing. Enable it in System Preferences.", primaryButtonTitle: "Open System Preferences", primaryAction: { permissionManager.openSystemPreferences() }, color: .orange @@ -133,7 +133,7 @@ struct ContentView: View { if permissionManager.accessibilityPermissionStatus == .denied { PermissionBannerView( title: "Accessibility Permission Required", - message: "SimplyTrack needs Accessibility access to detect Safari private browsing. Enable it in System Preferences.", + message: "SimplyTrack needs Accessibility access to detect Safari and Dia private browsing. Enable it in System Preferences.", primaryButtonTitle: "Open System Preferences", primaryAction: { permissionManager.openAccessibilityPreferences() }, color: .orange diff --git a/SimplyTrack/Views/Settings/PrivacySettingsView.swift b/SimplyTrack/Views/Settings/PrivacySettingsView.swift index 13bb6c3..f07cb64 100644 --- a/SimplyTrack/Views/Settings/PrivacySettingsView.swift +++ b/SimplyTrack/Views/Settings/PrivacySettingsView.swift @@ -101,6 +101,16 @@ struct PrivacySettingsView: View { .foregroundColor(.secondary) } + HStack { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + Text("Dia Incognito") + Spacer() + Text("Supported") + .font(.caption) + .foregroundColor(.secondary) + } + HStack { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green)