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