2
0
mirror of https://github.com/fork-maintainers/iceraven-browser synced 2024-11-19 09:25:34 +00:00

Bug 1853177 - Avoid multiple crash dialogs to be created and shown

This commit is contained in:
William Durand 2023-09-18 11:54:17 +02:00 committed by mergify[bot]
parent 5a683806c9
commit b3c8e1f3ee
2 changed files with 101 additions and 51 deletions

View File

@ -10,6 +10,7 @@ import android.widget.Button
import android.widget.TextView
import androidx.annotation.UiContext
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.LifecycleOwner
import mozilla.components.browser.state.action.ExtensionProcessDisabledPopupAction
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.Engine
@ -21,6 +22,34 @@ import org.mozilla.fenix.ext.components
import org.mozilla.geckoview.WebExtensionController
/**
* Controller for showing the user a dialog when the the extension process spawning has been disabled.
*
* @param context to show the AlertDialog
* @param store The [BrowserStore] which holds the state for showing the dialog
* @param webExtensionController to call when a user enables the process spawning
* @param builder to use for creating the dialog which can be styled as needed
* @param appName to be added to the message. Optional and mainly relevant for testing
*/
class ExtensionProcessDisabledController(
@UiContext context: Context,
store: BrowserStore,
engine: Engine = context.components.core.engine,
builder: AlertDialog.Builder = AlertDialog.Builder(context),
appName: String = context.appName,
) : ExtensionProcessDisabledPopupObserver(
store,
{ presentDialog(context, store, engine, builder, appName) },
) {
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
// In case the activity gets destroyed, we want to re-create the dialog.
shouldCreateDialog = true
}
companion object {
private var shouldCreateDialog: Boolean = true
/**
* Present a dialog to the user notifying of extension process spawning disabled and also asking
* whether they would like to continue trying or disable extensions. If the user chooses to retry,
* enable the extension process spawning with [WebExtensionController.enableExtensionProcessSpawning].
@ -31,13 +60,17 @@ import org.mozilla.geckoview.WebExtensionController
* @param builder to use for creating the dialog which can be styled as needed
* @param appName to be added to the message. Necessary to be added as a param for testing
*/
private fun presentDialog(
private fun presentDialog(
@UiContext context: Context,
store: BrowserStore,
engine: Engine,
builder: AlertDialog.Builder,
appName: String,
) {
) {
if (!shouldCreateDialog) {
return
}
val message = context.getString(R.string.addon_process_crash_dialog_message, appName)
var onDismissDialog: (() -> Unit)? = null
val layout = LayoutInflater.from(context)
@ -63,25 +96,11 @@ private fun presentDialog(
}
val dialog = builder.show()
onDismissDialog = { dialog?.dismiss() }
shouldCreateDialog = false
onDismissDialog = {
dialog?.dismiss()
shouldCreateDialog = true
}
}
}
}
/**
* Controller for showing the user a dialog when the the extension process spawning has been disabled.
*
* @param context to show the AlertDialog
* @param store The [BrowserStore] which holds the state for showing the dialog
* @param webExtensionController to call when a user enables the process spawning
* @param builder to use for creating the dialog which can be styled as needed
* @param appName to be added to the message. Optional and mainly relevant for testing
*/
class ExtensionProcessDisabledController(
@UiContext context: Context,
store: BrowserStore,
engine: Engine = context.components.core.engine,
builder: AlertDialog.Builder = AlertDialog.Builder(context),
appName: String = context.appName,
) : ExtensionProcessDisabledPopupObserver(
store,
{ presentDialog(context, store, engine, builder, appName) },
)

View File

@ -22,6 +22,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@ -40,8 +41,7 @@ class ExtensionProcessDisabledControllerTest {
val dialog: AlertDialog = mock()
val appName = "TestApp"
val builder: AlertDialog.Builder = mock()
val controller =
ExtensionProcessDisabledController(testContext, store, engine, builder, appName)
val controller = ExtensionProcessDisabledController(testContext, store, engine, builder, appName)
val buttonsContainerCaptor = argumentCaptor<View>()
controller.start()
@ -74,8 +74,7 @@ class ExtensionProcessDisabledControllerTest {
val appName = "TestApp"
val dialog: AlertDialog = mock()
val builder: AlertDialog.Builder = mock()
val controller =
ExtensionProcessDisabledController(testContext, store, engine, builder, appName)
val controller = ExtensionProcessDisabledController(testContext, store, engine, builder, appName)
val buttonsContainerCaptor = argumentCaptor<View>()
controller.start()
@ -100,4 +99,36 @@ class ExtensionProcessDisabledControllerTest {
verify(engine, never()).enableExtensionProcessSpawning()
verify(dialog).dismiss()
}
@Test
fun `WHEN dispatching the same event twice THEN the dialog should only be created once`() {
val store = BrowserStore()
val engine: Engine = mock()
val appName = "TestApp"
val dialog: AlertDialog = mock()
val builder: AlertDialog.Builder = mock()
val controller = ExtensionProcessDisabledController(testContext, store, engine, builder, appName)
val buttonsContainerCaptor = argumentCaptor<View>()
controller.start()
whenever(builder.show()).thenReturn(dialog)
// First dispatch...
store.dispatch(ExtensionProcessDisabledPopupAction(showPopup = true))
dispatcher.scheduler.advanceUntilIdle()
store.waitUntilIdle()
// Second dispatch... without having dismissed the dialog before!
store.dispatch(ExtensionProcessDisabledPopupAction(showPopup = true))
dispatcher.scheduler.advanceUntilIdle()
store.waitUntilIdle()
verify(builder).setView(buttonsContainerCaptor.capture())
verify(builder, times(1)).show()
// Click a button to dismiss the dialog.
buttonsContainerCaptor.value.findViewById<Button>(R.id.negative).performClick()
store.waitUntilIdle()
}
}