<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sessions/tab_restore_service.h"

#include <algorithm>
#include <iterator>
#include <map>

#include "base/callback.h"
#include "base/scoped_vector.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_command.h"
#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"

using base::Time;

// TimeFactory-----------------------------------------------------------------

TabRestoreService::TimeFactory::~TimeFactory() {}

// Entry ----------------------------------------------------------------------

// ID of the next Entry.
static SessionID::id_type next_entry_id = 1;

TabRestoreService::Entry::Entry()
    : id(next_entry_id++),
      type(TAB),
      from_last_session(false) {}

TabRestoreService::Entry::Entry(Type type)
    : id(next_entry_id++),
      type(type),
      from_last_session(false) {}

TabRestoreService::Entry::~Entry() {}

// TabRestoreService ----------------------------------------------------------

// static
const size_t TabRestoreService::kMaxEntries = 10;

// Identifier for commands written to file.
// The ordering in the file is as follows:
// . When the user closes a tab a command of type
//   kCommandSelectedNavigationInTab is written identifying the tab and
//   the selected index, then a kCommandPinnedState command if the tab was
//   pinned and kCommandSetExtensionAppID if the tab has an app id. This is
//   followed by any number of kCommandUpdateTabNavigation commands (1 per
//   navigation entry).
// . When the user closes a window a kCommandSelectedNavigationInTab command
//   is written out and followed by n tab closed sequences (as previoulsy
//   described).
// . When the user restores an entry a command of type kCommandRestoredEntry
//   is written.
static const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
static const SessionCommand::id_type kCommandRestoredEntry = 2;
static const SessionCommand::id_type kCommandWindow = 3;
static const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
static const SessionCommand::id_type kCommandPinnedState = 5;
static const SessionCommand::id_type kCommandSetExtensionAppID = 6;

// Number of entries (not commands) before we clobber the file and write
// everything.
static const int kEntriesPerReset = 40;

namespace {

// Payload structures.

typedef int32 RestoredEntryPayload;

// Payload used for the start of a window close. This is the old struct that is
// used for backwards compat when it comes to reading the session files. This
// struct must be POD, because we memset the contents.
struct WindowPayload {
  SessionID::id_type window_id;
  int32 selected_tab_index;
  int32 num_tabs;
};

// Payload used for the start of a tab close. This is the old struct that is
// used for backwards compat when it comes to reading the session files.
struct SelectedNavigationInTabPayload {
  SessionID::id_type id;
  int32 index;
};

// Payload used for the start of a window close.  This struct must be POD,
// because we memset the contents.
struct WindowPayload2 : WindowPayload {
  int64 timestamp;
};

// Payload used for the start of a tab close.
struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
  int64 timestamp;
};

// Only written if the tab is pinned.
typedef bool PinnedStatePayload;

typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;

// If |id_to_entry| contains an entry for |id| the corresponding entry is
// deleted and removed from both |id_to_entry| and |entries|. This is used
// when creating entries from the backend file.
void RemoveEntryByID(SessionID::id_type id,
                     IDToEntry* id_to_entry,
                     std::vector<TabRestoreService::Entry*>* entries) {
  // Look for the entry in the map. If it is present, erase it from both
  // collections and return.
  IDToEntry::iterator i = id_to_entry->find(id);
  if (i != id_to_entry->end()) {
    entries->erase(std::find(entries->begin(), entries->end(), i->second));
    delete i->second;
    id_to_entry->erase(i);
    return;
  }

  // Otherwise, loop over all items in the map and see if any of the Windows
  // have Tabs with the |id|.
  for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
       ++i) {
    if (i->second->type == TabRestoreService::WINDOW) {
      TabRestoreService::Window* window =
          static_cast<TabRestoreService::Window*>(i->second);
      std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
      for ( ; j != window->tabs.end(); ++j) {
        // If the ID matches one of this window's tabs, remove it from the list.
        if ((*j).id == id) {
          window->tabs.erase(j);
          return;
        }
      }
    }
  }
}

}  // namespace

TabRestoreService::Tab::Tab()
    : Entry(TAB),
      current_navigation_index(-1),
      browser_id(0),
      tabstrip_index(-1),
      pinned(false) {
}

TabRestoreService::Tab::~Tab() {
}

TabRestoreService::Window::Window() : Entry(WINDOW), selected_tab_index(-1) {
}

TabRestoreService::Window::~Window() {
}

TabRestoreService::TabRestoreService(Profile* profile,
    TabRestoreService::TimeFactory* time_factory)
    : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
                         FilePath()),
      load_state_(NOT_LOADED),
      restoring_(false),
      reached_max_(false),
      entries_to_write_(0),
      entries_written_(0),
      time_factory_(time_factory) {
}

