-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fire Mode: WebView profile management #8575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
f223862
Cache both the feature flag and profile support
0nko dafa73a
Add the profile management components
0nko 0aeae26
Make browser mode data provider function suspending
0nko b8c2fe0
Add cookie manager provider and web storage providers
0nko 399cb16
Update the log message
0nko 883f11d
Always release the latch, even when there's an exception
0nko 6e990b8
Add unit tests
0nko d9bdf8a
Fix the issue when profiles are lost when migration fails
0nko cc458ca
Updates the duck.ai host for cookie migration and add tests
0nko d9db7cd
Update the interface docs
0nko 6ab8cf1
Fix formatting
0nko 1c55081
Remove the initialize class and make the profile manager observe the …
0nko dd68c07
Remove cookie manager provider
0nko d7af9ae
Remove WebViewProfileManager and associated classes
0nko d92e33a
Add a simplified profile management
0nko c473208
Make isAvailable non suspended
0nko 8ca07c0
Bind the profile to the WebView
0nko 53e9ef2
Remove unused dependency
0nko 562ac40
Simplify the availability check and data provider interface
0nko e9a92ed
Move WebStorageProvider to app module
0nko 39ce5ad
Make the profile bind call the very first one
0nko cf25965
Fix formatting
0nko 4d90be3
Rename the interface to WebViewModeInitializer
0nko 6f986ed
Update the WebViewModeInitializer to return a result
0nko d41a0f2
Add the missing else branch
0nko 466b269
Fix lint issue
0nko f2a77af
Capture WebView profile binding failures
cursoragent 4676907
Check for the non-default mode instead of fire mode specifically when…
0nko 73bf380
Close current tab if Fire mode unsupported and mode is FIRE
0nko ab49d52
Fix the failing unit tests
0nko 5817366
Consolidate the error handling
0nko 2de1809
Fix the failing tests
0nko ae0a70d
Update the interface docs
0nko 513ec83
Destroy the webview when closing the tab
0nko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
app/src/main/java/com/duckduckgo/app/browser/mode/WebStorageProvider.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* | ||
| * Copyright (c) 2026 DuckDuckGo | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| package com.duckduckgo.app.browser.mode | ||
|
|
||
| import android.annotation.SuppressLint | ||
| import android.webkit.WebStorage | ||
| import androidx.annotation.UiThread | ||
| import androidx.webkit.ProfileStore | ||
| import com.duckduckgo.browsermode.api.BrowserMode | ||
| import com.duckduckgo.browsermode.api.BrowserModeDataProvider | ||
| import com.duckduckgo.browsermode.api.FireModeAvailability | ||
| import com.duckduckgo.browsermode.api.profileName | ||
| import com.duckduckgo.di.scopes.AppScope | ||
| import com.squareup.anvil.annotations.ContributesTo | ||
| import dagger.Binds | ||
| import dagger.Module | ||
| import dagger.SingleInstanceIn | ||
| import javax.inject.Inject | ||
|
|
||
| /** | ||
| * Resolves a per-mode [WebStorage]. Callers must invoke [forMode] on the main thread when | ||
| * `MULTI_PROFILE` is supported on the device — `ProfileStore` is tied to the WebView thread. | ||
| * Falls back to the default shared [WebStorage] when MultiProfile is unsupported. | ||
| */ | ||
| @SuppressLint("RequiresFeature") | ||
| @SingleInstanceIn(AppScope::class) | ||
| class WebStorageProvider @Inject constructor( | ||
| private val fireModeAvailability: FireModeAvailability, | ||
| ) : BrowserModeDataProvider<WebStorage> { | ||
|
|
||
| @UiThread | ||
| override fun forMode(mode: BrowserMode): WebStorage { | ||
| if (!fireModeAvailability.isAvailable()) return WebStorage.getInstance() | ||
| return ProfileStore.getInstance().getOrCreateProfile(mode.profileName).webStorage | ||
| } | ||
| } | ||
|
|
||
| @ContributesTo(AppScope::class) | ||
| @Module | ||
| abstract class WebStorageProviderModule { | ||
| @Binds | ||
| abstract fun bindWebStorageProvider(impl: WebStorageProvider): BrowserModeDataProvider<WebStorage> | ||
| } |
41 changes: 41 additions & 0 deletions
41
app/src/test/java/com/duckduckgo/app/browser/mode/WebStorageProviderTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| /* | ||
| * Copyright (c) 2026 DuckDuckGo | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| package com.duckduckgo.app.browser.mode | ||
|
|
||
| import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
| import com.duckduckgo.browsermode.api.BrowserMode | ||
| import com.duckduckgo.browsermode.api.FireModeAvailability | ||
| import org.junit.Assert.assertNotNull | ||
| import org.junit.Test | ||
| import org.junit.runner.RunWith | ||
| import org.mockito.kotlin.mock | ||
| import org.mockito.kotlin.whenever | ||
|
|
||
| @RunWith(AndroidJUnit4::class) | ||
| class WebStorageProviderTest { | ||
|
|
||
| private val fireModeAvailability: FireModeAvailability = mock() | ||
| private val testee = WebStorageProvider(fireModeAvailability) | ||
|
|
||
| @Test | ||
| fun `forMode returns default WebStorage when multi-profile is unavailable`() { | ||
| whenever(fireModeAvailability.isAvailable()).thenReturn(false) | ||
|
|
||
| assertNotNull(testee.forMode(BrowserMode.REGULAR)) | ||
| assertNotNull(testee.forMode(BrowserMode.FIRE)) | ||
| } | ||
| } |
31 changes: 31 additions & 0 deletions
31
...e/browser-mode-api/src/main/java/com/duckduckgo/browsermode/api/BrowserModeProfileName.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| /* | ||
| * Copyright (c) 2026 DuckDuckGo | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| package com.duckduckgo.browsermode.api | ||
|
|
||
| import androidx.webkit.Profile | ||
|
|
||
| /** | ||
| * Stable mapping from a [BrowserMode] to its WebView profile name. | ||
| * | ||
| * [REGULAR][BrowserMode.REGULAR] shares the default WebView profile; | ||
| * [FIRE][BrowserMode.FIRE] gets its own isolated profile. | ||
| */ | ||
| val BrowserMode.profileName: String | ||
| get() = when (this) { | ||
| BrowserMode.REGULAR -> Profile.DEFAULT_PROFILE_NAME | ||
| BrowserMode.FIRE -> "Fire" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
...e/browser-mode-api/src/main/java/com/duckduckgo/browsermode/api/WebViewModeInitializer.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* | ||
| * Copyright (c) 2026 DuckDuckGo | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| package com.duckduckgo.browsermode.api | ||
|
|
||
| import android.webkit.WebView | ||
|
|
||
| /** | ||
| * Binds a [WebView] to the WebView profile associated with a [BrowserMode], isolating cookies | ||
| * and origin-keyed storage (LocalStorage, IndexedDB) between modes. | ||
| * | ||
| * Must be called once per [WebView] before any other WebView API call, including `loadUrl`. The | ||
| * binding is permanent for the lifetime of the [WebView] — it cannot be changed once the | ||
| * [WebView] has been used. | ||
| * | ||
| * Fails on devices that do not support the `MULTI_PROFILE` WebView feature, regardless of the | ||
| * requested [BrowserMode]. | ||
| */ | ||
| interface WebViewModeInitializer { | ||
|
|
||
| /** | ||
| * Must be called on the main thread. | ||
| * | ||
| * @param webView a freshly-created [WebView] that has not yet been used. | ||
| * @param mode the [BrowserMode] whose profile should back this [WebView]. | ||
| * @return [Result.success] when the profile is bound to the [WebView]; [Result.failure] when | ||
| * the `MULTI_PROFILE` WebView feature is unsupported on this device, or when the underlying | ||
| * profile binding call throws. | ||
| */ | ||
| fun bind(webView: WebView, mode: BrowserMode): Result<Unit> | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
...ser-mode-impl/src/main/java/com/duckduckgo/browsermode/impl/RealWebViewModeInitializer.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /* | ||
| * Copyright (c) 2026 DuckDuckGo | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| package com.duckduckgo.browsermode.impl | ||
|
|
||
| import android.annotation.SuppressLint | ||
| import android.webkit.WebView | ||
| import androidx.webkit.ProfileStore | ||
| import androidx.webkit.WebViewCompat | ||
| import androidx.webkit.WebViewFeature | ||
| import com.duckduckgo.browsermode.api.BrowserMode | ||
| import com.duckduckgo.browsermode.api.WebViewModeInitializer | ||
| import com.duckduckgo.browsermode.api.profileName | ||
| import com.duckduckgo.di.scopes.AppScope | ||
| import com.squareup.anvil.annotations.ContributesBinding | ||
| import dagger.SingleInstanceIn | ||
| import javax.inject.Inject | ||
|
|
||
| @SingleInstanceIn(AppScope::class) | ||
| @ContributesBinding(AppScope::class) | ||
| class RealWebViewModeInitializer @Inject constructor( | ||
| private val webViewProfileBinder: WebViewProfileBinder, | ||
| ) : WebViewModeInitializer { | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| override fun bind(webView: WebView, mode: BrowserMode): Result<Unit> { | ||
| if (!WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE)) { | ||
| return Result.failure( | ||
| IllegalStateException( | ||
| "Attempting to bind a WebView profile to " + | ||
| "{${mode.name}} mode when MULTI_PROFILE feature is not available.", | ||
| ), | ||
| ) | ||
| } | ||
| return runCatching { | ||
| webViewProfileBinder.bind(webView, mode.profileName) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| interface WebViewProfileBinder { | ||
| fun bind(webView: WebView, profileName: String) | ||
| } | ||
|
|
||
| @ContributesBinding(AppScope::class) | ||
| @SuppressLint("RequiresFeature") | ||
| class AndroidXWebViewProfileBinder @Inject constructor() : WebViewProfileBinder { | ||
| override fun bind(webView: WebView, profileName: String) { | ||
| ProfileStore.getInstance().getOrCreateProfile(profileName) | ||
| WebViewCompat.setProfile(webView, profileName) | ||
| } | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.