Skip to content

Commit 4add01b

Browse files
committed
feat: session restore — persist open tab URLs + active index to session.json (Store::SaveSession/GetSessionTabs), restore on normal launch (skip if only a single new-tab). Saved on navigation (OnBrowserAddressChange) and tab close (RemoveTabAt).
1 parent 178ff94 commit 4add01b

4 files changed

Lines changed: 109 additions & 2 deletions

File tree

shell/src/browser_window.cc

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,31 @@ void BrowserWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
476476
// Push the theme colors into the freshly-built view hierarchy.
477477
window_->ThemeChanged();
478478

479-
// First tab.
480-
CreateTab(pending_initial_url_, /*select=*/true);
479+
// First tab(s). If a previous session was saved and the caller didn't ask
480+
// for a specific URL (i.e. a normal launch), restore the saved tabs.
481+
bool restored = false;
482+
if (pending_initial_url_ == GetNewTabURL()) {
483+
std::vector<std::string> saved = OpenNyxStore::Get()->GetSessionTabs();
484+
// Don't bother restoring a single new-tab page.
485+
const bool trivial =
486+
saved.empty() ||
487+
(saved.size() == 1 && (saved[0] == GetNewTabURL() ||
488+
saved[0] == "opennyx://newtab"));
489+
if (!trivial) {
490+
size_t active = OpenNyxStore::Get()->GetSessionActiveIndex();
491+
for (size_t i = 0; i < saved.size(); ++i) {
492+
CreateTab(saved[i], /*select=*/false);
493+
}
494+
if (active >= saved.size()) {
495+
active = 0;
496+
}
497+
SelectTab(active);
498+
restored = true;
499+
}
500+
}
501+
if (!restored) {
502+
CreateTab(pending_initial_url_, /*select=*/true);
503+
}
481504
pending_initial_url_.clear();
482505

483506
window_->Show();
@@ -1051,6 +1074,24 @@ void BrowserWindow::ZoomActiveTab(int delta) {
10511074
host->SetZoomLevel(level);
10521075
}
10531076

1077+
void BrowserWindow::SaveSessionState() {
1078+
CEF_REQUIRE_UI_THREAD();
1079+
std::vector<std::string> urls;
1080+
urls.reserve(tabs_.size());
1081+
for (const auto& t : tabs_) {
1082+
std::string url;
1083+
if (t.browser_view && t.browser_view->GetBrowser() &&
1084+
t.browser_view->GetBrowser()->GetMainFrame()) {
1085+
url = t.browser_view->GetBrowser()->GetMainFrame()->GetURL().ToString();
1086+
}
1087+
if (url.empty()) {
1088+
url = GetNewTabURL();
1089+
}
1090+
urls.push_back(url);
1091+
}
1092+
OpenNyxStore::Get()->SaveSession(urls, active_tab_);
1093+
}
1094+
10541095
void BrowserWindow::DragPoll(int seq) {
10551096
CEF_REQUIRE_UI_THREAD();
10561097
#if defined(_WIN32)
@@ -1196,6 +1237,8 @@ void BrowserWindow::OnBrowserAddressChange(CefRefPtr<CefBrowser> browser,
11961237
SetAddressBarText(ShouldHideUrlInAddressBar(spec) ? "" : spec);
11971238
}
11981239
UpdateChrome();
1240+
// Persist the session whenever a tab navigates somewhere new.
1241+
SaveSessionState();
11991242
}
12001243

12011244
void BrowserWindow::OnBrowserLoadingStateChange(CefRefPtr<CefBrowser> browser,
@@ -1452,6 +1495,8 @@ void BrowserWindow::RemoveTabAt(size_t index) {
14521495
SelectTab(active_tab_);
14531496
}
14541497
window_->InvalidateLayout();
1498+
// Persist the smaller tab set so a closed tab doesn't come back on restore.
1499+
SaveSessionState();
14551500
}
14561501

