[Android] Fix WebView.CanGoBack() returning true on first navigated page due to synthetic about:blank history entry - #35841
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35841Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35841" |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
/review -b feature/enhanced-reviewer -p android |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
|
/review -b feature/enhanced-reviewer -p android |
@kubaflo I validated this approach locally before finalizing the fix. Although Android documentation states that a |
|
/review -b feature/enhanced-reviewer -p android |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
0f26c16 to
d78126b
Compare
d78126b to
29bf655
Compare
The branch conflict has been resolved. |
|
/azp run maui-pr-uitests , maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/review tests |
Tests Failure Analysis
Test Failure Review: Likely unrelated - click to expandOverall verdict: Likely unrelated All three failing pipelines (
Recommended actionNo action needed from the PR author. All failures are pre-existing on Evidence detailsPR scope: 6 changed files — Android WebView platform files only ( maui-pr (Build 1471951)
maui-pr-devicetests (Build 1472099)
maui-pr-uitests (Build 1472098)
AzDO access: Unauthenticated — test-run APIs were skipped; build metadata, timelines, and log excerpts were used as the primary data source. |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
…age due to synthetic about:blank history entry (#35841) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Root Cause PR #32145 introduced `LoadUrl("about:blank")` inside `WebViewExtensions.UpdateSource()` to fix the layout overflow issue when `WebView.Source` is `null` (#32030). Although the `Navigated` event was suppressed for this synthetic load via `IsBlankNavigation()`, Android's native WebView unconditionally records every `LoadUrl()` call into its internal history stack. This caused the history on the first real page load to become: ```text [0] about:blank [1] real-url ``` As a result, `CanGoBack()` incorrectly returned `true` on the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen. ## Description of Change The fix introduces an Android-only `IsLoadingForLayout` flag on `MauiWebView` to track synthetic `about:blank` loads and safely clear them from history after the first real navigation completes. ### MauiWebView.cs Added internal `bool IsLoadingForLayout` property as a shared state bridge between `WebViewExtensions` and `MauiWebViewClient`. ### WebViewExtensions.cs Sets `IsLoadingForLayout = true` before triggering `LoadUrl("about:blank")` in the null-source layout path, marking the load as synthetic. ### MauiWebViewClient.cs In `OnPageFinished`, when a real URL completes while `IsLoadingForLayout` is `true`, calls `ClearHistory()` to remove the synthetic `about:blank` entry while preserving the current page. The flag is then reset and `UpdateCanGoBackForward()` is invoked before `Navigated` fires, ensuring `CanGoBack = false` is already correct inside user handlers. If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations. ### WebViewHandler.Android.cs Resets `IsLoadingForLayout = false` inside `DisconnectHandler` to prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches. ## Regression Introduced By PR #32145 ### Issues Fixed Fixes #35788 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac **Note**: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files. ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/255b4ab4-f933-4837-85c7-ffb1dbf61d3e" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a6f9adf7-91c8-4e9c-aade-ef6e39780b7e" /> |
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue.
Thank you!
Root Cause
PR #32145 introduced
LoadUrl("about:blank")insideWebViewExtensions.UpdateSource()to fix the layout overflow issue whenWebView.Sourceisnull(#32030).Although the
Navigatedevent was suppressed for this synthetic load viaIsBlankNavigation(), Android's native WebView unconditionally records everyLoadUrl()call into its internal history stack. This caused the history on the first real page load to become:As a result,
CanGoBack()incorrectly returnedtrueon the first real page load. Pressing back navigated to the synthetic blank page instead of a real previous page, leaving the user on an empty screen.Description of Change
The fix introduces an Android-only
IsLoadingForLayoutflag onMauiWebViewto track syntheticabout:blankloads and safely clear them from history after the first real navigation completes.MauiWebView.cs
Added internal
bool IsLoadingForLayoutproperty as a shared state bridge betweenWebViewExtensionsandMauiWebViewClient.WebViewExtensions.cs
Sets
IsLoadingForLayout = truebefore triggeringLoadUrl("about:blank")in the null-source layout path, marking the load as synthetic.MauiWebViewClient.cs
In
OnPageFinished, when a real URL completes whileIsLoadingForLayoutistrue, callsClearHistory()to remove the syntheticabout:blankentry while preserving the current page.The flag is then reset and
UpdateCanGoBackForward()is invoked beforeNavigatedfires, ensuringCanGoBack = falseis already correct inside user handlers.If the real navigation fails, the flag is reset immediately so stale state cannot incorrectly clear history on later navigations.
WebViewHandler.Android.cs
Resets
IsLoadingForLayout = falseinsideDisconnectHandlerto prevent stale state from surviving handler disconnect and reconnect scenarios such as Shell tab switches.Regression Introduced By
PR #32145
Issues Fixed
Fixes #35788
Tested the behaviour in the following platforms
Note: This is an Android-only regression. Android's native WebView unconditionally records every LoadUrl() call into its history stack with no API to suppress individual entries. iOS uses WKWebView and Windows uses WebView2 — both manage history differently and are unaffected. The fix is scoped entirely to Android platform files.
Screenshots
BeforeFix.40.mov
AfterFix.47.mov