Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
c35f49e
feat: add safari extension settings scaffold
Apr 22, 2026
8f31a3e
feat: add safari extension shared request model
Apr 22, 2026
738c5bf
feat: add safari extension suggestion action model
Apr 22, 2026
ba31bab
feat: add safari extension submission classifier
Apr 22, 2026
92eaea3
feat: add safari extension response models
Apr 22, 2026
3886cc5
feat: add safari web extension target scaffold
Apr 22, 2026
2c55177
feat: add safari web extension native bridge
Apr 22, 2026
7269d51
refactor: move safari bridge tests into shared models
Apr 22, 2026
c60a4c3
chore: remove safari web extension test helper plist
Apr 22, 2026
e905aab
feat: add safari page request builders
Apr 22, 2026
2e7ef1e
feat: add safari extension entry to autofill settings
Apr 23, 2026
1d8dbf5
docs: add safari extension implementation roadmap
Apr 23, 2026
1f7e8a5
feat: modernize safari extension setup view
Apr 23, 2026
d150389
feat: expand safari extension request processing
Apr 23, 2026
9131a09
feat: improve safari extension matched login handling
Apr 23, 2026
e346e4e
feat: wire safari extension handler to live processor
Apr 23, 2026
785e559
feat: apply safari extension responses in page script
Apr 23, 2026
c964011
feat: surface safari extension status responses
Apr 23, 2026
1a52407
feat: show safari extension status banners on page
Apr 23, 2026
7434d5b
feat: fill generated passwords into confirmation fields
Apr 23, 2026
c916417
feat: polish safari extension setup experience
Apr 23, 2026
691b1d8
feat: surface safari extension action panels
Apr 23, 2026
32a5f0c
feat: refine safari extension action panel copy
Apr 23, 2026
122f0f2
feat: add dismiss control to safari action panels
Apr 23, 2026
924cad6
feat: add primary actions to safari action panels
Apr 23, 2026
0e78892
feat: tighten safari save and password update classification
Apr 23, 2026
24acc38
feat: improve safari change password field detection
Apr 23, 2026
aa8fbae
feat: improve safari save login field selection
Apr 23, 2026
59363e9
feat: detect safari page actions from form fields
Apr 23, 2026
7313557
feat: trigger safari requests from detected page actions
Apr 23, 2026
a4e347a
feat: wire safari panel primary actions to request flows
Apr 23, 2026
6f1251d
feat: add safari request context metadata
Apr 23, 2026
89d3822
feat: preserve safari request context across native bridge
Apr 23, 2026
de70481
feat: persist confirmed safari login actions
Apr 23, 2026
2506848
feat: follow generated passwords with safari save prompts
Apr 23, 2026
4e690ea
feat: surface safari fill no-match guidance
Apr 23, 2026
0fc3cab
feat: surface safari fill completion feedback
Apr 23, 2026
f9d6c25
feat: show safari pending feedback for vault actions
Apr 23, 2026
5d70f4d
feat: add safari status banner tones
Apr 23, 2026
7e267c4
feat: personalize safari fill completion copy
Apr 23, 2026
a3b6fa0
feat: tailor safari fill copy to matched site host
Apr 23, 2026
fc13f86
feat: prefer safari fill on standard login pages
Apr 23, 2026
5f6b825
feat: use live generator settings for safari passwords
Apr 23, 2026
366a15f
fix: avoid dummy safari password fallback
Apr 24, 2026
41d6fdf
feat: polish safari generated password feedback
Apr 24, 2026
cd780bb
fix: surface safari bridge errors in-page
Apr 24, 2026
27150d8
feat: polish safari settings call to action
Apr 24, 2026
5272497
feat: polish safari settings progress label
Apr 24, 2026
cf590e6
fix: shape safari native bridge errors
Apr 24, 2026
e51897a
feat: polish safari setup next step copy
Apr 24, 2026
99c6dbc
feat: refine safari setup progress copy
Apr 24, 2026
618e604
fix: guard safari action panel submissions
Apr 24, 2026
f8800da
feat: broaden safari signup detection
Apr 24, 2026
0f9158c
fix: tighten safari signup heuristics
Apr 24, 2026
fb4e76b
feat: use safari form context for signup detection
Apr 24, 2026
ce7f812
feat: structure safari action panel content
Apr 24, 2026
e818e93
feat: emit safari action dismiss events
Apr 24, 2026
bb2ef1d
feat: polish safari action panel styling
Apr 24, 2026
ff47cfd
feat: add safari action panel eyebrow copy
Apr 24, 2026
6e5768b
test: cover safari update-login panel copy
Apr 24, 2026
03572a0
feat: detect safari signup buttons
Apr 24, 2026
010f464
fix: scope safari signup button detection
Apr 24, 2026
2937151
feat: detect safari signup submit buttons
Apr 24, 2026
d2c0ceb
fix: scope safari signup signals by form
Apr 24, 2026
d8b3b5f
fix: scope safari signup signals for orphan fields
Apr 24, 2026
076998c
fix: treat safari reset flows as password changes
Apr 24, 2026
732561c
fix: allow safari reset-password update flows
Apr 24, 2026
dcd7070
feat: support safari hidden-email signup steps
Apr 24, 2026
f4cf770
fix: scope safari hidden-email signup fallback
Apr 24, 2026
4f67002
feat: support safari invitation password setup
Apr 24, 2026
34dcc59
fix: classify safari invitation password setup as save
Apr 24, 2026
3dcd4d5
fix: narrow safari account setup detection
Apr 24, 2026
7fc96cd
fix: support safari activate-account setup copy
Apr 24, 2026
cf72317
[verified] fix safari bridge failure feedback
Apr 25, 2026
d57260e
[verified] fix safari setup completion detection
Apr 25, 2026
748d4f1
[verified] refactor safari extension identifier
Apr 25, 2026
e599fbb
[verified] polish safari setup progress
Apr 25, 2026
14e626d
[verified] align safari setup cta copy
Apr 25, 2026
187627f
[verified] refine safari setup follow-up copy
Apr 25, 2026
10af2d4
[verified] align safari status copy
Apr 25, 2026
492ca49
[verified] add safari setup section headings
Apr 25, 2026
cb5950d
[verified] cover safari headings across states
Apr 25, 2026
09f6110
[verified] add safari status summary rows
Apr 25, 2026
052b779
[verified] add safari state-aware hero titles
Apr 25, 2026
a7bb6a6
[verified] simplify safari status card
Apr 25, 2026
b470a6e
[verified] refine safari next-step titles
Apr 25, 2026
cafc90f
[verified] refine safari next-step message
Apr 25, 2026
7952b45
[verified] align safari checklist copy
Apr 25, 2026
b5379c7
feat: polish safari extension setup and action panel
Apr 27, 2026
00e4f26
feat: model safari extension follow-up responses
Apr 27, 2026
6aaf749
feat: prefer native safari follow-up context
Apr 27, 2026
999a4c0
feat: derive native safari password follow-ups
Apr 27, 2026
0202d25
feat: refine safari generated password follow-ups
Apr 27, 2026
5f34af8
feat: refine safari save follow-up classification
Apr 27, 2026
30b1677
feat: normalize safari save follow-up urls
Apr 27, 2026
c802eaf
feat: prefer safari signup follow-up saves
Apr 27, 2026
af506f7
feat: use page surfaces for safari follow-ups
Apr 27, 2026
60b5907
feat: use page surfaces for password follow-ups
Apr 27, 2026
5e23899
feat: preserve safari setup progress state
Apr 29, 2026
03aac75
feat: clarify safari setup completion copy
Apr 29, 2026
c2509c4
feat: track safari setup button trigger
Apr 29, 2026
02ffad5
test: cover safari web extension bridge and handler
Apr 29, 2026
63ec06f
fix: stabilize safari action extension simulator flows
May 1, 2026
0d62464
test: add safari fixture live qa coverage
May 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .maestro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Maestro smoke flows for Safari extension fixtures

