Skip to content

fix: handle SNI NewMenu signal and empty menu path - #529

Merged
BLumia merged 1 commit into
linuxdeepin:masterfrom
BLumia:agent/pms-bug-bot/1a5f7b51bc34
Oct 20, 2026
Merged

BLumia merged 1 commit into
linuxdeepin:masterfrom
BLumia:agent/pms-bug-bot/1a5f7b51bc34

Conversation

@BLumia

@BLumia BLumia commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

fix: handle SNI NewMenu signal and empty menu path

  1. Root cause: init() read m_menuPath only once without listening
    for NewMenu signal; race condition left it empty permanently
  2. Fix: add NewMenu signal to XML and connect it to refresh
    m_menuPath and destroy stale importer; make menu path lazy-read
    in menuImporter() instead of init(); guard empty path
  3. Impact: SNI tray apps now get correct right-click menu even
    under startup race; empty path falls back to ContextMenu(0,0)

Log: Fix empty right-click menu for SNI tray apps on startup race

Influence:

  1. Test right-click menu for normal SNI apps after full startup
  2. Test right-click menu during SNI app startup race condition
  3. Test ContextMenu fallback for SNI apps without DBusMenu
  4. Test menu refresh when app changes menu path at runtime

fix: 修复SNI应用启动竞态时托盘右键弹空菜单

  1. 根因:init() 只读取一次 m_menuPath 且未监听 NewMenu 信号,
    启动竞态时读到空路径导致永久失效,无法回退 ContextMenu
  2. 方案:XML 新增 NewMenu 信号声明,构造函数连接信号刷新
    m_menuPath 并销毁旧 importer;menuImporter() 改为惰性读取
    菜单路径并对空路径兜底返回 nullptr
  3. 影响:SNI 托盘应用在启动竞态下右键菜单恢复正常;空路径
    时回退 ContextMenu(0,0) 兜底

Log: 修复SNI应用启动竞态时托盘右键弹空菜单的问题

Influence:

  1. 测试 SNI 应用正常启动后右键菜单功能
  2. 测试 SNI 应用启动竞态时右键菜单功能
  3. 测试无 DBusMenu 的 SNI 应用右键回退 ContextMenu
  4. 测试应用运行中菜单路径变更后的菜单刷新

PMS: BUG-379875

Summary by Sourcery

Fix SNI tray menu initialization and refresh handling so right-click menus work across startup races and missing DBusMenu configurations.

Bug Fixes:

  • Fix SNI tray menus that remain unavailable when the menu path is initially empty during application startup.
  • Restore ContextMenu fallback behavior for SNI items without a DBusMenu path.
  • Refresh the imported menu when an SNI application changes its menu path at runtime.

Enhancements:

  • Make SNI menu path resolution lazy so menus can be initialized after startup signals arrive.

1. Root cause: init() read m_menuPath only once without listening
   for NewMenu signal; race condition left it empty permanently
2. Fix: add NewMenu signal to XML and connect it to refresh
   m_menuPath and destroy stale importer; make menu path lazy-read
   in menuImporter() instead of init(); guard empty path
3. Impact: SNI tray apps now get correct right-click menu even
   under startup race; empty path falls back to ContextMenu(0,0)

Log: Fix empty right-click menu for SNI tray apps on startup race

Influence:
1. Test right-click menu for normal SNI apps after full startup
2. Test right-click menu during SNI app startup race condition
3. Test ContextMenu fallback for SNI apps without DBusMenu
4. Test menu refresh when app changes menu path at runtime

fix: 修复SNI应用启动竞态时托盘右键弹空菜单

1. 根因:init() 只读取一次 m_menuPath 且未监听 NewMenu 信号,
   启动竞态时读到空路径导致永久失效,无法回退 ContextMenu
2. 方案:XML 新增 NewMenu 信号声明,构造函数连接信号刷新
   m_menuPath 并销毁旧 importer;menuImporter() 改为惰性读取
   菜单路径并对空路径兜底返回 nullptr
3. 影响:SNI 托盘应用在启动竞态下右键菜单恢复正常;空路径
   时回退 ContextMenu(0,0) 兜底

Log: 修复SNI应用启动竞态时托盘右键弹空菜单的问题

Influence:
1. 测试 SNI 应用正常启动后右键菜单功能
2. 测试 SNI 应用启动竞态时右键菜单功能
3. 测试无 DBusMenu 的 SNI 应用右键回退 ContextMenu
4. 测试应用运行中菜单路径变更后的菜单刷新

PMS: BUG-379875

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@sourcery-ai

sourcery-ai Bot commented Oct 16, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Fixes the SNI startup race by reacting to NewMenu and deferring menu-path lookup until menu use, while preventing invalid DBusMenu importer creation so callers can fall back to ContextMenu(0,0).

Sequence diagram for SNI menu refresh and fallback

