From 656f1c03cb8a9dcf38ea90a4f93e0485a1473a6d Mon Sep 17 00:00:00 2001 From: "torlando-agent[bot]" <281092095+torlando-agent[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:39:04 +0000 Subject: [PATCH] fix(nomadnet): validate addresses before teardown --- lib/tdeck_ui/UI/LXMF/NomadNetScreen.cpp | 9 ++++---- lib/tdeck_ui/UI/LXMF/UIManager.cpp | 18 ++++++++-------- tests/native/test_app_launcher_nomadnet.py | 24 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/tdeck_ui/UI/LXMF/NomadNetScreen.cpp b/lib/tdeck_ui/UI/LXMF/NomadNetScreen.cpp index a0ab8a77..12166b6b 100644 --- a/lib/tdeck_ui/UI/LXMF/NomadNetScreen.cpp +++ b/lib/tdeck_ui/UI/LXMF/NomadNetScreen.cpp @@ -480,7 +480,7 @@ void NomadNetScreen::activate_selected_link(){ if(_selected_link<0||static_cast(_selected_link)>=_page.links().size()||!_link)return; const auto target_view=_page.target(static_cast(_selected_link)); const std::string target(target_view.data(),target_view.size()); - if(_link(target))begin_navigation(target);else set_status("Browser action queue is busy"); + if(!_link(target))set_status("Browser action queue is busy"); } void NomadNetScreen::page_event(lv_event_t* event){ @@ -533,10 +533,10 @@ void NomadNetScreen::clicked(lv_event_t* event){ auto* self=static_cast(lv_event_get_user_data(event));auto* target=lv_event_get_target(event); if(target==self->_back_button&&self->_back)self->_back(); else if(target==self->_home_button&&self->_home)self->_home(); - else if(target==self->_reload_button&&self->_reload){const std::string address=self->address();if(self->_reload(address))self->begin_navigation(address);else self->set_status("Browser action queue is busy");} + else if(target==self->_reload_button&&self->_reload){const std::string address=self->address();if(!self->_reload(address))self->set_status("Browser action queue is busy");} else if(target==self->_save_button&&self->_save){if(!self->_save(self->address()))self->set_status("Browser action queue is busy");} else if(target==self->_edit_button){self->set_address_editing(true);self->set_status("Edit destination or page path");} - else if((target==self->_go_button||target==self->_address)&&self->_open){const std::string address=self->address();if(self->_open(address))self->begin_navigation(address);else self->set_status("Browser action queue is busy");} + else if((target==self->_go_button||target==self->_address)&&self->_open){const std::string address=self->address();if(!self->_open(address))self->set_status("Browser action queue is busy");} else{ const std::size_t code=reinterpret_cast(lv_obj_get_user_data(target)); if(code==1001)self->render_directory(View::HEARD); @@ -546,8 +546,7 @@ void NomadNetScreen::clicked(lv_event_t* event){ else if(code==1005){self->clear_document();self->show_browser(true);self->set_status("Enter a NomadNet address");} else if(code>2000&&code<=2000+self->_directory_targets.size()){ const std::string selected=self->_directory_targets[code-2001]; - if(self->_open&&self->_open(selected))self->begin_navigation(selected); - else self->set_status("Browser action queue is busy"); + if(!self->_open||!self->_open(selected))self->set_status("Browser action queue is busy"); } } } diff --git a/lib/tdeck_ui/UI/LXMF/UIManager.cpp b/lib/tdeck_ui/UI/LXMF/UIManager.cpp index ce14ac2a..a7182b5d 100644 --- a/lib/tdeck_ui/UI/LXMF/UIManager.cpp +++ b/lib/tdeck_ui/UI/LXMF/UIManager.cpp @@ -1940,17 +1940,10 @@ void UIManager::nomad_update_user_actions() { const std::string target = action.target(); nomad_heap_checkpoint("action-target-copied"); switch (action.kind) { - case NomadNet::UserActionKind::OPEN: { - { - LVGL_LOCK(); - nomad_heap_checkpoint("action-before-navigation"); - _nomadnet_screen->begin_navigation(target); - nomad_heap_checkpoint("action-after-navigation"); - } + case NomadNet::UserActionKind::OPEN: nomad_heap_checkpoint("action-before-open"); nomad_open(target); break; - } case NomadNet::UserActionKind::SAVE: { const bool save = !_nomad_library.page_saved(target); if (!_nomad_library.set_page_saved(target, save)) break; @@ -2099,6 +2092,15 @@ void UIManager::nomad_open(const std::string& address, bool add_history) { RouterLock router_lock; if (!router_lock.acquired()) return; nomad_heap_checkpoint("open-locked"); + { + // Validation must precede destructive UI cleanup so a malformed manual + // address cannot discard the current page or directory. Keep cleanup + // before any retained-Link request or new transport construction. + LVGL_LOCK(); + nomad_heap_checkpoint("action-before-navigation"); + _nomadnet_screen->begin_navigation(parsed.str()); + nomad_heap_checkpoint("action-after-navigation"); + } const bool same_destination = _nomad_state == NomadState::IDLE && _nomad_link && _nomad_link.status() == Type::Link::ACTIVE && !_nomad_url.destination_hex.empty() && diff --git a/tests/native/test_app_launcher_nomadnet.py b/tests/native/test_app_launcher_nomadnet.py index b00d8bc2..94f661b5 100644 --- a/tests/native/test_app_launcher_nomadnet.py +++ b/tests/native/test_app_launcher_nomadnet.py @@ -216,6 +216,30 @@ def test_begin_navigation_releases_directory_rows_before_transport(): assert begin.index("clear_directory();") < begin.index("show_browser(false);") +def test_nomadnet_validates_queued_addresses_before_navigation_teardown(): + """A malformed manual address must preserve the current page or directory.""" + screen = (INCLUDE / "NomadNetScreen.cpp").read_text() + manager = (INCLUDE / "UIManager.cpp").read_text() + + # UI callbacks enqueue only; the owner validates before starting navigation. + activate = screen[screen.index("void NomadNetScreen::activate_selected_link()"): + screen.index("void NomadNetScreen::page_event")] + clicked = screen[screen.index("void NomadNetScreen::clicked("):] + assert "begin_navigation(" not in activate + assert "begin_navigation(" not in clicked + + actions = manager[manager.index("void UIManager::nomad_update_user_actions()"): + manager.index("void UIManager::service_nomad_terminal_action()")] + assert "begin_navigation(" not in actions + + open_page = manager[manager.index("void UIManager::nomad_open("): + manager.index("void UIManager::nomad_reload()")] + parse = open_page.index("NomadNet::Url::parse") + navigation = open_page.index("_nomadnet_screen->begin_navigation(parsed.str())") + same_destination = open_page.index("const bool same_destination") + assert parse < navigation < same_destination + + def test_launcher_transition_has_one_final_focus_owner(): """Leaving and restoring Home must not transiently focus later tiles.""" home = (INCLUDE / "HomeScreen.cpp").read_text()