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

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

Issue 12541018: Allow showing pending URL for new tab navigations, but only if safe. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix title in new and cloned tabs. Created 7 years, 9 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 270 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698