summaryrefslogtreecommitdiffstats
path: root/chrome/browser/session_backend.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/session_backend.cc')
-rw-r--r--chrome/browser/session_backend.cc402
1 files changed, 402 insertions, 0 deletions
diff --git a/chrome/browser/session_backend.cc b/chrome/browser/session_backend.cc
new file mode 100644
index 0000000..01fdf99
--- /dev/null
+++ b/chrome/browser/session_backend.cc
@@ -0,0 +1,402 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/session_backend.h"
+
+#include <limits>
+
+#include "base/file_util.h"
+#include "base/histogram.h"
+#include "base/pickle.h"
+#include "chrome/common/scoped_vector.h"
+
+// File version number.
+static const int32 kFileCurrentVersion = 1;
+
+// The signature at the beginning of the file = SSNS (Sessions).
+static const int32 kFileSignature = 0x53534E53;
+
+namespace {
+
+// SessionFileReader ----------------------------------------------------------
+
+// SessionFileReader is responsible for reading the set of SessionCommands that
+// describe a Session back from a file. SessionFileRead does minimal error
+// checking on the file (pretty much only that the header is valid).
+
+class SessionFileReader {
+ public:
+ typedef SessionCommand::id_type id_type;
+ typedef SessionCommand::size_type size_type;
+
+ SessionFileReader(const std::wstring& path)
+ : handle_(CreateFile(path.c_str(), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)),
+ buffer_(SessionBackend::kFileReadBufferSize, 0),
+ buffer_position_(0),
+ available_count_(0),
+ errored_(false) {
+ }
+ // Reads the contents of the file specified in the constructor, returning
+ // true on success. It is up to the caller to free all SessionCommands
+ // added to commands.
+ bool Read(std::vector<SessionCommand*>* commands);
+
+ private:
+ // Reads a single command, returning it. A return value of NULL indicates
+ // either there are no commands, or there was an error. Use errored_ to
+ // distinguish the two. If NULL is returned, and there is no error, it means
+ // the end of file was sucessfully reached.
+ SessionCommand* ReadCommand();
+
+ // Shifts the unused portion of buffer_ to the beginning and fills the
+ // remaining portion with data from the file. Returns false if the buffer
+ // couldn't be filled. A return value of false only signals an error if
+ // errored_ is set to true.
+ bool FillBuffer();
+
+ // Whether an error condition has been detected (
+ bool errored_;
+
+ // As we read from the file, data goes here.
+ std::string buffer_;
+
+ // The file.
+ ScopedHandle handle_;
+
+ // Position in buffer_ of the data.
+ size_t buffer_position_;
+
+ // Number of available bytes; relative to buffer_position_.
+ size_t available_count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SessionFileReader);
+};
+
+bool SessionFileReader::Read(std::vector<SessionCommand*>* commands) {
+ if (!handle_.IsValid())
+ return false;
+ int32 header[2];
+ DWORD read_count;
+ TimeTicks start_time = TimeTicks::Now();
+ if (!ReadFile(handle_, &header, sizeof(header), &read_count, NULL) ||
+ read_count != sizeof(header) || header[0] != kFileSignature ||
+ header[1] != kFileCurrentVersion)
+ return false;
+
+ ScopedVector<SessionCommand> read_commands;
+ SessionCommand* command;
+ while ((command = ReadCommand()) && !errored_)
+ read_commands->push_back(command);
+ if (!errored_)
+ read_commands->swap(*commands);
+ UMA_HISTOGRAM_TIMES(L"SessionRestore.read_session_file_time",
+ TimeTicks::Now() - start_time);
+ return !errored_;
+}
+
+SessionCommand* SessionFileReader::ReadCommand() {
+ // Make sure there is enough in the buffer for the size of the next command.
+ if (available_count_ < sizeof(size_type)) {
+ if (!FillBuffer())
+ return NULL;
+ if (available_count_ < sizeof(size_type)) {
+ // Still couldn't read a valid size for the command, assume write was
+ // incomplete and return NULL.
+ return NULL;
+ }
+ }
+ // Get the size of the command.
+ size_type command_size;
+ memcpy(&command_size, &(buffer_[buffer_position_]), sizeof(command_size));
+ buffer_position_ += sizeof(command_size);
+ available_count_ -= sizeof(command_size);
+
+ if (command_size == 0) {
+ // Empty command. Shouldn't happen if write was successful, fail.
+ return NULL;
+ }
+
+ // Make sure buffer has the complete contents of the command.
+ if (command_size > available_count_) {
+ if (command_size > buffer_.size())
+ buffer_.resize((command_size / 1024 + 1) * 1024, 0);
+ if (!FillBuffer() || command_size > available_count_) {
+ // Again, assume the file was ok, and just the last chunk was lost.
+ return NULL;
+ }
+ }
+ const id_type command_id = buffer_[buffer_position_];
+ // NOTE: command_size includes the size of the id, which is not part of
+ // the contents of the SessionCommand.
+ SessionCommand* command =
+ new SessionCommand(command_id, command_size - sizeof(id_type));
+ if (command_size > sizeof(id_type)) {
+ memcpy(command->contents(),
+ &(buffer_[buffer_position_ + sizeof(id_type)]),
+ command_size - sizeof(id_type));
+ }
+ buffer_position_ += command_size;
+ available_count_ -= command_size;
+ return command;
+}
+
+bool SessionFileReader::FillBuffer() {
+ if (available_count_ > 0 && buffer_position_ > 0) {
+ // Shift buffer to beginning.
+ memmove(&(buffer_[0]), &(buffer_[buffer_position_]), available_count_);
+ }
+ buffer_position_ = 0;
+ DCHECK(buffer_position_ + available_count_ < buffer_.size());
+ DWORD read_count;
+ if (!ReadFile(handle_, &(buffer_[available_count_]),
+ static_cast<DWORD>(buffer_.size() - available_count_),
+ &read_count, NULL)) {
+ errored_ = true;
+ return false;
+ }
+ if (read_count == 0)
+ return false;
+ available_count_ += static_cast<int>(read_count);
+ return true;
+}
+
+} // namespace
+
+// SessionCommand -------------------------------------------------------------
+
+SessionCommand::SessionCommand(id_type id, size_type size)
+ : id_(id),
+ contents_(size, 0) {
+}
+
+SessionCommand::SessionCommand(id_type id, const Pickle& pickle)
+ : id_(id),
+ contents_(pickle.size(), 0) {
+ DCHECK(pickle.size() < std::numeric_limits<size_type>::max());
+ memcpy(contents(), pickle.data(), pickle.size());
+}
+
+bool SessionCommand::GetPayload(void* dest, size_t count) const {
+ if (size() != count)
+ return false;
+ memcpy(dest, &(contents_[0]), count);
+ return true;
+}
+
+Pickle* SessionCommand::PayloadAsPickle() const {
+ return new Pickle(contents(), static_cast<int>(size()));
+}
+
+// SessionBackend -------------------------------------------------------------
+
+// Target file name.
+static const wchar_t* const kCurrentSessionFileName = L"Current Session";
+
+// Previous target file.
+static const wchar_t* const kLastSessionFileName = L"Last Session";
+
+// Saved session file name.
+static const wchar_t* const kSavedSessionFileName = L"Saved Session";
+
+// static
+const int SessionBackend::kFileReadBufferSize = 1024;
+
+SessionBackend::SessionBackend(const std::wstring& path_to_dir)
+ : path_to_dir_(path_to_dir),
+ last_session_valid_(false),
+ inited_(false),
+ empty_file_(true) {
+ // NOTE: this is invoked on the main thread, don't do file access here.
+}
+
+void SessionBackend::Init() {
+ if (inited_)
+ return;
+
+ inited_ = true;
+
+ // Create the directory for session info.
+ file_util::CreateDirectory(path_to_dir_);
+
+ MoveCurrentSessionToLastSession();
+}
+
+void SessionBackend::SaveSession(
+ const std::vector<SessionCommand*>& commands) {
+ Init();
+ ScopedHandle handle(OpenAndWriteHeader(GetSavedSessionPath()));
+ if (handle.IsValid())
+ AppendCommandsToFile(handle, commands);
+ STLDeleteContainerPointers(commands.begin(), commands.end());
+}
+
+void SessionBackend::AppendCommands(
+ std::vector<SessionCommand*>* commands,
+ bool reset_first) {
+ Init();
+ if ((reset_first && !empty_file_) || !current_session_handle_.IsValid())
+ ResetFile();
+ if (current_session_handle_.IsValid() &&
+ !AppendCommandsToFile(current_session_handle_, *commands)) {
+ current_session_handle_.Set(NULL);
+ }
+ empty_file_ = false;
+ STLDeleteElements(commands);
+ delete commands;
+}
+
+void SessionBackend::ReadSession(
+ scoped_refptr<SessionService::InternalSavedSessionRequest> request) {
+ if (request->canceled())
+ return;
+ Init();
+ ReadSessionImpl(request->is_saved_session, &(request->commands));
+ request->ForwardResult(
+ SessionService::InternalSavedSessionRequest::TupleType(request->handle(),
+ request));
+}
+
+bool SessionBackend::ReadSessionImpl(bool use_save_file,
+ std::vector<SessionCommand*>* commands) {
+ Init();
+ const std::wstring path =
+ use_save_file ? GetSavedSessionPath() : GetLastSessionPath();
+ SessionFileReader file_reader(path);
+ return file_reader.Read(commands);
+}
+
+void SessionBackend::DeleteSession(bool saved_session) {
+ Init();
+ const std::wstring path =
+ saved_session ? GetSavedSessionPath() : GetLastSessionPath();
+ file_util::Delete(path, false);
+}
+
+void SessionBackend::CopyLastSessionToSavedSession() {
+ Init();
+ file_util::CopyFile(GetLastSessionPath(), GetSavedSessionPath());
+}
+
+void SessionBackend::MoveCurrentSessionToLastSession() {
+ Init();
+ current_session_handle_.Set(NULL);
+
+ const std::wstring current_session_path = GetCurrentSessionPath();
+ const std::wstring last_session_path = GetLastSessionPath();
+ if (file_util::PathExists(last_session_path))
+ file_util::Delete(last_session_path, false);
+ if (file_util::PathExists(current_session_path)) {
+ int64 file_size;
+ if (file_util::GetFileSize(current_session_path, &file_size)) {
+ UMA_HISTOGRAM_COUNTS(L"SessionRestore.last_session_file_size",
+ static_cast<int>(file_size / 1024));
+ }
+ last_session_valid_ = file_util::Move(current_session_path,
+ last_session_path);
+ }
+
+ if (file_util::PathExists(current_session_path))
+ file_util::Delete(current_session_path, false);
+
+ // Create and open the file for the current session.
+ ResetFile();
+}
+
+bool SessionBackend::AppendCommandsToFile(
+ HANDLE handle,
+ const std::vector<SessionCommand*>& commands) {
+ for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
+ i != commands.end(); ++i) {
+ DWORD wrote;
+ const size_type content_size = static_cast<size_type>((*i)->size());
+ const size_type total_size = content_size + sizeof(id_type);
+ UMA_HISTOGRAM_COUNTS(L"SessionRestore.command_size", total_size);
+ if (!WriteFile(handle, &total_size, sizeof(total_size), &wrote, NULL) ||
+ wrote != sizeof(total_size)) {
+ NOTREACHED() << "error writing";
+ return false;
+ }
+ id_type command_id = (*i)->id();
+ if (!WriteFile(handle, &command_id, sizeof(command_id), &wrote, NULL) ||
+ wrote != sizeof(command_id)) {
+ NOTREACHED() << "error writing";
+ return false;
+ }
+ if (!WriteFile(handle, (*i)->contents(), content_size, &wrote, NULL) ||
+ wrote != content_size) {
+ NOTREACHED() << "error writing";
+ return false;
+ }
+ }
+ return true;
+}
+
+void SessionBackend::ResetFile() {
+ DCHECK(inited_);
+ // Do Set(NULL) first to make sure we close current file (if open).
+ current_session_handle_.Set(NULL);
+ current_session_handle_.Set(OpenAndWriteHeader(GetCurrentSessionPath()));
+ empty_file_ = true;
+}
+
+HANDLE SessionBackend::OpenAndWriteHeader(const std::wstring& path) {
+ DCHECK(!path.empty());
+ ScopedHandle hfile(
+ CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
+ if (!hfile.IsValid())
+ return NULL;
+ int32 header[2];
+ header[0] = kFileSignature;
+ header[1] = kFileCurrentVersion;
+ DWORD wrote;
+ if (!WriteFile(hfile, &header, sizeof(header), &wrote, NULL) ||
+ wrote != sizeof(header))
+ return NULL;
+ return hfile.Take();
+}
+
+std::wstring SessionBackend::GetLastSessionPath() {
+ std::wstring path = path_to_dir_;
+ file_util::AppendToPath(&path, kLastSessionFileName);
+ return path;
+}
+
+std::wstring SessionBackend::GetSavedSessionPath() {
+ std::wstring path = path_to_dir_;
+ file_util::AppendToPath(&path, kSavedSessionFileName);
+ return path;
+}
+
+std::wstring SessionBackend::GetCurrentSessionPath() {
+ std::wstring path = path_to_dir_;
+ file_util::AppendToPath(&path, kCurrentSessionFileName);
+ return path;
+}