Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 1 | // 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 5 | #include "weblayer/browser/persistence/browser_persister.h" |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 6 | |
| 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 Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 21 | #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-Malek | 6ecfbd3 | 2020-07-22 18:11:31 | [diff] [blame] | 27 | #include "weblayer/browser/browser_context_impl.h" |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 28 | #include "weblayer/browser/browser_impl.h" |
Scott Violet | 343f4ec | 2020-01-30 02:58:08 | [diff] [blame] | 29 | #include "weblayer/browser/persistence/browser_persistence_common.h" |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 30 | #include "weblayer/browser/profile_impl.h" |
| 31 | #include "weblayer/browser/tab_impl.h" |
| 32 | |
| 33 | using sessions::ContentSerializedNavigationBuilder; |
| 34 | using sessions::SerializedNavigationEntry; |
| 35 | |
| 36 | namespace weblayer { |
| 37 | namespace { |
| 38 | |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 39 | int 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. |
| 49 | constexpr int kWritesPerReset = 250; |
| 50 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 51 | // BrowserPersister |
| 52 | // ------------------------------------------------------------- |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 53 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 54 | BrowserPersister::BrowserPersister(const base::FilePath& path, |
| 55 | BrowserImpl* browser, |
| 56 | const std::vector<uint8_t>& decryption_key) |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 57 | : browser_(browser), |
| 58 | browser_session_id_(SessionID::NewUnique()), |
| 59 | command_storage_manager_( |
Scott Violet | bf8b8aa7 | 2020-01-28 19:37:32 | [diff] [blame] | 60 | 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 Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 66 | browser_->AddObserver(this); |
Scott Violet | 2664a2c | 2020-07-07 20:23:32 | [diff] [blame] | 67 | command_storage_manager_->GetCurrentSessionCommands( |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 68 | base::BindOnce(&BrowserPersister::OnGotCurrentSessionCommands, |
Scott Violet | 2664a2c | 2020-07-07 20:23:32 | [diff] [blame] | 69 | weak_factory_.GetWeakPtr()), |
| 70 | decryption_key); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 71 | } |
| 72 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 73 | BrowserPersister::~BrowserPersister() { |
Scott Violet | bf8b8aa7 | 2020-01-28 19:37:32 | [diff] [blame] | 74 | SaveIfNecessary(); |
| 75 | browser_->RemoveObserver(this); |
| 76 | } |
| 77 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 78 | void BrowserPersister::SaveIfNecessary() { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 79 | if (command_storage_manager_->HasPendingSave()) |
| 80 | command_storage_manager_->Save(); |
Scott Violet | bf8b8aa7 | 2020-01-28 19:37:32 | [diff] [blame] | 81 | } |
| 82 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 83 | const std::vector<uint8_t>& BrowserPersister::GetCryptoKey() const { |
Scott Violet | bf8b8aa7 | 2020-01-28 19:37:32 | [diff] [blame] | 84 | return crypto_key_; |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 85 | } |
| 86 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 87 | bool BrowserPersister::ShouldUseDelayedSave() { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 88 | return true; |
| 89 | } |
| 90 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 91 | void BrowserPersister::OnWillSaveCommands() { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 92 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 102 | void BrowserPersister::OnGeneratedNewCryptoKey( |
| 103 | const std::vector<uint8_t>& key) { |
Scott Violet | bf8b8aa7 | 2020-01-28 19:37:32 | [diff] [blame] | 104 | crypto_key_ = key; |
| 105 | } |
| 106 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 107 | void BrowserPersister::OnTabAdded(Tab* tab) { |
Clark DuVall | b7c0994 | 2020-05-20 04:12:51 | [diff] [blame] | 108 | auto* tab_impl = static_cast<TabImpl*>(tab); |
| 109 | data_observer_.Add(tab_impl); |
| 110 | content::WebContents* web_contents = tab_impl->web_contents(); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 111 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 133 | void BrowserPersister::OnTabRemoved(Tab* tab, bool active_tab_changed) { |
Clark DuVall | b7c0994 | 2020-05-20 04:12:51 | [diff] [blame] | 134 | auto* tab_impl = static_cast<TabImpl*>(tab); |
| 135 | data_observer_.Remove(tab_impl); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 136 | // Allow the associated sessionStorage to get deleted; it won't be needed |
| 137 | // in the session restore. |
Clark DuVall | b7c0994 | 2020-05-20 04:12:51 | [diff] [blame] | 138 | content::WebContents* web_contents = tab_impl->web_contents(); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 139 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 157 | void BrowserPersister::OnActiveTabChanged(Tab* tab) { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 158 | 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 DuVall | b7c0994 | 2020-05-20 04:12:51 | [diff] [blame] | 166 | void 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 176 | void BrowserPersister::SetTabUserAgentOverride( |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 177 | const SessionID& window_id, |
| 178 | const SessionID& tab_id, |
Maks Orlovich | 30312696 | 2020-04-06 21:34:25 | [diff] [blame] | 179 | const sessions::SerializedUserAgentOverride& user_agent_override) { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 180 | if (rebuild_on_next_save_) |
| 181 | return; |
| 182 | |
| 183 | ScheduleCommand(sessions::CreateSetTabUserAgentOverrideCommand( |
| 184 | tab_id, user_agent_override)); |
| 185 | } |
| 186 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 187 | void BrowserPersister::SetSelectedNavigationIndex(const SessionID& window_id, |
| 188 | const SessionID& tab_id, |
| 189 | int index) { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 190 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 206 | void BrowserPersister::UpdateTabNavigation( |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 207 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 221 | void BrowserPersister::TabNavigationPathPruned(const SessionID& window_id, |
| 222 | const SessionID& tab_id, |
| 223 | int index, |
| 224 | int count) { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 225 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 258 | void BrowserPersister::TabNavigationPathEntriesDeleted( |
| 259 | const SessionID& window_id, |
| 260 | const SessionID& tab_id) { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 261 | 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 270 | void BrowserPersister::ScheduleRebuildOnNextSave() { |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 271 | rebuild_on_next_save_ = true; |
| 272 | command_storage_manager_->StartSaveTimer(); |
| 273 | } |
| 274 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 275 | void BrowserPersister::OnGotCurrentSessionCommands( |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 276 | std::vector<std::unique_ptr<sessions::SessionCommand>> commands) { |
| 277 | ScheduleRebuildOnNextSave(); |
| 278 | |
Scott Violet | 343f4ec | 2020-01-30 02:58:08 | [diff] [blame] | 279 | RestoreBrowserState(browser_, std::move(commands)); |
Scott Violet | f251a02 | 2020-10-07 16:54:43 | [diff] [blame^] | 280 | |
| 281 | is_restore_in_progress_ = false; |
| 282 | browser_->OnRestoreCompleted(); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 283 | } |
| 284 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 285 | void BrowserPersister::BuildCommandsForTab(TabImpl* tab, int index_in_browser) { |
Scott Violet | 343f4ec | 2020-01-30 02:58:08 | [diff] [blame] | 286 | command_storage_manager_->AppendRebuildCommands( |
| 287 | BuildCommandsForTabConfiguration(browser_session_id_, tab, |
| 288 | index_in_browser)); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 289 | |
Scott Violet | 343f4ec | 2020-01-30 02:58:08 | [diff] [blame] | 290 | const SessionID& session_id = GetSessionIDForTab(tab); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 291 | 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 Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 303 | 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 Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 317 | // Record the association between the sessionStorage namespace and the tab. |
| 318 | content::SessionStorageNamespace* session_storage_namespace = |
| 319 | controller.GetDefaultSessionStorageNamespace(); |
| 320 | ScheduleCommand(sessions::CreateSessionStorageAssociatedCommand( |
Scott Violet | 343f4ec | 2020-01-30 02:58:08 | [diff] [blame] | 321 | session_id, session_storage_namespace->id())); |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 322 | } |
| 323 | |
Scott Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 324 | void BrowserPersister::BuildCommandsForBrowser() { |
| 325 | // This is necessary for BrowserPersister to restore the browser. The type is |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 326 | // 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 Violet | 9843d0a | 2020-02-11 22:38:07 | [diff] [blame] | 346 | void BrowserPersister::ScheduleCommand( |
Scott Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 347 | 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 Violet | 87450ce | 2020-01-23 01:56:25 | [diff] [blame] | 356 | } // namespace weblayer |