Fixing build: GetViewContainer changed name from under me. :)
[chromium-blink-merge.git] / chrome / browser / session_backend.cc
blobfd9086ade44f5cc6d29e5d584d6719d836e3bb8e
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"
7 #include <limits>
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;
20 namespace {
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 {
29 public:
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),
37 buffer_position_(0),
38 available_count_(0),
39 errored_(false) {
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
43 // added to commands.
44 bool Read(std::vector<SessionCommand*>* commands);
46 private:
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.
57 bool FillBuffer();
59 // Whether an error condition has been detected (
60 bool errored_;
62 // As we read from the file, data goes here.
63 std::string buffer_;
65 // The file.
66 ScopedHandle handle_;
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())
79 return false;
80 int32 header[2];
81 DWORD read_count;
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)
86 return false;
88 ScopedVector<SessionCommand> read_commands;
89 SessionCommand* command;
90 while ((command = ReadCommand()) && !errored_)
91 read_commands->push_back(command);
92 if (!errored_)
93 read_commands->swap(*commands);
94 UMA_HISTOGRAM_TIMES(L"SessionRestore.read_session_file_time",
95 TimeTicks::Now() - start_time);
96 return !errored_;
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)) {
102 if (!FillBuffer())
103 return NULL;
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.
107 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.
118 return NULL;
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.
127 return NULL;
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;
142 return command;
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());
152 DWORD read_count;
153 if (!ReadFile(handle_, &(buffer_[available_count_]),
154 static_cast<DWORD>(buffer_.size() - available_count_),
155 &read_count, NULL)) {
156 errored_ = true;
157 return false;
159 if (read_count == 0)
160 return false;
161 available_count_ += static_cast<int>(read_count);
162 return true;
165 } // namespace
167 // SessionCommand -------------------------------------------------------------
169 SessionCommand::SessionCommand(id_type id, size_type size)
170 : id_(id),
171 contents_(size, 0) {
174 SessionCommand::SessionCommand(id_type id, const Pickle& pickle)
175 : id_(id),
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 {
182 if (size() != count)
183 return false;
184 memcpy(dest, &(contents_[0]), count);
185 return true;
188 Pickle* SessionCommand::PayloadAsPickle() const {
189 return new Pickle(contents(), static_cast<int>(size()));
192 // SessionBackend -------------------------------------------------------------
194 // Target file name.
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";
203 // static
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),
209 inited_(false),
210 empty_file_(true) {
211 // NOTE: this is invoked on the main thread, don't do file access here.
214 void SessionBackend::Init() {
215 if (inited_)
216 return;
218 inited_ = true;
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) {
228 Init();
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,
237 bool reset_first) {
238 Init();
239 if ((reset_first && !empty_file_) || !current_session_handle_.IsValid())
240 ResetFile();
241 if (current_session_handle_.IsValid() &&
242 !AppendCommandsToFile(current_session_handle_, *commands)) {
243 current_session_handle_.Set(NULL);
245 empty_file_ = false;
246 STLDeleteElements(commands);
247 delete commands;
250 void SessionBackend::ReadSession(
251 scoped_refptr<SessionService::InternalSavedSessionRequest> request) {
252 if (request->canceled())
253 return;
254 Init();
255 ReadSessionImpl(request->is_saved_session, &(request->commands));
256 request->ForwardResult(
257 SessionService::InternalSavedSessionRequest::TupleType(request->handle(),
258 request));
261 bool SessionBackend::ReadSessionImpl(bool use_save_file,
262 std::vector<SessionCommand*>* commands) {
263 Init();
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) {
271 Init();
272 const std::wstring path =
273 saved_session ? GetSavedSessionPath() : GetLastSessionPath();
274 file_util::Delete(path, false);
277 void SessionBackend::CopyLastSessionToSavedSession() {
278 Init();
279 file_util::CopyFile(GetLastSessionPath(), GetSavedSessionPath());
282 void SessionBackend::MoveCurrentSessionToLastSession() {
283 Init();
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)) {
291 int64 file_size;
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,
297 last_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.
304 ResetFile();
307 bool SessionBackend::AppendCommandsToFile(
308 HANDLE handle,
309 const std::vector<SessionCommand*>& commands) {
310 for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
311 i != commands.end(); ++i) {
312 DWORD wrote;
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";
319 return false;
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";
325 return false;
327 if (!WriteFile(handle, (*i)->contents(), content_size, &wrote, NULL) ||
328 wrote != content_size) {
329 NOTREACHED() << "error writing";
330 return false;
333 return true;
336 void SessionBackend::ResetFile() {
337 DCHECK(inited_);
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()));
341 empty_file_ = true;
344 HANDLE SessionBackend::OpenAndWriteHeader(const std::wstring& path) {
345 DCHECK(!path.empty());
346 ScopedHandle hfile(
347 CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL,
348 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
349 if (!hfile.IsValid())
350 return NULL;
351 int32 header[2];
352 header[0] = kFileSignature;
353 header[1] = kFileCurrentVersion;
354 DWORD wrote;
355 if (!WriteFile(hfile, &header, sizeof(header), &wrote, NULL) ||
356 wrote != sizeof(header))
357 return NULL;
358 return hfile.Take();
361 std::wstring SessionBackend::GetLastSessionPath() {
362 std::wstring path = path_to_dir_;
363 file_util::AppendToPath(&path, kLastSessionFileName);
364 return path;
367 std::wstring SessionBackend::GetSavedSessionPath() {
368 std::wstring path = path_to_dir_;
369 file_util::AppendToPath(&path, kSavedSessionFileName);
370 return path;
373 std::wstring SessionBackend::GetCurrentSessionPath() {
374 std::wstring path = path_to_dir_;
375 file_util::AppendToPath(&path, kCurrentSessionFileName);
376 return path;