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.
5 #include "chrome/browser/sessions/base_session_service.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"
21 using content::BrowserThread;
22 using content::NavigationEntry;
24 // BaseSessionService ---------------------------------------------------------
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);
38 pickle.WriteString(std::string());
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())
50 callback.Run(commands.Pass());
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());
60 task_runner->PostTask(FROM_HERE,
61 base::Bind(callback, base::Passed(&commands)));
67 // Delay between when a command is received, and when we save it to the
69 static const int kSaveDelayMS = 2500;
72 const int BaseSessionService::max_persist_navigation_count = 6;
74 BaseSessionService::BaseSessionService(SessionType type,
76 const base::FilePath& path)
79 pending_reset_(false),
80 commands_since_reset_(0),
82 content::BrowserThread::GetBlockingPool()->GetSequenceToken()) {
84 // We should never be created when incognito.
85 DCHECK(!profile->IsOffTheRecord());
87 backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path);
88 DCHECK(backend_.get());
91 BaseSessionService::~BaseSessionService() {
94 void BaseSessionService::DeleteLastSession() {
95 RunTaskOnBackendThread(
97 base::Bind(&SessionBackend::DeleteLastSession, backend()));
100 void BaseSessionService::ScheduleCommand(SessionCommand* command) {
102 commands_since_reset_++;
103 pending_commands_.push_back(command);
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(
114 base::Bind(&BaseSessionService::Save, weak_factory_.GetWeakPtr()),
115 base::TimeDelta::FromMilliseconds(kSaveDelayMS));
119 void BaseSessionService::Save() {
122 if (pending_commands_.empty())
125 RunTaskOnBackendThread(
127 base::Bind(&SessionBackend::AppendCommands, backend(),
128 new std::vector<SessionCommand*>(pending_commands_),
131 // Backend took ownership of commands.
132 pending_commands_.clear();
134 if (pending_reset_) {
135 commands_since_reset_ = 0;
136 pending_reset_ = false;
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.
146 pickle.WriteInt(tab_id);
147 // We only allow navigations up to 63k (which should be completely
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);
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.
161 pickle.WriteInt(tab_id);
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;
167 int bytes_written = 0;
169 WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id);
171 return new SessionCommand(command_id, pickle);
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.
180 pickle.WriteInt(tab_id);
182 // Enforce a max for the user agent length. They should never be anywhere
184 static const SessionCommand::size_type max_user_agent_size =
185 std::numeric_limits<SessionCommand::size_type>::max() - 1024;
187 int bytes_written = 0;
189 WriteStringToPickle(pickle, &bytes_written, max_user_agent_size,
190 user_agent_override);
192 return new SessionCommand(command_id, pickle);
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.
201 pickle.WriteInt(window_id);
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;
207 int bytes_written = 0;
209 WriteStringToPickle(pickle, &bytes_written, max_id_size, app_name);
211 return new SessionCommand(command_id, pickle);
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());
221 PickleIterator iterator(*pickle);
223 pickle->ReadInt(&iterator, tab_id) &&
224 navigation->ReadFromPickle(&iterator);
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());
235 PickleIterator iterator(*pickle);
236 return pickle->ReadInt(&iterator, tab_id) &&
237 pickle->ReadString(&iterator, extension_app_id);
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());
248 PickleIterator iterator(*pickle);
249 return pickle->ReadInt(&iterator, tab_id) &&
250 pickle->ReadString(&iterator, user_agent_override);
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());
261 PickleIterator iterator(*pickle);
262 return pickle->ReadInt(&iterator, window_id) &&
263 pickle->ReadString(&iterator, app_name);
266 bool BaseSessionService::ShouldTrackEntry(const GURL& url) {
267 return url.is_valid();
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);
277 InternalGetCommandsCallback run_if_not_canceled =
278 base::Bind(&RunIfNotCanceled, is_canceled, callback);
280 InternalGetCommandsCallback callback_runner =
281 base::Bind(&PostOrRunInternalGetCommandsCallback,
282 base::MessageLoopProxy::current(), run_if_not_canceled);
284 RunTaskOnBackendThread(
286 base::Bind(&SessionBackend::ReadLastSessionCommands, backend(),
287 is_canceled, callback_runner));
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_,
301 // Fall back to executing on the main thread if the sequence
302 // worker pool has been requested to shutdown (around shutdown