diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28eedf67..9c41cc1b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -303,7 +303,11 @@ jobs: - name: Override frida-swift with local FridaCore.xcframework run: | + # Stay on the same frida-swift revision as Package.swift / + # Package.resolved. Cloning tip-of-main regenerates bindings that + # no longer match this tree. git clone https://github.com/frida/frida-swift.git $RUNNER_TEMP/frida-swift + git -C $RUNNER_TEMP/frida-swift checkout 467d0360c2cc8ae7db9444316656b4154d22213d cp -R $RUNNER_TEMP/FridaCore.xcframework $RUNNER_TEMP/frida-swift/ python3 - <<'PY' import os, re @@ -344,11 +348,15 @@ jobs: - name: Generate agent bundle run: | + # Fork PRs do not receive Apple signing secrets. Disable signing so + # LumaBundleCompiler still builds without a local codesign identity. xcodebuild \ -project Luma.xcodeproj \ -scheme AgentBundle \ -configuration Release \ -derivedDataPath build/.derived \ + CODE_SIGN_IDENTITY=- \ + CODE_SIGNING_ALLOWED=NO \ build - name: Build @@ -602,7 +610,11 @@ jobs: - name: Override frida-swift with local FridaCore.xcframework run: | + # Stay on the same frida-swift revision as Package.swift / + # Package.resolved. Cloning tip-of-main regenerates bindings that + # no longer match this tree. git clone https://github.com/frida/frida-swift.git $RUNNER_TEMP/frida-swift + git -C $RUNNER_TEMP/frida-swift checkout 467d0360c2cc8ae7db9444316656b4154d22213d cp -R $RUNNER_TEMP/FridaCore.xcframework $RUNNER_TEMP/frida-swift/ python3 - <<'PY' import os, re @@ -643,11 +655,15 @@ jobs: - name: Generate agent bundle run: | + # Fork PRs do not receive Apple signing secrets. Disable signing so + # LumaBundleCompiler still builds without a local codesign identity. xcodebuild \ -project Luma.xcodeproj \ -scheme AgentBundle \ -configuration Release \ -derivedDataPath build/.derived-ios \ + CODE_SIGN_IDENTITY=- \ + CODE_SIGNING_ALLOWED=NO \ build - name: Build (unsigned, iOS Simulator) diff --git a/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index fd4adabb..7d3bf6f4 100644 --- a/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,7 +6,6 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/frida/frida-swift", "state" : { - "branch" : "main", "revision" : "467d0360c2cc8ae7db9444316656b4154d22213d" } }, diff --git a/Luma.xcodeproj/xcshareddata/xcschemes/AgentBundle.xcscheme b/Luma.xcodeproj/xcshareddata/xcschemes/AgentBundle.xcscheme new file mode 100644 index 00000000..b48e8aca --- /dev/null +++ b/Luma.xcodeproj/xcshareddata/xcschemes/AgentBundle.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/LumaGtk/Sources/LumaGtk/MainWindow.swift b/LumaGtk/Sources/LumaGtk/MainWindow.swift index 7b568807..d097387f 100644 --- a/LumaGtk/Sources/LumaGtk/MainWindow.swift +++ b/LumaGtk/Sources/LumaGtk/MainWindow.swift @@ -3062,7 +3062,16 @@ final class MainWindow: InstrumentUIHost { } private func activateGroupChild(sessionID: UUID, group: SessionSidebarGroup, key: String) { - groupChildActions[groupChildActionKey(sessionID: sessionID, group: group, key: key)]?() + let actionKey = groupChildActionKey(sessionID: sessionID, group: group, key: key) + // Browse-all opens a popover; keep that off the ListBox signal stack. + if key == "browse" { + Task { @MainActor [weak self] in + await Task.yield() + self?.groupChildActions[actionKey]?() + } + return + } + groupChildActions[actionKey]?() } private func toggleGroupExpansion(sessionID: UUID, group: SessionSidebarGroup) { diff --git a/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift b/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift index 10a278cf..cf152222 100644 --- a/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift +++ b/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift @@ -26,8 +26,11 @@ final class SidebarBrowserPopover { private var query: String = "" private var filterTask: Task? private var refreshGeneration: UInt = 0 + private var isChoosing = false - private static var visibleRowLimit: Int { 250 } + // Cap first paint so Windows GTK is not forced to allocate a huge ListBox + // while the popover is still mapping. + private static var visibleRowLimit: Int { 150 } init( items: [Item], @@ -73,7 +76,7 @@ final class SidebarBrowserPopover { key.onKeyPressed { [weak self] _, keyval, _, _ in MainActor.assumeIsolated { if Int32(keyval) == Gdk.keyEscape { - self?.dismiss() + self?.scheduleDismiss() return true } return false @@ -101,7 +104,7 @@ final class SidebarBrowserPopover { MainActor.assumeIsolated { self?.chooseFirstMatch() } } searchEntry.onStopSearch { [weak self] _ in - MainActor.assumeIsolated { self?.dismiss() } + MainActor.assumeIsolated { self?.scheduleDismiss() } } column.append(child: searchEntry) @@ -119,7 +122,9 @@ final class SidebarBrowserPopover { let index = Int(row.index) guard index >= 0, index < self.entries.count else { return } if case .item(let item) = self.entries[index] { - self.choose(item) + // Defer dismiss/unparent until after row-activated finishes; + // tearing the popover down mid-emission faults inside GTK. + self.scheduleChoose(item) } } } @@ -132,9 +137,15 @@ final class SidebarBrowserPopover { self.popover = popover self.listBox = listBox - refreshList() + // Map the empty popover first, then fill rows on a later turn so the + // initial present is not competing with a large ListBox rebuild. popover.popup() _ = searchEntry.grabFocus() + Task { @MainActor [weak self] in + await Task.yield() + guard let self, self.popover != nil else { return } + self.refreshList() + } } private func scheduleRefresh() { @@ -261,18 +272,32 @@ final class SidebarBrowserPopover { private func chooseFirstMatch() { for entry in entries { if case .item(let item) = entry { - choose(item) + scheduleChoose(item) return } } } - private func choose(_ item: Item) { - dismiss() - onChoose(item) + private func scheduleChoose(_ item: Item) { + guard !isChoosing else { return } + isChoosing = true + Task { @MainActor [weak self] in + await Task.yield() + guard let self else { return } + self.dismiss() + self.onChoose(item) + } + } + + private func scheduleDismiss() { + Task { @MainActor [weak self] in + await Task.yield() + self?.dismiss() + } } private func dismiss() { + guard popover != nil else { return } popover?.popdown() cleanup() } @@ -281,7 +306,10 @@ final class SidebarBrowserPopover { filterTask?.cancel() filterTask = nil refreshGeneration &+= 1 - popover?.unparent() + isChoosing = false + if let popover { + popover.unparent() + } popover = nil listBox = nil retainer = nil diff --git a/Package.swift b/Package.swift index 35590897..108e48f1 100644 --- a/Package.swift +++ b/Package.swift @@ -60,7 +60,11 @@ let package = Package( .executable(name: "LumaBundleCompiler", targets: ["LumaBundleCompiler"]), ], dependencies: [ - .package(url: "https://github.com/frida/frida-swift", branch: "main"), + // Pin to the revision recorded in Package.resolved. Floating on + // branch main pulled a regenerated Frida API that does not match + // this tree (and Apple CI clones tip-of-main when injecting the + // local FridaCore.xcframework). + .package(url: "https://github.com/frida/frida-swift", revision: "467d0360c2cc8ae7db9444316656b4154d22213d"), .package(url: "https://github.com/apple/swift-crypto", .upToNextMajor(from: "3.0.0")), .package(url: "https://github.com/groue/GRDB.swift", .upToNextMajor(from: "7.0.0")), .package(url: "https://github.com/radareorg/SwiftyR2", branch: "main"),