Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Closes #7023 - Add HistoryState to content state
Browse files Browse the repository at this point in the history
  • Loading branch information
NotWoods committed May 22, 2020
1 parent 9be6690 commit 1a84e76
Show file tree
Hide file tree
Showing 17 changed files with 296 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.Settings
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.manifest.WebAppManifestParser
import mozilla.components.concept.engine.request.RequestInterceptor
Expand Down Expand Up @@ -567,6 +568,14 @@ class GeckoEngineSession(
visits.toBooleanArray()
}
}

override fun onHistoryStateChange(
session: GeckoSession,
historyList: GeckoSession.HistoryDelegate.HistoryList
) {
val items = historyList.map { HistoryItem(title = it.title, uri = it.uri) }
notifyObservers { onHistoryStateChanged(items, historyList.currentIndex) }
}
}

@Suppress("ComplexMethod")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import mozilla.components.concept.engine.EngineSession.SafeBrowsingPolicy
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.UnsupportedSettingException
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.permission.PermissionRequest
Expand Down Expand Up @@ -869,6 +870,43 @@ class GeckoEngineSessionTest {
verify(historyTrackingDelegate, never()).getVisited(anyList())
}

@Test
fun `notifies configured history delegate of state changes`() = runBlockingTest {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider,
context = coroutineContext)
val observer = mock<EngineSession.Observer>()
engineSession.register(observer)

captureDelegates()

class MockHistoryList(
items: List<GeckoSession.HistoryDelegate.HistoryItem>,
private val currentIndex: Int
) : ArrayList<GeckoSession.HistoryDelegate.HistoryItem>(items), GeckoSession.HistoryDelegate.HistoryList {
override fun getCurrentIndex() = currentIndex
}

fun mockHistoryItem(title: String, uri: String): GeckoSession.HistoryDelegate.HistoryItem {
val item = mock<GeckoSession.HistoryDelegate.HistoryItem>()
whenever(item.title).thenReturn(title)
whenever(item.uri).thenReturn(uri)
return item
}

historyDelegate.value.onHistoryStateChange(mock(), MockHistoryList(emptyList(), 0))
verify(observer).onHistoryStateChanged(emptyList(), 0)

historyDelegate.value.onHistoryStateChange(mock(), MockHistoryList(listOf(
mockHistoryItem("Firefox", "https://firefox.com"),
mockHistoryItem("Mozilla", "http://mozilla.org")
), 1))
verify(observer).onHistoryStateChanged(listOf(
HistoryItem("Firefox", "https://firefox.com"),
HistoryItem("Mozilla", "http://mozilla.org")
), 1)
}

@Test
fun websiteTitleUpdates() {
val engineSession = GeckoEngineSession(mock(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.Settings
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.manifest.WebAppManifestParser
import mozilla.components.concept.engine.request.RequestInterceptor
Expand Down Expand Up @@ -579,6 +580,14 @@ class GeckoEngineSession(
visits.toBooleanArray()
}
}

override fun onHistoryStateChange(
session: GeckoSession,
historyList: GeckoSession.HistoryDelegate.HistoryList
) {
val items = historyList.map { HistoryItem(title = it.title, uri = it.uri) }
notifyObservers { onHistoryStateChanged(items, historyList.currentIndex) }
}
}

@Suppress("ComplexMethod")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import mozilla.components.concept.engine.EngineSession.SafeBrowsingPolicy
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.UnsupportedSettingException
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.permission.PermissionRequest
Expand Down Expand Up @@ -900,6 +901,43 @@ class GeckoEngineSessionTest {
verify(historyTrackingDelegate, never()).getVisited(anyList())
}

@Test
fun `notifies configured history delegate of state changes`() = runBlockingTest {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider,
context = coroutineContext)
val observer = mock<EngineSession.Observer>()
engineSession.register(observer)

captureDelegates()

class MockHistoryList(
items: List<GeckoSession.HistoryDelegate.HistoryItem>,
private val currentIndex: Int
) : ArrayList<GeckoSession.HistoryDelegate.HistoryItem>(items), GeckoSession.HistoryDelegate.HistoryList {
override fun getCurrentIndex() = currentIndex
}

fun mockHistoryItem(title: String, uri: String): GeckoSession.HistoryDelegate.HistoryItem {
val item = mock<GeckoSession.HistoryDelegate.HistoryItem>()
whenever(item.title).thenReturn(title)
whenever(item.uri).thenReturn(uri)
return item
}

historyDelegate.value.onHistoryStateChange(mock(), MockHistoryList(emptyList(), 0))
verify(observer).onHistoryStateChanged(emptyList(), 0)

historyDelegate.value.onHistoryStateChange(mock(), MockHistoryList(listOf(
mockHistoryItem("Firefox", "https://firefox.com"),
mockHistoryItem("Mozilla", "http://mozilla.org")
), 1))
verify(observer).onHistoryStateChanged(listOf(
HistoryItem("Firefox", "https://firefox.com"),
HistoryItem("Mozilla", "http://mozilla.org")
), 1)
}

