Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(704)

Side by Side Diff: content/browser/web_contents/navigation_controller_impl.cc

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

Powered by Google App Engine
This is Rietveld 408576698