- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / sessions / base_session_service.cc
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 "content/public/browser/browser_thread.h"
18 #include "content/public/browser/navigation_entry.h"
19 #include "content/public/common/referrer.h"
20
21 using content::BrowserThread;
22 using content::NavigationEntry;
23
24 // BaseSessionService ---------------------------------------------------------
25
26 namespace {
27
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);
37   } else {
38     pickle.WriteString(std::string());
39   }
40 }
41
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())
49     return;
50   callback.Run(commands.Pass());
51 }
52
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());
59   } else {
60     task_runner->PostTask(FROM_HERE,
61                           base::Bind(callback, base::Passed(&commands)));
62   }
63 }
64
65 }  // namespace
66
67 // Delay between when a command is received, and when we save it to the
68 // backend.
69 static const int kSaveDelayMS = 2500;
70
71 // static
72 const int BaseSessionService::max_persist_navigation_count = 6;
73
74 BaseSessionService::BaseSessionService(SessionType type,
75                                        Profile* profile,
76                                        const base::FilePath& path)
77     : profile_(profile),
78       weak_factory_(this),
79       pending_reset_(false),
80       commands_since_reset_(0),
81       sequence_token_(
82           content::BrowserThread::GetBlockingPool()->GetSequenceToken()) {
83   if (profile) {
84     // We should never be created when incognito.
85     DCHECK(!profile->IsOffTheRecord());
86   }
87   backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path);
88   DCHECK(backend_.get());
89 }
90
91 BaseSessionService::~BaseSessionService() {
92 }
93
94 void BaseSessionService::DeleteLastSession() {
95   RunTaskOnBackendThread(
96       FROM_HERE,
97       base::Bind(&SessionBackend::DeleteLastSession, backend()));
98 }
99
100 void BaseSessionService::ScheduleCommand(SessionCommand* command) {
101   DCHECK(command);
102   commands_since_reset_++;
103   pending_commands_.push_back(command);
104   StartSaveTimer();
105 }
106
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(
113         FROM_HERE,
114         base::Bind(&BaseSessionService::Save, weak_factory_.GetWeakPtr()),
115         base::TimeDelta::FromMilliseconds(kSaveDelayMS));
116   }
117 }
118
119 void BaseSessionService::Save() {
120   DCHECK(backend());
121
122   if (pending_commands_.empty())
123     return;
124
125   RunTaskOnBackendThread(
126       FROM_HERE,
127       base::Bind(&SessionBackend::AppendCommands, backend(),
128                  new std::vector<SessionCommand*>(pending_commands_),
129                  pending_reset_));
130
131   // Backend took ownership of commands.
132   pending_commands_.clear();
133
134   if (pending_reset_) {
135     commands_since_reset_ = 0;
136     pending_reset_ = false;
137   }
138 }
139
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.
145   Pickle pickle;
146   pickle.WriteInt(tab_id);
147   // We only allow navigations up to 63k (which should be completely
148   // reasonable).
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);
153 }
154
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.
160   Pickle pickle;
161   pickle.WriteInt(tab_id);
162
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;
166
167   int bytes_written = 0;
168
169   WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id);
170
171   return new SessionCommand(command_id, pickle);
172 }
173
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.
179   Pickle pickle;
180   pickle.WriteInt(tab_id);
181
182   // Enforce a max for the user agent length.  They should never be anywhere
183   // near this size.
184   static const SessionCommand::size_type max_user_agent_size =
185       std::numeric_limits<SessionCommand::size_type>::max() - 1024;
186
187   int bytes_written = 0;
188
189   WriteStringToPickle(pickle, &bytes_written, max_user_agent_size,
190       user_agent_override);
191
192   return new SessionCommand(command_id, pickle);
193 }
194
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.
200   Pickle pickle;
201   pickle.WriteInt(window_id);
202
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;
206
207   int bytes_written = 0;
208
209   WriteStringToPickle(pickle, &bytes_written, max_id_size, app_name);
210
211   return new SessionCommand(command_id, pickle);
212 }
213
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());
219   if (!pickle.get())
220     return false;
221   PickleIterator iterator(*pickle);
222   return
223       pickle->ReadInt(&iterator, tab_id) &&
224       navigation->ReadFromPickle(&iterator);
225 }
226
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());
232   if (!pickle.get())
233     return false;
234
235   PickleIterator iterator(*pickle);
236   return pickle->ReadInt(&iterator, tab_id) &&
237       pickle->ReadString(&iterator, extension_app_id);
238 }
239
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());
245   if (!pickle.get())
246     return false;
247
248   PickleIterator iterator(*pickle);
249   return pickle->ReadInt(&iterator, tab_id) &&
250       pickle->ReadString(&iterator, user_agent_override);
251 }
252
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());
258   if (!pickle.get())
259     return false;
260
261   PickleIterator iterator(*pickle);
262   return pickle->ReadInt(&iterator, window_id) &&
263       pickle->ReadString(&iterator, app_name);
264 }
265
266 bool BaseSessionService::ShouldTrackEntry(const GURL& url) {
267   return url.is_valid();
268 }
269
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);
276
277   InternalGetCommandsCallback run_if_not_canceled =
278       base::Bind(&RunIfNotCanceled, is_canceled, callback);
279
280   InternalGetCommandsCallback callback_runner =
281       base::Bind(&PostOrRunInternalGetCommandsCallback,
282                  base::MessageLoopProxy::current(), run_if_not_canceled);
283
284   RunTaskOnBackendThread(
285       FROM_HERE,
286       base::Bind(&SessionBackend::ReadLastSessionCommands, backend(),
287                  is_canceled, callback_runner));
288   return id;
289 }
290
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_,
298                                          from_here,
299                                          task);
300   } else {
301     // Fall back to executing on the main thread if the sequence
302     // worker pool has been requested to shutdown (around shutdown
303     // time).
304     task.Run();
305     return true;
306   }
307 }