This source file includes following definitions.
- available_count_
- ReadCommand
- FillBuffer
- empty_file_
- Init
- AppendCommands
- ReadLastSessionCommands
- ReadLastSessionCommandsImpl
- DeleteLastSession
- MoveCurrentSessionToLastSession
- ReadCurrentSessionCommandsImpl
- AppendCommandsToFile
- ResetFile
- OpenAndWriteHeader
- GetLastSessionPath
- GetCurrentSessionPath
#include "chrome/browser/sessions/session_backend.h"
#include <limits>
#include "base/file_util.h"
#include "base/files/file.h"
#include "base/memory/scoped_vector.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread_restrictions.h"
using base::TimeTicks;
static const int32 kFileCurrentVersion = 1;
static const int32 kFileSignature = 0x53534E53;
namespace {
struct FileHeader {
int32 signature;
int32 version;
};
class SessionFileReader {
public:
typedef SessionCommand::id_type id_type;
typedef SessionCommand::size_type size_type;
explicit SessionFileReader(const base::FilePath& path)
: errored_(false),
buffer_(SessionBackend::kFileReadBufferSize, 0),
buffer_position_(0),
available_count_(0) {
file_.reset(new base::File(
path, base::File::FLAG_OPEN | base::File::FLAG_READ));
}
bool Read(BaseSessionService::SessionType type,
std::vector<SessionCommand*>* commands);
private:
SessionCommand* ReadCommand();
bool FillBuffer();
bool errored_;
std::string buffer_;
scoped_ptr<base::File> file_;
size_t buffer_position_;
size_t available_count_;
DISALLOW_COPY_AND_ASSIGN(SessionFileReader);
};
bool SessionFileReader::Read(BaseSessionService::SessionType type,
std::vector<SessionCommand*>* commands) {
if (!file_->IsValid())
return false;
FileHeader header;
int read_count;
TimeTicks start_time = TimeTicks::Now();
read_count = file_->ReadAtCurrentPos(reinterpret_cast<char*>(&header),
sizeof(header));
if (read_count != sizeof(header) || header.signature != kFileSignature ||
header.version != kFileCurrentVersion)
return false;
ScopedVector<SessionCommand> read_commands;
SessionCommand* command;
while ((command = ReadCommand()) && !errored_)
read_commands.push_back(command);
if (!errored_)
read_commands.swap(*commands);
if (type == BaseSessionService::TAB_RESTORE) {
UMA_HISTOGRAM_TIMES("TabRestore.read_session_file_time",
TimeTicks::Now() - start_time);
} else {
UMA_HISTOGRAM_TIMES("SessionRestore.read_session_file_time",
TimeTicks::Now() - start_time);
}
return !errored_;
}
SessionCommand* SessionFileReader::ReadCommand() {
if (available_count_ < sizeof(size_type)) {
if (!FillBuffer())
return NULL;
if (available_count_ < sizeof(size_type)) {
VLOG(1) << "SessionFileReader::ReadCommand, file incomplete";
return NULL;
}
}
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) {
VLOG(1) << "SessionFileReader::ReadCommand, empty command";
return NULL;
}
if (command_size > available_count_) {
if (command_size > buffer_.size())
buffer_.resize((command_size / 1024 + 1) * 1024, 0);
if (!FillBuffer() || command_size > available_count_) {
VLOG(1) << "SessionFileReader::ReadCommand, last chunk lost";
return NULL;
}
}
const id_type command_id = buffer_[buffer_position_];
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) {
memmove(&(buffer_[0]), &(buffer_[buffer_position_]), available_count_);
}
buffer_position_ = 0;
DCHECK(buffer_position_ + available_count_ < buffer_.size());
int to_read = static_cast<int>(buffer_.size() - available_count_);
int read_count = file_->ReadAtCurrentPos(&(buffer_[available_count_]),
to_read);
if (read_count < 0) {
errored_ = true;
return false;
}
if (read_count == 0)
return false;
available_count_ += read_count;
return true;
}
}
static const char* kCurrentTabSessionFileName = "Current Tabs";
static const char* kLastTabSessionFileName = "Last Tabs";
static const char* kCurrentSessionFileName = "Current Session";
static const char* kLastSessionFileName = "Last Session";
const int SessionBackend::kFileReadBufferSize = 1024;
SessionBackend::SessionBackend(BaseSessionService::SessionType type,
const base::FilePath& path_to_dir)
: type_(type),
path_to_dir_(path_to_dir),
last_session_valid_(false),
inited_(false),
empty_file_(true) {
}
void SessionBackend::Init() {
if (inited_)
return;
inited_ = true;
base::CreateDirectory(path_to_dir_);
MoveCurrentSessionToLastSession();
}
void SessionBackend::AppendCommands(
std::vector<SessionCommand*>* commands,
bool reset_first) {
Init();
if ((reset_first && !empty_file_) || !current_session_file_.get() ||
!current_session_file_->IsValid()) {
ResetFile();
}
if (current_session_file_.get() && current_session_file_->IsValid() &&
!AppendCommandsToFile(current_session_file_.get(), *commands)) {
current_session_file_.reset(NULL);
}
empty_file_ = false;
STLDeleteElements(commands);
delete commands;
}
void SessionBackend::ReadLastSessionCommands(
const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
const BaseSessionService::InternalGetCommandsCallback& callback) {
if (is_canceled.Run())
return;
Init();
ScopedVector<SessionCommand> commands;
ReadLastSessionCommandsImpl(&(commands.get()));
callback.Run(commands.Pass());
}
bool SessionBackend::ReadLastSessionCommandsImpl(
std::vector<SessionCommand*>* commands) {
Init();
SessionFileReader file_reader(GetLastSessionPath());
return file_reader.Read(type_, commands);
}
void SessionBackend::DeleteLastSession() {
Init();
base::DeleteFile(GetLastSessionPath(), false);
}
void SessionBackend::MoveCurrentSessionToLastSession() {
Init();
current_session_file_.reset(NULL);
const base::FilePath current_session_path = GetCurrentSessionPath();
const base::FilePath last_session_path = GetLastSessionPath();
if (base::PathExists(last_session_path))
base::DeleteFile(last_session_path, false);
if (base::PathExists(current_session_path)) {
int64 file_size;
if (base::GetFileSize(current_session_path, &file_size)) {
if (type_ == BaseSessionService::TAB_RESTORE) {
UMA_HISTOGRAM_COUNTS("TabRestore.last_session_file_size",
static_cast<int>(file_size / 1024));
} else {
UMA_HISTOGRAM_COUNTS("SessionRestore.last_session_file_size",
static_cast<int>(file_size / 1024));
}
}
last_session_valid_ = base::Move(current_session_path, last_session_path);
}
if (base::PathExists(current_session_path))
base::DeleteFile(current_session_path, false);
ResetFile();
}
bool SessionBackend::ReadCurrentSessionCommandsImpl(
std::vector<SessionCommand*>* commands) {
Init();
SessionFileReader file_reader(GetCurrentSessionPath());
return file_reader.Read(type_, commands);
}
bool SessionBackend::AppendCommandsToFile(base::File* file,
const std::vector<SessionCommand*>& commands) {
for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
i != commands.end(); ++i) {
int wrote;
const size_type content_size = static_cast<size_type>((*i)->size());
const size_type total_size = content_size + sizeof(id_type);
if (type_ == BaseSessionService::TAB_RESTORE)
UMA_HISTOGRAM_COUNTS("TabRestore.command_size", total_size);
else
UMA_HISTOGRAM_COUNTS("SessionRestore.command_size", total_size);
wrote = file->WriteAtCurrentPos(reinterpret_cast<const char*>(&total_size),
sizeof(total_size));
if (wrote != sizeof(total_size)) {
NOTREACHED() << "error writing";
return false;
}
id_type command_id = (*i)->id();
wrote = file->WriteAtCurrentPos(reinterpret_cast<char*>(&command_id),
sizeof(command_id));
if (wrote != sizeof(command_id)) {
NOTREACHED() << "error writing";
return false;
}
if (content_size > 0) {
wrote = file->WriteAtCurrentPos(reinterpret_cast<char*>((*i)->contents()),
content_size);
if (wrote != content_size) {
NOTREACHED() << "error writing";
return false;
}
}
#if defined(OS_CHROMEOS)
file->Flush();
#endif
}
return true;
}
SessionBackend::~SessionBackend() {
if (current_session_file_.get()) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
current_session_file_.reset();
}
}
void SessionBackend::ResetFile() {
DCHECK(inited_);
if (current_session_file_.get()) {
const int header_size = static_cast<int>(sizeof(FileHeader));
if (current_session_file_->Seek(
base::File::FROM_BEGIN, header_size) != header_size ||
!current_session_file_->SetLength(header_size))
current_session_file_.reset(NULL);
}
if (!current_session_file_.get())
current_session_file_.reset(OpenAndWriteHeader(GetCurrentSessionPath()));
empty_file_ = true;
}
base::File* SessionBackend::OpenAndWriteHeader(const base::FilePath& path) {
DCHECK(!path.empty());
scoped_ptr<base::File> file(new base::File(
path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_EXCLUSIVE_READ));
if (!file->IsValid())
return NULL;
FileHeader header;
header.signature = kFileSignature;
header.version = kFileCurrentVersion;
int wrote = file->WriteAtCurrentPos(reinterpret_cast<char*>(&header),
sizeof(header));
if (wrote != sizeof(header))
return NULL;
return file.release();
}
base::FilePath SessionBackend::GetLastSessionPath() {
base::FilePath path = path_to_dir_;
if (type_ == BaseSessionService::TAB_RESTORE)
path = path.AppendASCII(kLastTabSessionFileName);
else
path = path.AppendASCII(kLastSessionFileName);
return path;
}
base::FilePath SessionBackend::GetCurrentSessionPath() {
base::FilePath path = path_to_dir_;
if (type_ == BaseSessionService::TAB_RESTORE)
path = path.AppendASCII(kCurrentTabSessionFileName);
else
path = path.AppendASCII(kCurrentSessionFileName);
return path;
}