Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: chrome/browser/sessions/in_memory_tab_restore_service.cc

Issue 10989027: Split TabRestoreService into InMemoryTRS and PersistentTRS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Split TRS into InMemoryTRS and PersistentTRS Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/sessions/in_memory_tab_restore_service.h"
6
7 #include <algorithm>
8 #include <iterator>
9 #include <map>
10
11 #include "base/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/tab_helper.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/sessions/session_command.h"
17 #include "chrome/browser/sessions/session_service.h"
18 #include "chrome/browser/sessions/session_service_factory.h"
19 #include "chrome/browser/sessions/session_types.h"
20 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
21 #include "chrome/browser/sessions/tab_restore_service_observer.h"
22 #include "chrome/browser/ui/tab_contents/tab_contents.h"
23 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
24 #include "chrome/common/extensions/extension.h"
25 #include "chrome/common/extensions/extension_constants.h"
26 #include "chrome/common/url_constants.h"
27 #include "content/public/browser/navigation_controller.h"
28 #include "content/public/browser/navigation_entry.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/session_storage_namespace.h"
31
32 using content::NavigationController;
33 using content::NavigationEntry;
34 using content::WebContents;
35
36 namespace {
37
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 DCHECK(profile->GetExtensionService());
42 if (!profile->GetExtensionService()->IsInstalledApp(url))
43 return;
44
45 AppLauncherHandler::RecordAppLaunchType(
46 extension_misc::APP_LAUNCH_NTP_RECENTLY_CLOSED);
47 #endif // !defined(OS_ANDROID)
48 }
49
50 } // namespace
51
52 InMemoryTabRestoreService::InMemoryTabRestoreService(
53 Profile* profile,
54 TabRestoreService::TimeFactory* time_factory)
55 : profile_(profile),
56 restoring_(false),
57 time_factory_(time_factory) {
58 }
59
60 InMemoryTabRestoreService::~InMemoryTabRestoreService() {
61 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
62 TabRestoreServiceDestroyed(this));
63 STLDeleteElements(&entries_);
64 time_factory_ = NULL;
65 }
66
67 void InMemoryTabRestoreService::AddObserver(
68 TabRestoreServiceObserver* observer) {
69 observer_list_.AddObserver(observer);
70 }
71
72 void InMemoryTabRestoreService::RemoveObserver(
73 TabRestoreServiceObserver* observer) {
74 observer_list_.RemoveObserver(observer);
75 }
76
77 void InMemoryTabRestoreService::CreateHistoricalTab(
78 content::WebContents* contents,
79 int index) {
80 if (restoring_)
81 return;
82
83 TabRestoreServiceDelegate* delegate =
84 TabRestoreServiceDelegate::FindDelegateForWebContents(contents);
85 if (closing_delegates_.find(delegate) != closing_delegates_.end())
86 return;
87
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;
92
93 AddEntry(local_tab.release(), true, true);
94 }
95
96 void InMemoryTabRestoreService::BrowserClosing(
97 TabRestoreServiceDelegate* delegate) {
98 closing_delegates_.insert(delegate);
99
100 scoped_ptr<Window> window(new Window());
101 window->selected_tab_index = delegate->GetSelectedIndex();
102 window->timestamp = TimeNow();
103 window->app_name = delegate->GetAppName();
104
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());
109 }
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++;
121 }
122 }
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);
133 }
134 }
135
136 void InMemoryTabRestoreService::BrowserClosed(
137 TabRestoreServiceDelegate* delegate) {
138 closing_delegates_.erase(delegate);
139 }
140
141 void InMemoryTabRestoreService::ClearEntries() {
142 OnClearEntries();
143 STLDeleteElements(&entries_);
144 NotifyTabsChanged();
145 }
146
147 const TabRestoreService::Entries& InMemoryTabRestoreService::entries() const {
148 return entries_;
149 }
150
151 void InMemoryTabRestoreService::RestoreMostRecentEntry(
152 TabRestoreServiceDelegate* delegate) {
153 if (entries_.empty())
154 return;
155
156 RestoreEntryById(delegate, entries_.front()->id, UNKNOWN);
157 }
158
159 TabRestoreService::Tab* InMemoryTabRestoreService::RemoveTabEntryById(
160 SessionID::id_type id) {
161 Entries::iterator i = GetEntryIteratorById(id);
162 if (i == entries_.end())
163 return NULL;
164
165 Entry* entry = *i;
166 if (entry->type != TAB)
167 return NULL;
168
169 Tab* tab = static_cast<Tab*>(entry);
170 entries_.erase(i);
171 return tab;
172 }
173
174 void InMemoryTabRestoreService::RestoreEntryById(
175 TabRestoreServiceDelegate* delegate,
176 SessionID::id_type id,
177 WindowOpenDisposition disposition) {
178 Entries::iterator entry_iterator = GetEntryIteratorById(id);
179 if (entry_iterator == entries_.end())
180 // Don't hoark here, we allow an invalid id.
181 return;
182
183 OnRestoreEntryById(id, entry_iterator);
184 restoring_ = true;
185 Entry* entry = *entry_iterator;
186
187 // If the entry's ID does not match the ID that is being restored, then the
188 // entry is a window from which a single tab will be restored.
189 bool restoring_tab_in_window = entry->id != id;
190
191 if (!restoring_tab_in_window) {
192 entries_.erase(entry_iterator);
193 entry_iterator = entries_.end();
194 }
195
196 // |delegate| will be NULL in cases where one isn't already available (eg,
197 // when invoked on Mac OS X with no windows open). In this case, create a
198 // new browser into which we restore the tabs.
199 if (entry->type == TAB) {
200 Tab* tab = static_cast<Tab*>(entry);
201 delegate = RestoreTab(*tab, delegate, disposition);
202 delegate->ShowBrowserWindow();
203 } else if (entry->type == WINDOW) {
204 TabRestoreServiceDelegate* current_delegate = delegate;
205 Window* window = static_cast<Window*>(entry);
206
207 // When restoring a window, either the entire window can be restored, or a
208 // single tab within it. If the entry's ID matches the one to restore, then
209 // the entire window will be restored.
210 if (!restoring_tab_in_window) {
211 delegate = TabRestoreServiceDelegate::Create(profile_, window->app_name);
212 for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
213 const Tab& tab = window->tabs[tab_i];
214 WebContents* restored_tab =
215 delegate->AddRestoredTab(tab.navigations, delegate->GetTabCount(),
216 tab.current_navigation_index,
217 tab.extension_app_id,
218 static_cast<int>(tab_i) ==
219 window->selected_tab_index,
220 tab.pinned, tab.from_last_session,
221 tab.session_storage_namespace,
222 tab.user_agent_override);
223 if (restored_tab) {
224 restored_tab->GetController().LoadIfNecessary();
225 RecordAppLaunch(profile_, tab);
226 }
227 }
228 // All the window's tabs had the same former browser_id.
229 if (window->tabs[0].has_browser()) {
230 UpdateTabBrowserIDs(window->tabs[0].browser_id,
231 delegate->GetSessionID().id());
232 }
233 } else {
234 // Restore a single tab from the window. Find the tab that matches the ID
235 // in the window and restore it.
236 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
237 tab_i != window->tabs.end(); ++tab_i) {
238 const Tab& tab = *tab_i;
239 if (tab.id == id) {
240 delegate = RestoreTab(tab, delegate, disposition);
241 window->tabs.erase(tab_i);
242 // If restoring the tab leaves the window with nothing else, delete it
243 // as well.
244 if (!window->tabs.size()) {
245 entries_.erase(entry_iterator);
246 delete entry;
247 } else {
248 // Update the browser ID of the rest of the tabs in the window so if
249 // any one is restored, it goes into the same window as the tab
250 // being restored now.
251 UpdateTabBrowserIDs(tab.browser_id,
252 delegate->GetSessionID().id());
253 for (std::vector<Tab>::iterator tab_j = window->tabs.begin();
254 tab_j != window->tabs.end(); ++tab_j) {
255 (*tab_j).browser_id = delegate->GetSessionID().id();
256 }
257 }
258 break;
259 }
260 }
261 }
262 delegate->ShowBrowserWindow();
263
264 if (disposition == CURRENT_TAB && current_delegate &&
265 current_delegate->GetActiveWebContents()) {
266 current_delegate->CloseTab();
267 }
268 } else {
269 NOTREACHED();
270 }
271
272 if (!restoring_tab_in_window) {
273 delete entry;
274 }
275
276 restoring_ = false;
277 NotifyTabsChanged();
278 }
279
280 void InMemoryTabRestoreService::LoadTabsFromLastSession() {
281 NOTIMPLEMENTED();
282 }
283
284 bool InMemoryTabRestoreService::IsLoaded() const {
285 NOTIMPLEMENTED();
286 return false;
287 }
288
289 void InMemoryTabRestoreService::Shutdown() {
290 NOTIMPLEMENTED();
291 }
292
293 void InMemoryTabRestoreService::PopulateTab(
294 Tab* tab,
295 int index,
296 TabRestoreServiceDelegate* delegate,
297 NavigationController* controller) {
298 const int pending_index = controller->GetPendingEntryIndex();
299 int entry_count = controller->GetEntryCount();
300 if (entry_count == 0 && pending_index == 0)
301 entry_count++;
302 tab->navigations.resize(static_cast<int>(entry_count));
303 for (int i = 0; i < entry_count; ++i) {
304 NavigationEntry* entry = (i == pending_index) ?
305 controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
306 tab->navigations[i] =
307 TabNavigation::FromNavigationEntry(i, *entry);
308 }
309 tab->timestamp = TimeNow();
310 tab->current_navigation_index = controller->GetCurrentEntryIndex();
311 if (tab->current_navigation_index == -1 && entry_count > 0)
312 tab->current_navigation_index = 0;
313 tab->tabstrip_index = index;
314
315 TabContents* tab_contents =
316 TabContents::FromWebContents(controller->GetWebContents());
317 // tab_contents is NULL in some browser tests.
318 if (tab_contents) {
319 const extensions::Extension* extension =
320 extensions::TabHelper::FromWebContents(controller->GetWebContents())->
321 extension_app();
322 if (extension)
323 tab->extension_app_id = extension->id();
324 }
325
326 tab->user_agent_override =
327 controller->GetWebContents()->GetUserAgentOverride();
328
329 // TODO(ajwong): This does not correctly handle storage for isolated apps.
330 tab->session_storage_namespace =
331 controller->GetDefaultSessionStorageNamespace();
332
333 // Delegate may be NULL during unit tests.
334 if (delegate) {
335 tab->browser_id = delegate->GetSessionID().id();
336 tab->pinned = delegate->IsTabPinned(tab->tabstrip_index);
337 }
338 }
339
340 void InMemoryTabRestoreService::AddEntry(Entry* entry,
341 bool notify,
342 bool to_front) {
343 if (!FilterEntry(entry) || (entries_.size() >= kMaxEntries && !to_front)) {
344 delete entry;
345 return;
346 }
347
348 if (to_front)
349 entries_.push_front(entry);
350 else
351 entries_.push_back(entry);
352
353 PruneEntries();
354
355 if (notify)
356 NotifyTabsChanged();
357
358 OnAddEntry();
359 }
360
361 void InMemoryTabRestoreService::PruneEntries() {
362 Entries new_entries;
363
364 for (TabRestoreService::Entries::const_iterator iter = entries_.begin();
365 iter != entries_.end(); ++iter) {
366 TabRestoreService::Entry* entry = *iter;
367
368 if (FilterEntry(entry) &&
369 new_entries.size() < kMaxEntries) {
370 new_entries.push_back(entry);
371 } else {
372 delete entry;
373 }
374 }
375
376 entries_ = new_entries;
377 }
378
379 void InMemoryTabRestoreService::NotifyTabsChanged() {
380 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
381 TabRestoreServiceChanged(this));
382 }
383
384 TabRestoreService::Entries::iterator
385 InMemoryTabRestoreService::GetEntryIteratorById(SessionID::id_type id) {
386 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
387 if ((*i)->id == id)
388 return i;
389
390 // For Window entries, see if the ID matches a tab. If so, report the window
391 // as the Entry.
392 if ((*i)->type == WINDOW) {
393 std::vector<Tab>& tabs = static_cast<Window*>(*i)->tabs;
394 for (std::vector<Tab>::iterator j = tabs.begin();
395 j != tabs.end(); ++j) {
396 if ((*j).id == id) {
397 return i;
398 }
399 }
400 }
401 }
402 return entries_.end();
403 }
404
405 bool InMemoryTabRestoreService::ConvertSessionWindowToWindow(
406 SessionWindow* session_window,
407 Window* window) {
408 for (size_t i = 0; i < session_window->tabs.size(); ++i) {
409 if (!session_window->tabs[i]->navigations.empty()) {
410 window->tabs.resize(window->tabs.size() + 1);
411 Tab& tab = window->tabs.back();
412 tab.pinned = session_window->tabs[i]->pinned;
413 tab.navigations.swap(session_window->tabs[i]->navigations);
414 tab.current_navigation_index =
415 session_window->tabs[i]->current_navigation_index;
416 tab.extension_app_id = session_window->tabs[i]->extension_app_id;
417 tab.timestamp = base::Time();
418 }
419 }
420 if (window->tabs.empty())
421 return false;
422
423 window->selected_tab_index =
424 std::min(session_window->selected_tab_index,
425 static_cast<int>(window->tabs.size() - 1));
426 window->timestamp = base::Time();
427 return true;
428 }
429
430 // static
431 bool InMemoryTabRestoreService::ValidateEntry(Entry* entry) {
432 if (entry->type == TAB)
433 return ValidateTab(static_cast<Tab*>(entry));
434
435 if (entry->type == WINDOW)
436 return ValidateWindow(static_cast<Window*>(entry));
437
438 NOTREACHED();
439 return false;
440 }
441
442 TabRestoreServiceDelegate* InMemoryTabRestoreService::RestoreTab(
443 const Tab& tab,
444 TabRestoreServiceDelegate* delegate,
445 WindowOpenDisposition disposition) {
446 if (disposition == CURRENT_TAB && delegate) {
447 delegate->ReplaceRestoredTab(tab.navigations,
448 tab.current_navigation_index,
449 tab.from_last_session,
450 tab.extension_app_id,
451 tab.session_storage_namespace,
452 tab.user_agent_override);
453 } else {
454 // We only respsect the tab's original browser if there's no disposition.
455 if (disposition == UNKNOWN && tab.has_browser())
456 delegate = TabRestoreServiceDelegate::FindDelegateWithID(tab.browser_id);
457
458 int tab_index = -1;
459
460 // |delegate| will be NULL in cases where one isn't already available (eg,
461 // when invoked on Mac OS X with no windows open). In this case, create a
462 // new browser into which we restore the tabs.
463 if (delegate && disposition != NEW_WINDOW) {
464 tab_index = tab.tabstrip_index;
465 } else {
466 delegate = TabRestoreServiceDelegate::Create(profile_, std::string());
467 if (tab.has_browser())
468 UpdateTabBrowserIDs(tab.browser_id, delegate->GetSessionID().id());
469 }
470
471 // Place the tab at the end if the tab index is no longer valid or
472 // we were passed a specific disposition.
473 if (tab_index < 0 || tab_index > delegate->GetTabCount() ||
474 disposition != UNKNOWN) {
475 tab_index = delegate->GetTabCount();
476 }
477
478 WebContents* web_contents = delegate->AddRestoredTab(
479 tab.navigations,
480 tab_index,
481 tab.current_navigation_index,
482 tab.extension_app_id,
483 disposition != NEW_BACKGROUND_TAB,
484 tab.pinned,
485 tab.from_last_session,
486 tab.session_storage_namespace,
487 tab.user_agent_override);
488 web_contents->GetController().LoadIfNecessary();
489 }
490 RecordAppLaunch(profile_, tab);
491 return delegate;
492 }
493
494
495 bool InMemoryTabRestoreService::ValidateTab(Tab* tab) {
496 if (tab->navigations.empty())
497 return false;
498
499 tab->current_navigation_index =
500 std::max(0, std::min(tab->current_navigation_index,
501 static_cast<int>(tab->navigations.size()) - 1));
502
503 return true;
504 }
505
506 bool InMemoryTabRestoreService::ValidateWindow(Window* window) {
507 window->selected_tab_index =
508 std::max(0, std::min(window->selected_tab_index,
509 static_cast<int>(window->tabs.size() - 1)));
510
511 int i = 0;
512 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
513 tab_i != window->tabs.end();) {
514 if (!ValidateTab(&(*tab_i))) {
515 tab_i = window->tabs.erase(tab_i);
516 if (i < window->selected_tab_index)
517 window->selected_tab_index--;
518 else if (i == window->selected_tab_index)
519 window->selected_tab_index = 0;
520 } else {
521 ++tab_i;
522 ++i;
523 }
524 }
525
526 if (window->tabs.empty())
527 return false;
528
529 return true;
530 }
531
532 bool InMemoryTabRestoreService::IsTabInteresting(const Tab* tab) {
533 if (tab->navigations.empty())
534 return false;
535
536 if (tab->navigations.size() > 1)
537 return true;
538
539 return tab->pinned ||
540 tab->navigations.at(0).virtual_url() !=
541 GURL(chrome::kChromeUINewTabURL);
542 }
543
544 bool InMemoryTabRestoreService::IsWindowInteresting(const Window* window) {
545 if (window->tabs.empty())
546 return false;
547
548 if (window->tabs.size() > 1)
549 return true;
550
551 return IsTabInteresting(&window->tabs[0]);
552 }
553
554 bool InMemoryTabRestoreService::FilterEntry(Entry* entry) {
555 if (!ValidateEntry(entry))
556 return false;
557
558 if (entry->type == TAB)
559 return IsTabInteresting(static_cast<Tab*>(entry));
560 else if (entry->type == WINDOW)
561 return IsWindowInteresting(static_cast<Window*>(entry));
562
563 NOTREACHED();
564 return false;
565 }
566
567 void InMemoryTabRestoreService::UpdateTabBrowserIDs(SessionID::id_type old_id,
568 SessionID::id_type new_id) {
569 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
570 Entry* entry = *i;
571 if (entry->type == TAB) {
572 Tab* tab = static_cast<Tab*>(entry);
573 if (tab->browser_id == old_id)
574 tab->browser_id = new_id;
575 }
576 }
577 }
578
579 void InMemoryTabRestoreService::DeleteLastSession() {
580 NOTIMPLEMENTED();
581 }
582
583 base::Time InMemoryTabRestoreService::TimeNow() const {
584 return time_factory_ ? time_factory_->TimeNow() : base::Time::Now();
585 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698