Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/navigation_controller.h" | 5 #include "chrome/browser/navigation_controller.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "chrome/common/navigation_types.h" | 11 #include "chrome/common/navigation_types.h" |
| 12 #include "chrome/common/resource_bundle.h" | 12 #include "chrome/common/resource_bundle.h" |
| 13 #include "chrome/common/scoped_vector.h" | 13 #include "chrome/common/scoped_vector.h" |
| 14 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
| 15 #include "chrome/browser/dom_ui/dom_ui_host.h" | 15 #include "chrome/browser/dom_ui/dom_ui_host.h" |
| 16 #include "chrome/browser/navigation_entry.h" | 16 #include "chrome/browser/navigation_entry.h" |
| 17 #include "chrome/browser/profile.h" | 17 #include "chrome/browser/profile.h" |
| 18 #include "chrome/browser/repost_form_warning_dialog.h" | 18 #include "chrome/browser/repost_form_warning_dialog.h" |
| 19 #include "chrome/browser/site_instance.h" | 19 #include "chrome/browser/site_instance.h" |
| 20 #include "chrome/browser/tab_contents.h" | 20 #include "chrome/browser/tab_contents.h" |
| 21 #include "chrome/browser/tab_contents_delegate.h" | 21 #include "chrome/browser/tab_contents_delegate.h" |
| 22 #include "chrome/common/chrome_switches.h" | 22 #include "chrome/common/chrome_switches.h" |
| 23 #include "net/base/net_util.h" | 23 #include "net/base/net_util.h" |
| 24 #include "webkit/glue/webkit_glue.h" | 24 #include "webkit/glue/webkit_glue.h" |
| 25 | 25 |
| 26 namespace { | |
| 27 | |
| 28 // Invoked when entries have been pruned, or removed. For example, if the | |
| 29 // current entries are [google, digg, yahoo], with the current entry google, | |
| 30 // and the user types in cnet, then digg and yahoo are pruned. | |
| 31 void NotifyPrunedEntries(NavigationController* nav_controller) { | |
| 32 NotificationService::current()->Notify( | |
| 33 NOTIFY_NAV_LIST_PRUNED, | |
| 34 Source<NavigationController>(nav_controller), | |
| 35 NotificationService::NoDetails()); | |
| 36 } | |
| 37 | |
| 38 // Ensure the given NavigationEntry has a valid state, so that WebKit does not | |
| 39 // get confused if we navigate back to it. | |
| 40 // | |
| 41 // An empty state is treated as a new navigation by WebKit, which would mean | |
| 42 // losing the navigation entries and generating a new navigation entry after | |
| 43 // this one. We don't want that. To avoid this we create a valid state which | |
| 44 // WebKit will not treat as a new navigation. | |
| 45 void SetContentStateIfEmpty(NavigationEntry* entry) { | |
| 46 if (entry->content_state().empty() && | |
| 47 (entry->tab_type() == TAB_CONTENTS_WEB || | |
| 48 entry->tab_type() == TAB_CONTENTS_NEW_TAB_UI || | |
| 49 entry->tab_type() == TAB_CONTENTS_ABOUT_UI || | |
| 50 entry->tab_type() == TAB_CONTENTS_HTML_DIALOG)) { | |
| 51 entry->set_content_state( | |
| 52 webkit_glue::CreateHistoryStateForURL(entry->url())); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 // Configure all the NavigationEntries in entries for restore. This resets | |
| 57 // the transition type to reload and makes sure the content state isn't empty. | |
| 58 void ConfigureEntriesForRestore( | |
| 59 std::vector<linked_ptr<NavigationEntry> >* entries) { | |
| 60 for (size_t i = 0; i < entries->size(); ++i) { | |
| 61 // Use a transition type of reload so that we don't incorrectly increase | |
| 62 // the typed count. | |
| 63 (*entries)[i]->set_transition_type(PageTransition::RELOAD); | |
| 64 (*entries)[i]->set_restored(true); | |
| 65 // NOTE(darin): This code is only needed for backwards compat. | |
| 66 SetContentStateIfEmpty((*entries)[i].get()); | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 // See NavigationController::IsURLInPageNavigation for how this works and why. | |
| 71 bool AreURLsInPageNavigation(const GURL& existing_url, const GURL& new_url) { | |
| 72 if (existing_url == new_url || !new_url.has_ref()) | |
| 73 return false; | |
| 74 | |
| 75 url_canon::Replacements<char> replacements; | |
| 76 replacements.ClearRef(); | |
| 77 return existing_url.ReplaceComponents(replacements) == | |
| 78 new_url.ReplaceComponents(replacements); | |
| 79 } | |
| 80 | |
| 81 } // namespace | |
| 82 | |
| 26 // TabContentsCollector --------------------------------------------------- | 83 // TabContentsCollector --------------------------------------------------- |
| 27 | 84 |
| 28 // We never destroy a TabContents synchronously because there are some | 85 // We never destroy a TabContents synchronously because there are some |
| 29 // complex code path that cause the current TabContents to be in the call | 86 // complex code path that cause the current TabContents to be in the call |
| 30 // stack. So instead, we use a TabContentsCollector which either destroys | 87 // stack. So instead, we use a TabContentsCollector which either destroys |
| 31 // the TabContents or does nothing if it has been cancelled. | 88 // the TabContents or does nothing if it has been cancelled. |
| 32 class TabContentsCollector : public Task { | 89 class TabContentsCollector : public Task { |
| 33 public: | 90 public: |
| 34 TabContentsCollector(NavigationController* target, | 91 TabContentsCollector(NavigationController* target, |
| 35 TabContentsType target_type) | 92 TabContentsType target_type) |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 94 // increase the typed count. | 151 // increase the typed count. |
| 95 PageTransition::RELOAD); | 152 PageTransition::RELOAD); |
| 96 entry->set_display_url(navigation.url); | 153 entry->set_display_url(navigation.url); |
| 97 entry->set_content_state(navigation.state); | 154 entry->set_content_state(navigation.state); |
| 98 entry->set_has_post_data( | 155 entry->set_has_post_data( |
| 99 navigation.type_mask & TabNavigation::HAS_POST_DATA); | 156 navigation.type_mask & TabNavigation::HAS_POST_DATA); |
| 100 entries->push_back(linked_ptr<NavigationEntry>(entry)); | 157 entries->push_back(linked_ptr<NavigationEntry>(entry)); |
| 101 } | 158 } |
| 102 } | 159 } |
| 103 | 160 |
| 104 // Configure all the NavigationEntries in entries for restore. This resets | |
| 105 // the transition type to reload and makes sure the content state isn't empty. | |
| 106 static void ConfigureEntriesForRestore( | |
| 107 std::vector<linked_ptr<NavigationEntry> >* entries) { | |
| 108 for (size_t i = 0, count = entries->size(); i < count; ++i) { | |
| 109 // Use a transition type of reload so that we don't incorrectly increase | |
| 110 // the typed count. | |
| 111 (*entries)[i]->set_transition_type(PageTransition::RELOAD); | |
| 112 (*entries)[i]->set_restored(true); | |
| 113 // NOTE(darin): This code is only needed for backwards compat. | |
| 114 NavigationController::SetContentStateIfEmpty((*entries)[i].get()); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 NavigationController::NavigationController(TabContents* contents, | 161 NavigationController::NavigationController(TabContents* contents, |
| 119 Profile* profile) | 162 Profile* profile) |
| 120 : profile_(profile), | 163 : profile_(profile), |
| 121 pending_entry_(NULL), | 164 pending_entry_(NULL), |
| 122 last_committed_entry_index_(-1), | 165 last_committed_entry_index_(-1), |
| 123 pending_entry_index_(-1), | 166 pending_entry_index_(-1), |
| 124 max_entry_count_(kMaxEntryCount), | 167 max_entry_count_(kMaxEntryCount), |
| 125 active_contents_(contents), | 168 active_contents_(contents), |
| 126 alternate_nav_url_fetcher_entry_unique_id_(0), | 169 alternate_nav_url_fetcher_entry_unique_id_(0), |
| 127 max_restored_page_id_(-1), | 170 max_restored_page_id_(-1), |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 175 NotificationService::NoDetails()); | 218 NotificationService::NoDetails()); |
| 176 } | 219 } |
| 177 | 220 |
| 178 TabContents* NavigationController::GetTabContents(TabContentsType t) { | 221 TabContents* NavigationController::GetTabContents(TabContentsType t) { |
| 179 // Make sure the TabContents is no longer scheduled for collection. | 222 // Make sure the TabContents is no longer scheduled for collection. |
| 180 CancelTabContentsCollection(t); | 223 CancelTabContentsCollection(t); |
| 181 return tab_contents_map_[t]; | 224 return tab_contents_map_[t]; |
| 182 } | 225 } |
| 183 | 226 |
| 184 void NavigationController::Reload() { | 227 void NavigationController::Reload() { |
| 185 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? | |
| 186 DiscardPendingEntryInternal(); | 228 DiscardPendingEntryInternal(); |
| 187 int current_index = GetCurrentEntryIndex(); | 229 int current_index = GetCurrentEntryIndex(); |
| 188 if (check_for_repost_ && current_index != -1 && | 230 if (check_for_repost_ && current_index != -1 && |
| 189 GetEntryAtIndex(current_index)->has_post_data() && | 231 GetEntryAtIndex(current_index)->has_post_data() && |
| 190 active_contents_->AsWebContents() && | 232 active_contents_->AsWebContents() && |
| 191 !active_contents_->AsWebContents()->showing_repost_interstitial()) { | 233 !active_contents_->AsWebContents()->showing_repost_interstitial()) { |
| 192 // The user is asking to reload a page with POST data and we're not showing | 234 // The user is asking to reload a page with POST data and we're not showing |
| 193 // the POST interstitial. Prompt to make sure they really want to do this. | 235 // the POST interstitial. Prompt to make sure they really want to do this. |
| 194 // If they do, RepostFormWarningDialog calls us back with | 236 // If they do, RepostFormWarningDialog calls us back with |
| 195 // ReloadDontCheckForRepost. | 237 // ReloadDontCheckForRepost. |
| 196 active_contents_->Activate(); | 238 active_contents_->Activate(); |
| 197 RepostFormWarningDialog::RunRepostFormWarningDialog(this); | 239 RepostFormWarningDialog::RunRepostFormWarningDialog(this); |
| 198 } else { | 240 } else { |
| 199 // Base the navigation on where we are now... | 241 // Base the navigation on where we are now... |
| 200 int current_index = GetCurrentEntryIndex(); | 242 int current_index = GetCurrentEntryIndex(); |
| 201 | 243 |
| 202 // If we are no where, then we can't reload. TODO(darin): We should add a | 244 // If we are no where, then we can't reload. TODO(darin): We should add a |
| 203 // CanReload method. | 245 // CanReload method. |
| 204 if (current_index == -1) | 246 if (current_index == -1) |
| 205 return; | 247 return; |
| 206 | 248 |
| 207 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? | |
| 208 DiscardPendingEntryInternal(); | 249 DiscardPendingEntryInternal(); |
| 209 | 250 |
| 210 pending_entry_index_ = current_index; | 251 pending_entry_index_ = current_index; |
| 211 entries_[pending_entry_index_]->set_transition_type(PageTransition::RELOAD); | 252 entries_[pending_entry_index_]->set_transition_type(PageTransition::RELOAD); |
| 212 NavigateToPendingEntry(true); | 253 NavigateToPendingEntry(true); |
| 213 } | 254 } |
| 214 } | 255 } |
| 215 | 256 |
| 216 NavigationEntry* NavigationController::GetEntryWithPageID( | 257 NavigationEntry* NavigationController::GetEntryWithPageID( |
| 217 TabContentsType type, SiteInstance* instance, int32 page_id) const { | 258 TabContentsType type, SiteInstance* instance, int32 page_id) const { |
| 218 int index = GetEntryIndexWithPageID(type, instance, page_id); | 259 int index = GetEntryIndexWithPageID(type, instance, page_id); |
| 219 return (index != -1) ? entries_[index].get() : NULL; | 260 return (index != -1) ? entries_[index].get() : NULL; |
| 220 } | 261 } |
| 221 | 262 |
| 222 void NavigationController::LoadEntry(NavigationEntry* entry) { | 263 void NavigationController::LoadEntry(NavigationEntry* entry) { |
| 223 // When navigating to a new page, we don't know for sure if we will actually | 264 // When navigating to a new page, we don't know for sure if we will actually |
| 224 // end up leaving the current page. The new page load could for example | 265 // end up leaving the current page. The new page load could for example |
| 225 // result in a download or a 'no content' response (e.g., a mailto: URL). | 266 // result in a download or a 'no content' response (e.g., a mailto: URL). |
| 226 | |
| 227 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? | |
| 228 DiscardPendingEntryInternal(); | 267 DiscardPendingEntryInternal(); |
| 229 pending_entry_ = entry; | 268 pending_entry_ = entry; |
| 230 NotificationService::current()->Notify( | 269 NotificationService::current()->Notify( |
| 231 NOTIFY_NAV_ENTRY_PENDING, | 270 NOTIFY_NAV_ENTRY_PENDING, |
| 232 Source<NavigationController>(this), | 271 Source<NavigationController>(this), |
| 233 NotificationService::NoDetails()); | 272 NotificationService::NoDetails()); |
| 234 NavigateToPendingEntry(false); | 273 NavigateToPendingEntry(false); |
| 235 } | 274 } |
| 236 | 275 |
| 237 /* static */ | |
| 238 void NavigationController::SetContentStateIfEmpty( | |
| 239 NavigationEntry* entry) { | |
| 240 if (entry->content_state().empty() && | |
| 241 (entry->tab_type() == TAB_CONTENTS_WEB || | |
| 242 entry->tab_type() == TAB_CONTENTS_NEW_TAB_UI || | |
| 243 entry->tab_type() == TAB_CONTENTS_ABOUT_UI || | |
| 244 entry->tab_type() == TAB_CONTENTS_HTML_DIALOG)) { | |
| 245 // The state is empty and the url will be rendered by WebKit. An empty | |
| 246 // state is treated as a new navigation by WebKit, which would mean | |
| 247 // losing the navigation entries and generating a new navigation | |
| 248 // entry after this one. We don't want that. To avoid this we create | |
| 249 // a valid state which WebKit will not treat as a new navigation. | |
| 250 entry->set_content_state( | |
| 251 webkit_glue::CreateHistoryStateForURL(entry->url())); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 NavigationEntry* NavigationController::GetActiveEntry() const { | 276 NavigationEntry* NavigationController::GetActiveEntry() const { |
| 256 NavigationEntry* entry = pending_entry_; | 277 NavigationEntry* entry = pending_entry_; |
| 257 if (!entry) | 278 if (!entry) |
| 258 entry = GetLastCommittedEntry(); | 279 entry = GetLastCommittedEntry(); |
| 259 return entry; | 280 return entry; |
| 260 } | 281 } |
| 261 | 282 |
| 262 int NavigationController::GetCurrentEntryIndex() const { | 283 int NavigationController::GetCurrentEntryIndex() const { |
| 263 if (pending_entry_index_ != -1) | 284 if (pending_entry_index_ != -1) |
| 264 return pending_entry_index_; | 285 return pending_entry_index_; |
| 265 return last_committed_entry_index_; | 286 return last_committed_entry_index_; |
| 266 } | 287 } |
| 267 | 288 |
| 268 NavigationEntry* NavigationController::GetLastCommittedEntry() const { | 289 NavigationEntry* NavigationController::GetLastCommittedEntry() const { |
| 269 if (last_committed_entry_index_ == -1) | 290 if (last_committed_entry_index_ == -1) |
| 270 return NULL; | 291 return NULL; |
| 271 return entries_[last_committed_entry_index_].get(); | 292 return entries_[last_committed_entry_index_].get(); |
| 272 } | 293 } |
| 273 | 294 |
| 274 NavigationEntry* NavigationController::GetEntryAtOffset(int offset) const { | 295 NavigationEntry* NavigationController::GetEntryAtOffset(int offset) const { |
| 275 int index = last_committed_entry_index_ + offset; | 296 int index = last_committed_entry_index_ + offset; |
| 276 if (index < 0 || index >= GetEntryCount()) | 297 if (index < 0 || index >= GetEntryCount()) |
| 277 return NULL; | 298 return NULL; |
| 278 | 299 |
| 279 return entries_[index].get(); | 300 return entries_[index].get(); |
| 280 } | 301 } |
| 281 | 302 |
| 282 bool NavigationController::CanStop() const { | |
| 283 // TODO(darin): do we have something pending that we can stop? | |
| 284 return false; | |
| 285 } | |
| 286 | |
| 287 bool NavigationController::CanGoBack() const { | 303 bool NavigationController::CanGoBack() const { |
| 288 return entries_.size() > 1 && GetCurrentEntryIndex() > 0; | 304 return entries_.size() > 1 && GetCurrentEntryIndex() > 0; |
| 289 } | 305 } |
| 290 | 306 |
| 291 bool NavigationController::CanGoForward() const { | 307 bool NavigationController::CanGoForward() const { |
| 292 int index = GetCurrentEntryIndex(); | 308 int index = GetCurrentEntryIndex(); |
| 293 return index >= 0 && index < (static_cast<int>(entries_.size()) - 1); | 309 return index >= 0 && index < (static_cast<int>(entries_.size()) - 1); |
| 294 } | 310 } |
| 295 | 311 |
| 296 void NavigationController::GoBack() { | 312 void NavigationController::GoBack() { |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 336 } | 352 } |
| 337 | 353 |
| 338 void NavigationController::GoToOffset(int offset) { | 354 void NavigationController::GoToOffset(int offset) { |
| 339 int index = last_committed_entry_index_ + offset; | 355 int index = last_committed_entry_index_ + offset; |
| 340 if (index < 0 || index >= GetEntryCount()) | 356 if (index < 0 || index >= GetEntryCount()) |
| 341 return; | 357 return; |
| 342 | 358 |
| 343 GoToIndex(index); | 359 GoToIndex(index); |
| 344 } | 360 } |
| 345 | 361 |
| 346 void NavigationController::Stop() { | |
| 347 DCHECK(CanStop()); | |
| 348 | |
| 349 // TODO(darin): we probably want to just call Stop on the active tab | |
| 350 // contents, but should we also call DiscardPendingEntry? | |
| 351 NOTREACHED() << "implement me"; | |
| 352 } | |
| 353 | |
| 354 void NavigationController::ReloadDontCheckForRepost() { | 362 void NavigationController::ReloadDontCheckForRepost() { |
| 355 Reload(); | 363 Reload(); |
| 356 } | 364 } |
| 357 | 365 |
| 358 void NavigationController::Destroy() { | 366 void NavigationController::Destroy() { |
| 359 // Close all tab contents owned by this controller. We make a list on the | 367 // Close all tab contents owned by this controller. We make a list on the |
| 360 // stack because they are removed from the map as they are Destroyed | 368 // stack because they are removed from the map as they are Destroyed |
| 361 // (invalidating the iterators), which may or may not occur synchronously. | 369 // (invalidating the iterators), which may or may not occur synchronously. |
| 362 // We also keep track of any NULL entries in the map so that we can clean | 370 // We also keep track of any NULL entries in the map so that we can clean |
| 363 // them out. | 371 // them out. |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 452 | 460 |
| 453 void NavigationController::LoadURLLazily(const GURL& url, | 461 void NavigationController::LoadURLLazily(const GURL& url, |
| 454 PageTransition::Type type, | 462 PageTransition::Type type, |
| 455 const std::wstring& title, | 463 const std::wstring& title, |
| 456 SkBitmap* icon) { | 464 SkBitmap* icon) { |
| 457 NavigationEntry* entry = CreateNavigationEntry(url, type); | 465 NavigationEntry* entry = CreateNavigationEntry(url, type); |
| 458 entry->set_title(title); | 466 entry->set_title(title); |
| 459 if (icon) | 467 if (icon) |
| 460 entry->favicon().set_bitmap(*icon); | 468 entry->favicon().set_bitmap(*icon); |
| 461 | 469 |
| 462 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? | |
| 463 DiscardPendingEntryInternal(); | 470 DiscardPendingEntryInternal(); |
| 464 pending_entry_ = entry; | 471 pending_entry_ = entry; |
| 465 load_pending_entry_when_active_ = true; | 472 load_pending_entry_when_active_ = true; |
| 466 } | 473 } |
| 467 | 474 |
| 468 bool NavigationController::LoadingURLLazily() { | 475 bool NavigationController::LoadingURLLazily() { |
| 469 return load_pending_entry_when_active_; | 476 return load_pending_entry_when_active_; |
| 470 } | 477 } |
| 471 | 478 |
| 472 const std::wstring& NavigationController::GetLazyTitle() const { | 479 const std::wstring& NavigationController::GetLazyTitle() const { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 486 } | 493 } |
| 487 | 494 |
| 488 void NavigationController::SetAlternateNavURLFetcher( | 495 void NavigationController::SetAlternateNavURLFetcher( |
| 489 AlternateNavURLFetcher* alternate_nav_url_fetcher) { | 496 AlternateNavURLFetcher* alternate_nav_url_fetcher) { |
| 490 DCHECK(!alternate_nav_url_fetcher_.get()); | 497 DCHECK(!alternate_nav_url_fetcher_.get()); |
| 491 DCHECK(pending_entry_); | 498 DCHECK(pending_entry_); |
| 492 alternate_nav_url_fetcher_.reset(alternate_nav_url_fetcher); | 499 alternate_nav_url_fetcher_.reset(alternate_nav_url_fetcher); |
| 493 alternate_nav_url_fetcher_entry_unique_id_ = pending_entry_->unique_id(); | 500 alternate_nav_url_fetcher_entry_unique_id_ = pending_entry_->unique_id(); |
| 494 } | 501 } |
| 495 | 502 |
| 496 void NavigationController::DidNavigateToEntry(NavigationEntry* entry, | 503 bool NavigationController::RendererDidNavigate( |
| 497 LoadCommittedDetails* details) { | 504 const ViewHostMsg_FrameNavigate_Params& params, |
| 498 DCHECK(active_contents_); | 505 bool is_interstitial, |
| 499 DCHECK(entry->tab_type() == active_contents_->type()); | 506 LoadCommittedDetails* details) { |
| 500 | 507 // Save the previous URL before we clobber it. |
| 501 SetContentStateIfEmpty(entry); | |
| 502 | |
| 503 entry->set_restored(false); | |
| 504 | |
| 505 // Update the details to list the last URL. Later, we'll update the current | |
| 506 // entry (after it's committed) and the details will be complete. | |
| 507 if (GetLastCommittedEntry()) | 508 if (GetLastCommittedEntry()) |
| 508 details->previous_url = GetLastCommittedEntry()->url(); | 509 details->previous_url = GetLastCommittedEntry()->url(); |
| 509 | 510 |
| 510 // If the entry is that of a page with PageID larger than any this Tab has | 511 // Assign the current site instance to any pending entry, so we can find it |
| 511 // seen before, then consider it a new navigation. Note that if the entry | 512 // later by calling GetEntryIndexWithPageID. We only care about this if the |
| 512 // has a SiteInstance, it should be the same as the SiteInstance of the | 513 // pending entry is an existing navigation and not a new one (or else we |
| 513 // active WebContents, because we have just navigated to it. | 514 // wouldn't care about finding it with GetEntryIndexWithPageID). |
| 514 DCHECK(entry->page_id() >= 0) << "Page ID must be set before calling us."; | 515 // |
| 515 if (entry->page_id() > GetMaxPageID()) { | 516 // TODO(brettw) this seems slightly bogus as we don't really know if the |
| 516 InsertEntry(entry); | 517 // pending entry is what this navigation is for. There is a similar TODO |
| 517 NotifyNavigationEntryCommitted(details); | 518 // w.r.t. the pending entry in RendererDidNavigateToNewPage. |
| 518 // It is now a safe time to schedule collection for any tab contents of a | 519 if (pending_entry_index_ >= 0) |
| 519 // different type, because a navigation is necessary to get back to them. | 520 pending_entry_->set_site_instance(active_contents_->GetSiteInstance()); |
| 520 ScheduleTabContentsCollectionForInactiveTabs(); | 521 |
| 521 return; | 522 // Do navigation-type specific actions. These will make and commit an entry. |
| 522 } | 523 switch (ClassifyNavigation(params)) { |
| 523 | 524 case NAV_NEW_PAGE: |
| 524 // Otherwise, we just need to update an existing entry with matching PageID. | 525 RendererDidNavigateToNewPage(params); |
| 525 // If the existing entry corresponds to the entry which is pending, then we | 526 break; |
| 526 // must update the current entry index accordingly. When navigating to the | 527 case NAV_EXISTING_PAGE: |
| 527 // same URL, a new PageID is not created. | 528 RendererDidNavigateToExistingPage(params); |
| 528 | 529 break; |
| 529 int existing_entry_index = GetEntryIndexWithPageID(entry->tab_type(), | 530 case NAV_SAME_PAGE: |
| 530 entry->site_instance(), | 531 RendererDidNavigateToSamePage(params); |
| 531 entry->page_id()); | 532 break; |
| 532 NavigationEntry* existing_entry = (existing_entry_index != -1) ? | 533 case NAV_IN_PAGE: |
| 533 entries_[existing_entry_index].get() : NULL; | 534 RendererDidNavigateInPage(params); |
| 534 if (!existing_entry) { | 535 break; |
| 535 // No existing entry, then simply ignore this navigation! | 536 case NAV_NEW_SUBFRAME: |
| 536 DLOG(WARNING) << "ignoring navigation for page: " << entry->page_id(); | 537 RendererDidNavigateNewSubframe(params); |
| 537 } else if ((existing_entry != pending_entry_) && pending_entry_ && | 538 break; |
| 538 (pending_entry_->page_id() == -1) && | 539 case NAV_AUTO_SUBFRAME: |
| 539 (pending_entry_->url() == existing_entry->url())) { | 540 if (!RendererDidNavigateAutoSubframe(params)) |
| 540 // In this case, we have a pending entry for a URL but WebCore didn't do a | 541 return false; |
| 541 // new navigation. This happens when you press enter in the URL bar to | 542 break; |
| 542 // reload. We will create a pending entry, but WebCore will convert it to | 543 case NAV_IGNORE: |
| 543 // a reload since it's the same page and not create a new entry for it | 544 // There is nothing we can do with this navigation, so we just return to |
| 544 // (the user doesn't want to have a new back/forward entry when they do | 545 // the caller that nothing has happened. |
| 545 // this). In this case, we want to just ignore the pending entry and go back | 546 return false; |
| 546 // to where we were. | 547 default: |
| 547 existing_entry->set_unique_id(pending_entry_->unique_id()); | 548 NOTREACHED(); |
| 548 DiscardPendingEntry(); | 549 } |
| 549 } else { | 550 |
| 550 DCHECK(existing_entry != entry); | 551 // All committed entries should have nonempty content state so WebKit doesn't |
| 551 // The given entry might provide a new URL... e.g., navigating back to a | 552 // get confused when we go back to them (see the function for details). |
| 552 // page in session history could have resulted in a new client redirect. | 553 SetContentStateIfEmpty(GetActiveEntry()); |
| 553 // The given entry might also provide a new title (typically an empty title | 554 |
| 554 // to overwrite the existing title). | 555 // WebKit doesn't set the "auto" transition on meta refreshes properly (bug |
| 555 existing_entry->set_url(entry->url()); | 556 // 1051891) so we manually set it for redirects which we normally treat as |
| 556 existing_entry->set_title(entry->title()); | 557 // "non-user-gestures" where we want to update stuff after navigations. |
| 557 existing_entry->favicon() = entry->favicon(); | 558 // |
| 558 existing_entry->set_content_state(entry->content_state()); | 559 // Note that the redirect check also checks for a pending entry to |
| 559 | 560 // differentiate real redirects from browser initiated navigations to a |
| 560 // TODO(brettw) why only copy the security style and no other SSL stuff? | 561 // redirected entry. This happens when you hit back to go to a page that was |
| 561 existing_entry->ssl().set_security_style(entry->ssl().security_style()); | 562 // the destination of a redirect, we don't want to treat it as a redirect |
| 562 | 563 // even though that's what its transition will be. See bug 1117048. |
| 563 const int prev_entry_index = last_committed_entry_index_; | 564 // |
| 564 if (existing_entry == pending_entry_) { | 565 // TODO(brettw) write a test for this complicated logic. |
| 565 DCHECK(pending_entry_index_ != -1); | 566 details->is_auto = (PageTransition::IsRedirect(params.transition) && |
| 566 last_committed_entry_index_ = pending_entry_index_; | 567 !GetPendingEntry()) || |
| 567 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? | 568 params.gesture == NavigationGestureAuto; |
| 568 DiscardPendingEntryInternal(); | 569 |
| 569 } else { | 570 // Now prep the rest of the details for the notification and broadcast. |
| 570 // NOTE: Do not update the unique ID here, as we don't want infobars etc. | 571 details->entry = GetActiveEntry(); |
| 571 // to dismiss. | 572 details->is_in_page = IsURLInPageNavigation(params.url); |
| 572 | 573 details->is_main_frame = PageTransition::IsMainFrame(params.transition); |
| 573 // The navigation could have been issued by the renderer, so be sure that | |
| 574 // we update our current index. | |
| 575 last_committed_entry_index_ = existing_entry_index; | |
| 576 } | |
| 577 IndexOfActiveEntryChanged(prev_entry_index); | |
| 578 } | |
| 579 | |
| 580 delete entry; | |
| 581 NotifyNavigationEntryCommitted(details); | 574 NotifyNavigationEntryCommitted(details); |
| 582 | 575 |
| 583 if (alternate_nav_url_fetcher_.get()) { | 576 // Because this call may synchronously show an infobar, we do it last, to |
| 584 // Because this call may synchronously show an infobar, we do it last, to | 577 // make sure all other state is stable and the infobar won't get blown away |
| 585 // make sure all other state is stable and the infobar won't get blown away | 578 // by some transition. |
| 586 // by some transition. | 579 // |
| 580 // TODO(brettw) bug 1324500: This logic should be moved out of here, it should | |
| 581 // listen for the notification instead. | |
| 582 if (alternate_nav_url_fetcher_.get()) | |
| 587 alternate_nav_url_fetcher_->OnNavigatedToEntry(); | 583 alternate_nav_url_fetcher_->OnNavigatedToEntry(); |
| 588 } | 584 |
| 585 // Broadcast the NOTIFY_FRAME_PROVISIONAL_LOAD_COMMITTED notification for use | |
| 586 // by the SSL manager. | |
| 587 // | |
| 588 // TODO(brettw) bug 1352803: this information should be combined with | |
| 589 // NOTIFY_NAV_ENTRY_COMMITTED so this one can be deleted. | |
| 590 ProvisionalLoadDetails provisional_details(details->is_main_frame, | |
| 591 is_interstitial, | |
| 592 details->is_in_page, | |
| 593 params.url, | |
| 594 params.security_info); | |
| 595 NotificationService::current()-> | |
| 596 Notify(NOTIFY_FRAME_PROVISIONAL_LOAD_COMMITTED, | |
| 597 Source<NavigationController>(this), | |
| 598 Details<ProvisionalLoadDetails>(&provisional_details)); | |
| 589 | 599 |
| 590 // It is now a safe time to schedule collection for any tab contents of a | 600 // It is now a safe time to schedule collection for any tab contents of a |
| 591 // different type, because a navigation is necessary to get back to them. | 601 // different type, because a navigation is necessary to get back to them. |
| 592 ScheduleTabContentsCollectionForInactiveTabs(); | 602 ScheduleTabContentsCollectionForInactiveTabs(); |
| 593 } | 603 return true; |
| 594 | 604 } |
| 605 | |
| 606 NavigationController::NavClass NavigationController::ClassifyNavigation( | |
| 607 const ViewHostMsg_FrameNavigate_Params& params) const { | |
| 608 // If a page makes a popup navigated to about blank, and then writes stuff | |
| 609 // like a subframe navigated to a real site, we'll get a notification with an | |
| 610 // invalid page ID. There's nothing we can do with these, so just ignore them. | |
| 611 if (params.page_id == -1) { | |
| 612 DCHECK(!GetActiveEntry()) << "Got an invalid page ID but we seem to be " | |
| 613 " navigated to a valid page. This should be impossible."; | |
| 614 return NAV_IGNORE; | |
| 615 } | |
| 616 | |
| 617 if (params.page_id > active_contents_->GetMaxPageID()) { | |
| 618 // Greater page IDs than we've ever seen before are new pages. We may or may | |
| 619 // not have a pending entry for the page, and this may or may not be the | |
| 620 // main frame. | |
| 621 if (PageTransition::IsMainFrame(params.transition)) | |
| 622 return NAV_NEW_PAGE; | |
| 623 return NAV_NEW_SUBFRAME; | |
| 624 } | |
| 625 | |
| 626 // Now we know that the notification is for an existing page. Find that entry. | |
| 627 int existing_entry_index = GetEntryIndexWithPageID( | |
| 628 active_contents_->type(), | |
| 629 active_contents_->GetSiteInstance(), | |
| 630 params.page_id); | |
| 631 if (existing_entry_index == -1) | |
|
jcampan
2008/09/10 22:38:12
What case would that be? Should we DCHECK?
| |
| 632 return NAV_IGNORE; | |
| 633 NavigationEntry* existing_entry = entries_[existing_entry_index].get(); | |
| 634 | |
| 635 if (pending_entry_ && | |
| 636 pending_entry_->url() == params.url && | |
| 637 existing_entry != pending_entry_ && | |
| 638 pending_entry_->page_id() == -1 && | |
| 639 pending_entry_->url() == existing_entry->url()) { | |
| 640 // In this case, we have a pending entry for a URL but WebCore didn't do a | |
| 641 // new navigation. This happens when you press enter in the URL bar to | |
| 642 // reload. We will create a pending entry, but WebKit will convert it to | |
| 643 // a reload since it's the same page and not create a new entry for it | |
| 644 // (the user doesn't want to have a new back/forward entry when they do | |
| 645 // this). In this case, we want to just ignore the pending entry and go | |
| 646 // back to where we were (the "existing entry"). | |
| 647 return NAV_SAME_PAGE; | |
| 648 } | |
| 649 | |
| 650 if (AreURLsInPageNavigation(existing_entry->url(), params.url)) | |
| 651 return NAV_IN_PAGE; | |
| 652 | |
| 653 if (!PageTransition::IsMainFrame(params.transition)) | |
| 654 return NAV_AUTO_SUBFRAME; // All manual subframes would get new IDs and | |
| 655 // were handled above. | |
| 656 // Since we weeded out "new" navigations above, we know this is an existing | |
| 657 // navigation. | |
| 658 return NAV_EXISTING_PAGE; | |
| 659 } | |
| 660 | |
| 661 void NavigationController::RendererDidNavigateToNewPage( | |
| 662 const ViewHostMsg_FrameNavigate_Params& params) { | |
| 663 NavigationEntry* new_entry; | |
| 664 if (pending_entry_) { | |
| 665 // TODO(brettw) this assumes that the pending entry is appropriate for the | |
| 666 // new page that was just loaded. I don't think this is necessarily the | |
| 667 // case! We should have some more tracking to know for sure. This goes along | |
| 668 // with a similar TODO at the top of RendererDidNavigate where we blindly | |
| 669 // set the site instance on the pending entry. | |
| 670 new_entry = new NavigationEntry(*pending_entry_); | |
| 671 | |
| 672 // Don't use the page type from the pending entry. Some interstitial page | |
| 673 // may have set the type to interstitial. Once we commit, however, the page | |
| 674 // type must always be normal. | |
| 675 new_entry->set_page_type(NavigationEntry::NORMAL_PAGE); | |
| 676 } else { | |
| 677 new_entry = new NavigationEntry(active_contents_->type()); | |
| 678 } | |
| 679 | |
| 680 new_entry->set_url(params.url); | |
| 681 new_entry->set_page_id(params.page_id); | |
| 682 new_entry->set_transition_type(params.transition); | |
| 683 new_entry->set_site_instance(active_contents_->GetSiteInstance()); | |
| 684 new_entry->set_has_post_data(params.is_post); | |
| 685 | |
| 686 InsertEntry(new_entry); | |
| 687 } | |
| 688 | |
| 689 void NavigationController::RendererDidNavigateToExistingPage( | |
| 690 const ViewHostMsg_FrameNavigate_Params& params) { | |
| 691 // We should only get here for main frame navigations. | |
| 692 DCHECK(PageTransition::IsMainFrame(params.transition)); | |
| 693 | |
| 694 // This is a back/forward navigation. The existing page for the ID is | |
| 695 // guaranteed to exist, and we just need to update it with new information | |
| 696 // from the renderer. | |
| 697 int entry_index = GetEntryIndexWithPageID( | |
| 698 active_contents_->type(), | |
| 699 active_contents_->GetSiteInstance(), | |
| 700 params.page_id); | |
| 701 DCHECK(entry_index >= 0 && | |
| 702 entry_index < static_cast<int>(entries_.size())); | |
| 703 NavigationEntry* entry = entries_[entry_index].get(); | |
| 704 | |
| 705 // The URL (and hence the site instance) may have changed due to redirects. | |
| 706 entry->set_url(params.url); | |
| 707 entry->set_site_instance(active_contents_->GetSiteInstance()); | |
|
Charlie Reis
2008/09/10 22:58:19
Hmm, I missed this one before. Did this correspon
brettw
2008/09/11 02:46:36
This gets hit during session restore. I clarified
Charlie Reis
2008/09/11 03:59:28
OK
| |
| 708 | |
| 709 // The entry we found in the list might be pending if the user hit | |
| 710 // back/forward/reload. This load should commit it (since it's already in the | |
| 711 // list, we can just discard the pending pointer). | |
| 712 // | |
| 713 // Note that we need to use the "internal" version since we don't want to | |
| 714 // actually change any other state, just kill the pointer. | |
| 715 if (entry == pending_entry_) | |
| 716 DiscardPendingEntryInternal(); | |
| 717 | |
| 718 int old_committed_entry_index = last_committed_entry_index_; | |
| 719 last_committed_entry_index_ = entry_index; | |
| 720 IndexOfActiveEntryChanged(old_committed_entry_index); | |
| 721 } | |
| 722 | |
| 723 void NavigationController::RendererDidNavigateToSamePage( | |
| 724 const ViewHostMsg_FrameNavigate_Params& params) { | |
| 725 // This mode implies we have a pending entry that's the same as an existing | |
| 726 // entry for this page ID. All we need to do is update the existing entry. | |
| 727 NavigationEntry* existing_entry = GetEntryWithPageID( | |
| 728 active_contents_->type(), | |
| 729 active_contents_->GetSiteInstance(), | |
| 730 params.page_id); | |
| 731 | |
| 732 // We assign the entry's unique ID to be that of the new one. Since this is | |
| 733 // always the result of a user action, we want to dismiss infobars, etc. like | |
| 734 // a regular user-initiated navigation. | |
| 735 existing_entry->set_unique_id(pending_entry_->unique_id()); | |
| 736 | |
| 737 DiscardPendingEntry(); | |
| 738 } | |
| 739 | |
| 740 void NavigationController::RendererDidNavigateInPage( | |
| 741 const ViewHostMsg_FrameNavigate_Params& params) { | |
| 742 DCHECK(PageTransition::IsMainFrame(params.transition)) << | |
| 743 "WebKit should only tell us about in-page navs for the main frame."; | |
| 744 // We're guaranteed to have an entry for this one. | |
| 745 NavigationEntry* existing_entry = GetEntryWithPageID( | |
| 746 active_contents_->type(), | |
| 747 active_contents_->GetSiteInstance(), | |
| 748 params.page_id); | |
| 749 | |
| 750 // Reference fragment navigation. We're guaranteed to have the last_committed | |
| 751 // entry and it will be the same page as the new navigation (minus the | |
| 752 // reference fragments, of course). | |
| 753 NavigationEntry* new_entry = new NavigationEntry(*existing_entry); | |
| 754 new_entry->set_page_id(params.page_id); | |
| 755 new_entry->set_url(params.url); | |
| 756 InsertEntry(new_entry); | |
| 757 } | |
| 758 | |
| 759 void NavigationController::RendererDidNavigateNewSubframe( | |
| 760 const ViewHostMsg_FrameNavigate_Params& params) { | |
| 761 // Manual subframe navigations just get the current entry cloned so the user | |
| 762 // can go back or forward to it. The actual subframe information will be | |
| 763 // stored in the page state for each of those entries. This happens out of | |
| 764 // band with the actual navigations. | |
| 765 DCHECK(GetLastCommittedEntry()); | |
| 766 NavigationEntry* new_entry = new NavigationEntry(*GetLastCommittedEntry()); | |
| 767 new_entry->set_page_id(params.page_id); | |
| 768 InsertEntry(new_entry); | |
| 769 } | |
| 770 | |
| 771 bool NavigationController::RendererDidNavigateAutoSubframe( | |
| 772 const ViewHostMsg_FrameNavigate_Params& params) { | |
| 773 // We're guaranteed to have a previously committed entry, and we now need to | |
| 774 // handle navigation inside of a subframe in it without creating a new entry. | |
| 775 DCHECK(GetLastCommittedEntry()); | |
| 776 | |
| 777 // Handle the case where we're navigating back/forward to a previous subframe | |
| 778 // navigation entry. This is case "2." in NAV_AUTO_SUBFRAME comment in the | |
| 779 // header file. In case "1." this will be a NOP. | |
| 780 int entry_index = GetEntryIndexWithPageID( | |
| 781 active_contents_->type(), | |
| 782 active_contents_->GetSiteInstance(), | |
| 783 params.page_id); | |
| 784 if (entry_index < 0 || | |
| 785 entry_index >= static_cast<int>(entries_.size())) { | |
| 786 NOTREACHED(); | |
| 787 return false; | |
| 788 } | |
| 789 | |
| 790 // Update the current navigation entry in case we're going back/forward. | |
| 791 if (entry_index != last_committed_entry_index_) { | |
| 792 int old_committed_entry_index = last_committed_entry_index_; | |
| 793 last_committed_entry_index_ = entry_index; | |
| 794 IndexOfActiveEntryChanged(old_committed_entry_index); | |
| 795 return true; | |
| 796 } | |
| 797 return false; | |
| 798 } | |
| 799 | |
| 800 void NavigationController::CommitPendingEntry() { | |
| 801 if (!GetPendingEntry()) | |
| 802 return; // Nothing to do. | |
| 803 | |
| 804 // Need to save the previous URL for the notification. | |
| 805 LoadCommittedDetails details; | |
| 806 if (GetLastCommittedEntry()) | |
| 807 details.previous_url = GetLastCommittedEntry()->url(); | |
| 808 | |
| 809 if (pending_entry_index_ >= 0) { | |
| 810 // This is a previous navigation (back/forward) that we're just now | |
| 811 // committing. Just mark it as committed. | |
| 812 int new_entry_index = pending_entry_index_; | |
| 813 DiscardPendingEntryInternal(); | |
| 814 | |
| 815 // Mark that entry as committed. | |
| 816 int old_committed_entry_index = last_committed_entry_index_; | |
| 817 last_committed_entry_index_ = new_entry_index; | |
| 818 IndexOfActiveEntryChanged(old_committed_entry_index); | |
| 819 } else { | |
| 820 // This is a new navigation. It's easiest to just copy the entry and insert | |
| 821 // it new again, since InsertEntry expects to take ownership and also | |
| 822 // discard the pending entry. We also need to synthesize a page ID. We can | |
| 823 // only do this because this function will only be called by our custom | |
| 824 // TabContents types. For WebContents, the IDs are generated by the | |
| 825 // renderer, so we can't do this. | |
| 826 pending_entry_->set_page_id(active_contents_->GetMaxPageID() + 1); | |
| 827 active_contents_->UpdateMaxPageID(pending_entry_->page_id()); | |
| 828 InsertEntry(new NavigationEntry(*pending_entry_)); | |
| 829 } | |
| 830 | |
| 831 // Broadcast the notification of the navigation. | |
| 832 details.entry = GetActiveEntry(); | |
| 833 details.is_auto = false; | |
| 834 details.is_in_page = AreURLsInPageNavigation(details.previous_url, | |
| 835 details.entry->url()); | |
| 836 details.is_main_frame = true; | |
| 837 NotifyNavigationEntryCommitted(&details); | |
| 838 } | |
| 595 | 839 |
| 596 int NavigationController::GetIndexOfEntry( | 840 int NavigationController::GetIndexOfEntry( |
| 597 const NavigationEntry* entry) const { | 841 const NavigationEntry* entry) const { |
| 598 const NavigationEntries::const_iterator i(std::find( | 842 const NavigationEntries::const_iterator i(std::find( |
| 599 entries_.begin(), | 843 entries_.begin(), |
| 600 entries_.end(), | 844 entries_.end(), |
| 601 entry)); | 845 entry)); |
| 602 return (i == entries_.end()) ? -1 : static_cast<int>(i - entries_.begin()); | 846 return (i == entries_.end()) ? -1 : static_cast<int>(i - entries_.begin()); |
| 603 } | 847 } |
| 604 | 848 |
| 605 void NavigationController::RemoveLastEntry() { | 849 void NavigationController::RemoveLastEntryForInterstitial() { |
| 606 int current_size = static_cast<int>(entries_.size()); | 850 int current_size = static_cast<int>(entries_.size()); |
| 607 | 851 |
| 608 if (current_size > 0) { | 852 if (current_size > 0) { |
| 609 if (pending_entry_ == entries_[current_size - 1] || | 853 if (pending_entry_ == entries_[current_size - 1] || |
| 610 pending_entry_index_ == current_size - 1) | 854 pending_entry_index_ == current_size - 1) |
| 611 DiscardPendingEntryInternal(); | 855 DiscardPendingEntryInternal(); |
| 612 | 856 |
| 613 entries_.pop_back(); | 857 entries_.pop_back(); |
| 614 | 858 |
| 615 if (last_committed_entry_index_ >= current_size - 1) | 859 if (last_committed_entry_index_ >= current_size - 1) { |
| 616 last_committed_entry_index_ = current_size - 2; | 860 last_committed_entry_index_ = current_size - 2; |
| 617 | 861 |
| 618 NotifyPrunedEntries(); | 862 // Broadcast the notification of the navigation. This is kind of a hack, |
| 863 // since the navigation wasn't actually committed. But this function is | |
| 864 // used for interstital pages, and the UI needs to get updated when the | |
| 865 // interstitial page | |
| 866 LoadCommittedDetails details; | |
| 867 details.entry = GetActiveEntry(); | |
| 868 details.is_auto = false; | |
| 869 details.is_in_page = false; | |
| 870 details.is_main_frame = true; | |
| 871 NotifyNavigationEntryCommitted(&details); | |
| 872 } | |
| 873 | |
| 874 NotifyPrunedEntries(this); | |
| 619 } | 875 } |
| 620 } | 876 } |
| 621 | 877 |
| 878 void NavigationController::AddDummyEntryForInterstitial( | |
| 879 const NavigationEntry& clone_me) { | |
| 880 // We need to send a commit notification for this transition. | |
| 881 LoadCommittedDetails details; | |
| 882 if (GetLastCommittedEntry()) | |
| 883 details.previous_url = GetLastCommittedEntry()->url(); | |
| 884 | |
| 885 NavigationEntry* new_entry = new NavigationEntry(clone_me); | |
| 886 InsertEntry(new_entry); | |
| 887 // Watch out, don't use clone_me after this. The caller may have passed in a | |
| 888 // reference to our pending entry, which means it would have been destroyed. | |
| 889 | |
| 890 details.is_auto = false; | |
| 891 details.entry = GetActiveEntry(); | |
| 892 details.is_in_page = false; | |
| 893 details.is_main_frame = true; | |
| 894 NotifyNavigationEntryCommitted(&details); | |
| 895 } | |
| 896 | |
| 897 bool NavigationController::IsURLInPageNavigation(const GURL& url) const { | |
| 898 NavigationEntry* last_committed = GetLastCommittedEntry(); | |
| 899 if (!last_committed) | |
| 900 return false; | |
| 901 return AreURLsInPageNavigation(last_committed->url(), url); | |
| 902 } | |
| 903 | |
| 622 void NavigationController::DiscardPendingEntry() { | 904 void NavigationController::DiscardPendingEntry() { |
| 623 DiscardPendingEntryInternal(); | 905 DiscardPendingEntryInternal(); |
| 624 | 906 |
| 625 // Synchronize the active_contents_ to the last committed entry. | 907 // Synchronize the active_contents_ to the last committed entry. |
| 626 NavigationEntry* last_entry = GetLastCommittedEntry(); | 908 NavigationEntry* last_entry = GetLastCommittedEntry(); |
| 627 if (last_entry && last_entry->tab_type() != active_contents_->type()) { | 909 if (last_entry && last_entry->tab_type() != active_contents_->type()) { |
| 628 TabContents* from_contents = active_contents_; | 910 TabContents* from_contents = active_contents_; |
| 629 from_contents->SetActive(false); | 911 from_contents->SetActive(false); |
| 630 | 912 |
| 631 // Switch back to the previous tab contents. | 913 // Switch back to the previous tab contents. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 668 | 950 |
| 669 // Prune any entries which are in front of the current entry. | 951 // Prune any entries which are in front of the current entry. |
| 670 if (current_size > 0) { | 952 if (current_size > 0) { |
| 671 bool pruned = false; | 953 bool pruned = false; |
| 672 while (last_committed_entry_index_ < (current_size - 1)) { | 954 while (last_committed_entry_index_ < (current_size - 1)) { |
| 673 pruned = true; | 955 pruned = true; |
| 674 entries_.pop_back(); | 956 entries_.pop_back(); |
| 675 current_size--; | 957 current_size--; |
| 676 } | 958 } |
| 677 if (pruned) // Only notify if we did prune something. | 959 if (pruned) // Only notify if we did prune something. |
| 678 NotifyPrunedEntries(); | 960 NotifyPrunedEntries(this); |
| 679 } | 961 } |
| 680 | 962 |
| 681 if (entries_.size() >= max_entry_count_) | 963 if (entries_.size() >= max_entry_count_) |
| 682 RemoveEntryAtIndex(0); | 964 RemoveEntryAtIndex(0); |
| 683 | 965 |
| 684 entries_.push_back(linked_ptr<NavigationEntry>(entry)); | 966 entries_.push_back(linked_ptr<NavigationEntry>(entry)); |
| 685 last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; | 967 last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; |
| 968 | |
| 969 // This is a new page ID, so we need everybody to know about it. | |
| 970 active_contents_->UpdateMaxPageID(entry->page_id()); | |
| 686 | 971 |
| 972 // TODO(brettw) this seems bogus. The tab contents can listen for the | |
| 973 // notification or use the details that we pass back to it. | |
| 687 active_contents_->NotifyDidNavigate(NAVIGATION_NEW, 0); | 974 active_contents_->NotifyDidNavigate(NAVIGATION_NEW, 0); |
| 688 } | 975 } |
| 689 | 976 |
| 690 void NavigationController::SetWindowID(const SessionID& id) { | 977 void NavigationController::SetWindowID(const SessionID& id) { |
| 691 window_id_ = id; | 978 window_id_ = id; |
| 692 NotificationService::current()->Notify(NOTIFY_TAB_PARENTED, | 979 NotificationService::current()->Notify(NOTIFY_TAB_PARENTED, |
| 693 Source<NavigationController>(this), | 980 Source<NavigationController>(this), |
| 694 NotificationService::NoDetails()); | 981 NotificationService::NoDetails()); |
| 695 } | 982 } |
| 696 | 983 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 716 GetTabContentsCreateIfNecessary(parent, *pending_entry_); | 1003 GetTabContentsCreateIfNecessary(parent, *pending_entry_); |
| 717 | 1004 |
| 718 contents->SetActive(true); | 1005 contents->SetActive(true); |
| 719 active_contents_ = contents; | 1006 active_contents_ = contents; |
| 720 | 1007 |
| 721 if (from_contents && from_contents != contents) { | 1008 if (from_contents && from_contents != contents) { |
| 722 if (from_contents->delegate()) | 1009 if (from_contents->delegate()) |
| 723 from_contents->delegate()->ReplaceContents(from_contents, contents); | 1010 from_contents->delegate()->ReplaceContents(from_contents, contents); |
| 724 } | 1011 } |
| 725 | 1012 |
| 726 if (!contents->Navigate(*pending_entry_, reload)) | 1013 NavigationEntry temp_entry(*pending_entry_); |
| 1014 if (!contents->NavigateToPendingEntry(reload)) | |
| 727 DiscardPendingEntry(); | 1015 DiscardPendingEntry(); |
| 728 } | 1016 } |
| 729 | 1017 |
| 730 void NavigationController::NotifyNavigationEntryCommitted( | 1018 void NavigationController::NotifyNavigationEntryCommitted( |
| 731 LoadCommittedDetails* details) { | 1019 LoadCommittedDetails* details) { |
| 732 // Reset the Alternate Nav URL Fetcher if we're loading some page it doesn't | 1020 // Reset the Alternate Nav URL Fetcher if we're loading some page it doesn't |
| 733 // care about. We must do this before calling Notify() below as that may | 1021 // care about. We must do this before calling Notify() below as that may |
| 734 // result in the creation of a new fetcher. | 1022 // result in the creation of a new fetcher. |
| 735 // | 1023 // |
| 736 // TODO(brettw) bug 1324500: this logic should be moved out of the controller! | 1024 // TODO(brettw) bug 1324500: this logic should be moved out of the controller! |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 748 active_contents_->NotifyNavigationStateChanged( | 1036 active_contents_->NotifyNavigationStateChanged( |
| 749 TabContents::INVALIDATE_EVERYTHING); | 1037 TabContents::INVALIDATE_EVERYTHING); |
| 750 | 1038 |
| 751 details->entry = GetActiveEntry(); | 1039 details->entry = GetActiveEntry(); |
| 752 NotificationService::current()->Notify( | 1040 NotificationService::current()->Notify( |
| 753 NOTIFY_NAV_ENTRY_COMMITTED, | 1041 NOTIFY_NAV_ENTRY_COMMITTED, |
| 754 Source<NavigationController>(this), | 1042 Source<NavigationController>(this), |
| 755 Details<LoadCommittedDetails>(details)); | 1043 Details<LoadCommittedDetails>(details)); |
| 756 } | 1044 } |
| 757 | 1045 |
| 758 void NavigationController::NotifyPrunedEntries() { | 1046 void NavigationController::IndexOfActiveEntryChanged(int prev_committed_index) { |
| 759 NotificationService::current()->Notify(NOTIFY_NAV_LIST_PRUNED, | |
| 760 Source<NavigationController>(this), | |
| 761 NotificationService::NoDetails()); | |
| 762 } | |
| 763 | |
| 764 void NavigationController::IndexOfActiveEntryChanged( | |
| 765 int prev_committed_index) { | |
| 766 NavigationType nav_type = NAVIGATION_BACK_FORWARD; | 1047 NavigationType nav_type = NAVIGATION_BACK_FORWARD; |
| 767 int relative_navigation_offset = | 1048 int relative_navigation_offset = |
| 768 GetLastCommittedEntryIndex() - prev_committed_index; | 1049 GetLastCommittedEntryIndex() - prev_committed_index; |
| 769 if (relative_navigation_offset == 0) { | 1050 if (relative_navigation_offset == 0) |
| 770 nav_type = NAVIGATION_REPLACE; | 1051 nav_type = NAVIGATION_REPLACE; |
| 771 } | 1052 |
| 1053 // TODO(brettw) I don't think this call should be necessary. There is already | |
| 1054 // a notification of this event that could be used, or maybe all the tab | |
| 1055 // contents' know when we navigate (WebContents does). | |
| 772 active_contents_->NotifyDidNavigate(nav_type, relative_navigation_offset); | 1056 active_contents_->NotifyDidNavigate(nav_type, relative_navigation_offset); |
| 773 } | 1057 } |
| 774 | 1058 |
| 775 TabContents* NavigationController::GetTabContentsCreateIfNecessary( | 1059 TabContents* NavigationController::GetTabContentsCreateIfNecessary( |
| 776 HWND parent, | 1060 HWND parent, |
| 777 const NavigationEntry& entry) { | 1061 const NavigationEntry& entry) { |
| 778 TabContents* contents = GetTabContents(entry.tab_type()); | 1062 TabContents* contents = GetTabContents(entry.tab_type()); |
| 779 if (!contents) { | 1063 if (!contents) { |
| 780 contents = TabContents::CreateWithType(entry.tab_type(), parent, profile_, | 1064 contents = TabContents::CreateWithType(entry.tab_type(), parent, profile_, |
| 781 entry.site_instance()); | 1065 entry.site_instance()); |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 812 NOTREACHED() << "Should not happen. Multiple contents for one type"; | 1096 NOTREACHED() << "Should not happen. Multiple contents for one type"; |
| 813 } else { | 1097 } else { |
| 814 tab_contents_map_[t] = some_contents; | 1098 tab_contents_map_[t] = some_contents; |
| 815 some_contents->set_controller(this); | 1099 some_contents->set_controller(this); |
| 816 } | 1100 } |
| 817 } | 1101 } |
| 818 if (some_contents->AsDOMUIHost()) | 1102 if (some_contents->AsDOMUIHost()) |
| 819 some_contents->AsDOMUIHost()->AttachMessageHandlers(); | 1103 some_contents->AsDOMUIHost()->AttachMessageHandlers(); |
| 820 } | 1104 } |
| 821 | 1105 |
| 822 void NavigationController::NotifyEntryChangedByPageID( | |
| 823 TabContentsType type, | |
| 824 SiteInstance *instance, | |
| 825 int32 page_id) { | |
| 826 int index = GetEntryIndexWithPageID(type, instance, page_id); | |
| 827 if (index != -1) | |
| 828 NotifyEntryChanged(entries_[index].get(), index); | |
| 829 } | |
| 830 | |
| 831 // static | 1106 // static |
| 832 void NavigationController::DisablePromptOnRepost() { | 1107 void NavigationController::DisablePromptOnRepost() { |
| 833 check_for_repost_ = false; | 1108 check_for_repost_ = false; |
| 834 } | 1109 } |
| 835 | 1110 |
| 836 void NavigationController::SetActive(bool is_active) { | 1111 void NavigationController::SetActive(bool is_active) { |
| 837 if (is_active) { | 1112 if (is_active) { |
| 838 if (needs_reload_) { | 1113 if (needs_reload_) { |
| 839 LoadIfNecessary(); | 1114 LoadIfNecessary(); |
| 840 } else if (load_pending_entry_when_active_) { | 1115 } else if (load_pending_entry_when_active_) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 881 if (!entries_.empty()) | 1156 if (!entries_.empty()) |
| 882 last_committed_entry_index_--; | 1157 last_committed_entry_index_--; |
| 883 else | 1158 else |
| 884 last_committed_entry_index_ = -1; | 1159 last_committed_entry_index_ = -1; |
| 885 } | 1160 } |
| 886 | 1161 |
| 887 // TODO(brettw) bug 1324021: we probably need some notification here so the | 1162 // TODO(brettw) bug 1324021: we probably need some notification here so the |
| 888 // session service can stay in sync. | 1163 // session service can stay in sync. |
| 889 } | 1164 } |
| 890 | 1165 |
| 891 int NavigationController::GetMaxPageID() const { | |
| 892 return active_contents_->GetMaxPageID(); | |
| 893 } | |
| 894 | |
| 895 NavigationController* NavigationController::Clone(HWND parent_hwnd) { | 1166 NavigationController* NavigationController::Clone(HWND parent_hwnd) { |
| 896 NavigationController* nc = new NavigationController(NULL, profile_); | 1167 NavigationController* nc = new NavigationController(NULL, profile_); |
| 897 | 1168 |
| 898 if (GetEntryCount() == 0) | 1169 if (GetEntryCount() == 0) |
| 899 return nc; | 1170 return nc; |
| 900 | 1171 |
| 901 nc->needs_reload_ = true; | 1172 nc->needs_reload_ = true; |
| 902 | 1173 |
| 903 nc->entries_.reserve(entries_.size()); | 1174 nc->entries_.reserve(entries_.size()); |
| 904 for (int i = 0, c = GetEntryCount(); i < c; ++i) { | 1175 for (int i = 0, c = GetEntryCount(); i < c; ++i) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 967 | 1238 |
| 968 void NavigationController::DiscardPendingEntryInternal() { | 1239 void NavigationController::DiscardPendingEntryInternal() { |
| 969 if (pending_entry_index_ == -1) | 1240 if (pending_entry_index_ == -1) |
| 970 delete pending_entry_; | 1241 delete pending_entry_; |
| 971 pending_entry_ = NULL; | 1242 pending_entry_ = NULL; |
| 972 pending_entry_index_ = -1; | 1243 pending_entry_index_ = -1; |
| 973 } | 1244 } |
| 974 | 1245 |
| 975 int NavigationController::GetEntryIndexWithPageID( | 1246 int NavigationController::GetEntryIndexWithPageID( |
| 976 TabContentsType type, SiteInstance* instance, int32 page_id) const { | 1247 TabContentsType type, SiteInstance* instance, int32 page_id) const { |
| 977 // The instance should only be specified for contents displaying web pages. | |
| 978 // TODO(evanm): checking against NEW_TAB_UI and HTML_DLG here is lame. | |
| 979 // It'd be nice for DomUIHost to just use SiteInstances for keeping content | |
| 980 // separated properly. | |
| 981 if (type != TAB_CONTENTS_WEB && | |
| 982 type != TAB_CONTENTS_NEW_TAB_UI && | |
| 983 type != TAB_CONTENTS_ABOUT_UI && | |
| 984 type != TAB_CONTENTS_HTML_DIALOG && | |
| 985 type != TAB_CONTENTS_VIEW_SOURCE && | |
| 986 type != TAB_CONTENTS_DEBUGGER) | |
| 987 DCHECK(instance == NULL); | |
| 988 | |
| 989 for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) { | 1248 for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) { |
| 990 if ((entries_[i]->tab_type() == type) && | 1249 if ((entries_[i]->tab_type() == type) && |
| 991 (entries_[i]->site_instance() == instance) && | 1250 (entries_[i]->site_instance() == instance) && |
| 992 (entries_[i]->page_id() == page_id)) | 1251 (entries_[i]->page_id() == page_id)) |
| 993 return i; | 1252 return i; |
| 994 } | 1253 } |
| 995 return -1; | 1254 return -1; |
| 996 } | 1255 } |
| OLD | NEW |