Home | History | Annotate | Download | only in sessions
      1 // Copyright (c) 2006-2008 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/base_session_service.h"
      6 
      7 #include "base/pickle.h"
      8 #include "base/stl_util-inl.h"
      9 #include "base/threading/thread.h"
     10 #include "chrome/browser/browser_process.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/sessions/session_backend.h"
     13 #include "chrome/browser/sessions/session_types.h"
     14 #include "content/browser/tab_contents/navigation_entry.h"
     15 #include "webkit/glue/webkit_glue.h"
     16 
     17 // InternalGetCommandsRequest -------------------------------------------------
     18 
     19 BaseSessionService::InternalGetCommandsRequest::~InternalGetCommandsRequest() {
     20   STLDeleteElements(&commands);
     21 }
     22 
     23 // BaseSessionService ---------------------------------------------------------
     24 
     25 namespace {
     26 
     27 // Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to
     28 // |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|).
     29 // |bytes_written| is incremented to reflect the data written.
     30 void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
     31                          const std::string& str) {
     32   int num_bytes = str.size() * sizeof(char);
     33   if (*bytes_written + num_bytes < max_bytes) {
     34     *bytes_written += num_bytes;
     35     pickle.WriteString(str);
     36   } else {
     37     pickle.WriteString(std::string());
     38   }
     39 }
     40 
     41 // string16 version of WriteStringToPickle.
     42 void WriteString16ToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
     43                            const string16& str) {
     44   int num_bytes = str.size() * sizeof(char16);
     45   if (*bytes_written + num_bytes < max_bytes) {
     46     *bytes_written += num_bytes;
     47     pickle.WriteString16(str);
     48   } else {
     49     pickle.WriteString16(string16());
     50   }
     51 }
     52 
     53 }  // namespace
     54 
     55 // Delay between when a command is received, and when we save it to the
     56 // backend.
     57 static const int kSaveDelayMS = 2500;
     58 
     59 // static
     60 const int BaseSessionService::max_persist_navigation_count = 6;
     61 
     62 BaseSessionService::BaseSessionService(SessionType type,
     63                                        Profile* profile,
     64                                        const FilePath& path)
     65     : profile_(profile),
     66       path_(path),
     67       backend_thread_(NULL),
     68       ALLOW_THIS_IN_INITIALIZER_LIST(save_factory_(this)),
     69       pending_reset_(false),
     70       commands_since_reset_(0) {
     71   if (profile) {
     72     // We should never be created when incognito.
     73     DCHECK(!profile->IsOffTheRecord());
     74   }
     75   backend_ = new SessionBackend(type,
     76       profile_ ? profile_->GetPath() : path_);
     77   DCHECK(backend_.get());
     78   backend_thread_ = g_browser_process->file_thread();
     79   if (!backend_thread_)
     80     backend_->Init();
     81   // If backend_thread is non-null, backend will init itself as appropriate.
     82 }
     83 
     84 BaseSessionService::~BaseSessionService() {
     85 }
     86 
     87 void BaseSessionService::DeleteLastSession() {
     88   if (!backend_thread()) {
     89     backend()->DeleteLastSession();
     90   } else {
     91     backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
     92         backend(), &SessionBackend::DeleteLastSession));
     93   }
     94 }
     95 
     96 void BaseSessionService::ScheduleCommand(SessionCommand* command) {
     97   DCHECK(command);
     98   commands_since_reset_++;
     99   pending_commands_.push_back(command);
    100   StartSaveTimer();
    101 }
    102 
    103 void BaseSessionService::StartSaveTimer() {
    104   // Don't start a timer when testing (profile == NULL or
    105   // MessageLoop::current() is NULL).
    106   if (MessageLoop::current() && profile() && save_factory_.empty()) {
    107     MessageLoop::current()->PostDelayedTask(FROM_HERE,
    108         save_factory_.NewRunnableMethod(&BaseSessionService::Save),
    109         kSaveDelayMS);
    110   }
    111 }
    112 
    113 void BaseSessionService::Save() {
    114   DCHECK(backend());
    115 
    116   if (pending_commands_.empty())
    117     return;
    118 
    119   if (!backend_thread()) {
    120     backend()->AppendCommands(
    121         new std::vector<SessionCommand*>(pending_commands_), pending_reset_);
    122   } else {
    123     backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    124         backend(), &SessionBackend::AppendCommands,
    125         new std::vector<SessionCommand*>(pending_commands_),
    126         pending_reset_));
    127   }
    128   // Backend took ownership of commands.
    129   pending_commands_.clear();
    130 
    131   if (pending_reset_) {
    132     commands_since_reset_ = 0;
    133     pending_reset_ = false;
    134   }
    135 }
    136 
    137 SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand(
    138     SessionID::id_type command_id,
    139     SessionID::id_type tab_id,
    140     int index,
    141     const NavigationEntry& entry) {
    142   // Use pickle to handle marshalling.
    143   Pickle pickle;
    144   pickle.WriteInt(tab_id);
    145   pickle.WriteInt(index);
    146 
    147   // We only allow navigations up to 63k (which should be completely
    148   // reasonable). On the off chance we get one that is too big, try to
    149   // keep the url.
    150 
    151   // Bound the string data (which is variable length) to
    152   // |max_state_size bytes| bytes.
    153   static const SessionCommand::size_type max_state_size =
    154       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
    155 
    156   int bytes_written = 0;
    157 
    158   WriteStringToPickle(pickle, &bytes_written, max_state_size,
    159                       entry.virtual_url().spec());
    160 
    161   WriteString16ToPickle(pickle, &bytes_written, max_state_size, entry.title());
    162 
    163   if (entry.has_post_data()) {
    164     // Remove the form data, it may contain sensitive information.
    165     WriteStringToPickle(pickle, &bytes_written, max_state_size,
    166         webkit_glue::RemoveFormDataFromHistoryState(entry.content_state()));
    167   } else {
    168     WriteStringToPickle(pickle, &bytes_written, max_state_size,
    169                         entry.content_state());
    170   }
    171 
    172   pickle.WriteInt(entry.transition_type());
    173   int type_mask = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0;
    174   pickle.WriteInt(type_mask);
    175 
    176   WriteStringToPickle(pickle, &bytes_written, max_state_size,
    177       entry.referrer().is_valid() ? entry.referrer().spec() : std::string());
    178 
    179   // Adding more data? Be sure and update TabRestoreService too.
    180   return new SessionCommand(command_id, pickle);
    181 }
    182 
    183 SessionCommand* BaseSessionService::CreateSetTabExtensionAppIDCommand(
    184     SessionID::id_type command_id,
    185     SessionID::id_type tab_id,
    186     const std::string& extension_id) {
    187   // Use pickle to handle marshalling.
    188   Pickle pickle;
    189   pickle.WriteInt(tab_id);
    190 
    191   // Enforce a max for ids. They should never be anywhere near this size.
    192   static const SessionCommand::size_type max_id_size =
    193       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
    194 
    195   int bytes_written = 0;
    196 
    197   WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id);
    198 
    199   return new SessionCommand(command_id, pickle);
    200 }
    201 
    202 bool BaseSessionService::RestoreUpdateTabNavigationCommand(
    203     const SessionCommand& command,
    204     TabNavigation* navigation,
    205     SessionID::id_type* tab_id) {
    206   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
    207   if (!pickle.get())
    208     return false;
    209   void* iterator = NULL;
    210   std::string url_spec;
    211   if (!pickle->ReadInt(&iterator, tab_id) ||
    212       !pickle->ReadInt(&iterator, &(navigation->index_)) ||
    213       !pickle->ReadString(&iterator, &url_spec) ||
    214       !pickle->ReadString16(&iterator, &(navigation->title_)) ||
    215       !pickle->ReadString(&iterator, &(navigation->state_)) ||
    216       !pickle->ReadInt(&iterator,
    217                        reinterpret_cast<int*>(&(navigation->transition_))))
    218     return false;
    219   // type_mask did not always exist in the written stream. As such, we
    220   // don't fail if it can't be read.
    221   bool has_type_mask = pickle->ReadInt(&iterator, &(navigation->type_mask_));
    222 
    223   if (has_type_mask) {
    224     // the "referrer" property was added after type_mask to the written
    225     // stream. As such, we don't fail if it can't be read.
    226     std::string referrer_spec;
    227     pickle->ReadString(&iterator, &referrer_spec);
    228     if (!referrer_spec.empty())
    229       navigation->referrer_ = GURL(referrer_spec);
    230   }
    231 
    232   navigation->virtual_url_ = GURL(url_spec);
    233   return true;
    234 }
    235 
    236 bool BaseSessionService::RestoreSetTabExtensionAppIDCommand(
    237     const SessionCommand& command,
    238     SessionID::id_type* tab_id,
    239     std::string* extension_app_id) {
    240   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
    241   if (!pickle.get())
    242     return false;
    243 
    244   void* iterator = NULL;
    245   return pickle->ReadInt(&iterator, tab_id) &&
    246       pickle->ReadString(&iterator, extension_app_id);
    247 }
    248 
    249 bool BaseSessionService::ShouldTrackEntry(const NavigationEntry& entry) {
    250   return entry.virtual_url().is_valid();
    251 }
    252 
    253 bool BaseSessionService::ShouldTrackEntry(const TabNavigation& navigation) {
    254   return navigation.virtual_url().is_valid();
    255 }
    256 
    257 BaseSessionService::Handle BaseSessionService::ScheduleGetLastSessionCommands(
    258     InternalGetCommandsRequest* request,
    259     CancelableRequestConsumerBase* consumer) {
    260   scoped_refptr<InternalGetCommandsRequest> request_wrapper(request);
    261   AddRequest(request, consumer);
    262   if (backend_thread()) {
    263     backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    264         backend(), &SessionBackend::ReadLastSessionCommands, request_wrapper));
    265   } else {
    266     backend()->ReadLastSessionCommands(request);
    267   }
    268   return request->handle();
    269 }
    270 
    271 BaseSessionService::Handle
    272     BaseSessionService::ScheduleGetCurrentSessionCommands(
    273         InternalGetCommandsRequest* request,
    274         CancelableRequestConsumerBase* consumer) {
    275   scoped_refptr<InternalGetCommandsRequest> request_wrapper(request);
    276   AddRequest(request, consumer);
    277   if (backend_thread()) {
    278     backend_thread()->message_loop()->PostTask(FROM_HERE,
    279         NewRunnableMethod(backend(),
    280                           &SessionBackend::ReadCurrentSessionCommands,
    281                           request_wrapper));
    282   } else {
    283     backend()->ReadCurrentSessionCommands(request);
    284   }
    285   return request->handle();
    286 }
    287