Home | History | Annotate | Download | only in sessions
      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