Skip to content

Commit 12be8c6

Browse files
committed
Merge branch 'release/5.36.2'
2 parents 56d31bb + 382b19a commit 12be8c6

File tree

8 files changed

+110
-12
lines changed

8 files changed

+110
-12
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2019 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.navigation
18+
19+
import android.content.Context
20+
import android.webkit.WebBackForwardList
21+
import android.webkit.WebView
22+
import androidx.test.annotation.UiThreadTest
23+
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
24+
import org.junit.Assert.assertNotNull
25+
import org.junit.Assert.assertNull
26+
import org.junit.Test
27+
28+
class WebViewBackForwardListSafeExtractorTest {
29+
30+
private val context = getInstrumentation().targetContext
31+
32+
@UiThreadTest
33+
@Test
34+
fun whenCopyBackForwardListCalledAndExceptionThrownThenNavigationListIsNull() {
35+
val testWebView = TestWebViewThrowsNullPointerException(context)
36+
assertNull(testWebView.safeCopyBackForwardList())
37+
}
38+
39+
@UiThreadTest
40+
@Test
41+
fun whenCopyBackForwardListCalledAndNoExceptionThrownThenNavigationListIsNotNull() {
42+
val testWebView = WebView(context)
43+
assertNotNull(testWebView.safeCopyBackForwardList())
44+
}
45+
}
46+
47+
class TestWebViewThrowsNullPointerException(context: Context) : WebView(context) {
48+
49+
override fun copyBackForwardList(): WebBackForwardList {
50+
throw NullPointerException("Deliberate")
51+
}
52+
}

