• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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