Repository navigation
Conversation
When its ImageSource changed, an ImageBrush subscribed to the new source before it disposed its subscription to the old one. A new source that has its image, or opens synchronously (a WriteableBitmap), delivers it while the brush subscribes, and the brush raises ImageOpened then. A handler that made the old source open again synchronously, such as oldBitmap.Invalidate(), still reached the brush, which then drew the old image for the new source. The brush now disposes the old subscription first, as Image does and as WinUI removes its callback from the old source first (CImageBrush::SetValue). A source that the brush's ImageOpened handler replaced while the brush subscribed to it keeps its subscription until that subscribe returns, and a synchronous open of it in between reached the brush too. Each subscription's callback now carries the source version it was made for, and the brush ignores deliveries for a version it has left. Fixes unoplatform#26368
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tick the box to add this pull request to the merge queue (same as
|
mikernet
added a commit
to Singulink/uno
that referenced
this pull request
Nov 13, 2026
Port of unoplatform#26370 commit f340a71 (issue unoplatform#26368). When its ImageSource changed, an ImageBrush subscribed to the new source before it disposed its subscription to the old one. A new source that has its image, or opens synchronously, delivers it while the brush subscribes, and the brush raises ImageOpened then: a handler that made the old source open again synchronously still reached the brush, which drew the old image for the new source. The brush now disposes the old subscription first, as WinUI does (CImageBrush::SetValue), and each subscription's callback carries the source version it was made for, so a source the handler replaced during the subscribe is ignored too. This build's SVG ImageOpened handling is unchanged.
mikernet
added a commit
to Singulink/uno
that referenced
this pull request
Nov 14, 2026
Takes Uno's servicing/6.8 from 6951c2c up to 5254ace (42 pull requests). Conflicts, by area: IME and TextBox (unoplatform#25984, backport of unoplatform#25804; unoplatform#25952, backport of unoplatform#25619; unoplatform#25976, backport of unoplatform#25818) - Takes upstream's composition design (unoplatform#25804): the browser's input is the source of truth for the text, compositions included. This drops the composition parts of the unoplatform#24502 port (90746b8): its TypeScript composition handling, the StartIndex/TextChangePending event arguments, the IsReadOnly forwarding to the hidden input, and its Given_TextBox.BrowserSoftKeyboard tests. - Keeps the non-IME browser fixes on top of it, in the form their master pull requests have on unoplatform#25804: Enter raised once per press and tracked by key code (unoplatform#25174, with Given_TextBox.BrowserEnterKey from master in place of the Enter tests that lived in the dropped file), the iOS refocus on tap (unoplatform#25185), pastes without text and isIOS from BrowserInputHelper. - Moves the unoplatform#26005 MaxLength fixes onto unoplatform#25804's native input path, as master has them: a refused browser edit keeps the TextBox's selection, TextBoxView.UpdateTextFromNative reports whether the edit went in, and the IME keeps its range on the applied text (master be85699, which d226897 had ported onto the unoplatform#24502 IME). Adds that commit's browser and platform-applied IME tests. - TextBox.pointers.skia.cs: upstream's file (unoplatform#25619 iOS touch editing, unoplatform#25818, unoplatform#25804), with the unoplatform#25126 port (the hold acts when it starts, a touch right-tap keeps the caret) in the form master's unoplatform#25126 branch has it on unoplatform#25619, including focusing before the iOS hold timer acts and its iOS not-focusable test. Pointers (unoplatform#25976, backport of unoplatform#25818) - Tapped and RightTapped are raised once the PointerReleased bubbled (unoplatform#25818), with the unoplatform#25732 port's window coordinates (2-argument routed event args, RawPosition for ContextRequested), as master's unoplatform#25732 branch has them. - The release wrapper of unoplatform#25818 keeps the unoplatform#25126 port's StopContextMenuTimer at the start of the release. DependencyObject (unoplatform#26142, backport of unoplatform#26123) - Takes upstream, then the code of unoplatform#25839 (97a2f17), which replaces this branch's unoplatform#25102 ports (800247f, be41da6, fd40bf9, 7546379). DependencyObjectStore.Binder.cs, .alc.cs, Application.Alc.cs, ResourceDictionary.alc.cs and their tests now match unoplatform#25839; this drops 800247f's own SingleParentNonFE_Parent_Removes_Value_Then_Next_Parent_Starts_Clean test, which unoplatform#25839 doesn't have. Imaging (unoplatform#25597, unoplatform#25915, unoplatform#25911, unoplatform#25897) - Keeps this branch's side: it has the later forms of these fixes (unoplatform#25124, unoplatform#25559, unoplatform#25633 and unoplatform#26370 ports), where upstream has the 6.8-only unoplatform#25597 design and earlier snapshots. Upstream's shared download, IgnoreImageCache and failed-load fixes are covered by SharedImageDecode. Adds upstream's When_Source_Outlives_Brushes test. ToolTip (unoplatform#26098, backport of unoplatform#25137 and unoplatform#26096) - Keeps this branch's unoplatform#25137 port and adds unoplatform#26096 in master's form: the service releases only a ToolTip it opened, and leaves one the app opened during the show delay to the app. Keeps both sides' tests. Flyout (unoplatform#26062, backport of unoplatform#26060) - This branch had the same commits already; keeps the "Keep non-resizable flyouts in window" port and its tests. Also removes a helper pair in Given_AccessibleTextBox that both sides added.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GitHub Issue: closes #26368
PR Type:
🐞 Bugfix
What changed? 🚀
When its
ImageSourcechanged, anImageBrushsubscribed to the new source before it disposed its subscription to the old one (#26368), since 0015c9b (5.0); before that, it disposed it first. A new source that has its image already, or opens synchronously (aWriteableBitmap), delivers it while the brush subscribes, and the brush raisesImageOpenedthen. If that handler, or other app code run meanwhile, made the old source open again synchronously (for exampleoldWriteableBitmap.Invalidate()), the old subscription was still active: the brush took the old image and drew it for the new source.Imagedoes (ReleaseSubscription) and as WinUI does:CImageBrush::SetValuecallsRemoveCallbackon the old source before it sets the new one (imagebrush.cpp).ImageOpenedhandler, raised while it subscribes, switches it to a third source, the subscription to the replaced source is only disposed once that first subscribe returns, and a synchronous open of the replaced source in between reached the brush too. Each subscription's callback now carries the version of the brush's source it was made for (the_subscriptionVersionthe brush already keeps for that case), and the brush ignores deliveries for a version it has left.Once disposed, a subscription is never notified: a source's asynchronous open, including a completed bitmap-cache entry, whose delivery is queued on the dispatcher, skips disposed subscriptions. So the bug needs app code that reopens a source synchronously while the brush switches. A brush whose
BitmapImage.UriSourcechanges keeps drawing the previous image until the new one opens; that is unchanged and matches WinUI, which keeps the surface while it switches URIs (CImageSource::ReloadImage).Imagealready disposed its old subscription first, and doesn't need the version check: the sources that run app code while anImagesubscribes (BitmapImage,SvgImageSource) deliver through the dispatcher, after theImagehas disposed the subscription, and the ones that open synchronously (WriteableBitmap,RenderTargetBitmap) raise no events.Tests
In
Given_Image_Release(Skia):When_Brush_Open_Handler_Reopens_Previous_Source_Then_Brush_Draws_New_One: a brush switched between twoWriteableBitmaps, whoseImageOpenedhandler invalidates the one it left, draws the new one.When_Brush_Open_Handler_Switches_Source_Then_Reopened_Replaced_Source_Not_Drawn: a brush whoseImageOpenedhandler switches it to a thirdWriteableBitmapand then invalidates the one it replaced draws the third one.Both fail without the change. With only the old subscription disposed first, the second test still fails. The version check alone would also make the first test pass, since it ignores the old source's delivery; disposing first keeps the old source from notifying the brush at all, as on WinUI.
Runs (Skia desktop):
Given_ImageBrush,Given_Image_Release,Given_Image,Given_WriteableBitmap,Given_BitmapSourceandGiven_RelativeTransform: 128 passed, 5 skipped and 1 failed. The failure,When_WriteableBitmap_Assigned_With_Data_Present, also fails with master's code on this machine, which runs at 125% display scaling (see #25633).Runs (Skia WebAssembly):
Given_Image_ReleaseandGiven_ImageBrush: 45 passed (the two new tests among them) and 26 desktop-only tests skipped.PR Checklist ✅
Screenshots Compare Test Runresults.