Skip to content

Conversation

@tomasstrba
Copy link
Contributor

@tomasstrba tomasstrba commented Nov 29, 2025

Task/Issue URL: https://app.asana.com/1/137249556945/project/1148564399326804/task/1212226055102691?focus=true

Description

This PR adds a new permission authorization flow and basic functionality of new permission center, which unblocks the notifications API work.

Unit tests, UI tests and support of popups will be added in a follow up PR.

Testing Steps

Test external schemes

"Always Allow" Flow

  1. Visit https://www.w3schools.com/html/tryit.asp?filename=tryhtml_links_mailto_subject
  2. Click on "Send email"
  3. Verify the new authorization popup appeared
  4. Click on "Always Allow" and make sure the Mail app is open
  5. Open a new tab with https://www.w3schools.com/html/tryit.asp?filename=tryhtml_links_mailto_subject again
  6. Click on "Send email"
  7. Verify the Mail is open without a permission dialog

"Never Allow" Flow

  1. Run Fire button to erase permissions
  2. Visit https://www.w3schools.com/html/tryit.asp?filename=tryhtml_links_mailto_subject
  3. Click on "Send email"
  4. Verify the new authorization popup appeared
  5. Click on "Never Allow" and make sure no third-party app is open
  6. Verify this again on a new tab

Test microphone

"Always Allow" Flow

  1. Reset system permissions by executing tccutil reset Microphone com.duckduckgo.macos.browser.debug in Terminal
  2. Visit https://permission.site/
  3. Click on "Microphone"
  4. Verify the system permission dialog is open and click on "Allow"
  5. After that, verify the new authorization popup appeared
  6. Click on "Always Allow"
  7. Make sure the microphone access has been granted
  8. Open https://permission.site/ on another tab
  9. Click on "Microphone"
  10. Verify the microphone access has been granted without any popup

"Never Allow" Flow

  1. Run Fire button to erase permissions
  2. Visit https://permission.site/
  3. Click on "Microphone"
  4. Verify the new authorization popup appeared
  5. Click on "Never Allow"
  6. Make sure microphone access has not been granted
  7. Open https://permission.site/ on another tab
  8. Click on "Microphone"
  9. Verify the microphone access has not been granted

"Deny" System Permission Flow

  1. Run Fire button to erase permissions
  2. Quit the app
  3. Reset system permissions by executing tccutil reset Microphone com.duckduckgo.macos.browser.debug in Terminal
  4. Run the app
  5. Visit https://permission.site/
  6. Click on "Microphone"
  7. Verify the system permission dialog is open and click on "Deny"
  8. Make sure microphone access has not been granted
    Note: Unfortunately, there is no callback from webview in case this happens, so we cannot trigger an info dialog if system permission is disabled for now. This applies to current behavior of the app as well. Investigation to address this will be scoped as a follow-up.

Test camera

Apply the same set of tests as microphone
To reset system permissions, execute tccutil reset Camera com.duckduckgo.macos.browser.debug

Test geolocation

Unfortunately, I haven't found an easy reliable way to to clear geolocation system permissions. Let's use a unique bundle id for each test case.

"Always Allow" Flow

  1. Change the bundle id in DeveloperID.xcconfig for example: MAIN_BUNDLE_IDENTIFIER[config=Debug][sdk=*] = $(MAIN_BUNDLE_IDENTIFIER_PREFIX).debugh
  2. Run the app
  3. Debug -> Skip Onboarding
  4. Debug -> Set Internal User State
  5. Turn on newPermissionView feature flag
  6. Visit https://permission.site
  7. Click on "Location"
  8. Observe the two step authorization popover
  9. Click on "Enable System Permissions"
  10. Verify the system permission dialog is shown
  11. Click on "Allow"
  12. Make sure the first step in authorization popover is marked as complete
  13. Click on "Always Allow"
  14. Verify the geolocation permissions are granted
  15. Open https://permission.site/ on another tab
  16. Click on "Location"
  17. Verify the location access has been granted without any popup
  18. Use Fire button to clear permissions
  19. Visit https://permission.site
  20. Click on "Location"
  21. Verify the new authorization popup appeared
  22. Click on "Always Allow"
  23. Make sure the location access has been granted

"Always Deny" Flow

  1. Use Fire button to clear permissions
  2. Visit https://permission.site
  3. Click on "Location"
  4. Verify the new authorization popup appeared
  5. Click on "Never Allow"
  6. Make sure the location access has not been granted

"Deny" System Permission Flow

  1. Change the bundle id in DeveloperID.xcconfig for example: MAIN_BUNDLE_IDENTIFIER[config=Debug][sdk=*] = $(MAIN_BUNDLE_IDENTIFIER_PREFIX).debugj
  2. Run the app
  3. Debug -> Skip Onboarding
  4. Debug -> Set Internal User State
  5. Turn on newPermissionView feature flag
  6. Visit https://permission.site
  7. Click on "Location"
  8. Observe the two step authorization popover
  9. Click on "Enable System Permissions"
  10. Verify the system permission dialog is shown
  11. Click on "Deny"
  12. Make sure the authorization popover is now communicating that the system permissions are disabled and linking user to System Settings

Test permission center

Test clearing permission

  1. Use Fire button to clear permissions
  2. Visit https://permission.site
  3. Click on "Microphone"
  4. In authorization popup, click on "Always Allow"
  5. Verify the permission was granted ("Microphone" button is green)
  6. Open permission center
  7. Verify the Microphone permission is in the list
  8. Click on 'x'
  9. Verify the permission was removed from the list and revoked on the website

