fix(nomadnet): validate addresses before teardown

This commit is contained in:
torlando-agent[bot]
2026-08-14 00:39:04 +00:00
parent f605aa386d
commit 656f1c03cb
3 changed files with 38 additions and 13 deletions
+4 -5
View File
@@ -480,7 +480,7 @@ void NomadNetScreen::activate_selected_link(){
if(_selected_link<0||static_cast<std::size_t>(_selected_link)>=_page.links().size()||!_link)return;
const auto target_view=_page.target(static_cast<std::size_t>(_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<NomadNetScreen*>(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<std::size_t>(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");
}
}
}
+10 -8
View File
@@ -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() &&
@@ -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()