app/src/main/java/com/duckduckgo/app/browser/BrowserChromeClient.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import android.view.View
2121
import android.webkit.ValueCallback
2222
import android.webkit.WebChromeClient
2323
import android.webkit.WebView
24+
import com.duckduckgo.app.browser.navigation.safeCopyBackForwardList
2425
import com.duckduckgo.app.global.exception.UncaughtExceptionRepository
2526
import com.duckduckgo.app.global.exception.UncaughtExceptionSource.*
2627
import kotlinx.coroutines.*
@@ -75,7 +76,7 @@ class BrowserChromeClient @Inject constructor(private val uncaughtExceptionRepos
7576
override fun onProgressChanged(webView: WebView, newProgress: Int) {
7677
try {
7778
Timber.d("onProgressChanged ${webView.url}, $newProgress")
78-
val navigationList = webView.copyBackForwardList()
79+
val navigationList = webView.safeCopyBackForwardList() ?: return
7980
webViewClientListener?.navigationStateChanged(WebViewNavigationState(navigationList))
8081
webViewClientListener?.progressChanged(newProgress)
8182
} catch (e: Throwable) {

app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
627627
}
628628

629629
viewModel.privacyGrade.observe(this, Observer<PrivacyGrade> {
630-
Timber.i("Observed grade: $it")
630+
Timber.d("Observed grade: $it")
631631
it?.let { privacyGrade ->
632632
val drawable = context?.getDrawable(privacyGrade.icon()) ?: return@let
633633
privacyGradeButton?.setImageDrawable(drawable)

app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import androidx.annotation.WorkerThread
2626
import com.duckduckgo.app.browser.BrowserWebViewClient.RequestOrigin.MainFrame
2727
import com.duckduckgo.app.browser.BrowserWebViewClient.RequestOrigin.SubFrame
2828
import com.duckduckgo.app.browser.model.BasicAuthenticationRequest
29+
import com.duckduckgo.app.browser.navigation.safeCopyBackForwardList
2930
import com.duckduckgo.app.global.exception.UncaughtExceptionRepository
3031
import com.duckduckgo.app.global.exception.UncaughtExceptionSource.*
3132
import com.duckduckgo.app.statistics.store.OfflinePixelCountDataStore
@@ -88,7 +89,8 @@ class BrowserWebViewClient(
8889
@UiThread
8990
override fun onPageStarted(webView: WebView, url: String?, favicon: Bitmap?) {
9091
try {
91-
webViewClientListener?.navigationStateChanged(WebViewNavigationState(webView.copyBackForwardList()))
92+
val navigationList = webView.safeCopyBackForwardList() ?: return
93+
webViewClientListener?.navigationStateChanged(WebViewNavigationState(navigationList))
9294
if (url != null && url == lastPageStarted) {
9395
webViewClientListener?.pageRefreshed(url)
9496
}
@@ -104,7 +106,8 @@ class BrowserWebViewClient(
104106
@UiThread
105107
override fun onPageFinished(webView: WebView, url: String?) {
106108
try {
107-
webViewClientListener?.navigationStateChanged(WebViewNavigationState(webView.copyBackForwardList()))
109+
val navigationList = webView.safeCopyBackForwardList() ?: return
110+
webViewClientListener?.navigationStateChanged(WebViewNavigationState(navigationList))
108111
} catch (e: Throwable) {
109112
GlobalScope.launch {
110113
uncaughtExceptionRepository.recordUncaughtException(e, ON_PAGE_FINISHED)

app/src/main/java/com/duckduckgo/app/browser/WebNavigationState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,6 @@ private val WebBackForwardList.isHttpsUpgrade: Boolean
132132
get() {
133133
if (currentIndex < 1) return false
134134
val current = originalUrl?.toUri() ?: return false
135-
val previous = getItemAtIndex(currentIndex - 1).originalUrl?.toUri() ?: return false
135+
val previous = getItemAtIndex(currentIndex - 1)?.originalUrl?.toUri() ?: return false
136136
return current.isHttpsVersionOfUri(previous)
137137
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2019 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.navigation
18+
19+
import android.webkit.WebBackForwardList
20+
import android.webkit.WebView
21+
import timber.log.Timber
22+
23+
/**
24+
* There is a bug in WebView whereby `webView.copyBackForwardList()` can internally throw a NPE
25+
*
26+
* This extension function can be used as a direct replacement of `copyBackForwardList()`
27+
* It will catch the NullPointerException and return `null` when it happens.
28+
*
29+
* https://bugs.chromium.org/p/chromium/issues/detail?id=498796
30+
*/
31+
fun WebView.safeCopyBackForwardList(): WebBackForwardList? {
32+
return try {
33+
copyBackForwardList()
34+
} catch (e: NullPointerException) {
35+
Timber.e(e, "Failed to extract WebView back forward list")
36+
null
37+
}
38+
}

app/src/main/java/com/duckduckgo/app/global/AlertingUncaughtExceptionHandler.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import com.duckduckgo.app.global.exception.UncaughtExceptionRepository
2020
import com.duckduckgo.app.global.exception.UncaughtExceptionSource
2121
import com.duckduckgo.app.statistics.store.OfflinePixelCountDataStore
2222
import kotlinx.coroutines.Dispatchers
23-
import kotlinx.coroutines.GlobalScope
24-
import kotlinx.coroutines.launch
23+
import kotlinx.coroutines.runBlocking
24+
import kotlinx.coroutines.withContext
2525

2626
class AlertingUncaughtExceptionHandler(
2727
private val originalHandler: Thread.UncaughtExceptionHandler,
@@ -30,11 +30,15 @@ class AlertingUncaughtExceptionHandler(
3030
) : Thread.UncaughtExceptionHandler {
3131

3232
override fun uncaughtException(t: Thread?, originalException: Throwable?) {
33-
GlobalScope.launch(Dispatchers.IO) {
34-
uncaughtExceptionRepository.recordUncaughtException(originalException, UncaughtExceptionSource.GLOBAL)
3533

36-
offlinePixelCountDataStore.applicationCrashCount += 1
37-
originalHandler.uncaughtException(t, originalException)
34+
// block until the exception has been fully processed
35+
runBlocking {
36+
withContext(Dispatchers.IO) {
37+
uncaughtExceptionRepository.recordUncaughtException(originalException, UncaughtExceptionSource.GLOBAL)
38+
offlinePixelCountDataStore.applicationCrashCount += 1
39+
}
3840
}
41+
originalHandler.uncaughtException(t, originalException)
3942
}
43+
4044
}

app/version/version.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION=5.36.1
1+
VERSION=5.36.2

0 commit comments

Comments
 (0)