Test changing permission
3. Click on "Camera"
4. In authorization popup, click on "Always Deny"
5. Verify the permission was not granted ("Camera" button is red)
6. Open permission center
7. Verify the Camera permission is in the list
8. Change to "Always Allow"
9. Open https://permission.site on another tab
10. Click on "Camera"
11. Verify the permission was automatically granted

Impact and Risks

What could go wrong?

Changes are hidden behind the feature flag, but current permission functionality is still at the risk


Internal references:

Definition of Done | Engineering Expectations | Tech Design Template


Note

Introduces a new SwiftUI permission authorization flow and a Permission Center popover (gated by feature flag), adds two-step geolocation handling, and refactors permission logic to support removable/persisted decisions.

  • Permissions UI (feature-flagged newPermissionView):
    • Add PermissionAuthorizationSwiftUIView and integrate into PermissionAuthorizationViewController/PermissionAuthorizationPopover.
    • Add PermissionCenterView, PermissionCenterViewModel, and PermissionCenterPopover; surface via new toolbar button in AddressBarButtonsViewController.
    • Add two-step geolocation UI/flow (enable system, then site decision).
  • Permission logic:
    • New SystemPermissionManager to query/request system-level auth (geolocation).
    • Update PermissionManager to accept FeatureFlagger, publish changes, and support removePermission.
    • Update PermissionModel to take FeatureFlagger, respect flag-dependent persistence, and support remove(_:).
    • Make PermissionType.canPersistGranted/DeniedDecision depend on feature flag; expose helpers for system-permission UI.
    • Pass featureFlagger through AppDelegate and Tab to permission components.
  • Address bar integration:
    • Show Permission Center button and popover; route approval popovers through new flow when enabled.
    • Adjust legacy buttons visibility logic and context menu to use flag-dependent persistence.
  • Privacy Dashboard:
    • Skip permission options when new view is enabled.
  • Localization/Styling:
    • Add strings for new permission prompts/center; add permissionWarningBackground color.
  • Tests/Project:
    • Add PermissionAuthorizationTypeTests; update existing permission tests and project files to include new sources.

Written by Cursor Bugbot for commit 13003ad. This will update automatically on new commits. Configure here.

@tomasstrba tomasstrba force-pushed the tom/new-permission-view-authorization branch from f0bec18 to fbab23a Compare November 29, 2025 08:56
Copy link
Contributor

@diegoreymendez diegoreymendez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tomasstrba - I found an issue while testing. See the video.

Also it seems to me that removing 1 row from the new panel reloads the page and removes all rows.

Screen.Recording.2025-12-02.at.15.12.38.mov

@diegoreymendez diegoreymendez self-requested a review December 2, 2025 14:35
Copy link
Contributor

@diegoreymendez diegoreymendez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few more comments.

@diegoreymendez diegoreymendez self-requested a review December 2, 2025 15:06
@tomasstrba
Copy link
Contributor Author

tomasstrba commented Dec 2, 2025

@diegoreymendez, thank you for all comments, they were all very useful and I addressed them.

To the main issue you mentioned above: I implemented reloading of website after removing a permission on purpose, because revoking the permission in webview didn't seem to work. Unfortunately, as a side effect, if there were any other permissions, those were dropped too.

I've investigated it deeper and found out that the testing website doesn't communicate the permission correctly. After clearing the permission now, you can observe the system icon from status bar disappears, but the permission stays green on website
Screenshot 2025-12-03 at 09 57 37

The PR is ready for another look

@diegoreymendez
Copy link
Contributor

diegoreymendez commented Dec 3, 2025

I implemented reloading of website after removing a permission on purpose, because revoking the permission in webview didn't seem to work

The issue is the UX is breaking the "visual contract" - the UI is saying users can remove permissions individually but they can't.

Is there a task where this is discussed / documented? If not, can you open one?

Once a task is up we can move forward here.

@tomasstrba
Copy link
Contributor Author

@diegoreymendez, apologies if I wasn't clear enough. I've removed the tab reloading and now users can remove permissions individually.

@diegoreymendez
Copy link
Contributor

Documenting our DM conversation: removing the reload is the right call, the issue is just that the site isn't refreshing properly, but that's on the site and it looks the same in Safari and Chrome.

Will be testing and hope to approve soon.

@diegoreymendez diegoreymendez self-requested a review December 3, 2025 10:14
@diegoreymendez
Copy link
Contributor

Hey @tomasstrba - one more issue: when the feature is enabled, if the app is open and running, I can easily get a blank icon on the address bar. People using the app when the flag becomes enabled are likely to hit this case.

Screenshot 2025-12-03 at 11 26 21

@tomasstrba
Copy link
Contributor Author

@diegoreymendez, if possible I'd address this issue in a follow-up PR. The new branch already contains some changes to presenting privacy center, which is more robust. Would that work for you?

Copy link
Contributor

@diegoreymendez diegoreymendez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving.

@tomasstrba - can we ref the follow-up task here for future context?

@tomasstrba
Copy link
Contributor Author

@diegoreymendez, I've created the task here -> Address empty permission center button empty state issue

Thank you for a great review!

@tomasstrba tomasstrba merged commit ad0dbbb into main Dec 3, 2025
51 checks passed
@tomasstrba tomasstrba deleted the tom/new-permission-view-authorization branch December 3, 2025 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants