base_session_service.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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#include "webkit/glue/webkit_glue.h" 21 22using content::BrowserThread; 23using content::NavigationEntry; 24 25// BaseSessionService --------------------------------------------------------- 26 27namespace { 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. 32void 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. 45void RunIfNotCanceled( 46 const 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 54void 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. 70static const int kSaveDelayMS = 2500; 71 72// static 73const int BaseSessionService::max_persist_navigation_count = 6; 74 75BaseSessionService::BaseSessionService(SessionType type, 76 Profile* profile, 77 const base::FilePath& path) 78 : profile_(profile), 79 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), 80 pending_reset_(false), 81 commands_since_reset_(0) { 82 if (profile) { 83 // We should never be created when incognito. 84 DCHECK(!profile->IsOffTheRecord()); 85 } 86 backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path); 87 DCHECK(backend_.get()); 88 89 // SessionBackend::Init() cannot be scheduled to be called here. There are 90 // service processes which create the BaseSessionService, but they should not 91 // initialize the backend. If they do, the backend will cycle the session 92 // restore files. That in turn prevents the session restore from working when 93 // the normal chromium process is launched. Normally, the backend will be 94 // initialized before it's actually used. However, if we're running as a part 95 // of a test, it must be initialized now. 96 if (!RunningInProduction()) 97 backend_->Init(); 98} 99 100BaseSessionService::~BaseSessionService() { 101} 102 103void BaseSessionService::DeleteLastSession() { 104 RunTaskOnBackendThread( 105 FROM_HERE, 106 base::Bind(&SessionBackend::DeleteLastSession, backend())); 107} 108 109void BaseSessionService::ScheduleCommand(SessionCommand* command) { 110 DCHECK(command); 111 commands_since_reset_++; 112 pending_commands_.push_back(command); 113 StartSaveTimer(); 114} 115 116void BaseSessionService::StartSaveTimer() { 117 // Don't start a timer when testing (profile == NULL or 118 // MessageLoop::current() is NULL). 119 if (MessageLoop::current() && profile() && !weak_factory_.HasWeakPtrs()) { 120 MessageLoop::current()->PostDelayedTask( 121 FROM_HERE, 122 base::Bind(&BaseSessionService::Save, weak_factory_.GetWeakPtr()), 123 base::TimeDelta::FromMilliseconds(kSaveDelayMS)); 124 } 125} 126 127void BaseSessionService::Save() { 128 DCHECK(backend()); 129 130 if (pending_commands_.empty()) 131 return; 132 133 RunTaskOnBackendThread( 134 FROM_HERE, 135 base::Bind(&SessionBackend::AppendCommands, backend(), 136 new std::vector<SessionCommand*>(pending_commands_), 137 pending_reset_)); 138 139 // Backend took ownership of commands. 140 pending_commands_.clear(); 141 142 if (pending_reset_) { 143 commands_since_reset_ = 0; 144 pending_reset_ = false; 145 } 146} 147 148SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand( 149 SessionID::id_type command_id, 150 SessionID::id_type tab_id, 151 const TabNavigation& navigation) { 152 // Use pickle to handle marshalling. 153 Pickle pickle; 154 pickle.WriteInt(tab_id); 155 navigation.WriteToPickle(&pickle); 156 return new SessionCommand(command_id, pickle); 157} 158 159SessionCommand* BaseSessionService::CreateSetTabExtensionAppIDCommand( 160 SessionID::id_type command_id, 161 SessionID::id_type tab_id, 162 const std::string& extension_id) { 163 // Use pickle to handle marshalling. 164 Pickle pickle; 165 pickle.WriteInt(tab_id); 166 167 // Enforce a max for ids. They should never be anywhere near this size. 168 static const SessionCommand::size_type max_id_size = 169 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 170 171 int bytes_written = 0; 172 173 WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id); 174 175 return new SessionCommand(command_id, pickle); 176} 177 178SessionCommand* BaseSessionService::CreateSetTabUserAgentOverrideCommand( 179 SessionID::id_type command_id, 180 SessionID::id_type tab_id, 181 const std::string& user_agent_override) { 182 // Use pickle to handle marshalling. 183 Pickle pickle; 184 pickle.WriteInt(tab_id); 185 186 // Enforce a max for the user agent length. They should never be anywhere 187 // near this size. 188 static const SessionCommand::size_type max_user_agent_size = 189 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 190 191 int bytes_written = 0; 192 193 WriteStringToPickle(pickle, &bytes_written, max_user_agent_size, 194 user_agent_override); 195 196 return new SessionCommand(command_id, pickle); 197} 198 199SessionCommand* BaseSessionService::CreateSetWindowAppNameCommand( 200 SessionID::id_type command_id, 201 SessionID::id_type window_id, 202 const std::string& app_name) { 203 // Use pickle to handle marshalling. 204 Pickle pickle; 205 pickle.WriteInt(window_id); 206 207 // Enforce a max for ids. They should never be anywhere near this size. 208 static const SessionCommand::size_type max_id_size = 209 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 210 211 int bytes_written = 0; 212 213 WriteStringToPickle(pickle, &bytes_written, max_id_size, app_name); 214 215 return new SessionCommand(command_id, pickle); 216} 217 218bool BaseSessionService::RestoreUpdateTabNavigationCommand( 219 const SessionCommand& command, 220 TabNavigation* navigation, 221 SessionID::id_type* tab_id) { 222 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 223 if (!pickle.get()) 224 return false; 225 PickleIterator iterator(*pickle); 226 return 227 pickle->ReadInt(&iterator, tab_id) && 228 navigation->ReadFromPickle(&iterator); 229} 230 231bool BaseSessionService::RestoreSetTabExtensionAppIDCommand( 232 const SessionCommand& command, 233 SessionID::id_type* tab_id, 234 std::string* extension_app_id) { 235 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 236 if (!pickle.get()) 237 return false; 238 239 PickleIterator iterator(*pickle); 240 return pickle->ReadInt(&iterator, tab_id) && 241 pickle->ReadString(&iterator, extension_app_id); 242} 243 244bool BaseSessionService::RestoreSetTabUserAgentOverrideCommand( 245 const SessionCommand& command, 246 SessionID::id_type* tab_id, 247 std::string* user_agent_override) { 248 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 249 if (!pickle.get()) 250 return false; 251 252 PickleIterator iterator(*pickle); 253 return pickle->ReadInt(&iterator, tab_id) && 254 pickle->ReadString(&iterator, user_agent_override); 255} 256 257bool BaseSessionService::RestoreSetWindowAppNameCommand( 258 const SessionCommand& command, 259 SessionID::id_type* window_id, 260 std::string* app_name) { 261 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 262 if (!pickle.get()) 263 return false; 264 265 PickleIterator iterator(*pickle); 266 return pickle->ReadInt(&iterator, window_id) && 267 pickle->ReadString(&iterator, app_name); 268} 269 270bool BaseSessionService::ShouldTrackEntry(const GURL& url) { 271 return url.is_valid(); 272} 273 274CancelableTaskTracker::TaskId 275 BaseSessionService::ScheduleGetLastSessionCommands( 276 const InternalGetCommandsCallback& callback, 277 CancelableTaskTracker* tracker) { 278 CancelableTaskTracker::IsCanceledCallback is_canceled; 279 CancelableTaskTracker::TaskId id = tracker->NewTrackedTaskId(&is_canceled); 280 281 InternalGetCommandsCallback run_if_not_canceled = 282 base::Bind(&RunIfNotCanceled, is_canceled, callback); 283 284 InternalGetCommandsCallback callback_runner = 285 base::Bind(&PostOrRunInternalGetCommandsCallback, 286 base::MessageLoopProxy::current(), run_if_not_canceled); 287 288 RunTaskOnBackendThread( 289 FROM_HERE, 290 base::Bind(&SessionBackend::ReadLastSessionCommands, backend(), 291 is_canceled, callback_runner)); 292 return id; 293} 294 295bool BaseSessionService::RunTaskOnBackendThread( 296 const tracked_objects::Location& from_here, 297 const base::Closure& task) { 298 if (profile_ && BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { 299 return BrowserThread::PostTask(BrowserThread::FILE, from_here, task); 300 } else { 301 // Fall back to executing on the main thread if the file thread 302 // has gone away (around shutdown time) or if we're running as 303 // part of a unit test that does not set profile_. 304 task.Run(); 305 return true; 306 } 307} 308 309bool BaseSessionService::RunningInProduction() const { 310 return profile_ && BrowserThread::IsMessageLoopValid(BrowserThread::FILE); 311} 312