14571502
void BrowserWindow::SelectTab(size_t index) {

shell/src/browser_window.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ class BrowserWindow : public CefWindowDelegate,
246246
// Applies a zoom step to the active tab. delta = +1 (in), -1 (out), 0 (reset
247247
// to 100%). Zoom uses CEF's logarithmic level: each step is ~1.2x.
248248
void ZoomActiveTab(int delta);
249+
250+
// Persists the current open-tab URLs + active index so they can be restored
251+
// on next launch. Cheap; called whenever tabs change.
252+
void SaveSessionState();
249253
// Frameless window controls.
250254
CefRefPtr<CefPanel> caption_spacer_; // flexible drag area
251255
CefRefPtr<CefLabelButton> minimize_button_;

shell/src/store.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,53 @@ void OpenNyxStore::LoadLocked() {
212212
}
213213
}
214214
}
215+
216+
// ---- session (open tabs) ----
217+
{
218+
const std::string raw = ReadFile(PathFor("session.json"));
219+
if (!raw.empty()) {
220+
json j = json::parse(raw, nullptr, false);
221+
if (j.is_object()) {
222+
session_active_ = j.value("active", (size_t)0);
223+
const auto& tabs = j["tabs"];
224+
if (tabs.is_array()) {
225+
for (const auto& t : tabs) {
226+
if (t.is_string()) {
227+
session_tabs_.push_back(t.get<std::string>());
228+
}
229+
}
230+
}
231+
}
232+
}
233+
}
234+
}
235+
236+
void OpenNyxStore::SaveSession(const std::vector<std::string>& tab_urls,
237+
size_t active_index) {
238+
std::lock_guard<std::mutex> lock(mutex_);
239+
EnsureLoaded();
240+
session_tabs_ = tab_urls;
241+
session_active_ = active_index;
242+
json j;
243+
j["active"] = active_index;
244+
json arr = json::array();
245+
for (const auto& u : tab_urls) {
246+
arr.push_back(u);
247+
}
248+
j["tabs"] = arr;
249+
WriteFileAtomic(PathFor("session.json"), j.dump(2));
250+
}
251+
252+
std::vector<std::string> OpenNyxStore::GetSessionTabs() {
253+
std::lock_guard<std::mutex> lock(mutex_);
254+
EnsureLoaded();
255+
return session_tabs_;
256+
}
257+
258+
size_t OpenNyxStore::GetSessionActiveIndex() {
259+
std::lock_guard<std::mutex> lock(mutex_);
260+
EnsureLoaded();
261+
return session_active_;
215262
}
216263

217264
void OpenNyxStore::SaveConfigLocked() {

shell/src/store.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ class OpenNyxStore {
9090
std::vector<DownloadEntry> GetDownloads();
9191
void ClearDownloads();
9292

93+
// ---- Session (open tabs, for restore-on-startup) ----
94+
// Persists the list of currently-open tab URLs (in tab order). Called by the
95+
// browser window whenever tabs change.
96+
void SaveSession(const std::vector<std::string>& tab_urls,
97+
size_t active_index);
98+
// Returns the previously-saved open-tab URLs (empty if none).
99+
std::vector<std::string> GetSessionTabs();
100+
size_t GetSessionActiveIndex();
101+
93102
// ---- Bulk ----
94103
// Clears history + downloads (leaves bookmarks + config intact). Cookies /
95104
// cache are cleared separately by the browser layer.
@@ -116,6 +125,8 @@ class OpenNyxStore {
116125
std::vector<HistoryEntry> history_; // newest last.
117126
std::vector<Bookmark> bookmarks_; // newest last.
118127
std::vector<DownloadEntry> downloads_; // newest last.
128+
std::vector<std::string> session_tabs_; // open tab URLs (tab order).
129+
size_t session_active_ = 0;
119130
};
120131

121132
#endif // OPENNYX_SHELL_SRC_STORE_H_

0 commit comments

Comments
 (0)