sequenceDiagram
    participant SNI as StatusNotifierItem
    participant Handler as SniTrayProtocolHandler
    participant Importer as DBusMenuImporter
    participant Context as ContextMenu

    SNI-->>Handler: NewMenu
    Handler->>SNI: menu()
    SNI-->>Handler: path()
    Handler->>Handler: delete m_dbusMenuImporter
    Handler->>Handler: m_dbusMenuImporter = nullptr

    Handler->>Handler: menuImporter()
    alt menu path is valid
        Handler->>Importer: DBusMenu(m_service, m_menuPath, Handler)
        Importer-->>Handler: menu importer
    else menu path is empty or /NO_DBUSMENU
        Handler-->>Context: ContextMenu(0, 0)
    end
Loading

File-Level Changes

Change Details Files
Track SNI menu-path updates and invalidate the DBusMenu importer when NewMenu is emitted.
  • Declare the NewMenu signal in the StatusNotifierItem D-Bus interface.
  • Connect NewMenu to refresh the cached path and delete the stale importer.
plugins/application-tray/api/dbus/org.kde.StatusNotifierItem.xml
plugins/application-tray/sniprotocolhandler.cpp
Resolve the menu path lazily and safely fall back when no DBusMenu is available.
  • Stop reading the menu path during initialization.
  • Read it on first importer access and return nullptr for empty or /NO_DBUSMENU paths.
plugins/application-tray/sniprotocolhandler.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 95 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
PR 标题 fix: handle SNI NewMenu signal and empty menu path
PR 作者 BLumia
修改文件 2 个文件 (org.kde.StatusNotifierItem.xml, sniprotocolhandler.cpp)
PMS Bug BUG-379875
评分详情 未发现安全漏洞,代码修复逻辑正确,惰性加载和信号处理实现合理。存在轻微的潜在 re-entrancy 风险和注释缺失,但不影响整体代码质量。

📋 代码变更概述

本次 PR 修复 SNI(StatusNotifierItem)应用启动竞态时托盘右键弹空菜单的问题。

根因:init() 仅在构造时读取一次 m_menuPath,未监听 NewMenu 信号,启动竞态时读到空路径导致永久失效,无法回退 ContextMenu。

修复方案:

  1. XML 新增 NewMenu 信号声明,符合 KDE SNI 规范
  2. 构造函数连接 NewMenu 信号,刷新 m_menuPath 并销毁旧 importer
  3. menuImporter() 改为惰性读取菜单路径,对空路径兜底返回 nullptr

🔍 详细分析

1. 语法逻辑 ✅ (22/25 分)

评价: 语法正确,逻辑清晰 ✅

潜在问题:

  1. eventFilter 中多次调用 menuImporter(),若 NewMenu 信号在 updateMenu() 执行期间(可能涉及 DBus 同步调用处理事件)触发,旧 m_dbusMenuImporter 被删除后,已获取的 QMenu* 指针变为悬空指针,存在潜在的 use-after-free 风险。建议将 menuImporter() 返回值存储到局部变量中复用,避免多次调用。

涉及文件及行号:

  • plugins/application-tray/sniprotocolhandler.cpp 第 344-353 行(eventFilter)与第 182-188 行(NewMenu lambda)

问题分析:
在 eventFilter() 中,右键点击处理逻辑如下:

if (!menuImporter()) {                    // 第一次调用
    m_sniInter->ContextMenu(0, 0);
    return false;
}
auto menu = menuImporter()->menu();        // 第二次调用
menuImporter()->updateMenu(menu);           // 第三次调用
menu->setFixedSize(menu->sizeHint());       // 使用 menu 指针

若 NewMenu 信号在 updateMenu() 执行期间被处理(DBus 调用可能触发事件循环),m_dbusMenuImporter 被删除并置空,QMenu 对象随之被销毁(Qt 父子对象关系),后续 menu->setFixedSize() 将访问已释放内存。

建议修复:

auto importer = menuImporter();
if (!importer) {
    m_sniInter->ContextMenu(0, 0);
    return false;
}
auto menu = importer->menu();
importer->updateMenu(menu);
menu->setFixedSize(menu->sizeHint());

2. 代码质量 ✅ (23/25 分)

评价: 代码结构清晰 ✅

潜在问题:

  1. NewMenu 信号处理 lambda 缺少注释说明。该 lambda 负责在菜单路径变更时刷新 m_menuPath 并销毁旧 importer,逻辑较为关键,建议添加注释说明为何需要删除并重建 importer。

涉及文件及行号:

  • plugins/application-tray/sniprotocolhandler.cpp 第 182-188 行

建议添加注释:

// 当应用菜单路径变更时,刷新 m_menuPath 并销毁旧的 importer
// 以便下次 menuImporter() 调用时创建新的 DBusMenu 实例
connect(m_sniInter, &StatusNotifierItem::NewMenu, this, [this] {
    m_menuPath = m_sniInter->menu().path();
    if (m_dbusMenuImporter) {
        delete m_dbusMenuImporter;
        m_dbusMenuImporter = nullptr;
    }
});

