已关闭
fix select标签弹出后不失焦 #78296
jiangjiacheng创建于 2025年12月25日关闭于 1月25日
fix select标签弹出后不失焦 #78296
已关闭
jiangjiacheng创建于 2025年12月25日关闭于 1月25日
2 个文件变更+40-5
@@ -1181,12 +1181,15 @@ void WebPattern::RegisterMenuLifeCycleCallback()
1181 1181 
1182void WebPattern::NotifyMenuLifeCycleEvent(MenuLifeCycleEvent menuLifeCycleEvent)1182void WebPattern::NotifyMenuLifeCycleEvent(MenuLifeCycleEvent menuLifeCycleEvent)
1183{1183{
1184- TAG_LOGI(AceLogTag::ACE_WEB, "Web contextMenu NotifyMenuLifeCycleEvent:%{public}d.",1184+ TAG_LOGI(AceLogTag::ACE_WEB, "Web NotifyMenuLifeCycleEvent:%{public}d selectPopupMenuShowing_:%{public}d.",
1185- static_cast<int>(menuLifeCycleEvent));1185+ static_cast<int>(menuLifeCycleEvent), selectPopupMenuShowing_);
1186+ if (selectPopupMenuShowing_) {
arkui-feature-managerarkui-feature-manager
arkui-feature-managerarkui-feature-manager2025年12月25日

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟠 潜在的并发数据竞态

位置: L1186-L1188 | 严重程度: High

❓ 问题描述

代码直接读取成员变量 selectPopupMenuShowing_ 而未使用内存屏障或原子操作。在典型的Web组件架构中,Web内核(渲染进程/线程)通常通过回调通知UI线程更新状态。如果 selectPopupMenuShowing_ 是在Web内核线程中被设置,而在此处(通常在UI线程)被读取,这将构成C++内存模型下的数据竞争,导致未定义行为或读取到陈旧值。

💡 修复建议

确保 selectPopupMenuShowing_ 的声明和访问是线程安全的。如果该变量可能被多线程访问,请将其声明为 std::atomic<bool> 或者在读写操作时使用互斥锁保护。此外,建议添加断言确保该方法在预期的线程(UI线程)上执行。

// 在头文件中声明
std::atomic<bool> selectPopupMenuShowing_ {false};

// 或者确保单线程访问
CHECK_RUN_ON(UIThread);
if (selectPopupMenuShowing_.load(std::memory_order_acquire)) { ... }

likedislike
arkui-feature-managerarkui-feature-manager2025年12月25日

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟡 状态标志位管理逻辑脆弱

位置: L1186-L1188 | 严重程度: Medium

❓ 问题描述

使用 if (selectPopupMenuShowing_) { return; } 进行早期返回,会无条件跳过后续所有的菜单事件处理逻辑。这是一种隐式的控制流依赖。如果 selectPopupMenuShowing_ 标志位因异常流程(如页面崩溃销毁、嵌套菜单逻辑错误)未能被正确重置为 false,将导致该Web组件后续所有的上下文菜单事件被静默忽略,造成严重的功能失效且难以调试。

💡 修复建议

增加对标志位有效性的校验,或者在日志中明确记录被拦截的事件类型,以便排查故障。同时,应确保在页面销毁(OnDetach/Dispose)时强制重置该状态机。

if (selectPopupMenuShowing_) {
    TAG_LOGW(AceLogTag::ACE_WEB, "Menu event ignored due to select popup showing: %{public}d", static_cast<int>(menuLifeCycleEvent));
    return;
}

likedislike
1187+ return;
1188+ }
1186 if (menuLifeCycleEvent == MenuLifeCycleEvent::ABOUT_TO_APPEAR) {1189 if (menuLifeCycleEvent == MenuLifeCycleEvent::ABOUT_TO_APPEAR) {
1187 isMenuShownFromWeb_ = true;1190 isMenuShownFromWeb_ = true;
1188 isLastEventMenuClose_ = false;1191 isLastEventMenuClose_ = false;
1189- } else if (menuLifeCycleEvent == MenuLifeCycleEvent::ON_DID_DISAPPEAR) {1192+ } else if (menuLifeCycleEvent == MenuLifeCycleEvent::ON_WILL_DISAPPEAR && isMenuShownFromWeb_) {
arkui-feature-manager
arkui-feature-managerarkui-feature-manager2025年12月25日

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟡 生命周期事件切换风险

位置: L1192 | 严重程度: Medium

❓ 问题描述

将处理逻辑从 ON_DID_DISAPPEAR(已消失)移动到 ON_WILL_DISAPPEAR(将消失)。如果框架层支持取消关闭操作,或者 ON_WILL_DISAPPEAR 触发后菜单未实际关闭,此时 isMenuShownFromWeb_ 已被置为 false,会导致状态与UI实际表现不一致。此外,结合新增的 selectPopupMenuShowing_ 守卫逻辑,如果 Select 弹窗触发 ON_WILL_DISAPPEARisMenuShownFromWeb_ 的状态流转需要仔细推演以避免逻辑死锁。

💡 修复建议

请确认 Ace 框架中 Menu 的 ON_WILL_DISAPPEAR 是否允许中止。如果允许,必须考虑回滚逻辑;如果不允许,建议添加注释说明为什么 WILL 阶段更适合处理此逻辑,以及它如何与 Select 弹窗的生命周期交互。


likedislike
1190 isMenuShownFromWeb_ = false;1193 isMenuShownFromWeb_ = false;
1191 isLastEventMenuClose_ = true;1194 isLastEventMenuClose_ = true;
1192 if (!isFocus_) {1195 if (!isFocus_) {
@@ -401,14 +401,46 @@ HWTEST_F(WebPatternPartOneTest, NotifyMenuLifeCycleEvent_001, TestSize.Level1)
401 webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ABOUT_TO_APPEAR);401 webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ABOUT_TO_APPEAR);
402 EXPECT_TRUE(webPattern->isMenuShownFromWeb_);402 EXPECT_TRUE(webPattern->isMenuShownFromWeb_);
403 webPattern->isFocus_ = true;403 webPattern->isFocus_ = true;
404- webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ON_DID_DISAPPEAR);404+ webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ON_WILL_DISAPPEAR);
405 EXPECT_FALSE(webPattern->isMenuShownFromWeb_);405 EXPECT_FALSE(webPattern->isMenuShownFromWeb_);
406 webPattern->isFocus_ = false;406 webPattern->isFocus_ = false;
407- webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ON_DID_DISAPPEAR);407+ webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ON_WILL_DISAPPEAR);
408 EXPECT_EQ(webPattern->delegate_->blurReason_, OHOS::NWeb::BlurReason::VIEW_SWITCH);408 EXPECT_EQ(webPattern->delegate_->blurReason_, OHOS::NWeb::BlurReason::VIEW_SWITCH);
409#endif409#endif
410}410}
411 411 
412+/**
413+ * @tc.name: NotifyMenuLifeCycleEvent_002
414+ * @tc.desc: NotifyMenuLifeCycleEvent.
415+ * @tc.type: FUNC
416+ */
417+HWTEST_F(WebPatternPartOneTest, NotifyMenuLifeCycleEvent_002, TestSize.Level1)
418+{
419+#ifdef OHOS_STANDARD_SYSTEM
420+ auto* stack = ViewStackProcessor::GetInstance();
421+ EXPECT_NE(stack, nullptr);
422+ auto nodeId = stack->ClaimNodeId();
423+ auto frameNode =
424+ FrameNode::GetOrCreateFrameNode(V2::WEB_ETS_TAG, nodeId, []() { return AceType::MakeRefPtr<WebPattern>(); });
425+ EXPECT_NE(frameNode, nullptr);
426+ stack->Push(frameNode);
427+ auto webPattern = frameNode->GetPattern<WebPattern>();
428+ ASSERT_NE(webPattern, nullptr);
429+ webPattern->OnModifyDone();
430+ ASSERT_NE(webPattern->delegate_, nullptr);
431+ webPattern->selectPopupMenuShowing_ = true;
432+ webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ABOUT_TO_APPEAR);
433+ EXPECT_FALSE(webPattern->isMenuShownFromWeb_);
434+ webPattern->selectPopupMenuShowing_ = false;
435+ webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ABOUT_TO_APPEAR);
436+ EXPECT_TRUE(webPattern->isMenuShownFromWeb_);
437+ webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ON_WILL_DISAPPEAR);
438+ EXPECT_FALSE(webPattern->isMenuShownFromWeb_);
439+ webPattern->NotifyMenuLifeCycleEvent(MenuLifeCycleEvent::ON_WILL_DISAPPEAR);
440+ EXPECT_TRUE(webPattern->isLastEventMenuClose_);
441+#endif
442+}
443+ 
412/**444/**
413 * @tc.name: ShowPreviewMenu_001445 * @tc.name: ShowPreviewMenu_001
414 * @tc.desc: ShowPreviewMenu.446 * @tc.desc: ShowPreviewMenu.