| 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 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 NavigationEntryImpl::FromNavigationEntry(GetActiveEntry()); | 285 NavigationEntryImpl::FromNavigationEntry(GetActiveEntry()); |
| 286 if (!active_entry) | 286 if (!active_entry) |
| 287 return; | 287 return; |
| 288 LoadURL(active_entry->GetURL(), | 288 LoadURL(active_entry->GetURL(), |
| 289 Referrer(), | 289 Referrer(), |
| 290 PAGE_TRANSITION_RELOAD, | 290 PAGE_TRANSITION_RELOAD, |
| 291 active_entry->extra_headers()); | 291 active_entry->extra_headers()); |
| 292 return; | 292 return; |
| 293 } | 293 } |
| 294 | 294 |
| 295 NavigationEntryImpl* entry = NULL; | 295 DiscardNonCommittedEntriesInternal(); |
| 296 int current_index = -1; | 296 int current_index = GetCurrentEntryIndex(); |
| 297 | 297 // If we are no where, then we can't reload. TODO(darin): We should add a |
| 298 // If we are reloading the initial navigation, just use the current | 298 // CanReload method. |
| 299 // pending entry. Otherwise look up the current entry. | 299 if (current_index == -1) { |
| 300 if (IsInitialNavigation() && pending_entry_) { | 300 return; |
| 301 entry = pending_entry_; | |
| 302 } else { | |
| 303 DiscardNonCommittedEntriesInternal(); | |
| 304 current_index = GetCurrentEntryIndex(); | |
| 305 if (current_index != -1) { | |
| 306 entry = NavigationEntryImpl::FromNavigationEntry( | |
| 307 GetEntryAtIndex(current_index)); | |
| 308 } | |
| 309 } | 301 } |
| 310 | 302 |
| 311 // If we are no where, then we can't reload. TODO(darin): We should add a | |
| 312 // CanReload method. | |
| 313 if (!entry) | |
| 314 return; | |
| 315 | |
| 316 if (g_check_for_repost && check_for_repost && | 303 if (g_check_for_repost && check_for_repost && |
| 317 entry->GetHasPostData()) { | 304 GetEntryAtIndex(current_index)->GetHasPostData()) { |
| 318 // The user is asking to reload a page with POST data. Prompt to make sure | 305 // 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 | 306 // they really want to do this. If they do, the dialog will call us back |
| 320 // with check_for_repost = false. | 307 // with check_for_repost = false. |
| 321 web_contents_->NotifyBeforeFormRepostWarningShow(); | 308 web_contents_->NotifyBeforeFormRepostWarningShow(); |
| 322 | 309 |
| 323 pending_reload_ = reload_type; | 310 pending_reload_ = reload_type; |
| 324 web_contents_->Activate(); | 311 web_contents_->Activate(); |
| 325 web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_); | 312 web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_); |
| 326 } else { | 313 } else { |
| 327 if (!IsInitialNavigation()) | 314 DiscardNonCommittedEntriesInternal(); |
| 328 DiscardNonCommittedEntriesInternal(); | 315 |
| 316 NavigationEntryImpl* entry = entries_[current_index].get(); |
| 317 SiteInstanceImpl* site_instance = entry->site_instance(); |
| 318 DCHECK(site_instance); |
| 329 | 319 |
| 330 // If we are reloading an entry that no longer belongs to the current | 320 // If we are reloading an entry that no longer belongs to the current |
| 331 // site instance (for example, refreshing a page for just installed app), | 321 // site instance (for example, refreshing a page for just installed app), |
| 332 // the reload must happen in a new process. | 322 // the reload must happen in a new process. |
| 333 // The new entry must have a new page_id and site instance, so it behaves | 323 // The new entry must have a new page_id and site instance, so it behaves |
| 334 // as new navigation (which happens to clear forward history). | 324 // as new navigation (which happens to clear forward history). |
| 335 // Tabs that are discarded due to low memory conditions may not have a site | 325 // Tabs that are discarded due to low memory conditions may not have a site |
| 336 // instance, and should not be treated as a cross-site reload. | 326 // instance, and should not be treated as a cross-site reload. |
| 337 SiteInstanceImpl* site_instance = entry->site_instance(); | |
| 338 if (site_instance && | 327 if (site_instance && |
| 339 site_instance->HasWrongProcessForURL(entry->GetURL())) { | 328 site_instance->HasWrongProcessForURL(entry->GetURL())) { |
| 340 // Create a navigation entry that resembles the current one, but do not | 329 // Create a navigation entry that resembles the current one, but do not |
| 341 // copy page id, site instance, content state, or timestamp. | 330 // copy page id, site instance, content state, or timestamp. |
| 342 NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry( | 331 NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry( |
| 343 CreateNavigationEntry( | 332 CreateNavigationEntry( |
| 344 entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(), | 333 entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(), |
| 345 false, entry->extra_headers(), browser_context_)); | 334 false, entry->extra_headers(), browser_context_)); |
| 346 | 335 |
| 347 // Mark the reload type as NO_RELOAD, so navigation will not be considered | 336 // Mark the reload type as NO_RELOAD, so navigation will not be considered |
| 348 // a reload in the renderer. | 337 // a reload in the renderer. |
| 349 reload_type = NavigationController::NO_RELOAD; | 338 reload_type = NavigationController::NO_RELOAD; |
| 350 | 339 |
| 351 nav_entry->set_should_replace_entry(true); | 340 nav_entry->set_should_replace_entry(true); |
| 352 pending_entry_ = nav_entry; | 341 pending_entry_ = nav_entry; |
| 353 } else { | 342 } else { |
| 354 pending_entry_ = entry; | |
| 355 pending_entry_index_ = current_index; | 343 pending_entry_index_ = current_index; |
| 356 | 344 |
| 357 // The title of the page being reloaded might have been removed in the | 345 // The title of the page being reloaded might have been removed in the |
| 358 // meanwhile, so we need to revert to the default title upon reload and | 346 // meanwhile, so we need to revert to the default title upon reload and |
| 359 // invalidate the previously cached title (SetTitle will do both). | 347 // invalidate the previously cached title (SetTitle will do both). |
| 360 // See Chromium issue 96041. | 348 // See Chromium issue 96041. |
| 361 pending_entry_->SetTitle(string16()); | 349 entries_[pending_entry_index_]->SetTitle(string16()); |
| 362 | 350 |
| 363 pending_entry_->SetTransitionType(PAGE_TRANSITION_RELOAD); | 351 entries_[pending_entry_index_]->SetTransitionType(PAGE_TRANSITION_RELOAD); |
| 364 } | 352 } |
| 365 | 353 |
| 366 NavigateToPendingEntry(reload_type); | 354 NavigateToPendingEntry(reload_type); |
| 367 } | 355 } |
| 368 } | 356 } |
| 369 | 357 |
| 370 void NavigationControllerImpl::CancelPendingReload() { | 358 void NavigationControllerImpl::CancelPendingReload() { |
| 371 DCHECK(pending_reload_ != NO_RELOAD); | 359 DCHECK(pending_reload_ != NO_RELOAD); |
| 372 pending_reload_ = NO_RELOAD; | 360 pending_reload_ = NO_RELOAD; |
| 373 } | 361 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 401 policy->IsDisabledScheme(entry->GetVirtualURL().scheme())) { | 389 policy->IsDisabledScheme(entry->GetVirtualURL().scheme())) { |
| 402 VLOG(1) << "URL not loaded because the scheme is blocked by policy: " | 390 VLOG(1) << "URL not loaded because the scheme is blocked by policy: " |
| 403 << entry->GetURL(); | 391 << entry->GetURL(); |
| 404 delete entry; | 392 delete entry; |
| 405 return; | 393 return; |
| 406 } | 394 } |
| 407 | 395 |
| 408 // When navigating to a new page, we don't know for sure if we will actually | 396 // When navigating to a new page, we don't know for sure if we will actually |
| 409 // end up leaving the current page. The new page load could for example | 397 // end up leaving the current page. The new page load could for example |
| 410 // result in a download or a 'no content' response (e.g., a mailto: URL). | 398 // result in a download or a 'no content' response (e.g., a mailto: URL). |
| 411 SetPendingEntry(entry); | |
| 412 NavigateToPendingEntry(NO_RELOAD); | |
| 413 } | |
| 414 | |
| 415 void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) { | |
| 416 DiscardNonCommittedEntriesInternal(); | 399 DiscardNonCommittedEntriesInternal(); |
| 417 pending_entry_ = entry; | 400 pending_entry_ = entry; |
| 418 NotificationService::current()->Notify( | 401 NotificationService::current()->Notify( |
| 419 NOTIFICATION_NAV_ENTRY_PENDING, | 402 NOTIFICATION_NAV_ENTRY_PENDING, |
| 420 Source<NavigationController>(this), | 403 Source<NavigationController>(this), |
| 421 Details<NavigationEntry>(entry)); | 404 Details<NavigationEntry>(entry)); |
| 405 NavigateToPendingEntry(NO_RELOAD); |
| 422 } | 406 } |
| 423 | 407 |
| 424 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { | 408 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { |
| 425 if (transient_entry_index_ != -1) | 409 if (transient_entry_index_ != -1) |
| 426 return entries_[transient_entry_index_].get(); | 410 return entries_[transient_entry_index_].get(); |
| 427 if (pending_entry_) | 411 if (pending_entry_) |
| 428 return pending_entry_; | 412 return pending_entry_; |
| 429 return GetLastCommittedEntry(); | 413 return GetLastCommittedEntry(); |
| 430 } | 414 } |
| 431 | 415 |
| 432 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { | 416 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { |
| 433 if (transient_entry_index_ != -1) | 417 if (transient_entry_index_ != -1) |
| 434 return entries_[transient_entry_index_].get(); | 418 return entries_[transient_entry_index_].get(); |
| 435 // The pending entry is safe to return for new (non-history), browser- | 419 // Only return the pending_entry for new (non-history), browser-initiated |
| 436 // initiated navigations. Most renderer-initiated navigations should not | 420 // navigations, in order to prevent URL spoof attacks. |
| 437 // show the pending entry, to prevent URL spoof attacks. | 421 // Ideally we would also show the pending entry's URL for new renderer- |
| 438 // | 422 // initiated navigations with no last committed entry (e.g., a link opening |
| 439 // We make an exception for renderer-initiated navigations in new tabs, as | 423 // in a new tab), but an attacker can insert content into the about:blank |
| 440 // long as no other page has tried to access the initial empty document in | 424 // page while the pending URL loads in that case. |
| 441 // the new tab. If another page modifies this blank page, a URL spoof is | 425 if (pending_entry_ && |
| 442 // possible, so we must stop showing the pending entry. | |
| 443 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( | |
| 444 web_contents_->GetRenderViewHost()); | |
| 445 bool safe_to_show_pending = | |
| 446 pending_entry_ && | |
| 447 // Require a new navigation. | |
| 448 pending_entry_->GetPageID() == -1 && | 426 pending_entry_->GetPageID() == -1 && |
| 449 // Require either browser-initiated or an unmodified new tab. | |
| 450 (!pending_entry_->is_renderer_initiated() || | |
| 451 (IsInitialNavigation() && | |
| 452 !GetLastCommittedEntry() && | |
| 453 !rvh->has_accessed_initial_document())); | |
| 454 | |
| 455 // Also allow showing the pending entry for history navigations in a new tab, | |
| 456 // such as Ctrl+Back. In this case, no existing page is visible and no one | |
| 457 // can script the new tab before it commits. | |
| 458 if (!safe_to_show_pending && | |
| 459 pending_entry_ && | |
| 460 pending_entry_->GetPageID() != -1 && | |
| 461 IsInitialNavigation() && | |
| 462 !pending_entry_->is_renderer_initiated()) | 427 !pending_entry_->is_renderer_initiated()) |
| 463 safe_to_show_pending = true; | |
| 464 | |
| 465 if (safe_to_show_pending) | |
| 466 return pending_entry_; | 428 return pending_entry_; |
| 467 return GetLastCommittedEntry(); | 429 return GetLastCommittedEntry(); |
| 468 } | 430 } |
| 469 | 431 |
| 470 int NavigationControllerImpl::GetCurrentEntryIndex() const { | 432 int NavigationControllerImpl::GetCurrentEntryIndex() const { |
| 471 if (transient_entry_index_ != -1) | 433 if (transient_entry_index_ != -1) |
| 472 return transient_entry_index_; | 434 return transient_entry_index_; |
| 473 if (pending_entry_index_ != -1) | 435 if (pending_entry_index_ != -1) |
| 474 return pending_entry_index_; | 436 return pending_entry_index_; |
| 475 return last_committed_entry_index_; | 437 return last_committed_entry_index_; |
| (...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1085 if (!PageTransitionIsMainFrame(params.transition)) { | 1047 if (!PageTransitionIsMainFrame(params.transition)) { |
| 1086 // All manual subframes would get new IDs and were handled above, so we | 1048 // All manual subframes would get new IDs and were handled above, so we |
| 1087 // know this is auto. Since the current page was found in the navigation | 1049 // know this is auto. Since the current page was found in the navigation |
| 1088 // entry list, we're guaranteed to have a last committed entry. | 1050 // entry list, we're guaranteed to have a last committed entry. |
| 1089 DCHECK(GetLastCommittedEntry()); | 1051 DCHECK(GetLastCommittedEntry()); |
| 1090 return NAVIGATION_TYPE_AUTO_SUBFRAME; | 1052 return NAVIGATION_TYPE_AUTO_SUBFRAME; |
| 1091 } | 1053 } |
| 1092 | 1054 |
| 1093 // Anything below here we know is a main frame navigation. | 1055 // Anything below here we know is a main frame navigation. |
| 1094 if (pending_entry_ && | 1056 if (pending_entry_ && |
| 1095 !pending_entry_->is_renderer_initiated() && | |
| 1096 existing_entry != pending_entry_ && | 1057 existing_entry != pending_entry_ && |
| 1097 pending_entry_->GetPageID() == -1 && | 1058 pending_entry_->GetPageID() == -1 && |
| 1098 existing_entry == GetLastCommittedEntry()) { | 1059 existing_entry == GetLastCommittedEntry()) { |
| 1099 // In this case, we have a pending entry for a URL but WebCore didn't do a | 1060 // In this case, we have a pending entry for a URL but WebCore didn't do a |
| 1100 // new navigation. This happens when you press enter in the URL bar to | 1061 // new navigation. This happens when you press enter in the URL bar to |
| 1101 // reload. We will create a pending entry, but WebKit will convert it to | 1062 // reload. We will create a pending entry, but WebKit will convert it to |
| 1102 // a reload since it's the same page and not create a new entry for it | 1063 // a reload since it's the same page and not create a new entry for it |
| 1103 // (the user doesn't want to have a new back/forward entry when they do | 1064 // (the user doesn't want to have a new back/forward entry when they do |
| 1104 // this). If this matches the last committed entry, we want to just ignore | 1065 // this). If this matches the last committed entry, we want to just ignore |
| 1105 // the pending entry and go back to where we were (the "existing entry"). | 1066 // 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... |
| 1863 const base::Callback<base::Time()>& get_timestamp_callback) { | 1824 const base::Callback<base::Time()>& get_timestamp_callback) { |
| 1864 get_timestamp_callback_ = get_timestamp_callback; | 1825 get_timestamp_callback_ = get_timestamp_callback; |
| 1865 } | 1826 } |
| 1866 | 1827 |
| 1867 void NavigationControllerImpl::SetTakeScreenshotCallbackForTest( | 1828 void NavigationControllerImpl::SetTakeScreenshotCallbackForTest( |
| 1868 const base::Callback<void(RenderViewHost*)>& take_screenshot_callback) { | 1829 const base::Callback<void(RenderViewHost*)>& take_screenshot_callback) { |
| 1869 take_screenshot_callback_ = take_screenshot_callback; | 1830 take_screenshot_callback_ = take_screenshot_callback; |
| 1870 } | 1831 } |
| 1871 | 1832 |
| 1872 } // namespace content | 1833 } // namespace content |
| OLD | NEW |