These flows are intentionally small and assume you are using the local fixture pages in `Docs/safari-extension-dev-fixtures/`.

## Prerequisites

1. Start the local fixture server:

```bash
cd ~/_dev/ios
python3 -m http.server 8123 -d Docs/safari-extension-dev-fixtures
```

2. Ensure the iOS simulator is booted.
3. Ensure Safari's first-run onboarding has already been dismissed on that simulator.
4. Install Maestro CLI according to the official docs:

```bash
curl -fsSL "https://get.maestro.mobile.dev" | bash
```

or

```bash
brew tap mobile-dev-inc/tap
brew install mobile-dev-inc/tap/maestro
```

## Run a smoke flow

```bash
maestro test .maestro/safari-signup-smoke.yaml
maestro test .maestro/safari-change-password-smoke.yaml
```

## Intended use

These are not full product regressions yet. They are a lightweight starting point for:
- opening the stable local fixture page
- proving simulator + Safari + CLI wiring works
- growing toward richer Safari extension E2E checks later
13 changes: 13 additions & 0 deletions .maestro/safari-change-password-smoke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
appId: com.apple.mobilesafari
---
- launchApp
- openLink: http://127.0.0.1:8123/change-password.html
- assertVisible: Update your password
- tapOn: Current password
- inputText: old-secret
- tapOn: New password
- inputText: new-secret-123
- tapOn: Confirm new password
- inputText: new-secret-123
- tapOn: Update password
- assertVisible: Change-password fixture intercepted submit locally.
13 changes: 13 additions & 0 deletions .maestro/safari-signup-smoke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
appId: com.apple.mobilesafari
---
- launchApp
- openLink: http://127.0.0.1:8123/signup.html
- assertVisible: Create your Bitwarden Safari test account
- tapOn: Email
- inputText: user@example.com
- tapOn: New password
- inputText: new-secret-123
- tapOn: Confirm new password
- inputText: new-secret-123
- tapOn: Create account
- assertVisible: Signup fixture intercepted submit locally for user@example.com.
31 changes: 28 additions & 3 deletions AuthenticatorBridgeKit/AuthenticatorBridgeDataStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,33 @@ public final nonisolated class AuthenticatorBridgeDataStore: @unchecked Sendable
return managedObjectModel
}()

