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

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

Issue 14283005: Allow showing pending URL for new tab navigations, but only if safe. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 7 years, 6 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/logging.h" 8 #include "base/logging.h"
9 #include "base/string_number_conversions.h" // Temporary 9 #include "base/string_number_conversions.h" // Temporary
10 #include "base/string_util.h" 10 #include "base/string_util.h"
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 NavigationEntryImpl::FromNavigationEntry(GetActiveEntry()); 275 NavigationEntryImpl::FromNavigationEntry(GetActiveEntry());
276 if (!active_entry) 276 if (!active_entry)
277 return; 277 return;
278 LoadURL(active_entry->GetURL(), 278 LoadURL(active_entry->GetURL(),
279 Referrer(), 279 Referrer(),
280 PAGE_TRANSITION_RELOAD, 280 PAGE_TRANSITION_RELOAD,
281 active_entry->extra_headers()); 281 active_entry->extra_headers());
282 return; 282 return;
283 } 283 }
284 284
285 DiscardNonCommittedEntriesInternal(); 285 NavigationEntryImpl* entry = NULL;
286 int current_index = GetCurrentEntryIndex(); 286 int current_index = -1;
287
288 // If we are reloading the initial navigation, just use the current
289 // pending entry. Otherwise look up the current entry.
290 if (IsInitialNavigation() && pending_entry_) {
291 entry = pending_entry_;
292 // The pending entry might be in entries_ (e.g., after a Clone), so we
293 // should also update the current_index.
294 current_index = pending_entry_index_;
295 } else {
296 DiscardNonCommittedEntriesInternal();
297 current_index = GetCurrentEntryIndex();
298 if (current_index != -1) {
299 entry = NavigationEntryImpl::FromNavigationEntry(
300 GetEntryAtIndex(current_index));
301 }
302 }
303
287 // If we are no where, then we can't reload. TODO(darin): We should add a 304 // If we are no where, then we can't reload. TODO(darin): We should add a
288 // CanReload method. 305 // CanReload method.
289 if (current_index == -1) { 306 if (!entry)
290 return; 307 return;
291 }
292 308
293 if (g_check_for_repost && check_for_repost && 309 if (g_check_for_repost && check_for_repost &&
294 GetEntryAtIndex(current_index)->GetHasPostData()) { 310 entry->GetHasPostData()) {
295 // The user is asking to reload a page with POST data. Prompt to make sure 311 // The user is asking to reload a page with POST data. Prompt to make sure
296 // they really want to do this. If they do, the dialog will call us back 312 // they really want to do this. If they do, the dialog will call us back
297 // with check_for_repost = false. 313 // with check_for_repost = false.
298 web_contents_->NotifyBeforeFormRepostWarningShow(); 314 web_contents_->NotifyBeforeFormRepostWarningShow();
299 315
300 pending_reload_ = reload_type; 316 pending_reload_ = reload_type;
301 web_contents_->Activate(); 317 web_contents_->Activate();
302 web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_); 318 web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_);
303 } else { 319 } else {
304 DiscardNonCommittedEntriesInternal(); 320 if (!IsInitialNavigation())
305 321 DiscardNonCommittedEntriesInternal();
306 NavigationEntryImpl* entry = entries_[current_index].get();
307 SiteInstanceImpl* site_instance = entry->site_instance();
308 DCHECK(site_instance);
309 322
310 // If we are reloading an entry that no longer belongs to the current 323 // If we are reloading an entry that no longer belongs to the current
311 // site instance (for example, refreshing a page for just installed app), 324 // site instance (for example, refreshing a page for just installed app),
312 // the reload must happen in a new process. 325 // the reload must happen in a new process.
313 // The new entry must have a new page_id and site instance, so it behaves 326 // The new entry must have a new page_id and site instance, so it behaves
314 // as new navigation (which happens to clear forward history). 327 // as new navigation (which happens to clear forward history).
315 // Tabs that are discarded due to low memory conditions may not have a site 328 // Tabs that are discarded due to low memory conditions may not have a site
316 // instance, and should not be treated as a cross-site reload. 329 // instance, and should not be treated as a cross-site reload.
330 SiteInstanceImpl* site_instance = entry->site_instance();
317 if (site_instance && 331 if (site_instance &&
318 site_instance->HasWrongProcessForURL(entry->GetURL())) { 332 site_instance->HasWrongProcessForURL(entry->GetURL())) {
319 // Create a navigation entry that resembles the current one, but do not 333 // Create a navigation entry that resembles the current one, but do not
320 // copy page id, site instance, content state, or timestamp. 334 // copy page id, site instance, content state, or timestamp.
321 NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry( 335 NavigationEntryImpl* nav_entry = NavigationEntryImpl::FromNavigationEntry(
322 CreateNavigationEntry( 336 CreateNavigationEntry(
323 entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(), 337 entry->GetURL(), entry->GetReferrer(), entry->GetTransitionType(),
324 false, entry->extra_headers(), browser_context_)); 338 false, entry->extra_headers(), browser_context_));
325 339
326 // Mark the reload type as NO_RELOAD, so navigation will not be considered 340 // Mark the reload type as NO_RELOAD, so navigation will not be considered
327 // a reload in the renderer. 341 // a reload in the renderer.
328 reload_type = NavigationController::NO_RELOAD; 342 reload_type = NavigationController::NO_RELOAD;
329 343
330 nav_entry->set_should_replace_entry(true); 344 nav_entry->set_should_replace_entry(true);
331 pending_entry_ = nav_entry; 345 pending_entry_ = nav_entry;
332 } else { 346 } else {
347 pending_entry_ = entry;
333 pending_entry_index_ = current_index; 348 pending_entry_index_ = current_index;
334 349
335 // The title of the page being reloaded might have been removed in the 350 // The title of the page being reloaded might have been removed in the
336 // meanwhile, so we need to revert to the default title upon reload and 351 // meanwhile, so we need to revert to the default title upon reload and
337 // invalidate the previously cached title (SetTitle will do both). 352 // invalidate the previously cached title (SetTitle will do both).
338 // See Chromium issue 96041. 353 // See Chromium issue 96041.
339 entries_[pending_entry_index_]->SetTitle(string16()); 354 pending_entry_->SetTitle(string16());
340 355
341 entries_[pending_entry_index_]->SetTransitionType(PAGE_TRANSITION_RELOAD); 356 pending_entry_->SetTransitionType(PAGE_TRANSITION_RELOAD);
342 } 357 }
343 358
344 NavigateToPendingEntry(reload_type); 359 NavigateToPendingEntry(reload_type);
345 } 360 }
346 } 361 }
347 362
348 void NavigationControllerImpl::CancelPendingReload() { 363 void NavigationControllerImpl::CancelPendingReload() {
349 DCHECK(pending_reload_ != NO_RELOAD); 364 DCHECK(pending_reload_ != NO_RELOAD);
350 pending_reload_ = NO_RELOAD; 365 pending_reload_ = NO_RELOAD;
351 } 366 }
(...skipping 27 matching lines...) Expand all
379 policy->IsDisabledScheme(entry->GetVirtualURL().scheme())) { 394 policy->IsDisabledScheme(entry->GetVirtualURL().scheme())) {
380 VLOG(1) << "URL not loaded because the scheme is blocked by policy: " 395 VLOG(1) << "URL not loaded because the scheme is blocked by policy: "
381 << entry->GetURL(); 396 << entry->GetURL();
382 delete entry; 397 delete entry;
383 return; 398 return;
384 } 399 }
385 400
386 // When navigating to a new page, we don't know for sure if we will actually 401 // When navigating to a new page, we don't know for sure if we will actually
387 // end up leaving the current page. The new page load could for example 402 // end up leaving the current page. The new page load could for example
388 // result in a download or a 'no content' response (e.g., a mailto: URL). 403 // result in a download or a 'no content' response (e.g., a mailto: URL).
404 SetPendingEntry(entry);
405 NavigateToPendingEntry(NO_RELOAD);
406 }
407
408 void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) {
389 DiscardNonCommittedEntriesInternal(); 409 DiscardNonCommittedEntriesInternal();
390 pending_entry_ = entry; 410 pending_entry_ = entry;
391 NotificationService::current()->Notify( 411 NotificationService::current()->Notify(
392 NOTIFICATION_NAV_ENTRY_PENDING, 412 NOTIFICATION_NAV_ENTRY_PENDING,
393 Source<NavigationController>(this), 413 Source<NavigationController>(this),
394 Details<NavigationEntry>(entry)); 414 Details<NavigationEntry>(entry));
395 NavigateToPendingEntry(NO_RELOAD);
396 } 415 }
397 416
398 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { 417 NavigationEntry* NavigationControllerImpl::GetActiveEntry() const {
399 if (transient_entry_index_ != -1) 418 if (transient_entry_index_ != -1)
400 return entries_[transient_entry_index_].get(); 419 return entries_[transient_entry_index_].get();
401 if (pending_entry_) 420 if (pending_entry_)
402 return pending_entry_; 421 return pending_entry_;
403 return GetLastCommittedEntry(); 422 return GetLastCommittedEntry();
404 } 423 }
405 424
406 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { 425 NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const {
407 if (transient_entry_index_ != -1) 426 if (transient_entry_index_ != -1)
408 return entries_[transient_entry_index_].get(); 427 return entries_[transient_entry_index_].get();
409 // Only return the pending_entry for new (non-history), browser-initiated 428 // The pending entry is safe to return for new (non-history), browser-
410 // navigations, in order to prevent URL spoof attacks. 429 // initiated navigations. Most renderer-initiated navigations should not
411 // Ideally we would also show the pending entry's URL for new renderer- 430 // show the pending entry, to prevent URL spoof attacks.
412 // initiated navigations with no last committed entry (e.g., a link opening 431 //
413 // in a new tab), but an attacker can insert content into the about:blank 432 // We make an exception for renderer-initiated navigations in new tabs, as
414 // page while the pending URL loads in that case. 433 // long as no other page has tried to access the initial empty document in
415 if (pending_entry_ && 434 // the new tab. If another page modifies this blank page, a URL spoof is
435 // possible, so we must stop showing the pending entry.
436 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
437 web_contents_->GetRenderViewHost());
438 bool safe_to_show_pending =
439 pending_entry_ &&
440 // Require a new navigation.
416 pending_entry_->GetPageID() == -1 && 441 pending_entry_->GetPageID() == -1 &&
442 // Require either browser-initiated or an unmodified new tab.
443 (!pending_entry_->is_renderer_initiated() ||
444 (IsInitialNavigation() &&
445 !GetLastCommittedEntry() &&
446 !rvh->has_accessed_initial_document()));
447
448 // Also allow showing the pending entry for history navigations in a new tab,
449 // such as Ctrl+Back. In this case, no existing page is visible and no one
450 // can script the new tab before it commits.
451 if (!safe_to_show_pending &&
452 pending_entry_ &&
453 pending_entry_->GetPageID() != -1 &&
454 IsInitialNavigation() &&
417 !pending_entry_->is_renderer_initiated()) 455 !pending_entry_->is_renderer_initiated())
456 safe_to_show_pending = true;
457
458 if (safe_to_show_pending)
418 return pending_entry_; 459 return pending_entry_;
419 return GetLastCommittedEntry(); 460 return GetLastCommittedEntry();
420 } 461 }
421 462
422 int NavigationControllerImpl::GetCurrentEntryIndex() const { 463 int NavigationControllerImpl::GetCurrentEntryIndex() const {
423 if (transient_entry_index_ != -1) 464 if (transient_entry_index_ != -1)
424 return transient_entry_index_; 465 return transient_entry_index_;
425 if (pending_entry_index_ != -1) 466 if (pending_entry_index_ != -1)
426 return pending_entry_index_; 467 return pending_entry_index_;
427 return last_committed_entry_index_; 468 return last_committed_entry_index_;
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after
898 if (!PageTransitionIsMainFrame(params.transition)) { 939 if (!PageTransitionIsMainFrame(params.transition)) {
899 // All manual subframes would get new IDs and were handled above, so we 940 // All manual subframes would get new IDs and were handled above, so we
900 // know this is auto. Since the current page was found in the navigation 941 // know this is auto. Since the current page was found in the navigation
901 // entry list, we're guaranteed to have a last committed entry. 942 // entry list, we're guaranteed to have a last committed entry.
902 DCHECK(GetLastCommittedEntry()); 943 DCHECK(GetLastCommittedEntry());
903 return NAVIGATION_TYPE_AUTO_SUBFRAME; 944 return NAVIGATION_TYPE_AUTO_SUBFRAME;
904 } 945 }
905 946
906 // Anything below here we know is a main frame navigation. 947 // Anything below here we know is a main frame navigation.
907 if (pending_entry_ && 948 if (pending_entry_ &&
949 !pending_entry_->is_renderer_initiated() &&
908 existing_entry != pending_entry_ && 950 existing_entry != pending_entry_ &&
909 pending_entry_->GetPageID() == -1 && 951 pending_entry_->GetPageID() == -1 &&
910 existing_entry == GetLastCommittedEntry()) { 952 existing_entry == GetLastCommittedEntry()) {
911 // In this case, we have a pending entry for a URL but WebCore didn't do a 953 // In this case, we have a pending entry for a URL but WebCore didn't do a
912 // new navigation. This happens when you press enter in the URL bar to 954 // new navigation. This happens when you press enter in the URL bar to
913 // reload. We will create a pending entry, but WebKit will convert it to 955 // reload. We will create a pending entry, but WebKit will convert it to
914 // a reload since it's the same page and not create a new entry for it 956 // a reload since it's the same page and not create a new entry for it
915 // (the user doesn't want to have a new back/forward entry when they do 957 // (the user doesn't want to have a new back/forward entry when they do
916 // this). If this matches the last committed entry, we want to just ignore 958 // this). If this matches the last committed entry, we want to just ignore
917 // the pending entry and go back to where we were (the "existing entry"). 959 // the pending entry and go back to where we were (the "existing entry").
(...skipping 733 matching lines...) Expand 10 before | Expand all | Expand 10 after
1651 } 1693 }
1652 } 1694 }
1653 } 1695 }
1654 1696
1655 void NavigationControllerImpl::SetGetTimestampCallbackForTest( 1697 void NavigationControllerImpl::SetGetTimestampCallbackForTest(
1656 const base::Callback<base::Time()>& get_timestamp_callback) { 1698 const base::Callback<base::Time()>& get_timestamp_callback) {
1657 get_timestamp_callback_ = get_timestamp_callback; 1699 get_timestamp_callback_ = get_timestamp_callback;
1658 } 1700 }
1659 1701
1660 } // namespace content 1702 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698