TabRestoreService::~TabRestoreService() {
  if (backend())
    Save();

  FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this));
  STLDeleteElements(&entries_);
  STLDeleteElements(&staging_entries_);
  time_factory_ = NULL;
}

void TabRestoreService::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void TabRestoreService::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void TabRestoreService::CreateHistoricalTab(NavigationController* tab) {
  if (restoring_)
    return;

  Browser* browser = Browser::GetBrowserForController(tab, NULL);
  if (closing_browsers_.find(browser) != closing_browsers_.end())
    return;

  scoped_ptr<Tab> local_tab(new Tab());
  PopulateTab(local_tab.get(), browser, tab);
  if (local_tab->navigations.empty())
    return;

  AddEntry(local_tab.release(), true, true);
}

void TabRestoreService::BrowserClosing(Browser* browser) {
  if (browser->type() != Browser::TYPE_NORMAL ||
      browser->tab_count() == 0)
    return;

  closing_browsers_.insert(browser);

  Window* window = new Window();
  window->selected_tab_index = browser->selected_index();
  window->timestamp = TimeNow();
  // Don't use std::vector::resize() because it will push copies of an empty tab
  // into the vector, which will give all tabs in a window the same ID.
  for (int i = 0; i < browser->tab_count(); ++i) {
    window->tabs.push_back(Tab());
  }
  size_t entry_index = 0;
  for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
    PopulateTab(&(window->tabs[entry_index]),
                browser,
                &browser->GetTabContentsAt(tab_index)->controller());
    if (window->tabs[entry_index].navigations.empty()) {
      window->tabs.erase(window->tabs.begin() + entry_index);
    } else {
      window->tabs[entry_index].browser_id = browser->session_id().id();
      entry_index++;
    }
  }
  if (window->tabs.empty()) {
    delete window;
    window = NULL;
  } else {
    window->selected_tab_index =
        std::min(static_cast<int>(window->tabs.size() - 1),
                 window->selected_tab_index);
    AddEntry(window, true, true);
  }
}

void TabRestoreService::BrowserClosed(Browser* browser) {
  closing_browsers_.erase(browser);
}

void TabRestoreService::ClearEntries() {
  // Mark all the tabs as closed so that we don't attempt to restore them.
  for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i)
    ScheduleCommand(CreateRestoredEntryCommand((*i)->id));

  entries_to_write_ = 0;

  // Schedule a pending reset so that we nuke the file on next write.
  set_pending_reset(true);

  // Schedule a command, otherwise if there are no pending commands Save does
  // nothing.
  ScheduleCommand(CreateRestoredEntryCommand(1));

  STLDeleteElements(&entries_);
  NotifyTabsChanged();
}

const TabRestoreService::Entries& TabRestoreService::entries() const {
  return entries_;
}

void TabRestoreService::RestoreMostRecentEntry(Browser* browser) {
  if (entries_.empty())
    return;

  RestoreEntryById(browser, entries_.front()->id, false);
}

