Repository navigation
Conversation
…their session first Custom tab activities resolved their browsing mode from the PRIVATE_BROWSING_MODE intent extra, else from the last known mode. AuthCustomTabActivity (Firefox account sign-in) is started without that extra, with a private session when the last known mode is private. Once Home switched to normal, restarting or recreating the sign-in tab resolved it to normal: its toolbar and menu turned normal and screenshots were allowed on a private session. Custom tabs now resolve their mode from their session, else the intent extra, else the last known mode, through resolveBrowsingMode(). Home has no bound session and is unchanged. To test: 1. Switch Home to private and start a Firefox account sign-in from settings. 2. Go back to Home through recents and switch it to normal. 3. Return to the sign-in tab through recents, then toggle the system dark theme. The sign-in tab stays private throughout: theme, menu, screenshots blocked.
…n a private tab "Open in private tab" on a top site only updated AppState.mode. The persisted last known mode, the secure window flag and the activity theme were only updated a moment later, when the browser fragment selected the new private tab. Switching through the activity's BrowsingModeManager applies all of them at once, as every other private-open flow does. To test: 1. In normal mode, long-press a top site on the homepage and pick "Open in private tab". 2. The app switches to the private theme and the site loads in one new private tab. 3. Repeat from the shortcuts screen; same result.
…en opening a recently closed tab The dispatch set AppState.mode to the value it already had, so it changed nothing.
…ollow the custom tab in front WebExtension popups and extension-created tabs ask the app whether browsing is private, and Fenix currently answers with AppState.mode. AppState.mode can't mean both "the main browser's mode" and "the mode of the window in focus" as these can differ. The main browser's UI would remain using AppState.mode. Custom tab's UI would remain using that tab's content.private mode. Features like web extensions - for which the framework outlives the current custom tab - would need a new way of knowing what mode to use. Previously using AppState.mode mostly worked for custom tabs only as a side effect: the main browser and every custom tab wrote their mode to AppState.mode when created or restarted, so it holds the mode of the last activity started, not of the one in front. In split screen, focus moves without a restart. A private custom tab can then open its extension popups in a normal session, and the main browser's extensions can open private tabs. Custom tabs also need to stop writing AppState.mode so they no longer change the main browser's mode. AppState now records the custom tab the user focused last, cleared when the main browser gains focus, and the check uses that custom tab's privacy if it still exists, else AppState.mode. Focus moving to another app keeps the last Fenix window, so extension background scripts follow it. The custom tab answer no longer depends on AppState.mode. The main browser side is fixed once custom tabs stop writing AppState.mode. To test: 1. Enable custom tab extensions, install an extension with a browser action that is not allowed in private browsing, and keep the main browser in normal mode. 2. Open a private custom tab (with "Open links in a private tab" enabled) and open the extension from the custom tab menu: no popup is shown. 3. With the private custom tab open, show the main browser next to it in split screen, then tap the custom tab and open the extension from its menu: no popup is shown. Before this change, the popup opened in a normal session. 4. Allow the extension in private browsing and repeat: the popup opens in a private session.
…heme The Compose theme read AppState.mode, which holds the mode of the activity created or restarted last. A custom tab next to Home in split screen could then get Home's theme, or Home the custom tab's. Private windows now carry an isPrivateTheme theme attribute and the Compose theme reads it from its context, so Compose always matches the window's View theme.
…ivacy The private tabs lock in a custom tab checked AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. The lock now checks the privacy of the tab shown in the window, and the unlock screen recognizes a custom tab from its navigation origin. Testing, with the private tabs lock enabled: - Open a private regular tab and send the app to the background. A private custom tab then shows the unlock screen without "Leave private tabs". - In split screen, show a private regular tab next to a normal custom tab. In the regular tab window, switch to normal mode from the homepage and back to private. Only the regular tab window shows the unlock screen. - A locked private regular tab still shows the unlock screen with "Leave private tabs".
…privacy of the tab shown The contextual menu (a dialog showing on top of any destination) was hidden while private browsing is locked if AppState.mode was private but this did not account for when a regular tab is shown in split view with a custom tab and each would have a different privacy mode. Now we'll check the privacy of the tab currently shown and for which to decide if to show the contextual menu. To test ensure that locking private tabs is enabled and then: Scenario 1: - in a private tab long click on a link to show the contextual menu - put the app to background - resume it and check that the lockscreen shows but no contextual menu. Scenario 2: - open in splitscreen a private custom tab and a regular tab in normal browsing - in the custom tab long click on a link to show the contextual menu - in the regular tab switch between private and regular tabs - check that private tabs get locked: the custom tab shows the lockscreen and its contextual menu is hidden.
…privacy of the tab shown Two site permission prompt behaviours depended on AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab: - In private tabs the "Don't ask again" checkbox is not shown. - While private browsing is locked, an open prompt in a private tab is dismissed when the browser stops, so it doesn't show over the unlock screen. Both now check the privacy of the tab currently in focus. Testing, on a page asking for a site permission such as location: - In a private custom tab, the prompt has no "Remember decision for this site" checkbox. - In a normal custom tab, the prompt has the checkbox. - In regular tabs, the checkbox is shown in normal mode and hidden in private mode, as before. - With the private tabs lock enabled and a private regular tab open, show the prompt in a private custom tab, send the app to the background and return. Only the unlock screen shows, without the prompt.
…ests based on the privacy of the tab shown When private browsing can be locked, a black overlay hides a private tab's content while an Android permission dialog is shown. Whether the tab is private was read from AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. Now this will be checked based on the tab currently in focus. Testing, with "Lock private tabs" enabled and Firefox's Android camera or location permission revoked, on a page requesting that permission: - In a private custom tab, with a normal regular tab open, allowing the site prompt shows the Android dialog over a black screen. - In a normal custom tab, with a private regular tab open, the page stays visible behind the Android dialog. - In regular tabs, the black screen shows in private mode and not in normal mode, as before.
…acy of the tab shown "View certificate" in the site security panel opened the website certificate in a private or normal tab based on AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. It now uses the privacy of the tab the panel was opened for, and falls back to AppState.mode only as a sane default though this should never be reached in real life. Testing, on an HTTPS page, open the site security panel from the toolbar, then "Connection is secure" and "View certificate": - In a private custom tab, with a normal regular tab open, the certificate opens in a private tab. - In a normal custom tab, with a private regular tab open, it opens in a normal tab. - In regular tabs, it opens in a tab of the current mode, as before.
…ivacy of the tab shown A custom tab's site security panel can open the tracking protection settings inside the custom tab window. Their "Learn more" links opened a new tab with the default privacy of AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. These screens don't know which custom tab opened them, so they now use the privacy of the custom tab currently in focus and AppState.mode otherwise. Testing, from a custom tab, open the site security panel from the toolbar, then "Privacy and security settings": - In a private custom tab, with a normal regular tab open, "Learn more" under Enhanced Tracking Protection opens a private tab. The same holds for "Learn more" in Exceptions. - In a normal custom tab, with a private regular tab open, they open a normal tab. - From Settings in regular tabs, they open a tab of the current mode, as before.
…vacy of the tab shown A custom tab's login prompt can open the saved logins screens inside the custom tab window through "Manage passwords". Opening a login's site or the sync help from there creates a tab with the default privacy of AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. These screens don't know which custom tab opened them, so they now use the privacy of the custom tab currently in focus and AppState.mode otherwise. Testing, with a saved login for a site: - In a private custom tab, with a normal regular tab open, focus a login field, tap "Manage passwords" in the login prompt, open the login and tap "Go to site". The site opens in a private tab. - In a normal custom tab, with a private regular tab open, the same steps open it in a normal tab. - From Settings > Passwords in regular tabs, "Go to site" opens a tab of the current mode, as before.
…rivacy of the tab shown An extension can open its options page while a custom tab is in front, and the options page then shows in the custom tab window. Going back from it leads to the add-on's details screen, from where reporting the add-on, opening its settings in a tab, and its website, permission and warning links opened tabs with the privacy of AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. These screens don't know which custom tab opened them, so they now use the privacy of the custom tab currently in focus and AppState.mode otherwise. Testing, with an extension whose popup calls browser.runtime.openOptionsPage() and "Enable Custom Tab Extension List" on in Secret Settings: - In a private custom tab, with a normal regular tab open, open the extension from the menu, open its options page, press Back and tap "Report". The report page opens in a private tab. The same holds for the details links and the permissions "Learn more". - In a normal custom tab, with a private regular tab open, they open in a normal tab. - From Settings > Extensions in regular tabs, they open in a tab of the current mode, as before.
…on the privacy of the tab shown A custom tab's in-app share screen can open "Connect another device" inside the custom tab window. Its "Learn About Sending Tabs…" opened the help in a tab with the default privacy of AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. This screen doesn't know which custom tab opened it, so it now uses the privacy of the custom tab currently in focus and AppState.mode otherwise. Testing, signed in to a Firefox account with no other devices that can receive tabs, and with "Enable Native Android Share Sheet" turned off in Secret Settings: - In a private custom tab, with a normal regular tab open, tap menu > Share > "Connect another device" > "Learn About Sending Tabs…". The help opens in a private tab. - In a normal custom tab, with a private regular tab open, the same steps open it in a normal tab. - From Share in a regular tab, the same button opens a tab of the current mode, as before.
…he privacy of the tab shown A custom tab's translation dialog can open the translation settings inside the custom tab window. "Learn more" on the "Download languages" screen opened the help in a tab with the default privacy of AppState.mode, which a custom tab shown next to a regular tab can share with that regular tab. This screen doesn't know which custom tab opened it, so it now uses the privacy of the custom tab currently in focus and AppState.mode otherwise. Testing, on a page in another language: - In a private custom tab, with a normal regular tab open, tap menu > "Translate page", open "Translation settings" from the dialog's options, then "Download languages" > "Learn more". The help opens in a private tab. - In a normal custom tab, with a private regular tab open, the same steps open it in a normal tab. - From Settings > Translations in regular tabs, "Learn more" opens a tab of the current mode, as before.
…s the last known mode Every activity's browsing mode manager saved its mode as the last known mode, so opening a normal custom tab while regular tabs were private made the next launch from the launcher start in normal mode. The mode is now saved by the regular tab window only. Custom tab activities, including the account sign-in custom tab, still resolve their mode from their session but don't save it. Testing, with "Open links in a private tab" off: - Open Firefox from the launcher and switch to private mode. Send Firefox to the background, open a link in a normal custom tab from another app and close it. Without returning to Firefox, force stop it and open it from the launcher: it starts on the private homepage. Before this change it started in normal mode. Unchanged, to check: - Open Firefox from the launcher, switch to normal mode, force stop it and open it from the launcher: it starts in normal mode. Repeat ending in private mode: it starts on the private homepage.
…s to the app state
Every custom tab activity published its mode as the global AppState.mode
when created or restarted, overwriting the mode of the regular tab window.
Everything shown in a custom tab now follows the custom tab's own privacy,
so custom tabs now only apply their secure window flag and leave
AppState.mode to the regular tab window.
Opening a normal custom tab no longer counts as leaving private mode, so
it no longer locks private tabs on its own.
Testing:
- With the private tabs lock on and private tabs open in the regular tab
window, open a normal custom tab from another app in the other half of
split screen. The private tabs stay unlocked. Sending the app to the
background still locks them.
- With "Open links in a private tab" on, open a link from another app and
show the private custom tab in split screen next to the regular tab
window in normal mode. The regular tab window stays normal.
Unchanged, to check:
- In the split screen above, with "Allow screenshots in private browsing"
off, the custom tab keeps its private theme, menu and blocked
screenshots. Switching the regular tab window between normal and private
doesn't change the custom tab.
- With the private tabs lock on and a private tab open in regular tabs,
send a private custom tab to the background and back: it shows the
unlock screen without "Leave private tabs".
- In a normal custom tab with the calling app's colors, exit fullscreen
video: the toolbar and system bars keep those colors.
- An installed web app opened from its launcher shortcut and a sandboxed
custom tab ("Learn more" in the VPN prompt) open and show their pages.
…singModeChanged The action is about to be dispatched directly instead of only by the browsing mode manager, so its name no longer refers to the manager. Only the regular tabs window dispatches it. No behaviour change.
…p store The regular tabs window saved its browsing mode as the last known mode when its mode changed. A new AppStore middleware now saves it for every BrowsingModeChanged action, before the new mode reaches the state. No behaviour change.
…indow from the app store The regular tabs window applied its secure window flag and theme when its browsing mode manager changed mode. A new BrowsingModeBinding now observes AppState.mode and applies them whenever the mode changes, so a mode change only has to be dispatched. The binding runs on the immediate main dispatcher, so the secure flag is set as soon as the mode is dispatched, before the theme switch. The secure flag is also re-applied when the window is created or restarted and after the mode is resolved for a new intent, as before. No intended behaviour.
… window into the app store The regular tabs window kept its own browsing mode in its browsing mode manager and published every change to AppState. Its manager now reads AppState.mode and dispatches BrowsingModeChanged whenever the mode is set or resolved from an intent: when the window is created (before its theme is set up), restarted or gets a new intent, and when a shortcut or widget opens the homepage or search. Custom tabs keep their own manager. This approach is only temporary code since there are more than 30 files working with a BrowsingModeManager and we'd want to migrate them in separate commits to more easily understand the changes.
…he app store The homepage, its navigation bar, toolbar, edge to edge background, tab strip colors, tabs cleanup, private browsing button and top sites, and the home intent processors read or changed the browsing mode through the activity's browsing mode manager. They now read AppState.mode and change it by dispatching BrowsingModeChanged, which is what that manager already did for the regular tabs window. The intent processors resolve the mode from the intent themselves. No intended behaviour change for the regular tabs window. The homepage is meant to be shown only there; bug 2084745 tracks the tabs tray being reachable from a custom tab, from where the homepage's private browsing button now switches the regular tabs window's mode. To check: - Switch between normal and private mode with the homepage's private browsing button: the homepage, toolbar and navigation bar follow the mode. - Long-press a top site and choose "Open in private tab": the site opens in a private tab and Firefox switches to private mode. - With homepage as a new tab on, in normal mode, long-press the Firefox launcher icon and choose "New private tab": the private homepage opens. With it off, a private search opens with the address bar focused. - With homepage as a new tab on, tap "Private search" on the "Search bar with private and voice search" widget: a private homepage tab opens with the address bar focused. - In normal mode, open the private browsing deep link. For a debug build: adb shell am start -a android.intent.action.VIEW -d "fenix-dev://enable_private_browsing" Firefox switches to private mode. - In Settings > Homepage > Wallpapers, choose the edge to edge (gradient) wallpaper. The homepage shows it in normal mode and the private background in private mode.
…te.mode for toolbars and search The browser and homepage toolbars, their store builders, the search middlewares and suggestion providers, the awesome bar, and the browser screen read or changed the browsing mode through the activity's browsing mode manager. They now read AppState.mode and change it by dispatching BrowsingModeChanged, which is what that manager already did for the regular tabs window. When leaving full screen, a custom tab now checks its own privacy instead of its activity's manager, which held the same value. No intended behaviour change. To check: - Long-press the toolbar's tab counter and choose "New private tab", then "New tab": Firefox switches to private mode and back, each time opening a tab or the search of that mode. - With different default search engines for normal and private browsing and search suggestions in private browsing allowed, type a query in private mode: suggestions and results use the private engine. - In History, search and open a result in each browsing mode: it opens in a tab of that mode. Searching bookmarks still works. - Select a private tab from the tabs tray while in normal mode: Firefox switches to private mode.
…te.mode for the remaining screens The tabs tray, history, history groups, the private tabs unlock screen, the private browsing settings, the homepage tab binding and the VPN unavailable dialog read or changed the browsing mode through the activity's browsing mode manager. They now read AppState.mode and change it by dispatching BrowsingModeChanged, which is what that manager already did for the regular tabs window. The VPN unavailable dialog can be shown in a custom tab, so it opens the tabs tray page of the focused tab's privacy, the same value its activity's manager held. No intended behaviour change for the regular tabs window. These screens are meant to be shown only there; bug 2084745 tracks the tabs tray being reachable from a custom tab, from where its actions and the history screens now change or follow the regular tabs window's mode. To check: - In the tabs tray, select a private tab while in normal mode, and open a new tab from the private tabs page: Firefox switches to private mode. - In History, select items and choose "Open in private tab": they open in private tabs and Firefox switches to private mode. The same from a history group. - With the private tabs lock on and a private tab shown, send Firefox to the background and return, cancel authentication, then tap "Leave private tabs": Firefox switches to normal mode. - With the private tabs lock off, in private mode, toggle "Allow screenshots in private browsing": it applies right away. - With homepage as a new tab on, close all tabs of the current mode from the tabs tray and leave it: a homepage tab of that mode is added.
Nothing outside the activities reads a browsing mode manager any more. The regular tabs window resolves its mode from the intent or the last known mode when it is created, restarted or gets a new intent, and dispatches it as AppState.mode. A custom tab resolves its mode session first, then from the intent, then from the last known mode, when it is created or restarted, without publishing it, and keeps it only for its own theme, secure window flag and accessibility announcement. BrowsingModeManager and its implementations are removed. BrowsingMode was moved to its own file. No intended behaviour change. To check, with "Allow screenshots in private browsing" off: - With "Open links in a private tab" on, open a link from another app and show the private custom tab in split screen next to the regular tabs window in normal mode: the custom tab has the private theme and blocks screenshots, the regular tabs window stays normal and allows them. - Switch the regular tabs window between normal and private mode: its theme and screenshot blocking follow, the custom tab doesn't change. - With the private custom tab next to the normal regular tabs window and Settings > Customize set to follow the device theme, switch the system between light and dark theme: both windows are recreated and the custom tab stays private. - Switch to private mode, force stop Firefox and open it from the launcher: it starts on the private homepage. - Sign in to a Mozilla account from Settings: the sign-in custom tab opens and completes as before.
|
Warning The target branch has been automatically set to Please Edit this PR if this wasn't your intention. |
|
The plan is for Pacific R-n-B to review this next Wednesday, October 28. Marking myself as a reviewer here as well. We'd prefer to review in Phab, when that's healthy again. |
MatthewTighe
left a comment
There was a problem hiding this comment.
Just some early feedback, will be coming back to this
| searchTermOrURL = url.toString(), | ||
| newTab = true, | ||
| private = | ||
| requireComponents.appStore.state.isForegroundTabOrCustomTabPrivate(requireComponents.core.store.state), |
There was a problem hiding this comment.
It is a little surprising to me to need to extract all this state just to send it to this Usecase. Both of these Stores are dependencies in the Usecase object hierarchy. Why can't we depend on them to handle business logic like this correctly, and instead need to wire this logic of state comprehension in through all these various points?
There was a problem hiding this comment.
Good catch!
The AppStore is indeed already available in FenixBrowserUseCases but the BrowserStore from which to read the current tab is not. Could be if we'd want it..
AppState.isForegroundTabOrCustomTabPrivate(BrowserState) is the sole API proposed to support this refactoring so we'd need both the states to use it.
Dropped
BrowsingModeManagerin favor ofAppStatefor regular tabs.And used
content.privatefromBrowserStatefor custom tabs as they can be used at the same time with the regular tabs (in multiwindow) and could need a different browsing mode thanAppState.modevalue.The approach taken in this PR, edgecases and other potential approaches are documented here.
Lando: link
🚫 This pull request has 3 blockers.