From 7c9d929457f0093fd4ff2c3ef7251d8912a8cd7c Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Tue, 19 May 2020 10:20:54 -0700 Subject: [PATCH] Closes #7023 - Add HistoryState to content state --- .../engine/gecko/GeckoEngineSession.kt | 9 ++++++++ .../engine/gecko/GeckoEngineSession.kt | 9 ++++++++ .../engine/gecko/GeckoEngineSession.kt | 9 ++++++++ .../browser/session/engine/EngineObserver.kt | 9 ++++++++ .../browser/state/action/BrowserAction.kt | 10 ++++++++ .../state/reducer/ContentStateReducer.kt | 4 ++++ .../browser/state/state/ContentState.kt | 4 +++- .../state/state/content/HistoryState.kt | 20 ++++++++++++++++ .../browser/state/action/ContentActionTest.kt | 23 +++++++++++++++++++ .../concept/engine/EngineSession.kt | 9 ++++++++ .../concept/engine/history/HistoryItem.kt | 15 ++++++++++++ docs/changelog.md | 6 +++++ 12 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 components/browser/state/src/main/java/mozilla/components/browser/state/state/content/HistoryState.kt create mode 100644 components/concept/engine/src/main/java/mozilla/components/concept/engine/history/HistoryItem.kt diff --git a/components/browser/engine-gecko-beta/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt b/components/browser/engine-gecko-beta/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt index be731d43a2d..1bf06aff3e6 100644 --- a/components/browser/engine-gecko-beta/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt +++ b/components/browser/engine-gecko-beta/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt @@ -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 @@ -580,6 +581,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") diff --git a/components/browser/engine-gecko-nightly/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt b/components/browser/engine-gecko-nightly/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt index 74c41d99193..97e8d260544 100644 --- a/components/browser/engine-gecko-nightly/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt +++ b/components/browser/engine-gecko-nightly/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt @@ -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 @@ -592,6 +593,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") diff --git a/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt b/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt index 030cd8669e2..c0bd0dde536 100644 --- a/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt +++ b/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt @@ -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 @@ -578,6 +579,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") diff --git a/components/browser/session/src/main/java/mozilla/components/browser/session/engine/EngineObserver.kt b/components/browser/session/src/main/java/mozilla/components/browser/session/engine/EngineObserver.kt index 7f5e156c0ab..b93ae66586b 100644 --- a/components/browser/session/src/main/java/mozilla/components/browser/session/engine/EngineObserver.kt +++ b/components/browser/session/src/main/java/mozilla/components/browser/session/engine/EngineObserver.kt @@ -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 @@ -285,4 +286,12 @@ internal class EngineObserver( override fun onRecordingStateChanged(devices: List) { session.recordingDevices = devices } + + override fun onHistoryStateChanged(historyList: List, currentIndex: Int) { + store?.dispatch(ContentAction.UpdateHistoryStateAction( + session.id, + historyList, + currentIndex + )) + } } diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt index 30d32bd2cf7..3ec25e49167 100644 --- a/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt @@ -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 @@ -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, + val currentIndex: Int + ) : ContentAction() } /** diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt index 52711449c6c..a9414cb698f 100644 --- a/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt @@ -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 { /** @@ -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)) + } } } } diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt index e8557c1de95..8b433c982a7 100644 --- a/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt @@ -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 @@ -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() ) diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/state/content/HistoryState.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/state/content/HistoryState.kt new file mode 100644 index 00000000000..49b6c9abc8d --- /dev/null +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/state/content/HistoryState.kt @@ -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 = emptyList(), + val currentIndex: Int = 0 +) diff --git a/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt b/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt index 32fe396a39f..44fde5238be 100644 --- a/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt +++ b/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt @@ -12,10 +12,12 @@ import mozilla.components.browser.state.state.SecurityInfoState import mozilla.components.browser.state.state.TabSessionState 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.browser.state.state.createCustomTab import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.HitResult +import mozilla.components.concept.engine.history.HistoryItem import mozilla.components.concept.engine.manifest.WebAppManifest import mozilla.components.concept.engine.prompt.PromptRequest import mozilla.components.concept.engine.window.WindowRequest @@ -600,4 +602,25 @@ class ContentActionTest { assertNull(tab.content.webAppManifest) } + + @Test + fun `UpdateHistoryStateAction updates history state`() { + val historyState = HistoryState( + items = listOf( + HistoryItem("Mozilla", "https://mozilla.org"), + HistoryItem("Firefox", "https://firefox.com") + ), + currentIndex = 1 + ) + + assertNotEquals(historyState, tab.content.history) + assertNotEquals(historyState, otherTab.content.history) + + store.dispatch( + ContentAction.UpdateHistoryStateAction(tab.id, historyState.items, historyState.currentIndex) + ).joinBlocking() + + assertEquals(historyState, tab.content.history) + assertNotEquals(historyState, otherTab.content.history) + } } diff --git a/components/concept/engine/src/main/java/mozilla/components/concept/engine/EngineSession.kt b/components/concept/engine/src/main/java/mozilla/components/concept/engine/EngineSession.kt index a4b88a4cd5b..472790fbaae 100644 --- a/components/concept/engine/src/main/java/mozilla/components/concept/engine/EngineSession.kt +++ b/components/concept/engine/src/main/java/mozilla/components/concept/engine/EngineSession.kt @@ -11,6 +11,7 @@ import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy. import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_ALL import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NON_TRACKERS 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 @@ -122,6 +123,14 @@ abstract class EngineSession( cookie: String? = null, userAgent: String? = null ) = Unit + + /** + * Event to indicate that this session has changed its history state. + * + * @param historyList The list of items in the session history. + * @param currentIndex Index of the current page in the history list. + */ + fun onHistoryStateChanged(historyList: List, currentIndex: Int) = Unit } /** diff --git a/components/concept/engine/src/main/java/mozilla/components/concept/engine/history/HistoryItem.kt b/components/concept/engine/src/main/java/mozilla/components/concept/engine/history/HistoryItem.kt new file mode 100644 index 00000000000..661c4214a08 --- /dev/null +++ b/components/concept/engine/src/main/java/mozilla/components/concept/engine/history/HistoryItem.kt @@ -0,0 +1,15 @@ +/* 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.concept.engine.history + +/** + * A representation of an entry in browser history. + * @property title The title of this history element. + * @property uri The URI of this history element. + */ +data class HistoryItem( + val title: String, + val uri: String +) diff --git a/docs/changelog.md b/docs/changelog.md index b8bdf0b627a..3d2733a16fd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -15,6 +15,12 @@ permalink: /changelog/ * **feature-media** * Adds `MediaFullscreenOrientationFeature` to autorotate activity while in fullscreen based on media aspect ratio. +* **concept-engine** + * Adds `onHistoryStateChanged` method and corresponding `HistoryItem` data class. + +* **browser-state** + * Adds `history` to `ContentState` to check the back and forward history list. + # 42.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v41.0.0...42.0.0)