blob: e1667e2945aeb95b14f2692e63349a5ea256c098 [file] [log] [blame]
[email protected]e1cd5452010-08-26 18:03:251// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]f3ec7742009-01-15 00:59:165#include "chrome/browser/tab_contents/navigation_controller.h"
initial.commit09911bf2008-07-26 23:55:296
initial.commit09911bf2008-07-26 23:55:297#include "base/file_util.h"
8#include "base/logging.h"
9#include "base/string_util.h"
[email protected]b689fce72009-03-17 22:45:3410#include "base/time.h"
[email protected]252cad62010-08-18 18:33:5711#include "base/utf_string_conversions.h"
[email protected]cd3d7892009-03-04 23:55:0612#include "chrome/browser/browser_about_handler.h"
initial.commit09911bf2008-07-26 23:55:2913#include "chrome/browser/browser_process.h"
[email protected]9423d9412009-04-14 22:13:5514#include "chrome/browser/browser_url_handler.h"
[email protected]4e6419c2010-01-15 04:50:3415#include "chrome/browser/in_process_webkit/dom_storage_context.h"
16#include "chrome/browser/in_process_webkit/webkit_context.h"
[email protected]37858e52010-08-26 00:22:0217#include "chrome/browser/prefs/pref_service.h"
[email protected]ce560f82009-06-03 09:39:4418#include "chrome/browser/profile.h"
[email protected]14e60c8d2009-06-29 03:56:5119#include "chrome/browser/renderer_host/site_instance.h"
[email protected]169627b2008-12-06 19:30:1920#include "chrome/browser/sessions/session_types.h"
[email protected]25396da2010-03-11 19:19:1021#include "chrome/browser/tab_contents/interstitial_page.h"
[email protected]f3ec7742009-01-15 00:59:1622#include "chrome/browser/tab_contents/navigation_entry.h"
[email protected]5c238752009-06-13 10:29:0723#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]14f3408a2009-08-31 20:53:5324#include "chrome/browser/tab_contents/tab_contents_delegate.h"
[email protected]3cc72b12010-03-18 23:03:0025#include "chrome/common/chrome_constants.h"
[email protected]299dabd2008-11-19 02:27:1626#include "chrome/common/navigation_types.h"
[email protected]bfd04a62009-02-01 18:16:5627#include "chrome/common/notification_service.h"
[email protected]a23de8572009-06-03 02:16:3228#include "chrome/common/pref_names.h"
[email protected]939856a2010-08-24 20:29:0229#include "chrome/common/render_messages_params.h"
[email protected]6de74452009-02-25 18:04:5930#include "chrome/common/url_constants.h"
[email protected]074f10562009-05-21 22:40:0531#include "grit/app_resources.h"
[email protected]a23de8572009-06-03 02:16:3232#include "net/base/escape.h"
33#include "net/base/net_util.h"
[email protected]31682282010-01-15 18:05:1634#include "net/base/mime_util.h"
[email protected]765b35502008-08-21 00:51:2035#include "webkit/glue/webkit_glue.h"
initial.commit09911bf2008-07-26 23:55:2936
[email protected]e9ba4472008-09-14 15:42:4337namespace {
38
[email protected]8030f012009-09-25 18:09:3739const int kInvalidateAllButShelves =
[email protected]6d7a6042010-08-12 20:12:4240 0xFFFFFFFF & ~TabContents::INVALIDATE_BOOKMARK_BAR;
[email protected]8030f012009-09-25 18:09:3741
[email protected]e9ba4472008-09-14 15:42:4342// Invoked when entries have been pruned, or removed. For example, if the
43// current entries are [google, digg, yahoo], with the current entry google,
44// and the user types in cnet, then digg and yahoo are pruned.
[email protected]c12bf1a12008-09-17 16:28:4945void NotifyPrunedEntries(NavigationController* nav_controller,
46 bool from_front,
47 int count) {
48 NavigationController::PrunedDetails details;
49 details.from_front = from_front;
50 details.count = count;
[email protected]e9ba4472008-09-14 15:42:4351 NotificationService::current()->Notify(
[email protected]bfd04a62009-02-01 18:16:5652 NotificationType::NAV_LIST_PRUNED,
[email protected]e9ba4472008-09-14 15:42:4353 Source<NavigationController>(nav_controller),
[email protected]c12bf1a12008-09-17 16:28:4954 Details<NavigationController::PrunedDetails>(&details));
[email protected]e9ba4472008-09-14 15:42:4355}
56
57// Ensure the given NavigationEntry has a valid state, so that WebKit does not
58// get confused if we navigate back to it.
[email protected]40bcc302009-03-02 20:50:3959//
[email protected]e9ba4472008-09-14 15:42:4360// An empty state is treated as a new navigation by WebKit, which would mean
61// losing the navigation entries and generating a new navigation entry after
62// this one. We don't want that. To avoid this we create a valid state which
63// WebKit will not treat as a new navigation.
64void SetContentStateIfEmpty(NavigationEntry* entry) {
[email protected]965524b2009-04-04 21:32:4065 if (entry->content_state().empty()) {
[email protected]e9ba4472008-09-14 15:42:4366 entry->set_content_state(
67 webkit_glue::CreateHistoryStateForURL(entry->url()));
68 }
69}
70
71// Configure all the NavigationEntries in entries for restore. This resets
72// the transition type to reload and makes sure the content state isn't empty.
73void ConfigureEntriesForRestore(
[email protected]5e369672009-11-03 23:48:3074 std::vector<linked_ptr<NavigationEntry> >* entries,
75 bool from_last_session) {
[email protected]e9ba4472008-09-14 15:42:4376 for (size_t i = 0; i < entries->size(); ++i) {
77 // Use a transition type of reload so that we don't incorrectly increase
78 // the typed count.
79 (*entries)[i]->set_transition_type(PageTransition::RELOAD);
[email protected]5e369672009-11-03 23:48:3080 (*entries)[i]->set_restore_type(from_last_session ?
81 NavigationEntry::RESTORE_LAST_SESSION :
82 NavigationEntry::RESTORE_CURRENT_SESSION);
[email protected]e9ba4472008-09-14 15:42:4383 // NOTE(darin): This code is only needed for backwards compat.
84 SetContentStateIfEmpty((*entries)[i].get());
85 }
86}
87
88// See NavigationController::IsURLInPageNavigation for how this works and why.
89bool AreURLsInPageNavigation(const GURL& existing_url, const GURL& new_url) {
[email protected]192d8c5e2010-02-23 07:26:3290 if (existing_url == new_url || !new_url.has_ref()) {
91 // TODO(jcampan): what about when navigating back from a ref URL to the top
92 // non ref URL? Nothing is loaded in that case but we return false here.
93 // The user could also navigate from the ref URL to the non ref URL by
94 // entering the non ref URL in the location bar or through a bookmark, in
95 // which case there would be a load. I am not sure if the non-load/load
96 // scenarios can be differentiated with the TransitionType.
[email protected]e9ba4472008-09-14 15:42:4397 return false;
[email protected]192d8c5e2010-02-23 07:26:3298 }
[email protected]e9ba4472008-09-14 15:42:4399
100 url_canon::Replacements<char> replacements;
101 replacements.ClearRef();
102 return existing_url.ReplaceComponents(replacements) ==
103 new_url.ReplaceComponents(replacements);
104}
105
[email protected]e9ba4472008-09-14 15:42:43106} // namespace
107
initial.commit09911bf2008-07-26 23:55:29108// NavigationController ---------------------------------------------------
109
[email protected]765b35502008-08-21 00:51:20110// static
[email protected]3cc72b12010-03-18 23:03:00111size_t NavigationController::max_entry_count_ =
112 chrome::kMaxSessionHistoryEntries;
[email protected]765b35502008-08-21 00:51:20113
initial.commit09911bf2008-07-26 23:55:29114// static
115bool NavigationController::check_for_repost_ = true;
116
initial.commit09911bf2008-07-26 23:55:29117NavigationController::NavigationController(TabContents* contents,
118 Profile* profile)
119 : profile_(profile),
[email protected]765b35502008-08-21 00:51:20120 pending_entry_(NULL),
121 last_committed_entry_index_(-1),
122 pending_entry_index_(-1),
[email protected]cbab76d2008-10-13 22:42:47123 transient_entry_index_(-1),
[email protected]9423d9412009-04-14 22:13:55124 tab_contents_(contents),
initial.commit09911bf2008-07-26 23:55:29125 max_restored_page_id_(-1),
[email protected]5d063842009-05-15 04:08:24126 ALLOW_THIS_IN_INITIALIZER_LIST(ssl_manager_(this)),
[email protected]38b8f4e2009-09-24 19:44:57127 needs_reload_(false),
[email protected]4e6419c2010-01-15 04:50:34128 session_storage_namespace_id_(profile->GetWebKitContext()->
[email protected]106a0812010-03-18 00:15:12129 dom_storage_context()->AllocateSessionStorageNamespaceId()),
130 pending_reload_(NO_RELOAD) {
initial.commit09911bf2008-07-26 23:55:29131 DCHECK(profile_);
initial.commit09911bf2008-07-26 23:55:29132}
133
initial.commit09911bf2008-07-26 23:55:29134NavigationController::~NavigationController() {
[email protected]cbab76d2008-10-13 22:42:47135 DiscardNonCommittedEntriesInternal();
[email protected]c0993872008-08-21 19:59:44136
[email protected]bfd04a62009-02-01 18:16:56137 NotificationService::current()->Notify(
138 NotificationType::TAB_CLOSED,
139 Source<NavigationController>(this),
140 NotificationService::NoDetails());
[email protected]59afea12010-01-20 04:48:29141
142 // When we go away, the session storage namespace will no longer be reachable.
143 profile_->GetWebKitContext()->DeleteSessionStorageNamespace(
144 session_storage_namespace_id_);
initial.commit09911bf2008-07-26 23:55:29145}
146
[email protected]ce3fa3c2009-04-20 19:55:57147void NavigationController::RestoreFromState(
148 const std::vector<TabNavigation>& navigations,
[email protected]5e369672009-11-03 23:48:30149 int selected_navigation,
150 bool from_last_session) {
[email protected]ce3fa3c2009-04-20 19:55:57151 // Verify that this controller is unused and that the input is valid.
152 DCHECK(entry_count() == 0 && !pending_entry());
153 DCHECK(selected_navigation >= 0 &&
154 selected_navigation < static_cast<int>(navigations.size()));
155
156 // Populate entries_ from the supplied TabNavigations.
157 needs_reload_ = true;
158 CreateNavigationEntriesFromTabNavigations(navigations, &entries_);
159
160 // And finish the restore.
[email protected]5e369672009-11-03 23:48:30161 FinishRestore(selected_navigation, from_last_session);
[email protected]ce3fa3c2009-04-20 19:55:57162}
163
[email protected]f1c74112008-10-30 16:17:04164void NavigationController::Reload(bool check_for_repost) {
[email protected]1ccb3568d2010-02-19 10:51:16165 ReloadInternal(check_for_repost, RELOAD);
166}
167void NavigationController::ReloadIgnoringCache(bool check_for_repost) {
168 ReloadInternal(check_for_repost, RELOAD_IGNORING_CACHE);
169}
170
171void NavigationController::ReloadInternal(bool check_for_repost,
172 ReloadType reload_type) {
[email protected]cbab76d2008-10-13 22:42:47173 // Reloading a transient entry does nothing.
174 if (transient_entry_index_ != -1)
175 return;
176
177 DiscardNonCommittedEntriesInternal();
initial.commit09911bf2008-07-26 23:55:29178 int current_index = GetCurrentEntryIndex();
[email protected]106a0812010-03-18 00:15:12179 // If we are no where, then we can't reload. TODO(darin): We should add a
180 // CanReload method.
181 if (current_index == -1) {
182 return;
183 }
184
[email protected]106a0812010-03-18 00:15:12185 if (check_for_repost_ && check_for_repost &&
[email protected]a3a1d142008-12-19 00:42:30186 GetEntryAtIndex(current_index)->has_post_data()) {
187 // The user is asking to reload a page with POST data. Prompt to make sure
[email protected]b5bb35f2009-02-05 20:17:07188 // they really want to do this. If they do, the dialog will call us back
189 // with check_for_repost = false.
[email protected]965bb092010-04-09 11:59:02190 NotificationService::current()->Notify(
191 NotificationType::REPOST_WARNING_SHOWN,
192 Source<NavigationController>(this),
193 NotificationService::NoDetails());
194
[email protected]106a0812010-03-18 00:15:12195 pending_reload_ = reload_type;
[email protected]9423d9412009-04-14 22:13:55196 tab_contents_->Activate();
[email protected]14f3408a2009-08-31 20:53:53197 tab_contents_->delegate()->ShowRepostFormWarningDialog(tab_contents_);
initial.commit09911bf2008-07-26 23:55:29198 } else {
[email protected]cbab76d2008-10-13 22:42:47199 DiscardNonCommittedEntriesInternal();
[email protected]765b35502008-08-21 00:51:20200
201 pending_entry_index_ = current_index;
[email protected]1e5645ff2008-08-27 18:09:07202 entries_[pending_entry_index_]->set_transition_type(PageTransition::RELOAD);
[email protected]1ccb3568d2010-02-19 10:51:16203 NavigateToPendingEntry(reload_type);
initial.commit09911bf2008-07-26 23:55:29204 }
205}
206
[email protected]106a0812010-03-18 00:15:12207void NavigationController::CancelPendingReload() {
208 DCHECK(pending_reload_ != NO_RELOAD);
209 pending_reload_ = NO_RELOAD;
210}
211
212void NavigationController::ContinuePendingReload() {
213 if (pending_reload_ == NO_RELOAD) {
214 NOTREACHED();
215 } else {
216 ReloadInternal(false, pending_reload_);
[email protected]965bb092010-04-09 11:59:02217 pending_reload_ = NO_RELOAD;
[email protected]106a0812010-03-18 00:15:12218 }
219}
220
[email protected]c70f9b82010-04-21 07:31:11221bool NavigationController::IsInitialNavigation() {
222 return last_document_loaded_.is_null();
223}
224
[email protected]b6ea7412010-05-04 23:26:47225// static
226NavigationEntry* NavigationController::CreateNavigationEntry(
227 const GURL& url, const GURL& referrer, PageTransition::Type transition,
228 Profile* profile) {
229 // Allow the browser URL handler to rewrite the URL. This will, for example,
230 // remove "view-source:" from the beginning of the URL to get the URL that
231 // will actually be loaded. This real URL won't be shown to the user, just
232 // used internally.
233 GURL loaded_url(url);
234 bool reverse_on_redirect = false;
235 BrowserURLHandler::RewriteURLIfNecessary(
236 &loaded_url, profile, &reverse_on_redirect);
237
238 NavigationEntry* entry = new NavigationEntry(
239 NULL, // The site instance for tabs is sent on navigation
240 // (TabContents::GetSiteInstance).
241 -1,
242 loaded_url,
243 referrer,
244 string16(),
245 transition);
246 entry->set_virtual_url(url);
247 entry->set_user_typed_url(url);
248 entry->set_update_virtual_url_with_url(reverse_on_redirect);
249 if (url.SchemeIsFile()) {
[email protected]8d19c7d2010-07-01 23:19:02250 // Use the filename as the title, not the full path.
251 // We need to call FormatUrl() to perform URL de-escaping;
252 // it's a bit ugly to grab the filename out of the resulting string.
[email protected]ddd231e2010-06-29 20:35:19253 std::wstring languages = UTF8ToWide(profile->GetPrefs()->GetString(
254 prefs::kAcceptLanguages));
[email protected]8d19c7d2010-07-01 23:19:02255 std::wstring formatted = net::FormatUrl(url, languages);
256 std::wstring filename =
257 FilePath::FromWStringHack(formatted).BaseName().ToWStringHack();
258 entry->set_title(WideToUTF16Hack(filename));
[email protected]b6ea7412010-05-04 23:26:47259 }
260 return entry;
261}
262
[email protected]765b35502008-08-21 00:51:20263NavigationEntry* NavigationController::GetEntryWithPageID(
[email protected]7f0005a2009-04-15 03:25:11264 SiteInstance* instance, int32 page_id) const {
265 int index = GetEntryIndexWithPageID(instance, page_id);
[email protected]765b35502008-08-21 00:51:20266 return (index != -1) ? entries_[index].get() : NULL;
267}
268
269void NavigationController::LoadEntry(NavigationEntry* entry) {
[email protected]cd3d7892009-03-04 23:55:06270 // Handle non-navigational URLs that popup dialogs and such, these should not
271 // actually navigate.
272 if (HandleNonNavigationAboutURL(entry->url()))
273 return;
274
[email protected]765b35502008-08-21 00:51:20275 // When navigating to a new page, we don't know for sure if we will actually
276 // end up leaving the current page. The new page load could for example
277 // result in a download or a 'no content' response (e.g., a mailto: URL).
[email protected]cbab76d2008-10-13 22:42:47278 DiscardNonCommittedEntriesInternal();
[email protected]765b35502008-08-21 00:51:20279 pending_entry_ = entry;
280 NotificationService::current()->Notify(
[email protected]bfd04a62009-02-01 18:16:56281 NotificationType::NAV_ENTRY_PENDING,
[email protected]765b35502008-08-21 00:51:20282 Source<NavigationController>(this),
283 NotificationService::NoDetails());
[email protected]1ccb3568d2010-02-19 10:51:16284 NavigateToPendingEntry(NO_RELOAD);
[email protected]765b35502008-08-21 00:51:20285}
286
[email protected]765b35502008-08-21 00:51:20287NavigationEntry* NavigationController::GetActiveEntry() const {
[email protected]cbab76d2008-10-13 22:42:47288 if (transient_entry_index_ != -1)
289 return entries_[transient_entry_index_].get();
290 if (pending_entry_)
291 return pending_entry_;
292 return GetLastCommittedEntry();
[email protected]765b35502008-08-21 00:51:20293}
294
295int NavigationController::GetCurrentEntryIndex() const {
[email protected]cbab76d2008-10-13 22:42:47296 if (transient_entry_index_ != -1)
297 return transient_entry_index_;
[email protected]765b35502008-08-21 00:51:20298 if (pending_entry_index_ != -1)
299 return pending_entry_index_;
300 return last_committed_entry_index_;
301}
302
303NavigationEntry* NavigationController::GetLastCommittedEntry() const {
304 if (last_committed_entry_index_ == -1)
305 return NULL;
306 return entries_[last_committed_entry_index_].get();
307}
308
[email protected]31682282010-01-15 18:05:16309bool NavigationController::CanViewSource() const {
310 bool is_supported_mime_type = net::IsSupportedNonImageMimeType(
311 tab_contents_->contents_mime_type().c_str());
312 NavigationEntry* active_entry = GetActiveEntry();
313 return active_entry && !active_entry->IsViewSourceMode() &&
314 is_supported_mime_type;
315}
316
[email protected]765b35502008-08-21 00:51:20317NavigationEntry* NavigationController::GetEntryAtOffset(int offset) const {
[email protected]cbab76d2008-10-13 22:42:47318 int index = (transient_entry_index_ != -1) ?
319 transient_entry_index_ + offset :
320 last_committed_entry_index_ + offset;
[email protected]7f0005a2009-04-15 03:25:11321 if (index < 0 || index >= entry_count())
[email protected]765b35502008-08-21 00:51:20322 return NULL;
323
324 return entries_[index].get();
325}
326
[email protected]765b35502008-08-21 00:51:20327bool NavigationController::CanGoBack() const {
328 return entries_.size() > 1 && GetCurrentEntryIndex() > 0;
329}
330
331bool NavigationController::CanGoForward() const {
332 int index = GetCurrentEntryIndex();
333 return index >= 0 && index < (static_cast<int>(entries_.size()) - 1);
334}
335
336void NavigationController::GoBack() {
337 if (!CanGoBack()) {
338 NOTREACHED();
339 return;
340 }
341
[email protected]25396da2010-03-11 19:19:10342 // If an interstitial page is showing, going back is equivalent to hiding the
343 // interstitial.
344 if (tab_contents_->interstitial_page()) {
345 tab_contents_->interstitial_page()->DontProceed();
346 return;
347 }
348
[email protected]765b35502008-08-21 00:51:20349 // Base the navigation on where we are now...
350 int current_index = GetCurrentEntryIndex();
351
[email protected]cbab76d2008-10-13 22:42:47352 DiscardNonCommittedEntries();
[email protected]765b35502008-08-21 00:51:20353
354 pending_entry_index_ = current_index - 1;
[email protected]1ccb3568d2010-02-19 10:51:16355 NavigateToPendingEntry(NO_RELOAD);
[email protected]765b35502008-08-21 00:51:20356}
357
358void NavigationController::GoForward() {
359 if (!CanGoForward()) {
360 NOTREACHED();
361 return;
362 }
363
[email protected]25396da2010-03-11 19:19:10364 // If an interstitial page is showing, the previous renderer is blocked and
365 // cannot make new requests. Unblock (and disable) it to allow this
366 // navigation to succeed. The interstitial will stay visible until the
367 // resulting DidNavigate.
368 if (tab_contents_->interstitial_page()) {
369 tab_contents_->interstitial_page()->CancelForNavigation();
370 }
371
[email protected]cbab76d2008-10-13 22:42:47372 bool transient = (transient_entry_index_ != -1);
373
[email protected]765b35502008-08-21 00:51:20374 // Base the navigation on where we are now...
375 int current_index = GetCurrentEntryIndex();
376
[email protected]cbab76d2008-10-13 22:42:47377 DiscardNonCommittedEntries();
[email protected]765b35502008-08-21 00:51:20378
[email protected]cbab76d2008-10-13 22:42:47379 pending_entry_index_ = current_index;
380 // If there was a transient entry, we removed it making the current index
381 // the next page.
382 if (!transient)
383 pending_entry_index_++;
384
[email protected]1ccb3568d2010-02-19 10:51:16385 NavigateToPendingEntry(NO_RELOAD);
[email protected]765b35502008-08-21 00:51:20386}
387
388void NavigationController::GoToIndex(int index) {
389 if (index < 0 || index >= static_cast<int>(entries_.size())) {
390 NOTREACHED();
391 return;
392 }
393
[email protected]cbab76d2008-10-13 22:42:47394 if (transient_entry_index_ != -1) {
395 if (index == transient_entry_index_) {
396 // Nothing to do when navigating to the transient.
397 return;
398 }
399 if (index > transient_entry_index_) {
400 // Removing the transient is goint to shift all entries by 1.
401 index--;
402 }
403 }
404
[email protected]25396da2010-03-11 19:19:10405 // If an interstitial page is showing, the previous renderer is blocked and
406 // cannot make new requests.
407 if (tab_contents_->interstitial_page()) {
408 if (index == GetCurrentEntryIndex() - 1) {
409 // Going back one entry is equivalent to hiding the interstitial.
410 tab_contents_->interstitial_page()->DontProceed();
411 return;
412 } else {
413 // Unblock the renderer (and disable the interstitial) to allow this
414 // navigation to succeed. The interstitial will stay visible until the
415 // resulting DidNavigate.
416 tab_contents_->interstitial_page()->CancelForNavigation();
417 }
418 }
419
[email protected]cbab76d2008-10-13 22:42:47420 DiscardNonCommittedEntries();
[email protected]765b35502008-08-21 00:51:20421
422 pending_entry_index_ = index;
[email protected]1ccb3568d2010-02-19 10:51:16423 NavigateToPendingEntry(NO_RELOAD);
[email protected]765b35502008-08-21 00:51:20424}
425
426void NavigationController::GoToOffset(int offset) {
[email protected]cbab76d2008-10-13 22:42:47427 int index = (transient_entry_index_ != -1) ?
428 transient_entry_index_ + offset :
429 last_committed_entry_index_ + offset;
[email protected]7f0005a2009-04-15 03:25:11430 if (index < 0 || index >= entry_count())
[email protected]765b35502008-08-21 00:51:20431 return;
432
433 GoToIndex(index);
434}
435
[email protected]cbab76d2008-10-13 22:42:47436void NavigationController::RemoveEntryAtIndex(int index,
437 const GURL& default_url) {
438 int size = static_cast<int>(entries_.size());
439 DCHECK(index < size);
440
441 DiscardNonCommittedEntries();
442
443 entries_.erase(entries_.begin() + index);
444
445 if (last_committed_entry_index_ == index) {
446 last_committed_entry_index_--;
447 // We removed the currently shown entry, so we have to load something else.
448 if (last_committed_entry_index_ != -1) {
449 pending_entry_index_ = last_committed_entry_index_;
[email protected]1ccb3568d2010-02-19 10:51:16450 NavigateToPendingEntry(NO_RELOAD);
[email protected]cbab76d2008-10-13 22:42:47451 } else {
452 // If there is nothing to show, show a default page.
[email protected]ed3456f2009-02-26 20:24:48453 LoadURL(default_url.is_empty() ? GURL("about:blank") : default_url,
[email protected]c0588052008-10-27 23:01:50454 GURL(), PageTransition::START_PAGE);
[email protected]cbab76d2008-10-13 22:42:47455 }
456 } else if (last_committed_entry_index_ > index) {
457 last_committed_entry_index_--;
458 }
[email protected]cbab76d2008-10-13 22:42:47459}
460
[email protected]38178a42009-12-17 18:58:32461void NavigationController::UpdateVirtualURLToURL(
462 NavigationEntry* entry, const GURL& new_url) {
463 GURL new_virtual_url(new_url);
464 if (BrowserURLHandler::ReverseURLRewrite(
465 &new_virtual_url, entry->virtual_url(), profile_)) {
466 entry->set_virtual_url(new_virtual_url);
467 }
468}
469
[email protected]cbab76d2008-10-13 22:42:47470void NavigationController::AddTransientEntry(NavigationEntry* entry) {
471 // Discard any current transient entry, we can only have one at a time.
472 int index = 0;
473 if (last_committed_entry_index_ != -1)
474 index = last_committed_entry_index_ + 1;
475 DiscardTransientEntry();
476 entries_.insert(entries_.begin() + index, linked_ptr<NavigationEntry>(entry));
[email protected]e1cd5452010-08-26 18:03:25477 transient_entry_index_ = index;
[email protected]8030f012009-09-25 18:09:37478 tab_contents_->NotifyNavigationStateChanged(kInvalidateAllButShelves);
[email protected]cbab76d2008-10-13 22:42:47479}
480
[email protected]c0588052008-10-27 23:01:50481void NavigationController::LoadURL(const GURL& url, const GURL& referrer,
initial.commit09911bf2008-07-26 23:55:29482 PageTransition::Type transition) {
483 // The user initiated a load, we don't need to reload anymore.
484 needs_reload_ = false;
485
[email protected]b6ea7412010-05-04 23:26:47486 NavigationEntry* entry = CreateNavigationEntry(url, referrer, transition,
487 profile_);
initial.commit09911bf2008-07-26 23:55:29488
489 LoadEntry(entry);
490}
491
[email protected]09b8f82f2009-06-16 20:22:11492void NavigationController::DocumentLoadedInFrame() {
493 last_document_loaded_ = base::TimeTicks::Now();
494}
495
[email protected]e9ba4472008-09-14 15:42:43496bool NavigationController::RendererDidNavigate(
497 const ViewHostMsg_FrameNavigate_Params& params,
[email protected]8030f012009-09-25 18:09:37498 int extra_invalidate_flags,
[email protected]e9ba4472008-09-14 15:42:43499 LoadCommittedDetails* details) {
[email protected]4bf3522c2010-08-19 21:00:20500
[email protected]0e8db942008-09-24 21:21:48501 // Save the previous state before we clobber it.
502 if (GetLastCommittedEntry()) {
[email protected]ecd9d8702008-08-28 22:10:17503 details->previous_url = GetLastCommittedEntry()->url();
[email protected]7f0005a2009-04-15 03:25:11504 details->previous_entry_index = last_committed_entry_index();
[email protected]0e8db942008-09-24 21:21:48505 } else {
506 details->previous_url = GURL();
507 details->previous_entry_index = -1;
508 }
[email protected]ecd9d8702008-08-28 22:10:17509
[email protected]e9ba4472008-09-14 15:42:43510 // Assign the current site instance to any pending entry, so we can find it
511 // later by calling GetEntryIndexWithPageID. We only care about this if the
512 // pending entry is an existing navigation and not a new one (or else we
513 // wouldn't care about finding it with GetEntryIndexWithPageID).
514 //
515 // TODO(brettw) this seems slightly bogus as we don't really know if the
516 // pending entry is what this navigation is for. There is a similar TODO
517 // w.r.t. the pending entry in RendererDidNavigateToNewPage.
[email protected]5e369672009-11-03 23:48:30518 if (pending_entry_index_ >= 0) {
[email protected]9423d9412009-04-14 22:13:55519 pending_entry_->set_site_instance(tab_contents_->GetSiteInstance());
[email protected]5e369672009-11-03 23:48:30520 pending_entry_->set_restore_type(NavigationEntry::RESTORE_NONE);
521 }
[email protected]e9ba4472008-09-14 15:42:43522
[email protected]192d8c5e2010-02-23 07:26:32523 // is_in_page must be computed before the entry gets committed.
524 details->is_in_page = IsURLInPageNavigation(params.url);
525
[email protected]e9ba4472008-09-14 15:42:43526 // Do navigation-type specific actions. These will make and commit an entry.
[email protected]0e8db942008-09-24 21:21:48527 details->type = ClassifyNavigation(params);
[email protected]4bf3522c2010-08-19 21:00:20528
[email protected]0e8db942008-09-24 21:21:48529 switch (details->type) {
530 case NavigationType::NEW_PAGE:
[email protected]befd8d822009-07-01 04:51:47531 RendererDidNavigateToNewPage(params, &(details->did_replace_entry));
[email protected]e9ba4472008-09-14 15:42:43532 break;
[email protected]0e8db942008-09-24 21:21:48533 case NavigationType::EXISTING_PAGE:
[email protected]e9ba4472008-09-14 15:42:43534 RendererDidNavigateToExistingPage(params);
535 break;
[email protected]0e8db942008-09-24 21:21:48536 case NavigationType::SAME_PAGE:
[email protected]e9ba4472008-09-14 15:42:43537 RendererDidNavigateToSamePage(params);
538 break;
[email protected]0e8db942008-09-24 21:21:48539 case NavigationType::IN_PAGE:
[email protected]befd8d822009-07-01 04:51:47540 RendererDidNavigateInPage(params, &(details->did_replace_entry));
[email protected]e9ba4472008-09-14 15:42:43541 break;
[email protected]0e8db942008-09-24 21:21:48542 case NavigationType::NEW_SUBFRAME:
[email protected]e9ba4472008-09-14 15:42:43543 RendererDidNavigateNewSubframe(params);
544 break;
[email protected]0e8db942008-09-24 21:21:48545 case NavigationType::AUTO_SUBFRAME:
[email protected]e9ba4472008-09-14 15:42:43546 if (!RendererDidNavigateAutoSubframe(params))
547 return false;
548 break;
[email protected]0e8db942008-09-24 21:21:48549 case NavigationType::NAV_IGNORE:
[email protected]e9ba4472008-09-14 15:42:43550 // There is nothing we can do with this navigation, so we just return to
551 // the caller that nothing has happened.
552 return false;
553 default:
554 NOTREACHED();
[email protected]765b35502008-08-21 00:51:20555 }
556
[email protected]e9ba4472008-09-14 15:42:43557 // All committed entries should have nonempty content state so WebKit doesn't
558 // get confused when we go back to them (see the function for details).
559 SetContentStateIfEmpty(GetActiveEntry());
[email protected]765b35502008-08-21 00:51:20560
[email protected]e9ba4472008-09-14 15:42:43561 // WebKit doesn't set the "auto" transition on meta refreshes properly (bug
562 // 1051891) so we manually set it for redirects which we normally treat as
563 // "non-user-gestures" where we want to update stuff after navigations.
564 //
565 // Note that the redirect check also checks for a pending entry to
566 // differentiate real redirects from browser initiated navigations to a
567 // redirected entry. This happens when you hit back to go to a page that was
568 // the destination of a redirect, we don't want to treat it as a redirect
569 // even though that's what its transition will be. See bug 1117048.
570 //
571 // TODO(brettw) write a test for this complicated logic.
572 details->is_auto = (PageTransition::IsRedirect(params.transition) &&
[email protected]7f0005a2009-04-15 03:25:11573 !pending_entry()) ||
[email protected]e9ba4472008-09-14 15:42:43574 params.gesture == NavigationGestureAuto;
[email protected]765b35502008-08-21 00:51:20575
[email protected]e9ba4472008-09-14 15:42:43576 // Now prep the rest of the details for the notification and broadcast.
577 details->entry = GetActiveEntry();
[email protected]e9ba4472008-09-14 15:42:43578 details->is_main_frame = PageTransition::IsMainFrame(params.transition);
[email protected]f072d2ce2008-09-17 17:16:24579 details->serialized_security_info = params.security_info;
[email protected]8a3422c92008-09-24 17:42:42580 details->is_content_filtered = params.is_content_filtered;
[email protected]2e39d2e2009-02-19 18:41:31581 details->http_status_code = params.http_status_code;
[email protected]8030f012009-09-25 18:09:37582 NotifyNavigationEntryCommitted(details, extra_invalidate_flags);
initial.commit09911bf2008-07-26 23:55:29583
[email protected]e9ba4472008-09-14 15:42:43584 return true;
initial.commit09911bf2008-07-26 23:55:29585}
586
[email protected]0e8db942008-09-24 21:21:48587NavigationType::Type NavigationController::ClassifyNavigation(
[email protected]e9ba4472008-09-14 15:42:43588 const ViewHostMsg_FrameNavigate_Params& params) const {
[email protected]e9ba4472008-09-14 15:42:43589 if (params.page_id == -1) {
[email protected]eef9de32009-09-23 17:19:46590 // The renderer generates the page IDs, and so if it gives us the invalid
591 // page ID (-1) we know it didn't actually navigate. This happens in a few
592 // cases:
593 //
594 // - If a page makes a popup navigated to about blank, and then writes
595 // stuff like a subframe navigated to a real page. We'll get the commit
596 // for the subframe, but there won't be any commit for the outer page.
597 //
598 // - We were also getting these for failed loads (for example, bug 21849).
599 // The guess is that we get a "load commit" for the alternate error page,
600 // but that doesn't affect the page ID, so we get the "old" one, which
601 // could be invalid. This can also happen for a cross-site transition
602 // that causes us to swap processes. Then the error page load will be in
603 // a new process with no page IDs ever assigned (and hence a -1 value),
604 // yet the navigation controller still might have previous pages in its
605 // list.
606 //
607 // In these cases, there's nothing we can do with them, so ignore.
[email protected]0e8db942008-09-24 21:21:48608 return NavigationType::NAV_IGNORE;
[email protected]e9ba4472008-09-14 15:42:43609 }
610
[email protected]9423d9412009-04-14 22:13:55611 if (params.page_id > tab_contents_->GetMaxPageID()) {
[email protected]e9ba4472008-09-14 15:42:43612 // Greater page IDs than we've ever seen before are new pages. We may or may
613 // not have a pending entry for the page, and this may or may not be the
614 // main frame.
615 if (PageTransition::IsMainFrame(params.transition))
[email protected]0e8db942008-09-24 21:21:48616 return NavigationType::NEW_PAGE;
[email protected]4c27ba82008-09-24 16:49:09617
618 // When this is a new subframe navigation, we should have a committed page
619 // for which it's a suframe in. This may not be the case when an iframe is
620 // navigated on a popup navigated to about:blank (the iframe would be
621 // written into the popup by script on the main page). For these cases,
622 // there isn't any navigation stuff we can do, so just ignore it.
623 if (!GetLastCommittedEntry())
[email protected]0e8db942008-09-24 21:21:48624 return NavigationType::NAV_IGNORE;
[email protected]4c27ba82008-09-24 16:49:09625
626 // Valid subframe navigation.
[email protected]0e8db942008-09-24 21:21:48627 return NavigationType::NEW_SUBFRAME;
[email protected]e9ba4472008-09-14 15:42:43628 }
629
630 // Now we know that the notification is for an existing page. Find that entry.
631 int existing_entry_index = GetEntryIndexWithPageID(
[email protected]9423d9412009-04-14 22:13:55632 tab_contents_->GetSiteInstance(),
[email protected]e9ba4472008-09-14 15:42:43633 params.page_id);
634 if (existing_entry_index == -1) {
635 // The page was not found. It could have been pruned because of the limit on
636 // back/forward entries (not likely since we'll usually tell it to navigate
637 // to such entries). It could also mean that the renderer is smoking crack.
638 NOTREACHED();
[email protected]0e8db942008-09-24 21:21:48639 return NavigationType::NAV_IGNORE;
[email protected]e9ba4472008-09-14 15:42:43640 }
641 NavigationEntry* existing_entry = entries_[existing_entry_index].get();
642
[email protected]e6035c22010-05-25 16:15:52643 if (!PageTransition::IsMainFrame(params.transition)) {
644 // All manual subframes would get new IDs and were handled above, so we
645 // know this is auto. Since the current page was found in the navigation
646 // entry list, we're guaranteed to have a last committed entry.
647 DCHECK(GetLastCommittedEntry());
648 return NavigationType::AUTO_SUBFRAME;
649 }
650
651 // Anything below here we know is a main frame navigation.
[email protected]e9ba4472008-09-14 15:42:43652 if (pending_entry_ &&
[email protected]e9ba4472008-09-14 15:42:43653 existing_entry != pending_entry_ &&
[email protected]a0e69262009-06-03 19:08:48654 pending_entry_->page_id() == -1) {
[email protected]e9ba4472008-09-14 15:42:43655 // In this case, we have a pending entry for a URL but WebCore didn't do a
656 // new navigation. This happens when you press enter in the URL bar to
657 // reload. We will create a pending entry, but WebKit will convert it to
658 // a reload since it's the same page and not create a new entry for it
659 // (the user doesn't want to have a new back/forward entry when they do
660 // this). In this case, we want to just ignore the pending entry and go
661 // back to where we were (the "existing entry").
[email protected]0e8db942008-09-24 21:21:48662 return NavigationType::SAME_PAGE;
[email protected]e9ba4472008-09-14 15:42:43663 }
664
[email protected]fc60f222008-12-18 17:36:54665 // Any toplevel navigations with the same base (minus the reference fragment)
666 // are in-page navigations. We weeded out subframe navigations above. Most of
667 // the time this doesn't matter since WebKit doesn't tell us about subframe
668 // navigations that don't actually navigate, but it can happen when there is
669 // an encoding override (it always sends a navigation request).
670 if (AreURLsInPageNavigation(existing_entry->url(), params.url))
671 return NavigationType::IN_PAGE;
672
[email protected]e9ba4472008-09-14 15:42:43673 // Since we weeded out "new" navigations above, we know this is an existing
[email protected]4c27ba82008-09-24 16:49:09674 // (back/forward) navigation.
[email protected]0e8db942008-09-24 21:21:48675 return NavigationType::EXISTING_PAGE;
[email protected]e9ba4472008-09-14 15:42:43676}
677
[email protected]09b8f82f2009-06-16 20:22:11678bool NavigationController::IsRedirect(
679 const ViewHostMsg_FrameNavigate_Params& params) {
680 // For main frame transition, we judge by params.transition.
681 // Otherwise, by params.redirects.
682 if (PageTransition::IsMainFrame(params.transition)) {
683 return PageTransition::IsRedirect(params.transition);
684 }
685 return params.redirects.size() > 1;
686}
687
[email protected]b6ea7412010-05-04 23:26:47688void NavigationController::CreateNavigationEntriesFromTabNavigations(
689 const std::vector<TabNavigation>& navigations,
690 std::vector<linked_ptr<NavigationEntry> >* entries) {
691 // Create a NavigationEntry for each of the navigations.
692 int page_id = 0;
693 for (std::vector<TabNavigation>::const_iterator i =
694 navigations.begin(); i != navigations.end(); ++i, ++page_id) {
695 linked_ptr<NavigationEntry> entry(i->ToNavigationEntry(page_id, profile_));
696 entries->push_back(entry);
697 }
698}
699
[email protected]e9ba4472008-09-14 15:42:43700void NavigationController::RendererDidNavigateToNewPage(
[email protected]befd8d822009-07-01 04:51:47701 const ViewHostMsg_FrameNavigate_Params& params, bool* did_replace_entry) {
[email protected]e9ba4472008-09-14 15:42:43702 NavigationEntry* new_entry;
703 if (pending_entry_) {
704 // TODO(brettw) this assumes that the pending entry is appropriate for the
705 // new page that was just loaded. I don't think this is necessarily the
706 // case! We should have some more tracking to know for sure. This goes along
707 // with a similar TODO at the top of RendererDidNavigate where we blindly
708 // set the site instance on the pending entry.
709 new_entry = new NavigationEntry(*pending_entry_);
710
711 // Don't use the page type from the pending entry. Some interstitial page
712 // may have set the type to interstitial. Once we commit, however, the page
713 // type must always be normal.
714 new_entry->set_page_type(NavigationEntry::NORMAL_PAGE);
715 } else {
[email protected]b680ad22009-04-15 23:19:42716 new_entry = new NavigationEntry;
[email protected]e9ba4472008-09-14 15:42:43717 }
718
719 new_entry->set_url(params.url);
[email protected]38178a42009-12-17 18:58:32720 if (new_entry->update_virtual_url_with_url())
721 UpdateVirtualURLToURL(new_entry, params.url);
[email protected]740fbda2009-02-18 21:38:08722 new_entry->set_referrer(params.referrer);
[email protected]e9ba4472008-09-14 15:42:43723 new_entry->set_page_id(params.page_id);
724 new_entry->set_transition_type(params.transition);
[email protected]9423d9412009-04-14 22:13:55725 new_entry->set_site_instance(tab_contents_->GetSiteInstance());
[email protected]e9ba4472008-09-14 15:42:43726 new_entry->set_has_post_data(params.is_post);
727
[email protected]befd8d822009-07-01 04:51:47728 InsertOrReplaceEntry(new_entry, *did_replace_entry);
[email protected]e9ba4472008-09-14 15:42:43729}
730
731void NavigationController::RendererDidNavigateToExistingPage(
732 const ViewHostMsg_FrameNavigate_Params& params) {
733 // We should only get here for main frame navigations.
734 DCHECK(PageTransition::IsMainFrame(params.transition));
735
736 // This is a back/forward navigation. The existing page for the ID is
[email protected]4c27ba82008-09-24 16:49:09737 // guaranteed to exist by ClassifyNavigation, and we just need to update it
738 // with new information from the renderer.
[email protected]7f0005a2009-04-15 03:25:11739 int entry_index = GetEntryIndexWithPageID(tab_contents_->GetSiteInstance(),
740 params.page_id);
[email protected]e9ba4472008-09-14 15:42:43741 DCHECK(entry_index >= 0 &&
742 entry_index < static_cast<int>(entries_.size()));
743 NavigationEntry* entry = entries_[entry_index].get();
744
745 // The URL may have changed due to redirects. The site instance will normally
746 // be the same except during session restore, when no site instance will be
747 // assigned.
748 entry->set_url(params.url);
[email protected]38178a42009-12-17 18:58:32749 if (entry->update_virtual_url_with_url())
750 UpdateVirtualURLToURL(entry, params.url);
[email protected]e9ba4472008-09-14 15:42:43751 DCHECK(entry->site_instance() == NULL ||
[email protected]9423d9412009-04-14 22:13:55752 entry->site_instance() == tab_contents_->GetSiteInstance());
753 entry->set_site_instance(tab_contents_->GetSiteInstance());
[email protected]e9ba4472008-09-14 15:42:43754
[email protected]d5a49e52010-01-08 03:01:41755 entry->set_has_post_data(params.is_post);
756
[email protected]e9ba4472008-09-14 15:42:43757 // The entry we found in the list might be pending if the user hit
758 // back/forward/reload. This load should commit it (since it's already in the
759 // list, we can just discard the pending pointer).
760 //
761 // Note that we need to use the "internal" version since we don't want to
762 // actually change any other state, just kill the pointer.
763 if (entry == pending_entry_)
[email protected]cbab76d2008-10-13 22:42:47764 DiscardNonCommittedEntriesInternal();
[email protected]40bcc302009-03-02 20:50:39765
[email protected]80858db52009-10-15 00:35:18766 // If a transient entry was removed, the indices might have changed, so we
767 // have to query the entry index again.
768 last_committed_entry_index_ =
769 GetEntryIndexWithPageID(tab_contents_->GetSiteInstance(), params.page_id);
[email protected]e9ba4472008-09-14 15:42:43770}
771
772void NavigationController::RendererDidNavigateToSamePage(
773 const ViewHostMsg_FrameNavigate_Params& params) {
774 // This mode implies we have a pending entry that's the same as an existing
[email protected]4c27ba82008-09-24 16:49:09775 // entry for this page ID. This entry is guaranteed to exist by
776 // ClassifyNavigation. All we need to do is update the existing entry.
[email protected]e9ba4472008-09-14 15:42:43777 NavigationEntry* existing_entry = GetEntryWithPageID(
[email protected]9423d9412009-04-14 22:13:55778 tab_contents_->GetSiteInstance(),
[email protected]e9ba4472008-09-14 15:42:43779 params.page_id);
780
781 // We assign the entry's unique ID to be that of the new one. Since this is
782 // always the result of a user action, we want to dismiss infobars, etc. like
783 // a regular user-initiated navigation.
784 existing_entry->set_unique_id(pending_entry_->unique_id());
785
[email protected]a0e69262009-06-03 19:08:48786 // The URL may have changed due to redirects.
[email protected]38178a42009-12-17 18:58:32787 if (existing_entry->update_virtual_url_with_url())
788 UpdateVirtualURLToURL(existing_entry, params.url);
[email protected]a0e69262009-06-03 19:08:48789 existing_entry->set_url(params.url);
790
[email protected]cbab76d2008-10-13 22:42:47791 DiscardNonCommittedEntries();
[email protected]e9ba4472008-09-14 15:42:43792}
793
794void NavigationController::RendererDidNavigateInPage(
[email protected]befd8d822009-07-01 04:51:47795 const ViewHostMsg_FrameNavigate_Params& params, bool* did_replace_entry) {
[email protected]e9ba4472008-09-14 15:42:43796 DCHECK(PageTransition::IsMainFrame(params.transition)) <<
797 "WebKit should only tell us about in-page navs for the main frame.";
798 // We're guaranteed to have an entry for this one.
799 NavigationEntry* existing_entry = GetEntryWithPageID(
[email protected]9423d9412009-04-14 22:13:55800 tab_contents_->GetSiteInstance(),
[email protected]e9ba4472008-09-14 15:42:43801 params.page_id);
802
803 // Reference fragment navigation. We're guaranteed to have the last_committed
804 // entry and it will be the same page as the new navigation (minus the
805 // reference fragments, of course).
806 NavigationEntry* new_entry = new NavigationEntry(*existing_entry);
807 new_entry->set_page_id(params.page_id);
[email protected]38178a42009-12-17 18:58:32808 if (new_entry->update_virtual_url_with_url())
809 UpdateVirtualURLToURL(new_entry, params.url);
[email protected]e9ba4472008-09-14 15:42:43810 new_entry->set_url(params.url);
[email protected]ccbe04e2010-03-17 17:58:43811
812 // This replaces the existing entry since the page ID didn't change.
813 *did_replace_entry = true;
814 InsertOrReplaceEntry(new_entry, true);
[email protected]e9ba4472008-09-14 15:42:43815}
816
817void NavigationController::RendererDidNavigateNewSubframe(
818 const ViewHostMsg_FrameNavigate_Params& params) {
[email protected]09b8f82f2009-06-16 20:22:11819 if (PageTransition::StripQualifier(params.transition) ==
820 PageTransition::AUTO_SUBFRAME) {
821 // This is not user-initiated. Ignore.
822 return;
823 }
[email protected]09b8f82f2009-06-16 20:22:11824
[email protected]e9ba4472008-09-14 15:42:43825 // Manual subframe navigations just get the current entry cloned so the user
826 // can go back or forward to it. The actual subframe information will be
827 // stored in the page state for each of those entries. This happens out of
828 // band with the actual navigations.
[email protected]4c27ba82008-09-24 16:49:09829 DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee "
830 << "that a last committed entry exists.";
[email protected]e9ba4472008-09-14 15:42:43831 NavigationEntry* new_entry = new NavigationEntry(*GetLastCommittedEntry());
832 new_entry->set_page_id(params.page_id);
[email protected]672eba292009-05-13 13:22:45833 InsertOrReplaceEntry(new_entry, false);
[email protected]e9ba4472008-09-14 15:42:43834}
835
836bool NavigationController::RendererDidNavigateAutoSubframe(
837 const ViewHostMsg_FrameNavigate_Params& params) {
838 // We're guaranteed to have a previously committed entry, and we now need to
839 // handle navigation inside of a subframe in it without creating a new entry.
840 DCHECK(GetLastCommittedEntry());
841
842 // Handle the case where we're navigating back/forward to a previous subframe
843 // navigation entry. This is case "2." in NAV_AUTO_SUBFRAME comment in the
844 // header file. In case "1." this will be a NOP.
845 int entry_index = GetEntryIndexWithPageID(
[email protected]9423d9412009-04-14 22:13:55846 tab_contents_->GetSiteInstance(),
[email protected]e9ba4472008-09-14 15:42:43847 params.page_id);
848 if (entry_index < 0 ||
849 entry_index >= static_cast<int>(entries_.size())) {
850 NOTREACHED();
851 return false;
852 }
853
854 // Update the current navigation entry in case we're going back/forward.
855 if (entry_index != last_committed_entry_index_) {
[email protected]e9ba4472008-09-14 15:42:43856 last_committed_entry_index_ = entry_index;
[email protected]e9ba4472008-09-14 15:42:43857 return true;
858 }
859 return false;
860}
861
[email protected]ce3fa3c2009-04-20 19:55:57862// TODO(brettw) I think this function is unnecessary.
[email protected]e9ba4472008-09-14 15:42:43863void NavigationController::CommitPendingEntry() {
[email protected]cbab76d2008-10-13 22:42:47864 DiscardTransientEntry();
865
[email protected]7f0005a2009-04-15 03:25:11866 if (!pending_entry())
[email protected]e9ba4472008-09-14 15:42:43867 return; // Nothing to do.
868
869 // Need to save the previous URL for the notification.
870 LoadCommittedDetails details;
[email protected]0e8db942008-09-24 21:21:48871 if (GetLastCommittedEntry()) {
[email protected]e9ba4472008-09-14 15:42:43872 details.previous_url = GetLastCommittedEntry()->url();
[email protected]7f0005a2009-04-15 03:25:11873 details.previous_entry_index = last_committed_entry_index();
[email protected]0e8db942008-09-24 21:21:48874 } else {
875 details.previous_entry_index = -1;
876 }
[email protected]e9ba4472008-09-14 15:42:43877
878 if (pending_entry_index_ >= 0) {
879 // This is a previous navigation (back/forward) that we're just now
880 // committing. Just mark it as committed.
[email protected]0e8db942008-09-24 21:21:48881 details.type = NavigationType::EXISTING_PAGE;
[email protected]e9ba4472008-09-14 15:42:43882 int new_entry_index = pending_entry_index_;
[email protected]cbab76d2008-10-13 22:42:47883 DiscardNonCommittedEntriesInternal();
[email protected]e9ba4472008-09-14 15:42:43884
885 // Mark that entry as committed.
[email protected]e9ba4472008-09-14 15:42:43886 last_committed_entry_index_ = new_entry_index;
[email protected]e9ba4472008-09-14 15:42:43887 } else {
888 // This is a new navigation. It's easiest to just copy the entry and insert
[email protected]672eba292009-05-13 13:22:45889 // it new again, since InsertOrReplaceEntry expects to take ownership and
890 // also discard the pending entry. We also need to synthesize a page ID. We
891 // can only do this because this function will only be called by our custom
[email protected]57c6a652009-05-04 07:58:34892 // TabContents types. For TabContents, the IDs are generated by the
[email protected]e9ba4472008-09-14 15:42:43893 // renderer, so we can't do this.
[email protected]0e8db942008-09-24 21:21:48894 details.type = NavigationType::NEW_PAGE;
[email protected]9423d9412009-04-14 22:13:55895 pending_entry_->set_page_id(tab_contents_->GetMaxPageID() + 1);
896 tab_contents_->UpdateMaxPageID(pending_entry_->page_id());
[email protected]672eba292009-05-13 13:22:45897 InsertOrReplaceEntry(new NavigationEntry(*pending_entry_), false);
[email protected]e9ba4472008-09-14 15:42:43898 }
899
900 // Broadcast the notification of the navigation.
901 details.entry = GetActiveEntry();
902 details.is_auto = false;
903 details.is_in_page = AreURLsInPageNavigation(details.previous_url,
904 details.entry->url());
905 details.is_main_frame = true;
[email protected]8030f012009-09-25 18:09:37906 NotifyNavigationEntryCommitted(&details, 0);
[email protected]e9ba4472008-09-14 15:42:43907}
[email protected]765b35502008-08-21 00:51:20908
909int NavigationController::GetIndexOfEntry(
910 const NavigationEntry* entry) const {
911 const NavigationEntries::const_iterator i(std::find(
912 entries_.begin(),
913 entries_.end(),
914 entry));
915 return (i == entries_.end()) ? -1 : static_cast<int>(i - entries_.begin());
916}
917
[email protected]e9ba4472008-09-14 15:42:43918bool NavigationController::IsURLInPageNavigation(const GURL& url) const {
919 NavigationEntry* last_committed = GetLastCommittedEntry();
920 if (!last_committed)
921 return false;
922 return AreURLsInPageNavigation(last_committed->url(), url);
923}
924
[email protected]ce3fa3c2009-04-20 19:55:57925void NavigationController::CopyStateFrom(const NavigationController& source) {
926 // Verify that we look new.
927 DCHECK(entry_count() == 0 && !pending_entry());
928
929 if (source.entry_count() == 0)
930 return; // Nothing new to do.
931
932 needs_reload_ = true;
[email protected]e1cd5452010-08-26 18:03:25933 InsertEntriesFrom(source, source.entry_count());
[email protected]ce3fa3c2009-04-20 19:55:57934
[email protected]4e6419c2010-01-15 04:50:34935 session_storage_namespace_id_ =
936 profile_->GetWebKitContext()->dom_storage_context()->CloneSessionStorage(
937 source.session_storage_namespace_id_);
938
[email protected]5e369672009-11-03 23:48:30939 FinishRestore(source.last_committed_entry_index_, false);
[email protected]ce3fa3c2009-04-20 19:55:57940}
941
[email protected]e1cd5452010-08-26 18:03:25942void NavigationController::CopyStateFromAndPrune(
943 const NavigationController& source) {
944 // This code is intended for use when the last entry is the active entry.
945 DCHECK((transient_entry_index_ != -1 &&
946 transient_entry_index_ == entry_count() - 1) ||
947 (pending_entry_ && (pending_entry_index_ == -1 ||
948 pending_entry_index_ == entry_count() - 1)) ||
949 (!pending_entry_ && last_committed_entry_index_ == entry_count() - 1));
950
951 // Remove all the entries leaving the active entry.
952 PruneAllButActive();
953
954 // Insert the entries from source. Don't use source.GetCurrentEntryIndex as
955 // we don't want to copy over the transient entry.
956 int max_source_index = source.pending_entry_index_ != -1 ?
957 source.pending_entry_index_ : source.last_committed_entry_index_;
958 if (max_source_index == -1)
959 max_source_index = source.entry_count();
960 else
961 max_source_index++;
962 InsertEntriesFrom(source, max_source_index);
963
964 // Adjust indices such that the last entry and pending are at the end now.
965 last_committed_entry_index_ = entry_count() - 1;
966 if (pending_entry_index_ != -1)
967 pending_entry_index_ = entry_count() - 1;
968 if (transient_entry_index_ != -1) {
969 // There's a transient entry. In this case we want the last committed to
970 // point to the previous entry.
971 transient_entry_index_ = entry_count() - 1;
972 if (last_committed_entry_index_ != -1)
973 last_committed_entry_index_--;
974 }
975
976 // Take over the session id from source.
977 session_id_ = source.session_id_;
978}
979
[email protected]cbab76d2008-10-13 22:42:47980void NavigationController::DiscardNonCommittedEntries() {
981 bool transient = transient_entry_index_ != -1;
982 DiscardNonCommittedEntriesInternal();
initial.commit09911bf2008-07-26 23:55:29983
[email protected]cbab76d2008-10-13 22:42:47984 // If there was a transient entry, invalidate everything so the new active
985 // entry state is shown.
986 if (transient) {
[email protected]8030f012009-09-25 18:09:37987 tab_contents_->NotifyNavigationStateChanged(kInvalidateAllButShelves);
[email protected]cbab76d2008-10-13 22:42:47988 }
initial.commit09911bf2008-07-26 23:55:29989}
990
[email protected]672eba292009-05-13 13:22:45991void NavigationController::InsertOrReplaceEntry(NavigationEntry* entry,
992 bool replace) {
[email protected]1e5645ff2008-08-27 18:09:07993 DCHECK(entry->transition_type() != PageTransition::AUTO_SUBFRAME);
[email protected]765b35502008-08-21 00:51:20994
995 // Copy the pending entry's unique ID to the committed entry.
996 // I don't know if pending_entry_index_ can be other than -1 here.
997 const NavigationEntry* const pending_entry = (pending_entry_index_ == -1) ?
998 pending_entry_ : entries_[pending_entry_index_].get();
999 if (pending_entry)
1000 entry->set_unique_id(pending_entry->unique_id());
1001
[email protected]cbab76d2008-10-13 22:42:471002 DiscardNonCommittedEntriesInternal();
[email protected]765b35502008-08-21 00:51:201003
1004 int current_size = static_cast<int>(entries_.size());
1005
[email protected]765b35502008-08-21 00:51:201006 if (current_size > 0) {
[email protected]672eba292009-05-13 13:22:451007 // Prune any entries which are in front of the current entry.
1008 // Also prune the current entry if we are to replace the current entry.
1009 int prune_up_to = replace ? last_committed_entry_index_ - 1
1010 : last_committed_entry_index_;
[email protected]c12bf1a12008-09-17 16:28:491011 int num_pruned = 0;
[email protected]672eba292009-05-13 13:22:451012 while (prune_up_to < (current_size - 1)) {
[email protected]c12bf1a12008-09-17 16:28:491013 num_pruned++;
[email protected]765b35502008-08-21 00:51:201014 entries_.pop_back();
1015 current_size--;
1016 }
[email protected]c12bf1a12008-09-17 16:28:491017 if (num_pruned > 0) // Only notify if we did prune something.
1018 NotifyPrunedEntries(this, false, num_pruned);
[email protected]765b35502008-08-21 00:51:201019 }
1020
[email protected]c12bf1a12008-09-17 16:28:491021 if (entries_.size() >= max_entry_count_) {
[email protected]cbab76d2008-10-13 22:42:471022 RemoveEntryAtIndex(0, GURL());
[email protected]c12bf1a12008-09-17 16:28:491023 NotifyPrunedEntries(this, true, 1);
1024 }
[email protected]765b35502008-08-21 00:51:201025
1026 entries_.push_back(linked_ptr<NavigationEntry>(entry));
1027 last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1;
[email protected]e9ba4472008-09-14 15:42:431028
1029 // This is a new page ID, so we need everybody to know about it.
[email protected]9423d9412009-04-14 22:13:551030 tab_contents_->UpdateMaxPageID(entry->page_id());
initial.commit09911bf2008-07-26 23:55:291031}
1032
1033void NavigationController::SetWindowID(const SessionID& id) {
1034 window_id_ = id;
[email protected]bfd04a62009-02-01 18:16:561035 NotificationService::current()->Notify(NotificationType::TAB_PARENTED,
[email protected]534e54b2008-08-13 15:40:091036 Source<NavigationController>(this),
1037 NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:291038}
1039
[email protected]1ccb3568d2010-02-19 10:51:161040void NavigationController::NavigateToPendingEntry(ReloadType reload_type) {
[email protected]72097fd02010-01-21 23:36:011041 needs_reload_ = false;
1042
initial.commit09911bf2008-07-26 23:55:291043 // For session history navigations only the pending_entry_index_ is set.
1044 if (!pending_entry_) {
[email protected]4bf3522c2010-08-19 21:00:201045 DCHECK_NE(pending_entry_index_, -1);
[email protected]765b35502008-08-21 00:51:201046 pending_entry_ = entries_[pending_entry_index_].get();
initial.commit09911bf2008-07-26 23:55:291047 }
1048
[email protected]1ccb3568d2010-02-19 10:51:161049 if (!tab_contents_->NavigateToPendingEntry(reload_type))
[email protected]cbab76d2008-10-13 22:42:471050 DiscardNonCommittedEntries();
initial.commit09911bf2008-07-26 23:55:291051}
1052
[email protected]ecd9d8702008-08-28 22:10:171053void NavigationController::NotifyNavigationEntryCommitted(
[email protected]8030f012009-09-25 18:09:371054 LoadCommittedDetails* details,
1055 int extra_invalidate_flags) {
[email protected]df1af242009-05-01 00:11:401056 details->entry = GetActiveEntry();
1057 NotificationDetails notification_details =
1058 Details<LoadCommittedDetails>(details);
1059
1060 // We need to notify the ssl_manager_ before the tab_contents_ so the
1061 // location bar will have up-to-date information about the security style
1062 // when it wants to draw. See http://crbug.com/11157
1063 ssl_manager_.DidCommitProvisionalLoad(notification_details);
1064
initial.commit09911bf2008-07-26 23:55:291065 // TODO(pkasting): http://b/1113079 Probably these explicit notification paths
1066 // should be removed, and interested parties should just listen for the
1067 // notification below instead.
[email protected]9423d9412009-04-14 22:13:551068 tab_contents_->NotifyNavigationStateChanged(
[email protected]8030f012009-09-25 18:09:371069 kInvalidateAllButShelves | extra_invalidate_flags);
initial.commit09911bf2008-07-26 23:55:291070
[email protected]ecd9d8702008-08-28 22:10:171071 NotificationService::current()->Notify(
[email protected]bfd04a62009-02-01 18:16:561072 NotificationType::NAV_ENTRY_COMMITTED,
[email protected]ecd9d8702008-08-28 22:10:171073 Source<NavigationController>(this),
[email protected]df1af242009-05-01 00:11:401074 notification_details);
initial.commit09911bf2008-07-26 23:55:291075}
1076
initial.commit09911bf2008-07-26 23:55:291077// static
1078void NavigationController::DisablePromptOnRepost() {
1079 check_for_repost_ = false;
1080}
1081
1082void NavigationController::SetActive(bool is_active) {
[email protected]ee613922009-09-02 20:38:221083 if (is_active && needs_reload_)
1084 LoadIfNecessary();
initial.commit09911bf2008-07-26 23:55:291085}
1086
1087void NavigationController::LoadIfNecessary() {
1088 if (!needs_reload_)
1089 return;
1090
initial.commit09911bf2008-07-26 23:55:291091 // Calling Reload() results in ignoring state, and not loading.
1092 // Explicitly use NavigateToPendingEntry so that the renderer uses the
1093 // cached state.
1094 pending_entry_index_ = last_committed_entry_index_;
[email protected]1ccb3568d2010-02-19 10:51:161095 NavigateToPendingEntry(NO_RELOAD);
initial.commit09911bf2008-07-26 23:55:291096}
1097
[email protected]534e54b2008-08-13 15:40:091098void NavigationController::NotifyEntryChanged(const NavigationEntry* entry,
1099 int index) {
1100 EntryChangedDetails det;
1101 det.changed_entry = entry;
1102 det.index = index;
[email protected]bfd04a62009-02-01 18:16:561103 NotificationService::current()->Notify(NotificationType::NAV_ENTRY_CHANGED,
[email protected]534e54b2008-08-13 15:40:091104 Source<NavigationController>(this),
1105 Details<EntryChangedDetails>(&det));
initial.commit09911bf2008-07-26 23:55:291106}
1107
[email protected]5e369672009-11-03 23:48:301108void NavigationController::FinishRestore(int selected_index,
1109 bool from_last_session) {
[email protected]7f0005a2009-04-15 03:25:111110 DCHECK(selected_index >= 0 && selected_index < entry_count());
[email protected]5e369672009-11-03 23:48:301111 ConfigureEntriesForRestore(&entries_, from_last_session);
initial.commit09911bf2008-07-26 23:55:291112
[email protected]61398152010-08-26 21:45:341113 set_max_restored_page_id(static_cast<int32>(entry_count()));
initial.commit09911bf2008-07-26 23:55:291114
1115 last_committed_entry_index_ = selected_index;
initial.commit09911bf2008-07-26 23:55:291116}
[email protected]765b35502008-08-21 00:51:201117
[email protected]cbab76d2008-10-13 22:42:471118void NavigationController::DiscardNonCommittedEntriesInternal() {
[email protected]765b35502008-08-21 00:51:201119 if (pending_entry_index_ == -1)
1120 delete pending_entry_;
1121 pending_entry_ = NULL;
1122 pending_entry_index_ = -1;
[email protected]cbab76d2008-10-13 22:42:471123
1124 DiscardTransientEntry();
1125}
1126
1127void NavigationController::DiscardTransientEntry() {
1128 if (transient_entry_index_ == -1)
1129 return;
[email protected]a0e69262009-06-03 19:08:481130 entries_.erase(entries_.begin() + transient_entry_index_);
[email protected]80858db52009-10-15 00:35:181131 if (last_committed_entry_index_ > transient_entry_index_)
1132 last_committed_entry_index_--;
[email protected]cbab76d2008-10-13 22:42:471133 transient_entry_index_ = -1;
[email protected]765b35502008-08-21 00:51:201134}
1135
1136int NavigationController::GetEntryIndexWithPageID(
[email protected]7f0005a2009-04-15 03:25:111137 SiteInstance* instance, int32 page_id) const {
[email protected]765b35502008-08-21 00:51:201138 for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) {
[email protected]7f0005a2009-04-15 03:25:111139 if ((entries_[i]->site_instance() == instance) &&
[email protected]1e5645ff2008-08-27 18:09:071140 (entries_[i]->page_id() == page_id))
[email protected]765b35502008-08-21 00:51:201141 return i;
1142 }
1143 return -1;
1144}
[email protected]cbab76d2008-10-13 22:42:471145
1146NavigationEntry* NavigationController::GetTransientEntry() const {
1147 if (transient_entry_index_ == -1)
1148 return NULL;
1149 return entries_[transient_entry_index_].get();
1150}
[email protected]e1cd5452010-08-26 18:03:251151
1152void NavigationController::PruneAllButActive() {
1153 int prune_count = entry_count();
1154 if (transient_entry_index_ != -1) {
1155 // There is a transient entry. Prune up to it.
1156 DCHECK_EQ(entry_count() - 1, transient_entry_index_);
1157 prune_count = transient_entry_index_;
1158 transient_entry_index_ = 0;
1159 last_committed_entry_index_ = -1;
1160 pending_entry_index_ = -1;
1161 } else if (!pending_entry_) {
1162 // There's no pending entry. Leave the last entry (if there is one).
1163 if (!prune_count)
1164 return;
1165
1166 prune_count--;
1167 last_committed_entry_index_ = 0;
1168 } else if (pending_entry_index_ != -1) {
1169 DCHECK_EQ(pending_entry_index_, prune_count - 1);
1170 pending_entry_index_ = 0;
1171 last_committed_entry_index_ = 0;
1172 prune_count--;
1173 } else {
1174 // There is a pending_entry, but it's not in entries_.
1175 pending_entry_index_ = -1;
1176 last_committed_entry_index_ = -1;
1177 }
1178
1179 if (tab_contents_->interstitial_page()) {
1180 // Normally the interstitial page hides itself if the user doesn't proceeed.
1181 // This would result in showing a NavigationEntry we just removed. Set this
1182 // so the interstitial triggers a reload if the user doesn't proceed.
1183 tab_contents_->interstitial_page()->set_reload_on_dont_proceed(true);
1184 }
1185
1186 entries_.erase(entries_.begin(), entries_.begin() + prune_count);
1187}
1188
1189void NavigationController::InsertEntriesFrom(
1190 const NavigationController& source,
1191 int max_index) {
1192 DCHECK_LE(max_index, source.entry_count());
1193 size_t insert_index = 0;
1194 for (int i = 0; i < max_index; i++) {
1195 // When cloning a tab, copy all entries except interstitial pages
1196 if (source.entries_[i].get()->page_type() !=
1197 NavigationEntry::INTERSTITIAL_PAGE) {
1198 entries_.insert(entries_.begin() + insert_index++,
1199 linked_ptr<NavigationEntry>(
1200 new NavigationEntry(*source.entries_[i])));
1201 }
1202 }
1203}