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 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 weak_factory_(this), 80 pending_reset_(false), 81 commands_since_reset_(0), 82 sequence_token_( 83 content::BrowserThread::GetBlockingPool()->GetSequenceToken()) { 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(chrome::kChromeUIScheme) && 271 (url.host() == chrome::kChromeUIQuitHost || 272 url.host() == chrome::kChromeUIRestartHost)); 273 } 274 275 CancelableTaskTracker::TaskId 276 BaseSessionService::ScheduleGetLastSessionCommands( 277 const InternalGetCommandsCallback& callback, 278 CancelableTaskTracker* tracker) { 279 CancelableTaskTracker::IsCanceledCallback is_canceled; 280 CancelableTaskTracker::TaskId id = tracker->NewTrackedTaskId(&is_canceled); 281 282 InternalGetCommandsCallback run_if_not_canceled = 283 base::Bind(&RunIfNotCanceled, is_canceled, callback); 284 285 InternalGetCommandsCallback callback_runner = 286 base::Bind(&PostOrRunInternalGetCommandsCallback, 287 base::MessageLoopProxy::current(), run_if_not_canceled); 288 289 RunTaskOnBackendThread( 290 FROM_HERE, 291 base::Bind(&SessionBackend::ReadLastSessionCommands, backend(), 292 is_canceled, callback_runner)); 293 return id; 294 } 295 296 bool BaseSessionService::RunTaskOnBackendThread( 297 const tracked_objects::Location& from_here, 298 const base::Closure& task) { 299 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 300 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 301 if (!pool->IsShutdownInProgress()) { 302 return pool->PostSequencedWorkerTask(sequence_token_, 303 from_here, 304 task); 305 } else { 306 // Fall back to executing on the main thread if the sequence 307 // worker pool has been requested to shutdown (around shutdown 308 // time). 309 task.Run(); 310 return true; 311 } 312 } 313