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