1 // Copyright 2013 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/extensions/api/sessions/sessions_api.h" 6 7 #include <vector> 8 9 #include "base/i18n/rtl.h" 10 #include "base/lazy_instance.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/time/time.h" 16 #include "chrome/browser/extensions/api/sessions/session_id.h" 17 #include "chrome/browser/extensions/api/tabs/windows_util.h" 18 #include "chrome/browser/extensions/extension_tab_util.h" 19 #include "chrome/browser/extensions/window_controller.h" 20 #include "chrome/browser/extensions/window_controller_list.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/search/search.h" 23 #include "chrome/browser/sessions/session_restore.h" 24 #include "chrome/browser/sessions/tab_restore_service_delegate.h" 25 #include "chrome/browser/sessions/tab_restore_service_factory.h" 26 #include "chrome/browser/sync/glue/synced_session.h" 27 #include "chrome/browser/sync/open_tabs_ui_delegate.h" 28 #include "chrome/browser/sync/profile_sync_service.h" 29 #include "chrome/browser/sync/profile_sync_service_factory.h" 30 #include "chrome/browser/ui/browser.h" 31 #include "chrome/browser/ui/browser_finder.h" 32 #include "chrome/browser/ui/host_desktop.h" 33 #include "chrome/browser/ui/tabs/tab_strip_model.h" 34 #include "chrome/common/pref_names.h" 35 #include "content/public/browser/web_contents.h" 36 #include "extensions/browser/extension_function_dispatcher.h" 37 #include "extensions/browser/extension_function_registry.h" 38 #include "extensions/browser/extension_system.h" 39 #include "extensions/common/error_utils.h" 40 #include "net/base/net_util.h" 41 #include "ui/base/layout.h" 42 43 namespace extensions { 44 45 namespace GetRecentlyClosed = api::sessions::GetRecentlyClosed; 46 namespace GetDevices = api::sessions::GetDevices; 47 namespace Restore = api::sessions::Restore; 48 namespace tabs = api::tabs; 49 namespace windows = api::windows; 50 51 const char kNoRecentlyClosedSessionsError[] = 52 "There are no recently closed sessions."; 53 const char kInvalidSessionIdError[] = "Invalid session id: \"*\"."; 54 const char kNoBrowserToRestoreSession[] = 55 "There are no browser windows to restore the session."; 56 const char kSessionSyncError[] = "Synced sessions are not available."; 57 const char kRestoreInIncognitoError[] = 58 "Can not restore sessions in incognito mode."; 59 60 // Comparator function for use with std::sort that will sort sessions by 61 // descending modified_time (i.e., most recent first). 62 bool SortSessionsByRecency(const browser_sync::SyncedSession* s1, 63 const browser_sync::SyncedSession* s2) { 64 return s1->modified_time > s2->modified_time; 65 } 66 67 // Comparator function for use with std::sort that will sort tabs in a window 68 // by descending timestamp (i.e., most recent first). 69 bool SortTabsByRecency(const SessionTab* t1, const SessionTab* t2) { 70 return t1->timestamp > t2->timestamp; 71 } 72 73 scoped_ptr<tabs::Tab> CreateTabModelHelper( 74 Profile* profile, 75 const sessions::SerializedNavigationEntry& current_navigation, 76 const std::string& session_id, 77 int index, 78 bool pinned, 79 int selected_index, 80 const Extension* extension) { 81 scoped_ptr<tabs::Tab> tab_struct(new tabs::Tab); 82 83 GURL gurl = current_navigation.virtual_url(); 84 std::string title = base::UTF16ToUTF8(current_navigation.title()); 85 86 tab_struct->session_id.reset(new std::string(session_id)); 87 tab_struct->url.reset(new std::string(gurl.spec())); 88 if (!title.empty()) { 89 tab_struct->title.reset(new std::string(title)); 90 } else { 91 const std::string languages = 92 profile->GetPrefs()->GetString(prefs::kAcceptLanguages); 93 tab_struct->title.reset(new std::string(base::UTF16ToUTF8( 94 net::FormatUrl(gurl, languages)))); 95 } 96 tab_struct->index = index; 97 tab_struct->pinned = pinned; 98 tab_struct->selected = index == selected_index; 99 tab_struct->active = false; 100 tab_struct->highlighted = false; 101 tab_struct->incognito = false; 102 ExtensionTabUtil::ScrubTabForExtension(extension, tab_struct.get()); 103 return tab_struct.Pass(); 104 } 105 106 scoped_ptr<windows::Window> CreateWindowModelHelper( 107 scoped_ptr<std::vector<linked_ptr<tabs::Tab> > > tabs, 108 const std::string& session_id, 109 const windows::Window::Type& type, 110 const windows::Window::State& state) { 111 scoped_ptr<windows::Window> window_struct(new windows::Window); 112 window_struct->tabs = tabs.Pass(); 113 window_struct->session_id.reset(new std::string(session_id)); 114 window_struct->incognito = false; 115 window_struct->always_on_top = false; 116 window_struct->focused = false; 117 window_struct->type = type; 118 window_struct->state = state; 119 return window_struct.Pass(); 120 } 121 122 scoped_ptr<api::sessions::Session> CreateSessionModelHelper( 123 int last_modified, 124 scoped_ptr<tabs::Tab> tab, 125 scoped_ptr<windows::Window> window) { 126 scoped_ptr<api::sessions::Session> session_struct(new api::sessions::Session); 127 session_struct->last_modified = last_modified; 128 if (tab) 129 session_struct->tab = tab.Pass(); 130 else if (window) 131 session_struct->window = window.Pass(); 132 else 133 NOTREACHED(); 134 return session_struct.Pass(); 135 } 136 137 bool is_tab_entry(const TabRestoreService::Entry* entry) { 138 return entry->type == TabRestoreService::TAB; 139 } 140 141 bool is_window_entry(const TabRestoreService::Entry* entry) { 142 return entry->type == TabRestoreService::WINDOW; 143 } 144 145 scoped_ptr<tabs::Tab> SessionsGetRecentlyClosedFunction::CreateTabModel( 146 const TabRestoreService::Tab& tab, int session_id, int selected_index) { 147 return CreateTabModelHelper(GetProfile(), 148 tab.navigations[tab.current_navigation_index], 149 base::IntToString(session_id), 150 tab.tabstrip_index, 151 tab.pinned, 152 selected_index, 153 GetExtension()); 154 } 155 156 scoped_ptr<windows::Window> 157 SessionsGetRecentlyClosedFunction::CreateWindowModel( 158 const TabRestoreService::Window& window, 159 int session_id) { 160 DCHECK(!window.tabs.empty()); 161 162 scoped_ptr<std::vector<linked_ptr<tabs::Tab> > > tabs( 163 new std::vector<linked_ptr<tabs::Tab> >); 164 for (size_t i = 0; i < window.tabs.size(); ++i) { 165 tabs->push_back(make_linked_ptr( 166 CreateTabModel(window.tabs[i], window.tabs[i].id, 167 window.selected_tab_index).release())); 168 } 169 170 return CreateWindowModelHelper(tabs.Pass(), 171 base::IntToString(session_id), 172 windows::Window::TYPE_NORMAL, 173 windows::Window::STATE_NORMAL); 174 } 175 176 scoped_ptr<api::sessions::Session> 177 SessionsGetRecentlyClosedFunction::CreateSessionModel( 178 const TabRestoreService::Entry* entry) { 179 scoped_ptr<tabs::Tab> tab; 180 scoped_ptr<windows::Window> window; 181 switch (entry->type) { 182 case TabRestoreService::TAB: 183 tab = CreateTabModel( 184 *static_cast<const TabRestoreService::Tab*>(entry), entry->id, -1); 185 break; 186 case TabRestoreService::WINDOW: 187 window = CreateWindowModel( 188 *static_cast<const TabRestoreService::Window*>(entry), entry->id); 189 break; 190 default: 191 NOTREACHED(); 192 } 193 return CreateSessionModelHelper(entry->timestamp.ToTimeT(), 194 tab.Pass(), 195 window.Pass()); 196 } 197 198 bool SessionsGetRecentlyClosedFunction::RunSync() { 199 scoped_ptr<GetRecentlyClosed::Params> params( 200 GetRecentlyClosed::Params::Create(*args_)); 201 EXTENSION_FUNCTION_VALIDATE(params); 202 int max_results = api::sessions::MAX_SESSION_RESULTS; 203 if (params->filter && params->filter->max_results) 204 max_results = *params->filter->max_results; 205 EXTENSION_FUNCTION_VALIDATE(max_results >= 0 && 206 max_results <= api::sessions::MAX_SESSION_RESULTS); 207 208 std::vector<linked_ptr<api::sessions::Session> > result; 209 TabRestoreService* tab_restore_service = 210 TabRestoreServiceFactory::GetForProfile(GetProfile()); 211 212 // TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in 213 // incognito mode) 214 if (!tab_restore_service) { 215 DCHECK_NE(GetProfile(), GetProfile()->GetOriginalProfile()) 216 << "TabRestoreService expected for normal profiles"; 217 results_ = GetRecentlyClosed::Results::Create( 218 std::vector<linked_ptr<api::sessions::Session> >()); 219 return true; 220 } 221 222 // List of entries. They are ordered from most to least recent. 223 // We prune the list to contain max 25 entries at any time and removes 224 // uninteresting entries. 225 TabRestoreService::Entries entries = tab_restore_service->entries(); 226 for (TabRestoreService::Entries::const_iterator it = entries.begin(); 227 it != entries.end() && static_cast<int>(result.size()) < max_results; 228 ++it) { 229 TabRestoreService::Entry* entry = *it; 230 result.push_back(make_linked_ptr(CreateSessionModel(entry).release())); 231 } 232 233 results_ = GetRecentlyClosed::Results::Create(result); 234 return true; 235 } 236 237 scoped_ptr<tabs::Tab> SessionsGetDevicesFunction::CreateTabModel( 238 const std::string& session_tag, 239 const SessionTab& tab, 240 int tab_index, 241 int selected_index) { 242 std::string session_id = SessionId(session_tag, tab.tab_id.id()).ToString(); 243 return CreateTabModelHelper(GetProfile(), 244 tab.navigations[ 245 tab.normalized_navigation_index()], 246 session_id, 247 tab_index, 248 tab.pinned, 249 selected_index, 250 GetExtension()); 251 } 252 253 scoped_ptr<windows::Window> SessionsGetDevicesFunction::CreateWindowModel( 254 const SessionWindow& window, const std::string& session_tag) { 255 DCHECK(!window.tabs.empty()); 256 257 // Prune tabs that are not syncable or are NewTabPage. Then, sort the tabs 258 // from most recent to least recent. 259 std::vector<const SessionTab*> tabs_in_window; 260 for (size_t i = 0; i < window.tabs.size(); ++i) { 261 const SessionTab* tab = window.tabs[i]; 262 if (tab->navigations.empty()) 263 continue; 264 const sessions::SerializedNavigationEntry& current_navigation = 265 tab->navigations.at(tab->normalized_navigation_index()); 266 if (chrome::IsNTPURL(current_navigation.virtual_url(), GetProfile())) { 267 continue; 268 } 269 tabs_in_window.push_back(tab); 270 } 271 if (tabs_in_window.empty()) 272 return scoped_ptr<windows::Window>(); 273 std::sort(tabs_in_window.begin(), tabs_in_window.end(), SortTabsByRecency); 274 275 scoped_ptr<std::vector<linked_ptr<tabs::Tab> > > tabs( 276 new std::vector<linked_ptr<tabs::Tab> >); 277 for (size_t i = 0; i < tabs_in_window.size(); ++i) { 278 tabs->push_back(make_linked_ptr( 279 CreateTabModel(session_tag, *tabs_in_window[i], i, 280 window.selected_tab_index).release())); 281 } 282 283 std::string session_id = 284 SessionId(session_tag, window.window_id.id()).ToString(); 285 286 windows::Window::Type type = windows::Window::TYPE_NONE; 287 switch (window.type) { 288 case Browser::TYPE_TABBED: 289 type = windows::Window::TYPE_NORMAL; 290 break; 291 case Browser::TYPE_POPUP: 292 type = windows::Window::TYPE_POPUP; 293 break; 294 } 295 296 windows::Window::State state = windows::Window::STATE_NONE; 297 switch (window.show_state) { 298 case ui::SHOW_STATE_NORMAL: 299 state = windows::Window::STATE_NORMAL; 300 break; 301 case ui::SHOW_STATE_MINIMIZED: 302 state = windows::Window::STATE_MINIMIZED; 303 break; 304 case ui::SHOW_STATE_MAXIMIZED: 305 state = windows::Window::STATE_MAXIMIZED; 306 break; 307 case ui::SHOW_STATE_FULLSCREEN: 308 state = windows::Window::STATE_FULLSCREEN; 309 break; 310 case ui::SHOW_STATE_DEFAULT: 311 case ui::SHOW_STATE_INACTIVE: 312 case ui::SHOW_STATE_DETACHED: 313 case ui::SHOW_STATE_END: 314 break; 315 } 316 317 scoped_ptr<windows::Window> window_struct( 318 CreateWindowModelHelper(tabs.Pass(), session_id, type, state)); 319 // TODO(dwankri): Dig deeper to resolve bounds not being optional, so closed 320 // windows in GetRecentlyClosed can have set values in Window helper. 321 window_struct->left.reset(new int(window.bounds.x())); 322 window_struct->top.reset(new int(window.bounds.y())); 323 window_struct->width.reset(new int(window.bounds.width())); 324 window_struct->height.reset(new int(window.bounds.height())); 325 326 return window_struct.Pass(); 327 } 328 329 scoped_ptr<api::sessions::Session> 330 SessionsGetDevicesFunction::CreateSessionModel( 331 const SessionWindow& window, const std::string& session_tag) { 332 scoped_ptr<windows::Window> window_model( 333 CreateWindowModel(window, session_tag)); 334 // There is a chance that after pruning uninteresting tabs the window will be 335 // empty. 336 return !window_model ? scoped_ptr<api::sessions::Session>() 337 : CreateSessionModelHelper(window.timestamp.ToTimeT(), 338 scoped_ptr<tabs::Tab>(), 339 window_model.Pass()); 340 } 341 342 scoped_ptr<api::sessions::Device> SessionsGetDevicesFunction::CreateDeviceModel( 343 const browser_sync::SyncedSession* session) { 344 int max_results = api::sessions::MAX_SESSION_RESULTS; 345 // Already validated in RunAsync(). 346 scoped_ptr<GetDevices::Params> params(GetDevices::Params::Create(*args_)); 347 if (params->filter && params->filter->max_results) 348 max_results = *params->filter->max_results; 349 350 scoped_ptr<api::sessions::Device> device_struct(new api::sessions::Device); 351 device_struct->info = session->session_name; 352 device_struct->device_name = session->session_name; 353 354 for (browser_sync::SyncedSession::SyncedWindowMap::const_iterator it = 355 session->windows.begin(); it != session->windows.end() && 356 static_cast<int>(device_struct->sessions.size()) < max_results; ++it) { 357 scoped_ptr<api::sessions::Session> session_model(CreateSessionModel( 358 *it->second, session->session_tag)); 359 if (session_model) 360 device_struct->sessions.push_back(make_linked_ptr( 361 session_model.release())); 362 } 363 return device_struct.Pass(); 364 } 365 366 bool SessionsGetDevicesFunction::RunSync() { 367 ProfileSyncService* service = 368 ProfileSyncServiceFactory::GetInstance()->GetForProfile(GetProfile()); 369 if (!(service && service->GetPreferredDataTypes().Has(syncer::SESSIONS))) { 370 // Sync not enabled. 371 results_ = GetDevices::Results::Create( 372 std::vector<linked_ptr<api::sessions::Device> >()); 373 return true; 374 } 375 376 browser_sync::OpenTabsUIDelegate* open_tabs = 377 service->GetOpenTabsUIDelegate(); 378 std::vector<const browser_sync::SyncedSession*> sessions; 379 if (!(open_tabs && open_tabs->GetAllForeignSessions(&sessions))) { 380 results_ = GetDevices::Results::Create( 381 std::vector<linked_ptr<api::sessions::Device> >()); 382 return true; 383 } 384 385 scoped_ptr<GetDevices::Params> params(GetDevices::Params::Create(*args_)); 386 EXTENSION_FUNCTION_VALIDATE(params); 387 if (params->filter && params->filter->max_results) { 388 EXTENSION_FUNCTION_VALIDATE(*params->filter->max_results >= 0 && 389 *params->filter->max_results <= api::sessions::MAX_SESSION_RESULTS); 390 } 391 392 std::vector<linked_ptr<api::sessions::Device> > result; 393 // Sort sessions from most recent to least recent. 394 std::sort(sessions.begin(), sessions.end(), SortSessionsByRecency); 395 for (size_t i = 0; i < sessions.size(); ++i) { 396 result.push_back(make_linked_ptr(CreateDeviceModel(sessions[i]).release())); 397 } 398 399 results_ = GetDevices::Results::Create(result); 400 return true; 401 } 402 403 void SessionsRestoreFunction::SetInvalidIdError(const std::string& invalid_id) { 404 SetError(ErrorUtils::FormatErrorMessage(kInvalidSessionIdError, invalid_id)); 405 } 406 407 408 void SessionsRestoreFunction::SetResultRestoredTab( 409 content::WebContents* contents) { 410 scoped_ptr<base::DictionaryValue> tab_value( 411 ExtensionTabUtil::CreateTabValue(contents, GetExtension())); 412 scoped_ptr<tabs::Tab> tab(tabs::Tab::FromValue(*tab_value)); 413 scoped_ptr<api::sessions::Session> restored_session(CreateSessionModelHelper( 414 base::Time::Now().ToTimeT(), 415 tab.Pass(), 416 scoped_ptr<windows::Window>())); 417 results_ = Restore::Results::Create(*restored_session); 418 } 419 420 bool SessionsRestoreFunction::SetResultRestoredWindow(int window_id) { 421 WindowController* controller = NULL; 422 if (!windows_util::GetWindowFromWindowID(this, window_id, &controller)) { 423 // error_ is set by GetWindowFromWindowId function call. 424 return false; 425 } 426 scoped_ptr<base::DictionaryValue> window_value( 427 controller->CreateWindowValueWithTabs(GetExtension())); 428 scoped_ptr<windows::Window> window(windows::Window::FromValue( 429 *window_value)); 430 results_ = Restore::Results::Create(*CreateSessionModelHelper( 431 base::Time::Now().ToTimeT(), 432 scoped_ptr<tabs::Tab>(), 433 window.Pass())); 434 return true; 435 } 436 437 bool SessionsRestoreFunction::RestoreMostRecentlyClosed(Browser* browser) { 438 TabRestoreService* tab_restore_service = 439 TabRestoreServiceFactory::GetForProfile(GetProfile()); 440 chrome::HostDesktopType host_desktop_type = browser->host_desktop_type(); 441 TabRestoreService::Entries entries = tab_restore_service->entries(); 442 443 if (entries.empty()) { 444 SetError(kNoRecentlyClosedSessionsError); 445 return false; 446 } 447 448 bool is_window = is_window_entry(entries.front()); 449 TabRestoreServiceDelegate* delegate = 450 TabRestoreServiceDelegate::FindDelegateForWebContents( 451 browser->tab_strip_model()->GetActiveWebContents()); 452 std::vector<content::WebContents*> contents = 453 tab_restore_service->RestoreMostRecentEntry(delegate, host_desktop_type); 454 DCHECK(contents.size()); 455 456 if (is_window) { 457 return SetResultRestoredWindow( 458 ExtensionTabUtil::GetWindowIdOfTab(contents[0])); 459 } 460 461 SetResultRestoredTab(contents[0]); 462 return true; 463 } 464 465 bool SessionsRestoreFunction::RestoreLocalSession(const SessionId& session_id, 466 Browser* browser) { 467 TabRestoreService* tab_restore_service = 468 TabRestoreServiceFactory::GetForProfile(GetProfile()); 469 chrome::HostDesktopType host_desktop_type = browser->host_desktop_type(); 470 TabRestoreService::Entries entries = tab_restore_service->entries(); 471 472 if (entries.empty()) { 473 SetInvalidIdError(session_id.ToString()); 474 return false; 475 } 476 477 // Check if the recently closed list contains an entry with the provided id. 478 bool is_window = false; 479 for (TabRestoreService::Entries::iterator it = entries.begin(); 480 it != entries.end(); ++it) { 481 if ((*it)->id == session_id.id()) { 482 // The only time a full window is being restored is if the entry ID 483 // matches the provided ID and the entry type is Window. 484 is_window = is_window_entry(*it); 485 break; 486 } 487 } 488 489 TabRestoreServiceDelegate* delegate = 490 TabRestoreServiceDelegate::FindDelegateForWebContents( 491 browser->tab_strip_model()->GetActiveWebContents()); 492 std::vector<content::WebContents*> contents = 493 tab_restore_service->RestoreEntryById(delegate, 494 session_id.id(), 495 host_desktop_type, 496 UNKNOWN); 497 // If the ID is invalid, contents will be empty. 498 if (!contents.size()) { 499 SetInvalidIdError(session_id.ToString()); 500 return false; 501 } 502 503 // Retrieve the window through any of the tabs in contents. 504 if (is_window) { 505 return SetResultRestoredWindow( 506 ExtensionTabUtil::GetWindowIdOfTab(contents[0])); 507 } 508 509 SetResultRestoredTab(contents[0]); 510 return true; 511 } 512 513 bool SessionsRestoreFunction::RestoreForeignSession(const SessionId& session_id, 514 Browser* browser) { 515 ProfileSyncService* service = 516 ProfileSyncServiceFactory::GetInstance()->GetForProfile(GetProfile()); 517 if (!(service && service->GetPreferredDataTypes().Has(syncer::SESSIONS))) { 518 SetError(kSessionSyncError); 519 return false; 520 } 521 browser_sync::OpenTabsUIDelegate* open_tabs = 522 service->GetOpenTabsUIDelegate(); 523 if (!open_tabs) { 524 SetError(kSessionSyncError); 525 return false; 526 } 527 528 const SessionTab* tab = NULL; 529 if (open_tabs->GetForeignTab(session_id.session_tag(), 530 session_id.id(), 531 &tab)) { 532 TabStripModel* tab_strip = browser->tab_strip_model(); 533 content::WebContents* contents = tab_strip->GetActiveWebContents(); 534 535 content::WebContents* tab_contents = 536 SessionRestore::RestoreForeignSessionTab(contents, *tab, 537 NEW_FOREGROUND_TAB); 538 SetResultRestoredTab(tab_contents); 539 return true; 540 } 541 542 // Restoring a full window. 543 std::vector<const SessionWindow*> windows; 544 if (!open_tabs->GetForeignSession(session_id.session_tag(), &windows)) { 545 SetInvalidIdError(session_id.ToString()); 546 return false; 547 } 548 549 std::vector<const SessionWindow*>::const_iterator window = windows.begin(); 550 while (window != windows.end() 551 && (*window)->window_id.id() != session_id.id()) { 552 ++window; 553 } 554 if (window == windows.end()) { 555 SetInvalidIdError(session_id.ToString()); 556 return false; 557 } 558 559 chrome::HostDesktopType host_desktop_type = browser->host_desktop_type(); 560 // Only restore one window at a time. 561 std::vector<Browser*> browsers = SessionRestore::RestoreForeignSessionWindows( 562 GetProfile(), host_desktop_type, window, window + 1); 563 // Will always create one browser because we only restore one window per call. 564 DCHECK_EQ(1u, browsers.size()); 565 return SetResultRestoredWindow(ExtensionTabUtil::GetWindowId(browsers[0])); 566 } 567 568 bool SessionsRestoreFunction::RunSync() { 569 scoped_ptr<Restore::Params> params(Restore::Params::Create(*args_)); 570 EXTENSION_FUNCTION_VALIDATE(params); 571 572 Browser* browser = chrome::FindBrowserWithProfile( 573 GetProfile(), chrome::HOST_DESKTOP_TYPE_NATIVE); 574 if (!browser) { 575 SetError(kNoBrowserToRestoreSession); 576 return false; 577 } 578 579 if (GetProfile() != GetProfile()->GetOriginalProfile()) { 580 SetError(kRestoreInIncognitoError); 581 return false; 582 } 583 584 if (!params->session_id) 585 return RestoreMostRecentlyClosed(browser); 586 587 scoped_ptr<SessionId> session_id(SessionId::Parse(*params->session_id)); 588 if (!session_id) { 589 SetInvalidIdError(*params->session_id); 590 return false; 591 } 592 593 return session_id->IsForeign() ? 594 RestoreForeignSession(*session_id, browser) 595 : RestoreLocalSession(*session_id, browser); 596 } 597 598 SessionsEventRouter::SessionsEventRouter(Profile* profile) 599 : profile_(profile), 600 tab_restore_service_(TabRestoreServiceFactory::GetForProfile(profile)) { 601 // TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in 602 // incognito mode) 603 if (tab_restore_service_) { 604 tab_restore_service_->LoadTabsFromLastSession(); 605 tab_restore_service_->AddObserver(this); 606 } 607 } 608 609 SessionsEventRouter::~SessionsEventRouter() { 610 if (tab_restore_service_) 611 tab_restore_service_->RemoveObserver(this); 612 } 613 614 void SessionsEventRouter::TabRestoreServiceChanged( 615 TabRestoreService* service) { 616 scoped_ptr<base::ListValue> args(new base::ListValue()); 617 EventRouter::Get(profile_)->BroadcastEvent(make_scoped_ptr( 618 new Event(api::sessions::OnChanged::kEventName, args.Pass()))); 619 } 620 621 void SessionsEventRouter::TabRestoreServiceDestroyed( 622 TabRestoreService* service) { 623 tab_restore_service_ = NULL; 624 } 625 626 SessionsAPI::SessionsAPI(content::BrowserContext* context) 627 : browser_context_(context) { 628 EventRouter::Get(browser_context_)->RegisterObserver(this, 629 api::sessions::OnChanged::kEventName); 630 } 631 632 SessionsAPI::~SessionsAPI() { 633 } 634 635 void SessionsAPI::Shutdown() { 636 EventRouter::Get(browser_context_)->UnregisterObserver(this); 637 } 638 639 static base::LazyInstance<BrowserContextKeyedAPIFactory<SessionsAPI> > 640 g_factory = LAZY_INSTANCE_INITIALIZER; 641 642 BrowserContextKeyedAPIFactory<SessionsAPI>* 643 SessionsAPI::GetFactoryInstance() { 644 return g_factory.Pointer(); 645 } 646 647 void SessionsAPI::OnListenerAdded(const EventListenerInfo& details) { 648 sessions_event_router_.reset( 649 new SessionsEventRouter(Profile::FromBrowserContext(browser_context_))); 650 EventRouter::Get(browser_context_)->UnregisterObserver(this); 651 } 652 653 } // namespace extensions 654