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