browser_navigator.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/browser_navigator.h" 6 7#include <algorithm> 8 9#include "base/command_line.h" 10#include "base/stringprintf.h" 11#include "base/utf_string_conversions.h" 12#include "chrome/browser/browser_about_handler.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/extensions/tab_helper.h" 15#include "chrome/browser/google/google_url_tracker.h" 16#include "chrome/browser/prefs/incognito_mode_prefs.h" 17#include "chrome/browser/prefs/pref_service.h" 18#include "chrome/browser/prerender/prerender_manager.h" 19#include "chrome/browser/prerender/prerender_manager_factory.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/tab_contents/tab_util.h" 22#include "chrome/browser/ui/browser.h" 23#include "chrome/browser/ui/browser_finder.h" 24#include "chrome/browser/ui/browser_tabstrip.h" 25#include "chrome/browser/ui/browser_window.h" 26#include "chrome/browser/ui/omnibox/location_bar.h" 27#include "chrome/browser/ui/singleton_tabs.h" 28#include "chrome/browser/ui/status_bubble.h" 29#include "chrome/browser/ui/tab_contents/tab_contents.h" 30#include "chrome/browser/ui/tabs/tab_strip_model.h" 31#include "chrome/browser/web_applications/web_app.h" 32#include "chrome/common/chrome_notification_types.h" 33#include "chrome/common/extensions/extension.h" 34#include "chrome/common/pref_names.h" 35#include "chrome/common/url_constants.h" 36#include "content/public/browser/browser_url_handler.h" 37#include "content/public/browser/notification_service.h" 38#include "content/public/browser/render_view_host.h" 39#include "content/public/browser/web_contents.h" 40 41using content::GlobalRequestID; 42using content::WebContents; 43 44namespace { 45 46// Returns true if the specified Browser can open tabs. Not all Browsers support 47// multiple tabs, such as app frames and popups. This function returns false for 48// those types of Browser. 49bool WindowCanOpenTabs(Browser* browser) { 50 return browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP) || 51 browser->tab_strip_model()->empty(); 52} 53 54// Finds an existing Browser compatible with |profile|, making a new one if no 55// such Browser is located. 56Browser* GetOrCreateBrowser(Profile* profile, 57 chrome::HostDesktopType host_desktop_type) { 58 Browser* browser = 59 browser::FindTabbedBrowser(profile, false, host_desktop_type); 60 return browser ? browser : new Browser(Browser::CreateParams(profile)); 61} 62 63// Change some of the navigation parameters based on the particular URL. 64// Currently this applies to some chrome:// pages which we always want to open 65// in a non-incognito window. Note that even though a ChromeOS guest session is 66// technically an incognito window, these URLs are allowed. 67// Returns true on success. Otherwise, if changing params leads the browser into 68// an erroneous state, returns false. 69bool AdjustNavigateParamsForURL(chrome::NavigateParams* params) { 70 if (params->target_contents != NULL || 71 chrome::IsURLAllowedInIncognito(params->url, 72 params->initiating_profile) || 73 Profile::IsGuestSession()) { 74 return true; 75 } 76 77 Profile* profile = params->initiating_profile; 78 79 if (profile->IsOffTheRecord() || params->disposition == OFF_THE_RECORD) { 80 profile = profile->GetOriginalProfile(); 81 82 // If incognito is forced, we punt. 83 PrefService* prefs = profile->GetPrefs(); 84 if (prefs && IncognitoModePrefs::GetAvailability(prefs) == 85 IncognitoModePrefs::FORCED) { 86 return false; 87 } 88 89 params->disposition = SINGLETON_TAB; 90 params->browser = 91 browser::FindOrCreateTabbedBrowser(profile, params->host_desktop_type); 92 params->window_action = chrome::NavigateParams::SHOW_WINDOW; 93 } 94 95 return true; 96} 97 98// Returns a Browser that can host the navigation or tab addition specified in 99// |params|. This might just return the same Browser specified in |params|, or 100// some other if that Browser is deemed incompatible. 101Browser* GetBrowserForDisposition(chrome::NavigateParams* params) { 102 // If no source TabContents was specified, we use the selected one from 103 // the target browser. This must happen first, before 104 // GetBrowserForDisposition() has a chance to replace |params->browser| with 105 // another one. 106 if (!params->source_contents && params->browser) 107 params->source_contents = chrome::GetActiveTabContents(params->browser); 108 109 Profile* profile = params->initiating_profile; 110 111 switch (params->disposition) { 112 case CURRENT_TAB: 113 if (params->browser) 114 return params->browser; 115 // Find a compatible window and re-execute this command in it. Otherwise 116 // re-run with NEW_WINDOW. 117 return GetOrCreateBrowser(profile, params->host_desktop_type); 118 case SINGLETON_TAB: 119 case NEW_FOREGROUND_TAB: 120 case NEW_BACKGROUND_TAB: 121 // See if we can open the tab in the window this navigator is bound to. 122 if (params->browser && WindowCanOpenTabs(params->browser)) 123 return params->browser; 124 // Find a compatible window and re-execute this command in it. Otherwise 125 // re-run with NEW_WINDOW. 126 return GetOrCreateBrowser(profile, params->host_desktop_type); 127 case NEW_POPUP: { 128 // Make a new popup window. 129 // Coerce app-style if |source| represents an app. 130 std::string app_name; 131 if (!params->extension_app_id.empty()) { 132 app_name = web_app::GenerateApplicationNameFromExtensionId( 133 params->extension_app_id); 134 } else if (!params->browser->app_name().empty()) { 135 app_name = params->browser->app_name(); 136 } else if (params->source_contents) { 137 extensions::TabHelper* extensions_tab_helper = 138 extensions::TabHelper::FromWebContents( 139 params->source_contents->web_contents()); 140 if (extensions_tab_helper->is_app()) { 141 app_name = web_app::GenerateApplicationNameFromExtensionId( 142 extensions_tab_helper->extension_app()->id()); 143 } 144 } 145 if (app_name.empty()) { 146 Browser::CreateParams browser_params(Browser::TYPE_POPUP, profile); 147 browser_params.initial_bounds = params->window_bounds; 148 return new Browser(browser_params); 149 } 150 151 return new Browser(Browser::CreateParams::CreateForApp( 152 Browser::TYPE_POPUP, app_name, params->window_bounds, profile)); 153 } 154 case NEW_WINDOW: { 155 // Make a new normal browser window. 156 return new Browser(Browser::CreateParams(profile)); 157 } 158 case OFF_THE_RECORD: 159 // Make or find an incognito window. 160 return GetOrCreateBrowser(profile->GetOffTheRecordProfile(), 161 params->host_desktop_type); 162 // The following types all result in no navigation. 163 case SUPPRESS_OPEN: 164 case SAVE_TO_DISK: 165 case IGNORE_ACTION: 166 return NULL; 167 default: 168 NOTREACHED(); 169 } 170 return NULL; 171} 172 173// Fix disposition and other parameter values depending on prevailing 174// conditions. 175void NormalizeDisposition(chrome::NavigateParams* params) { 176 // Calculate the WindowOpenDisposition if necessary. 177 if (params->browser->tab_strip_model()->empty() && 178 (params->disposition == NEW_BACKGROUND_TAB || 179 params->disposition == CURRENT_TAB || 180 params->disposition == SINGLETON_TAB)) { 181 params->disposition = NEW_FOREGROUND_TAB; 182 } 183 if (params->browser->profile()->IsOffTheRecord() && 184 params->disposition == OFF_THE_RECORD) { 185 params->disposition = NEW_FOREGROUND_TAB; 186 } 187 if (!params->source_contents && params->disposition == CURRENT_TAB) 188 params->disposition = NEW_FOREGROUND_TAB; 189 190 switch (params->disposition) { 191 case NEW_BACKGROUND_TAB: 192 // Disposition trumps add types. ADD_ACTIVE is a default, so we need to 193 // remove it if disposition implies the tab is going to open in the 194 // background. 195 params->tabstrip_add_types &= ~TabStripModel::ADD_ACTIVE; 196 break; 197 198 case NEW_WINDOW: 199 case NEW_POPUP: 200 // Code that wants to open a new window typically expects it to be shown 201 // automatically. 202 if (params->window_action == chrome::NavigateParams::NO_ACTION) 203 params->window_action = chrome::NavigateParams::SHOW_WINDOW; 204 // Fall-through. 205 case NEW_FOREGROUND_TAB: 206 case SINGLETON_TAB: 207 params->tabstrip_add_types |= TabStripModel::ADD_ACTIVE; 208 break; 209 210 default: 211 break; 212 } 213} 214 215// Obtain the profile used by the code that originated the Navigate() request. 216Profile* GetSourceProfile(chrome::NavigateParams* params) { 217 if (params->source_contents) 218 return params->source_contents->profile(); 219 220 return params->initiating_profile; 221} 222 223void LoadURLInContents(WebContents* target_contents, 224 const GURL& url, 225 chrome::NavigateParams* params) { 226 content::NavigationController::LoadURLParams load_url_params(url); 227 load_url_params.referrer = params->referrer; 228 load_url_params.transition_type = params->transition; 229 load_url_params.extra_headers = params->extra_headers; 230 231 if (params->transferred_global_request_id != GlobalRequestID()) { 232 load_url_params.is_renderer_initiated = params->is_renderer_initiated; 233 load_url_params.transferred_global_request_id = 234 params->transferred_global_request_id; 235 } else if (params->is_renderer_initiated) { 236 load_url_params.is_renderer_initiated = true; 237 } 238 target_contents->GetController().LoadURLWithParams(load_url_params); 239} 240 241// This class makes sure the Browser object held in |params| is made visible 242// by the time it goes out of scope, provided |params| wants it to be shown. 243class ScopedBrowserDisplayer { 244 public: 245 explicit ScopedBrowserDisplayer(chrome::NavigateParams* params) 246 : params_(params) { 247 } 248 ~ScopedBrowserDisplayer() { 249 if (params_->window_action == chrome::NavigateParams::SHOW_WINDOW_INACTIVE) 250 params_->browser->window()->ShowInactive(); 251 else if (params_->window_action == chrome::NavigateParams::SHOW_WINDOW) 252 params_->browser->window()->Show(); 253 } 254 private: 255 chrome::NavigateParams* params_; 256 DISALLOW_COPY_AND_ASSIGN(ScopedBrowserDisplayer); 257}; 258 259// This class manages the lifetime of a TabContents created by the 260// Navigate() function. When Navigate() creates a TabContents for a URL, 261// an instance of this class takes ownership of it via TakeOwnership() until the 262// TabContents is added to a tab strip at which time ownership is 263// relinquished via ReleaseOwnership(). If this object goes out of scope without 264// being added to a tab strip, the created TabContents is deleted to 265// avoid a leak and the params->target_contents field is set to NULL. 266class ScopedTargetContentsOwner { 267 public: 268 explicit ScopedTargetContentsOwner(chrome::NavigateParams* params) 269 : params_(params) { 270 } 271 ~ScopedTargetContentsOwner() { 272 if (target_contents_owner_.get()) 273 params_->target_contents = NULL; 274 } 275 276 // Assumes ownership of |params_|' target_contents until ReleaseOwnership 277 // is called. 278 void TakeOwnership() { 279 target_contents_owner_.reset(params_->target_contents); 280 } 281 282 // Relinquishes ownership of |params_|' target_contents. 283 TabContents* ReleaseOwnership() { 284 return target_contents_owner_.release(); 285 } 286 287 private: 288 chrome::NavigateParams* params_; 289 scoped_ptr<TabContents> target_contents_owner_; 290 DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner); 291}; 292 293// If a prerendered page exists for |url|, replace the page at |target_contents| 294// with it. 295bool SwapInPrerender(TabContents* target_contents, const GURL& url) { 296 prerender::PrerenderManager* prerender_manager = 297 prerender::PrerenderManagerFactory::GetForProfile( 298 target_contents->profile()); 299 WebContents* web_contents = target_contents->web_contents(); 300 return prerender_manager && 301 prerender_manager->MaybeUsePrerenderedPage(web_contents, url); 302} 303 304} // namespace 305 306namespace chrome { 307 308NavigateParams::NavigateParams(Browser* a_browser, 309 const GURL& a_url, 310 content::PageTransition a_transition) 311 : url(a_url), 312 target_contents(NULL), 313 source_contents(NULL), 314 disposition(CURRENT_TAB), 315 transition(a_transition), 316 is_renderer_initiated(false), 317 tabstrip_index(-1), 318 tabstrip_add_types(TabStripModel::ADD_ACTIVE), 319 window_action(NO_ACTION), 320 user_gesture(true), 321 path_behavior(RESPECT), 322 ref_behavior(IGNORE_REF), 323 browser(a_browser), 324 initiating_profile(NULL) { 325 if (a_browser) 326 host_desktop_type = a_browser->host_desktop_type(); 327 else 328 host_desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE; 329 } 330 331NavigateParams::NavigateParams(Browser* a_browser, 332 TabContents* a_target_contents) 333 : target_contents(a_target_contents), 334 source_contents(NULL), 335 disposition(CURRENT_TAB), 336 transition(content::PAGE_TRANSITION_LINK), 337 is_renderer_initiated(false), 338 tabstrip_index(-1), 339 tabstrip_add_types(TabStripModel::ADD_ACTIVE), 340 window_action(NO_ACTION), 341 user_gesture(true), 342 path_behavior(RESPECT), 343 ref_behavior(IGNORE_REF), 344 browser(a_browser), 345 initiating_profile(NULL) { 346 if (a_browser) 347 host_desktop_type = a_browser->host_desktop_type(); 348 else 349 host_desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE; 350 } 351 352NavigateParams::NavigateParams(Profile* a_profile, 353 const GURL& a_url, 354 content::PageTransition a_transition) 355 : url(a_url), 356 target_contents(NULL), 357 source_contents(NULL), 358 disposition(NEW_FOREGROUND_TAB), 359 transition(a_transition), 360 is_renderer_initiated(false), 361 tabstrip_index(-1), 362 tabstrip_add_types(TabStripModel::ADD_ACTIVE), 363 window_action(SHOW_WINDOW), 364 user_gesture(true), 365 path_behavior(RESPECT), 366 ref_behavior(IGNORE_REF), 367 browser(NULL), 368 initiating_profile(a_profile), 369 host_desktop_type(chrome::HOST_DESKTOP_TYPE_NATIVE) {} 370 371NavigateParams::~NavigateParams() {} 372 373void Navigate(NavigateParams* params) { 374 Browser* source_browser = params->browser; 375 if (source_browser) 376 params->initiating_profile = source_browser->profile(); 377 DCHECK(params->initiating_profile); 378 379 if (!AdjustNavigateParamsForURL(params)) 380 return; 381 382 ExtensionService* service = params->initiating_profile->GetExtensionService(); 383 if (service) 384 service->ShouldBlockUrlInBrowserTab(¶ms->url); 385 386 // The browser window may want to adjust the disposition. 387 if (params->disposition == NEW_POPUP && 388 source_browser && 389 source_browser->window()) { 390 params->disposition = 391 source_browser->window()->GetDispositionForPopupBounds( 392 params->window_bounds); 393 } 394 395 params->browser = GetBrowserForDisposition(params); 396 397 if (!params->browser) 398 return; 399 400 // Navigate() must not return early after this point. 401 402 if (GetSourceProfile(params) != params->browser->profile()) { 403 // A tab is being opened from a link from a different profile, we must reset 404 // source information that may cause state to be shared. 405 params->source_contents = NULL; 406 params->referrer = content::Referrer(); 407 } 408 409 // Make sure the Browser is shown if params call for it. 410 ScopedBrowserDisplayer displayer(params); 411 412 // Makes sure any TabContents created by this function is destroyed if 413 // not properly added to a tab strip. 414 ScopedTargetContentsOwner target_contents_owner(params); 415 416 // Some dispositions need coercion to base types. 417 NormalizeDisposition(params); 418 419 // If a new window has been created, it needs to be displayed. 420 if (params->window_action == NavigateParams::NO_ACTION && 421 source_browser != params->browser && 422 params->browser->tab_strip_model()->empty()) { 423 params->window_action = NavigateParams::SHOW_WINDOW; 424 } 425 426 // If we create a popup window from a non user-gesture, don't activate it. 427 if (params->window_action == NavigateParams::SHOW_WINDOW && 428 params->disposition == NEW_POPUP && 429 params->user_gesture == false) { 430 params->window_action = NavigateParams::SHOW_WINDOW_INACTIVE; 431 } 432 433 // Determine if the navigation was user initiated. If it was, we need to 434 // inform the target TabContents, and we may need to update the UI. 435 content::PageTransition base_transition = 436 content::PageTransitionStripQualifier(params->transition); 437 bool user_initiated = 438 params->transition & content::PAGE_TRANSITION_FROM_ADDRESS_BAR || 439 base_transition == content::PAGE_TRANSITION_TYPED || 440 base_transition == content::PAGE_TRANSITION_AUTO_BOOKMARK || 441 base_transition == content::PAGE_TRANSITION_GENERATED || 442 base_transition == content::PAGE_TRANSITION_AUTO_TOPLEVEL || 443 base_transition == content::PAGE_TRANSITION_RELOAD || 444 base_transition == content::PAGE_TRANSITION_KEYWORD; 445 446 // Check if this is a singleton tab that already exists 447 int singleton_index = chrome::GetIndexOfSingletonTab(params); 448 449 // If no target TabContents was specified, we need to construct one if 450 // we are supposed to target a new tab; unless it's a singleton that already 451 // exists. 452 if (!params->target_contents && singleton_index < 0) { 453 GURL url; 454 if (params->url.is_empty()) { 455 url = params->browser->profile()->GetHomePage(); 456 params->transition = content::PageTransitionFromInt( 457 params->transition | content::PAGE_TRANSITION_HOME_PAGE); 458 } else { 459 url = params->url; 460 } 461 462 if (params->disposition != CURRENT_TAB) { 463 WebContents* source_contents = params->source_contents ? 464 params->source_contents->web_contents() : NULL; 465 params->target_contents = 466 chrome::TabContentsFactory( 467 params->browser->profile(), 468 tab_util::GetSiteInstanceForNewTab( 469 params->browser->profile(), url), 470 MSG_ROUTING_NONE, 471 source_contents); 472 // This function takes ownership of |params->target_contents| until it 473 // is added to a TabStripModel. 474 target_contents_owner.TakeOwnership(); 475 extensions::TabHelper::FromWebContents( 476 params->target_contents->web_contents())-> 477 SetExtensionAppById(params->extension_app_id); 478 // TODO(sky): figure out why this is needed. Without it we seem to get 479 // failures in startup tests. 480 // By default, content believes it is not hidden. When adding contents 481 // in the background, tell it that it's hidden. 482 if ((params->tabstrip_add_types & TabStripModel::ADD_ACTIVE) == 0) { 483 // TabStripModel::AddTabContents invokes WasHidden if not foreground. 484 params->target_contents->web_contents()->WasHidden(); 485 } 486 } else { 487 // ... otherwise if we're loading in the current tab, the target is the 488 // same as the source. 489 params->target_contents = params->source_contents; 490 DCHECK(params->target_contents); 491 } 492 493 if (user_initiated) 494 params->target_contents->web_contents()->UserGestureDone(); 495 496 if (SwapInPrerender(params->target_contents, url)) 497 return; 498 499 // Try to handle non-navigational URLs that popup dialogs and such, these 500 // should not actually navigate. 501 if (!HandleNonNavigationAboutURL(url)) { 502 // Perform the actual navigation, tracking whether it came from the 503 // renderer. 504 505 LoadURLInContents(params->target_contents->web_contents(), url, params); 506 } 507 } else { 508 // |target_contents| was specified non-NULL, and so we assume it has already 509 // been navigated appropriately. We need to do nothing more other than 510 // add it to the appropriate tabstrip. 511 } 512 513 // If the user navigated from the omnibox, and the selected tab is going to 514 // lose focus, then make sure the focus for the source tab goes away from the 515 // omnibox. 516 if (params->source_contents && 517 (params->disposition == NEW_FOREGROUND_TAB || 518 params->disposition == NEW_WINDOW) && 519 (params->tabstrip_add_types & TabStripModel::ADD_INHERIT_OPENER)) 520 params->source_contents->web_contents()->Focus(); 521 522 if (params->source_contents == params->target_contents) { 523 // The navigation occurred in the source tab. 524 params->browser->UpdateUIForNavigationInTab( 525 params->target_contents, 526 params->transition, 527 user_initiated); 528 } else if (singleton_index == -1) { 529 // If some non-default value is set for the index, we should tell the 530 // TabStripModel to respect it. 531 if (params->tabstrip_index != -1) 532 params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX; 533 534 // The navigation should insert a new tab into the target Browser. 535 params->browser->tab_strip_model()->AddTabContents( 536 params->target_contents, 537 params->tabstrip_index, 538 params->transition, 539 params->tabstrip_add_types); 540 // Now that the |params->target_contents| is safely owned by the target 541 // Browser's TabStripModel, we can release ownership. 542 target_contents_owner.ReleaseOwnership(); 543 } 544 545 if (singleton_index >= 0) { 546 WebContents* target = 547 chrome::GetWebContentsAt(params->browser, singleton_index); 548 549 if (target->IsCrashed()) { 550 target->GetController().Reload(true); 551 } else if (params->path_behavior == NavigateParams::IGNORE_AND_NAVIGATE && 552 target->GetURL() != params->url) { 553 LoadURLInContents(target, params->url, params); 554 } 555 556 // If the singleton tab isn't already selected, select it. 557 if (params->source_contents != params->target_contents) 558 chrome::ActivateTabAt(params->browser, singleton_index, user_initiated); 559 } 560 561 if (params->disposition != CURRENT_TAB) { 562 content::NotificationService::current()->Notify( 563 chrome::NOTIFICATION_TAB_ADDED, 564 content::Source<content::WebContentsDelegate>(params->browser), 565 content::Details<WebContents>(params->target_contents->web_contents())); 566 } 567} 568 569bool IsURLAllowedInIncognito(const GURL& url, 570 content::BrowserContext* browser_context) { 571 // Most URLs are allowed in incognito; the following are exceptions. 572 // chrome://extensions is on the list because it redirects to 573 // chrome://settings. 574 if (url.scheme() == chrome::kChromeUIScheme && 575 (url.host() == chrome::kChromeUISettingsHost || 576 url.host() == chrome::kChromeUISettingsFrameHost || 577 url.host() == chrome::kChromeUIExtensionsHost || 578 url.host() == chrome::kChromeUIBookmarksHost || 579 url.host() == chrome::kChromeUISyncPromoHost || 580 url.host() == chrome::kChromeUIUberHost)) { 581 return false; 582 } 583 584 GURL rewritten_url = url; 585 bool reverse_on_redirect = false; 586 content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary( 587 &rewritten_url, browser_context, &reverse_on_redirect); 588 589 // Some URLs are mapped to uber subpages. Do not allow them in incognito. 590 return !(rewritten_url.scheme() == chrome::kChromeUIScheme && 591 rewritten_url.host() == chrome::kChromeUIUberHost); 592} 593 594} // namespace chrome 595