Roll src/third_party/WebKit 1cd8cc5:4e01097 (svn 202507:202508)
[chromium-blink-merge.git] / chrome / browser / sessions / tab_restore_service_helper.cc
blobff84794713a9836a9f2990c8280aa137a9f50979
1 // Copyright 2012 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.
5 #include "chrome/browser/sessions/tab_restore_service_helper.h"
7 #include <algorithm>
8 #include <iterator>
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
14 #include "chrome/browser/sessions/tab_restore_service_observer.h"
15 #include "components/sessions/core/live_tab.h"
16 #include "components/sessions/core/tab_restore_service_client.h"
17 #include "components/sessions/serialized_navigation_entry.h"
18 #include "components/sessions/session_types.h"
20 using sessions::LiveTab;
22 // TabRestoreServiceHelper::Observer -------------------------------------------
24 TabRestoreServiceHelper::Observer::~Observer() {}
26 void TabRestoreServiceHelper::Observer::OnClearEntries() {}
28 void TabRestoreServiceHelper::Observer::OnRestoreEntryById(
29 SessionID::id_type id,
30 Entries::const_iterator entry_iterator) {
33 void TabRestoreServiceHelper::Observer::OnAddEntry() {}
35 // TabRestoreServiceHelper -----------------------------------------------------
37 TabRestoreServiceHelper::TabRestoreServiceHelper(
38 TabRestoreService* tab_restore_service,
39 Observer* observer,
40 sessions::TabRestoreServiceClient* client,
41 TabRestoreService::TimeFactory* time_factory)
42 : tab_restore_service_(tab_restore_service),
43 observer_(observer),
44 client_(client),
45 restoring_(false),
46 time_factory_(time_factory) {
47 DCHECK(tab_restore_service_);
50 TabRestoreServiceHelper::~TabRestoreServiceHelper() {
51 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
52 TabRestoreServiceDestroyed(tab_restore_service_));
53 STLDeleteElements(&entries_);
56 void TabRestoreServiceHelper::AddObserver(
57 TabRestoreServiceObserver* observer) {
58 observer_list_.AddObserver(observer);
61 void TabRestoreServiceHelper::RemoveObserver(
62 TabRestoreServiceObserver* observer) {
63 observer_list_.RemoveObserver(observer);
66 void TabRestoreServiceHelper::CreateHistoricalTab(LiveTab* live_tab,
67 int index) {
68 if (restoring_)
69 return;
71 TabRestoreServiceDelegate* delegate =
72 client_->FindTabRestoreServiceDelegateForTab(live_tab);
73 if (closing_delegates_.find(delegate) != closing_delegates_.end())
74 return;
76 scoped_ptr<Tab> local_tab(new Tab());
77 PopulateTab(local_tab.get(), index, delegate, live_tab);
78 if (local_tab->navigations.empty())
79 return;
81 AddEntry(local_tab.release(), true, true);
84 void TabRestoreServiceHelper::BrowserClosing(
85 TabRestoreServiceDelegate* delegate) {
86 closing_delegates_.insert(delegate);
88 scoped_ptr<Window> window(new Window());
89 window->selected_tab_index = delegate->GetSelectedIndex();
90 window->timestamp = TimeNow();
91 window->app_name = delegate->GetAppName();
93 // Don't use std::vector::resize() because it will push copies of an empty tab
94 // into the vector, which will give all tabs in a window the same ID.
95 for (int i = 0; i < delegate->GetTabCount(); ++i) {
96 window->tabs.push_back(Tab());
98 size_t entry_index = 0;
99 for (int tab_index = 0; tab_index < delegate->GetTabCount(); ++tab_index) {
100 PopulateTab(&(window->tabs[entry_index]), tab_index, delegate,
101 delegate->GetLiveTabAt(tab_index));
102 if (window->tabs[entry_index].navigations.empty()) {
103 window->tabs.erase(window->tabs.begin() + entry_index);
104 } else {
105 window->tabs[entry_index].browser_id = delegate->GetSessionID().id();
106 entry_index++;
109 if (window->tabs.size() == 1 && window->app_name.empty()) {
110 // Short-circuit creating a Window if only 1 tab was present. This fixes
111 // http://crbug.com/56744. Copy the Tab because it's owned by an object on
112 // the stack.
113 AddEntry(new Tab(window->tabs[0]), true, true);
114 } else if (!window->tabs.empty()) {
115 window->selected_tab_index =
116 std::min(static_cast<int>(window->tabs.size() - 1),
117 window->selected_tab_index);
118 AddEntry(window.release(), true, true);
122 void TabRestoreServiceHelper::BrowserClosed(
123 TabRestoreServiceDelegate* delegate) {
124 closing_delegates_.erase(delegate);
127 void TabRestoreServiceHelper::ClearEntries() {
128 if (observer_)
129 observer_->OnClearEntries();
130 STLDeleteElements(&entries_);
131 NotifyTabsChanged();
134 const TabRestoreService::Entries& TabRestoreServiceHelper::entries() const {
135 return entries_;
138 std::vector<LiveTab*> TabRestoreServiceHelper::RestoreMostRecentEntry(
139 TabRestoreServiceDelegate* delegate,
140 int host_desktop_type) {
141 if (entries_.empty())
142 return std::vector<LiveTab*>();
144 return RestoreEntryById(delegate, entries_.front()->id, host_desktop_type,
145 UNKNOWN);
148 TabRestoreService::Tab* TabRestoreServiceHelper::RemoveTabEntryById(
149 SessionID::id_type id) {
150 Entries::iterator i = GetEntryIteratorById(id);
151 if (i == entries_.end())
152 return NULL;
154 Entry* entry = *i;
155 if (entry->type != TabRestoreService::TAB)
156 return NULL;
158 Tab* tab = static_cast<Tab*>(entry);
159 entries_.erase(i);
160 return tab;
163 std::vector<LiveTab*> TabRestoreServiceHelper::RestoreEntryById(
164 TabRestoreServiceDelegate* delegate,
165 SessionID::id_type id,
166 int host_desktop_type,
167 WindowOpenDisposition disposition) {
168 Entries::iterator entry_iterator = GetEntryIteratorById(id);
169 if (entry_iterator == entries_.end())
170 // Don't hoark here, we allow an invalid id.
171 return std::vector<LiveTab*>();
173 if (observer_)
174 observer_->OnRestoreEntryById(id, entry_iterator);
175 restoring_ = true;
176 Entry* entry = *entry_iterator;
178 // If the entry's ID does not match the ID that is being restored, then the
179 // entry is a window from which a single tab will be restored.
180 bool restoring_tab_in_window = entry->id != id;
182 if (!restoring_tab_in_window) {
183 entries_.erase(entry_iterator);
184 entry_iterator = entries_.end();
187 // |delegate| will be NULL in cases where one isn't already available (eg,
188 // when invoked on Mac OS X with no windows open). In this case, create a
189 // new browser into which we restore the tabs.
190 std::vector<LiveTab*> live_tabs;
191 if (entry->type == TabRestoreService::TAB) {
192 Tab* tab = static_cast<Tab*>(entry);
193 LiveTab* restored_tab = NULL;
194 delegate = RestoreTab(*tab, delegate, host_desktop_type, disposition,
195 &restored_tab);
196 live_tabs.push_back(restored_tab);
197 delegate->ShowBrowserWindow();
198 } else if (entry->type == TabRestoreService::WINDOW) {
199 TabRestoreServiceDelegate* current_delegate = delegate;
200 Window* window = static_cast<Window*>(entry);
202 // When restoring a window, either the entire window can be restored, or a
203 // single tab within it. If the entry's ID matches the one to restore, then
204 // the entire window will be restored.
205 if (!restoring_tab_in_window) {
206 delegate = client_->CreateTabRestoreServiceDelegate(host_desktop_type,
207 window->app_name);
208 for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
209 const Tab& tab = window->tabs[tab_i];
210 LiveTab* restored_tab = delegate->AddRestoredTab(
211 tab.navigations, delegate->GetTabCount(),
212 tab.current_navigation_index, tab.extension_app_id,
213 static_cast<int>(tab_i) == window->selected_tab_index, tab.pinned,
214 tab.from_last_session, tab.client_data.get(),
215 tab.user_agent_override);
216 if (restored_tab) {
217 restored_tab->LoadIfNecessary();
218 client_->OnTabRestored(
219 tab.navigations.at(tab.current_navigation_index).virtual_url());
220 live_tabs.push_back(restored_tab);
223 // All the window's tabs had the same former browser_id.
224 if (window->tabs[0].has_browser()) {
225 UpdateTabBrowserIDs(window->tabs[0].browser_id,
226 delegate->GetSessionID().id());
228 } else {
229 // Restore a single tab from the window. Find the tab that matches the ID
230 // in the window and restore it.
231 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
232 tab_i != window->tabs.end(); ++tab_i) {
233 const Tab& tab = *tab_i;
234 if (tab.id == id) {
235 LiveTab* restored_tab = NULL;
236 delegate = RestoreTab(tab, delegate, host_desktop_type, disposition,
237 &restored_tab);
238 live_tabs.push_back(restored_tab);
239 window->tabs.erase(tab_i);
240 // If restoring the tab leaves the window with nothing else, delete it
241 // as well.
242 if (!window->tabs.size()) {
243 entries_.erase(entry_iterator);
244 delete entry;
245 } else {
246 // Update the browser ID of the rest of the tabs in the window so if
247 // any one is restored, it goes into the same window as the tab
248 // being restored now.
249 UpdateTabBrowserIDs(tab.browser_id,
250 delegate->GetSessionID().id());
251 for (std::vector<Tab>::iterator tab_j = window->tabs.begin();
252 tab_j != window->tabs.end(); ++tab_j) {
253 (*tab_j).browser_id = delegate->GetSessionID().id();
256 break;
260 delegate->ShowBrowserWindow();
262 if (disposition == CURRENT_TAB && current_delegate &&
263 current_delegate->GetActiveLiveTab()) {
264 current_delegate->CloseTab();
266 } else {
267 NOTREACHED();
270 if (!restoring_tab_in_window) {
271 delete entry;
274 restoring_ = false;
275 NotifyTabsChanged();
276 return live_tabs;
279 void TabRestoreServiceHelper::NotifyTabsChanged() {
280 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
281 TabRestoreServiceChanged(tab_restore_service_));
284 void TabRestoreServiceHelper::NotifyLoaded() {
285 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
286 TabRestoreServiceLoaded(tab_restore_service_));
289 void TabRestoreServiceHelper::AddEntry(Entry* entry,
290 bool notify,
291 bool to_front) {
292 if (!FilterEntry(entry) || (entries_.size() >= kMaxEntries && !to_front)) {
293 delete entry;
294 return;
297 if (to_front)
298 entries_.push_front(entry);
299 else
300 entries_.push_back(entry);
302 PruneEntries();
304 if (notify)
305 NotifyTabsChanged();
307 if (observer_)
308 observer_->OnAddEntry();
311 void TabRestoreServiceHelper::PruneEntries() {
312 Entries new_entries;
314 for (TabRestoreService::Entries::const_iterator iter = entries_.begin();
315 iter != entries_.end(); ++iter) {
316 TabRestoreService::Entry* entry = *iter;
318 if (FilterEntry(entry) &&
319 new_entries.size() < kMaxEntries) {
320 new_entries.push_back(entry);
321 } else {
322 delete entry;
326 entries_ = new_entries;
329 TabRestoreService::Entries::iterator
330 TabRestoreServiceHelper::GetEntryIteratorById(SessionID::id_type id) {
331 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
332 if ((*i)->id == id)
333 return i;
335 // For Window entries, see if the ID matches a tab. If so, report the window
336 // as the Entry.
337 if ((*i)->type == TabRestoreService::WINDOW) {
338 std::vector<Tab>& tabs = static_cast<Window*>(*i)->tabs;
339 for (std::vector<Tab>::iterator j = tabs.begin();
340 j != tabs.end(); ++j) {
341 if ((*j).id == id) {
342 return i;
347 return entries_.end();
350 // static
351 bool TabRestoreServiceHelper::ValidateEntry(Entry* entry) {
352 if (entry->type == TabRestoreService::TAB)
353 return ValidateTab(static_cast<Tab*>(entry));
355 if (entry->type == TabRestoreService::WINDOW)
356 return ValidateWindow(static_cast<Window*>(entry));
358 NOTREACHED();
359 return false;
362 void TabRestoreServiceHelper::PopulateTab(Tab* tab,
363 int index,
364 TabRestoreServiceDelegate* delegate,
365 LiveTab* live_tab) {
366 const int pending_index = live_tab->GetPendingEntryIndex();
367 int entry_count = live_tab->GetEntryCount();
368 if (entry_count == 0 && pending_index == 0)
369 entry_count++;
370 tab->navigations.resize(static_cast<int>(entry_count));
371 for (int i = 0; i < entry_count; ++i) {
372 sessions::SerializedNavigationEntry entry =
373 (i == pending_index) ? live_tab->GetPendingEntry()
374 : live_tab->GetEntryAtIndex(i);
375 tab->navigations[i] = entry;
377 tab->timestamp = TimeNow();
378 tab->current_navigation_index = live_tab->GetCurrentEntryIndex();
379 if (tab->current_navigation_index == -1 && entry_count > 0)
380 tab->current_navigation_index = 0;
381 tab->tabstrip_index = index;
383 tab->extension_app_id = client_->GetExtensionAppIDForTab(live_tab);
385 tab->user_agent_override = live_tab->GetUserAgentOverride();
387 tab->client_data = client_->GetTabClientDataForTab(live_tab);
389 // Delegate may be NULL during unit tests.
390 if (delegate) {
391 tab->browser_id = delegate->GetSessionID().id();
392 tab->pinned = delegate->IsTabPinned(tab->tabstrip_index);
396 TabRestoreServiceDelegate* TabRestoreServiceHelper::RestoreTab(
397 const Tab& tab,
398 TabRestoreServiceDelegate* delegate,
399 int host_desktop_type,
400 WindowOpenDisposition disposition,
401 LiveTab** live_tab) {
402 LiveTab* restored_tab;
403 if (disposition == CURRENT_TAB && delegate) {
404 restored_tab = delegate->ReplaceRestoredTab(
405 tab.navigations, tab.current_navigation_index, tab.from_last_session,
406 tab.extension_app_id, tab.client_data.get(), tab.user_agent_override);
407 } else {
408 // We only respsect the tab's original browser if there's no disposition.
409 if (disposition == UNKNOWN && tab.has_browser()) {
410 delegate = client_->FindTabRestoreServiceDelegateWithID(
411 tab.browser_id, host_desktop_type);
414 int tab_index = -1;
416 // |delegate| will be NULL in cases where one isn't already available (eg,
417 // when invoked on Mac OS X with no windows open). In this case, create a
418 // new browser into which we restore the tabs.
419 if (delegate && disposition != NEW_WINDOW) {
420 tab_index = tab.tabstrip_index;
421 } else {
422 delegate = client_->CreateTabRestoreServiceDelegate(host_desktop_type,
423 std::string());
424 if (tab.has_browser())
425 UpdateTabBrowserIDs(tab.browser_id, delegate->GetSessionID().id());
428 // Place the tab at the end if the tab index is no longer valid or
429 // we were passed a specific disposition.
430 if (tab_index < 0 || tab_index > delegate->GetTabCount() ||
431 disposition != UNKNOWN) {
432 tab_index = delegate->GetTabCount();
435 restored_tab = delegate->AddRestoredTab(
436 tab.navigations, tab_index, tab.current_navigation_index,
437 tab.extension_app_id, disposition != NEW_BACKGROUND_TAB, tab.pinned,
438 tab.from_last_session, tab.client_data.get(), tab.user_agent_override);
439 restored_tab->LoadIfNecessary();
441 client_->OnTabRestored(
442 tab.navigations.at(tab.current_navigation_index).virtual_url());
443 if (live_tab)
444 *live_tab = restored_tab;
446 return delegate;
450 bool TabRestoreServiceHelper::ValidateTab(Tab* tab) {
451 if (tab->navigations.empty())
452 return false;
454 tab->current_navigation_index =
455 std::max(0, std::min(tab->current_navigation_index,
456 static_cast<int>(tab->navigations.size()) - 1));
458 return true;
461 bool TabRestoreServiceHelper::ValidateWindow(Window* window) {
462 window->selected_tab_index =
463 std::max(0, std::min(window->selected_tab_index,
464 static_cast<int>(window->tabs.size() - 1)));
466 int i = 0;
467 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
468 tab_i != window->tabs.end();) {
469 if (!ValidateTab(&(*tab_i))) {
470 tab_i = window->tabs.erase(tab_i);
471 if (i < window->selected_tab_index)
472 window->selected_tab_index--;
473 else if (i == window->selected_tab_index)
474 window->selected_tab_index = 0;
475 } else {
476 ++tab_i;
477 ++i;
481 if (window->tabs.empty())
482 return false;
484 return true;
487 bool TabRestoreServiceHelper::IsTabInteresting(const Tab* tab) {
488 if (tab->navigations.empty())
489 return false;
491 if (tab->navigations.size() > 1)
492 return true;
494 return tab->pinned ||
495 tab->navigations.at(0).virtual_url() != client_->GetNewTabURL();
498 bool TabRestoreServiceHelper::IsWindowInteresting(const Window* window) {
499 if (window->tabs.empty())
500 return false;
502 if (window->tabs.size() > 1)
503 return true;
505 return IsTabInteresting(&window->tabs[0]);
508 bool TabRestoreServiceHelper::FilterEntry(Entry* entry) {
509 if (!ValidateEntry(entry))
510 return false;
512 if (entry->type == TabRestoreService::TAB)
513 return IsTabInteresting(static_cast<Tab*>(entry));
514 else if (entry->type == TabRestoreService::WINDOW)
515 return IsWindowInteresting(static_cast<Window*>(entry));
517 NOTREACHED();
518 return false;
521 void TabRestoreServiceHelper::UpdateTabBrowserIDs(SessionID::id_type old_id,
522 SessionID::id_type new_id) {
523 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
524 Entry* entry = *i;
525 if (entry->type == TabRestoreService::TAB) {
526 Tab* tab = static_cast<Tab*>(entry);
527 if (tab->browser_id == old_id)
528 tab->browser_id = new_id;
533 base::Time TabRestoreServiceHelper::TimeNow() const {
534 return time_factory_ ? time_factory_->TimeNow() : base::Time::Now();