Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / sessions / tab_restore_service_helper.cc
bloba66fbe667a9a8568e91d4429aa4f00cd8d940ec2
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/content/content_serialized_navigation_builder.h"
16 #include "components/sessions/core/tab_restore_service_client.h"
17 #include "components/sessions/session_types.h"
18 #include "content/public/browser/navigation_controller.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/session_storage_namespace.h"
21 #include "content/public/browser/web_contents.h"
23 #if defined(ENABLE_EXTENSIONS)
24 #include "chrome/browser/extensions/tab_helper.h"
25 #endif
27 using content::NavigationController;
28 using content::NavigationEntry;
29 using content::WebContents;
31 // TabRestoreServiceHelper::Observer -------------------------------------------
33 TabRestoreServiceHelper::Observer::~Observer() {}
35 void TabRestoreServiceHelper::Observer::OnClearEntries() {}
37 void TabRestoreServiceHelper::Observer::OnRestoreEntryById(
38 SessionID::id_type id,
39 Entries::const_iterator entry_iterator) {
42 void TabRestoreServiceHelper::Observer::OnAddEntry() {}
44 // TabRestoreServiceHelper -----------------------------------------------------
46 TabRestoreServiceHelper::TabRestoreServiceHelper(
47 TabRestoreService* tab_restore_service,
48 Observer* observer,
49 Profile* profile,
50 sessions::TabRestoreServiceClient* client,
51 TabRestoreService::TimeFactory* time_factory)
52 : tab_restore_service_(tab_restore_service),
53 observer_(observer),
54 profile_(profile),
55 client_(client),
56 restoring_(false),
57 time_factory_(time_factory) {
58 DCHECK(tab_restore_service_);
61 TabRestoreServiceHelper::~TabRestoreServiceHelper() {
62 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
63 TabRestoreServiceDestroyed(tab_restore_service_));
64 STLDeleteElements(&entries_);
67 void TabRestoreServiceHelper::AddObserver(
68 TabRestoreServiceObserver* observer) {
69 observer_list_.AddObserver(observer);
72 void TabRestoreServiceHelper::RemoveObserver(
73 TabRestoreServiceObserver* observer) {
74 observer_list_.RemoveObserver(observer);
77 void TabRestoreServiceHelper::CreateHistoricalTab(
78 content::WebContents* contents,
79 int index) {
80 if (restoring_)
81 return;
83 TabRestoreServiceDelegate* delegate =
84 TabRestoreServiceDelegate::FindDelegateForWebContents(contents);
85 if (closing_delegates_.find(delegate) != closing_delegates_.end())
86 return;
88 scoped_ptr<Tab> local_tab(new Tab());
89 PopulateTab(local_tab.get(), index, delegate, &contents->GetController());
90 if (local_tab->navigations.empty())
91 return;
93 AddEntry(local_tab.release(), true, true);
96 void TabRestoreServiceHelper::BrowserClosing(
97 TabRestoreServiceDelegate* delegate) {
98 closing_delegates_.insert(delegate);
100 scoped_ptr<Window> window(new Window());
101 window->selected_tab_index = delegate->GetSelectedIndex();
102 window->timestamp = TimeNow();
103 window->app_name = delegate->GetAppName();
105 // Don't use std::vector::resize() because it will push copies of an empty tab
106 // into the vector, which will give all tabs in a window the same ID.
107 for (int i = 0; i < delegate->GetTabCount(); ++i) {
108 window->tabs.push_back(Tab());
110 size_t entry_index = 0;
111 for (int tab_index = 0; tab_index < delegate->GetTabCount(); ++tab_index) {
112 PopulateTab(&(window->tabs[entry_index]),
113 tab_index,
114 delegate,
115 &delegate->GetWebContentsAt(tab_index)->GetController());
116 if (window->tabs[entry_index].navigations.empty()) {
117 window->tabs.erase(window->tabs.begin() + entry_index);
118 } else {
119 window->tabs[entry_index].browser_id = delegate->GetSessionID().id();
120 entry_index++;
123 if (window->tabs.size() == 1 && window->app_name.empty()) {
124 // Short-circuit creating a Window if only 1 tab was present. This fixes
125 // http://crbug.com/56744. Copy the Tab because it's owned by an object on
126 // the stack.
127 AddEntry(new Tab(window->tabs[0]), true, true);
128 } else if (!window->tabs.empty()) {
129 window->selected_tab_index =
130 std::min(static_cast<int>(window->tabs.size() - 1),
131 window->selected_tab_index);
132 AddEntry(window.release(), true, true);
136 void TabRestoreServiceHelper::BrowserClosed(
137 TabRestoreServiceDelegate* delegate) {
138 closing_delegates_.erase(delegate);
141 void TabRestoreServiceHelper::ClearEntries() {
142 if (observer_)
143 observer_->OnClearEntries();
144 STLDeleteElements(&entries_);
145 NotifyTabsChanged();
148 const TabRestoreService::Entries& TabRestoreServiceHelper::entries() const {
149 return entries_;
152 std::vector<content::WebContents*>
153 TabRestoreServiceHelper::RestoreMostRecentEntry(
154 TabRestoreServiceDelegate* delegate,
155 chrome::HostDesktopType host_desktop_type) {
156 if (entries_.empty())
157 return std::vector<WebContents*>();
159 return RestoreEntryById(delegate, entries_.front()->id, host_desktop_type,
160 UNKNOWN);
163 TabRestoreService::Tab* TabRestoreServiceHelper::RemoveTabEntryById(
164 SessionID::id_type id) {
165 Entries::iterator i = GetEntryIteratorById(id);
166 if (i == entries_.end())
167 return NULL;
169 Entry* entry = *i;
170 if (entry->type != TabRestoreService::TAB)
171 return NULL;
173 Tab* tab = static_cast<Tab*>(entry);
174 entries_.erase(i);
175 return tab;
178 std::vector<content::WebContents*> TabRestoreServiceHelper::RestoreEntryById(
179 TabRestoreServiceDelegate* delegate,
180 SessionID::id_type id,
181 chrome::HostDesktopType host_desktop_type,
182 WindowOpenDisposition disposition) {
183 Entries::iterator entry_iterator = GetEntryIteratorById(id);
184 if (entry_iterator == entries_.end())
185 // Don't hoark here, we allow an invalid id.
186 return std::vector<WebContents*>();
188 if (observer_)
189 observer_->OnRestoreEntryById(id, entry_iterator);
190 restoring_ = true;
191 Entry* entry = *entry_iterator;
193 // If the entry's ID does not match the ID that is being restored, then the
194 // entry is a window from which a single tab will be restored.
195 bool restoring_tab_in_window = entry->id != id;
197 if (!restoring_tab_in_window) {
198 entries_.erase(entry_iterator);
199 entry_iterator = entries_.end();
202 // |delegate| will be NULL in cases where one isn't already available (eg,
203 // when invoked on Mac OS X with no windows open). In this case, create a
204 // new browser into which we restore the tabs.
205 std::vector<WebContents*> web_contents;
206 if (entry->type == TabRestoreService::TAB) {
207 Tab* tab = static_cast<Tab*>(entry);
208 WebContents* restored_tab = NULL;
209 delegate = RestoreTab(*tab, delegate, host_desktop_type, disposition,
210 &restored_tab);
211 web_contents.push_back(restored_tab);
212 delegate->ShowBrowserWindow();
213 } else if (entry->type == TabRestoreService::WINDOW) {
214 TabRestoreServiceDelegate* current_delegate = delegate;
215 Window* window = static_cast<Window*>(entry);
217 // When restoring a window, either the entire window can be restored, or a
218 // single tab within it. If the entry's ID matches the one to restore, then
219 // the entire window will be restored.
220 if (!restoring_tab_in_window) {
221 delegate = TabRestoreServiceDelegate::Create(profile_, host_desktop_type,
222 window->app_name);
223 for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
224 const Tab& tab = window->tabs[tab_i];
225 WebContents* restored_tab = delegate->AddRestoredTab(
226 tab.navigations,
227 delegate->GetTabCount(),
228 tab.current_navigation_index,
229 tab.extension_app_id,
230 static_cast<int>(tab_i) == window->selected_tab_index,
231 tab.pinned,
232 tab.from_last_session,
233 tab.session_storage_namespace.get(),
234 tab.user_agent_override);
235 if (restored_tab) {
236 restored_tab->GetController().LoadIfNecessary();
237 client_->OnTabRestored(
238 tab.navigations.at(tab.current_navigation_index).virtual_url());
239 web_contents.push_back(restored_tab);
242 // All the window's tabs had the same former browser_id.
243 if (window->tabs[0].has_browser()) {
244 UpdateTabBrowserIDs(window->tabs[0].browser_id,
245 delegate->GetSessionID().id());
247 } else {
248 // Restore a single tab from the window. Find the tab that matches the ID
249 // in the window and restore it.
250 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
251 tab_i != window->tabs.end(); ++tab_i) {
252 const Tab& tab = *tab_i;
253 if (tab.id == id) {
254 WebContents* restored_tab = NULL;
255 delegate = RestoreTab(tab, delegate, host_desktop_type, disposition,
256 &restored_tab);
257 web_contents.push_back(restored_tab);
258 window->tabs.erase(tab_i);
259 // If restoring the tab leaves the window with nothing else, delete it
260 // as well.
261 if (!window->tabs.size()) {
262 entries_.erase(entry_iterator);
263 delete entry;
264 } else {
265 // Update the browser ID of the rest of the tabs in the window so if
266 // any one is restored, it goes into the same window as the tab
267 // being restored now.
268 UpdateTabBrowserIDs(tab.browser_id,
269 delegate->GetSessionID().id());
270 for (std::vector<Tab>::iterator tab_j = window->tabs.begin();
271 tab_j != window->tabs.end(); ++tab_j) {
272 (*tab_j).browser_id = delegate->GetSessionID().id();
275 break;
279 delegate->ShowBrowserWindow();
281 if (disposition == CURRENT_TAB && current_delegate &&
282 current_delegate->GetActiveWebContents()) {
283 current_delegate->CloseTab();
285 } else {
286 NOTREACHED();
289 if (!restoring_tab_in_window) {
290 delete entry;
293 restoring_ = false;
294 NotifyTabsChanged();
295 return web_contents;
298 void TabRestoreServiceHelper::NotifyTabsChanged() {
299 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
300 TabRestoreServiceChanged(tab_restore_service_));
303 void TabRestoreServiceHelper::NotifyLoaded() {
304 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
305 TabRestoreServiceLoaded(tab_restore_service_));
308 void TabRestoreServiceHelper::AddEntry(Entry* entry,
309 bool notify,
310 bool to_front) {
311 if (!FilterEntry(entry) || (entries_.size() >= kMaxEntries && !to_front)) {
312 delete entry;
313 return;
316 if (to_front)
317 entries_.push_front(entry);
318 else
319 entries_.push_back(entry);
321 PruneEntries();
323 if (notify)
324 NotifyTabsChanged();
326 if (observer_)
327 observer_->OnAddEntry();
330 void TabRestoreServiceHelper::PruneEntries() {
331 Entries new_entries;
333 for (TabRestoreService::Entries::const_iterator iter = entries_.begin();
334 iter != entries_.end(); ++iter) {
335 TabRestoreService::Entry* entry = *iter;
337 if (FilterEntry(entry) &&
338 new_entries.size() < kMaxEntries) {
339 new_entries.push_back(entry);
340 } else {
341 delete entry;
345 entries_ = new_entries;
348 TabRestoreService::Entries::iterator
349 TabRestoreServiceHelper::GetEntryIteratorById(SessionID::id_type id) {
350 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
351 if ((*i)->id == id)
352 return i;
354 // For Window entries, see if the ID matches a tab. If so, report the window
355 // as the Entry.
356 if ((*i)->type == TabRestoreService::WINDOW) {
357 std::vector<Tab>& tabs = static_cast<Window*>(*i)->tabs;
358 for (std::vector<Tab>::iterator j = tabs.begin();
359 j != tabs.end(); ++j) {
360 if ((*j).id == id) {
361 return i;
366 return entries_.end();
369 // static
370 bool TabRestoreServiceHelper::ValidateEntry(Entry* entry) {
371 if (entry->type == TabRestoreService::TAB)
372 return ValidateTab(static_cast<Tab*>(entry));
374 if (entry->type == TabRestoreService::WINDOW)
375 return ValidateWindow(static_cast<Window*>(entry));
377 NOTREACHED();
378 return false;
381 void TabRestoreServiceHelper::PopulateTab(
382 Tab* tab,
383 int index,
384 TabRestoreServiceDelegate* delegate,
385 NavigationController* controller) {
386 const int pending_index = controller->GetPendingEntryIndex();
387 int entry_count = controller->GetEntryCount();
388 if (entry_count == 0 && pending_index == 0)
389 entry_count++;
390 tab->navigations.resize(static_cast<int>(entry_count));
391 for (int i = 0; i < entry_count; ++i) {
392 NavigationEntry* entry = (i == pending_index) ?
393 controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
394 tab->navigations[i] =
395 sessions::ContentSerializedNavigationBuilder::FromNavigationEntry(
396 i, *entry);
398 tab->timestamp = TimeNow();
399 tab->current_navigation_index = controller->GetCurrentEntryIndex();
400 if (tab->current_navigation_index == -1 && entry_count > 0)
401 tab->current_navigation_index = 0;
402 tab->tabstrip_index = index;
404 #if defined(ENABLE_EXTENSIONS)
405 extensions::TabHelper* extensions_tab_helper =
406 extensions::TabHelper::FromWebContents(controller->GetWebContents());
407 // extensions_tab_helper is NULL in some browser tests.
408 if (extensions_tab_helper) {
409 const extensions::Extension* extension =
410 extensions_tab_helper->extension_app();
411 if (extension)
412 tab->extension_app_id = extension->id();
414 #endif
416 tab->user_agent_override =
417 controller->GetWebContents()->GetUserAgentOverride();
419 // TODO(ajwong): This does not correctly handle storage for isolated apps.
420 tab->session_storage_namespace =
421 controller->GetDefaultSessionStorageNamespace();
423 // Delegate may be NULL during unit tests.
424 if (delegate) {
425 tab->browser_id = delegate->GetSessionID().id();
426 tab->pinned = delegate->IsTabPinned(tab->tabstrip_index);
430 TabRestoreServiceDelegate* TabRestoreServiceHelper::RestoreTab(
431 const Tab& tab,
432 TabRestoreServiceDelegate* delegate,
433 chrome::HostDesktopType host_desktop_type,
434 WindowOpenDisposition disposition,
435 WebContents** contents) {
436 WebContents* web_contents;
437 if (disposition == CURRENT_TAB && delegate) {
438 web_contents = delegate->ReplaceRestoredTab(
439 tab.navigations,
440 tab.current_navigation_index,
441 tab.from_last_session,
442 tab.extension_app_id,
443 tab.session_storage_namespace.get(),
444 tab.user_agent_override);
445 } else {
446 // We only respsect the tab's original browser if there's no disposition.
447 if (disposition == UNKNOWN && tab.has_browser()) {
448 delegate = TabRestoreServiceDelegate::FindDelegateWithID(
449 tab.browser_id, host_desktop_type);
452 int tab_index = -1;
454 // |delegate| will be NULL in cases where one isn't already available (eg,
455 // when invoked on Mac OS X with no windows open). In this case, create a
456 // new browser into which we restore the tabs.
457 if (delegate && disposition != NEW_WINDOW) {
458 tab_index = tab.tabstrip_index;
459 } else {
460 delegate = TabRestoreServiceDelegate::Create(profile_, host_desktop_type,
461 std::string());
462 if (tab.has_browser())
463 UpdateTabBrowserIDs(tab.browser_id, delegate->GetSessionID().id());
466 // Place the tab at the end if the tab index is no longer valid or
467 // we were passed a specific disposition.
468 if (tab_index < 0 || tab_index > delegate->GetTabCount() ||
469 disposition != UNKNOWN) {
470 tab_index = delegate->GetTabCount();
473 web_contents = delegate->AddRestoredTab(tab.navigations,
474 tab_index,
475 tab.current_navigation_index,
476 tab.extension_app_id,
477 disposition != NEW_BACKGROUND_TAB,
478 tab.pinned,
479 tab.from_last_session,
480 tab.session_storage_namespace.get(),
481 tab.user_agent_override);
482 web_contents->GetController().LoadIfNecessary();
484 client_->OnTabRestored(
485 tab.navigations.at(tab.current_navigation_index).virtual_url());
486 if (contents)
487 *contents = web_contents;
489 return delegate;
493 bool TabRestoreServiceHelper::ValidateTab(Tab* tab) {
494 if (tab->navigations.empty())
495 return false;
497 tab->current_navigation_index =
498 std::max(0, std::min(tab->current_navigation_index,
499 static_cast<int>(tab->navigations.size()) - 1));
501 return true;
504 bool TabRestoreServiceHelper::ValidateWindow(Window* window) {
505 window->selected_tab_index =
506 std::max(0, std::min(window->selected_tab_index,
507 static_cast<int>(window->tabs.size() - 1)));
509 int i = 0;
510 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
511 tab_i != window->tabs.end();) {
512 if (!ValidateTab(&(*tab_i))) {
513 tab_i = window->tabs.erase(tab_i);
514 if (i < window->selected_tab_index)
515 window->selected_tab_index--;
516 else if (i == window->selected_tab_index)
517 window->selected_tab_index = 0;
518 } else {
519 ++tab_i;
520 ++i;
524 if (window->tabs.empty())
525 return false;
527 return true;
530 bool TabRestoreServiceHelper::IsTabInteresting(const Tab* tab) {
531 if (tab->navigations.empty())
532 return false;
534 if (tab->navigations.size() > 1)
535 return true;
537 return tab->pinned ||
538 tab->navigations.at(0).virtual_url() != client_->GetNewTabURL();
541 bool TabRestoreServiceHelper::IsWindowInteresting(const Window* window) {
542 if (window->tabs.empty())
543 return false;
545 if (window->tabs.size() > 1)
546 return true;
548 return IsTabInteresting(&window->tabs[0]);
551 bool TabRestoreServiceHelper::FilterEntry(Entry* entry) {
552 if (!ValidateEntry(entry))
553 return false;
555 if (entry->type == TabRestoreService::TAB)
556 return IsTabInteresting(static_cast<Tab*>(entry));
557 else if (entry->type == TabRestoreService::WINDOW)
558 return IsWindowInteresting(static_cast<Window*>(entry));
560 NOTREACHED();
561 return false;
564 void TabRestoreServiceHelper::UpdateTabBrowserIDs(SessionID::id_type old_id,
565 SessionID::id_type new_id) {
566 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
567 Entry* entry = *i;
568 if (entry->type == TabRestoreService::TAB) {
569 Tab* tab = static_cast<Tab*>(entry);
570 if (tab->browser_id == old_id)
571 tab->browser_id = new_id;
576 base::Time TabRestoreServiceHelper::TimeNow() const {
577 return time_factory_ ? time_factory_->TimeNow() : base::Time::Now();