Home | History | Annotate | Download | only in frame_host
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/frame_host/navigation_controller_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/debug/trace_event.h"
     10 #include "base/logging.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/strings/string_number_conversions.h"  // Temporary
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "base/time/time.h"
     16 #include "cc/base/switches.h"
     17 #include "content/browser/browser_url_handler_impl.h"
     18 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
     19 #include "content/browser/dom_storage/session_storage_namespace_impl.h"
     20 #include "content/browser/frame_host/debug_urls.h"
     21 #include "content/browser/frame_host/interstitial_page_impl.h"
     22 #include "content/browser/frame_host/navigation_entry_impl.h"
     23 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
     24 #include "content/browser/renderer_host/render_view_host_impl.h"  // Temporary
     25 #include "content/browser/site_instance_impl.h"
     26 #include "content/common/frame_messages.h"
     27 #include "content/common/view_messages.h"
     28 #include "content/public/browser/browser_context.h"
     29 #include "content/public/browser/content_browser_client.h"
     30 #include "content/public/browser/invalidate_type.h"
     31 #include "content/public/browser/navigation_details.h"
     32 #include "content/public/browser/notification_service.h"
     33 #include "content/public/browser/notification_types.h"
     34 #include "content/public/browser/render_widget_host.h"
     35 #include "content/public/browser/render_widget_host_view.h"
     36 #include "content/public/browser/storage_partition.h"
     37 #include "content/public/browser/user_metrics.h"
     38 #include "content/public/common/content_client.h"
     39 #include "content/public/common/content_constants.h"
     40 #include "net/base/escape.h"
     41 #include "net/base/mime_util.h"
     42 #include "net/base/net_util.h"
     43 #include "skia/ext/platform_canvas.h"
     44 #include "url/url_constants.h"
     45 
     46 namespace content {
     47 namespace {
     48 
     49 // Invoked when entries have been pruned, or removed. For example, if the
     50 // current entries are [google, digg, yahoo], with the current entry google,
     51 // and the user types in cnet, then digg and yahoo are pruned.
     52 void NotifyPrunedEntries(NavigationControllerImpl* nav_controller,
     53                          bool from_front,
     54                          int count) {
     55   PrunedDetails details;
     56   details.from_front = from_front;
     57   details.count = count;
     58   NotificationService::current()->Notify(
     59       NOTIFICATION_NAV_LIST_PRUNED,
     60       Source<NavigationController>(nav_controller),
     61       Details<PrunedDetails>(&details));
     62 }
     63 
     64 // Ensure the given NavigationEntry has a valid state, so that WebKit does not
     65 // get confused if we navigate back to it.
     66 //
     67 // An empty state is treated as a new navigation by WebKit, which would mean
     68 // losing the navigation entries and generating a new navigation entry after
     69 // this one. We don't want that. To avoid this we create a valid state which
     70 // WebKit will not treat as a new navigation.
     71 void SetPageStateIfEmpty(NavigationEntryImpl* entry) {
     72   if (!entry->GetPageState().IsValid())
     73     entry->SetPageState(PageState::CreateFromURL(entry->GetURL()));
     74 }
     75 
     76 NavigationEntryImpl::RestoreType ControllerRestoreTypeToEntryType(
     77     NavigationController::RestoreType type) {
     78   switch (type) {
     79     case NavigationController::RESTORE_CURRENT_SESSION:
     80       return NavigationEntryImpl::RESTORE_CURRENT_SESSION;
     81     case NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY:
     82       return NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY;
     83     case NavigationController::RESTORE_LAST_SESSION_CRASHED:
     84       return NavigationEntryImpl::RESTORE_LAST_SESSION_CRASHED;
     85   }
     86   NOTREACHED();
     87   return NavigationEntryImpl::RESTORE_CURRENT_SESSION;
     88 }
     89 
     90 // Configure all the NavigationEntries in entries for restore. This resets
     91 // the transition type to reload and makes sure the content state isn't empty.
     92 void ConfigureEntriesForRestore(
     93     std::vector<linked_ptr<NavigationEntryImpl> >* entries,
     94     NavigationController::RestoreType type) {
     95   for (size_t i = 0; i < entries->size(); ++i) {
     96     // Use a transition type of reload so that we don't incorrectly increase
     97     // the typed count.
     98     (*entries)[i]->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
     99     (*entries)[i]->set_restore_type(ControllerRestoreTypeToEntryType(type));
    100     // NOTE(darin): This code is only needed for backwards compat.
    101     SetPageStateIfEmpty((*entries)[i].get());
    102   }
    103 }
    104 
    105 // There are two general cases where a navigation is in page:
    106 // 1. A fragment navigation, in which the url is kept the same except for the
    107 //    reference fragment.
    108 // 2. A history API navigation (pushState and replaceState). This case is
    109 //    always in-page, but the urls are not guaranteed to match excluding the
    110 //    fragment. The relevant spec allows pushState/replaceState to any URL on
    111 //    the same origin.
    112 // However, due to reloads, even identical urls are *not* guaranteed to be
    113 // in-page navigations, we have to trust the renderer almost entirely.
    114 // The one thing we do know is that cross-origin navigations will *never* be
    115 // in-page. Therefore, trust the renderer if the URLs are on the same origin,
    116 // and assume the renderer is malicious if a cross-origin navigation claims to
    117 // be in-page.
    118 bool AreURLsInPageNavigation(const GURL& existing_url,
    119                              const GURL& new_url,
    120                              bool renderer_says_in_page,
    121                              RenderFrameHost* rfh) {
    122   WebPreferences prefs = rfh->GetRenderViewHost()->GetWebkitPreferences();
    123   bool is_same_origin = existing_url.is_empty() ||
    124                         // TODO(japhet): We should only permit navigations
    125                         // originating from about:blank to be in-page if the
    126                         // about:blank is the first document that frame loaded.
    127                         // We don't have sufficient information to identify
    128                         // that case at the moment, so always allow about:blank
    129                         // for now.
    130                         existing_url == GURL(url::kAboutBlankURL) ||
    131                         existing_url.GetOrigin() == new_url.GetOrigin() ||
    132                         !prefs.web_security_enabled ||
    133                         (prefs.allow_universal_access_from_file_urls &&
    134                          existing_url.SchemeIs(url::kFileScheme));
    135   if (!is_same_origin && renderer_says_in_page)
    136       rfh->GetProcess()->ReceivedBadMessage();
    137   return is_same_origin && renderer_says_in_page;
    138 }
    139 
    140 // Determines whether or not we should be carrying over a user agent override
    141 // between two NavigationEntries.
    142 bool ShouldKeepOverride(const NavigationEntry* last_entry) {
    143   return last_entry && last_entry->GetIsOverridingUserAgent();
    144 }
    145 
    146 }  // namespace
    147 
    148 // NavigationControllerImpl ----------------------------------------------------
    149 
    150 const size_t kMaxEntryCountForTestingNotSet = static_cast<size_t>(-1);
    151 
    152 // static
    153 size_t NavigationControllerImpl::max_entry_count_for_testing_ =
    154     kMaxEntryCountForTestingNotSet;
    155 
    156 // Should Reload check for post data? The default is true, but is set to false
    157 // when testing.
    158 static bool g_check_for_repost = true;
    159 
    160 // static
    161 NavigationEntry* NavigationController::CreateNavigationEntry(
    162       const GURL& url,
    163       const Referrer& referrer,
    164       ui::PageTransition transition,
    165       bool is_renderer_initiated,
    166       const std::string& extra_headers,
    167       BrowserContext* browser_context) {
    168   // Allow the browser URL handler to rewrite the URL. This will, for example,
    169   // remove "view-source:" from the beginning of the URL to get the URL that
    170   // will actually be loaded. This real URL won't be shown to the user, just
    171   // used internally.
    172   GURL loaded_url(url);
    173   bool reverse_on_redirect = false;
    174   BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
    175       &loaded_url, browser_context, &reverse_on_redirect);
    176 
    177   NavigationEntryImpl* entry = new NavigationEntryImpl(
    178       NULL,  // The site instance for tabs is sent on navigation
    179              // (WebContents::GetSiteInstance).
    180       -1,
    181       loaded_url,
    182       referrer,
    183       base::string16(),
    184       transition,
    185       is_renderer_initiated);
    186   entry->SetVirtualURL(url);
    187   entry->set_user_typed_url(url);
    188   entry->set_update_virtual_url_with_url(reverse_on_redirect);
    189   entry->set_extra_headers(extra_headers);
    190   return entry;
    191 }
    192 
    193 // static
    194 void NavigationController::DisablePromptOnRepost() {
    195   g_check_for_repost = false;
    196 }
    197 
    198 base::Time NavigationControllerImpl::TimeSmoother::GetSmoothedTime(
    199     base::Time t) {
    200   // If |t| is between the water marks, we're in a run of duplicates
    201   // or just getting out of it, so increase the high-water mark to get
    202   // a time that probably hasn't been used before and return it.
    203   if (low_water_mark_ <= t && t <= high_water_mark_) {
    204     high_water_mark_ += base::TimeDelta::FromMicroseconds(1);
    205     return high_water_mark_;
    206   }
    207 
    208   // Otherwise, we're clear of the last duplicate run, so reset the
    209   // water marks.
    210   low_water_mark_ = high_water_mark_ = t;
    211   return t;
    212 }
    213 
    214 NavigationControllerImpl::NavigationControllerImpl(
    215     NavigationControllerDelegate* delegate,
    216     BrowserContext* browser_context)
    217     : browser_context_(browser_context),
    218       pending_entry_(NULL),
    219       last_committed_entry_index_(-1),
    220       pending_entry_index_(-1),
    221       transient_entry_index_(-1),
    222       delegate_(delegate),
    223       max_restored_page_id_(-1),
    224       ssl_manager_(this),
    225       needs_reload_(false),
    226       is_initial_navigation_(true),
    227       in_navigate_to_pending_entry_(false),
    228       pending_reload_(NO_RELOAD),
    229       get_timestamp_callback_(base::Bind(&base::Time::Now)),
    230       screenshot_manager_(new NavigationEntryScreenshotManager(this)) {
    231   DCHECK(browser_context_);
    232 }
    233 
    234 NavigationControllerImpl::~NavigationControllerImpl() {
    235   DiscardNonCommittedEntriesInternal();
    236 }
    237 
    238 WebContents* NavigationControllerImpl::GetWebContents() const {
    239   return delegate_->GetWebContents();
    240 }
    241 
    242 BrowserContext* NavigationControllerImpl::GetBrowserContext() const {
    243   return browser_context_;
    244 }
    245 
    246 void NavigationControllerImpl::SetBrowserContext(
    247     BrowserContext* browser_context) {
    248   browser_context_ = browser_context;
    249 }
    250 
    251 void NavigationControllerImpl::Restore(
    252     int selected_navigation,
    253     RestoreType type,
    254     std::vector<NavigationEntry*>* entries) {
    255   // Verify that this controller is unused and that the input is valid.
    256   DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
    257   DCHECK(selected_navigation >= 0 &&
    258          selected_navigation < static_cast<int>(entries->size()));
    259 
    260   needs_reload_ = true;
    261   for (size_t i = 0; i < entries->size(); ++i) {
    262     NavigationEntryImpl* entry =
    263         NavigationEntryImpl::FromNavigationEntry((*entries)[i]);
    264     entries_.push_back(linked_ptr<NavigationEntryImpl>(entry));
    265   }
    266   entries->clear();
    267 
    268   // And finish the restore.
    269   FinishRestore(selected_navigation, type);
    270 }
    271 
    272 void NavigationControllerImpl::Reload(bool check_for_repost) {
    273   ReloadInternal(check_for_repost, RELOAD);
    274 }
    275 void NavigationControllerImpl::ReloadIgnoringCache(bool check_for_repost) {
    276   ReloadInternal(check_for_repost, RELOAD_IGNORING_CACHE);
    277 }
    278 void NavigationControllerImpl::ReloadOriginalRequestURL(bool check_for_repost) {
    279   ReloadInternal(check_for_repost, RELOAD_ORIGINAL_REQUEST_URL);
    280 }
    281 
    282 void NavigationControllerImpl::ReloadInternal(bool check_for_repost,
    283                                               ReloadType reload_type) {
    284   if (transient_entry_index_ != -1) {
    285     // If an interstitial is showing, treat a reload as a navigation to the
    286     // transient entry's URL.
    287     NavigationEntryImpl* transient_entry =
    288         NavigationEntryImpl::FromNavigationEntry(GetTransientEntry());
    289     if (!transient_entry)
    290       return;
    291     LoadURL(transient_entry->GetURL(),
    292             Referrer(),
    293             ui::PAGE_TRANSITION_RELOAD,
    294             transient_entry->extra_headers());
    295     return;
    296   }
    297 
    298   NavigationEntryImpl* entry = NULL;
    299   int current_index = -1;
    300 
    301   // If we are reloading the initial navigation, just use the current
    302   // pending entry.  Otherwise look up the current entry.
    303   if (IsInitialNavigation() && pending_entry_) {
    304     entry = pending_entry_;
    305     // The pending entry might be in entries_ (e.g., after a Clone), so we
    306     // should also update the current_index.
    307     current_index = pending_entry_index_;
    308   } else {
    309     DiscardNonCommittedEntriesInternal();
    310     current_index = GetCurrentEntryIndex();
    311     if (current_index != -1) {
    312       entry = NavigationEntryImpl::FromNavigationEntry(
    313           GetEntryAtIndex(current_index));
    314     }
    315   }
    316 
    317   // If we are no where, then we can't reload.  TODO(darin): We should add a
    318   // CanReload method.
    319   if (!entry)
    320     return;
    321 
    322   if (reload_type == NavigationControllerImpl::RELOAD_ORIGINAL_REQUEST_URL &&
    323       entry->GetOriginalRequestURL().is_valid() && !entry->GetHasPostData()) {
    324     // We may have been redirected when navigating to the current URL.
    325     // Use the URL the user originally intended to visit, if it's valid and if a
    326     // POST wasn't involved; the latter case avoids issues with sending data to
    327     // the wrong page.
    328     entry->SetURL(entry->GetOriginalRequestURL());
    329     entry->SetReferrer(Referrer());
    330   }
    331 
    332   if (g_check_for_repost && check_for_repost &&
    333       entry->GetHasPostData()) {
    334     // The user is asking to reload a page with POST data. Prompt to make sure
    335     // they really want to do this. If they do, the dialog will call us back
    336     // with check_for_repost = false.
    337     delegate_->NotifyBeforeFormRepostWarningShow();
    338 
    339     pending_reload_ = reload_type;
    340     delegate_->ActivateAndShowRepostFormWarningDialog();
    341   } else {
    342     if (!IsInitialNavigation())
    343       DiscardNonCommittedEntriesInternal();
    344 
    345     // If we are reloading an entry that no longer belongs to the current
    346     // site instance (for example, refreshing a page for just installed app),
    347     // the reload must happen in a new process.
    348     // The new entry must have a new page_id and site instance, so it behaves
    349     // as new navigation (which happens to clear forward history).
    350     // Tabs that are discarded due to low memory conditions may not have a site
    351     // instance, and should not be treated as a cross-site reload.
    352     SiteInstanceImpl* site_instance = entry->site_instance();
    353     // Permit reloading guests without further checks.
    354     bool is_isolated_guest = site_instance && site_instance->HasProcess() &&
    355         site_instance->GetProcess()->IsIsolatedGuest();
    356     if (!is_isolated_guest && site_instance &&
    357         site_instance->HasWrongProcessForURL(entry->GetURL())) {
    358       // Create a navigation entry that resembles the current one, but do not
    359       // copy page id, site instance, content state, or timestamp.
    360       NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry(
    361           CreateNavigationEntry(
    362               entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(),
    363               false, entry->extra_headers(), browser_context_));
    364 
    365       // Mark the reload type as NO_RELOAD, so navigation will not be considered
    366       // a reload in the renderer.
    367       reload_type = NavigationController::NO_RELOAD;
    368 
    369       nav_entry->set_should_replace_entry(true);
    370       pending_entry_ = nav_entry;
    371     } else {
    372       pending_entry_ = entry;
    373       pending_entry_index_ = current_index;
    374 
    375       // The title of the page being reloaded might have been removed in the
    376       // meanwhile, so we need to revert to the default title upon reload and
    377       // invalidate the previously cached title (SetTitle will do both).
    378       // See Chromium issue 96041.
    379       pending_entry_->SetTitle(base::string16());
    380 
    381       pending_entry_->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
    382     }
    383 
    384     NavigateToPendingEntry(reload_type);
    385   }
    386 }
    387 
    388 void NavigationControllerImpl::CancelPendingReload() {
    389   DCHECK(pending_reload_ != NO_RELOAD);
    390   pending_reload_ = NO_RELOAD;
    391 }
    392 
    393 void NavigationControllerImpl::ContinuePendingReload() {
    394   if (pending_reload_ == NO_RELOAD) {
    395     NOTREACHED();
    396   } else {
    397     ReloadInternal(false, pending_reload_);
    398     pending_reload_ = NO_RELOAD;
    399   }
    400 }
    401 
    402 bool NavigationControllerImpl::IsInitialNavigation() const {
    403   return is_initial_navigation_;
    404 }
    405 
    406 NavigationEntryImpl* NavigationControllerImpl::GetEntryWithPageID(
    407   SiteInstance* instance, int32 page_id) const {
    408   int index = GetEntryIndexWithPageID(instance, page_id);
    409   return (index != -1) ? entries_[index].get() : NULL;
    410 }
    411 
    412 void NavigationControllerImpl::LoadEntry(NavigationEntryImpl* entry) {
    413   // When navigating to a new page, we don't know for sure if we will actually
    414   // end up leaving the current page.  The new page load could for example
    415   // result in a download or a 'no content' response (e.g., a mailto: URL).
    416   SetPendingEntry(entry);
    417   NavigateToPendingEntry(NO_RELOAD);
    418 }
    419 
    420 void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) {
    421   DiscardNonCommittedEntriesInternal();
    422   pending_entry_ = entry;
    423   NotificationService::current()->Notify(
    424       NOTIFICATION_NAV_ENTRY_PENDING,
    425       Source<NavigationController>(this),
    426       Details<NavigationEntry>(entry));
    427 }
    428 
    429 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const {
    430   if (transient_entry_index_ != -1)
    431     return entries_[transient_entry_index_].get();
    432   if (pending_entry_)
    433     return pending_entry_;
    434   return GetLastCommittedEntry();
    435 }
    436 
    437 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const {
    438   if (transient_entry_index_ != -1)
    439     return entries_[transient_entry_index_].get();
    440   // The pending entry is safe to return for new (non-history), browser-
    441   // initiated navigations.  Most renderer-initiated navigations should not
    442   // show the pending entry, to prevent URL spoof attacks.
    443   //
    444   // We make an exception for renderer-initiated navigations in new tabs, as
    445   // long as no other page has tried to access the initial empty document in
    446   // the new tab.  If another page modifies this blank page, a URL spoof is
    447   // possible, so we must stop showing the pending entry.
    448   bool safe_to_show_pending =
    449       pending_entry_ &&
    450       // Require a new navigation.
    451       pending_entry_->GetPageID() == -1 &&
    452       // Require either browser-initiated or an unmodified new tab.
    453       (!pending_entry_->is_renderer_initiated() || IsUnmodifiedBlankTab());
    454 
    455   // Also allow showing the pending entry for history navigations in a new tab,
    456   // such as Ctrl+Back.  In this case, no existing page is visible and no one
    457   // can script the new tab before it commits.
    458   if (!safe_to_show_pending &&
    459       pending_entry_ &&
    460       pending_entry_->GetPageID() != -1 &&
    461       IsInitialNavigation() &&
    462       !pending_entry_->is_renderer_initiated())
    463     safe_to_show_pending = true;
    464 
    465   if (safe_to_show_pending)
    466     return pending_entry_;
    467   return GetLastCommittedEntry();
    468 }
    469 
    470 int NavigationControllerImpl::GetCurrentEntryIndex() const {
    471   if (transient_entry_index_ != -1)
    472     return transient_entry_index_;
    473   if (pending_entry_index_ != -1)
    474     return pending_entry_index_;
    475   return last_committed_entry_index_;
    476 }
    477 
    478 NavigationEntry* NavigationControllerImpl::GetLastCommittedEntry() const {
    479   if (last_committed_entry_index_ == -1)
    480     return NULL;
    481   return entries_[last_committed_entry_index_].get();
    482 }
    483 
    484 bool NavigationControllerImpl::CanViewSource() const {
    485   const std::string& mime_type = delegate_->GetContentsMimeType();
    486   bool is_viewable_mime_type = net::IsSupportedNonImageMimeType(mime_type) &&
    487       !net::IsSupportedMediaMimeType(mime_type);
    488   NavigationEntry* visible_entry = GetVisibleEntry();
    489   return visible_entry && !visible_entry->IsViewSourceMode() &&
    490       is_viewable_mime_type && !delegate_->GetInterstitialPage();
    491 }
    492 
    493 int NavigationControllerImpl::GetLastCommittedEntryIndex() const {
    494   return last_committed_entry_index_;
    495 }
    496 
    497 int NavigationControllerImpl::GetEntryCount() const {
    498   DCHECK(entries_.size() <= max_entry_count());
    499   return static_cast<int>(entries_.size());
    500 }
    501 
    502 NavigationEntry* NavigationControllerImpl::GetEntryAtIndex(
    503     int index) const {
    504   return entries_.at(index).get();
    505 }
    506 
    507 NavigationEntry* NavigationControllerImpl::GetEntryAtOffset(
    508     int offset) const {
    509   int index = GetIndexForOffset(offset);
    510   if (index < 0 || index >= GetEntryCount())
    511     return NULL;
    512 
    513   return entries_[index].get();
    514 }
    515 
    516 int NavigationControllerImpl::GetIndexForOffset(int offset) const {
    517   return GetCurrentEntryIndex() + offset;
    518 }
    519 
    520 void NavigationControllerImpl::TakeScreenshot() {
    521   screenshot_manager_->TakeScreenshot();
    522 }
    523 
    524 void NavigationControllerImpl::SetScreenshotManager(
    525     NavigationEntryScreenshotManager* manager) {
    526   screenshot_manager_.reset(manager ? manager :
    527                             new NavigationEntryScreenshotManager(this));
    528 }
    529 
    530 bool NavigationControllerImpl::CanGoBack() const {
    531   return entries_.size() > 1 && GetCurrentEntryIndex() > 0;
    532 }
    533 
    534 bool NavigationControllerImpl::CanGoForward() const {
    535   int index = GetCurrentEntryIndex();
    536   return index >= 0 && index < (static_cast<int>(entries_.size()) - 1);
    537 }
    538 
    539 bool NavigationControllerImpl::CanGoToOffset(int offset) const {
    540   int index = GetIndexForOffset(offset);
    541   return index >= 0 && index < GetEntryCount();
    542 }
    543 
    544 void NavigationControllerImpl::GoBack() {
    545   if (!CanGoBack()) {
    546     NOTREACHED();
    547     return;
    548   }
    549 
    550   // Base the navigation on where we are now...
    551   int current_index = GetCurrentEntryIndex();
    552 
    553   DiscardNonCommittedEntries();
    554 
    555   pending_entry_index_ = current_index - 1;
    556   entries_[pending_entry_index_]->SetTransitionType(
    557       ui::PageTransitionFromInt(
    558           entries_[pending_entry_index_]->GetTransitionType() |
    559           ui::PAGE_TRANSITION_FORWARD_BACK));
    560   NavigateToPendingEntry(NO_RELOAD);
    561 }
    562 
    563 void NavigationControllerImpl::GoForward() {
    564   if (!CanGoForward()) {
    565     NOTREACHED();
    566     return;
    567   }
    568 
    569   bool transient = (transient_entry_index_ != -1);
    570 
    571   // Base the navigation on where we are now...
    572   int current_index = GetCurrentEntryIndex();
    573 
    574   DiscardNonCommittedEntries();
    575 
    576   pending_entry_index_ = current_index;
    577   // If there was a transient entry, we removed it making the current index
    578   // the next page.
    579   if (!transient)
    580     pending_entry_index_++;
    581 
    582   entries_[pending_entry_index_]->SetTransitionType(
    583       ui::PageTransitionFromInt(
    584           entries_[pending_entry_index_]->GetTransitionType() |
    585           ui::PAGE_TRANSITION_FORWARD_BACK));
    586   NavigateToPendingEntry(NO_RELOAD);
    587 }
    588 
    589 void NavigationControllerImpl::GoToIndex(int index) {
    590   if (index < 0 || index >= static_cast<int>(entries_.size())) {
    591     NOTREACHED();
    592     return;
    593   }
    594 
    595   if (transient_entry_index_ != -1) {
    596     if (index == transient_entry_index_) {
    597       // Nothing to do when navigating to the transient.
    598       return;
    599     }
    600     if (index > transient_entry_index_) {
    601       // Removing the transient is goint to shift all entries by 1.
    602       index--;
    603     }
    604   }
    605 
    606   DiscardNonCommittedEntries();
    607 
    608   pending_entry_index_ = index;
    609   entries_[pending_entry_index_]->SetTransitionType(
    610       ui::PageTransitionFromInt(
    611           entries_[pending_entry_index_]->GetTransitionType() |
    612           ui::PAGE_TRANSITION_FORWARD_BACK));
    613   NavigateToPendingEntry(NO_RELOAD);
    614 }
    615 
    616 void NavigationControllerImpl::GoToOffset(int offset) {
    617   if (!CanGoToOffset(offset))
    618     return;
    619 
    620   GoToIndex(GetIndexForOffset(offset));
    621 }
    622 
    623 bool NavigationControllerImpl::RemoveEntryAtIndex(int index) {
    624   if (index == last_committed_entry_index_ ||
    625       index == pending_entry_index_)
    626     return false;
    627 
    628   RemoveEntryAtIndexInternal(index);
    629   return true;
    630 }
    631 
    632 void NavigationControllerImpl::UpdateVirtualURLToURL(
    633     NavigationEntryImpl* entry, const GURL& new_url) {
    634   GURL new_virtual_url(new_url);
    635   if (BrowserURLHandlerImpl::GetInstance()->ReverseURLRewrite(
    636           &new_virtual_url, entry->GetVirtualURL(), browser_context_)) {
    637     entry->SetVirtualURL(new_virtual_url);
    638   }
    639 }
    640 
    641 void NavigationControllerImpl::LoadURL(
    642     const GURL& url,
    643     const Referrer& referrer,
    644     ui::PageTransition transition,
    645     const std::string& extra_headers) {
    646   LoadURLParams params(url);
    647   params.referrer = referrer;
    648   params.transition_type = transition;
    649   params.extra_headers = extra_headers;
    650   LoadURLWithParams(params);
    651 }
    652 
    653 void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
    654   TRACE_EVENT1("browser,navigation",
    655                "NavigationControllerImpl::LoadURLWithParams",
    656                "url", params.url.possibly_invalid_spec());
    657   if (HandleDebugURL(params.url, params.transition_type)) {
    658     // If Telemetry is running, allow the URL load to proceed as if it's
    659     // unhandled, otherwise Telemetry can't tell if Navigation completed.
    660     if (!CommandLine::ForCurrentProcess()->HasSwitch(
    661             cc::switches::kEnableGpuBenchmarking))
    662       return;
    663   }
    664 
    665   // Any renderer-side debug URLs or javascript: URLs should be ignored if the
    666   // renderer process is not live, unless it is the initial navigation of the
    667   // tab.
    668   if (IsRendererDebugURL(params.url)) {
    669     // TODO(creis): Find the RVH for the correct frame.
    670     if (!delegate_->GetRenderViewHost()->IsRenderViewLive() &&
    671         !IsInitialNavigation())
    672       return;
    673   }
    674 
    675   // Checks based on params.load_type.
    676   switch (params.load_type) {
    677     case LOAD_TYPE_DEFAULT:
    678       break;
    679     case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST:
    680       if (!params.url.SchemeIs(url::kHttpScheme) &&
    681           !params.url.SchemeIs(url::kHttpsScheme)) {
    682         NOTREACHED() << "Http post load must use http(s) scheme.";
    683         return;
    684       }
    685       break;
    686     case LOAD_TYPE_DATA:
    687       if (!params.url.SchemeIs(url::kDataScheme)) {
    688         NOTREACHED() << "Data load must use data scheme.";
    689         return;
    690       }
    691       break;
    692     default:
    693       NOTREACHED();
    694       break;
    695   };
    696 
    697   // The user initiated a load, we don't need to reload anymore.
    698   needs_reload_ = false;
    699 
    700   bool override = false;
    701   switch (params.override_user_agent) {
    702     case UA_OVERRIDE_INHERIT:
    703       override = ShouldKeepOverride(GetLastCommittedEntry());
    704       break;
    705     case UA_OVERRIDE_TRUE:
    706       override = true;
    707       break;
    708     case UA_OVERRIDE_FALSE:
    709       override = false;
    710       break;
    711     default:
    712       NOTREACHED();
    713       break;
    714   }
    715 
    716   NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
    717       CreateNavigationEntry(
    718           params.url,
    719           params.referrer,
    720           params.transition_type,
    721           params.is_renderer_initiated,
    722           params.extra_headers,
    723           browser_context_));
    724   if (params.frame_tree_node_id != -1)
    725     entry->set_frame_tree_node_id(params.frame_tree_node_id);
    726   if (params.redirect_chain.size() > 0)
    727     entry->SetRedirectChain(params.redirect_chain);
    728   if (params.should_replace_current_entry)
    729     entry->set_should_replace_entry(true);
    730   entry->set_should_clear_history_list(params.should_clear_history_list);
    731   entry->SetIsOverridingUserAgent(override);
    732   entry->set_transferred_global_request_id(
    733       params.transferred_global_request_id);
    734   entry->SetFrameToNavigate(params.frame_name);
    735 
    736   switch (params.load_type) {
    737     case LOAD_TYPE_DEFAULT:
    738       break;
    739     case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST:
    740       entry->SetHasPostData(true);
    741       entry->SetBrowserInitiatedPostData(
    742           params.browser_initiated_post_data.get());
    743       break;
    744     case LOAD_TYPE_DATA:
    745       entry->SetBaseURLForDataURL(params.base_url_for_data_url);
    746       entry->SetVirtualURL(params.virtual_url_for_data_url);
    747       entry->SetCanLoadLocalResources(params.can_load_local_resources);
    748       break;
    749     default:
    750       NOTREACHED();
    751       break;
    752   };
    753 
    754   LoadEntry(entry);
    755 }
    756 
    757 bool NavigationControllerImpl::RendererDidNavigate(
    758     RenderFrameHost* rfh,
    759     const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
    760     LoadCommittedDetails* details) {
    761   is_initial_navigation_ = false;
    762 
    763   // Save the previous state before we clobber it.
    764   if (GetLastCommittedEntry()) {
    765     details->previous_url = GetLastCommittedEntry()->GetURL();
    766     details->previous_entry_index = GetLastCommittedEntryIndex();
    767   } else {
    768     details->previous_url = GURL();
    769     details->previous_entry_index = -1;
    770   }
    771 
    772   // If we have a pending entry at this point, it should have a SiteInstance.
    773   // Restored entries start out with a null SiteInstance, but we should have
    774   // assigned one in NavigateToPendingEntry.
    775   DCHECK(pending_entry_index_ == -1 || pending_entry_->site_instance());
    776 
    777   // If we are doing a cross-site reload, we need to replace the existing
    778   // navigation entry, not add another entry to the history. This has the side
    779   // effect of removing forward browsing history, if such existed.
    780   // Or if we are doing a cross-site redirect navigation,
    781   // we will do a similar thing.
    782   details->did_replace_entry =
    783       pending_entry_ && pending_entry_->should_replace_entry();
    784 
    785   // Do navigation-type specific actions. These will make and commit an entry.
    786   details->type = ClassifyNavigation(rfh, params);
    787 
    788   // is_in_page must be computed before the entry gets committed.
    789   details->is_in_page = AreURLsInPageNavigation(rfh->GetLastCommittedURL(),
    790       params.url, params.was_within_same_page, rfh);
    791 
    792   switch (details->type) {
    793     case NAVIGATION_TYPE_NEW_PAGE:
    794       RendererDidNavigateToNewPage(rfh, params, details->did_replace_entry);
    795       break;
    796     case NAVIGATION_TYPE_EXISTING_PAGE:
    797       RendererDidNavigateToExistingPage(rfh, params);
    798       break;
    799     case NAVIGATION_TYPE_SAME_PAGE:
    800       RendererDidNavigateToSamePage(rfh, params);
    801       break;
    802     case NAVIGATION_TYPE_IN_PAGE:
    803       RendererDidNavigateInPage(rfh, params, &details->did_replace_entry);
    804       break;
    805     case NAVIGATION_TYPE_NEW_SUBFRAME:
    806       RendererDidNavigateNewSubframe(rfh, params);
    807       break;
    808     case NAVIGATION_TYPE_AUTO_SUBFRAME:
    809       if (!RendererDidNavigateAutoSubframe(rfh, params))
    810         return false;
    811       break;
    812     case NAVIGATION_TYPE_NAV_IGNORE:
    813       // If a pending navigation was in progress, this canceled it.  We should
    814       // discard it and make sure it is removed from the URL bar.  After that,
    815       // there is nothing we can do with this navigation, so we just return to
    816       // the caller that nothing has happened.
    817       if (pending_entry_) {
    818         DiscardNonCommittedEntries();
    819         delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_URL);
    820       }
    821       return false;
    822     default:
    823       NOTREACHED();
    824   }
    825 
    826   // At this point, we know that the navigation has just completed, so
    827   // record the time.
    828   //
    829   // TODO(akalin): Use "sane time" as described in
    830   // http://www.chromium.org/developers/design-documents/sane-time .
    831   base::Time timestamp =
    832       time_smoother_.GetSmoothedTime(get_timestamp_callback_.Run());
    833   DVLOG(1) << "Navigation finished at (smoothed) timestamp "
    834            << timestamp.ToInternalValue();
    835 
    836   // We should not have a pending entry anymore.  Clear it again in case any
    837   // error cases above forgot to do so.
    838   DiscardNonCommittedEntriesInternal();
    839 
    840   // All committed entries should have nonempty content state so WebKit doesn't
    841   // get confused when we go back to them (see the function for details).
    842   DCHECK(params.page_state.IsValid());
    843   NavigationEntryImpl* active_entry =
    844       NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
    845   active_entry->SetTimestamp(timestamp);
    846   active_entry->SetHttpStatusCode(params.http_status_code);
    847   active_entry->SetPageState(params.page_state);
    848   active_entry->SetRedirectChain(params.redirects);
    849 
    850   // Use histogram to track memory impact of redirect chain because it's now
    851   // not cleared for committed entries.
    852   size_t redirect_chain_size = 0;
    853   for (size_t i = 0; i < params.redirects.size(); ++i) {
    854     redirect_chain_size += params.redirects[i].spec().length();
    855   }
    856   UMA_HISTOGRAM_COUNTS("Navigation.RedirectChainSize", redirect_chain_size);
    857 
    858   // Once it is committed, we no longer need to track several pieces of state on
    859   // the entry.
    860   active_entry->ResetForCommit();
    861 
    862   // The active entry's SiteInstance should match our SiteInstance.
    863   // TODO(creis): This check won't pass for subframes until we create entries
    864   // for subframe navigations.
    865   if (ui::PageTransitionIsMainFrame(params.transition))
    866     CHECK(active_entry->site_instance() == rfh->GetSiteInstance());
    867 
    868   // Remember the bindings the renderer process has at this point, so that
    869   // we do not grant this entry additional bindings if we come back to it.
    870   active_entry->SetBindings(
    871       static_cast<RenderFrameHostImpl*>(rfh)->GetEnabledBindings());
    872 
    873   // Now prep the rest of the details for the notification and broadcast.
    874   details->entry = active_entry;
    875   details->is_main_frame =
    876       ui::PageTransitionIsMainFrame(params.transition);
    877   details->serialized_security_info = params.security_info;
    878   details->http_status_code = params.http_status_code;
    879   NotifyNavigationEntryCommitted(details);
    880 
    881   return true;
    882 }
    883 
    884 NavigationType NavigationControllerImpl::ClassifyNavigation(
    885     RenderFrameHost* rfh,
    886     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const {
    887   if (params.page_id == -1) {
    888     // TODO(nasko, creis):  An out-of-process child frame has no way of
    889     // knowing the page_id of its parent, so it is passing back -1. The
    890     // semantics here should be re-evaluated during session history refactor
    891     // (see http://crbug.com/236848). For now, we assume this means the
    892     // child frame loaded and proceed. Note that this may do the wrong thing
    893     // for cross-process AUTO_SUBFRAME navigations.
    894     if (rfh->IsCrossProcessSubframe())
    895       return NAVIGATION_TYPE_NEW_SUBFRAME;
    896 
    897     // The renderer generates the page IDs, and so if it gives us the invalid
    898     // page ID (-1) we know it didn't actually navigate. This happens in a few
    899     // cases:
    900     //
    901     // - If a page makes a popup navigated to about blank, and then writes
    902     //   stuff like a subframe navigated to a real page. We'll get the commit
    903     //   for the subframe, but there won't be any commit for the outer page.
    904     //
    905     // - We were also getting these for failed loads (for example, bug 21849).
    906     //   The guess is that we get a "load commit" for the alternate error page,
    907     //   but that doesn't affect the page ID, so we get the "old" one, which
    908     //   could be invalid. This can also happen for a cross-site transition
    909     //   that causes us to swap processes. Then the error page load will be in
    910     //   a new process with no page IDs ever assigned (and hence a -1 value),
    911     //   yet the navigation controller still might have previous pages in its
    912     //   list.
    913     //
    914     // In these cases, there's nothing we can do with them, so ignore.
    915     return NAVIGATION_TYPE_NAV_IGNORE;
    916   }
    917 
    918   if (params.page_id > delegate_->GetMaxPageIDForSiteInstance(
    919           rfh->GetSiteInstance())) {
    920     // Greater page IDs than we've ever seen before are new pages. We may or may
    921     // not have a pending entry for the page, and this may or may not be the
    922     // main frame.
    923     if (ui::PageTransitionIsMainFrame(params.transition))
    924       return NAVIGATION_TYPE_NEW_PAGE;
    925 
    926     // When this is a new subframe navigation, we should have a committed page
    927     // for which it's a suframe in. This may not be the case when an iframe is
    928     // navigated on a popup navigated to about:blank (the iframe would be
    929     // written into the popup by script on the main page). For these cases,
    930     // there isn't any navigation stuff we can do, so just ignore it.
    931     if (!GetLastCommittedEntry())
    932       return NAVIGATION_TYPE_NAV_IGNORE;
    933 
    934     // Valid subframe navigation.
    935     return NAVIGATION_TYPE_NEW_SUBFRAME;
    936   }
    937 
    938   // We only clear the session history when navigating to a new page.
    939   DCHECK(!params.history_list_was_cleared);
    940 
    941   // Now we know that the notification is for an existing page. Find that entry.
    942   int existing_entry_index = GetEntryIndexWithPageID(
    943       rfh->GetSiteInstance(),
    944       params.page_id);
    945   if (existing_entry_index == -1) {
    946     // The page was not found. It could have been pruned because of the limit on
    947     // back/forward entries (not likely since we'll usually tell it to navigate
    948     // to such entries). It could also mean that the renderer is smoking crack.
    949     NOTREACHED();
    950 
    951     // Because the unknown entry has committed, we risk showing the wrong URL in
    952     // release builds. Instead, we'll kill the renderer process to be safe.
    953     LOG(ERROR) << "terminating renderer for bad navigation: " << params.url;
    954     RecordAction(base::UserMetricsAction("BadMessageTerminate_NC"));
    955 
    956     // Temporary code so we can get more information.  Format:
    957     //  http://url/foo.html#page1#max3#frame1#ids:2_Nx,1_1x,3_2
    958     std::string temp = params.url.spec();
    959     temp.append("#page");
    960     temp.append(base::IntToString(params.page_id));
    961     temp.append("#max");
    962     temp.append(base::IntToString(delegate_->GetMaxPageID()));
    963     temp.append("#frame");
    964     temp.append(base::IntToString(rfh->GetRoutingID()));
    965     temp.append("#ids");
    966     for (int i = 0; i < static_cast<int>(entries_.size()); ++i) {
    967       // Append entry metadata (e.g., 3_7x):
    968       //  3: page_id
    969       //  7: SiteInstance ID, or N for null
    970       //  x: appended if not from the current SiteInstance
    971       temp.append(base::IntToString(entries_[i]->GetPageID()));
    972       temp.append("_");
    973       if (entries_[i]->site_instance())
    974         temp.append(base::IntToString(entries_[i]->site_instance()->GetId()));
    975       else
    976         temp.append("N");
    977       if (entries_[i]->site_instance() != rfh->GetSiteInstance())
    978         temp.append("x");
    979       temp.append(",");
    980     }
    981     GURL url(temp);
    982     static_cast<RenderFrameHostImpl*>(rfh)->render_view_host()->Send(
    983         new ViewMsg_TempCrashWithData(url));
    984     return NAVIGATION_TYPE_NAV_IGNORE;
    985   }
    986   NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get();
    987 
    988   if (!ui::PageTransitionIsMainFrame(params.transition)) {
    989     // All manual subframes would get new IDs and were handled above, so we
    990     // know this is auto. Since the current page was found in the navigation
    991     // entry list, we're guaranteed to have a last committed entry.
    992     DCHECK(GetLastCommittedEntry());
    993     return NAVIGATION_TYPE_AUTO_SUBFRAME;
    994   }
    995 
    996   // Anything below here we know is a main frame navigation.
    997   if (pending_entry_ &&
    998       !pending_entry_->is_renderer_initiated() &&
    999       existing_entry != pending_entry_ &&
   1000       pending_entry_->GetPageID() == -1 &&
   1001       existing_entry == GetLastCommittedEntry()) {
   1002     // In this case, we have a pending entry for a URL but WebCore didn't do a
   1003     // new navigation. This happens when you press enter in the URL bar to
   1004     // reload. We will create a pending entry, but WebKit will convert it to
   1005     // a reload since it's the same page and not create a new entry for it
   1006     // (the user doesn't want to have a new back/forward entry when they do
   1007     // this). If this matches the last committed entry, we want to just ignore
   1008     // the pending entry and go back to where we were (the "existing entry").
   1009     return NAVIGATION_TYPE_SAME_PAGE;
   1010   }
   1011 
   1012   // Any toplevel navigations with the same base (minus the reference fragment)
   1013   // are in-page navigations. We weeded out subframe navigations above. Most of
   1014   // the time this doesn't matter since WebKit doesn't tell us about subframe
   1015   // navigations that don't actually navigate, but it can happen when there is
   1016   // an encoding override (it always sends a navigation request).
   1017   if (AreURLsInPageNavigation(existing_entry->GetURL(), params.url,
   1018                               params.was_within_same_page, rfh)) {
   1019     return NAVIGATION_TYPE_IN_PAGE;
   1020   }
   1021 
   1022   // Since we weeded out "new" navigations above, we know this is an existing
   1023   // (back/forward) navigation.
   1024   return NAVIGATION_TYPE_EXISTING_PAGE;
   1025 }
   1026 
   1027 void NavigationControllerImpl::RendererDidNavigateToNewPage(
   1028     RenderFrameHost* rfh,
   1029     const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
   1030     bool replace_entry) {
   1031   NavigationEntryImpl* new_entry;
   1032   bool update_virtual_url;
   1033   // Only make a copy of the pending entry if it is appropriate for the new page
   1034   // that was just loaded.  We verify this at a coarse grain by checking that
   1035   // the SiteInstance hasn't been assigned to something else.
   1036   if (pending_entry_ &&
   1037       (!pending_entry_->site_instance() ||
   1038        pending_entry_->site_instance() == rfh->GetSiteInstance())) {
   1039     new_entry = new NavigationEntryImpl(*pending_entry_);
   1040 
   1041     // Don't use the page type from the pending entry. Some interstitial page
   1042     // may have set the type to interstitial. Once we commit, however, the page
   1043     // type must always be normal.
   1044     new_entry->set_page_type(PAGE_TYPE_NORMAL);
   1045     update_virtual_url = new_entry->update_virtual_url_with_url();
   1046   } else {
   1047     new_entry = new NavigationEntryImpl;
   1048 
   1049     // Find out whether the new entry needs to update its virtual URL on URL
   1050     // change and set up the entry accordingly. This is needed to correctly
   1051     // update the virtual URL when replaceState is called after a pushState.
   1052     GURL url = params.url;
   1053     bool needs_update = false;
   1054     BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
   1055         &url, browser_context_, &needs_update);
   1056     new_entry->set_update_virtual_url_with_url(needs_update);
   1057 
   1058     // When navigating to a new page, give the browser URL handler a chance to
   1059     // update the virtual URL based on the new URL. For example, this is needed
   1060     // to show chrome://bookmarks/#1 when the bookmarks webui extension changes
   1061     // the URL.
   1062     update_virtual_url = needs_update;
   1063   }
   1064 
   1065   if (params.url_is_unreachable)
   1066     new_entry->set_page_type(PAGE_TYPE_ERROR);
   1067   new_entry->SetURL(params.url);
   1068   if (update_virtual_url)
   1069     UpdateVirtualURLToURL(new_entry, params.url);
   1070   new_entry->SetReferrer(params.referrer);
   1071   new_entry->SetPageID(params.page_id);
   1072   new_entry->SetTransitionType(params.transition);
   1073   new_entry->set_site_instance(
   1074       static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
   1075   new_entry->SetHasPostData(params.is_post);
   1076   new_entry->SetPostID(params.post_id);
   1077   new_entry->SetOriginalRequestURL(params.original_request_url);
   1078   new_entry->SetIsOverridingUserAgent(params.is_overriding_user_agent);
   1079 
   1080   // history.pushState() is classified as a navigation to a new page, but
   1081   // sets was_within_same_page to true. In this case, we already have the
   1082   // title and favicon available, so set them immediately.
   1083   if (params.was_within_same_page && GetLastCommittedEntry()) {
   1084     new_entry->SetTitle(GetLastCommittedEntry()->GetTitle());
   1085     new_entry->GetFavicon() = GetLastCommittedEntry()->GetFavicon();
   1086   }
   1087 
   1088   DCHECK(!params.history_list_was_cleared || !replace_entry);
   1089   // The browser requested to clear the session history when it initiated the
   1090   // navigation. Now we know that the renderer has updated its state accordingly
   1091   // and it is safe to also clear the browser side history.
   1092   if (params.history_list_was_cleared) {
   1093     DiscardNonCommittedEntriesInternal();
   1094     entries_.clear();
   1095     last_committed_entry_index_ = -1;
   1096   }
   1097 
   1098   InsertOrReplaceEntry(new_entry, replace_entry);
   1099 }
   1100 
   1101 void NavigationControllerImpl::RendererDidNavigateToExistingPage(
   1102     RenderFrameHost* rfh,
   1103     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
   1104   // We should only get here for main frame navigations.
   1105   DCHECK(ui::PageTransitionIsMainFrame(params.transition));
   1106 
   1107   // This is a back/forward navigation. The existing page for the ID is
   1108   // guaranteed to exist by ClassifyNavigation, and we just need to update it
   1109   // with new information from the renderer.
   1110   int entry_index = GetEntryIndexWithPageID(rfh->GetSiteInstance(),
   1111                                             params.page_id);
   1112   DCHECK(entry_index >= 0 &&
   1113          entry_index < static_cast<int>(entries_.size()));
   1114   NavigationEntryImpl* entry = entries_[entry_index].get();
   1115 
   1116   // The URL may have changed due to redirects.
   1117   entry->SetURL(params.url);
   1118   entry->SetReferrer(params.referrer);
   1119   if (entry->update_virtual_url_with_url())
   1120     UpdateVirtualURLToURL(entry, params.url);
   1121 
   1122   // The redirected to page should not inherit the favicon from the previous
   1123   // page.
   1124   if (ui::PageTransitionIsRedirect(params.transition))
   1125     entry->GetFavicon() = FaviconStatus();
   1126 
   1127   // The site instance will normally be the same except during session restore,
   1128   // when no site instance will be assigned.
   1129   DCHECK(entry->site_instance() == NULL ||
   1130          entry->site_instance() == rfh->GetSiteInstance());
   1131   entry->set_site_instance(
   1132       static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
   1133 
   1134   entry->SetHasPostData(params.is_post);
   1135   entry->SetPostID(params.post_id);
   1136 
   1137   // The entry we found in the list might be pending if the user hit
   1138   // back/forward/reload. This load should commit it (since it's already in the
   1139   // list, we can just discard the pending pointer).  We should also discard the
   1140   // pending entry if it corresponds to a different navigation, since that one
   1141   // is now likely canceled.  If it is not canceled, we will treat it as a new
   1142   // navigation when it arrives, which is also ok.
   1143   //
   1144   // Note that we need to use the "internal" version since we don't want to
   1145   // actually change any other state, just kill the pointer.
   1146   DiscardNonCommittedEntriesInternal();
   1147 
   1148   // If a transient entry was removed, the indices might have changed, so we
   1149   // have to query the entry index again.
   1150   last_committed_entry_index_ =
   1151       GetEntryIndexWithPageID(rfh->GetSiteInstance(), params.page_id);
   1152 }
   1153 
   1154 void NavigationControllerImpl::RendererDidNavigateToSamePage(
   1155     RenderFrameHost* rfh,
   1156     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
   1157   // This mode implies we have a pending entry that's the same as an existing
   1158   // entry for this page ID. This entry is guaranteed to exist by
   1159   // ClassifyNavigation. All we need to do is update the existing entry.
   1160   NavigationEntryImpl* existing_entry = GetEntryWithPageID(
   1161       rfh->GetSiteInstance(), params.page_id);
   1162 
   1163   // We assign the entry's unique ID to be that of the new one. Since this is
   1164   // always the result of a user action, we want to dismiss infobars, etc. like
   1165   // a regular user-initiated navigation.
   1166   existing_entry->set_unique_id(pending_entry_->GetUniqueID());
   1167 
   1168   // The URL may have changed due to redirects.
   1169   if (existing_entry->update_virtual_url_with_url())
   1170     UpdateVirtualURLToURL(existing_entry, params.url);
   1171   existing_entry->SetURL(params.url);
   1172   existing_entry->SetReferrer(params.referrer);
   1173 
   1174   // The page may have been requested with a different HTTP method.
   1175   existing_entry->SetHasPostData(params.is_post);
   1176   existing_entry->SetPostID(params.post_id);
   1177 
   1178   DiscardNonCommittedEntries();
   1179 }
   1180 
   1181 void NavigationControllerImpl::RendererDidNavigateInPage(
   1182     RenderFrameHost* rfh,
   1183     const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
   1184     bool* did_replace_entry) {
   1185   DCHECK(ui::PageTransitionIsMainFrame(params.transition)) <<
   1186       "WebKit should only tell us about in-page navs for the main frame.";
   1187   // We're guaranteed to have an entry for this one.
   1188   NavigationEntryImpl* existing_entry = GetEntryWithPageID(
   1189       rfh->GetSiteInstance(), params.page_id);
   1190 
   1191   // Reference fragment navigation. We're guaranteed to have the last_committed
   1192   // entry and it will be the same page as the new navigation (minus the
   1193   // reference fragments, of course).  We'll update the URL of the existing
   1194   // entry without pruning the forward history.
   1195   existing_entry->SetURL(params.url);
   1196   if (existing_entry->update_virtual_url_with_url())
   1197     UpdateVirtualURLToURL(existing_entry, params.url);
   1198 
   1199   existing_entry->SetHasPostData(params.is_post);
   1200   existing_entry->SetPostID(params.post_id);
   1201 
   1202   // This replaces the existing entry since the page ID didn't change.
   1203   *did_replace_entry = true;
   1204 
   1205   DiscardNonCommittedEntriesInternal();
   1206 
   1207   // If a transient entry was removed, the indices might have changed, so we
   1208   // have to query the entry index again.
   1209   last_committed_entry_index_ =
   1210       GetEntryIndexWithPageID(rfh->GetSiteInstance(), params.page_id);
   1211 }
   1212 
   1213 void NavigationControllerImpl::RendererDidNavigateNewSubframe(
   1214     RenderFrameHost* rfh,
   1215     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
   1216   if (ui::PageTransitionCoreTypeIs(params.transition,
   1217                                    ui::PAGE_TRANSITION_AUTO_SUBFRAME)) {
   1218     // This is not user-initiated. Ignore.
   1219     DiscardNonCommittedEntriesInternal();
   1220     return;
   1221   }
   1222 
   1223   // Manual subframe navigations just get the current entry cloned so the user
   1224   // can go back or forward to it. The actual subframe information will be
   1225   // stored in the page state for each of those entries. This happens out of
   1226   // band with the actual navigations.
   1227   DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee "
   1228                                   << "that a last committed entry exists.";
   1229   NavigationEntryImpl* new_entry = new NavigationEntryImpl(
   1230       *NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()));
   1231   new_entry->SetPageID(params.page_id);
   1232   InsertOrReplaceEntry(new_entry, false);
   1233 }
   1234 
   1235 bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
   1236     RenderFrameHost* rfh,
   1237     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
   1238   // We're guaranteed to have a previously committed entry, and we now need to
   1239   // handle navigation inside of a subframe in it without creating a new entry.
   1240   DCHECK(GetLastCommittedEntry());
   1241 
   1242   // Handle the case where we're navigating back/forward to a previous subframe
   1243   // navigation entry. This is case "2." in NAV_AUTO_SUBFRAME comment in the
   1244   // header file. In case "1." this will be a NOP.
   1245   int entry_index = GetEntryIndexWithPageID(
   1246       rfh->GetSiteInstance(),
   1247       params.page_id);
   1248   if (entry_index < 0 ||
   1249       entry_index >= static_cast<int>(entries_.size())) {
   1250     NOTREACHED();
   1251     return false;
   1252   }
   1253 
   1254   // Update the current navigation entry in case we're going back/forward.
   1255   if (entry_index != last_committed_entry_index_) {
   1256     last_committed_entry_index_ = entry_index;
   1257     DiscardNonCommittedEntriesInternal();
   1258     return true;
   1259   }
   1260 
   1261   // We do not need to discard the pending entry in this case, since we will
   1262   // not generate commit notifications for this auto-subframe navigation.
   1263   return false;
   1264 }
   1265 
   1266 int NavigationControllerImpl::GetIndexOfEntry(
   1267     const NavigationEntryImpl* entry) const {
   1268   const NavigationEntries::const_iterator i(std::find(
   1269       entries_.begin(),
   1270       entries_.end(),
   1271       entry));
   1272   return (i == entries_.end()) ? -1 : static_cast<int>(i - entries_.begin());
   1273 }
   1274 
   1275 bool NavigationControllerImpl::IsURLInPageNavigation(
   1276     const GURL& url,
   1277     bool renderer_says_in_page,
   1278     RenderFrameHost* rfh) const {
   1279   NavigationEntry* last_committed = GetLastCommittedEntry();
   1280   return last_committed && AreURLsInPageNavigation(
   1281       last_committed->GetURL(), url, renderer_says_in_page, rfh);
   1282 }
   1283 
   1284 void NavigationControllerImpl::CopyStateFrom(
   1285     const NavigationController& temp) {
   1286   const NavigationControllerImpl& source =
   1287       static_cast<const NavigationControllerImpl&>(temp);
   1288   // Verify that we look new.
   1289   DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
   1290 
   1291   if (source.GetEntryCount() == 0)
   1292     return;  // Nothing new to do.
   1293 
   1294   needs_reload_ = true;
   1295   InsertEntriesFrom(source, source.GetEntryCount());
   1296 
   1297   for (SessionStorageNamespaceMap::const_iterator it =
   1298            source.session_storage_namespace_map_.begin();
   1299        it != source.session_storage_namespace_map_.end();
   1300        ++it) {
   1301     SessionStorageNamespaceImpl* source_namespace =
   1302         static_cast<SessionStorageNamespaceImpl*>(it->second.get());
   1303     session_storage_namespace_map_[it->first] = source_namespace->Clone();
   1304   }
   1305 
   1306   FinishRestore(source.last_committed_entry_index_, RESTORE_CURRENT_SESSION);
   1307 
   1308   // Copy the max page id map from the old tab to the new tab.  This ensures
   1309   // that new and existing navigations in the tab's current SiteInstances
   1310   // are identified properly.
   1311   delegate_->CopyMaxPageIDsFrom(source.delegate()->GetWebContents());
   1312 }
   1313 
   1314 void NavigationControllerImpl::CopyStateFromAndPrune(
   1315     NavigationController* temp,
   1316     bool replace_entry) {
   1317   // It is up to callers to check the invariants before calling this.
   1318   CHECK(CanPruneAllButLastCommitted());
   1319 
   1320   NavigationControllerImpl* source =
   1321       static_cast<NavigationControllerImpl*>(temp);
   1322   // The SiteInstance and page_id of the last committed entry needs to be
   1323   // remembered at this point, in case there is only one committed entry
   1324   // and it is pruned.  We use a scoped_refptr to ensure the SiteInstance
   1325   // can't be freed during this time period.
   1326   NavigationEntryImpl* last_committed =
   1327       NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
   1328   scoped_refptr<SiteInstance> site_instance(
   1329       last_committed->site_instance());
   1330   int32 minimum_page_id = last_committed->GetPageID();
   1331   int32 max_page_id =
   1332       delegate_->GetMaxPageIDForSiteInstance(site_instance.get());
   1333 
   1334   // Remove all the entries leaving the active entry.
   1335   PruneAllButLastCommittedInternal();
   1336 
   1337   // We now have one entry, possibly with a new pending entry.  Ensure that
   1338   // adding the entries from source won't put us over the limit.
   1339   DCHECK_EQ(1, GetEntryCount());
   1340   if (!replace_entry)
   1341     source->PruneOldestEntryIfFull();
   1342 
   1343   // Insert the entries from source. Don't use source->GetCurrentEntryIndex as
   1344   // we don't want to copy over the transient entry.  Ignore any pending entry,
   1345   // since it has not committed in source.
   1346   int max_source_index = source->last_committed_entry_index_;
   1347   if (max_source_index == -1)
   1348     max_source_index = source->GetEntryCount();
   1349   else
   1350     max_source_index++;
   1351 
   1352   // Ignore the source's current entry if merging with replacement.
   1353   // TODO(davidben): This should preserve entries forward of the current
   1354   // too. http://crbug.com/317872
   1355   if (replace_entry && max_source_index > 0)
   1356     max_source_index--;
   1357 
   1358   InsertEntriesFrom(*source, max_source_index);
   1359 
   1360   // Adjust indices such that the last entry and pending are at the end now.
   1361   last_committed_entry_index_ = GetEntryCount() - 1;
   1362 
   1363   delegate_->SetHistoryLengthAndPrune(site_instance.get(),
   1364                                       max_source_index,
   1365                                       minimum_page_id);
   1366 
   1367   // Copy the max page id map from the old tab to the new tab.  This ensures
   1368   // that new and existing navigations in the tab's current SiteInstances
   1369   // are identified properly.
   1370   delegate_->CopyMaxPageIDsFrom(source->delegate()->GetWebContents());
   1371   max_restored_page_id_ = source->max_restored_page_id_;
   1372 
   1373   // If there is a last committed entry, be sure to include it in the new
   1374   // max page ID map.
   1375   if (max_page_id > -1) {
   1376     delegate_->UpdateMaxPageIDForSiteInstance(site_instance.get(),
   1377                                               max_page_id);
   1378   }
   1379 }
   1380 
   1381 bool NavigationControllerImpl::CanPruneAllButLastCommitted() {
   1382   // If there is no last committed entry, we cannot prune.  Even if there is a
   1383   // pending entry, it may not commit, leaving this WebContents blank, despite
   1384   // possibly giving it new entries via CopyStateFromAndPrune.
   1385   if (last_committed_entry_index_ == -1)
   1386     return false;
   1387 
   1388   // We cannot prune if there is a pending entry at an existing entry index.
   1389   // It may not commit, so we have to keep the last committed entry, and thus
   1390   // there is no sensible place to keep the pending entry.  It is ok to have
   1391   // a new pending entry, which can optionally commit as a new navigation.
   1392   if (pending_entry_index_ != -1)
   1393     return false;
   1394 
   1395   // We should not prune if we are currently showing a transient entry.
   1396   if (transient_entry_index_ != -1)
   1397     return false;
   1398 
   1399   return true;
   1400 }
   1401 
   1402 void NavigationControllerImpl::PruneAllButLastCommitted() {
   1403   PruneAllButLastCommittedInternal();
   1404 
   1405   // We should still have a last committed entry.
   1406   DCHECK_NE(-1, last_committed_entry_index_);
   1407 
   1408   // We pass 0 instead of GetEntryCount() for the history_length parameter of
   1409   // SetHistoryLengthAndPrune, because it will create history_length additional
   1410   // history entries.
   1411   // TODO(jochen): This API is confusing and we should clean it up.
   1412   // http://crbug.com/178491
   1413   NavigationEntryImpl* entry =
   1414       NavigationEntryImpl::FromNavigationEntry(GetVisibleEntry());
   1415   delegate_->SetHistoryLengthAndPrune(
   1416       entry->site_instance(), 0, entry->GetPageID());
   1417 }
   1418 
   1419 void NavigationControllerImpl::PruneAllButLastCommittedInternal() {
   1420   // It is up to callers to check the invariants before calling this.
   1421   CHECK(CanPruneAllButLastCommitted());
   1422 
   1423   // Erase all entries but the last committed entry.  There may still be a
   1424   // new pending entry after this.
   1425   entries_.erase(entries_.begin(),
   1426                  entries_.begin() + last_committed_entry_index_);
   1427   entries_.erase(entries_.begin() + 1, entries_.end());
   1428   last_committed_entry_index_ = 0;
   1429 }
   1430 
   1431 void NavigationControllerImpl::ClearAllScreenshots() {
   1432   screenshot_manager_->ClearAllScreenshots();
   1433 }
   1434 
   1435 void NavigationControllerImpl::SetSessionStorageNamespace(
   1436     const std::string& partition_id,
   1437     SessionStorageNamespace* session_storage_namespace) {
   1438   if (!session_storage_namespace)
   1439     return;
   1440 
   1441   // We can't overwrite an existing SessionStorage without violating spec.
   1442   // Attempts to do so may give a tab access to another tab's session storage
   1443   // so die hard on an error.
   1444   bool successful_insert = session_storage_namespace_map_.insert(
   1445       make_pair(partition_id,
   1446                 static_cast<SessionStorageNamespaceImpl*>(
   1447                     session_storage_namespace)))
   1448           .second;
   1449   CHECK(successful_insert) << "Cannot replace existing SessionStorageNamespace";
   1450 }
   1451 
   1452 void NavigationControllerImpl::SetMaxRestoredPageID(int32 max_id) {
   1453   max_restored_page_id_ = max_id;
   1454 }
   1455 
   1456 int32 NavigationControllerImpl::GetMaxRestoredPageID() const {
   1457   return max_restored_page_id_;
   1458 }
   1459 
   1460 bool NavigationControllerImpl::IsUnmodifiedBlankTab() const {
   1461   return IsInitialNavigation() &&
   1462          !GetLastCommittedEntry() &&
   1463          !delegate_->HasAccessedInitialDocument();
   1464 }
   1465 
   1466 SessionStorageNamespace*
   1467 NavigationControllerImpl::GetSessionStorageNamespace(SiteInstance* instance) {
   1468   std::string partition_id;
   1469   if (instance) {
   1470     // TODO(ajwong): When GetDefaultSessionStorageNamespace() goes away, remove
   1471     // this if statement so |instance| must not be NULL.
   1472     partition_id =
   1473         GetContentClient()->browser()->GetStoragePartitionIdForSite(
   1474             browser_context_, instance->GetSiteURL());
   1475   }
   1476 
   1477   SessionStorageNamespaceMap::const_iterator it =
   1478       session_storage_namespace_map_.find(partition_id);
   1479   if (it != session_storage_namespace_map_.end())
   1480     return it->second.get();
   1481 
   1482   // Create one if no one has accessed session storage for this partition yet.
   1483   //
   1484   // TODO(ajwong): Should this use the |partition_id| directly rather than
   1485   // re-lookup via |instance|?  http://crbug.com/142685
   1486   StoragePartition* partition =
   1487               BrowserContext::GetStoragePartition(browser_context_, instance);
   1488   SessionStorageNamespaceImpl* session_storage_namespace =
   1489       new SessionStorageNamespaceImpl(
   1490           static_cast<DOMStorageContextWrapper*>(
   1491               partition->GetDOMStorageContext()));
   1492   session_storage_namespace_map_[partition_id] = session_storage_namespace;
   1493 
   1494   return session_storage_namespace;
   1495 }
   1496 
   1497 SessionStorageNamespace*
   1498 NavigationControllerImpl::GetDefaultSessionStorageNamespace() {
   1499   // TODO(ajwong): Remove if statement in GetSessionStorageNamespace().
   1500   return GetSessionStorageNamespace(NULL);
   1501 }
   1502 
   1503 const SessionStorageNamespaceMap&
   1504 NavigationControllerImpl::GetSessionStorageNamespaceMap() const {
   1505   return session_storage_namespace_map_;
   1506 }
   1507 
   1508 bool NavigationControllerImpl::NeedsReload() const {
   1509   return needs_reload_;
   1510 }
   1511 
   1512 void NavigationControllerImpl::SetNeedsReload() {
   1513   needs_reload_ = true;
   1514 }
   1515 
   1516 void NavigationControllerImpl::RemoveEntryAtIndexInternal(int index) {
   1517   DCHECK(index < GetEntryCount());
   1518   DCHECK(index != last_committed_entry_index_);
   1519 
   1520   DiscardNonCommittedEntries();
   1521 
   1522   entries_.erase(entries_.begin() + index);
   1523   if (last_committed_entry_index_ > index)
   1524     last_committed_entry_index_--;
   1525 }
   1526 
   1527 void NavigationControllerImpl::DiscardNonCommittedEntries() {
   1528   bool transient = transient_entry_index_ != -1;
   1529   DiscardNonCommittedEntriesInternal();
   1530 
   1531   // If there was a transient entry, invalidate everything so the new active
   1532   // entry state is shown.
   1533   if (transient) {
   1534     delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
   1535   }
   1536 }
   1537 
   1538 NavigationEntry* NavigationControllerImpl::GetPendingEntry() const {
   1539   return pending_entry_;
   1540 }
   1541 
   1542 int NavigationControllerImpl::GetPendingEntryIndex() const {
   1543   return pending_entry_index_;
   1544 }
   1545 
   1546 void NavigationControllerImpl::InsertOrReplaceEntry(NavigationEntryImpl* entry,
   1547                                                     bool replace) {
   1548   DCHECK(entry->GetTransitionType() != ui::PAGE_TRANSITION_AUTO_SUBFRAME);
   1549 
   1550   // Copy the pending entry's unique ID to the committed entry.
   1551   // I don't know if pending_entry_index_ can be other than -1 here.
   1552   const NavigationEntryImpl* const pending_entry =
   1553       (pending_entry_index_ == -1) ?
   1554           pending_entry_ : entries_[pending_entry_index_].get();
   1555   if (pending_entry)
   1556     entry->set_unique_id(pending_entry->GetUniqueID());
   1557 
   1558   DiscardNonCommittedEntriesInternal();
   1559 
   1560   int current_size = static_cast<int>(entries_.size());
   1561 
   1562   if (current_size > 0) {
   1563     // Prune any entries which are in front of the current entry.
   1564     // Also prune the current entry if we are to replace the current entry.
   1565     // last_committed_entry_index_ must be updated here since calls to
   1566     // NotifyPrunedEntries() below may re-enter and we must make sure
   1567     // last_committed_entry_index_ is not left in an invalid state.
   1568     if (replace)
   1569       --last_committed_entry_index_;
   1570 
   1571     int num_pruned = 0;
   1572     while (last_committed_entry_index_ < (current_size - 1)) {
   1573       num_pruned++;
   1574       entries_.pop_back();
   1575       current_size--;
   1576     }
   1577     if (num_pruned > 0)  // Only notify if we did prune something.
   1578       NotifyPrunedEntries(this, false, num_pruned);
   1579   }
   1580 
   1581   PruneOldestEntryIfFull();
   1582 
   1583   entries_.push_back(linked_ptr<NavigationEntryImpl>(entry));
   1584   last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1;
   1585 
   1586   // This is a new page ID, so we need everybody to know about it.
   1587   delegate_->UpdateMaxPageID(entry->GetPageID());
   1588 }
   1589 
   1590 void NavigationControllerImpl::PruneOldestEntryIfFull() {
   1591   if (entries_.size() >= max_entry_count()) {
   1592     DCHECK_EQ(max_entry_count(), entries_.size());
   1593     DCHECK_GT(last_committed_entry_index_, 0);
   1594     RemoveEntryAtIndex(0);
   1595     NotifyPrunedEntries(this, true, 1);
   1596   }
   1597 }
   1598 
   1599 void NavigationControllerImpl::NavigateToPendingEntry(ReloadType reload_type) {
   1600   needs_reload_ = false;
   1601 
   1602   // If we were navigating to a slow-to-commit page, and the user performs
   1603   // a session history navigation to the last committed page, RenderViewHost
   1604   // will force the throbber to start, but WebKit will essentially ignore the
   1605   // navigation, and won't send a message to stop the throbber. To prevent this
   1606   // from happening, we drop the navigation here and stop the slow-to-commit
   1607   // page from loading (which would normally happen during the navigation).
   1608   if (pending_entry_index_ != -1 &&
   1609       pending_entry_index_ == last_committed_entry_index_ &&
   1610       (entries_[pending_entry_index_]->restore_type() ==
   1611           NavigationEntryImpl::RESTORE_NONE) &&
   1612       (entries_[pending_entry_index_]->GetTransitionType() &
   1613           ui::PAGE_TRANSITION_FORWARD_BACK)) {
   1614     delegate_->Stop();
   1615 
   1616     // If an interstitial page is showing, we want to close it to get back
   1617     // to what was showing before.
   1618     if (delegate_->GetInterstitialPage())
   1619       delegate_->GetInterstitialPage()->DontProceed();
   1620 
   1621     DiscardNonCommittedEntries();
   1622     return;
   1623   }
   1624 
   1625   // If an interstitial page is showing, the previous renderer is blocked and
   1626   // cannot make new requests.  Unblock (and disable) it to allow this
   1627   // navigation to succeed.  The interstitial will stay visible until the
   1628   // resulting DidNavigate.
   1629   if (delegate_->GetInterstitialPage()) {
   1630     static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())->
   1631         CancelForNavigation();
   1632   }
   1633 
   1634   // For session history navigations only the pending_entry_index_ is set.
   1635   if (!pending_entry_) {
   1636     DCHECK_NE(pending_entry_index_, -1);
   1637     pending_entry_ = entries_[pending_entry_index_].get();
   1638   }
   1639 
   1640   // This call does not support re-entrancy.  See http://crbug.com/347742.
   1641   CHECK(!in_navigate_to_pending_entry_);
   1642   in_navigate_to_pending_entry_ = true;
   1643   bool success = delegate_->NavigateToPendingEntry(reload_type);
   1644   in_navigate_to_pending_entry_ = false;
   1645 
   1646   if (!success)
   1647     DiscardNonCommittedEntries();
   1648 
   1649   // If the entry is being restored and doesn't have a SiteInstance yet, fill
   1650   // it in now that we know. This allows us to find the entry when it commits.
   1651   if (pending_entry_ && !pending_entry_->site_instance() &&
   1652       pending_entry_->restore_type() != NavigationEntryImpl::RESTORE_NONE) {
   1653     pending_entry_->set_site_instance(static_cast<SiteInstanceImpl*>(
   1654         delegate_->GetPendingSiteInstance()));
   1655     pending_entry_->set_restore_type(NavigationEntryImpl::RESTORE_NONE);
   1656   }
   1657 }
   1658 
   1659 void NavigationControllerImpl::NotifyNavigationEntryCommitted(
   1660     LoadCommittedDetails* details) {
   1661   details->entry = GetLastCommittedEntry();
   1662 
   1663   // We need to notify the ssl_manager_ before the web_contents_ so the
   1664   // location bar will have up-to-date information about the security style
   1665   // when it wants to draw.  See http://crbug.com/11157
   1666   ssl_manager_.DidCommitProvisionalLoad(*details);
   1667 
   1668   delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
   1669   delegate_->NotifyNavigationEntryCommitted(*details);
   1670 
   1671   // TODO(avi): Remove. http://crbug.com/170921
   1672   NotificationDetails notification_details =
   1673       Details<LoadCommittedDetails>(details);
   1674   NotificationService::current()->Notify(
   1675       NOTIFICATION_NAV_ENTRY_COMMITTED,
   1676       Source<NavigationController>(this),
   1677       notification_details);
   1678 }
   1679 
   1680 // static
   1681 size_t NavigationControllerImpl::max_entry_count() {
   1682   if (max_entry_count_for_testing_ != kMaxEntryCountForTestingNotSet)
   1683      return max_entry_count_for_testing_;
   1684   return kMaxSessionHistoryEntries;
   1685 }
   1686 
   1687 void NavigationControllerImpl::SetActive(bool is_active) {
   1688   if (is_active && needs_reload_)
   1689     LoadIfNecessary();
   1690 }
   1691 
   1692 void NavigationControllerImpl::LoadIfNecessary() {
   1693   if (!needs_reload_)
   1694     return;
   1695 
   1696   // Calling Reload() results in ignoring state, and not loading.
   1697   // Explicitly use NavigateToPendingEntry so that the renderer uses the
   1698   // cached state.
   1699   pending_entry_index_ = last_committed_entry_index_;
   1700   NavigateToPendingEntry(NO_RELOAD);
   1701 }
   1702 
   1703 void NavigationControllerImpl::NotifyEntryChanged(const NavigationEntry* entry,
   1704                                                   int index) {
   1705   EntryChangedDetails det;
   1706   det.changed_entry = entry;
   1707   det.index = index;
   1708   NotificationService::current()->Notify(
   1709       NOTIFICATION_NAV_ENTRY_CHANGED,
   1710       Source<NavigationController>(this),
   1711       Details<EntryChangedDetails>(&det));
   1712 }
   1713 
   1714 void NavigationControllerImpl::FinishRestore(int selected_index,
   1715                                              RestoreType type) {
   1716   DCHECK(selected_index >= 0 && selected_index < GetEntryCount());
   1717   ConfigureEntriesForRestore(&entries_, type);
   1718 
   1719   SetMaxRestoredPageID(static_cast<int32>(GetEntryCount()));
   1720 
   1721   last_committed_entry_index_ = selected_index;
   1722 }
   1723 
   1724 void NavigationControllerImpl::DiscardNonCommittedEntriesInternal() {
   1725   DiscardPendingEntry();
   1726   DiscardTransientEntry();
   1727 }
   1728 
   1729 void NavigationControllerImpl::DiscardPendingEntry() {
   1730   // It is not safe to call DiscardPendingEntry while NavigateToEntry is in
   1731   // progress, since this will cause a use-after-free.  (We only allow this
   1732   // when the tab is being destroyed for shutdown, since it won't return to
   1733   // NavigateToEntry in that case.)  http://crbug.com/347742.
   1734   CHECK(!in_navigate_to_pending_entry_ || delegate_->IsBeingDestroyed());
   1735 
   1736   if (pending_entry_index_ == -1)
   1737     delete pending_entry_;
   1738   pending_entry_ = NULL;
   1739   pending_entry_index_ = -1;
   1740 }
   1741 
   1742 void NavigationControllerImpl::DiscardTransientEntry() {
   1743   if (transient_entry_index_ == -1)
   1744     return;
   1745   entries_.erase(entries_.begin() + transient_entry_index_);
   1746   if (last_committed_entry_index_ > transient_entry_index_)
   1747     last_committed_entry_index_--;
   1748   transient_entry_index_ = -1;
   1749 }
   1750 
   1751 int NavigationControllerImpl::GetEntryIndexWithPageID(
   1752     SiteInstance* instance, int32 page_id) const {
   1753   for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) {
   1754     if ((entries_[i]->site_instance() == instance) &&
   1755         (entries_[i]->GetPageID() == page_id))
   1756       return i;
   1757   }
   1758   return -1;
   1759 }
   1760 
   1761 NavigationEntry* NavigationControllerImpl::GetTransientEntry() const {
   1762   if (transient_entry_index_ == -1)
   1763     return NULL;
   1764   return entries_[transient_entry_index_].get();
   1765 }
   1766 
   1767 void NavigationControllerImpl::SetTransientEntry(NavigationEntry* entry) {
   1768   // Discard any current transient entry, we can only have one at a time.
   1769   int index = 0;
   1770   if (last_committed_entry_index_ != -1)
   1771     index = last_committed_entry_index_ + 1;
   1772   DiscardTransientEntry();
   1773   entries_.insert(
   1774       entries_.begin() + index, linked_ptr<NavigationEntryImpl>(
   1775           NavigationEntryImpl::FromNavigationEntry(entry)));
   1776   transient_entry_index_ = index;
   1777   delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
   1778 }
   1779 
   1780 void NavigationControllerImpl::InsertEntriesFrom(
   1781     const NavigationControllerImpl& source,
   1782     int max_index) {
   1783   DCHECK_LE(max_index, source.GetEntryCount());
   1784   size_t insert_index = 0;
   1785   for (int i = 0; i < max_index; i++) {
   1786     // When cloning a tab, copy all entries except interstitial pages
   1787     if (source.entries_[i].get()->GetPageType() !=
   1788         PAGE_TYPE_INTERSTITIAL) {
   1789       entries_.insert(entries_.begin() + insert_index++,
   1790                       linked_ptr<NavigationEntryImpl>(
   1791                           new NavigationEntryImpl(*source.entries_[i])));
   1792     }
   1793   }
   1794 }
   1795 
   1796 void NavigationControllerImpl::SetGetTimestampCallbackForTest(
   1797     const base::Callback<base::Time()>& get_timestamp_callback) {
   1798   get_timestamp_callback_ = get_timestamp_callback;
   1799 }
   1800 
   1801 }  // namespace content
   1802