blob: 8e2c3423ee4cdc066c050a25256711b456b0573e [file] [log] [blame]
Scott Violet87450ce2020-01-23 01:56:251// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Scott Violet9843d0a2020-02-11 22:38:075#include "weblayer/browser/persistence/browser_persister.h"
Scott Violet87450ce2020-01-23 01:56:256
7#include <stddef.h>
8
9#include <algorithm>
10#include <utility>
11#include <vector>
12
13#include "base/bind.h"
14#include "components/sessions/content/content_serialized_navigation_builder.h"
15#include "components/sessions/content/session_tab_helper.h"
16#include "components/sessions/core/command_storage_manager.h"
17#include "components/sessions/core/session_command.h"
18#include "components/sessions/core/session_constants.h"
19#include "components/sessions/core/session_id.h"
20#include "components/sessions/core/session_types.h"
Scott Violet87450ce2020-01-23 01:56:2521#include "content/public/browser/navigation_details.h"
22#include "content/public/browser/navigation_entry.h"
23#include "content/public/browser/restore_type.h"
24#include "content/public/browser/session_storage_namespace.h"
25#include "content/public/browser/storage_partition.h"
26#include "content/public/browser/web_contents.h"
John Abd-El-Malek6ecfbd32020-07-22 18:11:3127#include "weblayer/browser/browser_context_impl.h"
Scott Violet87450ce2020-01-23 01:56:2528#include "weblayer/browser/browser_impl.h"
Scott Violet343f4ec2020-01-30 02:58:0829#include "weblayer/browser/persistence/browser_persistence_common.h"
Scott Violet87450ce2020-01-23 01:56:2530#include "weblayer/browser/profile_impl.h"
31#include "weblayer/browser/tab_impl.h"
32
33using sessions::ContentSerializedNavigationBuilder;
34using sessions::SerializedNavigationEntry;
35
36namespace weblayer {
37namespace {
38
Scott Violet87450ce2020-01-23 01:56:2539int GetIndexOfTab(BrowserImpl* browser, Tab* tab) {
40 const std::vector<Tab*>& tabs = browser->GetTabs();
41 auto iter = std::find(tabs.begin(), tabs.end(), tab);
42 DCHECK(iter != tabs.end());
43 return static_cast<int>(iter - tabs.begin());
44}
45
46} // namespace
47
48// Every kWritesPerReset commands triggers recreating the file.
49constexpr int kWritesPerReset = 250;
50
Scott Violet9843d0a2020-02-11 22:38:0751// BrowserPersister
52// -------------------------------------------------------------
Scott Violet87450ce2020-01-23 01:56:2553
Scott Violet9843d0a2020-02-11 22:38:0754BrowserPersister::BrowserPersister(const base::FilePath& path,
55 BrowserImpl* browser,
56 const std::vector<uint8_t>& decryption_key)
Scott Violet87450ce2020-01-23 01:56:2557 : browser_(browser),
58 browser_session_id_(SessionID::NewUnique()),
59 command_storage_manager_(
Scott Violetbf8b8aa72020-01-28 19:37:3260 std::make_unique<sessions::CommandStorageManager>(
61 path,
62 this,
63 browser->profile()->GetBrowserContext()->IsOffTheRecord())),
64 rebuild_on_next_save_(false),
65 crypto_key_(decryption_key) {
Scott Violet87450ce2020-01-23 01:56:2566 browser_->AddObserver(this);
Scott Violet2664a2c2020-07-07 20:23:3267 command_storage_manager_->GetCurrentSessionCommands(
Scott Violet9843d0a2020-02-11 22:38:0768 base::BindOnce(&BrowserPersister::OnGotCurrentSessionCommands,
Scott Violet2664a2c2020-07-07 20:23:3269 weak_factory_.GetWeakPtr()),
70 decryption_key);
Scott Violet87450ce2020-01-23 01:56:2571}
72
Scott Violet9843d0a2020-02-11 22:38:0773BrowserPersister::~BrowserPersister() {
Scott Violetbf8b8aa72020-01-28 19:37:3274 SaveIfNecessary();
75 browser_->RemoveObserver(this);
76}
77
Scott Violet9843d0a2020-02-11 22:38:0778void BrowserPersister::SaveIfNecessary() {
Scott Violet87450ce2020-01-23 01:56:2579 if (command_storage_manager_->HasPendingSave())
80 command_storage_manager_->Save();
Scott Violetbf8b8aa72020-01-28 19:37:3281}
82
Scott Violet9843d0a2020-02-11 22:38:0783const std::vector<uint8_t>& BrowserPersister::GetCryptoKey() const {
Scott Violetbf8b8aa72020-01-28 19:37:3284 return crypto_key_;
Scott Violet87450ce2020-01-23 01:56:2585}
86
Scott Violet9843d0a2020-02-11 22:38:0787bool BrowserPersister::ShouldUseDelayedSave() {
Scott Violet87450ce2020-01-23 01:56:2588 return true;
89}
90
Scott Violet9843d0a2020-02-11 22:38:0791void BrowserPersister::OnWillSaveCommands() {
Scott Violet87450ce2020-01-23 01:56:2592 if (!rebuild_on_next_save_)
93 return;
94
95 rebuild_on_next_save_ = false;
96 command_storage_manager_->set_pending_reset(true);
97 command_storage_manager_->ClearPendingCommands();
98 tab_to_available_range_.clear();
99 BuildCommandsForBrowser();
100}
101
Scott Violet9843d0a2020-02-11 22:38:07102void BrowserPersister::OnGeneratedNewCryptoKey(
103 const std::vector<uint8_t>& key) {
Scott Violetbf8b8aa72020-01-28 19:37:32104 crypto_key_ = key;
105}
106
Scott Violet9843d0a2020-02-11 22:38:07107void BrowserPersister::OnTabAdded(Tab* tab) {
Clark DuVallb7c09942020-05-20 04:12:51108 auto* tab_impl = static_cast<TabImpl*>(tab);
109 data_observer_.Add(tab_impl);
110 content::WebContents* web_contents = tab_impl->web_contents();
Scott Violet87450ce2020-01-23 01:56:25111 auto* tab_helper = sessions::SessionTabHelper::FromWebContents(web_contents);
112 DCHECK(tab_helper);
113 tab_helper->SetWindowID(browser_session_id_);
114
115 // Record the association between the SessionStorageNamespace and the
116 // tab.
117 content::SessionStorageNamespace* session_storage_namespace =
118 web_contents->GetController().GetDefaultSessionStorageNamespace();
119 session_storage_namespace->SetShouldPersist(true);
120
121 if (rebuild_on_next_save_)
122 return;
123
124 int index = GetIndexOfTab(browser_, tab);
125 BuildCommandsForTab(static_cast<TabImpl*>(tab), index);
126 const std::vector<Tab*>& tabs = browser_->GetTabs();
127 for (int i = index + 1; i < static_cast<int>(tabs.size()); ++i) {
128 ScheduleCommand(sessions::CreateSetTabIndexInWindowCommand(
129 GetSessionIDForTab(tabs[i]), i));
130 }
131}
132
Scott Violet9843d0a2020-02-11 22:38:07133void BrowserPersister::OnTabRemoved(Tab* tab, bool active_tab_changed) {
Clark DuVallb7c09942020-05-20 04:12:51134 auto* tab_impl = static_cast<TabImpl*>(tab);
135 data_observer_.Remove(tab_impl);
Scott Violet87450ce2020-01-23 01:56:25136 // Allow the associated sessionStorage to get deleted; it won't be needed
137 // in the session restore.
Clark DuVallb7c09942020-05-20 04:12:51138 content::WebContents* web_contents = tab_impl->web_contents();
Scott Violet87450ce2020-01-23 01:56:25139 content::SessionStorageNamespace* session_storage_namespace =
140 web_contents->GetController().GetDefaultSessionStorageNamespace();
141 session_storage_namespace->SetShouldPersist(false);
142
143 if (rebuild_on_next_save_)
144 return;
145
146 ScheduleCommand(sessions::CreateTabClosedCommand(GetSessionIDForTab(tab)));
147 const std::vector<Tab*>& tabs = browser_->GetTabs();
148 for (size_t i = 0; i < tabs.size(); ++i) {
149 ScheduleCommand(sessions::CreateSetTabIndexInWindowCommand(
150 GetSessionIDForTab(tabs[i]), i));
151 }
152 auto i = tab_to_available_range_.find(GetSessionIDForTab(tab));
153 if (i != tab_to_available_range_.end())
154 tab_to_available_range_.erase(i);
155}
156
Scott Violet9843d0a2020-02-11 22:38:07157void BrowserPersister::OnActiveTabChanged(Tab* tab) {
Scott Violet87450ce2020-01-23 01:56:25158 if (rebuild_on_next_save_)
159 return;
160
161 const int index = tab == nullptr ? -1 : GetIndexOfTab(browser_, tab);
162 ScheduleCommand(sessions::CreateSetSelectedTabInWindowCommand(
163 browser_session_id_, index));
164}
165
Clark DuVallb7c09942020-05-20 04:12:51166void BrowserPersister::OnDataChanged(
167 TabImpl* tab,
168 const std::map<std::string, std::string>& data) {
169 if (rebuild_on_next_save_)
170 return;
171
172 ScheduleCommand(
173 sessions::CreateSetTabDataCommand(GetSessionIDForTab(tab), data));
174}
175
Scott Violet9843d0a2020-02-11 22:38:07176void BrowserPersister::SetTabUserAgentOverride(
Scott Violet87450ce2020-01-23 01:56:25177 const SessionID& window_id,
178 const SessionID& tab_id,
Maks Orlovich303126962020-04-06 21:34:25179 const sessions::SerializedUserAgentOverride& user_agent_override) {
Scott Violet87450ce2020-01-23 01:56:25180 if (rebuild_on_next_save_)
181 return;
182
183 ScheduleCommand(sessions::CreateSetTabUserAgentOverrideCommand(
184 tab_id, user_agent_override));
185}
186
Scott Violet9843d0a2020-02-11 22:38:07187void BrowserPersister::SetSelectedNavigationIndex(const SessionID& window_id,
188 const SessionID& tab_id,
189 int index) {
Scott Violet87450ce2020-01-23 01:56:25190 if (rebuild_on_next_save_)
191 return;
192
193 if (tab_to_available_range_.find(tab_id) != tab_to_available_range_.end()) {
194 if (index < tab_to_available_range_[tab_id].first ||
195 index > tab_to_available_range_[tab_id].second) {
196 // The new index is outside the range of what we've archived, schedule
197 // a reset.
198 ScheduleRebuildOnNextSave();
199 return;
200 }
201 }
202 ScheduleCommand(
203 sessions::CreateSetSelectedNavigationIndexCommand(tab_id, index));
204}
205
Scott Violet9843d0a2020-02-11 22:38:07206void BrowserPersister::UpdateTabNavigation(
Scott Violet87450ce2020-01-23 01:56:25207 const SessionID& window_id,
208 const SessionID& tab_id,
209 const SerializedNavigationEntry& navigation) {
210 if (rebuild_on_next_save_)
211 return;
212
213 if (tab_to_available_range_.find(tab_id) != tab_to_available_range_.end()) {
214 std::pair<int, int>& range = tab_to_available_range_[tab_id];
215 range.first = std::min(navigation.index(), range.first);
216 range.second = std::max(navigation.index(), range.second);
217 }
218 ScheduleCommand(CreateUpdateTabNavigationCommand(tab_id, navigation));
219}
220
Scott Violet9843d0a2020-02-11 22:38:07221void BrowserPersister::TabNavigationPathPruned(const SessionID& window_id,
222 const SessionID& tab_id,
223 int index,
224 int count) {
Scott Violet87450ce2020-01-23 01:56:25225 if (rebuild_on_next_save_)
226 return;
227
228 DCHECK_GE(index, 0);
229 DCHECK_GT(count, 0);
230
231 // Update the range of available indices.
232 if (tab_to_available_range_.find(tab_id) != tab_to_available_range_.end()) {
233 std::pair<int, int>& range = tab_to_available_range_[tab_id];
234
235 // if both range.first and range.second are also deleted.
236 if (range.second >= index && range.second < index + count &&
237 range.first >= index && range.first < index + count) {
238 range.first = range.second = 0;
239 } else {
240 // Update range.first
241 if (range.first >= index + count)
242 range.first = range.first - count;
243 else if (range.first >= index && range.first < index + count)
244 range.first = index;
245
246 // Update range.second
247 if (range.second >= index + count)
248 range.second = std::max(range.first, range.second - count);
249 else if (range.second >= index && range.second < index + count)
250 range.second = std::max(range.first, index - 1);
251 }
252 }
253
254 return ScheduleCommand(
255 sessions::CreateTabNavigationPathPrunedCommand(tab_id, index, count));
256}
257
Scott Violet9843d0a2020-02-11 22:38:07258void BrowserPersister::TabNavigationPathEntriesDeleted(
259 const SessionID& window_id,
260 const SessionID& tab_id) {
Scott Violet87450ce2020-01-23 01:56:25261 if (rebuild_on_next_save_)
262 return;
263
264 // Multiple tabs might be affected by this deletion, so the rebuild is
265 // delayed until next save.
266 rebuild_on_next_save_ = true;
267 command_storage_manager_->StartSaveTimer();
268}
269
Scott Violet9843d0a2020-02-11 22:38:07270void BrowserPersister::ScheduleRebuildOnNextSave() {
Scott Violet87450ce2020-01-23 01:56:25271 rebuild_on_next_save_ = true;
272 command_storage_manager_->StartSaveTimer();
273}
274
Scott Violet9843d0a2020-02-11 22:38:07275void BrowserPersister::OnGotCurrentSessionCommands(
Scott Violet87450ce2020-01-23 01:56:25276 std::vector<std::unique_ptr<sessions::SessionCommand>> commands) {
277 ScheduleRebuildOnNextSave();
278
Scott Violet343f4ec2020-01-30 02:58:08279 RestoreBrowserState(browser_, std::move(commands));
Scott Violetf251a022020-10-07 16:54:43280
281 is_restore_in_progress_ = false;
282 browser_->OnRestoreCompleted();
Scott Violet87450ce2020-01-23 01:56:25283}
284
Scott Violet9843d0a2020-02-11 22:38:07285void BrowserPersister::BuildCommandsForTab(TabImpl* tab, int index_in_browser) {
Scott Violet343f4ec2020-01-30 02:58:08286 command_storage_manager_->AppendRebuildCommands(
287 BuildCommandsForTabConfiguration(browser_session_id_, tab,
288 index_in_browser));
Scott Violet87450ce2020-01-23 01:56:25289
Scott Violet343f4ec2020-01-30 02:58:08290 const SessionID& session_id = GetSessionIDForTab(tab);
Scott Violet87450ce2020-01-23 01:56:25291 content::NavigationController& controller =
292 tab->web_contents()->GetController();
293 const int current_index = controller.GetCurrentEntryIndex();
294 const int min_index =
295 std::max(current_index - sessions::gMaxPersistNavigationCount, 0);
296 const int max_index =
297 std::min(current_index + sessions::gMaxPersistNavigationCount,
298 controller.GetEntryCount());
299 const int pending_index = controller.GetPendingEntryIndex();
300 tab_to_available_range_[session_id] =
301 std::pair<int, int>(min_index, max_index);
302
Scott Violet87450ce2020-01-23 01:56:25303 for (int i = min_index; i < max_index; ++i) {
304 content::NavigationEntry* entry = (i == pending_index)
305 ? controller.GetPendingEntry()
306 : controller.GetEntryAtIndex(i);
307 DCHECK(entry);
308 const SerializedNavigationEntry navigation =
309 ContentSerializedNavigationBuilder::FromNavigationEntry(i, entry);
310 command_storage_manager_->AppendRebuildCommand(
311 CreateUpdateTabNavigationCommand(session_id, navigation));
312 }
313 command_storage_manager_->AppendRebuildCommand(
314 sessions::CreateSetSelectedNavigationIndexCommand(session_id,
315 current_index));
316
Scott Violet87450ce2020-01-23 01:56:25317 // Record the association between the sessionStorage namespace and the tab.
318 content::SessionStorageNamespace* session_storage_namespace =
319 controller.GetDefaultSessionStorageNamespace();
320 ScheduleCommand(sessions::CreateSessionStorageAssociatedCommand(
Scott Violet343f4ec2020-01-30 02:58:08321 session_id, session_storage_namespace->id()));
Scott Violet87450ce2020-01-23 01:56:25322}
323
Scott Violet9843d0a2020-02-11 22:38:07324void BrowserPersister::BuildCommandsForBrowser() {
325 // This is necessary for BrowserPersister to restore the browser. The type is
Scott Violet87450ce2020-01-23 01:56:25326 // effectively ignored.
327 command_storage_manager_->AppendRebuildCommand(
328 sessions::CreateSetWindowTypeCommand(
329 browser_session_id_,
330 sessions::SessionWindow::WindowType::TYPE_NORMAL));
331
332 int active_index = -1;
333 int tab_index = 0;
334 for (Tab* tab : browser_->GetTabs()) {
335 BuildCommandsForTab(static_cast<TabImpl*>(tab), tab_index);
336 if (tab == browser_->GetActiveTab())
337 active_index = tab_index;
338 ++tab_index;
339 }
340
341 command_storage_manager_->AppendRebuildCommand(
342 sessions::CreateSetSelectedTabInWindowCommand(browser_session_id_,
343 active_index));
344}
345
Scott Violet9843d0a2020-02-11 22:38:07346void BrowserPersister::ScheduleCommand(
Scott Violet87450ce2020-01-23 01:56:25347 std::unique_ptr<sessions::SessionCommand> command) {
348 DCHECK(command);
349 if (ReplacePendingCommand(command_storage_manager_.get(), &command))
350 return;
351 command_storage_manager_->ScheduleCommand(std::move(command));
352 if (command_storage_manager_->commands_since_reset() >= kWritesPerReset)
353 ScheduleRebuildOnNextSave();
354}
355
Scott Violet87450ce2020-01-23 01:56:25356} // namespace weblayer