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