This source file includes following definitions.
- NotifyPrunedEntries
- SetPageStateIfEmpty
- ControllerRestoreTypeToEntryType
- ConfigureEntriesForRestore
- AreURLsInPageNavigation
- ShouldKeepOverride
- CreateNavigationEntry
- DisablePromptOnRepost
- GetSmoothedTime
- screenshot_manager_
- GetWebContents
- GetBrowserContext
- SetBrowserContext
- Restore
- Reload
- ReloadIgnoringCache
- ReloadOriginalRequestURL
- ReloadInternal
- CancelPendingReload
- ContinuePendingReload
- IsInitialNavigation
- GetEntryWithPageID
- LoadEntry
- SetPendingEntry
- GetActiveEntry
- GetVisibleEntry
- GetCurrentEntryIndex
- GetLastCommittedEntry
- CanViewSource
- GetLastCommittedEntryIndex
- GetEntryCount
- GetEntryAtIndex
- GetEntryAtOffset
- GetIndexForOffset
- TakeScreenshot
- SetScreenshotManager
- CanGoBack
- CanGoForward
- CanGoToOffset
- GoBack
- GoForward
- GoToIndex
- GoToOffset
- RemoveEntryAtIndex
- UpdateVirtualURLToURL
- LoadURL
- LoadURLWithParams
- RendererDidNavigate
- ClassifyNavigation
- RendererDidNavigateToNewPage
- RendererDidNavigateToExistingPage
- RendererDidNavigateToSamePage
- RendererDidNavigateInPage
- RendererDidNavigateNewSubframe
- RendererDidNavigateAutoSubframe
- GetIndexOfEntry
- IsURLInPageNavigation
- CopyStateFrom
- CopyStateFromAndPrune
- CanPruneAllButLastCommitted
- PruneAllButLastCommitted
- PruneAllButLastCommittedInternal
- ClearAllScreenshots
- SetSessionStorageNamespace
- SetMaxRestoredPageID
- GetMaxRestoredPageID
- GetSessionStorageNamespace
- GetDefaultSessionStorageNamespace
- GetSessionStorageNamespaceMap
- NeedsReload
- SetNeedsReload
- RemoveEntryAtIndexInternal
- DiscardNonCommittedEntries
- GetPendingEntry
- GetPendingEntryIndex
- InsertOrReplaceEntry
- PruneOldestEntryIfFull
- NavigateToPendingEntry
- NotifyNavigationEntryCommitted
- max_entry_count
- SetActive
- LoadIfNecessary
- NotifyEntryChanged
- FinishRestore
- DiscardNonCommittedEntriesInternal
- DiscardPendingEntry
- DiscardTransientEntry
- GetEntryIndexWithPageID
- GetTransientEntry
- SetTransientEntry
- InsertEntriesFrom
- SetGetTimestampCallbackForTest
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/browser/browser_url_handler_impl.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/invalidate_type.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/url_constants.h"
#include "net/base/escape.h"
#include "net/base/mime_util.h"
#include "net/base/net_util.h"
#include "skia/ext/platform_canvas.h"
namespace content {
namespace {
const int kInvalidateAll = 0xFFFFFFFF;
void NotifyPrunedEntries(NavigationControllerImpl* nav_controller,
bool from_front,
int count) {
PrunedDetails details;
details.from_front = from_front;
details.count = count;
NotificationService::current()->Notify(
NOTIFICATION_NAV_LIST_PRUNED,
Source<NavigationController>(nav_controller),
Details<PrunedDetails>(&details));
}
void SetPageStateIfEmpty(NavigationEntryImpl* entry) {
if (!entry->GetPageState().IsValid())
entry->SetPageState(PageState::CreateFromURL(entry->GetURL()));
}
NavigationEntryImpl::RestoreType ControllerRestoreTypeToEntryType(
NavigationController::RestoreType type) {
switch (type) {
case NavigationController::RESTORE_CURRENT_SESSION:
return NavigationEntryImpl::RESTORE_CURRENT_SESSION;
case NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY:
return NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY;
case NavigationController::RESTORE_LAST_SESSION_CRASHED:
return NavigationEntryImpl::RESTORE_LAST_SESSION_CRASHED;
}
NOTREACHED();
return NavigationEntryImpl::RESTORE_CURRENT_SESSION;
}
void ConfigureEntriesForRestore(
std::vector<linked_ptr<NavigationEntryImpl> >* entries,
NavigationController::RestoreType type) {
for (size_t i = 0; i < entries->size(); ++i) {
(*entries)[i]->SetTransitionType(PAGE_TRANSITION_RELOAD);
(*entries)[i]->set_restore_type(ControllerRestoreTypeToEntryType(type));
SetPageStateIfEmpty((*entries)[i].get());
}
}
bool AreURLsInPageNavigation(const GURL& existing_url,
const GURL& new_url,
bool renderer_says_in_page,
NavigationType navigation_type) {
if (existing_url == new_url)
return renderer_says_in_page;
if (!new_url.has_ref()) {
return navigation_type == NAVIGATION_TYPE_IN_PAGE;
}
url_canon::Replacements<char> replacements;
replacements.ClearRef();
return existing_url.ReplaceComponents(replacements) ==
new_url.ReplaceComponents(replacements);
}
bool ShouldKeepOverride(const NavigationEntry* last_entry) {
return last_entry && last_entry->GetIsOverridingUserAgent();
}
}
const size_t kMaxEntryCountForTestingNotSet = -1;
size_t NavigationControllerImpl::max_entry_count_for_testing_ =
kMaxEntryCountForTestingNotSet;
static bool g_check_for_repost = true;
NavigationEntry* NavigationController::CreateNavigationEntry(
const GURL& url,
const Referrer& referrer,
PageTransition transition,
bool is_renderer_initiated,
const std::string& extra_headers,
BrowserContext* browser_context) {
GURL loaded_url(url);
bool reverse_on_redirect = false;
BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
&loaded_url, browser_context, &reverse_on_redirect);
NavigationEntryImpl* entry = new NavigationEntryImpl(
NULL,
-1,
loaded_url,
referrer,
base::string16(),
transition,
is_renderer_initiated);
entry->SetVirtualURL(url);
entry->set_user_typed_url(url);
entry->set_update_virtual_url_with_url(reverse_on_redirect);
entry->set_extra_headers(extra_headers);
return entry;
}
void NavigationController::DisablePromptOnRepost() {
g_check_for_repost = false;
}
base::Time NavigationControllerImpl::TimeSmoother::GetSmoothedTime(
base::Time t) {
if (low_water_mark_ <= t && t <= high_water_mark_) {
high_water_mark_ += base::TimeDelta::FromMicroseconds(1);
return high_water_mark_;
}
low_water_mark_ = high_water_mark_ = t;
return t;
}
NavigationControllerImpl::NavigationControllerImpl(
NavigationControllerDelegate* delegate,
BrowserContext* browser_context)
: browser_context_(browser_context),
pending_entry_(NULL),
last_committed_entry_index_(-1),
pending_entry_index_(-1),
transient_entry_index_(-1),
delegate_(delegate),
max_restored_page_id_(-1),
ssl_manager_(this),
needs_reload_(false),
is_initial_navigation_(true),
in_navigate_to_pending_entry_(false),
pending_reload_(NO_RELOAD),
get_timestamp_callback_(base::Bind(&base::Time::Now)),
screenshot_manager_(new NavigationEntryScreenshotManager(this)) {
DCHECK(browser_context_);
}
NavigationControllerImpl::~NavigationControllerImpl() {
DiscardNonCommittedEntriesInternal();
}
WebContents* NavigationControllerImpl::GetWebContents() const {
return delegate_->GetWebContents();
}
BrowserContext* NavigationControllerImpl::GetBrowserContext() const {
return browser_context_;
}
void NavigationControllerImpl::SetBrowserContext(
BrowserContext* browser_context) {
browser_context_ = browser_context;
}
void NavigationControllerImpl::Restore(
int selected_navigation,
RestoreType type,
std::vector<NavigationEntry*>* entries) {
DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
DCHECK(selected_navigation >= 0 &&
selected_navigation < static_cast<int>(entries->size()));
needs_reload_ = true;
for (size_t i = 0; i < entries->size(); ++i) {
NavigationEntryImpl* entry =
NavigationEntryImpl::FromNavigationEntry((*entries)[i]);
entries_.push_back(linked_ptr<NavigationEntryImpl>(entry));
}
entries->clear();
FinishRestore(selected_navigation, type);
}
void NavigationControllerImpl::Reload(bool check_for_repost) {
ReloadInternal(check_for_repost, RELOAD);
}
void NavigationControllerImpl::ReloadIgnoringCache(bool check_for_repost) {
ReloadInternal(check_for_repost, RELOAD_IGNORING_CACHE);
}
void NavigationControllerImpl::ReloadOriginalRequestURL(bool check_for_repost) {
ReloadInternal(check_for_repost, RELOAD_ORIGINAL_REQUEST_URL);
}
void NavigationControllerImpl::ReloadInternal(bool check_for_repost,
ReloadType reload_type) {
if (transient_entry_index_ != -1) {
NavigationEntryImpl* transient_entry =
NavigationEntryImpl::FromNavigationEntry(GetTransientEntry());
if (!transient_entry)
return;
LoadURL(transient_entry->GetURL(),
Referrer(),
PAGE_TRANSITION_RELOAD,
transient_entry->extra_headers());
return;
}
NavigationEntryImpl* entry = NULL;
int current_index = -1;
if (IsInitialNavigation() && pending_entry_) {
entry = pending_entry_;
current_index = pending_entry_index_;
} else {
DiscardNonCommittedEntriesInternal();
current_index = GetCurrentEntryIndex();
if (current_index != -1) {
entry = NavigationEntryImpl::FromNavigationEntry(
GetEntryAtIndex(current_index));
}
}
if (!entry)
return;
if (reload_type == NavigationControllerImpl::RELOAD_ORIGINAL_REQUEST_URL &&
entry->GetOriginalRequestURL().is_valid() && !entry->GetHasPostData()) {
entry->SetURL(entry->GetOriginalRequestURL());
entry->SetReferrer(Referrer());
}
if (g_check_for_repost && check_for_repost &&
entry->GetHasPostData()) {
delegate_->NotifyBeforeFormRepostWarningShow();
pending_reload_ = reload_type;
delegate_->ActivateAndShowRepostFormWarningDialog();
} else {
if (!IsInitialNavigation())
DiscardNonCommittedEntriesInternal();
SiteInstanceImpl* site_instance = entry->site_instance();
bool is_guest = site_instance && site_instance->HasProcess() &&
site_instance->GetProcess()->IsGuest();
if (!is_guest && site_instance &&
site_instance->HasWrongProcessForURL(entry->GetURL())) {
NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry(
CreateNavigationEntry(
entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(),
false, entry->extra_headers(), browser_context_));
reload_type = NavigationController::NO_RELOAD;
nav_entry->set_should_replace_entry(true);
pending_entry_ = nav_entry;
} else {
pending_entry_ = entry;
pending_entry_index_ = current_index;
pending_entry_->SetTitle(base::string16());
pending_entry_->SetTransitionType(PAGE_TRANSITION_RELOAD);
}
NavigateToPendingEntry(reload_type);
}
}
void NavigationControllerImpl::CancelPendingReload() {
DCHECK(pending_reload_ != NO_RELOAD);
pending_reload_ = NO_RELOAD;
}
void NavigationControllerImpl::ContinuePendingReload() {
if (pending_reload_ == NO_RELOAD) {
NOTREACHED();
} else {
ReloadInternal(false, pending_reload_);
pending_reload_ = NO_RELOAD;
}
}
bool NavigationControllerImpl::IsInitialNavigation() const {
return is_initial_navigation_;
}
NavigationEntryImpl* NavigationControllerImpl::GetEntryWithPageID(
SiteInstance* instance, int32 page_id) const {
int index = GetEntryIndexWithPageID(instance, page_id);
return (index != -1) ? entries_[index].get() : NULL;
}
void NavigationControllerImpl::LoadEntry(NavigationEntryImpl* entry) {
SetPendingEntry(entry);
NavigateToPendingEntry(NO_RELOAD);
}
void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) {
DiscardNonCommittedEntriesInternal();
pending_entry_ = entry;
NotificationService::current()->Notify(
NOTIFICATION_NAV_ENTRY_PENDING,
Source<NavigationController>(this),
Details<NavigationEntry>(entry));
}
NavigationEntry* NavigationControllerImpl::GetActiveEntry() const {
if (transient_entry_index_ != -1)
return entries_[transient_entry_index_].get();
if (pending_entry_)
return pending_entry_;
return GetLastCommittedEntry();
}
NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const {
if (transient_entry_index_ != -1)
return entries_[transient_entry_index_].get();
RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
delegate_->GetRenderViewHost());
bool safe_to_show_pending =
pending_entry_ &&
pending_entry_->GetPageID() == -1 &&
(!pending_entry_->is_renderer_initiated() ||
(IsInitialNavigation() &&
!GetLastCommittedEntry() &&
!rvh->has_accessed_initial_document()));
if (!safe_to_show_pending &&
pending_entry_ &&
pending_entry_->GetPageID() != -1 &&
IsInitialNavigation() &&
!pending_entry_->is_renderer_initiated())
safe_to_show_pending = true;
if (safe_to_show_pending)
return pending_entry_;
return GetLastCommittedEntry();
}
int NavigationControllerImpl::GetCurrentEntryIndex() const {
if (transient_entry_index_ != -1)
return transient_entry_index_;
if (pending_entry_index_ != -1)
return pending_entry_index_;
return last_committed_entry_index_;
}
NavigationEntry* NavigationControllerImpl::GetLastCommittedEntry() const {
if (last_committed_entry_index_ == -1)
return NULL;
return entries_[last_committed_entry_index_].get();
}
bool NavigationControllerImpl::CanViewSource() const {
const std::string& mime_type = delegate_->GetContentsMimeType();
bool is_viewable_mime_type = net::IsSupportedNonImageMimeType(mime_type) &&
!net::IsSupportedMediaMimeType(mime_type);
NavigationEntry* visible_entry = GetVisibleEntry();
return visible_entry && !visible_entry->IsViewSourceMode() &&
is_viewable_mime_type && !delegate_->GetInterstitialPage();
}
int NavigationControllerImpl::GetLastCommittedEntryIndex() const {
return last_committed_entry_index_;
}
int NavigationControllerImpl::GetEntryCount() const {
DCHECK(entries_.size() <= max_entry_count());
return static_cast<int>(entries_.size());
}
NavigationEntry* NavigationControllerImpl::GetEntryAtIndex(
int index) const {
return entries_.at(index).get();
}
NavigationEntry* NavigationControllerImpl::GetEntryAtOffset(
int offset) const {
int index = GetIndexForOffset(offset);
if (index < 0 || index >= GetEntryCount())
return NULL;
return entries_[index].get();
}
int NavigationControllerImpl::GetIndexForOffset(int offset) const {
return GetCurrentEntryIndex() + offset;
}
void NavigationControllerImpl::TakeScreenshot() {
screenshot_manager_->TakeScreenshot();
}
void NavigationControllerImpl::SetScreenshotManager(
NavigationEntryScreenshotManager* manager) {
screenshot_manager_.reset(manager ? manager :
new NavigationEntryScreenshotManager(this));
}
bool NavigationControllerImpl::CanGoBack() const {
return entries_.size() > 1 && GetCurrentEntryIndex() > 0;
}
bool NavigationControllerImpl::CanGoForward() const {
int index = GetCurrentEntryIndex();
return index >= 0 && index < (static_cast<int>(entries_.size()) - 1);
}
bool NavigationControllerImpl::CanGoToOffset(int offset) const {
int index = GetIndexForOffset(offset);
return index >= 0 && index < GetEntryCount();
}
void NavigationControllerImpl::GoBack() {
if (!CanGoBack()) {
NOTREACHED();
return;
}
int current_index = GetCurrentEntryIndex();
DiscardNonCommittedEntries();
pending_entry_index_ = current_index - 1;
entries_[pending_entry_index_]->SetTransitionType(
PageTransitionFromInt(
entries_[pending_entry_index_]->GetTransitionType() |
PAGE_TRANSITION_FORWARD_BACK));
NavigateToPendingEntry(NO_RELOAD);
}
void NavigationControllerImpl::GoForward() {
if (!CanGoForward()) {
NOTREACHED();
return;
}
bool transient = (transient_entry_index_ != -1);
int current_index = GetCurrentEntryIndex();
DiscardNonCommittedEntries();
pending_entry_index_ = current_index;
if (!transient)
pending_entry_index_++;
entries_[pending_entry_index_]->SetTransitionType(
PageTransitionFromInt(
entries_[pending_entry_index_]->GetTransitionType() |
PAGE_TRANSITION_FORWARD_BACK));
NavigateToPendingEntry(NO_RELOAD);
}
void NavigationControllerImpl::GoToIndex(int index) {
if (index < 0 || index >= static_cast<int>(entries_.size())) {
NOTREACHED();
return;
}
if (transient_entry_index_ != -1) {
if (index == transient_entry_index_) {
return;
}
if (index > transient_entry_index_) {
index--;
}
}
DiscardNonCommittedEntries();
pending_entry_index_ = index;
entries_[pending_entry_index_]->SetTransitionType(
PageTransitionFromInt(
entries_[pending_entry_index_]->GetTransitionType() |
PAGE_TRANSITION_FORWARD_BACK));
NavigateToPendingEntry(NO_RELOAD);
}
void NavigationControllerImpl::GoToOffset(int offset) {
if (!CanGoToOffset(offset))
return;
GoToIndex(GetIndexForOffset(offset));
}
bool NavigationControllerImpl::RemoveEntryAtIndex(int index) {
if (index == last_committed_entry_index_ ||
index == pending_entry_index_)
return false;
RemoveEntryAtIndexInternal(index);
return true;
}
void NavigationControllerImpl::UpdateVirtualURLToURL(
NavigationEntryImpl* entry, const GURL& new_url) {
GURL new_virtual_url(new_url);
if (BrowserURLHandlerImpl::GetInstance()->ReverseURLRewrite(
&new_virtual_url, entry->GetVirtualURL(), browser_context_)) {
entry->SetVirtualURL(new_virtual_url);
}
}
void NavigationControllerImpl::LoadURL(
const GURL& url,
const Referrer& referrer,
PageTransition transition,
const std::string& extra_headers) {
LoadURLParams params(url);
params.referrer = referrer;
params.transition_type = transition;
params.extra_headers = extra_headers;
LoadURLWithParams(params);
}
void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
TRACE_EVENT0("browser", "NavigationControllerImpl::LoadURLWithParams");
if (HandleDebugURL(params.url, params.transition_type))
return;
if (IsRendererDebugURL(params.url)) {
if (!delegate_->GetRenderViewHost()->IsRenderViewLive() &&
!IsInitialNavigation())
return;
}
switch (params.load_type) {
case LOAD_TYPE_DEFAULT:
break;
case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST:
if (!params.url.SchemeIs(kHttpScheme) &&
!params.url.SchemeIs(kHttpsScheme)) {
NOTREACHED() << "Http post load must use http(s) scheme.";
return;
}
break;
case LOAD_TYPE_DATA:
if (!params.url.SchemeIs(kDataScheme)) {
NOTREACHED() << "Data load must use data scheme.";
return;
}
break;
default:
NOTREACHED();
break;
};
needs_reload_ = false;
bool override = false;
switch (params.override_user_agent) {
case UA_OVERRIDE_INHERIT:
override = ShouldKeepOverride(GetLastCommittedEntry());
break;
case UA_OVERRIDE_TRUE:
override = true;
break;
case UA_OVERRIDE_FALSE:
override = false;
break;
default:
NOTREACHED();
break;
}
NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
CreateNavigationEntry(
params.url,
params.referrer,
params.transition_type,
params.is_renderer_initiated,
params.extra_headers,
browser_context_));
if (params.frame_tree_node_id != -1)
entry->set_frame_tree_node_id(params.frame_tree_node_id);
if (params.redirect_chain.size() > 0)
entry->set_redirect_chain(params.redirect_chain);
if (params.should_replace_current_entry)
entry->set_should_replace_entry(true);
entry->set_should_clear_history_list(params.should_clear_history_list);
entry->SetIsOverridingUserAgent(override);
entry->set_transferred_global_request_id(
params.transferred_global_request_id);
entry->SetFrameToNavigate(params.frame_name);
switch (params.load_type) {
case LOAD_TYPE_DEFAULT:
break;
case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST:
entry->SetHasPostData(true);
entry->SetBrowserInitiatedPostData(
params.browser_initiated_post_data.get());
break;
case LOAD_TYPE_DATA:
entry->SetBaseURLForDataURL(params.base_url_for_data_url);
entry->SetVirtualURL(params.virtual_url_for_data_url);
entry->SetCanLoadLocalResources(params.can_load_local_resources);
break;
default:
NOTREACHED();
break;
};
LoadEntry(entry);
}
bool NavigationControllerImpl::RendererDidNavigate(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
LoadCommittedDetails* details) {
is_initial_navigation_ = false;
if (GetLastCommittedEntry()) {
details->previous_url = GetLastCommittedEntry()->GetURL();
details->previous_entry_index = GetLastCommittedEntryIndex();
} else {
details->previous_url = GURL();
details->previous_entry_index = -1;
}
DCHECK(pending_entry_index_ == -1 || pending_entry_->site_instance());
details->did_replace_entry =
pending_entry_ && pending_entry_->should_replace_entry();
details->type = ClassifyNavigation(rfh, params);
details->is_in_page = IsURLInPageNavigation(
params.url, params.was_within_same_page, details->type);
switch (details->type) {
case NAVIGATION_TYPE_NEW_PAGE:
RendererDidNavigateToNewPage(rfh, params, details->did_replace_entry);
break;
case NAVIGATION_TYPE_EXISTING_PAGE:
RendererDidNavigateToExistingPage(rfh, params);
break;
case NAVIGATION_TYPE_SAME_PAGE:
RendererDidNavigateToSamePage(rfh, params);
break;
case NAVIGATION_TYPE_IN_PAGE:
RendererDidNavigateInPage(rfh, params, &details->did_replace_entry);
break;
case NAVIGATION_TYPE_NEW_SUBFRAME:
RendererDidNavigateNewSubframe(rfh, params);
break;
case NAVIGATION_TYPE_AUTO_SUBFRAME:
if (!RendererDidNavigateAutoSubframe(rfh, params))
return false;
break;
case NAVIGATION_TYPE_NAV_IGNORE:
if (pending_entry_) {
DiscardNonCommittedEntries();
delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_URL);
}
return false;
default:
NOTREACHED();
}
base::Time timestamp =
time_smoother_.GetSmoothedTime(get_timestamp_callback_.Run());
DVLOG(1) << "Navigation finished at (smoothed) timestamp "
<< timestamp.ToInternalValue();
DiscardNonCommittedEntriesInternal();
DCHECK(params.page_state.IsValid());
NavigationEntryImpl* active_entry =
NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
active_entry->SetTimestamp(timestamp);
active_entry->SetHttpStatusCode(params.http_status_code);
active_entry->SetPageState(params.page_state);
active_entry->ResetForCommit();
if (PageTransitionIsMainFrame(params.transition))
CHECK(active_entry->site_instance() == rfh->GetSiteInstance());
active_entry->SetBindings(
static_cast<RenderFrameHostImpl*>(rfh)->GetEnabledBindings());
details->entry = active_entry;
details->is_main_frame =
PageTransitionIsMainFrame(params.transition);
details->serialized_security_info = params.security_info;
details->http_status_code = params.http_status_code;
NotifyNavigationEntryCommitted(details);
return true;
}
NavigationType NavigationControllerImpl::ClassifyNavigation(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const {
if (params.page_id == -1) {
return NAVIGATION_TYPE_NAV_IGNORE;
}
if (params.page_id > delegate_->GetMaxPageIDForSiteInstance(
rfh->GetSiteInstance())) {
if (PageTransitionIsMainFrame(params.transition))
return NAVIGATION_TYPE_NEW_PAGE;
if (!GetLastCommittedEntry())
return NAVIGATION_TYPE_NAV_IGNORE;
return NAVIGATION_TYPE_NEW_SUBFRAME;
}
DCHECK(!params.history_list_was_cleared);
int existing_entry_index = GetEntryIndexWithPageID(
rfh->GetSiteInstance(),
params.page_id);
if (existing_entry_index == -1) {
NOTREACHED();
LOG(ERROR) << "terminating renderer for bad navigation: " << params.url;
RecordAction(base::UserMetricsAction("BadMessageTerminate_NC"));
std::string temp = params.url.spec();
temp.append("#page");
temp.append(base::IntToString(params.page_id));
temp.append("#max");
temp.append(base::IntToString(delegate_->GetMaxPageID()));
temp.append("#frame");
temp.append(base::IntToString(rfh->GetRoutingID()));
temp.append("#ids");
for (int i = 0; i < static_cast<int>(entries_.size()); ++i) {
temp.append(base::IntToString(entries_[i]->GetPageID()));
temp.append("_");
if (entries_[i]->site_instance())
temp.append(base::IntToString(entries_[i]->site_instance()->GetId()));
else
temp.append("N");
if (entries_[i]->site_instance() != rfh->GetSiteInstance())
temp.append("x");
temp.append(",");
}
GURL url(temp);
static_cast<RenderFrameHostImpl*>(rfh)->render_view_host()->Send(
new ViewMsg_TempCrashWithData(url));
return NAVIGATION_TYPE_NAV_IGNORE;
}
NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get();
if (!PageTransitionIsMainFrame(params.transition)) {
DCHECK(GetLastCommittedEntry());
return NAVIGATION_TYPE_AUTO_SUBFRAME;
}
if (pending_entry_ &&
!pending_entry_->is_renderer_initiated() &&
existing_entry != pending_entry_ &&
pending_entry_->GetPageID() == -1 &&
existing_entry == GetLastCommittedEntry()) {
return NAVIGATION_TYPE_SAME_PAGE;
}
if (AreURLsInPageNavigation(existing_entry->GetURL(), params.url,
params.was_within_same_page,
NAVIGATION_TYPE_UNKNOWN)) {
return NAVIGATION_TYPE_IN_PAGE;
}
return NAVIGATION_TYPE_EXISTING_PAGE;
}
void NavigationControllerImpl::RendererDidNavigateToNewPage(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
bool replace_entry) {
NavigationEntryImpl* new_entry;
bool update_virtual_url;
if (pending_entry_ &&
(!pending_entry_->site_instance() ||
pending_entry_->site_instance() == rfh->GetSiteInstance())) {
new_entry = new NavigationEntryImpl(*pending_entry_);
new_entry->set_page_type(PAGE_TYPE_NORMAL);
update_virtual_url = new_entry->update_virtual_url_with_url();
} else {
new_entry = new NavigationEntryImpl;
GURL url = params.url;
bool needs_update = false;
BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
&url, browser_context_, &needs_update);
new_entry->set_update_virtual_url_with_url(needs_update);
update_virtual_url = needs_update;
}
new_entry->SetURL(params.url);
if (update_virtual_url)
UpdateVirtualURLToURL(new_entry, params.url);
new_entry->SetReferrer(params.referrer);
new_entry->SetPageID(params.page_id);
new_entry->SetTransitionType(params.transition);
new_entry->set_site_instance(
static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
new_entry->SetHasPostData(params.is_post);
new_entry->SetPostID(params.post_id);
new_entry->SetOriginalRequestURL(params.original_request_url);
new_entry->SetIsOverridingUserAgent(params.is_overriding_user_agent);
DCHECK(!params.history_list_was_cleared || !replace_entry);
if (params.history_list_was_cleared) {
DiscardNonCommittedEntriesInternal();
entries_.clear();
last_committed_entry_index_ = -1;
}
InsertOrReplaceEntry(new_entry, replace_entry);
}
void NavigationControllerImpl::RendererDidNavigateToExistingPage(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
DCHECK(PageTransitionIsMainFrame(params.transition));
int entry_index = GetEntryIndexWithPageID(rfh->GetSiteInstance(),
params.page_id);
DCHECK(entry_index >= 0 &&
entry_index < static_cast<int>(entries_.size()));
NavigationEntryImpl* entry = entries_[entry_index].get();
entry->SetURL(params.url);
entry->SetReferrer(params.referrer);
if (entry->update_virtual_url_with_url())
UpdateVirtualURLToURL(entry, params.url);
if (PageTransitionIsRedirect(params.transition))
entry->GetFavicon() = FaviconStatus();
DCHECK(entry->site_instance() == NULL ||
entry->site_instance() == rfh->GetSiteInstance());
entry->set_site_instance(
static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
entry->SetHasPostData(params.is_post);
entry->SetPostID(params.post_id);
DiscardNonCommittedEntriesInternal();
last_committed_entry_index_ =
GetEntryIndexWithPageID(rfh->GetSiteInstance(), params.page_id);
}
void NavigationControllerImpl::RendererDidNavigateToSamePage(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
NavigationEntryImpl* existing_entry = GetEntryWithPageID(
rfh->GetSiteInstance(), params.page_id);
existing_entry->set_unique_id(pending_entry_->GetUniqueID());
if (existing_entry->update_virtual_url_with_url())
UpdateVirtualURLToURL(existing_entry, params.url);
existing_entry->SetURL(params.url);
existing_entry->SetReferrer(params.referrer);
existing_entry->SetHasPostData(params.is_post);
existing_entry->SetPostID(params.post_id);
DiscardNonCommittedEntries();
}
void NavigationControllerImpl::RendererDidNavigateInPage(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
bool* did_replace_entry) {
DCHECK(PageTransitionIsMainFrame(params.transition)) <<
"WebKit should only tell us about in-page navs for the main frame.";
NavigationEntryImpl* existing_entry = GetEntryWithPageID(
rfh->GetSiteInstance(), params.page_id);
existing_entry->SetURL(params.url);
if (existing_entry->update_virtual_url_with_url())
UpdateVirtualURLToURL(existing_entry, params.url);
*did_replace_entry = true;
DiscardNonCommittedEntriesInternal();
last_committed_entry_index_ =
GetEntryIndexWithPageID(rfh->GetSiteInstance(), params.page_id);
}
void NavigationControllerImpl::RendererDidNavigateNewSubframe(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
if (PageTransitionCoreTypeIs(params.transition,
PAGE_TRANSITION_AUTO_SUBFRAME)) {
DiscardNonCommittedEntriesInternal();
return;
}
DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee "
<< "that a last committed entry exists.";
NavigationEntryImpl* new_entry = new NavigationEntryImpl(
*NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()));
new_entry->SetPageID(params.page_id);
InsertOrReplaceEntry(new_entry, false);
}
bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
RenderFrameHost* rfh,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
DCHECK(GetLastCommittedEntry());
int entry_index = GetEntryIndexWithPageID(
rfh->GetSiteInstance(),
params.page_id);
if (entry_index < 0 ||
entry_index >= static_cast<int>(entries_.size())) {
NOTREACHED();
return false;
}
if (entry_index != last_committed_entry_index_) {
last_committed_entry_index_ = entry_index;
DiscardNonCommittedEntriesInternal();
return true;
}
return false;
}
int NavigationControllerImpl::GetIndexOfEntry(
const NavigationEntryImpl* entry) const {
const NavigationEntries::const_iterator i(std::find(
entries_.begin(),
entries_.end(),
entry));
return (i == entries_.end()) ? -1 : static_cast<int>(i - entries_.begin());
}
bool NavigationControllerImpl::IsURLInPageNavigation(
const GURL& url,
bool renderer_says_in_page,
NavigationType navigation_type) const {
NavigationEntry* last_committed = GetLastCommittedEntry();
return last_committed && AreURLsInPageNavigation(
last_committed->GetURL(), url, renderer_says_in_page, navigation_type);
}
void NavigationControllerImpl::CopyStateFrom(
const NavigationController& temp) {
const NavigationControllerImpl& source =
static_cast<const NavigationControllerImpl&>(temp);
DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
if (source.GetEntryCount() == 0)
return;
needs_reload_ = true;
InsertEntriesFrom(source, source.GetEntryCount());
for (SessionStorageNamespaceMap::const_iterator it =
source.session_storage_namespace_map_.begin();
it != source.session_storage_namespace_map_.end();
++it) {
SessionStorageNamespaceImpl* source_namespace =
static_cast<SessionStorageNamespaceImpl*>(it->second.get());
session_storage_namespace_map_[it->first] = source_namespace->Clone();
}
FinishRestore(source.last_committed_entry_index_, RESTORE_CURRENT_SESSION);
delegate_->CopyMaxPageIDsFrom(source.delegate()->GetWebContents());
}
void NavigationControllerImpl::CopyStateFromAndPrune(
NavigationController* temp,
bool replace_entry) {
CHECK(CanPruneAllButLastCommitted());
NavigationControllerImpl* source =
static_cast<NavigationControllerImpl*>(temp);
NavigationEntryImpl* last_committed =
NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
scoped_refptr<SiteInstance> site_instance(
last_committed->site_instance());
int32 minimum_page_id = last_committed->GetPageID();
int32 max_page_id =
delegate_->GetMaxPageIDForSiteInstance(site_instance.get());
PruneAllButLastCommittedInternal();
DCHECK_EQ(1, GetEntryCount());
if (!replace_entry)
source->PruneOldestEntryIfFull();
int max_source_index = source->last_committed_entry_index_;
if (max_source_index == -1)
max_source_index = source->GetEntryCount();
else
max_source_index++;
if (replace_entry && max_source_index > 0)
max_source_index--;
InsertEntriesFrom(*source, max_source_index);
last_committed_entry_index_ = GetEntryCount() - 1;
delegate_->SetHistoryLengthAndPrune(site_instance.get(),
max_source_index,
minimum_page_id);
delegate_->CopyMaxPageIDsFrom(source->delegate()->GetWebContents());
if (max_page_id > -1) {
delegate_->UpdateMaxPageIDForSiteInstance(site_instance.get(),
max_page_id);
}
}
bool NavigationControllerImpl::CanPruneAllButLastCommitted() {
if (last_committed_entry_index_ == -1)
return false;
if (pending_entry_index_ != -1)
return false;
if (transient_entry_index_ != -1)
return false;
return true;
}
void NavigationControllerImpl::PruneAllButLastCommitted() {
PruneAllButLastCommittedInternal();
DCHECK_NE(-1, last_committed_entry_index_);
NavigationEntryImpl* entry =
NavigationEntryImpl::FromNavigationEntry(GetVisibleEntry());
delegate_->SetHistoryLengthAndPrune(
entry->site_instance(), 0, entry->GetPageID());
}
void NavigationControllerImpl::PruneAllButLastCommittedInternal() {
CHECK(CanPruneAllButLastCommitted());
entries_.erase(entries_.begin(),
entries_.begin() + last_committed_entry_index_);
entries_.erase(entries_.begin() + 1, entries_.end());
last_committed_entry_index_ = 0;
}
void NavigationControllerImpl::ClearAllScreenshots() {
screenshot_manager_->ClearAllScreenshots();
}
void NavigationControllerImpl::SetSessionStorageNamespace(
const std::string& partition_id,
SessionStorageNamespace* session_storage_namespace) {
if (!session_storage_namespace)
return;
bool successful_insert = session_storage_namespace_map_.insert(
make_pair(partition_id,
static_cast<SessionStorageNamespaceImpl*>(
session_storage_namespace)))
.second;
CHECK(successful_insert) << "Cannot replace existing SessionStorageNamespace";
}
void NavigationControllerImpl::SetMaxRestoredPageID(int32 max_id) {
max_restored_page_id_ = max_id;
}
int32 NavigationControllerImpl::GetMaxRestoredPageID() const {
return max_restored_page_id_;
}
SessionStorageNamespace*
NavigationControllerImpl::GetSessionStorageNamespace(SiteInstance* instance) {
std::string partition_id;
if (instance) {
partition_id =
GetContentClient()->browser()->GetStoragePartitionIdForSite(
browser_context_, instance->GetSiteURL());
}
SessionStorageNamespaceMap::const_iterator it =
session_storage_namespace_map_.find(partition_id);
if (it != session_storage_namespace_map_.end())
return it->second.get();
StoragePartition* partition =
BrowserContext::GetStoragePartition(browser_context_, instance);
SessionStorageNamespaceImpl* session_storage_namespace =
new SessionStorageNamespaceImpl(
static_cast<DOMStorageContextWrapper*>(
partition->GetDOMStorageContext()));
session_storage_namespace_map_[partition_id] = session_storage_namespace;
return session_storage_namespace;
}
SessionStorageNamespace*
NavigationControllerImpl::GetDefaultSessionStorageNamespace() {
return GetSessionStorageNamespace(NULL);
}
const SessionStorageNamespaceMap&
NavigationControllerImpl::GetSessionStorageNamespaceMap() const {
return session_storage_namespace_map_;
}
bool NavigationControllerImpl::NeedsReload() const {
return needs_reload_;
}
void NavigationControllerImpl::SetNeedsReload() {
needs_reload_ = true;
}
void NavigationControllerImpl::RemoveEntryAtIndexInternal(int index) {
DCHECK(index < GetEntryCount());
DCHECK(index != last_committed_entry_index_);
DiscardNonCommittedEntries();
entries_.erase(entries_.begin() + index);
if (last_committed_entry_index_ > index)
last_committed_entry_index_--;
}
void NavigationControllerImpl::DiscardNonCommittedEntries() {
bool transient = transient_entry_index_ != -1;
DiscardNonCommittedEntriesInternal();
if (transient) {
delegate_->NotifyNavigationStateChanged(kInvalidateAll);
}
}
NavigationEntry* NavigationControllerImpl::GetPendingEntry() const {
return pending_entry_;
}
int NavigationControllerImpl::GetPendingEntryIndex() const {
return pending_entry_index_;
}
void NavigationControllerImpl::InsertOrReplaceEntry(NavigationEntryImpl* entry,
bool replace) {
DCHECK(entry->GetTransitionType() != PAGE_TRANSITION_AUTO_SUBFRAME);
const NavigationEntryImpl* const pending_entry =
(pending_entry_index_ == -1) ?
pending_entry_ : entries_[pending_entry_index_].get();
if (pending_entry)
entry->set_unique_id(pending_entry->GetUniqueID());
DiscardNonCommittedEntriesInternal();
int current_size = static_cast<int>(entries_.size());
if (current_size > 0) {
if (replace)
--last_committed_entry_index_;
int num_pruned = 0;
while (last_committed_entry_index_ < (current_size - 1)) {
num_pruned++;
entries_.pop_back();
current_size--;
}
if (num_pruned > 0)
NotifyPrunedEntries(this, false, num_pruned);
}
PruneOldestEntryIfFull();
entries_.push_back(linked_ptr<NavigationEntryImpl>(entry));
last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1;
delegate_->UpdateMaxPageID(entry->GetPageID());
}
void NavigationControllerImpl::PruneOldestEntryIfFull() {
if (entries_.size() >= max_entry_count()) {
DCHECK_EQ(max_entry_count(), entries_.size());
DCHECK_GT(last_committed_entry_index_, 0);
RemoveEntryAtIndex(0);
NotifyPrunedEntries(this, true, 1);
}
}
void NavigationControllerImpl::NavigateToPendingEntry(ReloadType reload_type) {
needs_reload_ = false;
if (pending_entry_index_ != -1 &&
pending_entry_index_ == last_committed_entry_index_ &&
(entries_[pending_entry_index_]->restore_type() ==
NavigationEntryImpl::RESTORE_NONE) &&
(entries_[pending_entry_index_]->GetTransitionType() &
PAGE_TRANSITION_FORWARD_BACK)) {
delegate_->Stop();
if (delegate_->GetInterstitialPage())
delegate_->GetInterstitialPage()->DontProceed();
DiscardNonCommittedEntries();
return;
}
if (delegate_->GetInterstitialPage()) {
static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())->
CancelForNavigation();
}
if (!pending_entry_) {
DCHECK_NE(pending_entry_index_, -1);
pending_entry_ = entries_[pending_entry_index_].get();
}
CHECK(!in_navigate_to_pending_entry_);
in_navigate_to_pending_entry_ = true;
bool success = delegate_->NavigateToPendingEntry(reload_type);
in_navigate_to_pending_entry_ = false;
if (!success)
DiscardNonCommittedEntries();
if (pending_entry_ && !pending_entry_->site_instance() &&
pending_entry_->restore_type() != NavigationEntryImpl::RESTORE_NONE) {
pending_entry_->set_site_instance(static_cast<SiteInstanceImpl*>(
delegate_->GetPendingSiteInstance()));
pending_entry_->set_restore_type(NavigationEntryImpl::RESTORE_NONE);
}
}
void NavigationControllerImpl::NotifyNavigationEntryCommitted(
LoadCommittedDetails* details) {
details->entry = GetLastCommittedEntry();
ssl_manager_.DidCommitProvisionalLoad(*details);
delegate_->NotifyNavigationStateChanged(kInvalidateAll);
delegate_->NotifyNavigationEntryCommitted(*details);
NotificationDetails notification_details =
Details<LoadCommittedDetails>(details);
NotificationService::current()->Notify(
NOTIFICATION_NAV_ENTRY_COMMITTED,
Source<NavigationController>(this),
notification_details);
}
size_t NavigationControllerImpl::max_entry_count() {
if (max_entry_count_for_testing_ != kMaxEntryCountForTestingNotSet)
return max_entry_count_for_testing_;
return kMaxSessionHistoryEntries;
}
void NavigationControllerImpl::SetActive(bool is_active) {
if (is_active && needs_reload_)
LoadIfNecessary();
}
void NavigationControllerImpl::LoadIfNecessary() {
if (!needs_reload_)
return;
pending_entry_index_ = last_committed_entry_index_;
NavigateToPendingEntry(NO_RELOAD);
}
void NavigationControllerImpl::NotifyEntryChanged(const NavigationEntry* entry,
int index) {
EntryChangedDetails det;
det.changed_entry = entry;
det.index = index;
NotificationService::current()->Notify(
NOTIFICATION_NAV_ENTRY_CHANGED,
Source<NavigationController>(this),
Details<EntryChangedDetails>(&det));
}
void NavigationControllerImpl::FinishRestore(int selected_index,
RestoreType type) {
DCHECK(selected_index >= 0 && selected_index < GetEntryCount());
ConfigureEntriesForRestore(&entries_, type);
SetMaxRestoredPageID(static_cast<int32>(GetEntryCount()));
last_committed_entry_index_ = selected_index;
}
void NavigationControllerImpl::DiscardNonCommittedEntriesInternal() {
DiscardPendingEntry();
DiscardTransientEntry();
}
void NavigationControllerImpl::DiscardPendingEntry() {
CHECK(!in_navigate_to_pending_entry_);
if (pending_entry_index_ == -1)
delete pending_entry_;
pending_entry_ = NULL;
pending_entry_index_ = -1;
}
void NavigationControllerImpl::DiscardTransientEntry() {
if (transient_entry_index_ == -1)
return;
entries_.erase(entries_.begin() + transient_entry_index_);
if (last_committed_entry_index_ > transient_entry_index_)
last_committed_entry_index_--;
transient_entry_index_ = -1;
}
int NavigationControllerImpl::GetEntryIndexWithPageID(
SiteInstance* instance, int32 page_id) const {
for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) {
if ((entries_[i]->site_instance() == instance) &&
(entries_[i]->GetPageID() == page_id))
return i;
}
return -1;
}
NavigationEntry* NavigationControllerImpl::GetTransientEntry() const {
if (transient_entry_index_ == -1)
return NULL;
return entries_[transient_entry_index_].get();
}
void NavigationControllerImpl::SetTransientEntry(NavigationEntry* entry) {
int index = 0;
if (last_committed_entry_index_ != -1)
index = last_committed_entry_index_ + 1;
DiscardTransientEntry();
entries_.insert(
entries_.begin() + index, linked_ptr<NavigationEntryImpl>(
NavigationEntryImpl::FromNavigationEntry(entry)));
transient_entry_index_ = index;
delegate_->NotifyNavigationStateChanged(kInvalidateAll);
}
void NavigationControllerImpl::InsertEntriesFrom(
const NavigationControllerImpl& source,
int max_index) {
DCHECK_LE(max_index, source.GetEntryCount());
size_t insert_index = 0;
for (int i = 0; i < max_index; i++) {
if (source.entries_[i].get()->GetPageType() !=
PAGE_TYPE_INTERSTITIAL) {
entries_.insert(entries_.begin() + insert_index++,
linked_ptr<NavigationEntryImpl>(
new NavigationEntryImpl(*source.entries_[i])));
}
}
}
void NavigationControllerImpl::SetGetTimestampCallbackForTest(
const base::Callback<base::Time()>& get_timestamp_callback) {
get_timestamp_callback_ = get_timestamp_callback;
}
}