| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/web_contents/navigation_controller_impl.h" | 5 #include "content/browser/web_contents/navigation_controller_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 NavigationEntryImpl::FromNavigationEntry(GetActiveEntry()); | 281 NavigationEntryImpl::FromNavigationEntry(GetActiveEntry()); |
| 282 if (!active_entry) | 282 if (!active_entry) |
| 283 return; | 283 return; |
| 284 LoadURL(active_entry->GetURL(), | 284 LoadURL(active_entry->GetURL(), |
| 285 Referrer(), | 285 Referrer(), |
| 286 PAGE_TRANSITION_RELOAD, | 286 PAGE_TRANSITION_RELOAD, |
| 287 active_entry->extra_headers()); | 287 active_entry->extra_headers()); |
| 288 return; | 288 return; |
| 289 } | 289 } |
| 290 | 290 |
| 291 DiscardNonCommittedEntriesInternal(); | 291 NavigationEntryImpl* entry = NULL; |
| 292 int current_index = GetCurrentEntryIndex(); | 292 int current_index = -1; |
| 293 |
| 294 // If we are reloading the initial navigation, just use the current |
| 295 // pending entry. Otherwise look up the current entry. |
| 296 if (IsInitialNavigation() && pending_entry_) { |
| 297 entry = pending_entry_; |
| 298 } else { |
| 299 DiscardNonCommittedEntriesInternal(); |
| 300 current_index = GetCurrentEntryIndex(); |
| 301 if (current_index != -1) { |
| 302 entry = NavigationEntryImpl::FromNavigationEntry( |
| 303 GetEntryAtIndex(current_index)); |
| 304 } |
| 305 } |
| 306 |
| 293 // If we are no where, then we can't reload. TODO(darin): We should add a | 307 // If we are no where, then we can't reload. TODO(darin): We should add a |
| 294 // CanReload method. | 308 // CanReload method. |
| 295 if (current_index == -1) { | 309 if (!entry) |
| 296 return; | 310 return; |
| 297 } | |
| 298 | 311 |
| 299 if (g_check_for_repost && check_for_repost && | 312 if (g_check_for_repost && check_for_repost && |
| 300 GetEntryAtIndex(current_index)->GetHasPostData()) { | 313 entry->GetHasPostData()) { |
| 301 // The user is asking to reload a page with POST data. Prompt to make sure | 314 // The user is asking to reload a page with POST data. Prompt to make sure |
| 302 // they really want to do this. If they do, the dialog will call us back | 315 // they really want to do this. If they do, the dialog will call us back |
| 303 // with check_for_repost = false. | 316 // with check_for_repost = false. |
| 304 web_contents_->NotifyBeforeFormRepostWarningShow(); | 317 web_contents_->NotifyBeforeFormRepostWarningShow(); |
| 305 | 318 |
| 306 pending_reload_ = reload_type; | 319 pending_reload_ = reload_type; |
| 307 web_contents_->Activate(); | 320 web_contents_->Activate(); |
| 308 web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_); | 321 web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_); |
| 309 } else { | 322 } else { |
| 310 DiscardNonCommittedEntriesInternal(); | 323 if (!IsInitialNavigation()) |
| 311 | 324 DiscardNonCommittedEntriesInternal(); |
| 312 NavigationEntryImpl* entry = entries_[current_index].get(); | |
| 313 SiteInstanceImpl* site_instance = entry->site_instance(); | |
| 314 DCHECK(site_instance); | |
| 315 | 325 |
| 316 // If we are reloading an entry that no longer belongs to the current | 326 // If we are reloading an entry that no longer belongs to the current |
| 317 // site instance (for example, refreshing a page for just installed app), | 327 // site instance (for example, refreshing a page for just installed app), |
| 318 // the reload must happen in a new process. | 328 // the reload must happen in a new process. |
| 319 // The new entry must have a new page_id and site instance, so it behaves | 329 // The new entry must have a new page_id and site instance, so it behaves |
| 320 // as new navigation (which happens to clear forward history). | 330 // as new navigation (which happens to clear forward history). |
| 321 // Tabs that are discarded due to low memory conditions may not have a site | 331 // Tabs that are discarded due to low memory conditions may not have a site |
| 322 // instance, and should not be treated as a cross-site reload. | 332 // instance, and should not be treated as a cross-site reload. |
| 333 SiteInstanceImpl* site_instance = entry->site_instance(); |
| 323 if (site_instance && | 334 if (site_instance && |
| 324 site_instance->HasWrongProcessForURL(entry->GetURL())) { | 335 site_instance->HasWrongProcessForURL(entry->GetURL())) { |
| 325 // Create a navigation entry that resembles the current one, but do not | 336 // Create a navigation entry that resembles the current one, but do not |
| 326 // copy page id, site instance, content state, or timestamp. | 337 // copy page id, site instance, content state, or timestamp. |
| 327 NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry( | 338 NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry( |
| 328 CreateNavigationEntry( | 339 CreateNavigationEntry( |
| 329 entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(), | 340 entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(), |
| 330 false, entry->extra_headers(), browser_context_)); | 341 false, entry->extra_headers(), browser_context_)); |
| 331 | 342 |
| 332 // Mark the reload type as NO_RELOAD, so navigation will not be considered | 343 // Mark the reload type as NO_RELOAD, so navigation will not be considered |
| 333 // a reload in the renderer. | 344 // a reload in the renderer. |
| 334 reload_type = NavigationController::NO_RELOAD; | 345 reload_type = NavigationController::NO_RELOAD; |
| 335 | 346 |
| 336 nav_entry->set_should_replace_entry(true); | 347 nav_entry->set_should_replace_entry(true); |
| 337 pending_entry_ = nav_entry; | 348 pending_entry_ = nav_entry; |
| 338 } else { | 349 } else { |
| 350 pending_entry_ = entry; |
| 339 pending_entry_index_ = current_index; | 351 pending_entry_index_ = current_index; |
| 340 | 352 |
| 341 // The title of the page being reloaded might have been removed in the | 353 // The title of the page being reloaded might have been removed in the |
| 342 // meanwhile, so we need to revert to the default title upon reload and | 354 // meanwhile, so we need to revert to the default title upon reload and |
| 343 // invalidate the previously cached title (SetTitle will do both). | 355 // invalidate the previously cached title (SetTitle will do both). |
| 344 // See Chromium issue 96041. | 356 // See Chromium issue 96041. |
| 345 entries_[pending_entry_index_]->SetTitle(string16()); | 357 pending_entry_->SetTitle(string16()); |
| 346 | 358 |
| 347 entries_[pending_entry_index_]->SetTransitionType(PAGE_TRANSITION_RELOAD); | 359 pending_entry_->SetTransitionType(PAGE_TRANSITION_RELOAD); |
| 348 } | 360 } |
| 349 | 361 |
| 350 NavigateToPendingEntry(reload_type); | 362 NavigateToPendingEntry(reload_type); |
| 351 } | 363 } |
| 352 } | 364 } |
| 353 | 365 |
| 354 void NavigationControllerImpl::CancelPendingReload() { | 366 void NavigationControllerImpl::CancelPendingReload() { |
| 355 DCHECK(pending_reload_ != NO_RELOAD); | 367 DCHECK(pending_reload_ != NO_RELOAD); |
| 356 pending_reload_ = NO_RELOAD; | 368 pending_reload_ = NO_RELOAD; |
| 357 } | 369 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 385 policy->IsDisabledScheme(entry->GetVirtualURL().scheme())) { | 397 policy->IsDisabledScheme(entry->GetVirtualURL().scheme())) { |
| 386 VLOG(1) << "URL not loaded because the scheme is blocked by policy: " | 398 VLOG(1) << "URL not loaded because the scheme is blocked by policy: " |
| 387 << entry->GetURL(); | 399 << entry->GetURL(); |
| 388 delete entry; | 400 delete entry; |
| 389 return; | 401 return; |
| 390 } | 402 } |
| 391 | 403 |
| 392 // When navigating to a new page, we don't know for sure if we will actually | 404 // When navigating to a new page, we don't know for sure if we will actually |
| 393 // end up leaving the current page. The new page load could for example | 405 // end up leaving the current page. The new page load could for example |
| 394 // result in a download or a 'no content' response (e.g., a mailto: URL). | 406 // result in a download or a 'no content' response (e.g., a mailto: URL). |
| 407 SetPendingEntry(entry); |
| 408 NavigateToPendingEntry(NO_RELOAD); |
| 409 } |
| 410 |
| 411 void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) { |
| 395 DiscardNonCommittedEntriesInternal(); | 412 DiscardNonCommittedEntriesInternal(); |
| 396 pending_entry_ = entry; | 413 pending_entry_ = entry; |
| 397 NotificationService::current()->Notify( | 414 NotificationService::current()->Notify( |
| 398 NOTIFICATION_NAV_ENTRY_PENDING, | 415 NOTIFICATION_NAV_ENTRY_PENDING, |
| 399 Source<NavigationController>(this), | 416 Source<NavigationController>(this), |
| 400 Details<NavigationEntry>(entry)); | 417 Details<NavigationEntry>(entry)); |
| 401 NavigateToPendingEntry(NO_RELOAD); | |
| 402 } | 418 } |
| 403 | 419 |
| 404 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { | 420 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { |
| 405 if (transient_entry_index_ != -1) | 421 if (transient_entry_index_ != -1) |
| 406 return entries_[transient_entry_index_].get(); | 422 return entries_[transient_entry_index_].get(); |
| 407 if (pending_entry_) | 423 if (pending_entry_) |
| 408 return pending_entry_; | 424 return pending_entry_; |
| 409 return GetLastCommittedEntry(); | 425 return GetLastCommittedEntry(); |
| 410 } | 426 } |
| 411 | 427 |
| 412 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { | 428 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { |
| 413 if (transient_entry_index_ != -1) | 429 if (transient_entry_index_ != -1) |
| 414 return entries_[transient_entry_index_].get(); | 430 return entries_[transient_entry_index_].get(); |
| 415 // Only return the pending_entry for new (non-history), browser-initiated | 431 // The pending entry is safe to return for new (non-history), browser- |
| 416 // navigations, in order to prevent URL spoof attacks. | 432 // initiated navigations. Most renderer-initiated navigations should not |
| 417 // Ideally we would also show the pending entry's URL for new renderer- | 433 // show the pending entry, to prevent URL spoof attacks. |
| 418 // initiated navigations with no last committed entry (e.g., a link opening | 434 // |
| 419 // in a new tab), but an attacker can insert content into the about:blank | 435 // We make an exception for renderer-initiated navigations in new tabs, as |
| 420 // page while the pending URL loads in that case. | 436 // long as no other page has tried to access the initial empty document in |
| 421 if (pending_entry_ && | 437 // the new tab. If another page modifies this blank page, a URL spoof is |
| 438 // possible, so we must stop showing the pending entry. |
| 439 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( |
| 440 web_contents_->GetRenderViewHost()); |
| 441 bool safe_to_show_pending = |
| 442 pending_entry_ && |
| 443 // Require a new navigation. |
| 422 pending_entry_->GetPageID() == -1 && | 444 pending_entry_->GetPageID() == -1 && |
| 445 // Require either browser-initiated or an unmodified new tab. |
| 446 (!pending_entry_->is_renderer_initiated() || |
| 447 (IsInitialNavigation() && |
| 448 !GetLastCommittedEntry() && |
| 449 !rvh->has_accessed_initial_document())); |
| 450 |
| 451 // Also allow showing the pending entry for history navigations in a new tab, |
| 452 // such as Ctrl+Back. In this case, no existing page is visible and no one |
| 453 // can script the new tab before it commits. |
| 454 if (!safe_to_show_pending && |
| 455 pending_entry_ && |
| 456 pending_entry_->GetPageID() != -1 && |
| 457 IsInitialNavigation() && |
| 423 !pending_entry_->is_renderer_initiated()) | 458 !pending_entry_->is_renderer_initiated()) |
| 459 safe_to_show_pending = true; |
| 460 |
| 461 if (safe_to_show_pending) |
| 424 return pending_entry_; | 462 return pending_entry_; |
| 425 return GetLastCommittedEntry(); | 463 return GetLastCommittedEntry(); |
| 426 } | 464 } |
| 427 | 465 |
| 428 int NavigationControllerImpl::GetCurrentEntryIndex() const { | 466 int NavigationControllerImpl::GetCurrentEntryIndex() const { |
| 429 if (transient_entry_index_ != -1) | 467 if (transient_entry_index_ != -1) |
| 430 return transient_entry_index_; | 468 return transient_entry_index_; |
| 431 if (pending_entry_index_ != -1) | 469 if (pending_entry_index_ != -1) |
| 432 return pending_entry_index_; | 470 return pending_entry_index_; |
| 433 return last_committed_entry_index_; | 471 return last_committed_entry_index_; |
| (...skipping 600 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1034 if (!PageTransitionIsMainFrame(params.transition)) { | 1072 if (!PageTransitionIsMainFrame(params.transition)) { |
| 1035 // All manual subframes would get new IDs and were handled above, so we | 1073 // All manual subframes would get new IDs and were handled above, so we |
| 1036 // know this is auto. Since the current page was found in the navigation | 1074 // know this is auto. Since the current page was found in the navigation |
| 1037 // entry list, we're guaranteed to have a last committed entry. | 1075 // entry list, we're guaranteed to have a last committed entry. |
| 1038 DCHECK(GetLastCommittedEntry()); | 1076 DCHECK(GetLastCommittedEntry()); |
| 1039 return NAVIGATION_TYPE_AUTO_SUBFRAME; | 1077 return NAVIGATION_TYPE_AUTO_SUBFRAME; |
| 1040 } | 1078 } |
| 1041 | 1079 |
| 1042 // Anything below here we know is a main frame navigation. | 1080 // Anything below here we know is a main frame navigation. |
| 1043 if (pending_entry_ && | 1081 if (pending_entry_ && |
| 1082 !pending_entry_->is_renderer_initiated() && |
| 1044 existing_entry != pending_entry_ && | 1083 existing_entry != pending_entry_ && |
| 1045 pending_entry_->GetPageID() == -1 && | 1084 pending_entry_->GetPageID() == -1 && |
| 1046 existing_entry == GetLastCommittedEntry()) { | 1085 existing_entry == GetLastCommittedEntry()) { |
| 1047 // In this case, we have a pending entry for a URL but WebCore didn't do a | 1086 // In this case, we have a pending entry for a URL but WebCore didn't do a |
| 1048 // new navigation. This happens when you press enter in the URL bar to | 1087 // new navigation. This happens when you press enter in the URL bar to |
| 1049 // reload. We will create a pending entry, but WebKit will convert it to | 1088 // reload. We will create a pending entry, but WebKit will convert it to |
| 1050 // a reload since it's the same page and not create a new entry for it | 1089 // a reload since it's the same page and not create a new entry for it |
| 1051 // (the user doesn't want to have a new back/forward entry when they do | 1090 // (the user doesn't want to have a new back/forward entry when they do |
| 1052 // this). If this matches the last committed entry, we want to just ignore | 1091 // this). If this matches the last committed entry, we want to just ignore |
| 1053 // the pending entry and go back to where we were (the "existing entry"). | 1092 // the pending entry and go back to where we were (the "existing entry"). |
| (...skipping 757 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1811 const base::Callback<base::Time()>& get_timestamp_callback) { | 1850 const base::Callback<base::Time()>& get_timestamp_callback) { |
| 1812 get_timestamp_callback_ = get_timestamp_callback; | 1851 get_timestamp_callback_ = get_timestamp_callback; |
| 1813 } | 1852 } |
| 1814 | 1853 |
| 1815 void NavigationControllerImpl::SetTakeScreenshotCallbackForTest( | 1854 void NavigationControllerImpl::SetTakeScreenshotCallbackForTest( |
| 1816 const base::Callback<void(RenderViewHost*)>& take_screenshot_callback) { | 1855 const base::Callback<void(RenderViewHost*)>& take_screenshot_callback) { |
| 1817 take_screenshot_callback_ = take_screenshot_callback; | 1856 take_screenshot_callback_ = take_screenshot_callback; |
| 1818 } | 1857 } |
| 1819 | 1858 |
| 1820 } // namespace content | 1859 } // namespace content |
| OLD | NEW |