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.
5 #include "chrome/browser/session_backend.h"
9 #include "base/file_util.h"
10 #include "base/histogram.h"
11 #include "base/pickle.h"
12 #include "chrome/common/scoped_vector.h"
14 // File version number.
15 static const int32 kFileCurrentVersion
= 1;
17 // The signature at the beginning of the file = SSNS (Sessions).
18 static const int32 kFileSignature
= 0x53534E53;
22 // SessionFileReader ----------------------------------------------------------
24 // SessionFileReader is responsible for reading the set of SessionCommands that
25 // describe a Session back from a file. SessionFileRead does minimal error
26 // checking on the file (pretty much only that the header is valid).
28 class SessionFileReader
{
30 typedef SessionCommand::id_type id_type
;
31 typedef SessionCommand::size_type size_type
;
33 SessionFileReader(const std::wstring
& path
)
34 : handle_(CreateFile(path
.c_str(), GENERIC_READ
, 0, NULL
,
35 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
)),
36 buffer_(SessionBackend::kFileReadBufferSize
, 0),
41 // Reads the contents of the file specified in the constructor, returning
42 // true on success. It is up to the caller to free all SessionCommands
44 bool Read(std::vector
<SessionCommand
*>* commands
);
47 // Reads a single command, returning it. A return value of NULL indicates
48 // either there are no commands, or there was an error. Use errored_ to
49 // distinguish the two. If NULL is returned, and there is no error, it means
50 // the end of file was sucessfully reached.
51 SessionCommand
* ReadCommand();
53 // Shifts the unused portion of buffer_ to the beginning and fills the
54 // remaining portion with data from the file. Returns false if the buffer
55 // couldn't be filled. A return value of false only signals an error if
56 // errored_ is set to true.
59 // Whether an error condition has been detected (
62 // As we read from the file, data goes here.
68 // Position in buffer_ of the data.
69 size_t buffer_position_
;
71 // Number of available bytes; relative to buffer_position_.
72 size_t available_count_
;
74 DISALLOW_EVIL_CONSTRUCTORS(SessionFileReader
);
77 bool SessionFileReader::Read(std::vector
<SessionCommand
*>* commands
) {
78 if (!handle_
.IsValid())
82 TimeTicks start_time
= TimeTicks::Now();
83 if (!ReadFile(handle_
, &header
, sizeof(header
), &read_count
, NULL
) ||
84 read_count
!= sizeof(header
) || header
[0] != kFileSignature
||
85 header
[1] != kFileCurrentVersion
)
88 ScopedVector
<SessionCommand
> read_commands
;
89 SessionCommand
* command
;
90 while ((command
= ReadCommand()) && !errored_
)
91 read_commands
->push_back(command
);
93 read_commands
->swap(*commands
);
94 UMA_HISTOGRAM_TIMES(L
"SessionRestore.read_session_file_time",
95 TimeTicks::Now() - start_time
);
99 SessionCommand
* SessionFileReader::ReadCommand() {
100 // Make sure there is enough in the buffer for the size of the next command.
101 if (available_count_
< sizeof(size_type
)) {
104 if (available_count_
< sizeof(size_type
)) {
105 // Still couldn't read a valid size for the command, assume write was
106 // incomplete and return NULL.
110 // Get the size of the command.
111 size_type command_size
;
112 memcpy(&command_size
, &(buffer_
[buffer_position_
]), sizeof(command_size
));
113 buffer_position_
+= sizeof(command_size
);
114 available_count_
-= sizeof(command_size
);
116 if (command_size
== 0) {
117 // Empty command. Shouldn't happen if write was successful, fail.
121 // Make sure buffer has the complete contents of the command.
122 if (command_size
> available_count_
) {
123 if (command_size
> buffer_
.size())
124 buffer_
.resize((command_size
/ 1024 + 1) * 1024, 0);
125 if (!FillBuffer() || command_size
> available_count_
) {
126 // Again, assume the file was ok, and just the last chunk was lost.
130 const id_type command_id
= buffer_
[buffer_position_
];
131 // NOTE: command_size includes the size of the id, which is not part of
132 // the contents of the SessionCommand.
133 SessionCommand
* command
=
134 new SessionCommand(command_id
, command_size
- sizeof(id_type
));
135 if (command_size
> sizeof(id_type
)) {
136 memcpy(command
->contents(),
137 &(buffer_
[buffer_position_
+ sizeof(id_type
)]),
138 command_size
- sizeof(id_type
));
140 buffer_position_
+= command_size
;
141 available_count_
-= command_size
;
145 bool SessionFileReader::FillBuffer() {
146 if (available_count_
> 0 && buffer_position_
> 0) {
147 // Shift buffer to beginning.
148 memmove(&(buffer_
[0]), &(buffer_
[buffer_position_
]), available_count_
);
150 buffer_position_
= 0;
151 DCHECK(buffer_position_
+ available_count_
< buffer_
.size());
153 if (!ReadFile(handle_
, &(buffer_
[available_count_
]),
154 static_cast<DWORD
>(buffer_
.size() - available_count_
),
155 &read_count
, NULL
)) {
161 available_count_
+= static_cast<int>(read_count
);
167 // SessionCommand -------------------------------------------------------------
169 SessionCommand::SessionCommand(id_type id
, size_type size
)
174 SessionCommand::SessionCommand(id_type id
, const Pickle
& pickle
)
176 contents_(pickle
.size(), 0) {
177 DCHECK(pickle
.size() < std::numeric_limits
<size_type
>::max());
178 memcpy(contents(), pickle
.data(), pickle
.size());
181 bool SessionCommand::GetPayload(void* dest
, size_t count
) const {
184 memcpy(dest
, &(contents_
[0]), count
);
188 Pickle
* SessionCommand::PayloadAsPickle() const {
189 return new Pickle(contents(), static_cast<int>(size()));
192 // SessionBackend -------------------------------------------------------------
195 static const wchar_t* const kCurrentSessionFileName
= L
"Current Session";
197 // Previous target file.
198 static const wchar_t* const kLastSessionFileName
= L
"Last Session";
200 // Saved session file name.
201 static const wchar_t* const kSavedSessionFileName
= L
"Saved Session";
204 const int SessionBackend::kFileReadBufferSize
= 1024;
206 SessionBackend::SessionBackend(const std::wstring
& path_to_dir
)
207 : path_to_dir_(path_to_dir
),
208 last_session_valid_(false),
211 // NOTE: this is invoked on the main thread, don't do file access here.
214 void SessionBackend::Init() {
220 // Create the directory for session info.
221 file_util::CreateDirectory(path_to_dir_
);
223 MoveCurrentSessionToLastSession();
226 void SessionBackend::SaveSession(
227 const std::vector
<SessionCommand
*>& commands
) {
229 ScopedHandle
handle(OpenAndWriteHeader(GetSavedSessionPath()));
230 if (handle
.IsValid())
231 AppendCommandsToFile(handle
, commands
);
232 STLDeleteContainerPointers(commands
.begin(), commands
.end());
235 void SessionBackend::AppendCommands(
236 std::vector
<SessionCommand
*>* commands
,
239 if ((reset_first
&& !empty_file_
) || !current_session_handle_
.IsValid())
241 if (current_session_handle_
.IsValid() &&
242 !AppendCommandsToFile(current_session_handle_
, *commands
)) {
243 current_session_handle_
.Set(NULL
);
246 STLDeleteElements(commands
);
250 void SessionBackend::ReadSession(
251 scoped_refptr
<SessionService::InternalSavedSessionRequest
> request
) {
252 if (request
->canceled())
255 ReadSessionImpl(request
->is_saved_session
, &(request
->commands
));
256 request
->ForwardResult(
257 SessionService::InternalSavedSessionRequest::TupleType(request
->handle(),
261 bool SessionBackend::ReadSessionImpl(bool use_save_file
,
262 std::vector
<SessionCommand
*>* commands
) {
264 const std::wstring path
=
265 use_save_file
? GetSavedSessionPath() : GetLastSessionPath();
266 SessionFileReader
file_reader(path
);
267 return file_reader
.Read(commands
);
270 void SessionBackend::DeleteSession(bool saved_session
) {
272 const std::wstring path
=
273 saved_session
? GetSavedSessionPath() : GetLastSessionPath();
274 file_util::Delete(path
, false);
277 void SessionBackend::CopyLastSessionToSavedSession() {
279 file_util::CopyFile(GetLastSessionPath(), GetSavedSessionPath());
282 void SessionBackend::MoveCurrentSessionToLastSession() {
284 current_session_handle_
.Set(NULL
);
286 const std::wstring current_session_path
= GetCurrentSessionPath();
287 const std::wstring last_session_path
= GetLastSessionPath();
288 if (file_util::PathExists(last_session_path
))
289 file_util::Delete(last_session_path
, false);
290 if (file_util::PathExists(current_session_path
)) {
292 if (file_util::GetFileSize(current_session_path
, &file_size
)) {
293 UMA_HISTOGRAM_COUNTS(L
"SessionRestore.last_session_file_size",
294 static_cast<int>(file_size
/ 1024));
296 last_session_valid_
= file_util::Move(current_session_path
,
300 if (file_util::PathExists(current_session_path
))
301 file_util::Delete(current_session_path
, false);
303 // Create and open the file for the current session.
307 bool SessionBackend::AppendCommandsToFile(
309 const std::vector
<SessionCommand
*>& commands
) {
310 for (std::vector
<SessionCommand
*>::const_iterator i
= commands
.begin();
311 i
!= commands
.end(); ++i
) {
313 const size_type content_size
= static_cast<size_type
>((*i
)->size());
314 const size_type total_size
= content_size
+ sizeof(id_type
);
315 UMA_HISTOGRAM_COUNTS(L
"SessionRestore.command_size", total_size
);
316 if (!WriteFile(handle
, &total_size
, sizeof(total_size
), &wrote
, NULL
) ||
317 wrote
!= sizeof(total_size
)) {
318 NOTREACHED() << "error writing";
321 id_type command_id
= (*i
)->id();
322 if (!WriteFile(handle
, &command_id
, sizeof(command_id
), &wrote
, NULL
) ||
323 wrote
!= sizeof(command_id
)) {
324 NOTREACHED() << "error writing";
327 if (!WriteFile(handle
, (*i
)->contents(), content_size
, &wrote
, NULL
) ||
328 wrote
!= content_size
) {
329 NOTREACHED() << "error writing";
336 void SessionBackend::ResetFile() {
338 // Do Set(NULL) first to make sure we close current file (if open).
339 current_session_handle_
.Set(NULL
);
340 current_session_handle_
.Set(OpenAndWriteHeader(GetCurrentSessionPath()));
344 HANDLE
SessionBackend::OpenAndWriteHeader(const std::wstring
& path
) {
345 DCHECK(!path
.empty());
347 CreateFile(path
.c_str(), GENERIC_WRITE
, 0, NULL
,
348 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
));
349 if (!hfile
.IsValid())
352 header
[0] = kFileSignature
;
353 header
[1] = kFileCurrentVersion
;
355 if (!WriteFile(hfile
, &header
, sizeof(header
), &wrote
, NULL
) ||
356 wrote
!= sizeof(header
))
361 std::wstring
SessionBackend::GetLastSessionPath() {
362 std::wstring path
= path_to_dir_
;
363 file_util::AppendToPath(&path
, kLastSessionFileName
);
367 std::wstring
SessionBackend::GetSavedSessionPath() {
368 std::wstring path
= path_to_dir_
;
369 file_util::AppendToPath(&path
, kSavedSessionFileName
);
373 std::wstring
SessionBackend::GetCurrentSessionPath() {
374 std::wstring path
= path_to_dir_
;
375 file_util::AppendToPath(&path
, kCurrentSessionFileName
);