Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.duckduckgo.app.browser.LongPressHandler.RequiredAction.OpenInNewTab
import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserDetector
import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserNotification
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
import com.duckduckgo.app.browser.session.WebViewSessionStorage
import com.duckduckgo.app.global.db.AppConfigurationDao
import com.duckduckgo.app.global.db.AppConfigurationEntity
import com.duckduckgo.app.global.db.AppDatabase
Expand Down Expand Up @@ -110,6 +111,9 @@ class BrowserTabViewModelTest {
@Mock
private lateinit var tabsDao: TabsDao

@Mock
private lateinit var webViewSessionStorage: WebViewSessionStorage

@Captor
private lateinit var commandCaptor: ArgumentCaptor<Command>

Expand Down Expand Up @@ -143,7 +147,8 @@ class BrowserTabViewModelTest {
defaultBrowserNotification = mockDefaultBrowserNotification,
defaultBrowserDetector = mockDefaultBrowserDetector,
longPressHandler = mockLongPressHandler,
appConfigurationDao = appConfigurationDao
appConfigurationDao = appConfigurationDao,
webViewSessionStorage = webViewSessionStorage
)

testee.loadData("abc", null)
Expand Down Expand Up @@ -684,6 +689,40 @@ class BrowserTabViewModelTest {
verify(mockCommandObserver, never()).onChanged(any())
}

@Test
fun whenWebSessionRestoredThenGlobalLayoutSwitchedToShowingBrowser() {
testee.onWebSessionRestored()
assertFalse(globalLayoutViewState().isNewTabState)
}

@Test
fun whenWebViewSessionIsToBeSavedThenUnderlyingSessionStoredCalled() {
testee.saveWebViewState(null, "")
verify(webViewSessionStorage).saveSession(anyOrNull(), anyString())
}

@Test
fun whenRestoringWebViewSessionNotRestorableThenPreviousUrlLoaded() {
whenever(mockOmnibarConverter.convertQueryToUrl("foo.com")).thenReturn("foo.com")
whenever(webViewSessionStorage.restoreSession(anyOrNull(), anyString())).thenReturn(false)
testee.restoreWebViewState(null, "foo.com")
assertEquals("foo.com", testee.url.value)
}

@Test
fun whenRestoringWebViewSessionNotRestorableAndNoPreviousUrlThenNoUrlLoaded() {
whenever(webViewSessionStorage.restoreSession(anyOrNull(), anyString())).thenReturn(false)
testee.restoreWebViewState(null, "")
assertNull(testee.url.value)
}

@Test
fun whenWebViewSessionRestorableThenSessionRestored() {
whenever(webViewSessionStorage.restoreSession(anyOrNull(), anyString())).thenReturn(true)
testee.restoreWebViewState(null, "")
assertFalse(globalLayoutViewState().isNewTabState)
}

private fun captureCommands(): ArgumentCaptor<Command> {
verify(mockCommandObserver, Mockito.atLeastOnce()).onChanged(commandCaptor.capture())
return commandCaptor
Expand All @@ -694,4 +733,5 @@ class BrowserTabViewModelTest {
private fun loadingViewState() = testee.loadingViewState.value!!
private fun autoCompleteViewState() = testee.autoCompleteViewState.value!!
private fun findInPageViewState() = testee.findInPageViewState.value!!
private fun globalLayoutViewState() = testee.globalLayoutState.value!!
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import android.webkit.CookieManager
import android.webkit.ValueCallback
import android.webkit.WebStorage
import android.webkit.WebView
import com.duckduckgo.app.browser.session.WebViewSessionInMemoryStorage
import com.nhaarman.mockito_kotlin.*
import org.junit.Assert.assertTrue
import org.junit.Test
Expand All @@ -36,7 +37,7 @@ class WebDataManagerTest {

private val mockStorage: WebStorage = mock()

private val testee = WebDataManager(host)
private val testee = WebDataManager(host, WebViewSessionInMemoryStorage())

@UiThreadTest
@Test
Expand All @@ -60,7 +61,7 @@ class WebDataManagerTest {

whenever(mockCookieManager.getCookie(host)).thenReturn("da=abc; dz=zyx")
whenever(mockCookieManager.getCookie(externalHost)).thenReturn("ea=abc; ez=zyx")
testee.clearExternalCookies(mockCookieManager, {})
testee.clearExternalCookies(mockCookieManager) {}

val captor = argumentCaptor<ValueCallback<Boolean>>()
verify(mockCookieManager).removeAllCookies(captor.capture())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.duckduckgo.app.tabs.ui
import android.arch.core.executor.testing.InstantTaskExecutorRule
import android.arch.lifecycle.Observer
import com.duckduckgo.app.browser.R
import com.duckduckgo.app.browser.session.WebViewSessionInMemoryStorage
import com.duckduckgo.app.tabs.model.TabEntity
import com.duckduckgo.app.tabs.model.TabRepository
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command
Expand Down Expand Up @@ -53,7 +54,7 @@ class TabSwitcherViewModelTest {
fun before() {
MockitoAnnotations.initMocks(this)
whenever(mockTabRepository.add()).thenReturn("TAB_ID")
testee = TabSwitcherViewModel(mockTabRepository)
testee = TabSwitcherViewModel(mockTabRepository, WebViewSessionInMemoryStorage())
testee.command.observeForever(mockCommandObserver)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class BrowserActivity : DuckDuckGoActivity() {
@Inject
lateinit var viewModelFactory: ViewModelFactory

@Inject
lateinit var webDataManager: WebDataManager

private var currentTab: BrowserTabFragment? = null

private val viewModel: BrowserViewModel by lazy {
Expand Down Expand Up @@ -168,6 +171,7 @@ class BrowserActivity : DuckDuckGoActivity() {

fun launchFire() {
FireDialog(context = this,
webDataManager = webDataManager,
clearStarted = { viewModel.onClearRequested() },
clearComplete = { viewModel.onClearComplete() }
).show()
Expand Down
15 changes: 13 additions & 2 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import com.duckduckgo.app.browser.downloader.FileDownloader
import com.duckduckgo.app.browser.downloader.FileDownloader.PendingFileDownload
import com.duckduckgo.app.browser.filechooser.FileChooserIntentBuilder
import com.duckduckgo.app.browser.omnibar.KeyboardAwareEditText
import com.duckduckgo.app.browser.session.WebViewSessionStorage
import com.duckduckgo.app.browser.useragent.UserAgentProvider
import com.duckduckgo.app.global.ViewModelFactory
import com.duckduckgo.app.global.view.*
Expand Down Expand Up @@ -104,6 +105,9 @@ class BrowserTabFragment : Fragment(), FindListener {
@Inject
lateinit var fileDownloadNotificationManager: FileDownloadNotificationManager

@Inject
lateinit var webViewSessionStorage: WebViewSessionStorage

val tabId get() = arguments!![TAB_ID_ARG] as String

private val initialUrl get() = arguments!![URL_EXTRA_ARG] as String?
Expand Down Expand Up @@ -591,14 +595,19 @@ class BrowserTabFragment : Fragment(), FindListener {
}
}

/**
* Attempting to save the WebView's state can result in a TransactionTooLargeException being thrown.
* This will only happen if the bundle size is too large - but the exact size is undefined.
* Instead of saving using normal Android state mechanism - use our own implementation instead.
*/
override fun onSaveInstanceState(bundle: Bundle) {
webView?.saveState(bundle)
viewModel.saveWebViewState(webView, tabId)
super.onSaveInstanceState(bundle)
}

override fun onViewStateRestored(bundle: Bundle?) {
viewModel.restoreWebViewState(webView, omnibarTextInput.text.toString())
super.onViewStateRestored(bundle)
webView?.restoreState(bundle)
}

override fun onHiddenChanged(hidden: Boolean) {
Expand Down Expand Up @@ -827,6 +836,8 @@ class BrowserTabFragment : Fragment(), FindListener {

fun renderGlobalViewState(viewState: GlobalLayoutViewState) {
renderIfChanged(viewState, lastSeenGlobalViewState) {
lastSeenGlobalViewState = viewState

if (viewState.isNewTabState) {
newTabLayout.show()
browserLayout.hide()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.duckduckgo.app.browser.LongPressHandler.RequiredAction
import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserDetector
import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserNotification
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
import com.duckduckgo.app.browser.session.WebViewSessionStorage
import com.duckduckgo.app.global.SingleLiveEvent
import com.duckduckgo.app.global.db.AppConfigurationDao
import com.duckduckgo.app.global.db.AppConfigurationEntity
Expand Down Expand Up @@ -77,6 +78,7 @@ class BrowserTabViewModel(
private val defaultBrowserDetector: DefaultBrowserDetector,
private val defaultBrowserNotification: DefaultBrowserNotification,
private val longPressHandler: LongPressHandler,
private val webViewSessionStorage: WebViewSessionStorage,
appConfigurationDao: AppConfigurationDao
) : WebViewClientListener, SaveBookmarkListener, ViewModel() {

Expand Down Expand Up @@ -508,6 +510,10 @@ class BrowserTabViewModel(
numberMatches = numberOfMatches)
}

fun onWebSessionRestored() {
globalLayoutState.value = GlobalLayoutViewState(isNewTabState = false)
}

fun desktopSiteModeToggled(urlString: String?, desktopSiteRequested: Boolean) {
val currentBrowserViewState = currentBrowserViewState()
browserViewState.value = currentBrowserViewState.copy(isDesktopBrowsingMode = desktopSiteRequested)
Expand Down Expand Up @@ -559,6 +565,23 @@ class BrowserTabViewModel(
val currentDefaultBrowserViewState = currentDefaultBrowserViewState()
defaultBrowserViewState.value = currentDefaultBrowserViewState.copy(showHomeScreenCallToActionButton = false)
}

fun saveWebViewState(webView: WebView?, tabId: String) {
webViewSessionStorage.saveSession(webView, tabId)
}

fun restoreWebViewState(webView: WebView?, lastUrl: String) {
val sessionRestored = webViewSessionStorage.restoreSession(webView, tabId)
if (sessionRestored) {
Timber.v("Successfully restored session")
onWebSessionRestored()
} else {
if (lastUrl.isNotBlank()) {
Timber.w("Restoring last url but page history has been lost - url=[$lastUrl]")
onUserSubmittedQuery(lastUrl)
}
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import android.webkit.CookieManager
import android.webkit.WebStorage
import android.webkit.WebView
import android.webkit.WebViewDatabase
import com.duckduckgo.app.browser.session.WebViewSessionStorage

class WebDataManager(private val host: String) {
class WebDataManager(private val host: String, private val webViewSessionStorage: WebViewSessionStorage) {

fun clearData(webView: WebView, webStorage: WebStorage, context: Context) {
webView.clearCache(true)
Expand Down Expand Up @@ -53,4 +54,8 @@ class WebDataManager(private val host: String) {
clearAllCallback()
}
}

fun clearWebViewSessions() {
webViewSessionStorage.deleteAllSessions()
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import android.content.Context
import com.duckduckgo.app.browser.*
import com.duckduckgo.app.browser.defaultBrowsing.AndroidDefaultBrowserDetector
import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserDetector
import com.duckduckgo.app.browser.session.WebViewSessionInMemoryStorage
import com.duckduckgo.app.browser.session.WebViewSessionStorage
import com.duckduckgo.app.global.AppUrl
import com.duckduckgo.app.global.install.AppInstallStore
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.store.StatisticsDataStore
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

@Module
class BrowserModule {
Expand All @@ -47,4 +51,12 @@ class BrowserModule {
fun defaultWebBrowserCapability(context: Context, appInstallStore: AppInstallStore): DefaultBrowserDetector {
return AndroidDefaultBrowserDetector(context, appInstallStore)
}

@Singleton
@Provides
fun webViewSessionStorage(): WebViewSessionStorage = WebViewSessionInMemoryStorage()

@Singleton
@Provides
fun webDataManager(webViewSessionStorage: WebViewSessionStorage) = WebDataManager(AppUrl.Url.HOST, webViewSessionStorage)
}
Loading