void TabRestoreService::RestoreEntryById(Browser* browser,
                                         SessionID::id_type id,
                                         bool replace_existing_tab) {
  Entries::iterator i = GetEntryIteratorById(id);
  if (i == entries_.end()) {
    // Don't hoark here, we allow an invalid id.
    return;
  }

  size_t index = 0;
  for (Entries::iterator j = entries_.begin(); j != i && j != entries_.end();
       ++j, ++index) {}
  if (static_cast<int>(index) < entries_to_write_)
    entries_to_write_--;

  ScheduleCommand(CreateRestoredEntryCommand(id));

  restoring_ = true;
  Entry* entry = *i;

  // If the entry's ID does not match the ID that is being restored, then the
  // entry is a window from which a single tab will be restored.
  bool restoring_tab_in_window = entry->id != id;

  if (!restoring_tab_in_window) {
    entries_.erase(i);
    i = entries_.end();
  }

  // |browser| will be NULL in cases where one isn't already available (eg,
  // when invoked on Mac OS X with no windows open). In this case, create a
  // new browser into which we restore the tabs.
  if (entry->type == TAB) {
    Tab* tab = static_cast<Tab*>(entry);
    browser = RestoreTab(*tab, browser, replace_existing_tab);
  } else if (entry->type == WINDOW) {
    Browser* current_browser = browser;
    Window* window = static_cast<Window*>(entry);

    // When restoring a window, either the entire window can be restored, or a
    // single tab within it. If the entry's ID matches the one to restore, then
    // the entire window will be restored.
    if (!restoring_tab_in_window) {
      browser = Browser::Create(profile());
      for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
        const Tab& tab = window->tabs[tab_i];<commit_msg>Fixes regression the resulted in not showing window after doing a tab restore in some situations.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sessions/tab_restore_service.h"

#include <algorithm>
#include <iterator>
#include <map>

#include "base/callback.h"
#include "base/scoped_vector.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_command.h"
#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"

using base::Time;

// TimeFactory-----------------------------------------------------------------

TabRestoreService::TimeFactory::~TimeFactory() {}

// Entry ----------------------------------------------------------------------

// ID of the next Entry.
static SessionID::id_type next_entry_id = 1;

TabRestoreService::Entry::Entry()
    : id(next_entry_id++),
      type(TAB),
      from_last_session(false) {}

TabRestoreService::Entry::Entry(Type type)
    : id(next_entry_id++),
      type(type),
      from_last_session(false) {}

TabRestoreService::Entry::~Entry() {}

// TabRestoreService ----------------------------------------------------------

// static
const size_t TabRestoreService::kMaxEntries = 10;

// Identifier for commands written to file.
// The ordering in the file is as follows:
// . When the user closes a tab a command of type
//   kCommandSelectedNavigationInTab is written identifying the tab and
//   the selected index, then a kCommandPinnedState command if the tab was
//   pinned and kCommandSetExtensionAppID if the tab has an app id. This is
//   followed by any number of kCommandUpdateTabNavigation commands (1 per
//   navigation entry).
// . When the user closes a window a kCommandSelectedNavigationInTab command
//   is written out and followed by n tab closed sequences (as previoulsy
//   described).
// . When the user restores an entry a command of type kCommandRestoredEntry
//   is written.
static const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
static const SessionCommand::id_type kCommandRestoredEntry = 2;
static const SessionCommand::id_type kCommandWindow = 3;
static const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
static const SessionCommand::id_type kCommandPinnedState = 5;
static const SessionCommand::id_type kCommandSetExtensionAppID = 6;

// Number of entries (not commands) before we clobber the file and write
// everything.
static const int kEntriesPerReset = 40;

namespace {

// Payload structures.

typedef int32 RestoredEntryPayload;

// Payload used for the start of a window close. This is the old struct that is
// used for backwards compat when it comes to reading the session files. This
// struct must be POD, because we memset the contents.
struct WindowPayload {
  SessionID::id_type window_id;
  int32 selected_tab_index;
  int32 num_tabs;
};

// Payload used for the start of a tab close. This is the old struct that is
// used for backwards compat when it comes to reading the session files.
struct SelectedNavigationInTabPayload {
  SessionID::id_type id;
  int32 index;
};

// Payload used for the start of a window close.  This struct must be POD,
// because we memset the contents.
struct WindowPayload2 : WindowPayload {
  int64 timestamp;
};

// Payload used for the start of a tab close.
struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
  int64 timestamp;
};

// Only written if the tab is pinned.
typedef bool PinnedStatePayload;

typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;

// If |id_to_entry| contains an entry for |id| the corresponding entry is
// deleted and removed from both |id_to_entry| and |entries|. This is used
// when creating entries from the backend file.
void RemoveEntryByID(SessionID::id_type id,
                     IDToEntry* id_to_entry,
                     std::vector<TabRestoreService::Entry*>* entries) {
  // Look for the entry in the map. If it is present, erase it from both
  // collections and return.
  IDToEntry::iterator i = id_to_entry->find(id);
  if (i != id_to_entry->end()) {
    entries->erase(std::find(entries->begin(), entries->end(), i->second));
    delete i->second;
    id_to_entry->erase(i);
    return;
  }

  // Otherwise, loop over all items in the map and see if any of the Windows
  // have Tabs with the |id|.
  for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
       ++i) {
    if (i->second->type == TabRestoreService::WINDOW) {
      TabRestoreService::Window* window =
          static_cast<TabRestoreService::Window*>(i->second);
      std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
      for ( ; j != window->tabs.end(); ++j) {
        // If the ID matches one of this window's tabs, remove it from the list.
        if ((*j).id == id) {
          window->tabs.erase(j);
          return;
        }
      }
    }
  }
}

}  // namespace

TabRestoreService::Tab::Tab()
    : Entry(TAB),
      current_navigation_index(-1),
      browser_id(0),
      tabstrip_index(-1),
      pinned(false) {
}

TabRestoreService::Tab::~Tab() {
}

TabRestoreService::Window::Window() : Entry(WINDOW), selected_tab_index(-1) {
}

TabRestoreService::Window::~Window() {
}

TabRestoreService::TabRestoreService(Profile* profile,
    TabRestoreService::TimeFactory* time_factory)
    : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
                         FilePath()),
      load_state_(NOT_LOADED),
      restoring_(false),
      reached_max_(false),
      entries_to_write_(0),
      entries_written_(0),
      time_factory_(time_factory) {
}

TabRestoreService::~TabRestoreService() {
  if (backend())
    Save();

  FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this));
  STLDeleteElements(&entries_);
  STLDeleteElements(&staging_entries_);
  time_factory_ = NULL;
}

