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 "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"
22 using content::BrowserThread
;
23 using content::NavigationEntry
;
25 // BaseSessionService ---------------------------------------------------------
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
);
39 pickle
.WriteString(std::string());
43 // Helper used by ScheduleGetLastSessionCommands. It runs callback on TaskRunner
44 // thread if it's not canceled.
45 void RunIfNotCanceled(
46 const base::CancelableTaskTracker::IsCanceledCallback
& is_canceled
,
47 const BaseSessionService::InternalGetCommandsCallback
& callback
,
48 ScopedVector
<SessionCommand
> commands
) {
49 if (is_canceled
.Run())
51 callback
.Run(commands
.Pass());
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());
61 task_runner
->PostTask(FROM_HERE
,
62 base::Bind(callback
, base::Passed(&commands
)));
68 // Delay between when a command is received, and when we save it to the
70 static const int kSaveDelayMS
= 2500;
73 const int BaseSessionService::max_persist_navigation_count
= 6;
75 BaseSessionService::BaseSessionService(SessionType type
,
77 const base::FilePath
& path
)
80 pending_reset_(false),
81 commands_since_reset_(0),
83 content::BrowserThread::GetBlockingPool()->GetSequenceToken()) {
85 // We should never be created when incognito.
86 DCHECK(!profile
->IsOffTheRecord());
88 backend_
= new SessionBackend(type
, profile_
? profile_
->GetPath() : path
);
89 DCHECK(backend_
.get());
92 BaseSessionService::~BaseSessionService() {
95 void BaseSessionService::DeleteLastSession() {
96 RunTaskOnBackendThread(
98 base::Bind(&SessionBackend::DeleteLastSession
, backend()));
101 void BaseSessionService::ScheduleCommand(SessionCommand
* command
) {
103 commands_since_reset_
++;
104 pending_commands_
.push_back(command
);
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(
115 base::Bind(&BaseSessionService::Save
, weak_factory_
.GetWeakPtr()),
116 base::TimeDelta::FromMilliseconds(kSaveDelayMS
));
120 void BaseSessionService::Save() {
123 if (pending_commands_
.empty())
126 RunTaskOnBackendThread(
128 base::Bind(&SessionBackend::AppendCommands
, backend(),
129 new std::vector
<SessionCommand
*>(pending_commands_
),
132 // Backend took ownership of commands.
133 pending_commands_
.clear();
135 if (pending_reset_
) {
136 commands_since_reset_
= 0;
137 pending_reset_
= false;
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.
147 pickle
.WriteInt(tab_id
);
148 // We only allow navigations up to 63k (which should be completely
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
);
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.
162 pickle
.WriteInt(tab_id
);
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;
168 int bytes_written
= 0;
170 WriteStringToPickle(pickle
, &bytes_written
, max_id_size
, extension_id
);
172 return new SessionCommand(command_id
, pickle
);
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.
181 pickle
.WriteInt(tab_id
);
183 // Enforce a max for the user agent length. They should never be anywhere
185 static const SessionCommand::size_type max_user_agent_size
=
186 std::numeric_limits
<SessionCommand::size_type
>::max() - 1024;
188 int bytes_written
= 0;
190 WriteStringToPickle(pickle
, &bytes_written
, max_user_agent_size
,
191 user_agent_override
);
193 return new SessionCommand(command_id
, pickle
);
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.
202 pickle
.WriteInt(window_id
);
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;
208 int bytes_written
= 0;
210 WriteStringToPickle(pickle
, &bytes_written
, max_id_size
, app_name
);
212 return new SessionCommand(command_id
, pickle
);
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());
222 PickleIterator
iterator(*pickle
);
224 pickle
->ReadInt(&iterator
, tab_id
) &&
225 navigation
->ReadFromPickle(&iterator
);
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());
236 PickleIterator
iterator(*pickle
);
237 return pickle
->ReadInt(&iterator
, tab_id
) &&
238 pickle
->ReadString(&iterator
, extension_app_id
);
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());
249 PickleIterator
iterator(*pickle
);
250 return pickle
->ReadInt(&iterator
, tab_id
) &&
251 pickle
->ReadString(&iterator
, user_agent_override
);
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());
262 PickleIterator
iterator(*pickle
);
263 return pickle
->ReadInt(&iterator
, window_id
) &&
264 pickle
->ReadString(&iterator
, app_name
);
267 bool BaseSessionService::ShouldTrackEntry(const GURL
& url
) {
268 // Blacklist chrome://quit and chrome://restart to avoid quit or restart
270 return url
.is_valid() && !(url
.SchemeIs(content::kChromeUIScheme
) &&
271 (url
.host() == chrome::kChromeUIQuitHost
||
272 url
.host() == chrome::kChromeUIRestartHost
));
275 base::CancelableTaskTracker::TaskId
276 BaseSessionService::ScheduleGetLastSessionCommands(
277 const InternalGetCommandsCallback
& callback
,
278 base::CancelableTaskTracker
* tracker
) {
279 base::CancelableTaskTracker::IsCanceledCallback is_canceled
;
280 base::CancelableTaskTracker::TaskId id
=
281 tracker
->NewTrackedTaskId(&is_canceled
);
283 InternalGetCommandsCallback run_if_not_canceled
=
284 base::Bind(&RunIfNotCanceled
, is_canceled
, callback
);
286 InternalGetCommandsCallback callback_runner
=
287 base::Bind(&PostOrRunInternalGetCommandsCallback
,
288 base::MessageLoopProxy::current(), run_if_not_canceled
);
290 RunTaskOnBackendThread(
292 base::Bind(&SessionBackend::ReadLastSessionCommands
, backend(),
293 is_canceled
, callback_runner
));
297 bool BaseSessionService::RunTaskOnBackendThread(
298 const tracked_objects::Location
& from_here
,
299 const base::Closure
& task
) {
300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
301 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
302 if (!pool
->IsShutdownInProgress()) {
303 return pool
->PostSequencedWorkerTask(sequence_token_
,
307 // Fall back to executing on the main thread if the sequence
308 // worker pool has been requested to shutdown (around shutdown