-
Notifications
You must be signed in to change notification settings - Fork 59
Expand file tree
/
Copy pathPrivacyFeature.swift
More file actions
722 lines (571 loc) · 22.7 KB
/
PrivacyFeature.swift
File metadata and controls
722 lines (571 loc) · 22.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
//
// PrivacyFeature.swift
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Features whose `rawValue` should be the key to access their corresponding `PrivacyConfigurationData.PrivacyFeature` object
public enum PrivacyFeature: String {
case contentBlocking
case duckPlayer
case fingerprintingTemporaryStorage
case fingerprintingBattery
case fingerprintingScreenSize
case fingerprintingCanvas
case gpc
case httpsUpgrade = "https"
case autoconsent
case clickToLoad
case autofill
case autofillBreakageReporter
case ampLinks
case trackingParameters
case customUserAgent
case referrer
case adClickAttribution
case windowsWaitlist
case windowsDownloadLink
case incontextSignup
case newTabContinueSetUp
case newTabSearchField
case dbp
case sync
case privacyDashboard
case updates
case privacyPro
case sslCertificates
case toggleReports
case maliciousSiteProtection
case brokenSitePrompt
case remoteMessaging
case additionalCampaignPixelParams
case syncPromotion
case autofillSurveys
case marketplaceAdPostback
case networkProtection
case aiChat
case contextualOnboarding
case textZoom
case adAttributionReporting
case forceOldAppDelegate
case htmlHistoryPage
case tabManager
case tabSuspension
case tabSwitcherTrackerCount
case webViewStateRestoration
case experimentalTheming
case setAsDefaultAndAddToDock
case contentScopeExperiments
case extendedOnboarding
case macOSBrowserConfig
case iOSBrowserConfig
// Demonstrative case for default value. Remove once a real-world feature is added
case intentionallyLocalOnlyFeatureForTests
case delayedWebviewPresentation
case disableFireAnimation
case htmlNewTabPage
case daxEasterEggLogos
case daxEasterEggPermanentLogo
case openFireWindowByDefault
case attributedMetrics
case dataImport
case duckAiChatHistory
case serp
case popupBlocking
case pageContext
case webExtensions
case forceDarkModeOnWebsites
case promoQueue
case adBlockingExtension
}
/// An abstraction to be implemented by any "subfeature" of a given `PrivacyConfiguration` feature.
/// The `rawValue` should be the key to access their corresponding `PrivacyConfigurationData.PrivacyFeature.Feature` object
/// `parent` corresponds to the top level feature under which these subfeatures can be accessed
public protocol PrivacySubfeature: RawRepresentable where RawValue == String {
var parent: PrivacyFeature { get }
}
// MARK: Subfeature definitions
public enum MacOSBrowserConfigSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.macOSBrowserConfig
}
// Demonstrative case for default value. Remove once a real-world feature is added
case intentionallyLocalOnlySubfeatureForTests
/// https://app.asana.com/1/137249556945/project/1206580121312550/task/1209808389662317?focus=true
case willSoonDropBigSurSupport
/// Hang reporting feature flag
case hangReporting
/// https://app.asana.com/1/137249556945/project/72649045549333/task/1211260578559159?focus=true
case unifiedURLPredictor
// Gradual rollout for new Fire dialog replacing the legacy popover
// https://app.asana.com/1/137249556945/project/72649045549333/task/1210417832822045
case fireDialog
// Controls visibility of the "Manage individual sites" link in the Fire dialog
case fireDialogIndividualSitesLink
/// Use WKDownload for favicon fetching to bypass App Transport Security restrictions on HTTP URLs
case faviconWKDownload
/// New App Store Update flow feature flag
/// https://app.asana.com/1/137249556945/project/1199230911884351/task/1211563301906360?focus=true
case appStoreUpdateFlow
/// Hide manual update option and always use automatic updates
case automaticUpdatesOnly
/// Warn before quit confirmation overlay
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1212444166689969
case warnBeforeQuit
/// Feature flag for a macOS Tahoe fix only
/// https://app.asana.com/1/137249556945/project/1204006570077678/task/1211448334620171?focus=true
case blurryAddressBarTahoeFix
/// Feature Flag for the First Time Quit Survey
/// https://app.asana.com/1/137249556945/inbox/1203972458584425/item/1212200919350194/story/1212483080081687
case firstTimeQuitSurvey
/// Web Notifications API polyfill - allows websites to show notifications via native macOS Notification Center
/// https://app.asana.com/1/137249556945/project/414235014887631/task/1211395954816928?focus=true
case webNotifications
/// Whether the wide event POST endpoint is enabled
/// https://app.asana.com/1/137249556945/project/1199333091098016/task/1212738953909168?focus=true
case wideEventPostEndpoint
/// Memory Pressure Reporter
/// https://app.asana.com/1/137249556945/project/1201048563534612/task/1212762049862427?focus=true
case memoryPressureReporting
/// Memory Usage Reporting
/// https://app.asana.com/1/137249556945/project/72649045549333/task/1212762049862432?focus=true
case memoryUsageReporting
/// Failsafe flag for disabling call stack tree depth limiting in crash collector
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1213037858764817
case crashCollectionLimitCallStackTreeDepth
/// Ctrl+click compatibility fix to preserve right click behavior
case controlClickFix
/// Enable Look Up (three-finger click) while keeping link preview disabled
case webViewLookUpAction
/// Enables showing browsing history domains in the first-time quit survey
case websitesHistoryFirstTimeQuitSurvey
case semaphoreAlwaysVisible
/// Autoplay policy control via WKWebpagePreferences
case autoplayPolicy
case tabAnimations
/// Enables lazy reload for the more options menu
case lazyMenuRebuild
case addToDockAppStore
case screenTimeCleaning
}
public enum iOSBrowserConfigSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.iOSBrowserConfig
}
// Demonstrative case for default value. Remove once a real-world feature is added
case intentionallyLocalOnlySubfeatureForTests
case widgetReporting
// Local inactivity provisional notifications delivered to Notification Center.
// https://app.asana.com/1/137249556945/project/72649045549333/task/1211003501974970?focus=true
case inactivityNotification
/// https://app.asana.com/1/137249556945/project/715106103902962/task/1210997282929955?focus=true
case unifiedURLPredictor
/// https://app.asana.com/1/137249556945/project/72649045549333/task/1211660503405838?focus=true
case forgetAllInSettings
/// https://app.asana.com/1/137249556945/project/481882893211075/task/1212057154681076?focus=true
case productTelemetrySurfaceUsage
/// https://app.asana.com/1/137249556945/project/414709148257752/task/1212395110448661?focus=true
case appRatingPrompt
/// https://app.asana.com/1/137249556945/project/1206329551987282/task/1212238464901412?focus=true
case showWhatsNewPromptOnDemand
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1212875994217788?focus=true
case genericBackgroundTask
/// Whether the wide event POST endpoint is enabled
/// https://app.asana.com/1/137249556945/project/1199333091098016/task/1212738953909168?focus=true
case wideEventPostEndpoint
/// Failsafe flag for disabling call stack tree depth limiting in crash collector
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1213037858764805
case crashCollectionLimitCallStackTreeDepth
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1212835969125260
case browsingMenuSheetEnabledByDefault
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1213336304802675
case showNTPAfterIdleReturn
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1213557229772465?focus=true
case autoplayBlocking
case crashReportOptInStatusResetting
case fireproofingETLDPlus1
case screenTimeCleaning
case minimalChromeInLandscape
/// https://app.asana.com/1/137249556945/project/1206329551987282/task/1211806114021630?focus=true
case onboardingRebranding
/// https://app.asana.com/1/137249556945/task/1213314048601761
case fireMode
/// https://app.asana.com/1/137249556945/project/1211834678943996/task/1213965646075290
case fireButtonRefinements
/// https://app.asana.com/1/137249556945/project/715106103902962/task/1212810377867736
case filterAddressBarUpdates
}
public enum TabManagerSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.tabManager
}
case multiSelection
}
public enum AutofillSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.autofill
}
case credentialsAutofill
case credentialsSaving
case inlineIconCredentials
case accessCredentialManagement
case autofillPasswordGeneration
case onByDefault
case onForExistingUsers
case unknownUsernameCategorization
case credentialsImportPromotionForExistingUsers
case partialFormSaves
case autofillCreditCards
case autofillCreditCardsOnByDefault
case passwordVariantCategorization
case autocompleteAttributeSupport
case inputFocusApi
case canPromoteImportPasswordsInPasswordManagement
case canPromoteImportPasswordsInBrowser
case createFireproofFaviconUpdaterSecureVaultInBackground
case autofillExtensionSettings
case canPromoteAutofillExtensionInBrowser
case canPromoteAutofillExtensionInPasswordManagement
case migrateKeychainAccessibility
case autofillPasswordSearchPrioritizeDomain
case onboardingExperiment
case autofillPasswordsStatusBar
}
public enum DBPSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature {
.dbp
}
case waitlist
case waitlistBetaActive
case freemium
case remoteBrokerDelivery
case emailConfirmationDecoupling
case foregroundRunningOnAppActive
case foregroundRunningWhenDashboardOpen
case clickActionDelayReductionOptimization
case continuedProcessing
case pirRollout
case goToMarket
case webViewUserAgent
}
public enum AIChatSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature {
.aiChat
}
/// Displays the AI Chat icon in the iOS browsing menu toolbar.
case browsingToolbarShortcut
/// Displays the AI Chat icon in the iOS address bar while on a SERP.
case addressBarShortcut
/// Web and native integration for opening AI Chat in a custom webview.
case deepLink
/// Keep AI Chat session after the user closes it
case keepSession
/// Adds capability to load AI Chat in a sidebar
case sidebar
/// Experimental address bar with duck.ai
case experimentalAddressBar
/// Global switch to disable all AI Chat related functionality
case globalToggle
/// Adds support for passing currently visible website context to the sidebar
case pageContext
/// Enables updated AI features settings screen
case aiFeaturesSettingsUpdate
/// Show AI Chat address bar choice screen
case showAIChatAddressBarChoiceScreen
/// Adds toggle for controlling 'Ask Follow-Up Questions' setting.
case serpSettingsFollowUpQuestions
/// Rollout feature flag for entry point improvements
case improvements
/// Allows user to clear AI Chat history with the fire button or auto-clear
case clearAIChatHistory
/// Signals that the iOS app should display duck.ai chats in "full mode" i.e in a tab, not a sheet
case fullDuckAIMode
/// Enables native-side support for standalone migration flows in AI Chat
case standaloneMigration
/// Allows to present Search Experience choice screen during onboarding
case onboardingSearchExperience
/// Enables Duck.ai query experiment during onboarding
case onboardingDuckAIQueryExperiment
/// Enables the omnibar toggle for AI Chat
case omnibarToggle
/// Enables the omnibar onboarding for AI Chat
case omnibarOnboarding
/// Enables the omnibar cluster for AI Chat
case omnibarCluster
/// Enables the omnibar tools (customize, search toggle, image upload) for AI Chat
case omnibarTools
/// Enables the default omnibar toggle position setting for AI Chat
case omnibarDefaultPosition
/// Controls showing the Hide AI section in Settings -> AI Features
case showHideAiGeneratedImages
case unifiedToggleInput
/// Signals that the iOS app should display duck.ai chats in "contextual mode" when opened from specific entry points
case contextualDuckAIMode
/// Controls whether automatic page context attachment defaults to enabled
case autoAttachContextByDefault
/// Signals that the iPad app should display duck.ai chats in a tab instead of a sheet
case iPadDuckaiOnTab
/// Signals that the iPad app should display the duck.ai toggle
case iPadAIChatToggle
/// Controls deletion of Synced chats
case supportsSyncChatsDeletion
case sidebarResizable
case sidebarFloating
/// Enables recent AI chats on the New Tab Page omnibar
case ntpRecentChats
/// Enables the "View all chats" button on the New Tab Page omnibar
case ntpViewAllChats
/// Enables AI chat tools (model selector, image upload) on the New Tab Page omnibar
case ntpChatTools
/// Enables support for adding multiple page contexts to a single chat session
case multiplePageContexts
/// Enables attaching content from multiple open tabs to Duck.ai chat
case attachMoreTabs
/// Enables page context feature on iPad
case iPadPageContext
/// Enables voice chat shortcut in the focused address bar
case voiceShortcut
/// Enables improved contextual sheet UX (welcome message, ask about page, etc.)
case contextualSheetImprovements
/// Enables removing individual AI chat suggestions
case removeSuggestion
/// Enables the fire button in the contextual AI chat sheet
case contextualFireButton
/// Enables the Duck.ai top-level main menu shortcut (macOS only)
case mainMenuShortcut
/// Enables the Duck.ai submenu in the more options (hamburger) menu (macOS only)
case moreOptionsMenuShortcut
/// Enables native-side storage for AI Chat (settings, chats, files)
case nativeStorage
/// Prevents about: scheme navigations (e.g. about:srcdoc) from opening new tabs in the sidebar
case sidebarAboutSchemeNavigationFix
/// Enabled 'View all chats' for Duck.ai in the omnibar
case viewAllChatsNativeOmnibar
/// Enables image generation mode toggle in the Duck.ai omnibar
case omnibarImageGeneration
/// Enables web search tool in the Duck.ai omnibar
case omnibarWebSearch
/// Enables querying AI Chat data directly from local storage instead of via webview
case nativeDataAccess
}
public enum HtmlNewTabPageSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature {
.htmlNewTabPage
}
/// Global switch to disable New Tab Page search box
case omnibar
/// Global switch to control managing state of NTP in frontend using tab IDs
case newTabPageTabIDs
/// Controls whether the Next Steps List widget is enabled on New Tab Page
case nextStepsListWidget
}
public enum NetworkProtectionSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature {
.networkProtection
}
/// App Exclusions for the VPN
/// https://app.asana.com/0/1206580121312550/1209150117333883/f
case appExclusions
/// App Store System Extension support
/// https://app.asana.com/0/0/1209402073283584
case appStoreSystemExtension
/// App Store System Extension Update Message support
/// https://app.asana.com/0/1203108348835387/1209710972679271/f
case appStoreSystemExtensionMessage
/// Display user tips for Network Protection
/// https://app.asana.com/0/72649045549333/1208231259093710/f
case userTips
/// Enforce routes for the VPN to fix TunnelVision
/// https://app.asana.com/0/72649045549333/1208617860225199/f
case enforceRoutes
/// Risky Domain Protection for VPN
/// https://app.asana.com/0/1204186595873227/1206489252288889
case riskyDomainsProtection
/// Connection failure loop detection for VPN
/// https://app.asana.com/1/137249556945/project/1207603085593419/task/1213755794484487?focus=true
case connectionFailureLoopDetection
}
public enum SyncSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.sync
}
case level0ShowSync
case level1AllowDataSyncing
case level2AllowSetupFlows
case level3AllowCreateAccount
case seamlessAccountSwitching
case exchangeKeysToSyncWithAnotherDevice
case canScanUrlBasedSyncSetupBarcodes
case canInterceptSyncSetupUrls
case syncSetupBarcodeIsUrlBased
case refactorOfSyncPreferences
case newSyncEntryPoints
case newDeviceSyncPrompt
case syncAutoRestore
case syncCreditCards
case syncIdentities
case aiChatSync
case simplifiedSyncSetupExperiment
}
public enum AutoconsentSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.autoconsent
}
case onByDefault
case filterlist
case heuristicAction
}
public enum PrivacyProSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature { .privacyPro }
case allowPurchase
case allowPurchaseStripe
case useUnifiedFeedback
case privacyProOnboardingPromotion
case paidAIChat
case supportsAlternateStripePaymentFlow
case winBackOffer
case vpnMenuItem
case blackFridayCampaign
case allowProTierPurchase
case freeTrialConversionWideEvent
case subscriptionPromoForReinstallers
}
public enum DuckPlayerSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .duckPlayer }
case pip
case autoplay
case openInNewTab
case customError
case enableDuckPlayer // iOS DuckPlayer rollout feature
case nativeUI // Use Duckplayer's native UI
}
public enum SyncPromotionSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .syncPromotion }
case bookmarks
case passwords
}
public enum HTMLHistoryPageSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature { .htmlHistoryPage }
case isLaunched
}
public enum TabSuspensionSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature { .tabSuspension }
case memoryPressureTrigger
}
public enum ContentBlockingSubfeature: String, Equatable, PrivacySubfeature {
public var parent: PrivacyFeature { .contentBlocking }
case tdsNextExperimentBaseline
case tdsNextExperimentFeb25
case tdsNextExperimentMar25
case tdsNextExperimentApr25
case tdsNextExperimentMay25
case tdsNextExperimentJun25
case tdsNextExperimentJul25
case tdsNextExperimentAug25
case tdsNextExperimentSep25
case tdsNextExperimentOct25
case tdsNextExperimentNov25
case tdsNextExperimentDec25
case tdsNextExperiment001
case tdsNextExperiment002
case tdsNextExperiment003
case tdsNextExperiment004
case tdsNextExperiment005
case tdsNextExperiment006
case tdsNextExperiment007
case tdsNextExperiment008
case tdsNextExperiment009
case tdsNextExperiment010
case tdsNextExperiment011
case tdsNextExperiment012
case tdsNextExperiment013
case tdsNextExperiment014
case tdsNextExperiment015
case tdsNextExperiment016
case tdsNextExperiment017
case tdsNextExperiment018
case tdsNextExperiment019
case tdsNextExperiment020
case tdsNextExperiment021
case tdsNextExperiment022
case tdsNextExperiment023
case tdsNextExperiment024
case tdsNextExperiment025
case tdsNextExperiment026
case tdsNextExperiment027
case tdsNextExperiment028
case tdsNextExperiment029
case tdsNextExperiment030
}
public enum MaliciousSiteProtectionSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .maliciousSiteProtection }
case onByDefault // Rollout feature
case scamProtection
}
public enum OnboardingSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .extendedOnboarding }
case showSettingsCompleteSetupSection
}
public enum ExperimentalThemingSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .experimentalTheming }
case visualUpdates // Rollout
}
public enum AttributedMetricsSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .attributedMetrics }
case emitAllMetrics
case retention
case canEmitRetention
case searchDaysAvg
case canEmitSearchDaysAvg
case searchCountAvg
case canEmitSearchCountAvg
case adClickCountAvg
case canEmitAdClickCountAvg
case aiUsageAvg
case canEmitAIUsageAvg
case subscriptionRetention
case canEmitSubscriptionRetention
case syncDevices
case canEmitSyncDevices
case sendOriginParam
}
public enum DataImportSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .dataImport }
case newSafariFilePicker
case newDataImportExperience
case dataImportSummarySyncPromotion
}
public enum SERPSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.serp
}
/// Global switch to disable New Tab Page search box
case storeSerpSettings
}
public enum PopupBlockingSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature {
.popupBlocking
}
case createWebViewGatingFailsafe
}
public enum WebExtensionsSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .webExtensions }
case embeddedExtension = "embedded"
case embeddedRollout
}
public enum ForceDarkModeOnWebsitesSubfeature: String, PrivacySubfeature {
public var parent: PrivacyFeature { .forceDarkModeOnWebsites }
case featureRollout
}