3. 代码性能 ✅ (20/20 分)

评价: 性能良好,资源使用合理 ✅

潜在问题: 无

惰性加载(lazy loading)实际上是性能改进:避免了 init() 中不必要的 DBus 调用,仅在首次需要菜单时才读取路径。NewMenu 信号是低频事件,handler 中的 DBus 属性读取和 delete 操作对性能无显著影响。


4. 代码安全 ✅ (30/30 分)

评价: 存在0个安全漏洞 ✅

🔐 发现 0 个安全漏洞

安全漏洞详情: 未发现安全漏洞

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个

安全分析:

  • 无命令注入、SQL 注入风险
  • 无硬编码密钥或敏感信息
  • 无缓冲区溢出风险
  • DBus 路径使用 Qt QString 管理,无路径遍历风险
  • 内存管理使用 delete + nullptr 模式,符合规范

💡 改进建议代码示例

// 1. eventFilter 中复用 importer 指针,避免 use-after-free
// 修改前:
if (!menuImporter()) {
    m_sniInter->ContextMenu(0, 0);
    return false;
}
auto menu = menuImporter()->menu();
menuImporter()->updateMenu(menu);

// 修改后:
auto importer = menuImporter();
if (!importer) {
    m_sniInter->ContextMenu(0, 0);
    return false;
}
auto menu = importer->menu();
importer->updateMenu(menu);
// 2. 为 NewMenu lambda 添加注释
// 当应用菜单路径变更时,刷新 m_menuPath 并销毁旧的 importer
// 以便下次 menuImporter() 调用时创建新的 DBusMenu 实例
connect(m_sniInter, &StatusNotifierItem::NewMenu, this, [this] {
    m_menuPath = m_sniInter->menu().path();
    if (m_dbusMenuImporter) {
        delete m_dbusMenuImporter;
        m_dbusMenuImporter = nullptr;
    }
});

📝 审查总结

本次 PR 修复了 SNI 托盘应用启动竞态时右键菜单为空的问题,修改方案合理:

  • XML 信号声明符合 KDE SNI 规范
  • 惰性加载和 NewMenu 信号处理逻辑正确
  • 空路径兜底处理完善,支持 ContextMenu 回退

主要建议:

  1. eventFilter 中应复用 menuImporter() 返回值,消除潜在的 use-after-free 风险
  2. 关键 lambda 应添加注释说明设计意图

整体代码质量良好,可以合并。


本报告由 AI 代码审查工具自动生成
扫描时间:2026-10-16 15:05:00

@Ivy233

Ivy233 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Review 总结

整体方向正确,LGTM。

核心修复(menuImporter() 惰性重读 Menu + 空路径返回 nullptr)确实能解决启动竞态导致的右键空菜单;NewMenu 信号也符合 KDE 实际使用的 org.kde.StatusNotifierItem 接口(freedesktop 文档本身未列 NewMenu,因此保留惰性读取是必要的补充)。改动范围小,我按生成的 interface 对修改后的 sniprotocolhandler.cpp 做了编译检查,可以通过;CI 也全部通过。

以下是记录性说明,均不阻塞合并:

  1. 同步 D-Bus 读取的位置
    当前 StatusNotifierItem 使用 DTK 接口的默认 sync=true,并设置了 setTimeout(2000);本 PR 把 Menu 的首次读取从 init()(QThreadPool worker)移到了 menuImporter() 的调用路径(右键、NewMenu、主题切换)。如果某个 SNI 应用卡住,理论上主线程可能等待最长 2s。不过插件现有的 icon() / toolTip() 等属性读取本来就在主线程同步执行(例如 TrayWidget::paintEvent()),本次改动没有引入新的风险类别,右键也是用户触发的低频操作,因此不构成阻塞项。若后续要系统性优化,可以单独讨论恢复 setSync(false) 并消费 MenuChanged / getAllProperties(),不需要放在本 PR。

  2. NewMenu 到达时的 importer 销毁
    当前实现会立即 delete importer,~DBusMenuImporter() 会对 QMenu 做 deleteLater()。如果菜单正打开时应用发送 NewMenu(运行中替换菜单),用户会看到菜单关闭;路径未变化时也会丢弃已加载的缓存。属于可接受的行为选择,后续可按实际产品预期决定是否比较路径或改用 deleteLater()。

  3. 测试
    建议后续补一个 mock SNI 服务的回归测试:延迟设置 Menu 并发送 NewMenu,覆盖启动竞态、菜单打开期间路径变化、以及无 DBusMenu 时回退 ContextMenu。当前 PR 的手工测试项已经覆盖了这些场景。

以上仅为 review 记录,不要求在本 PR 中处理。感谢修复。

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia, Ivy233

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@BLumia
BLumia merged commit 7f42e60 into linuxdeepin:master Oct 20, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants