diff --git a/components/webauthn/android/BUILD.gn b/components/webauthn/android/BUILD.gn index b6885d691036ef..18b4550f6ae4e9 100644 --- a/components/webauthn/android/BUILD.gn +++ b/components/webauthn/android/BUILD.gn @@ -205,6 +205,7 @@ source_set("unit_tests") { robolectric_library("junit") { sources = [ + "junit/src/org/chromium/components/webauthn/AuthenticatorFactoryRobolectricTest.java", "junit/src/org/chromium/components/webauthn/AuthenticatorImplPasswordOnlyTest.java", "junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java", "junit/src/org/chromium/components/webauthn/BarrierTest.java", diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java index 6769fb488450bd..ded920e810e2ff 100644 --- a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java +++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java @@ -10,6 +10,7 @@ import org.chromium.base.ContextUtils; import org.chromium.blink.mojom.Authenticator; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.content_public.browser.LifecycleState; import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContentsStatics; @@ -32,7 +33,8 @@ public class AuthenticatorFactory implements InterfaceFactory<@Nullable Authenti @Override public @Nullable Authenticator createImpl() { - if (mRenderFrameHost == null) { + if (mRenderFrameHost == null + || mRenderFrameHost.getLifecycleState() != LifecycleState.ACTIVE) { return null; } WebContents webContents = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost); diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorFactoryRobolectricTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorFactoryRobolectricTest.java new file mode 100644 index 00000000000000..7e697e6ea0756c --- /dev/null +++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorFactoryRobolectricTest.java @@ -0,0 +1,103 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.webauthn; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Batch; +import org.chromium.content_public.browser.LifecycleState; +import org.chromium.content_public.browser.RenderFrameHost; +import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.browser.WebContentsStatics; +import org.chromium.url.GURL; +import org.chromium.url.Origin; + +/** Tests for {@link AuthenticatorFactory}. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +@Batch(Batch.UNIT_TESTS) +@SmallTest +public class AuthenticatorFactoryRobolectricTest { + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private RenderFrameHost mRenderFrameHost; + @Mock private RenderFrameHost mMainFrame; + @Mock private WebContents mWebContents; + @Mock private WebauthnModeProvider mModeProviderMock; + + @Before + public void setUp() { + Origin origin = Origin.create(new GURL("https://example.com")); + when(mRenderFrameHost.getLifecycleState()).thenReturn(LifecycleState.ACTIVE); + when(mRenderFrameHost.getLastCommittedOrigin()).thenReturn(origin); + when(mMainFrame.getLastCommittedOrigin()).thenReturn(origin); + when(mWebContents.getMainFrame()).thenReturn(mMainFrame); + WebContentsStatics.setWebContentsForTesting(mWebContents); + WebauthnModeProvider.setInstanceForTesting(mModeProviderMock); + when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.CHROME); + when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.CHROME); + } + + @After + public void tearDown() { + WebauthnModeProvider.setInstanceForTesting(null); + } + + private AuthenticatorFactory createFactory(RenderFrameHost renderFrameHost) { + return new AuthenticatorFactory(renderFrameHost, /* confirmationFactory= */ null); + } + + @Test + public void testCreateImpl_whenRenderFrameHostActive_returnsAuthenticator() { + assertThat(createFactory(mRenderFrameHost).createImpl()) + .isInstanceOf(AuthenticatorImpl.class); + } + + @Test + public void testCreateImpl_whenRenderFrameHostNull_returnsNull() { + assertThat(createFactory(null).createImpl()).isNull(); + } + + @Test + public void testCreateImpl_whenRenderFrameHostInBackForwardCache_returnsNull() { + when(mRenderFrameHost.getLifecycleState()) + .thenReturn(LifecycleState.IN_BACK_FORWARD_CACHE); + assertThat(createFactory(mRenderFrameHost).createImpl()).isNull(); + } + + @Test + public void testCreateImpl_whenRenderFrameHostPendingDeletion_returnsNull() { + when(mRenderFrameHost.getLifecycleState()).thenReturn(LifecycleState.PENDING_DELETION); + assertThat(createFactory(mRenderFrameHost).createImpl()).isNull(); + } + + @Test + public void testCreateImpl_whenRenderFrameHostPrerendering_returnsNull() { + when(mRenderFrameHost.getLifecycleState()).thenReturn(LifecycleState.PRERENDERING); + assertThat(createFactory(mRenderFrameHost).createImpl()).isNull(); + } + + @Test + public void testCreateImpl_whenWebauthnModeNone_returnsNull() { + when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.NONE); + assertThat(createFactory(mRenderFrameHost).createImpl()).isNull(); + } +}