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