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.
WriteStringToPickle(Pickle & pickle,int * bytes_written,int max_bytes,const std::string & str)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.
RunIfNotCanceled(const base::CancelableTaskTracker::IsCanceledCallback & is_canceled,const BaseSessionService::InternalGetCommandsCallback & callback,ScopedVector<SessionCommand> commands)45 void RunIfNotCanceled(
46 const base::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
PostOrRunInternalGetCommandsCallback(base::TaskRunner * task_runner,const BaseSessionService::InternalGetCommandsCallback & callback,ScopedVector<SessionCommand> commands)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
BaseSessionService(SessionType type,Profile * profile,const base::FilePath & path)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
~BaseSessionService()92 BaseSessionService::~BaseSessionService() {
93 }
94
DeleteLastSession()95 void BaseSessionService::DeleteLastSession() {
96 RunTaskOnBackendThread(
97 FROM_HERE,
98 base::Bind(&SessionBackend::DeleteLastSession, backend()));
99 }
100
ScheduleCommand(SessionCommand * command)101 void BaseSessionService::ScheduleCommand(SessionCommand* command) {
102 DCHECK(command);
103 commands_since_reset_++;
104 pending_commands_.push_back(command);
105 StartSaveTimer();
106 }
107
StartSaveTimer()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
Save()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
CreateUpdateTabNavigationCommand(SessionID::id_type command_id,SessionID::id_type tab_id,const sessions::SerializedNavigationEntry & navigation)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
CreateSetTabExtensionAppIDCommand(SessionID::id_type command_id,SessionID::id_type tab_id,const std::string & extension_id)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
CreateSetTabUserAgentOverrideCommand(SessionID::id_type command_id,SessionID::id_type tab_id,const std::string & user_agent_override)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
CreateSetWindowAppNameCommand(SessionID::id_type command_id,SessionID::id_type window_id,const std::string & app_name)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
RestoreUpdateTabNavigationCommand(const SessionCommand & command,sessions::SerializedNavigationEntry * navigation,SessionID::id_type * tab_id)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
RestoreSetTabExtensionAppIDCommand(const SessionCommand & command,SessionID::id_type * tab_id,std::string * extension_app_id)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
RestoreSetTabUserAgentOverrideCommand(const SessionCommand & command,SessionID::id_type * tab_id,std::string * user_agent_override)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
RestoreSetWindowAppNameCommand(const SessionCommand & command,SessionID::id_type * window_id,std::string * app_name)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
ShouldTrackEntry(const GURL & url)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(content::kChromeUIScheme) &&
271 (url.host() == chrome::kChromeUIQuitHost ||
272 url.host() == chrome::kChromeUIRestartHost));
273 }
274
275 base::CancelableTaskTracker::TaskId
ScheduleGetLastSessionCommands(const InternalGetCommandsCallback & callback,base::CancelableTaskTracker * tracker)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);
282
283 InternalGetCommandsCallback run_if_not_canceled =
284 base::Bind(&RunIfNotCanceled, is_canceled, callback);
285
286 InternalGetCommandsCallback callback_runner =
287 base::Bind(&PostOrRunInternalGetCommandsCallback,
288 base::MessageLoopProxy::current(), run_if_not_canceled);
289
290 RunTaskOnBackendThread(
291 FROM_HERE,
292 base::Bind(&SessionBackend::ReadLastSessionCommands, backend(),
293 is_canceled, callback_runner));
294 return id;
295 }
296
RunTaskOnBackendThread(const tracked_objects::Location & from_here,const base::Closure & task)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_,
304 from_here,
305 task);
306 } else {
307 // Fall back to executing on the main thread if the sequence
308 // worker pool has been requested to shutdown (around shutdown
309 // time).
310 task.Run();
311 return true;
312 }
313 }
314