@Test
fun websiteTitleUpdates() {
val engineSession = GeckoEngineSession(mock(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.Settings
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.manifest.WebAppManifestParser
import mozilla.components.concept.engine.request.RequestInterceptor
Expand Down Expand Up @@ -565,6 +566,14 @@ class GeckoEngineSession(
visits.toBooleanArray()
}
}

override fun onHistoryStateChange(
session: GeckoSession,
historyList: GeckoSession.HistoryDelegate.HistoryList
) {
val items = historyList.map { HistoryItem(title = it.title, uri = it.uri) }
notifyObservers { onHistoryStateChanged(items, historyList.currentIndex) }
}
}

@Suppress("ComplexMethod")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import mozilla.components.concept.engine.EngineSession.SafeBrowsingPolicy
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.UnsupportedSettingException
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.permission.PermissionRequest
Expand Down Expand Up @@ -844,6 +845,43 @@ class GeckoEngineSessionTest {
verify(historyTrackingDelegate, never()).getVisited(anyList())
}

@Test
fun `notifies configured history delegate of state changes`() = runBlockingTest {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider,
context = coroutineContext)
val observer = mock<EngineSession.Observer>()
engineSession.register(observer)

captureDelegates()

class MockHistoryList(
items: List<GeckoSession.HistoryDelegate.HistoryItem>,
private val currentIndex: Int
) : ArrayList<GeckoSession.HistoryDelegate.HistoryItem>(items), GeckoSession.HistoryDelegate.HistoryList {
override fun getCurrentIndex() = currentIndex
}

fun mockHistoryItem(title: String, uri: String): GeckoSession.HistoryDelegate.HistoryItem {
val item = mock<GeckoSession.HistoryDelegate.HistoryItem>()
whenever(item.title).thenReturn(title)
whenever(item.uri).thenReturn(uri)
return item
}

historyDelegate.value.onHistoryStateChange(mock(), MockHistoryList(emptyList(), 0))
verify(observer).onHistoryStateChanged(emptyList(), 0)

historyDelegate.value.onHistoryStateChange(mock(), MockHistoryList(listOf(
mockHistoryItem("Firefox", "https://firefox.com"),
mockHistoryItem("Mozilla", "http://mozilla.org")
), 1))
verify(observer).onHistoryStateChanged(listOf(
HistoryItem("Firefox", "https://firefox.com"),
HistoryItem("Mozilla", "http://mozilla.org")
), 1)
}

@Test
fun websiteTitleUpdates() {
val engineSession = GeckoEngineSession(mock(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.media.Media
import mozilla.components.concept.engine.media.RecordingDevice
Expand Down Expand Up @@ -285,4 +286,12 @@ internal class EngineObserver(
override fun onRecordingStateChanged(devices: List<RecordingDevice>) {
session.recordingDevices = devices
}

override fun onHistoryStateChanged(historyList: List<HistoryItem>, currentIndex: Int) {
store?.dispatch(ContentAction.UpdateHistoryStateAction(
session.id,
historyList,
currentIndex
))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.Settings
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.media.Media
import mozilla.components.concept.engine.permission.PermissionRequest
Expand Down Expand Up @@ -704,4 +705,36 @@ class EngineObserverTest {

assertEquals("", session.searchTerms)
}

@Test
fun `onHistoryStateChanged dispatches UpdateHistoryStateAction`() {
val session = Session("")
val store = mock(BrowserStore::class.java)
val observer = EngineObserver(session, store)
whenever(store.dispatch(any())).thenReturn(mock())

observer.onHistoryStateChanged(emptyList(), 0)
verify(store).dispatch(
ContentAction.UpdateHistoryStateAction(
session.id,
emptyList(),
currentIndex = 0
)
)

observer.onHistoryStateChanged(listOf(
HistoryItem("Firefox", "https://firefox.com"),
HistoryItem("Mozilla", "http://mozilla.org")
), 1)
verify(store).dispatch(
ContentAction.UpdateHistoryStateAction(
session.id,
listOf(
HistoryItem("Firefox", "https://firefox.com"),
HistoryItem("Mozilla", "http://mozilla.org")
),
currentIndex = 1
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ 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.concept.engine.history.HistoryItem
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.media.Media
import mozilla.components.concept.engine.prompt.PromptRequest
Expand Down Expand Up @@ -288,6 +289,15 @@ sealed class ContentAction : BrowserAction() {
* Removes the [WebAppManifest] of the [ContentState] with the given [sessionId].
*/
data class RemoveWebAppManifestAction(val sessionId: String) : ContentAction()

/**
* Updates the [ContentState] of the given [sessionId] to indicate the current history state.
*/
data class UpdateHistoryStateAction(
val sessionId: String,
val historyList: List<HistoryItem>,
val currentIndex: Int
) : ContentAction()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.content.HistoryState

internal object ContentStateReducer {
/**
Expand Down Expand Up @@ -120,6 +121,9 @@ internal object ContentStateReducer {
is ContentAction.UpdateFirstContentfulPaintStateAction -> updateContentState(state, action.sessionId) {
it.copy(firstContentfulPaint = action.firstContentfulPaint)
}
is ContentAction.UpdateHistoryStateAction -> updateContentState(state, action.sessionId) {
it.copy(history = HistoryState(action.historyList, action.currentIndex))
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package mozilla.components.browser.state.state
import android.graphics.Bitmap
import mozilla.components.browser.state.state.content.DownloadState
import mozilla.components.browser.state.state.content.FindResultState
import mozilla.components.browser.state.state.content.HistoryState
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.concept.engine.prompt.PromptRequest
Expand Down Expand Up @@ -62,5 +63,6 @@ data class ContentState(
val canGoBack: Boolean = false,
val canGoForward: Boolean = false,
val webAppManifest: WebAppManifest? = null,
val firstContentfulPaint: Boolean = false
val firstContentfulPaint: Boolean = false,
val history: HistoryState = HistoryState()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.browser.state.state.content

import mozilla.components.concept.engine.history.HistoryItem

/**
* Value type that represents browser history.
*
* @property items All the items in the browser history.
* @property currentIndex The index of the currently selected [HistoryItem].
* If this is equal to lastIndex, then there are no pages to go "forward" to.
* If this is 0, then there are no pages to go "back" to.
*/
data class HistoryState(
val items: List<HistoryItem> = emptyList(),
val currentIndex: Int = 0
)
Loading

0 comments on commit 1a84e76

Please sign in to comment.