Home | History | Annotate | Download | only in sessions
      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/base_session_service.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/pickle.h"
     10 #include "base/stl_util.h"
     11 #include "base/threading/thread.h"
     12 #include "chrome/browser/browser_process.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/sessions/session_backend.h"
     15 #include "chrome/browser/sessions/session_types.h"
     16 #include "chrome/common/chrome_switches.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "content/public/browser/navigation_entry.h"
     19 #include "content/public/common/referrer.h"
     20 
     21 using content::BrowserThread;
     22 using content::NavigationEntry;
     23 
     24 // BaseSessionService ---------------------------------------------------------
     25 
     26 namespace {
     27 
     28 // Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to
     29 // |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|).
     30 // |bytes_written| is incremented to reflect the data written.
     31 void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
     32                          const std::string& str) {
     33   int num_bytes = str.size() * sizeof(char);
     34   if (*bytes_written + num_bytes < max_bytes) {
     35     *bytes_written += num_bytes;
     36     pickle.WriteString(str);
     37   } else {
     38     pickle.WriteString(std::string());
     39   }
     40 }
     41 
     42 // Helper used by ScheduleGetLastSessionCommands. It runs callback on TaskRunner
     43 // thread if it's not canceled.
     44 void RunIfNotCanceled(
     45     const CancelableTaskTracker::IsCanceledCallback& is_canceled,
     46     const BaseSessionService::InternalGetCommandsCallback& callback,
     47     ScopedVector<SessionCommand> commands) {
     48   if (is_canceled.Run())
     49     return;
     50   callback.Run(commands.Pass());
     51 }
     52 
     53 void PostOrRunInternalGetCommandsCallback(
     54     base::TaskRunner* task_runner,
     55     const BaseSessionService::InternalGetCommandsCallback& callback,
     56     ScopedVector<SessionCommand> commands) {
     57   if (task_runner->RunsTasksOnCurrentThread()) {
     58     callback.Run(commands.Pass());
     59   } else {
     60     task_runner->PostTask(FROM_HERE,
     61                           base::Bind(callback, base::Passed(&commands)));
     62   }
     63 }
     64 
     65 }  // namespace
     66 
     67 // Delay between when a command is received, and when we save it to the
     68 // backend.
     69 static const int kSaveDelayMS = 2500;
     70 
     71 // static
     72 const int BaseSessionService::max_persist_navigation_count = 6;
     73 
     74 BaseSessionService::BaseSessionService(SessionType type,
     75                                        Profile* profile,
     76                                        const base::FilePath& path)
     77     : profile_(profile),
     78       weak_factory_(this),
     79       pending_reset_(false),
     80       commands_since_reset_(0),
     81       sequence_token_(
     82           content::BrowserThread::GetBlockingPool()->GetSequenceToken()) {
     83   if (profile) {
     84     // We should never be created when incognito.
     85     DCHECK(!profile->IsOffTheRecord());
     86   }
     87   backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path);
     88   DCHECK(backend_.get());
     89 }
     90 
     91 BaseSessionService::~BaseSessionService() {
     92 }
     93 
     94 void BaseSessionService::DeleteLastSession() {
     95   RunTaskOnBackendThread(
     96       FROM_HERE,
     97       base::Bind(&SessionBackend::DeleteLastSession, backend()));
     98 }
     99 
    100 void BaseSessionService::ScheduleCommand(SessionCommand* command) {
    101   DCHECK(command);
    102   commands_since_reset_++;
    103   pending_commands_.push_back(command);
    104   StartSaveTimer();
    105 }
    106 
    107 void BaseSessionService::StartSaveTimer() {
    108   // Don't start a timer when testing (profile == NULL or
    109   // MessageLoop::current() is NULL).
    110   if (base::MessageLoop::current() && profile() &&
    111       !weak_factory_.HasWeakPtrs()) {
    112     base::MessageLoop::current()->PostDelayedTask(
    113         FROM_HERE,
    114         base::Bind(&BaseSessionService::Save, weak_factory_.GetWeakPtr()),
    115         base::TimeDelta::FromMilliseconds(kSaveDelayMS));
    116   }
    117 }
    118 
    119 void BaseSessionService::Save() {
    120   DCHECK(backend());
    121 
    122   if (pending_commands_.empty())
    123     return;
    124 
    125   RunTaskOnBackendThread(
    126       FROM_HERE,
    127       base::Bind(&SessionBackend::AppendCommands, backend(),
    128                  new std::vector<SessionCommand*>(pending_commands_),
    129                  pending_reset_));
    130 
    131   // Backend took ownership of commands.
    132   pending_commands_.clear();
    133 
    134   if (pending_reset_) {
    135     commands_since_reset_ = 0;
    136     pending_reset_ = false;
    137   }
    138 }
    139 
    140 SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand(
    141     SessionID::id_type command_id,
    142     SessionID::id_type tab_id,
    143     const sessions::SerializedNavigationEntry& navigation) {
    144   // Use pickle to handle marshalling.
    145   Pickle pickle;
    146   pickle.WriteInt(tab_id);
    147   // We only allow navigations up to 63k (which should be completely
    148   // reasonable).
    149   static const size_t max_state_size =
    150       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
    151   navigation.WriteToPickle(max_state_size, &pickle);
    152   return new SessionCommand(command_id, pickle);
    153 }
    154 
    155 SessionCommand* BaseSessionService::CreateSetTabExtensionAppIDCommand(
    156     SessionID::id_type command_id,
    157     SessionID::id_type tab_id,
    158     const std::string& extension_id) {
    159   // Use pickle to handle marshalling.
    160   Pickle pickle;
    161   pickle.WriteInt(tab_id);
    162 
    163   // Enforce a max for ids. They should never be anywhere near this size.
    164   static const SessionCommand::size_type max_id_size =
    165       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
    166 
    167   int bytes_written = 0;
    168 
    169   WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id);
    170 
    171   return new SessionCommand(command_id, pickle);
    172 }
    173 
    174 SessionCommand* BaseSessionService::CreateSetTabUserAgentOverrideCommand(
    175     SessionID::id_type command_id,
    176     SessionID::id_type tab_id,
    177     const std::string& user_agent_override) {
    178   // Use pickle to handle marshalling.
    179   Pickle pickle;
    180   pickle.WriteInt(tab_id);
    181 
    182   // Enforce a max for the user agent length.  They should never be anywhere
    183   // near this size.
    184   static const SessionCommand::size_type max_user_agent_size =
    185       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
    186 
    187   int bytes_written = 0;
    188 
    189   WriteStringToPickle(pickle, &bytes_written, max_user_agent_size,
    190       user_agent_override);
    191 
    192   return new SessionCommand(command_id, pickle);
    193 }
    194 
    195 SessionCommand* BaseSessionService::CreateSetWindowAppNameCommand(
    196     SessionID::id_type command_id,
    197     SessionID::id_type window_id,
    198     const std::string& app_name) {
    199   // Use pickle to handle marshalling.
    200   Pickle pickle;
    201   pickle.WriteInt(window_id);
    202 
    203   // Enforce a max for ids. They should never be anywhere near this size.
    204   static const SessionCommand::size_type max_id_size =
    205       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
    206 
    207   int bytes_written = 0;
    208 
    209   WriteStringToPickle(pickle, &bytes_written, max_id_size, app_name);
    210 
    211   return new SessionCommand(command_id, pickle);
    212 }
    213 
    214 bool BaseSessionService::RestoreUpdateTabNavigationCommand(
    215     const SessionCommand& command,
    216     sessions::SerializedNavigationEntry* navigation,
    217     SessionID::id_type* tab_id) {
    218   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
    219   if (!pickle.get())
    220     return false;
    221   PickleIterator iterator(*pickle);
    222   return
    223       pickle->ReadInt(&iterator, tab_id) &&
    224       navigation->ReadFromPickle(&iterator);
    225 }
    226 
    227 bool BaseSessionService::RestoreSetTabExtensionAppIDCommand(
    228     const SessionCommand& command,
    229     SessionID::id_type* tab_id,
    230     std::string* extension_app_id) {
    231   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
    232   if (!pickle.get())
    233     return false;
    234 
    235   PickleIterator iterator(*pickle);
    236   return pickle->ReadInt(&iterator, tab_id) &&
    237       pickle->ReadString(&iterator, extension_app_id);
    238 }
    239 
    240 bool BaseSessionService::RestoreSetTabUserAgentOverrideCommand(
    241     const SessionCommand& command,
    242     SessionID::id_type* tab_id,
    243     std::string* user_agent_override) {
    244   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
    245   if (!pickle.get())
    246     return false;
    247 
    248   PickleIterator iterator(*pickle);
    249   return pickle->ReadInt(&iterator, tab_id) &&
    250       pickle->ReadString(&iterator, user_agent_override);
    251 }
    252 
    253 bool BaseSessionService::RestoreSetWindowAppNameCommand(
    254     const SessionCommand& command,
    255     SessionID::id_type* window_id,
    256     std::string* app_name) {
    257   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
    258   if (!pickle.get())
    259     return false;
    260 
    261   PickleIterator iterator(*pickle);
    262   return pickle->ReadInt(&iterator, window_id) &&
    263       pickle->ReadString(&iterator, app_name);
    264 }
    265 
    266 bool BaseSessionService::ShouldTrackEntry(const GURL& url) {
    267   return url.is_valid();
    268 }
    269 
    270 CancelableTaskTracker::TaskId
    271     BaseSessionService::ScheduleGetLastSessionCommands(
    272     const InternalGetCommandsCallback& callback,
    273     CancelableTaskTracker* tracker) {
    274   CancelableTaskTracker::IsCanceledCallback is_canceled;
    275   CancelableTaskTracker::TaskId id = tracker->NewTrackedTaskId(&is_canceled);
    276 
    277   InternalGetCommandsCallback run_if_not_canceled =
    278       base::Bind(&RunIfNotCanceled, is_canceled, callback);
    279 
    280   InternalGetCommandsCallback callback_runner =
    281       base::Bind(&PostOrRunInternalGetCommandsCallback,
    282                  base::MessageLoopProxy::current(), run_if_not_canceled);
    283 
    284   RunTaskOnBackendThread(
    285       FROM_HERE,
    286       base::Bind(&SessionBackend::ReadLastSessionCommands, backend(),
    287                  is_canceled, callback_runner));
    288   return id;
    289 }
    290 
    291 bool BaseSessionService::RunTaskOnBackendThread(
    292     const tracked_objects::Location& from_here,
    293     const base::Closure& task) {
    294   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    295   base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
    296   if (!pool->IsShutdownInProgress()) {
    297     return pool->PostSequencedWorkerTask(sequence_token_,
    298                                          from_here,
    299                                          task);
    300   } else {
    301     // Fall back to executing on the main thread if the sequence
    302     // worker pool has been requested to shutdown (around shutdown
    303     // time).
    304     task.Run();
    305     return true;
    306   }
    307 }
    308