diff options
author | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-06 19:30:19 +0000 |
---|---|---|
committer | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-06 19:30:19 +0000 |
commit | 169627b81ce036a7014476c366b060e050b5ff70 (patch) | |
tree | 6231d8cfaa065513b4742d99204b25da4178037a /chrome/browser/sessions/base_session_service.cc | |
parent | ee824a436efbdeed4ca78efc4dd2aa4976ba43a9 (diff) | |
download | chromium_src-169627b81ce036a7014476c366b060e050b5ff70.zip chromium_src-169627b81ce036a7014476c366b060e050b5ff70.tar.gz chromium_src-169627b81ce036a7014476c366b060e050b5ff70.tar.bz2 |
Makes the tab restore service persist closed tabs/windows to disk and
reload them when asked.
Sorry for largish looking change. It's made big by refactoring common
code between TabRestoreService and SessionService into a common
superclass. At the same time I removed some dead code and shuffled the
session related classes into a single directory for easier perusal.
BUG=384
TEST=close the browser, start the browser and make sure the new tab
page shows closed windows/tabs from the previous session.
Review URL: http://codereview.chromium.org/13152
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6490 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sessions/base_session_service.cc')
-rw-r--r-- | chrome/browser/sessions/base_session_service.cc | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/chrome/browser/sessions/base_session_service.cc b/chrome/browser/sessions/base_session_service.cc new file mode 100644 index 0000000..b26f7e0 --- /dev/null +++ b/chrome/browser/sessions/base_session_service.cc @@ -0,0 +1,237 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sessions/base_session_service.h" + +#include "base/pickle.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/sessions/session_backend.h" +#include "chrome/browser/sessions/session_types.h" +#include "chrome/browser/tab_contents.h" +#include "chrome/common/stl_util-inl.h" + +// InternalGetCommandsRequest ------------------------------------------------- + +BaseSessionService::InternalGetCommandsRequest::~InternalGetCommandsRequest() { + STLDeleteElements(&commands); +} + +// BaseSessionService --------------------------------------------------------- + +namespace { + +// Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to +// |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|). +// |bytes_written| is incremented to reflect the data written. +void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes, + const std::string& str) { + int num_bytes = str.size() * sizeof(char); + if (*bytes_written + num_bytes < max_bytes) { + *bytes_written += num_bytes; + pickle.WriteString(str); + } else { + pickle.WriteString(std::string()); + } +} + +// Wide version of WriteStringToPickle. +void WriteWStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes, + const std::wstring& str) { + int num_bytes = str.size() * sizeof(wchar_t); + if (*bytes_written + num_bytes < max_bytes) { + *bytes_written += num_bytes; + pickle.WriteWString(str); + } else { + pickle.WriteWString(std::wstring()); + } +} + +} // namespace + +// Delay between when a command is received, and when we save it to the +// backend. +static const int kSaveDelayMS = 2500; + +// static +const int BaseSessionService::max_persist_navigation_count = 6; + +BaseSessionService::BaseSessionService(SessionType type, + Profile* profile, + const std::wstring& path) + : profile_(profile), + path_(path), + backend_thread_(NULL), +#pragma warning(suppress: 4355) // Okay to pass "this" here. + save_factory_(this), + pending_reset_(false), + commands_since_reset_(0) { + if (profile) { + // We should never be created when off the record. + DCHECK(!profile->IsOffTheRecord()); + } + backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path_); + DCHECK(backend_.get()); + backend_thread_ = g_browser_process->file_thread(); + if (!backend_thread_) + backend_->Init(); + // If backend_thread is non-null, backend will init itself as appropriate. +} + +BaseSessionService::~BaseSessionService() { +} + +void BaseSessionService::DeleteLastSession() { + if (!backend_thread()) { + backend()->DeleteLastSession(); + } else { + backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + backend(), &SessionBackend::DeleteLastSession)); + } +} + +void BaseSessionService::ScheduleCommand(SessionCommand* command) { + DCHECK(command); + commands_since_reset_++; + pending_commands_.push_back(command); + StartSaveTimer(); +} + +void BaseSessionService::StartSaveTimer() { + // Don't start a timer when testing (profile == NULL or + // MessageLoop::current() is NULL). + if (MessageLoop::current() && profile() && save_factory_.empty()) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + save_factory_.NewRunnableMethod(&BaseSessionService::Save), + kSaveDelayMS); + } +} + +void BaseSessionService::Save() { + DCHECK(backend()); + + if (pending_commands_.empty()) + return; + + if (!backend_thread()) { + backend()->AppendCommands( + new std::vector<SessionCommand*>(pending_commands_), pending_reset_); + } else { + backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + backend(), &SessionBackend::AppendCommands, + new std::vector<SessionCommand*>(pending_commands_), + pending_reset_)); + } + // Backend took ownership of commands. + pending_commands_.clear(); + + if (pending_reset_) { + commands_since_reset_ = 0; + pending_reset_ = false; + } +} + +SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand( + SessionID::id_type command_id, + SessionID::id_type tab_id, + int index, + const NavigationEntry& entry) { + // Use pickle to handle marshalling. + Pickle pickle; + pickle.WriteInt(tab_id); + pickle.WriteInt(index); + + // We only allow navigations up to 63k (which should be completely + // reasonable). On the off chance we get one that is too big, try to + // keep the url. + + // Bound the string data (which is variable length) to + // |max_state_size bytes| bytes. + static const SessionCommand::size_type max_state_size = + std::numeric_limits<SessionCommand::size_type>::max() - 1024; + + int bytes_written = 0; + + WriteStringToPickle(pickle, &bytes_written, max_state_size, + entry.display_url().spec()); + + WriteWStringToPickle(pickle, &bytes_written, max_state_size, + entry.title()); + + WriteStringToPickle(pickle, &bytes_written, max_state_size, + entry.content_state()); + + pickle.WriteInt(entry.transition_type()); + int type_mask = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0; + pickle.WriteInt(type_mask); + + WriteStringToPickle(pickle, &bytes_written, max_state_size, + entry.referrer().is_valid() ? entry.referrer().spec() : std::string()); + + // Adding more data? Be sure and update TabRestoreService too. + return new SessionCommand(command_id, pickle); +} + +bool BaseSessionService::RestoreUpdateTabNavigationCommand( + const SessionCommand& command, + TabNavigation* navigation, + SessionID::id_type* tab_id) { + scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); + if (!pickle.get()) + return false; + void* iterator = NULL; + std::string url_spec; + if (!pickle->ReadInt(&iterator, tab_id) || + !pickle->ReadInt(&iterator, &(navigation->index_)) || + !pickle->ReadString(&iterator, &url_spec) || + !pickle->ReadWString(&iterator, &(navigation->title_)) || + !pickle->ReadString(&iterator, &(navigation->state_)) || + !pickle->ReadInt(&iterator, + reinterpret_cast<int*>(&(navigation->transition_)))) + return false; + // type_mask did not always exist in the written stream. As such, we + // don't fail if it can't be read. + bool has_type_mask = pickle->ReadInt(&iterator, &(navigation->type_mask_)); + + if (has_type_mask) { + // the "referrer" property was added after type_mask to the written + // stream. As such, we don't fail if it can't be read. + std::string referrer_spec; + pickle->ReadString(&iterator, &referrer_spec); + if (!referrer_spec.empty()) + navigation->referrer_ = GURL(referrer_spec); + } + + navigation->url_ = GURL(url_spec); + return true; +} + +bool BaseSessionService::ShouldTrackEntry(const NavigationEntry& entry) { + // Don't track entries that have post data. Post data may contain passwords + // and other sensitive data users don't want stored to disk. + return entry.display_url().is_valid() && !entry.has_post_data(); +} + +bool BaseSessionService::ShouldTrackEntry(const TabNavigation& navigation) { + // Don't track entries that have post data. Post data may contain passwords + // and other sensitive data users don't want stored to disk. + return navigation.url().is_valid() && + (navigation.type_mask() & TabNavigation::HAS_POST_DATA) == 0; +} + +BaseSessionService::Handle BaseSessionService::ScheduleGetLastSessionCommands( + InternalGetCommandsRequest* request, + CancelableRequestConsumerBase* consumer) { + scoped_refptr<InternalGetCommandsRequest> request_wrapper(request); + AddRequest(request, consumer); + if (backend_thread()) { + backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + backend(), &SessionBackend::ReadLastSessionCommands, request)); + } else { + backend()->ReadLastSessionCommands(request); + } + return request->handle(); +} |