static func persistedStoreURL(
fileManager: FileManager = .default,
groupIdentifier: String,
bundleIdentifier: String? = Bundle.main.bundleIdentifier,
bundlePath: String = Bundle.main.bundlePath,
containerURLProvider: (FileManager, String) -> URL? = { fileManager, groupIdentifier in
fileManager.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)
}
) -> URL {
#if targetEnvironment(simulator)
if bundlePath.contains(".appex") {
let applicationSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
?? fileManager.temporaryDirectory
let directoryURL = applicationSupportURL
.appendingPathComponent(bundleIdentifier ?? "AuthenticatorBridgeExtension", isDirectory: true)
try? fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true)
return directoryURL.appendingPathComponent("\(authenticatorBridgeModelName).sqlite")
}
#endif

if let containerURL = containerURLProvider(fileManager, groupIdentifier) {
return containerURL.appendingPathComponent("\(authenticatorBridgeModelName).sqlite")
}

return fileManager.temporaryDirectory.appendingPathComponent("\(authenticatorBridgeModelName).sqlite")
}

// MARK: Properties

/// A thread-safe lock for `backgroundContext`. Once we have a minimum of iOS 16, we can use an
Expand Down Expand Up @@ -99,9 +126,7 @@ public final nonisolated class AuthenticatorBridgeDataStore: @unchecked Sendable
case .memory:
storeDescription = NSPersistentStoreDescription(url: URL(fileURLWithPath: "/dev/null"))
case .persisted:
let storeURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)!
.appendingPathComponent("\(authenticatorBridgeModelName).sqlite")
let storeURL = Self.persistedStoreURL(groupIdentifier: groupIdentifier)
storeDescription = NSPersistentStoreDescription(url: storeURL)
}
persistentContainer.persistentStoreDescriptions = [storeDescription]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import XCTest

@testable import AuthenticatorBridgeKit

final class AuthenticatorBridgeDataStoreTests: XCTestCase {
func test_persistedStoreURL_prefersAppGroupContainer() {
let subject = AuthenticatorBridgeDataStore.persistedStoreURL(
groupIdentifier: "group.com.8bit.bitwarden",
bundleIdentifier: "com.8bit.bitwarden",
bundlePath: "/tmp/Bitwarden.app",
containerURLProvider: { _, _ in URL(fileURLWithPath: "/tmp/group-container", isDirectory: true) }
)

XCTAssertEqual(subject.path, "/tmp/group-container/Bitwarden-Authenticator.sqlite")
}

func test_persistedStoreURL_fallsBackForSimulatorAppExtension() {
let fileManager = FileManager.default
let subject = AuthenticatorBridgeDataStore.persistedStoreURL(
fileManager: fileManager,
groupIdentifier: "group.com.8bit.bitwarden",
bundleIdentifier: "com.8bit.bitwarden.find-login-action-extension",
bundlePath: "/tmp/BitwardenActionExtension.appex",
containerURLProvider: { _, _ in nil }
)

let applicationSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
?? fileManager.temporaryDirectory
XCTAssertEqual(
subject.path,
applicationSupportURL
.appendingPathComponent("com.8bit.bitwarden.find-login-action-extension", isDirectory: true)
.appendingPathComponent("Bitwarden-Authenticator.sqlite")
.path
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ protocol StateService: AnyObject {

/// The errors thrown from a `StateService`.
///
enum StateServiceError: Error {
enum StateServiceError: Error, NonLoggableError {
/// There are no known accounts.
case noAccounts

Expand Down
22 changes: 22 additions & 0 deletions Bitwarden/Application/TestHelpers/Support/UITests-Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
Loading
Loading