fix app link issues
This commit is contained in:
+2
@@ -24,6 +24,7 @@ import eu.weblibre.flutter_mozilla_components.services.MediaSessionService
|
|||||||
import eu.weblibre.flutter_mozilla_components.activities.NotificationActivity
|
import eu.weblibre.flutter_mozilla_components.activities.NotificationActivity
|
||||||
import eu.weblibre.flutter_mozilla_components.R
|
import eu.weblibre.flutter_mozilla_components.R
|
||||||
import eu.weblibre.flutter_mozilla_components.ext.getPreferenceKey
|
import eu.weblibre.flutter_mozilla_components.ext.getPreferenceKey
|
||||||
|
import eu.weblibre.flutter_mozilla_components.middleware.AppLinksCancelRetryMiddleware
|
||||||
import eu.weblibre.flutter_mozilla_components.middleware.FlutterEventMiddleware
|
import eu.weblibre.flutter_mozilla_components.middleware.FlutterEventMiddleware
|
||||||
import eu.weblibre.flutter_mozilla_components.middleware.HistoryMetadataMiddleware
|
import eu.weblibre.flutter_mozilla_components.middleware.HistoryMetadataMiddleware
|
||||||
import eu.weblibre.flutter_mozilla_components.middleware.HistoryMetadataService
|
import eu.weblibre.flutter_mozilla_components.middleware.HistoryMetadataService
|
||||||
@@ -231,6 +232,7 @@ class Core(
|
|||||||
// Must run before any engine middleware so we can rewrite
|
// Must run before any engine middleware so we can rewrite
|
||||||
// sandbox new-tab URLs before Gecko issues a request.
|
// sandbox new-tab URLs before Gecko issues a request.
|
||||||
SandboxCaptureMiddleware,
|
SandboxCaptureMiddleware,
|
||||||
|
AppLinksCancelRetryMiddleware(),
|
||||||
HistoryMetadataMiddleware(historyMetadataService),
|
HistoryMetadataMiddleware(historyMetadataService),
|
||||||
// Correlates url -> contextId so WebLibreHistoryDelegate can
|
// Correlates url -> contextId so WebLibreHistoryDelegate can
|
||||||
// resolve a visit's container at record time.
|
// resolve a visit's container at record time.
|
||||||
|
|||||||
+149
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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 https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.weblibre.flutter_mozilla_components.middleware
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import mozilla.components.browser.state.action.BrowserAction
|
||||||
|
import mozilla.components.browser.state.action.ContentAction
|
||||||
|
import mozilla.components.browser.state.action.EngineAction
|
||||||
|
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
||||||
|
import mozilla.components.browser.state.state.BrowserState
|
||||||
|
import mozilla.components.concept.engine.EngineSession
|
||||||
|
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags.Companion.EXTERNAL
|
||||||
|
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags.Companion.LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE
|
||||||
|
import mozilla.components.lib.state.Middleware
|
||||||
|
import mozilla.components.lib.state.Store
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround for Android Components/GeckoView app-link cancel handling: the
|
||||||
|
* cancel load can be clobbered by Gecko's recovery load back to the previous
|
||||||
|
* history entry. This retries only that specific cancel-load signature.
|
||||||
|
*/
|
||||||
|
class AppLinksCancelRetryMiddleware(
|
||||||
|
private val handler: Handler = Handler(Looper.getMainLooper()),
|
||||||
|
private val retryDelayMillis: Long = RETRY_DELAY_MILLIS,
|
||||||
|
) : Middleware<BrowserState, BrowserAction> {
|
||||||
|
private val pendingCancels = mutableMapOf<String, PendingCancel>()
|
||||||
|
|
||||||
|
override fun invoke(
|
||||||
|
store: Store<BrowserState, BrowserAction>,
|
||||||
|
next: (BrowserAction) -> Unit,
|
||||||
|
action: BrowserAction,
|
||||||
|
) {
|
||||||
|
when (action) {
|
||||||
|
is EngineAction.OptimizedLoadUrlTriggeredAction -> {
|
||||||
|
recordCancelLoad(store, action)
|
||||||
|
}
|
||||||
|
is ContentAction.UpdateLoadRequestAction -> {
|
||||||
|
handleLoadRequest(store, action)
|
||||||
|
}
|
||||||
|
is ContentAction.UpdateUrlAction -> {
|
||||||
|
handleUrlUpdate(action)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
next(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordCancelLoad(
|
||||||
|
store: Store<BrowserState, BrowserAction>,
|
||||||
|
action: EngineAction.OptimizedLoadUrlTriggeredAction,
|
||||||
|
) {
|
||||||
|
if (!action.flags.contains(EXTERNAL) ||
|
||||||
|
!action.flags.contains(LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourceUrl = store.state.findTabOrCustomTab(action.tabId)?.content?.url
|
||||||
|
?: return
|
||||||
|
if (sourceUrl == action.url || sourceUrl == ABOUT_BLANK) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingCancels[action.tabId] = PendingCancel(
|
||||||
|
tabId = action.tabId,
|
||||||
|
sourceUrl = sourceUrl,
|
||||||
|
targetUrl = action.url,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLoadRequest(
|
||||||
|
store: Store<BrowserState, BrowserAction>,
|
||||||
|
action: ContentAction.UpdateLoadRequestAction,
|
||||||
|
) {
|
||||||
|
val pending = pendingCancels[action.sessionId] ?: return
|
||||||
|
when (action.loadRequest.url) {
|
||||||
|
pending.sourceUrl -> scheduleRetry(store, pending)
|
||||||
|
pending.targetUrl -> pendingCancels.remove(action.sessionId)
|
||||||
|
ABOUT_BLANK -> {}
|
||||||
|
else -> pendingCancels.remove(action.sessionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleUrlUpdate(action: ContentAction.UpdateUrlAction) {
|
||||||
|
val pending = pendingCancels[action.sessionId] ?: return
|
||||||
|
when (action.url) {
|
||||||
|
pending.targetUrl -> pendingCancels.remove(action.sessionId)
|
||||||
|
pending.sourceUrl, ABOUT_BLANK -> {}
|
||||||
|
else -> pendingCancels.remove(action.sessionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scheduleRetry(
|
||||||
|
store: Store<BrowserState, BrowserAction>,
|
||||||
|
pending: PendingCancel,
|
||||||
|
) {
|
||||||
|
if (pending.retryScheduled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val scheduled = pending.copy(retryScheduled = true)
|
||||||
|
pendingCancels[pending.tabId] = scheduled
|
||||||
|
|
||||||
|
handler.postDelayed({
|
||||||
|
if (pendingCancels[pending.tabId] != scheduled) {
|
||||||
|
return@postDelayed
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentUrl = store.state.findTabOrCustomTab(pending.tabId)?.content?.url
|
||||||
|
if (currentUrl == pending.targetUrl) {
|
||||||
|
pendingCancels.remove(pending.tabId)
|
||||||
|
return@postDelayed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUrl == pending.sourceUrl || currentUrl == ABOUT_BLANK) {
|
||||||
|
pendingCancels.remove(pending.tabId)
|
||||||
|
store.dispatch(
|
||||||
|
EngineAction.LoadUrlAction(
|
||||||
|
tabId = pending.tabId,
|
||||||
|
url = pending.targetUrl,
|
||||||
|
flags = EngineSession.LoadUrlFlags.select(
|
||||||
|
LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
pendingCancels.remove(pending.tabId)
|
||||||
|
}
|
||||||
|
}, retryDelayMillis)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class PendingCancel(
|
||||||
|
val tabId: String,
|
||||||
|
val sourceUrl: String,
|
||||||
|
val targetUrl: String,
|
||||||
|
val retryScheduled: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val ABOUT_BLANK = "about:blank"
|
||||||
|
const val RETRY_DELAY_MILLIS = 1000L
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user