Commit 902ab858 authored by Christian Sadilek's avatar Christian Sadilek
Browse files

Closes #3558: browser-state: Add engine session state and keep in sync

parent 606dd32c
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import kotlin.math.min
@Suppress("TooManyFunctions", "LargeClass")
class LegacySessionManager(
    val engine: Engine,
    private val engineSessionLinker: SessionManager.EngineSessionLinker,
    delegate: Observable<SessionManager.Observer> = ObserverRegistry()
) : Observable<SessionManager.Observer> by delegate {
    // It's important that any access to `values` is synchronized;
@@ -236,7 +237,7 @@ class LegacySessionManager(
     * - once snapshot has been restored, and appropriate session has been selected, onSessionsRestored
     *   notification will fire.
     *
     * @param snapshot A [Snapshot] which may be produced by [createSnapshot].
     * @param snapshot A [SessionManager.Snapshot] which may be produced by [createSnapshot].
     * @param updateSelection Whether the selected session should be updated from the restored snapshot.
     */
    fun restore(snapshot: SessionManager.Snapshot, updateSelection: Boolean = true) = synchronized(values) {
@@ -299,6 +300,7 @@ class LegacySessionManager(
                engineSession.loadUrl(session.url)
            }
        }
        engineSessionLinker.link(session, engineSession)
    }

    private fun unlink(session: Session) {
@@ -311,6 +313,7 @@ class LegacySessionManager(
                engineObserver = null
            }
        }
        engineSessionLinker.unlink(session)
    }

    /**
+30 −5
Original line number Diff line number Diff line
@@ -8,6 +8,9 @@ import mozilla.components.browser.session.ext.syncDispatch
import mozilla.components.browser.session.ext.toCustomTabSessionState
import mozilla.components.browser.session.ext.toTabSessionState
import mozilla.components.browser.state.action.CustomTabListAction
import mozilla.components.browser.state.action.EngineAction.LinkEngineSessionAction
import mozilla.components.browser.state.action.EngineAction.UpdateEngineSessionStateAction
import mozilla.components.browser.state.action.EngineAction.UnlinkEngineSessionAction
import mozilla.components.browser.state.action.SystemAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.store.BrowserStore
@@ -24,8 +27,24 @@ import java.lang.IllegalArgumentException
class SessionManager(
    val engine: Engine,
    private val store: BrowserStore? = null,
    private val delegate: LegacySessionManager = LegacySessionManager(engine)
    private val delegate: LegacySessionManager = LegacySessionManager(engine, EngineSessionLinker(store))
) : Observable<SessionManager.Observer> by delegate {

    /**
     * This class only exists for migrating from browser-session
     * to browser-state. We need a way to dispatch the corresponding browser
     * actions when an engine session is linked and unlinked.
     */
    class EngineSessionLinker(private val store: BrowserStore?) {
        fun link(session: Session, engineSession: EngineSession) {
            store?.syncDispatch(LinkEngineSessionAction(session.id, engineSession))
        }

        fun unlink(session: Session) {
            store?.syncDispatch(UnlinkEngineSessionAction(session.id))
        }
    }

    /**
     * Returns the number of session including CustomTab sessions.
     */
