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. 4 5 #include "chrome/browser/sessions/base_session_service.h" 6 7 #include "base/pickle.h" 8 #include "base/stl_util-inl.h" 9 #include "base/threading/thread.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/sessions/session_backend.h" 13 #include "chrome/browser/sessions/session_types.h" 14 #include "content/browser/tab_contents/navigation_entry.h" 15 #include "webkit/glue/webkit_glue.h" 16 17 // InternalGetCommandsRequest ------------------------------------------------- 18 19 BaseSessionService::InternalGetCommandsRequest::~InternalGetCommandsRequest() { 20 STLDeleteElements(&commands); 21 } 22 23 // BaseSessionService --------------------------------------------------------- 24 25 namespace { 26 27 // Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to 28 // |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|). 29 // |bytes_written| is incremented to reflect the data written. 30 void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes, 31 const std::string& str) { 32 int num_bytes = str.size() * sizeof(char); 33 if (*bytes_written + num_bytes < max_bytes) { 34 *bytes_written += num_bytes; 35 pickle.WriteString(str); 36 } else { 37 pickle.WriteString(std::string()); 38 } 39 } 40 41 // string16 version of WriteStringToPickle. 42 void WriteString16ToPickle(Pickle& pickle, int* bytes_written, int max_bytes, 43 const string16& str) { 44 int num_bytes = str.size() * sizeof(char16); 45 if (*bytes_written + num_bytes < max_bytes) { 46 *bytes_written += num_bytes; 47 pickle.WriteString16(str); 48 } else { 49 pickle.WriteString16(string16()); 50 } 51 } 52 53 } // namespace 54 55 // Delay between when a command is received, and when we save it to the 56 // backend. 57 static const int kSaveDelayMS = 2500; 58 59 // static 60 const int BaseSessionService::max_persist_navigation_count = 6; 61 62 BaseSessionService::BaseSessionService(SessionType type, 63 Profile* profile, 64 const FilePath& path) 65 : profile_(profile), 66 path_(path), 67 backend_thread_(NULL), 68 ALLOW_THIS_IN_INITIALIZER_LIST(save_factory_(this)), 69 pending_reset_(false), 70 commands_since_reset_(0) { 71 if (profile) { 72 // We should never be created when incognito. 73 DCHECK(!profile->IsOffTheRecord()); 74 } 75 backend_ = new SessionBackend(type, 76 profile_ ? profile_->GetPath() : path_); 77 DCHECK(backend_.get()); 78 backend_thread_ = g_browser_process->file_thread(); 79 if (!backend_thread_) 80 backend_->Init(); 81 // If backend_thread is non-null, backend will init itself as appropriate. 82 } 83 84 BaseSessionService::~BaseSessionService() { 85 } 86 87 void BaseSessionService::DeleteLastSession() { 88 if (!backend_thread()) { 89 backend()->DeleteLastSession(); 90 } else { 91 backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 92 backend(), &SessionBackend::DeleteLastSession)); 93 } 94 } 95 96 void BaseSessionService::ScheduleCommand(SessionCommand* command) { 97 DCHECK(command); 98 commands_since_reset_++; 99 pending_commands_.push_back(command); 100 StartSaveTimer(); 101 } 102 103 void BaseSessionService::StartSaveTimer() { 104 // Don't start a timer when testing (profile == NULL or 105 // MessageLoop::current() is NULL). 106 if (MessageLoop::current() && profile() && save_factory_.empty()) { 107 MessageLoop::current()->PostDelayedTask(FROM_HERE, 108 save_factory_.NewRunnableMethod(&BaseSessionService::Save), 109 kSaveDelayMS); 110 } 111 } 112 113 void BaseSessionService::Save() { 114 DCHECK(backend()); 115 116 if (pending_commands_.empty()) 117 return; 118 119 if (!backend_thread()) { 120 backend()->AppendCommands( 121 new std::vector<SessionCommand*>(pending_commands_), pending_reset_); 122 } else { 123 backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 124 backend(), &SessionBackend::AppendCommands, 125 new std::vector<SessionCommand*>(pending_commands_), 126 pending_reset_)); 127 } 128 // Backend took ownership of commands. 129 pending_commands_.clear(); 130 131 if (pending_reset_) { 132 commands_since_reset_ = 0; 133 pending_reset_ = false; 134 } 135 } 136 137 SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand( 138 SessionID::id_type command_id, 139 SessionID::id_type tab_id, 140 int index, 141 const NavigationEntry& entry) { 142 // Use pickle to handle marshalling. 143 Pickle pickle; 144 pickle.WriteInt(tab_id); 145 pickle.WriteInt(index); 146 147 // We only allow navigations up to 63k (which should be completely 148 // reasonable). On the off chance we get one that is too big, try to 149 // keep the url. 150 151 // Bound the string data (which is variable length) to 152 // |max_state_size bytes| bytes. 153 static const SessionCommand::size_type max_state_size = 154 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 155 156 int bytes_written = 0; 157 158 WriteStringToPickle(pickle, &bytes_written, max_state_size, 159 entry.virtual_url().spec()); 160 161 WriteString16ToPickle(pickle, &bytes_written, max_state_size, entry.title()); 162 163 if (entry.has_post_data()) { 164 // Remove the form data, it may contain sensitive information. 165 WriteStringToPickle(pickle, &bytes_written, max_state_size, 166 webkit_glue::RemoveFormDataFromHistoryState(entry.content_state())); 167 } else { 168 WriteStringToPickle(pickle, &bytes_written, max_state_size, 169 entry.content_state()); 170 } 171 172 pickle.WriteInt(entry.transition_type()); 173 int type_mask = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0; 174 pickle.WriteInt(type_mask); 175 176 WriteStringToPickle(pickle, &bytes_written, max_state_size, 177 entry.referrer().is_valid() ? entry.referrer().spec() : std::string()); 178 179 // Adding more data? Be sure and update TabRestoreService too. 180 return new SessionCommand(command_id, pickle); 181 } 182 183 SessionCommand* BaseSessionService::CreateSetTabExtensionAppIDCommand( 184 SessionID::id_type command_id, 185 SessionID::id_type tab_id, 186 const std::string& extension_id) { 187 // Use pickle to handle marshalling. 188 Pickle pickle; 189 pickle.WriteInt(tab_id); 190 191 // Enforce a max for ids. They should never be anywhere near this size. 192 static const SessionCommand::size_type max_id_size = 193 std::numeric_limits<SessionCommand::size_type>::max() - 1024; 194 195 int bytes_written = 0; 196 197 WriteStringToPickle(pickle, &bytes_written, max_id_size, extension_id); 198 199 return new SessionCommand(command_id, pickle); 200 } 201 202 bool BaseSessionService::RestoreUpdateTabNavigationCommand( 203 const SessionCommand& command, 204 TabNavigation* navigation, 205 SessionID::id_type* tab_id) { 206 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 207 if (!pickle.get()) 208 return false; 209 void* iterator = NULL; 210 std::string url_spec; 211 if (!pickle->ReadInt(&iterator, tab_id) || 212 !pickle->ReadInt(&iterator, &(navigation->index_)) || 213 !pickle->ReadString(&iterator, &url_spec) || 214 !pickle->ReadString16(&iterator, &(navigation->title_)) || 215 !pickle->ReadString(&iterator, &(navigation->state_)) || 216 !pickle->ReadInt(&iterator, 217 reinterpret_cast<int*>(&(navigation->transition_)))) 218 return false; 219 // type_mask did not always exist in the written stream. As such, we 220 // don't fail if it can't be read. 221 bool has_type_mask = pickle->ReadInt(&iterator, &(navigation->type_mask_)); 222 223 if (has_type_mask) { 224 // the "referrer" property was added after type_mask to the written 225 // stream. As such, we don't fail if it can't be read. 226 std::string referrer_spec; 227 pickle->ReadString(&iterator, &referrer_spec); 228 if (!referrer_spec.empty()) 229 navigation->referrer_ = GURL(referrer_spec); 230 } 231 232 navigation->virtual_url_ = GURL(url_spec); 233 return true; 234 } 235 236 bool BaseSessionService::RestoreSetTabExtensionAppIDCommand( 237 const SessionCommand& command, 238 SessionID::id_type* tab_id, 239 std::string* extension_app_id) { 240 scoped_ptr<Pickle> pickle(command.PayloadAsPickle()); 241 if (!pickle.get()) 242 return false; 243 244 void* iterator = NULL; 245 return pickle->ReadInt(&iterator, tab_id) && 246 pickle->ReadString(&iterator, extension_app_id); 247 } 248 249 bool BaseSessionService::ShouldTrackEntry(const NavigationEntry& entry) { 250 return entry.virtual_url().is_valid(); 251 } 252 253 bool BaseSessionService::ShouldTrackEntry(const TabNavigation& navigation) { 254 return navigation.virtual_url().is_valid(); 255 } 256 257 BaseSessionService::Handle BaseSessionService::ScheduleGetLastSessionCommands( 258 InternalGetCommandsRequest* request, 259 CancelableRequestConsumerBase* consumer) { 260 scoped_refptr<InternalGetCommandsRequest> request_wrapper(request); 261 AddRequest(request, consumer); 262 if (backend_thread()) { 263 backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 264 backend(), &SessionBackend::ReadLastSessionCommands, request_wrapper)); 265 } else { 266 backend()->ReadLastSessionCommands(request); 267 } 268 return request->handle(); 269 } 270 271 BaseSessionService::Handle 272 BaseSessionService::ScheduleGetCurrentSessionCommands( 273 InternalGetCommandsRequest* request, 274 CancelableRequestConsumerBase* consumer) { 275 scoped_refptr<InternalGetCommandsRequest> request_wrapper(request); 276 AddRequest(request, consumer); 277 if (backend_thread()) { 278 backend_thread()->message_loop()->PostTask(FROM_HERE, 279 NewRunnableMethod(backend(), 280 &SessionBackend::ReadCurrentSessionCommands, 281 request_wrapper)); 282 } else { 283 backend()->ReadCurrentSessionCommands(request); 284 } 285 return request->handle(); 286 } 287