base_session_service.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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// InternalGetCommandsRequest ------------------------------------------------- 26 27BaseSessionService::InternalGetCommandsRequest::InternalGetCommandsRequest( 28 const CallbackType& callback) 29 : CancelableRequest<InternalGetCommandsCallback>(callback) { 30} 31 32BaseSessionService::InternalGetCommandsRequest::~InternalGetCommandsRequest() { 33 STLDeleteElements(&commands); 34} 35 36// BaseSessionService --------------------------------------------------------- 37 38namespace { 39 40// Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to 41// |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|). 42// |bytes_written| is incremented to reflect the data written. 43void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes, 44 const std::string& str) { 45 int num_bytes = str.size() * sizeof(char); 46 if (*bytes_written + num_bytes < max_bytes) { 47 *bytes_written += num_bytes; 48 pickle.WriteString(str); 49 } else { 50 pickle.WriteString(std::string()); 51 } 52} 53 54} // namespace 55 56// Delay between when a command is received, and when we save it to the 57// backend. 58static const int kSaveDelayMS = 2500; 59 60// static 61const int BaseSessionService::max_persist_navigation_count = 6; 62 63BaseSessionService::BaseSessionService(SessionType type, 64 Profile* profile, 65 const FilePath& path) 66 : profile_(profile), 67 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), 68 pending_reset_(false), 69 commands_since_reset_(0) { 70 if (profile) { 71 // We should never be created when incognito. 72 DCHECK(!profile->IsOffTheRecord()); 73 } 74 backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path); 75 DCHECK(backend_.get()); 76 77 // SessionBackend::Init() cannot be scheduled to be called here. There are 78 // service processes which create the BaseSessionService, but they should not 79 // initialize the backend. If they do, the backend will cycle the session 80 // restore files. That in turn prevents the session restore from working when 81 // the normal chromium process is launched. Normally, the backend will be 82 // initialized before it's actually used. However, if we're running as a part 83 // of a test, it must be initialized now. 84 if (!RunningInProduction()) 85 backend_->Init(); 86} 87 88BaseSessionService::~BaseSessionService() { 89} 90 91void BaseSessionService::DeleteLastSession() { 92 RunTaskOnBackendThread( 93 FROM_HERE, 94 base::Bind(&SessionBackend::DeleteLastSession, backend())); 95} 96 97void BaseSessionService::ScheduleCommand(SessionCommand* command) { 98 DCHECK(command); 99 commands_since_reset_++; 100 pending_commands_.push_back(command); 101 StartSaveTimer(); 102} 103 104void BaseSessionService::StartSaveTimer() { 105 // Don't start a timer when testing (profile == NULL or 106 // MessageLoop::current() is NULL). 107 if (MessageLoop::current() && profile() && !weak_factory_.HasWeakPtrs()) { 108 MessageLoop::current()->PostDelayedTask( 109 FROM_HERE, 110 base::Bind(&BaseSessionService::Save, weak_factory_.GetWeakPtr()), 111 base::TimeDelta::FromMilliseconds(kSaveDelayMS)); 112 } 113} 114 115void BaseSessionService::Save() { 116 DCHECK(backend()); 117 118 if (pending_commands_.empty()) 119 return; 120 121 RunTaskOnBackendThread( 122 FROM_HERE, 123 base::Bind(&SessionBackend::AppendCommands, backend(), 124 new std::vector<SessionCommand*>(pending_commands_), 125 pending_reset_)); 126 127 // Backend took ownership of commands. 128 pending_commands_.clear(); 129 130 if (pending_reset_) { 131 commands_since_reset_ = 0; 132 pending_reset_ = false; 133 } 134} 135 136SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand( 137 SessionID::id_type command_id, 138 SessionID::id_type tab_id, 139 const TabNavigation& navigation) { 140 // Use pickle to handle marshalling. 141 Pickle pickle; 142 pickle.WriteInt(tab_id); 143 navigation.WriteToPickle(&pickle); 144 return new SessionCommand(command_id, pickle); 145} 146 147SessionCommand* BaseSessionService::CreateSetTabExtensionAppIDCommand( 148 SessionID::id_type command_id, 149 SessionID::id_type tab_id, 150 const std::string& extension_id) { 151 // Use pickle to handle marshalling. 152 Pickle pickle; 153 pickle.WriteInt(tab_id); 154 155 // Enforce a max for ids. They should never be anywhere near this size. 156 static const SessionCommand::size_type max_id_size = 157 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 158 159 int bytes_written = 0; 160 161 WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id); 162 163 return new SessionCommand(command_id, pickle); 164} 165 166SessionCommand* BaseSessionService::CreateSetTabUserAgentOverrideCommand( 167 SessionID::id_type command_id, 168 SessionID::id_type tab_id, 169 const std::string& user_agent_override) { 170 // Use pickle to handle marshalling. 171 Pickle pickle; 172 pickle.WriteInt(tab_id); 173 174 // Enforce a max for the user agent length. They should never be anywhere 175 // near this size. 176 static const SessionCommand::size_type max_user_agent_size = 177 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 178 179 int bytes_written = 0; 180 181 WriteStringToPickle(pickle, &bytes_written, max_user_agent_size, 182 user_agent_override); 183 184 return new SessionCommand(command_id, pickle); 185} 186 187SessionCommand* BaseSessionService::CreateSetWindowAppNameCommand( 188 SessionID::id_type command_id, 189 SessionID::id_type window_id, 190 const std::string& app_name) { 191 // Use pickle to handle marshalling. 192 Pickle pickle; 193 pickle.WriteInt(window_id); 194 195 // Enforce a max for ids. They should never be anywhere near this size. 196 static const SessionCommand::size_type max_id_size = 197 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 198 199 int bytes_written = 0; 200 201 WriteStringToPickle(pickle, &bytes_written, max_id_size, app_name); 202 203 return new SessionCommand(command_id, pickle); 204} 205 206bool BaseSessionService::RestoreUpdateTabNavigationCommand( 207 const SessionCommand& command, 208 TabNavigation* navigation, 209 SessionID::id_type* tab_id) { 210 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 211 if (!pickle.get()) 212 return false; 213 PickleIterator iterator(*pickle); 214 return 215 pickle->ReadInt(&iterator, tab_id) && 216 navigation->ReadFromPickle(&iterator); 217} 218 219bool BaseSessionService::RestoreSetTabExtensionAppIDCommand( 220 const SessionCommand& command, 221 SessionID::id_type* tab_id, 222 std::string* extension_app_id) { 223 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 224 if (!pickle.get()) 225 return false; 226 227 PickleIterator iterator(*pickle); 228 return pickle->ReadInt(&iterator, tab_id) && 229 pickle->ReadString(&iterator, extension_app_id); 230} 231 232bool BaseSessionService::RestoreSetTabUserAgentOverrideCommand( 233 const SessionCommand& command, 234 SessionID::id_type* tab_id, 235 std::string* user_agent_override) { 236 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 237 if (!pickle.get()) 238 return false; 239 240 PickleIterator iterator(*pickle); 241 return pickle->ReadInt(&iterator, tab_id) && 242 pickle->ReadString(&iterator, user_agent_override); 243} 244 245bool BaseSessionService::RestoreSetWindowAppNameCommand( 246 const SessionCommand& command, 247 SessionID::id_type* window_id, 248 std::string* app_name) { 249 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 250 if (!pickle.get()) 251 return false; 252 253 PickleIterator iterator(*pickle); 254 return pickle->ReadInt(&iterator, window_id) && 255 pickle->ReadString(&iterator, app_name); 256} 257 258bool BaseSessionService::ShouldTrackEntry(const GURL& url) { 259 return url.is_valid(); 260} 261 262BaseSessionService::Handle BaseSessionService::ScheduleGetLastSessionCommands( 263 InternalGetCommandsRequest* request, 264 CancelableRequestConsumerBase* consumer) { 265 scoped_refptr<InternalGetCommandsRequest> request_wrapper(request); 266 AddRequest(request, consumer); 267 RunTaskOnBackendThread( 268 FROM_HERE, 269 base::Bind(&SessionBackend::ReadLastSessionCommands, backend(), 270 request_wrapper)); 271 return request->handle(); 272} 273 274bool BaseSessionService::RunTaskOnBackendThread( 275 const tracked_objects::Location& from_here, 276 const base::Closure& task) { 277 if (profile_ && BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { 278 return BrowserThread::PostTask(BrowserThread::FILE, from_here, task); 279 } else { 280 // Fall back to executing on the main thread if the file thread 281 // has gone away (around shutdown time) or if we're running as 282 // part of a unit test that does not set profile_. 283 task.Run(); 284 return true; 285 } 286} 287 288bool BaseSessionService::RunningInProduction() const { 289 return profile_ && BrowserThread::IsMessageLoopValid(BrowserThread::FILE); 290} 291