Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5fe8c68
Unlock by biometrics private tabs feature
DarkSatyr Feb 25, 2026
0e381b9
added settings
DarkSatyr Feb 25, 2026
a3fcfde
moved to new source of truth BrowserViewControllerState
DarkSatyr Mar 4, 2026
44f7609
added complete event logic, handling and processing
DarkSatyr Mar 6, 2026
9bd0e79
refactored state and middleware, moved orchestration into middleware …
DarkSatyr Mar 7, 2026
3eccf3d
fixed code alignment
DarkSatyr Mar 7, 2026
ca69c3b
fixed bugs with layer on background event
DarkSatyr Mar 7, 2026
98adb6b
fixed bugs, lock screen improvement
DarkSatyr Mar 7, 2026
4c7b7b0
removed extra code
DarkSatyr Mar 7, 2026
12261ab
fixed bug with setting change, fixed retry button styling
DarkSatyr Mar 7, 2026
5908abe
fixed 1 unit test
DarkSatyr Mar 7, 2026
f8d4e71
returned back version number
DarkSatyr Mar 7, 2026
ed9a933
fixed proj file
DarkSatyr Mar 7, 2026
7c92ad3
added feature flag privateTabsLockFeature and made use of it
DarkSatyr Mar 11, 2026
e447743
fixed errors with feature name
DarkSatyr Mar 11, 2026
cd8cab7
added auto biometry request prompt on view appearance
DarkSatyr Mar 11, 2026
0b0e6f9
renamed PrivateLockActionType.setTrayDisplayContextAndPanelType to Pr…
DarkSatyr Mar 13, 2026
c326d3a
renamed cases for PrivateLockActionType enum to match firefox guidelines
DarkSatyr Mar 13, 2026
426916c
renamed cases for PrivateLockMiddlewareActionType enum to match firef…
DarkSatyr Mar 13, 2026
907be36
combined separated guards into one, added explanation comment
DarkSatyr Mar 13, 2026
a5bdd56
renamed auth(reason:windowUUID:) to startPrivateTabsAuthFlow(reason:w…
DarkSatyr Mar 13, 2026
925f807
moved PrivateLockDomainState and all related enums to separate file
DarkSatyr Mar 13, 2026
c2b128d
removed PrivateTabsLockFeatureGate, changed feature flags check to .b…
DarkSatyr Mar 13, 2026
2415333
Merge branch 'main' into feature-lock-private-tabs
DarkSatyr Mar 13, 2026
4e539ab
reverted proj file dev team changes
DarkSatyr Mar 14, 2026
8d972e9
replaced static funcs unlock, lock with instance funcs
DarkSatyr Mar 14, 2026
2607bbb
pr review fixes, guards, strings, styling
DarkSatyr Mar 14, 2026
74ec4d9
lint style fixes
DarkSatyr Mar 14, 2026
8d1cfa9
Merge branch 'main' into feature-lock-private-tabs
DarkSatyr Mar 16, 2026
ab1c6a2
Merge branch 'main' into feature-lock-private-tabs
DarkSatyr Mar 18, 2026
119801e
Merge branch 'main' into feature-lock-private-tabs
DarkSatyr Mar 20, 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
1 change: 1 addition & 0 deletions BrowserKit/Sources/Shared/Prefs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ public struct PrefsKeys {
// Firefox settings
public struct Settings {
public static let closePrivateTabs = "ClosePrivateTabs"
public static let lockPrivateTabs = "LockPrivateTabs"
public static let sentFromFirefoxWhatsApp = "SentFromFirefoxWhatsApp"
public static let navigationToolbarMiddleButton = "settings.navigationToolbarMiddleButton"
public static let translationsFeature = "settings.translationFeature"
Expand Down
28 changes: 28 additions & 0 deletions firefox-ios/Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,11 @@
74B195441CF503FC007F36EF /* RecentlyClosedTabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B195431CF503FC007F36EF /* RecentlyClosedTabs.swift */; };
74E36D781B71323500D69DA1 /* SettingsContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E36D771B71323500D69DA1 /* SettingsContentViewController.swift */; };
74F80D342A0A52D700013C3D /* PrivacyPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F80D332A0A52D700013C3D /* PrivacyPolicyViewController.swift */; };
767742A92F4F649500DEC06D /* PrivateLockAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767742A82F4F649500DEC06D /* PrivateLockAction.swift */; };
767742AB2F4F6B4B00DEC06D /* PrivateTabsLockOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767742AA2F4F6B4B00DEC06D /* PrivateTabsLockOverlayView.swift */; };
76AFB6632F58B16C0064E6AC /* PrivateLockMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76AFB6622F58B16C0064E6AC /* PrivateLockMiddleware.swift */; };
76DC672D2F641E6B00BE6F6A /* PrivateLockDomainState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76DC672C2F641E6B00BE6F6A /* PrivateLockDomainState.swift */; };
76F1091F2F5C9F6600305C29 /* FirefoxGradientBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F1091E2F5C9F6600305C29 /* FirefoxGradientBackgroundView.swift */; };
781C19CF2A780BEC0000DF46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 781C19CE2A780BEC0000DF46 /* Common */; };
787EDD852943EE75002B93AE /* JumpBackInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787EDD832943EE75002B93AE /* JumpBackInTests.swift */; };
789A0B232E2E969D004547CE /* FxUserState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5423F62DF6EF17000AA578 /* FxUserState.swift */; };
Expand Down Expand Up @@ -8812,9 +8817,14 @@
764643DCB449658AAA8ED829 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/InfoPlist.strings; sourceTree = "<group>"; };
765243D790B967A69BD5DCF7 /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lv; path = lv.lproj/ErrorPages.strings; sourceTree = "<group>"; };
766F49788D099E2201B74791 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Intro.strings; sourceTree = "<group>"; };
767742A82F4F649500DEC06D /* PrivateLockAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateLockAction.swift; sourceTree = "<group>"; };
767742AA2F4F6B4B00DEC06D /* PrivateTabsLockOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateTabsLockOverlayView.swift; sourceTree = "<group>"; };
76784C13A069B829DCA22A6C /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/LoginManager.strings; sourceTree = "<group>"; };
7681461D8ADDD3E9662A2A53 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Shared.strings; sourceTree = "<group>"; };
7693448FA2E2865EA28005EE /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ErrorPages.strings; sourceTree = "<group>"; };
76AFB6622F58B16C0064E6AC /* PrivateLockMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateLockMiddleware.swift; sourceTree = "<group>"; };
76DC672C2F641E6B00BE6F6A /* PrivateLockDomainState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateLockDomainState.swift; sourceTree = "<group>"; };
76F1091E2F5C9F6600305C29 /* FirefoxGradientBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirefoxGradientBackgroundView.swift; sourceTree = "<group>"; };
772744E9858179A908A070DB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ClearPrivateData.strings; sourceTree = "<group>"; };
77294827911067CD5994B5FD /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/ClearHistoryConfirm.strings; sourceTree = "<group>"; };
779D4B8EAE580D7B2B55EC3F /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/HistoryPanel.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -12203,6 +12213,8 @@
1DDE3DB22AC34E1E0039363B /* TabCell.swift */,
F605DD572CC73469009A671B /* TabDisplayDiffableDataSource.swift */,
213BF7522AC21D1B00C53A64 /* TabDisplayPanelViewController.swift */,
767742AA2F4F6B4B00DEC06D /* PrivateTabsLockOverlayView.swift */,
76F1091E2F5C9F6600305C29 /* FirefoxGradientBackgroundView.swift */,
214EF4142AC5D5D0005BCCDA /* TabDisplayView.swift */,
5A679E4A2B239FAE004F2B0D /* TabPeekViewController.swift */,
8A06DF5E2DE0C0EB007B7E9D /* TabTitleSupplementaryView.swift */,
Expand Down Expand Up @@ -12917,6 +12929,7 @@
5A2918CA2B522338002B197E /* GeneralBrowserAction.swift */,
7ADC1D182C27D35B003ED924 /* WebContextMenuActionsProvider.swift */,
8A8D277C2CC000BE0076AD3A /* NavigationBrowserAction.swift */,
767742A82F4F649500DEC06D /* PrivateLockAction.swift */,
);
path = Actions;
sourceTree = "<group>";
Expand Down Expand Up @@ -13048,6 +13061,14 @@
path = NativeErrorPage;
sourceTree = "<group>";
};
76AFB6612F58B1590064E6AC /* Middleware */ = {
isa = PBXGroup;
children = (
76AFB6622F58B16C0064E6AC /* PrivateLockMiddleware.swift */,
);
path = Middleware;
sourceTree = "<group>";
};
7AC7E04E2C160EB600051D4D /* Reader */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -13149,6 +13170,7 @@
81122E202B221AC0003DD9F8 /* SearchScreenState.swift */,
5A1947142B8FA9E0009C7A6C /* BrowserViewType.swift */,
8A8D277A2CBFFD710076AD3A /* BrowserNavigationType.swift */,
76DC672C2F641E6B00BE6F6A /* PrivateLockDomainState.swift */,
);
path = State;
sourceTree = "<group>";
Expand Down Expand Up @@ -15302,6 +15324,7 @@
21365E382F30FD580000C369 /* BrowserViewControllerLayoutManager.swift */,
0BBFBF232DFC201300160911 /* WebEngineIntegration */,
5A2918C92B522326002B197E /* Actions */,
76AFB6612F58B1590064E6AC /* Middleware */,
81122E1F2B2219AA003DD9F8 /* Views */,
81122E1E2B2219A0003DD9F8 /* State */,
81122E1D2B221998003DD9F8 /* Extensions */,
Expand Down Expand Up @@ -18744,6 +18767,7 @@
8AC6F2242D6E243F00D10A9F /* ExperimentEmptyPrivateTabsView.swift in Sources */,
1D0BA05C24F46A0400D731B5 /* TopSitesProvider.swift in Sources */,
A093CD292F35408E0017774C /* MozAdsClientFactory.swift in Sources */,
767742AB2F4F6B4B00DEC06D /* PrivateTabsLockOverlayView.swift in Sources */,
DFACBF7F277B5F7B003D5F41 /* LegacyWallpaperBackgroundView.swift in Sources */,
8A7D08E32CAAF7C30035999C /* HomepageViewController.swift in Sources */,
D01017F5219CB6BD009CBB5A /* DownloadContentScript.swift in Sources */,
Expand Down Expand Up @@ -18965,6 +18989,7 @@
CDB3BE8724746787009320EE /* FirefoxAccountSignInViewController.swift in Sources */,
8A471183287F6D9C00F5A6EA /* BookmarksPanelViewModel.swift in Sources */,
C705FD582EF354D300AC5EE7 /* PrivacyNoticeUpdate.swift in Sources */,
76DC672D2F641E6B00BE6F6A /* PrivateLockDomainState.swift in Sources */,
0B8A39EF2D3514C100853E47 /* EditBookmarkDiffableDataSource.swift in Sources */,
8A44F20E2B585E1F0016BC81 /* HomepageTelemetry.swift in Sources */,
EDD2A7FA2CDBD1D100ED464C /* SearchEngineElement+initFromSearchEngine.swift in Sources */,
Expand Down Expand Up @@ -19503,6 +19528,7 @@
8A19ACB62A3290F9001C2147 /* NotificationsSetting.swift in Sources */,
21365E392F30FD700000C369 /* BrowserViewControllerLayoutManager.swift in Sources */,
43D16B8229831E6A009F8279 /* CreditCardInputField.swift in Sources */,
76F1091F2F5C9F6600305C29 /* FirefoxGradientBackgroundView.swift in Sources */,
8187561A2BB4618500DCD1F3 /* OnboardingViewControllerState.swift in Sources */,
F82F68B12E86EF5F002E42D1 /* DeleteAutofillKeysSetting.swift in Sources */,
EB98550124226EF70040F24B /* AppDelegate+SyncSentTabs.swift in Sources */,
Expand Down Expand Up @@ -19553,6 +19579,7 @@
E13C072D2C2189B80087E404 /* ToolbarActionConfiguration.swift in Sources */,
C24B31292E86B8470049134A /* GenericSelectableItemCellView.swift in Sources */,
43175DB826B87D2C00C41C31 /* AdsTelemetryHelper.swift in Sources */,
767742A92F4F649500DEC06D /* PrivateLockAction.swift in Sources */,
E58368AA287D632F0087A449 /* StoryProvider.swift in Sources */,
8A3EF7FF2A2FCFBB00796E3A /* ChangeToChinaSetting.swift in Sources */,
1D558A5A2BEE7D07001EF527 /* WindowSimpleTabsCoordinator.swift in Sources */,
Expand All @@ -19570,6 +19597,7 @@
E13C07292C217D700087E404 /* NavigationBarState.swift in Sources */,
59A68FD5260B8D520F890F4A /* ReaderPanel.swift in Sources */,
0A93C8AA2C87070300BEA143 /* TrackingProtectionConnectionStatusView.swift in Sources */,
76AFB6632F58B16C0064E6AC /* PrivateLockMiddleware.swift in Sources */,
39BD570E2E53F3ED00CA1317 /* MerinoFeedFetcher.swift in Sources */,
C849E46126B9C39B00260F0B /* EnhancedTrackingProtectionVC.swift in Sources */,
8AA0A6632CAC40AA00AC7EB3 /* HomepageDiffableDataSource.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion firefox-ios/Client/Configuration/version.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
APP_VERSION = 149.2
APP_VERSION = 149.2
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,9 @@ final class BrowserCoordinator: BaseCoordinator,
}

func didFinishSettings(from coordinator: SettingsCoordinator) {
router.dismiss(animated: true, completion: nil)
router.dismiss(animated: true, completion: { [weak browserViewController] in
browserViewController?.settingsControllerDidHide()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is this change needed for?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

it's just a hook to check actual state on possible settings change in order to show tray and lock on it

remove(child: coordinator)
}

Expand Down Expand Up @@ -944,7 +946,7 @@ final class BrowserCoordinator: BaseCoordinator,
coordinator.showQRCode(delegate: delegate)
}

func showTabTray(selectedPanel: TabTrayPanelType) {
func showTabTray(selectedPanel: TabTrayPanelType, animated: Bool) {
guard !childCoordinators.contains(where: { $0 is TabTrayCoordinator }) else {
return // flow is already handled
}
Expand Down Expand Up @@ -983,9 +985,12 @@ final class BrowserCoordinator: BaseCoordinator,
if featureFlags.isFeatureEnabled(.tabTrayUIExperiments, checking: .buildOnly) &&
UIDevice.current.userInterfaceIdiom != .pad && selectedPanel != .syncedTabs {
guard let tabTrayVC = tabTrayCoordinator.tabTrayViewController else { return }
present(navigationController, customTransition: tabTrayVC, style: modalPresentationStyle)
present(navigationController,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a case where animated is false now?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

showPrivacyOverlayIfNeeded in bvc calls with animated false on appDidEnterBackgroundNotification event

customTransition: tabTrayVC,
style: modalPresentationStyle,
animated: animated)
} else {
present(navigationController)
present(navigationController, animated: animated)
}
guard browserViewController.isAppStoreReviewTriggerEnabled else { return }
browserViewController.ratingPromptManager.showRatingPromptIfNeeded()
Expand All @@ -994,12 +999,13 @@ final class BrowserCoordinator: BaseCoordinator,
// This implementation of present is specifically for the animation on .tabTrayUIExperiments
private func present(_ viewController: UIViewController,
customTransition: UIViewControllerTransitioningDelegate,
style: UIModalPresentationStyle) {
style: UIModalPresentationStyle,
animated: Bool = true) {
browserViewController.willNavigateAway(from: tabManager.selectedTab)
if !UIAccessibility.isReduceMotionEnabled {
router.present(
viewController,
animated: true,
animated: animated,
customTransition: customTransition,
presentationStyle: style
)
Expand All @@ -1008,9 +1014,9 @@ final class BrowserCoordinator: BaseCoordinator,
}
}

private func present(_ viewController: UIViewController) {
private func present(_ viewController: UIViewController, animated: Bool = true) {
browserViewController.willNavigateAway(from: tabManager.selectedTab)
router.present(viewController)
router.present(viewController, animated: animated)
}

func showBackForwardList() {
Expand Down Expand Up @@ -1253,6 +1259,15 @@ final class BrowserCoordinator: BaseCoordinator,
// [FXIOS-10482] Initial bandaid for memory leaking during tab tray open/close. Needs further investigation.
coordinator.dismissChildTabTrayPanels()
remove(child: coordinator)
let panel = TabTrayPanelType.convert(from: tabManager.selectedTab)
store.dispatch(
PrivateLockAction(
windowUUID: windowUUID,
actionType: PrivateLockActionType.didChangeTrayPresentation,
trayDisplayContext: .page,
trayPanelType: panel
)
)
Comment on lines +1263 to +1270
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It is not clear to me from the naming what the action is actually doing here. Is there a more intuitive name we could use? Here are the action naming conventions if helpful! https://github.com/mozilla-mobile/firefox-ios/wiki/Redux-Guidelines---FAQs#tips

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point, fixed

}

// MARK: - WindowEventCoordinator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protocol BrowserNavigationHandler: AnyObject, QRCodeNavigationHandler {

/// Shows the Tab Tray View Controller.
@MainActor
func showTabTray(selectedPanel: TabTrayPanelType)
func showTabTray(selectedPanel: TabTrayPanelType, animated: Bool)

/// Shows the Back Forward List View Controller.
@MainActor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum NimbusFeatureFlagID: String, CaseIterable {
case homepageStoriesScrollDirection
case homepageStoryCategories
case needsReloadRefactor
case privateTabsLock
case shouldUseBrandRefreshConfiguration
case shouldUseJapanConfiguration
case microsurvey
Expand Down Expand Up @@ -103,6 +104,7 @@ enum NimbusFeatureFlagID: String, CaseIterable {
.needsReloadRefactor,
.noInternetConnectionErrorPage,
.otherErrorPages,
.privateTabsLock,
.quickAnswers,
.recentSearches,
.relayIntegration,
Expand Down Expand Up @@ -160,6 +162,8 @@ struct NimbusFlaggableFeature: HasNimbusSearchBar {
return FlagKeys.SentFromFirefox
case .startAtHome:
return FlagKeys.StartAtHome
case .privateTabsLock:
return PrefsKeys.Settings.lockPrivateTabs
// Cases where users do not have the option to manipulate a setting. Please add in alphabetical order.
case .aiKillSwitch,
.appearanceMenu,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Common
import Redux

enum PrivateLockActionType: ActionType {
case privateAuthRequested(String)
case didChangeTrayDisplayContext
case didChangeTrayPresentation
case didEnterBackground
case willEnterForeground
case didChangePrivateTabsLockSetting
}

struct PrivateLockAction: Action {
let windowUUID: WindowUUID
let actionType: ActionType
let trayDisplayContext: BrowserViewControllerState.TrayDisplayContext?
let trayPanelType: TabTrayPanelType?

init(windowUUID: WindowUUID,
actionType: ActionType,
trayDisplayContext: BrowserViewControllerState.TrayDisplayContext? = nil,
trayPanelType: TabTrayPanelType? = nil) {
self.windowUUID = windowUUID
self.actionType = actionType
self.trayDisplayContext = trayDisplayContext
self.trayPanelType = trayPanelType
}
}

enum PrivateLockMiddlewareActionType: ActionType {
case didChangePrivateLockState
case didChangeTabTrayPanelType
case didChangeTrayDisplayContext
}

struct PrivateLockMiddlewareAction: Action {
let windowUUID: WindowUUID
let actionType: ActionType
let privateLockState: PrivateLockDomainState?
let trayPanelType: TabTrayPanelType?
let trayDisplayContext: BrowserViewControllerState.TrayDisplayContext?
let privateLockEnabled: Bool?

init(windowUUID: WindowUUID,
actionType: ActionType,
privatePanelLockState: PrivateLockDomainState? = nil,
trayPanelType: TabTrayPanelType? = nil,
trayDisplayContext: BrowserViewControllerState.TrayDisplayContext? = nil,
privateLockEnabled: Bool? = nil) {
self.windowUUID = windowUUID
self.actionType = actionType
self.privateLockState = privatePanelLockState
self.trayPanelType = trayPanelType
self.trayDisplayContext = trayDisplayContext
self.privateLockEnabled = privateLockEnabled
}
}
Loading