• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2008 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/pickle.h"
8 #include "base/stl_util-inl.h"
9 #include "base/threading/thread.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/sessions/session_backend.h"
13 #include "chrome/browser/sessions/session_types.h"
14 #include "content/browser/tab_contents/navigation_entry.h"
15 #include "webkit/glue/webkit_glue.h"
16 
17 // InternalGetCommandsRequest -------------------------------------------------
18 
~InternalGetCommandsRequest()19 BaseSessionService::InternalGetCommandsRequest::~InternalGetCommandsRequest() {
20   STLDeleteElements(&commands);
21 }
22 
23 // BaseSessionService ---------------------------------------------------------
24 
25 namespace {
26 
27 // Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to
28 // |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|).
29 // |bytes_written| is incremented to reflect the data written.
WriteStringToPickle(Pickle & pickle,int * bytes_written,int max_bytes,const std::string & str)30 void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
31                          const std::string& str) {
32   int num_bytes = str.size() * sizeof(char);
33   if (*bytes_written + num_bytes < max_bytes) {
34     *bytes_written += num_bytes;
35     pickle.WriteString(str);
36   } else {
37     pickle.WriteString(std::string());
38   }
39 }
40 
41 // string16 version of WriteStringToPickle.
WriteString16ToPickle(Pickle & pickle,int * bytes_written,int max_bytes,const string16 & str)42 void WriteString16ToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
43                            const string16& str) {
44   int num_bytes = str.size() * sizeof(char16);
45   if (*bytes_written + num_bytes < max_bytes) {
46     *bytes_written += num_bytes;
47     pickle.WriteString16(str);
48   } else {
49     pickle.WriteString16(string16());
50   }
51 }
52 
53 }  // namespace
54 
55 // Delay between when a command is received, and when we save it to the
56 // backend.
57 static const int kSaveDelayMS = 2500;
58 
59 // static
60 const int BaseSessionService::max_persist_navigation_count = 6;
61 
BaseSessionService(SessionType type,Profile * profile,const FilePath & path)62 BaseSessionService::BaseSessionService(SessionType type,
63                                        Profile* profile,
64                                        const FilePath& path)
65     : profile_(profile),
66       path_(path),
67       backend_thread_(NULL),
68       ALLOW_THIS_IN_INITIALIZER_LIST(save_factory_(this)),
69       pending_reset_(false),
70       commands_since_reset_(0) {
71   if (profile) {
72     // We should never be created when incognito.
73     DCHECK(!profile->IsOffTheRecord());
74   }
75   backend_ = new SessionBackend(type,
76       profile_ ? profile_->GetPath() : path_);
77   DCHECK(backend_.get());
78   backend_thread_ = g_browser_process->file_thread();
79   if (!backend_thread_)
80     backend_->Init();
81   // If backend_thread is non-null, backend will init itself as appropriate.
82 }
83 
~BaseSessionService()84 BaseSessionService::~BaseSessionService() {
85 }
86 
DeleteLastSession()87 void BaseSessionService::DeleteLastSession() {
88   if (!backend_thread()) {
89     backend()->DeleteLastSession();
90   } else {
91     backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
92         backend(), &SessionBackend::DeleteLastSession));
93   }
94 }
95 
ScheduleCommand(SessionCommand * command)96 void BaseSessionService::ScheduleCommand(SessionCommand* command) {
97   DCHECK(command);
98   commands_since_reset_++;
99   pending_commands_.push_back(command);
100   StartSaveTimer();
101 }
102 
StartSaveTimer()103 void BaseSessionService::StartSaveTimer() {
104   // Don't start a timer when testing (profile == NULL or
105   // MessageLoop::current() is NULL).
106   if (MessageLoop::current() && profile() && save_factory_.empty()) {
107     MessageLoop::current()->PostDelayedTask(FROM_HERE,
108         save_factory_.NewRunnableMethod(&BaseSessionService::Save),
109         kSaveDelayMS);
110   }
111 }
112 
Save()113 void BaseSessionService::Save() {
114   DCHECK(backend());
115 
116   if (pending_commands_.empty())
117     return;
118 
119   if (!backend_thread()) {
120     backend()->AppendCommands(
121         new std::vector<SessionCommand*>(pending_commands_), pending_reset_);
122   } else {
123     backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
124         backend(), &SessionBackend::AppendCommands,
125         new std::vector<SessionCommand*>(pending_commands_),
126         pending_reset_));
127   }
128   // Backend took ownership of commands.
129   pending_commands_.clear();
130 
131   if (pending_reset_) {
132     commands_since_reset_ = 0;
133     pending_reset_ = false;
134   }
135 }
136 
CreateUpdateTabNavigationCommand(SessionID::id_type command_id,SessionID::id_type tab_id,int index,const NavigationEntry & entry)137 SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand(
138     SessionID::id_type command_id,
139     SessionID::id_type tab_id,
140     int index,
141     const NavigationEntry& entry) {
142   // Use pickle to handle marshalling.
143   Pickle pickle;
144   pickle.WriteInt(tab_id);
145   pickle.WriteInt(index);
146 
147   // We only allow navigations up to 63k (which should be completely
148   // reasonable). On the off chance we get one that is too big, try to
149   // keep the url.
150 
151   // Bound the string data (which is variable length) to
152   // |max_state_size bytes| bytes.
153   static const SessionCommand::size_type max_state_size =
154       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
155 
156   int bytes_written = 0;
157 
158   WriteStringToPickle(pickle, &bytes_written, max_state_size,
159                       entry.virtual_url().spec());
160 
161   WriteString16ToPickle(pickle, &bytes_written, max_state_size, entry.title());
162 
163   if (entry.has_post_data()) {
164     // Remove the form data, it may contain sensitive information.
165     WriteStringToPickle(pickle, &bytes_written, max_state_size,
166         webkit_glue::RemoveFormDataFromHistoryState(entry.content_state()));
167   } else {
168     WriteStringToPickle(pickle, &bytes_written, max_state_size,
169                         entry.content_state());
170   }
171 
172   pickle.WriteInt(entry.transition_type());
173   int type_mask = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0;
174   pickle.WriteInt(type_mask);
175 
176   WriteStringToPickle(pickle, &bytes_written, max_state_size,
177       entry.referrer().is_valid() ? entry.referrer().spec() : std::string());
178 
179   // Adding more data? Be sure and update TabRestoreService too.
180   return new SessionCommand(command_id, pickle);
181 }
182 
CreateSetTabExtensionAppIDCommand(SessionID::id_type command_id,SessionID::id_type tab_id,const std::string & extension_id)183 SessionCommand* BaseSessionService::CreateSetTabExtensionAppIDCommand(
184     SessionID::id_type command_id,
185     SessionID::id_type tab_id,
186     const std::string& extension_id) {
187   // Use pickle to handle marshalling.
188   Pickle pickle;
189   pickle.WriteInt(tab_id);
190 
191   // Enforce a max for ids. They should never be anywhere near this size.
192   static const SessionCommand::size_type max_id_size =
193       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
194 
195   int bytes_written = 0;
196 
197   WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id);
198 
199   return new SessionCommand(command_id, pickle);
200 }
201 
RestoreUpdateTabNavigationCommand(const SessionCommand & command,TabNavigation * navigation,SessionID::id_type * tab_id)202 bool BaseSessionService::RestoreUpdateTabNavigationCommand(
203     const SessionCommand& command,
204     TabNavigation* navigation,
205     SessionID::id_type* tab_id) {
206   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
207   if (!pickle.get())
208     return false;
209   void* iterator = NULL;
210   std::string url_spec;
211   if (!pickle->ReadInt(&iterator, tab_id) ||
212       !pickle->ReadInt(&iterator, &(navigation->index_)) ||
213       !pickle->ReadString(&iterator, &url_spec) ||
214       !pickle->ReadString16(&iterator, &(navigation->title_)) ||
215       !pickle->ReadString(&iterator, &(navigation->state_)) ||
216       !pickle->ReadInt(&iterator,
217                        reinterpret_cast<int*>(&(navigation->transition_))))
218     return false;
219   // type_mask did not always exist in the written stream. As such, we
220   // don't fail if it can't be read.
221   bool has_type_mask = pickle->ReadInt(&iterator, &(navigation->type_mask_));
222 
223   if (has_type_mask) {
224     // the "referrer" property was added after type_mask to the written
225     // stream. As such, we don't fail if it can't be read.
226     std::string referrer_spec;
227     pickle->ReadString(&iterator, &referrer_spec);
228     if (!referrer_spec.empty())
229       navigation->referrer_ = GURL(referrer_spec);
230   }
231 
232   navigation->virtual_url_ = GURL(url_spec);
233   return true;
234 }
235 
RestoreSetTabExtensionAppIDCommand(const SessionCommand & command,SessionID::id_type * tab_id,std::string * extension_app_id)236 bool BaseSessionService::RestoreSetTabExtensionAppIDCommand(
237     const SessionCommand& command,
238     SessionID::id_type* tab_id,
239     std::string* extension_app_id) {
240   scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
241   if (!pickle.get())
242     return false;
243 
244   void* iterator = NULL;
245   return pickle->ReadInt(&iterator, tab_id) &&
246       pickle->ReadString(&iterator, extension_app_id);
247 }
248 
ShouldTrackEntry(const NavigationEntry & entry)249 bool BaseSessionService::ShouldTrackEntry(const NavigationEntry& entry) {
250   return entry.virtual_url().is_valid();
251 }
252 
ShouldTrackEntry(const TabNavigation & navigation)253 bool BaseSessionService::ShouldTrackEntry(const TabNavigation& navigation) {
254   return navigation.virtual_url().is_valid();
255 }
256 
ScheduleGetLastSessionCommands(InternalGetCommandsRequest * request,CancelableRequestConsumerBase * consumer)257 BaseSessionService::Handle BaseSessionService::ScheduleGetLastSessionCommands(
258     InternalGetCommandsRequest* request,
259     CancelableRequestConsumerBase* consumer) {
260   scoped_refptr<InternalGetCommandsRequest> request_wrapper(request);
261   AddRequest(request, consumer);
262   if (backend_thread()) {
263     backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
264         backend(), &SessionBackend::ReadLastSessionCommands, request_wrapper));
265   } else {
266     backend()->ReadLastSessionCommands(request);
267   }
268   return request->handle();
269 }
270 
271 BaseSessionService::Handle
ScheduleGetCurrentSessionCommands(InternalGetCommandsRequest * request,CancelableRequestConsumerBase * consumer)272     BaseSessionService::ScheduleGetCurrentSessionCommands(
273         InternalGetCommandsRequest* request,
274         CancelableRequestConsumerBase* consumer) {
275   scoped_refptr<InternalGetCommandsRequest> request_wrapper(request);
276   AddRequest(request, consumer);
277   if (backend_thread()) {
278     backend_thread()->message_loop()->PostTask(FROM_HERE,
279         NewRunnableMethod(backend(),
280                           &SessionBackend::ReadCurrentSessionCommands,
281                           request_wrapper));
282   } else {
283     backend()->ReadCurrentSessionCommands(request);
284   }
285   return request->handle();
286 }
287