void TabRestoreService::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void TabRestoreService::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void TabRestoreService::CreateHistoricalTab(NavigationController* tab) {
  if (restoring_)
    return;

  Browser* browser = Browser::GetBrowserForController(tab, NULL);
  if (closing_browsers_.find(browser) != closing_browsers_.end())
    return;

  scoped_ptr<Tab> local_tab(new Tab());
  PopulateTab(local_tab.get(), browser, tab);
  if (local_tab->navigations.empty())
    return;

  AddEntry(local_tab.release(), true, true);
}

void TabRestoreService::BrowserClosing(Browser* browser) {
  if (browser->type() != Browser::TYPE_NORMAL ||
      browser->tab_count() == 0)
    return;

  closing_browsers_.insert(browser);

  Window* window = new Window();
  window->selected_tab_index = browser->selected_index();
  window->timestamp = TimeNow();
  // Don't use std::vector::resize() because it will push copies of an empty tab
  // into the vector, which will give all tabs in a window the same ID.
  for (int i = 0; i < browser->tab_count(); ++i) {
    window->tabs.push_back(Tab());
  }
  size_t entry_index = 0;
  for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
    PopulateTab(&(window->tabs[entry_index]),
                browser,
                &browser->GetTabContentsAt(tab_index)->controller());
    if (window->tabs[entry_index].navigations.empty()) {
      window->tabs.erase(window->tabs.begin() + entry_index);
    } else {
      window->tabs[entry_index].browser_id = browser->session_id().id();
      entry_index++;
    }
  }
  if (window->tabs.empty()) {
    delete window;
    window = NULL;
  } else {
    window->selected_tab_index =
        std::min(static_cast<int>(window->tabs.size() - 1),
                 window->selected_tab_index);
    AddEntry(window, true, true);
  }
}

void TabRestoreService::BrowserClosed(Browser* browser) {
  closing_browsers_.erase(browser);
}

void TabRestoreService::ClearEntries() {
  // Mark all the tabs as closed so that we don't attempt to restore them.
  for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i)
    ScheduleCommand(CreateRestoredEntryCommand((*i)->id));

  entries_to_write_ = 0;

  // Schedule a pending reset so that we nuke the file on next write.
  set_pending_reset(true);

  // Schedule a command, otherwise if there are no pending commands Save does
  // nothing.
  ScheduleCommand(CreateRestoredEntryCommand(1));

  STLDeleteElements(&entries_);
  NotifyTabsChanged();
}

const TabRestoreService::Entries& TabRestoreService::entries() const {
  return entries_;
}

void TabRestoreService::RestoreMostRecentEntry(Browser* browser) {
  if (entries_.empty())
    return;

  RestoreEntryById(browser, entries_.front()->id, false);
}

void TabRestoreService::RestoreEntryById(Browser* browser,
                                         SessionID::id_type id,
                                         bool replace_existing_tab) {
  Entries::iterator i = GetEntryIteratorById(id);
  if (i == entries_.end()) {
    // Don't hoark here, we allow an invalid id.
    return;
  }

  size_t index = 0;
  for (Entries::iterator j = entries_.begin(); j != i && j != entries_.end();
       ++j, ++index) {}
  if (static_cast<int>(index) < entries_to_write_)
    entries_to_write_--;

  ScheduleCommand(CreateRestoredEntryCommand(id));

  restoring_ = true;
  Entry* entry = *i;

  // If the entry's ID does not match the ID that is being restored, then the
  // entry is a window from which a single tab will be restored.
  bool restoring_tab_in_window = entry->id != id;

  if (!restoring_tab_in_window) {
    entries_.erase(i);
    i = entries_.end();
  }

  // |browser| will be NULL in cases where one isn't already available (eg,
  // when invoked on Mac OS X with no windows open). In this case, create a
  // new browser into which we restore the tabs.
  if (entry->type == TAB) {
    Tab* tab = static_cast<Tab*>(entry);
    browser = RestoreTab(*tab, browser, replace_existing_tab);
    browser->window()->Show();
  } else if (entry->type == WINDOW) {
    Browser* current_browser = browser;
    Window* window = static_cast<Window*>(entry);

    // When restoring a window, either the entire window can be restored, or a
    // single tab within it. If the entry's ID matches the one to restore, then
    // the entire window will be restored.
    if (!restoring_tab_in_window) {
      browser = Browser::Create(profile());
      for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
        const Tab& tab = window->tabs[tab_i];
{'fraction_non_alphanumeric': 0.13295718250898667, 'fraction_numerical': 0.004459207353141921, 'mean_word_length': 3.503688524590164, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2758381', 'n_tokens_mistral': 6624, 'n_tokens_neox': 6450, 'n_words': 1857}