@@ -158,13 +177,14 @@ class SessionManager(

        delegate.restore(snapshot, updateSelection)

        val tabs = snapshot.sessions
        val items = snapshot.sessions
            .filter {
                // A restored snapshot should not contain any custom tab so we should be able to safely ignore
                // them here.
                !it.session.isCustomTabSession()
            }
            .map { item ->

        val tabs = items.map { item ->
            item.session.toTabSessionState()
        }

@@ -180,6 +200,11 @@ class SessionManager(
        }

        store?.syncDispatch(TabListAction.RestoreAction(tabs, selectedTabId))

        items.forEach { item ->
            item.engineSession?.let { store?.syncDispatch(LinkEngineSessionAction(item.session.id, it)) }
            item.engineSessionState?.let { store?.syncDispatch(UpdateEngineSessionStateAction(item.session.id, it)) }
        }
    }

    /**
+68 −0
Original line number Diff line number Diff line
@@ -7,6 +7,9 @@ package mozilla.components.browser.session
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.support.base.observer.Consumable
@@ -18,6 +21,7 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.`when`

/**
@@ -737,4 +741,68 @@ class SessionManagerMigrationTest {
            assertNull(tab.content.download)
        }
    }

    @Test
    fun `Linking session to engine session`() {
        val store = BrowserStore()
        val engine: Engine = mock()

        val engineSession1: EngineSession = mock()
        doReturn(engineSession1).`when`(engine).createSession(false)

        val sessionManager = SessionManager(engine, store)

        val session = Session(id = "session", initialUrl = "https://www.mozilla.org")
        sessionManager.add(session)

        assertNull(sessionManager.getEngineSession(session))
        assertEquals(engineSession1, sessionManager.getOrCreateEngineSession(session))
        store.state.findTab("session")!!.also { tab ->
            assertEquals(engineSession1, tab.engineState.engineSession)
        }

        // Force unlink and link again
        val engineSession2: EngineSession = mock()
        doReturn(engineSession2).`when`(engine).createSession(false)
        session.engineSessionHolder.engineSession = null
        assertEquals(engineSession2, sessionManager.getOrCreateEngineSession(session))
        store.state.findTab("session")!!.also { tab ->
            assertEquals(engineSession2, tab.engineState.engineSession)
        }
    }

    @Test
    fun `Restoring engine session with state`() {
        val engine: Engine = mock()
        val store = BrowserStore()
        val sessionManager = SessionManager(engine, store)

        val engineSession: EngineSession = mock()
        val engineSessionState: EngineSessionState = mock()

        val snapshot = SessionManager.Snapshot(
            listOf(
                SessionManager.Snapshot.Item(
                    session = Session(id = "session1", initialUrl = "http://www.firefox.com"),
                    engineSessionState = engineSessionState
                ),
                SessionManager.Snapshot.Item(
                    session = Session(id = "session2", initialUrl = "http://www.mozilla.org"),
                    engineSession = engineSession
                )
            ),
            selectedSessionIndex = 0
        )

        sessionManager.restore(snapshot)
        assertEquals(2, sessionManager.size)

        store.state.findTab("session1")!!.also { tab ->
            assertEquals(engineSessionState, tab.engineState.engineSessionState)
        }

        store.state.findTab("session2")!!.also { tab ->
            assertEquals(engineSession, tab.engineState.engineSession)
        }
    }
}
+28 −0
Original line number Diff line number Diff line
@@ -8,11 +8,14 @@ import android.graphics.Bitmap
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.CustomTabSessionState
import mozilla.components.browser.state.state.EngineState
import mozilla.components.browser.state.state.SecurityInfoState
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.TrackingProtectionState
import mozilla.components.browser.state.state.content.DownloadState
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.lib.state.Action
@@ -215,3 +218,28 @@ sealed class TrackingProtectionAction : BrowserAction() {
     */
    data class ClearTrackersAction(val tabId: String) : TrackingProtectionAction()
}

/**
 * [BrowserAction] implementations related to updating the [EngineState] of a single [SessionState] inside
 * [BrowserState].
 */
sealed class EngineAction : BrowserAction() {

    /**
     * Attaches the provided [EngineSession] to the session with the provided [sessionId].
     */
    data class LinkEngineSessionAction(val sessionId: String, val engineSession: EngineSession) : EngineAction()

    /**
     * Detaches the current [EngineSession] from the session with the provided [sessionId].
     */
    data class UnlinkEngineSessionAction(val sessionId: String) : EngineAction()

    /**
     * Updates the [EngineSessionState] of the session with the provided [sessionId].
     */
    data class UpdateEngineSessionStateAction(
        val sessionId: String,
        val engineSessionState: EngineSessionState
    ) : EngineAction()
}
+2 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ package mozilla.components.browser.state.reducer
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.action.CustomTabListAction
import mozilla.components.browser.state.action.EngineAction
import mozilla.components.browser.state.action.SystemAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.action.TrackingProtectionAction
@@ -28,6 +29,7 @@ internal object BrowserStateReducer {
            is SystemAction -> SystemReducer.reduce(state, action)
            is TabListAction -> TabListReducer.reduce(state, action)
            is TrackingProtectionAction -> TrackingProtectionStateReducer.reduce(state, action)
            is EngineAction -> EngineStateReducer.reduce(state, action)
        }
    }
}
Loading