Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / sessions / tab_restore_service_helper.cc
blob7c1121f43e42da9a72bf63996189c9d793826244
1 // Copyright (c) 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/extensions/tab_helper.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sessions/session_types.h"
16 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
17 #include "chrome/browser/sessions/tab_restore_service_observer.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/common/url_constants.h"
20 #include "content/public/browser/navigation_controller.h"
21 #include "content/public/browser/navigation_entry.h"
22 #include "content/public/browser/session_storage_namespace.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_set.h"
28 #if !defined(OS_ANDROID)
29 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
30 #endif
32 using content::NavigationController;
33 using content::NavigationEntry;
34 using content::WebContents;
36 namespace {
38 void RecordAppLaunch(Profile* profile, const TabRestoreService::Tab& tab) {
39 #if !defined(OS_ANDROID)
40 GURL url = tab.navigations.at(tab.current_navigation_index).virtual_url();
41 const extensions::Extension* extension =
42 extensions::ExtensionRegistry::Get(profile)
43 ->enabled_extensions().GetAppByURL(url);
44 if (!extension)
45 return;
47 CoreAppLauncherHandler::RecordAppLaunchType(
48 extension_misc::APP_LAUNCH_NTP_RECENTLY_CLOSED,
49 extension->GetType());
50 #endif // !defined(OS_ANDROID)
53 } // namespace
55 // TabRestoreServiceHelper::Observer -------------------------------------------
57 TabRestoreServiceHelper::Observer::~Observer() {}
59 void TabRestoreServiceHelper::Observer::OnClearEntries() {}
61 void TabRestoreServiceHelper::Observer::OnRestoreEntryById(
62 SessionID::id_type id,
63 Entries::const_iterator entry_iterator) {
66 void TabRestoreServiceHelper::Observer::OnAddEntry() {}
68 // TabRestoreServiceHelper -----------------------------------------------------
70 TabRestoreServiceHelper::TabRestoreServiceHelper(
71 TabRestoreService* tab_restore_service,
72 Observer* observer,
73 Profile* profile,
74 TabRestoreService::TimeFactory* time_factory)
75 : tab_restore_service_(tab_restore_service),
76 observer_(observer),
77 profile_(profile),
78 restoring_(false),
79 time_factory_(time_factory) {
80 DCHECK(tab_restore_service_);
83 TabRestoreServiceHelper::~TabRestoreServiceHelper() {
84 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
85 TabRestoreServiceDestroyed(tab_restore_service_));
86 STLDeleteElements(&entries_);
89 void TabRestoreServiceHelper::AddObserver(
90 TabRestoreServiceObserver* observer) {
91 observer_list_.AddObserver(observer);
94 void TabRestoreServiceHelper::RemoveObserver(
95 TabRestoreServiceObserver* observer) {
96 observer_list_.RemoveObserver(observer);
99 void TabRestoreServiceHelper::CreateHistoricalTab(
100 content::WebContents* contents,
101 int index) {
102 if (restoring_)
103 return;
105 TabRestoreServiceDelegate* delegate =
106 TabRestoreServiceDelegate::FindDelegateForWebContents(contents);
107 if (closing_delegates_.find(delegate) != closing_delegates_.end())
108 return;
110 scoped_ptr<Tab> local_tab(new Tab());
111 PopulateTab(local_tab.get(), index, delegate, &contents->GetController());
112 if (local_tab->navigations.empty())
113 return;
115 AddEntry(local_tab.release(), true, true);
118 void TabRestoreServiceHelper::BrowserClosing(
119 TabRestoreServiceDelegate* delegate) {
120 closing_delegates_.insert(delegate);
122 scoped_ptr<Window> window(new Window());
123 window->selected_tab_index = delegate->GetSelectedIndex();
124 window->timestamp = TimeNow();
125 window->app_name = delegate->GetAppName();
127 // Don't use std::vector::resize() because it will push copies of an empty tab
128 // into the vector, which will give all tabs in a window the same ID.
129 for (int i = 0; i < delegate->GetTabCount(); ++i) {
130 window->tabs.push_back(Tab());
132 size_t entry_index = 0;
133 for (int tab_index = 0; tab_index < delegate->GetTabCount(); ++tab_index) {
134 PopulateTab(&(window->tabs[entry_index]),
135 tab_index,
136 delegate,
137 &delegate->GetWebContentsAt(tab_index)->GetController());
138 if (window->tabs[entry_index].navigations.empty()) {
139 window->tabs.erase(window->tabs.begin() + entry_index);
140 } else {
141 window->tabs[entry_index].browser_id = delegate->GetSessionID().id();
142 entry_index++;
145 if (window->tabs.size() == 1 && window->app_name.empty()) {
146 // Short-circuit creating a Window if only 1 tab was present. This fixes
147 // http://crbug.com/56744. Copy the Tab because it's owned by an object on
148 // the stack.
149 AddEntry(new Tab(window->tabs[0]), true, true);
150 } else if (!window->tabs.empty()) {
151 window->selected_tab_index =
152 std::min(static_cast<int>(window->tabs.size() - 1),
153 window->selected_tab_index);
154 AddEntry(window.release(), true, true);
158 void TabRestoreServiceHelper::BrowserClosed(
159 TabRestoreServiceDelegate* delegate) {
160 closing_delegates_.erase(delegate);
163 void TabRestoreServiceHelper::ClearEntries() {
164 if (observer_)
165 observer_->OnClearEntries();
166 STLDeleteElements(&entries_);
167 NotifyTabsChanged();
170 const TabRestoreService::Entries& TabRestoreServiceHelper::entries() const {
171 return entries_;
174 std::vector<content::WebContents*>
175 TabRestoreServiceHelper::RestoreMostRecentEntry(
176 TabRestoreServiceDelegate* delegate,
177 chrome::HostDesktopType host_desktop_type) {
178 if (entries_.empty())
179 return std::vector<WebContents*>();
181 return RestoreEntryById(delegate, entries_.front()->id, host_desktop_type,
182 UNKNOWN);
185 TabRestoreService::Tab* TabRestoreServiceHelper::RemoveTabEntryById(
186 SessionID::id_type id) {
187 Entries::iterator i = GetEntryIteratorById(id);
188 if (i == entries_.end())
189 return NULL;
191 Entry* entry = *i;
192 if (entry->type != TabRestoreService::TAB)
193 return NULL;
195 Tab* tab = static_cast<Tab*>(entry);
196 entries_.erase(i);
197 return tab;
200 std::vector<content::WebContents*> TabRestoreServiceHelper::RestoreEntryById(
201 TabRestoreServiceDelegate* delegate,
202 SessionID::id_type id,
203 chrome::HostDesktopType host_desktop_type,
204 WindowOpenDisposition disposition) {
205 Entries::iterator entry_iterator = GetEntryIteratorById(id);
206 if (entry_iterator == entries_.end())
207 // Don't hoark here, we allow an invalid id.
208 return std::vector<WebContents*>();
210 if (observer_)
211 observer_->OnRestoreEntryById(id, entry_iterator);
212 restoring_ = true;
213 Entry* entry = *entry_iterator;
215 // If the entry's ID does not match the ID that is being restored, then the
216 // entry is a window from which a single tab will be restored.
217 bool restoring_tab_in_window = entry->id != id;
219 if (!restoring_tab_in_window) {
220 entries_.erase(entry_iterator);
221 entry_iterator = entries_.end();
224 // |delegate| will be NULL in cases where one isn't already available (eg,
225 // when invoked on Mac OS X with no windows open). In this case, create a
226 // new browser into which we restore the tabs.
227 std::vector<WebContents*> web_contents;
228 if (entry->type == TabRestoreService::TAB) {
229 Tab* tab = static_cast<Tab*>(entry);
230 WebContents* restored_tab = NULL;
231 delegate = RestoreTab(*tab, delegate, host_desktop_type, disposition,
232 &restored_tab);
233 web_contents.push_back(restored_tab);
234 delegate->ShowBrowserWindow();
235 } else if (entry->type == TabRestoreService::WINDOW) {
236 TabRestoreServiceDelegate* current_delegate = delegate;
237 Window* window = static_cast<Window*>(entry);
239 // When restoring a window, either the entire window can be restored, or a
240 // single tab within it. If the entry's ID matches the one to restore, then
241 // the entire window will be restored.
242 if (!restoring_tab_in_window) {
243 delegate = TabRestoreServiceDelegate::Create(profile_, host_desktop_type,
244 window->app_name);
245 for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
246 const Tab& tab = window->tabs[tab_i];
247 WebContents* restored_tab = delegate->AddRestoredTab(
248 tab.navigations,
249 delegate->GetTabCount(),
250 tab.current_navigation_index,
251 tab.extension_app_id,
252 static_cast<int>(tab_i) == window->selected_tab_index,
253 tab.pinned,
254 tab.from_last_session,
255 tab.session_storage_namespace.get(),
256 tab.user_agent_override);
257 if (restored_tab) {
258 restored_tab->GetController().LoadIfNecessary();
259 RecordAppLaunch(profile_, tab);
260 web_contents.push_back(restored_tab);
263 // All the window's tabs had the same former browser_id.
264 if (window->tabs[0].has_browser()) {
265 UpdateTabBrowserIDs(window->tabs[0].browser_id,
266 delegate->GetSessionID().id());
268 } else {
269 // Restore a single tab from the window. Find the tab that matches the ID
270 // in the window and restore it.
271 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
272 tab_i != window->tabs.end(); ++tab_i) {
273 const Tab& tab = *tab_i;
274 if (tab.id == id) {
275 WebContents* restored_tab = NULL;
276 delegate = RestoreTab(tab, delegate, host_desktop_type, disposition,
277 &restored_tab);
278 web_contents.push_back(restored_tab);
279 window->tabs.erase(tab_i);
280 // If restoring the tab leaves the window with nothing else, delete it
281 // as well.
282 if (!window->tabs.size()) {
283 entries_.erase(entry_iterator);
284 delete entry;
285 } else {
286 // Update the browser ID of the rest of the tabs in the window so if
287 // any one is restored, it goes into the same window as the tab
288 // being restored now.
289 UpdateTabBrowserIDs(tab.browser_id,
290 delegate->GetSessionID().id());
291 for (std::vector<Tab>::iterator tab_j = window->tabs.begin();
292 tab_j != window->tabs.end(); ++tab_j) {
293 (*tab_j).browser_id = delegate->GetSessionID().id();
296 break;
300 delegate->ShowBrowserWindow();
302 if (disposition == CURRENT_TAB && current_delegate &&
303 current_delegate->GetActiveWebContents()) {
304 current_delegate->CloseTab();
306 } else {
307 NOTREACHED();
310 if (!restoring_tab_in_window) {
311 delete entry;
314 restoring_ = false;
315 NotifyTabsChanged();
316 return web_contents;
319 void TabRestoreServiceHelper::NotifyTabsChanged() {
320 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
321 TabRestoreServiceChanged(tab_restore_service_));
324 void TabRestoreServiceHelper::NotifyLoaded() {
325 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
326 TabRestoreServiceLoaded(tab_restore_service_));
329 void TabRestoreServiceHelper::AddEntry(Entry* entry,
330 bool notify,
331 bool to_front) {
332 if (!FilterEntry(entry) || (entries_.size() >= kMaxEntries && !to_front)) {
333 delete entry;
334 return;
337 if (to_front)
338 entries_.push_front(entry);
339 else
340 entries_.push_back(entry);
342 PruneEntries();
344 if (notify)
345 NotifyTabsChanged();
347 if (observer_)
348 observer_->OnAddEntry();
351 void TabRestoreServiceHelper::PruneEntries() {
352 Entries new_entries;
354 for (TabRestoreService::Entries::const_iterator iter = entries_.begin();
355 iter != entries_.end(); ++iter) {
356 TabRestoreService::Entry* entry = *iter;
358 if (FilterEntry(entry) &&
359 new_entries.size() < kMaxEntries) {
360 new_entries.push_back(entry);
361 } else {
362 delete entry;
366 entries_ = new_entries;
369 TabRestoreService::Entries::iterator
370 TabRestoreServiceHelper::GetEntryIteratorById(SessionID::id_type id) {
371 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
372 if ((*i)->id == id)
373 return i;
375 // For Window entries, see if the ID matches a tab. If so, report the window
376 // as the Entry.
377 if ((*i)->type == TabRestoreService::WINDOW) {
378 std::vector<Tab>& tabs = static_cast<Window*>(*i)->tabs;
379 for (std::vector<Tab>::iterator j = tabs.begin();
380 j != tabs.end(); ++j) {
381 if ((*j).id == id) {
382 return i;
387 return entries_.end();
390 // static
391 bool TabRestoreServiceHelper::ValidateEntry(Entry* entry) {
392 if (entry->type == TabRestoreService::TAB)
393 return ValidateTab(static_cast<Tab*>(entry));
395 if (entry->type == TabRestoreService::WINDOW)
396 return ValidateWindow(static_cast<Window*>(entry));
398 NOTREACHED();
399 return false;
402 void TabRestoreServiceHelper::PopulateTab(
403 Tab* tab,
404 int index,
405 TabRestoreServiceDelegate* delegate,
406 NavigationController* controller) {
407 const int pending_index = controller->GetPendingEntryIndex();
408 int entry_count = controller->GetEntryCount();
409 if (entry_count == 0 && pending_index == 0)
410 entry_count++;
411 tab->navigations.resize(static_cast<int>(entry_count));
412 for (int i = 0; i < entry_count; ++i) {
413 NavigationEntry* entry = (i == pending_index) ?
414 controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
415 tab->navigations[i] =
416 sessions::SerializedNavigationEntry::FromNavigationEntry(i, *entry);
418 tab->timestamp = TimeNow();
419 tab->current_navigation_index = controller->GetCurrentEntryIndex();
420 if (tab->current_navigation_index == -1 && entry_count > 0)
421 tab->current_navigation_index = 0;
422 tab->tabstrip_index = index;
424 extensions::TabHelper* extensions_tab_helper =
425 extensions::TabHelper::FromWebContents(controller->GetWebContents());
426 // extensions_tab_helper is NULL in some browser tests.
427 if (extensions_tab_helper) {
428 const extensions::Extension* extension =
429 extensions_tab_helper->extension_app();
430 if (extension)
431 tab->extension_app_id = extension->id();
434 tab->user_agent_override =
435 controller->GetWebContents()->GetUserAgentOverride();
437 // TODO(ajwong): This does not correctly handle storage for isolated apps.
438 tab->session_storage_namespace =
439 controller->GetDefaultSessionStorageNamespace();
441 // Delegate may be NULL during unit tests.
442 if (delegate) {
443 tab->browser_id = delegate->GetSessionID().id();
444 tab->pinned = delegate->IsTabPinned(tab->tabstrip_index);
448 TabRestoreServiceDelegate* TabRestoreServiceHelper::RestoreTab(
449 const Tab& tab,
450 TabRestoreServiceDelegate* delegate,
451 chrome::HostDesktopType host_desktop_type,
452 WindowOpenDisposition disposition,
453 WebContents** contents) {
454 WebContents* web_contents;
455 if (disposition == CURRENT_TAB && delegate) {
456 web_contents = delegate->ReplaceRestoredTab(
457 tab.navigations,
458 tab.current_navigation_index,
459 tab.from_last_session,
460 tab.extension_app_id,
461 tab.session_storage_namespace.get(),
462 tab.user_agent_override);
463 } else {
464 // We only respsect the tab's original browser if there's no disposition.
465 if (disposition == UNKNOWN && tab.has_browser()) {
466 delegate = TabRestoreServiceDelegate::FindDelegateWithID(
467 tab.browser_id, host_desktop_type);
470 int tab_index = -1;
472 // |delegate| will be NULL in cases where one isn't already available (eg,
473 // when invoked on Mac OS X with no windows open). In this case, create a
474 // new browser into which we restore the tabs.
475 if (delegate && disposition != NEW_WINDOW) {
476 tab_index = tab.tabstrip_index;
477 } else {
478 delegate = TabRestoreServiceDelegate::Create(profile_, host_desktop_type,
479 std::string());
480 if (tab.has_browser())
481 UpdateTabBrowserIDs(tab.browser_id, delegate->GetSessionID().id());
484 // Place the tab at the end if the tab index is no longer valid or
485 // we were passed a specific disposition.
486 if (tab_index < 0 || tab_index > delegate->GetTabCount() ||
487 disposition != UNKNOWN) {
488 tab_index = delegate->GetTabCount();
491 web_contents = delegate->AddRestoredTab(tab.navigations,
492 tab_index,
493 tab.current_navigation_index,
494 tab.extension_app_id,
495 disposition != NEW_BACKGROUND_TAB,
496 tab.pinned,
497 tab.from_last_session,
498 tab.session_storage_namespace.get(),
499 tab.user_agent_override);
500 web_contents->GetController().LoadIfNecessary();
502 RecordAppLaunch(profile_, tab);
503 if (contents)
504 *contents = web_contents;
506 return delegate;
510 bool TabRestoreServiceHelper::ValidateTab(Tab* tab) {
511 if (tab->navigations.empty())
512 return false;
514 tab->current_navigation_index =
515 std::max(0, std::min(tab->current_navigation_index,
516 static_cast<int>(tab->navigations.size()) - 1));
518 return true;
521 bool TabRestoreServiceHelper::ValidateWindow(Window* window) {
522 window->selected_tab_index =
523 std::max(0, std::min(window->selected_tab_index,
524 static_cast<int>(window->tabs.size() - 1)));
526 int i = 0;
527 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
528 tab_i != window->tabs.end();) {
529 if (!ValidateTab(&(*tab_i))) {
530 tab_i = window->tabs.erase(tab_i);
531 if (i < window->selected_tab_index)
532 window->selected_tab_index--;
533 else if (i == window->selected_tab_index)
534 window->selected_tab_index = 0;
535 } else {
536 ++tab_i;
537 ++i;
541 if (window->tabs.empty())
542 return false;
544 return true;
547 bool TabRestoreServiceHelper::IsTabInteresting(const Tab* tab) {
548 if (tab->navigations.empty())
549 return false;
551 if (tab->navigations.size() > 1)
552 return true;
554 return tab->pinned ||
555 tab->navigations.at(0).virtual_url() !=
556 GURL(chrome::kChromeUINewTabURL);
559 bool TabRestoreServiceHelper::IsWindowInteresting(const Window* window) {
560 if (window->tabs.empty())
561 return false;
563 if (window->tabs.size() > 1)
564 return true;
566 return IsTabInteresting(&window->tabs[0]);
569 bool TabRestoreServiceHelper::FilterEntry(Entry* entry) {
570 if (!ValidateEntry(entry))
571 return false;
573 if (entry->type == TabRestoreService::TAB)
574 return IsTabInteresting(static_cast<Tab*>(entry));
575 else if (entry->type == TabRestoreService::WINDOW)
576 return IsWindowInteresting(static_cast<Window*>(entry));
578 NOTREACHED();
579 return false;
582 void TabRestoreServiceHelper::UpdateTabBrowserIDs(SessionID::id_type old_id,
583 SessionID::id_type new_id) {
584 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
585 Entry* entry = *i;
586 if (entry->type == TabRestoreService::TAB) {
587 Tab* tab = static_cast<Tab*>(entry);
588 if (tab->browser_id == old_id)
589 tab->browser_id = new_id;
594 base::Time TabRestoreServiceHelper::TimeNow() const {
595 return time_factory_ ? time_factory_->TimeNow() : base::Time::Now();