Skip to content

Commit 5e35dab

Browse files
authored
feat: add Dia browser support with incognito detection (#74)
1 parent 720fa3c commit 5e35dab

11 files changed

Lines changed: 117 additions & 4 deletions

File tree

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ body:
6262
- Arc
6363
- Brave
6464
- Vivaldi
65+
- Dia
6566
- Firefox (experimental)
6667

6768
validations:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ SimplyTrack requires several macOS permissions to function properly:
8383

8484
1. **Automation Permission**: To track browser activity
8585
- System Preferences → Privacy & Security → Automation
86-
- Enable SimplyTrack for your browsers (Safari, Chrome, Edge, Arc, Brave, Vivaldi, Firefox)
86+
- Enable SimplyTrack for your browsers (Safari, Chrome, Edge, Arc, Brave, Vivaldi, Dia, Firefox)
8787

8888
2. **System Events Permission**: For Safari and Firefox browser integration
8989
- System Preferences → Privacy & Security → Automation

SimplyTrack/App/AppDelegate.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
5050
// Initialize all services with proper dependency injection
5151
initializeServices()
5252

53+
// Prompt for accessibility permission if not already granted
54+
PermissionManager.shared.checkAccessibilityPermission()
55+
5356
// Start services in correct order
5457
menuBarManager?.setupMenuBar()
5558
trackingService?.startTracking()

SimplyTrack/Managers/PermissionManager.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import AppKit
99
import ApplicationServices
1010
import Foundation
11+
import os.log
1112

1213
/// Status of macOS system permissions required for app functionality.
1314
/// Used to track automation permissions needed for browser integration.
@@ -27,6 +28,8 @@ class PermissionManager: ObservableObject {
2728
/// Shared singleton instance for permission management
2829
static let shared = PermissionManager()
2930

31+
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PermissionManager")
32+
3033
/// Current status of automation permissions for browser AppleScript access
3134
@Published var automationPermissionStatus: PermissionStatus = .notDetermined
3235
/// Current status of System Events automation permissions (needed for Safari private browsing detection)
@@ -43,6 +46,7 @@ class PermissionManager: ObservableObject {
4346
"company.thebrowser.Browser",
4447
"com.brave.Browser",
4548
"com.vivaldi.Vivaldi",
49+
"company.thebrowser.dia",
4650
"org.mozilla.firefox",
4751
]
4852

@@ -89,6 +93,24 @@ class PermissionManager: ObservableObject {
8993
}
9094
}
9195

96+
/// Checks if accessibility permissions are granted and prompts if not.
97+
/// Should be called at app startup to trigger the macOS permission dialog.
98+
/// Does not set `.denied` on failure — the browser error paths handle that
99+
/// once the user has had a chance to respond to the system prompt.
100+
func checkAccessibilityPermission() {
101+
let trusted = AXIsProcessTrustedWithOptions(
102+
[kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary
103+
)
104+
if trusted {
105+
logger.info("Accessibility permission already granted")
106+
Task { @MainActor in
107+
self.accessibilityPermissionStatus = .granted
108+
}
109+
} else {
110+
logger.warning("Accessibility permission not granted — user prompted")
111+
}
112+
}
113+
92114
/// Opens System Preferences to the Automation privacy settings.
93115
/// Allows users to grant AppleScript permissions for browser automation and System Events access.
94116
func openSystemPreferences() {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// DiaBrowser.swift
3+
// SimplyTrack
4+
//
5+
6+
import Foundation
7+
import os.log
8+
9+
/// Dia-specific implementation of browser interface.
10+
/// Handles URL detection for the Dia browser by The Browser Company.
11+
/// Dia exposes a custom AppleScript interface with tab and URL support.
12+
class DiaBrowser: BaseBrowser {
13+
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "DiaBrowser")
14+
15+
init() {
16+
super.init(bundleId: "company.thebrowser.dia", displayName: "Dia")
17+
}
18+
19+
/// Dia-specific AppleScript for URL retrieval
20+
override var currentURLScript: String {
21+
return """
22+
tell application "Dia"
23+
if (count of windows) > 0 then
24+
return URL of active tab of window 1
25+
end if
26+
end tell
27+
"""
28+
}
29+
30+
/// Checks if Dia is currently in incognito mode.
31+
/// Dia doesn't expose a private browsing property in its AppleScript dictionary,
32+
/// so we use the accessibility API to check the window's AXIdentifier which
33+
/// contains "bigIncognitoBrowserWindow" for private windows.
34+
override func isInPrivateBrowsingMode() -> Bool {
35+
let script = """
36+
tell application "System Events"
37+
tell process "Dia"
38+
if (count of windows) > 0 then
39+
return value of attribute "AXIdentifier" of front window
40+
end if
41+
end tell
42+
end tell
43+
"""
44+
45+
let scriptResult = executeAppleScript(script)
46+
47+
if let error = scriptResult.error {
48+
if scriptResult.errorCode == -1719 {
49+
logger.debug("Dia System Events transient error (invalid index): \(error.description)")
50+
} else if scriptResult.errorCode == -1743 || scriptResult.errorCode == -1744 {
51+
PermissionManager.shared.handleSystemEventsPermissionResult(success: false)
52+
} else if scriptResult.errorCode == -25211 {
53+
PermissionManager.shared.handleAccessibilityPermissionResult(success: false)
54+
} else {
55+
logger.error("Dia System Events AppleScript error: \(error.description)")
56+
}
57+
return false
58+
}
59+
60+
if scriptResult.result != nil {
61+
PermissionManager.shared.handleSystemEventsPermissionResult(success: true)
62+
PermissionManager.shared.handleAccessibilityPermissionResult(success: true)
63+
}
64+
65+
guard let identifier = scriptResult.result else {
66+
return false
67+
}
68+
69+
return identifier.contains("Incognito")
70+
}
71+
}

SimplyTrack/Services/Browsers/FirefoxBrowser.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class FirefoxBrowser: BaseBrowser {
9393
)
9494
} else if scriptResult.errorCode == -1743 || scriptResult.errorCode == -1744 {
9595
PermissionManager.shared.handleSystemEventsPermissionResult(success: false)
96+
} else if scriptResult.errorCode == -25211 {
97+
PermissionManager.shared.handleAccessibilityPermissionResult(success: false)
9698
} else if scriptResult.errorCode == -1719 || scriptResult.errorCode == -1728 {
9799
// Firefox's accessibility hierarchy can shift during navigation or between versions.
98100
logger.debug("Firefox address bar unavailable in current accessibility tree: \(error.description)")

SimplyTrack/Services/Browsers/SafariBrowser.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class SafariBrowser: BaseBrowser {
5757
} else if scriptResult.errorCode == -1743 || scriptResult.errorCode == -1744 {
5858
// System Events permission errors
5959
PermissionManager.shared.handleSystemEventsPermissionResult(success: false)
60+
} else if scriptResult.errorCode == -25211 {
61+
PermissionManager.shared.handleAccessibilityPermissionResult(success: false)
6062
} else {
6163
// Log non-permission System Events errors
6264
logger.error("Safari System Events AppleScript error: \(error.description)")

SimplyTrack/Services/WebTrackingService.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SimplyTrack
44
//
55
// Handles browser integration, AppleScript execution, favicon fetching, and website detection
6-
// Supports Safari, Chrome, Edge, Arc, Brave, Vivaldi, and Firefox through AppleScript communication for website tracking
6+
// Supports Safari, Chrome, Edge, Arc, Brave, Vivaldi, Dia, and Firefox through AppleScript communication for website tracking
77
//
88

99
import AppKit
@@ -63,6 +63,7 @@ class WebTrackingService {
6363
ArcBrowser(),
6464
BraveBrowser(),
6565
VivaldiBrowser(),
66+
DiaBrowser(),
6667
FirefoxBrowser(),
6768
].reduce(into: [:]) { result, browser in
6869
result[browser.bundleId] = browser

SimplyTrack/SimplyTrack.entitlements

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<string>company.thebrowser.Browser</string>
1313
<string>com.brave.Browser</string>
1414
<string>com.vivaldi.Vivaldi</string>
15+
<string>company.thebrowser.dia</string>
1516
<string>org.mozilla.firefox</string>
1617
<string>com.apple.systemevents</string>
1718
</array>

SimplyTrack/Views/ContentView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ struct ContentView: View {
122122
if permissionManager.systemEventsPermissionStatus == .denied {
123123
PermissionBannerView(
124124
title: "System Events Permission Required",
125-
message: "SimplyTrack needs System Events access to detect Safari private browsing. Enable it in System Preferences.",
125+
message: "SimplyTrack needs System Events access to detect Safari and Dia private browsing. Enable it in System Preferences.",
126126
primaryButtonTitle: "Open System Preferences",
127127
primaryAction: { permissionManager.openSystemPreferences() },
128128
color: .orange
@@ -133,7 +133,7 @@ struct ContentView: View {
133133
if permissionManager.accessibilityPermissionStatus == .denied {
134134
PermissionBannerView(
135135
title: "Accessibility Permission Required",
136-
message: "SimplyTrack needs Accessibility access to detect Safari private browsing. Enable it in System Preferences.",
136+
message: "SimplyTrack needs Accessibility access to detect Safari and Dia private browsing. Enable it in System Preferences.",
137137
primaryButtonTitle: "Open System Preferences",
138138
primaryAction: { permissionManager.openAccessibilityPreferences() },
139139
color: .orange

0 commit comments

Comments
 (0)