1 // Copyright (c) 2011 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 "chrome/browser/browser_url_handler.h" 11 #include "chrome/browser/extensions/extension_tab_helper.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/tabs/tab_strip_model.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser_list.h" 16 #include "chrome/browser/ui/browser_window.h" 17 #include "chrome/browser/ui/omnibox/location_bar.h" 18 #include "chrome/browser/ui/status_bubble.h" 19 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 20 #include "chrome/browser/ui/webui/chrome_web_ui_factory.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/url_constants.h" 23 #include "content/browser/site_instance.h" 24 #include "content/browser/tab_contents/tab_contents.h" 25 26 namespace { 27 28 // Returns an appropriate SiteInstance for WebUI URLs, or the SiteInstance for 29 // |source_contents| if it represents the same website as |url|. Returns NULL 30 // otherwise. 31 SiteInstance* GetSiteInstance(TabContents* source_contents, Profile* profile, 32 const GURL& url) { 33 // If url is a WebUI or extension, we need to be sure to use the right type 34 // of renderer process up front. Otherwise, we create a normal SiteInstance 35 // as part of creating the tab. 36 if (ChromeWebUIFactory::GetInstance()->UseWebUIForURL(profile, url)) 37 return SiteInstance::CreateSiteInstanceForURL(profile, url); 38 39 if (!source_contents) 40 return NULL; 41 42 // Don't use this logic when "--process-per-tab" is specified. 43 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab) && 44 SiteInstance::IsSameWebSite(source_contents->profile(), 45 source_contents->GetURL(), 46 url)) { 47 return source_contents->GetSiteInstance(); 48 } 49 return NULL; 50 } 51 52 // Returns true if the specified Browser can open tabs. Not all Browsers support 53 // multiple tabs, such as app frames and popups. This function returns false for 54 // those types of Browser. 55 bool WindowCanOpenTabs(Browser* browser) { 56 return browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP) || 57 browser->tabstrip_model()->empty(); 58 } 59 60 // Finds an existing Browser compatible with |profile|, making a new one if no 61 // such Browser is located. 62 Browser* GetOrCreateBrowser(Profile* profile) { 63 Browser* browser = BrowserList::FindBrowserWithType(profile, 64 Browser::TYPE_NORMAL, 65 false); 66 return browser ? browser : Browser::Create(profile); 67 } 68 69 // Returns true if two URLs are equal after taking |replacements| into account. 70 bool CompareURLsWithReplacements( 71 const GURL& url, 72 const GURL& other, 73 const url_canon::Replacements<char>& replacements) { 74 if (url == other) 75 return true; 76 77 GURL url_replaced = url.ReplaceComponents(replacements); 78 GURL other_replaced = other.ReplaceComponents(replacements); 79 return url_replaced == other_replaced; 80 } 81 82 // Returns the index of an existing singleton tab in |params->browser| matching 83 // the URL specified in |params|. 84 int GetIndexOfSingletonTab(browser::NavigateParams* params) { 85 if (params->disposition != SINGLETON_TAB) 86 return -1; 87 88 // In case the URL was rewritten by the BrowserURLHandler we need to ensure 89 // that we do not open another URL that will get redirected to the rewritten 90 // URL. 91 GURL rewritten_url(params->url); 92 bool reverse_on_redirect = false; 93 BrowserURLHandler::RewriteURLIfNecessary(&rewritten_url, 94 params->browser->profile(), 95 &reverse_on_redirect); 96 97 // If there are several matches: prefer the active tab by starting there. 98 int start_index = std::max(0, params->browser->active_index()); 99 int tab_count = params->browser->tab_count(); 100 for (int i = 0; i < tab_count; ++i) { 101 int tab_index = (start_index + i) % tab_count; 102 TabContentsWrapper* tab = 103 params->browser->GetTabContentsWrapperAt(tab_index); 104 105 url_canon::Replacements<char> replacements; 106 replacements.ClearRef(); 107 if (params->path_behavior == browser::NavigateParams::IGNORE_AND_NAVIGATE || 108 params->path_behavior == browser::NavigateParams::IGNORE_AND_STAY_PUT) { 109 replacements.ClearPath(); 110 replacements.ClearQuery(); 111 } 112 113 if (CompareURLsWithReplacements(tab->tab_contents()->GetURL(), 114 params->url, replacements) || 115 CompareURLsWithReplacements(tab->tab_contents()->GetURL(), 116 rewritten_url, replacements)) { 117 params->target_contents = tab; 118 return tab_index; 119 } 120 } 121 122 return -1; 123 } 124 125 // Change some of the navigation parameters based on the particular URL. 126 // Currently this applies to chrome://settings and the bookmark manager, 127 // which we always want to open in a normal (not incognito) window. Guest 128 // session is an exception. 129 void AdjustNavigateParamsForURL(browser::NavigateParams* params) { 130 if (!params->target_contents && 131 params->url.scheme() == chrome::kChromeUIScheme && 132 (params->url.host() == chrome::kChromeUISettingsHost || 133 params->url.host() == chrome::kChromeUIBookmarksHost)) { 134 Profile* profile = 135 params->browser ? params->browser->profile() : params->profile; 136 137 if (profile->IsOffTheRecord() && !Profile::IsGuestSession()) { 138 profile = profile->GetOriginalProfile(); 139 140 params->disposition = SINGLETON_TAB; 141 params->profile = profile; 142 params->browser = Browser::GetOrCreateTabbedBrowser(profile); 143 params->window_action = browser::NavigateParams::SHOW_WINDOW; 144 } 145 } 146 } 147 148 // Returns a Browser that can host the navigation or tab addition specified in 149 // |params|. This might just return the same Browser specified in |params|, or 150 // some other if that Browser is deemed incompatible. 151 Browser* GetBrowserForDisposition(browser::NavigateParams* params) { 152 // If no source TabContents was specified, we use the selected one from the 153 // target browser. This must happen first, before GetBrowserForDisposition() 154 // has a chance to replace |params->browser| with another one. 155 if (!params->source_contents && params->browser) 156 params->source_contents = 157 params->browser->GetSelectedTabContentsWrapper(); 158 159 Profile* profile = 160 params->browser ? params->browser->profile() : params->profile; 161 162 switch (params->disposition) { 163 case CURRENT_TAB: 164 if (!params->browser && profile) { 165 // We specified a profile instead of a browser; find or create one. 166 params->browser = Browser::GetOrCreateTabbedBrowser(profile); 167 } 168 return params->browser; 169 case SINGLETON_TAB: 170 case NEW_FOREGROUND_TAB: 171 case NEW_BACKGROUND_TAB: 172 // See if we can open the tab in the window this navigator is bound to. 173 if (params->browser && WindowCanOpenTabs(params->browser)) 174 return params->browser; 175 // Find a compatible window and re-execute this command in it. Otherwise 176 // re-run with NEW_WINDOW. 177 if (profile) 178 return GetOrCreateBrowser(profile); 179 return NULL; 180 case NEW_POPUP: { 181 // Make a new popup window. Coerce app-style if |params->browser| or the 182 // |source| represents an app. 183 Browser::Type type = Browser::TYPE_POPUP; 184 if ((params->browser && (params->browser->type() & Browser::TYPE_APP)) || 185 (params->source_contents && 186 params->source_contents->extension_tab_helper()->is_app())) { 187 type = Browser::TYPE_APP_POPUP; 188 } 189 if (profile) { 190 Browser* browser = new Browser(type, profile); 191 browser->set_override_bounds(params->window_bounds); 192 browser->InitBrowserWindow(); 193 return browser; 194 } 195 return NULL; 196 } 197 case NEW_WINDOW: 198 // Make a new normal browser window. 199 if (profile) { 200 Browser* browser = new Browser(Browser::TYPE_NORMAL, profile); 201 browser->InitBrowserWindow(); 202 return browser; 203 } 204 return NULL; 205 case OFF_THE_RECORD: 206 // Make or find an incognito window. 207 if (profile) 208 return GetOrCreateBrowser(profile->GetOffTheRecordProfile()); 209 return NULL; 210 // The following types all result in no navigation. 211 case SUPPRESS_OPEN: 212 case SAVE_TO_DISK: 213 case IGNORE_ACTION: 214 return NULL; 215 default: 216 NOTREACHED(); 217 } 218 return NULL; 219 } 220 221 // Fix disposition and other parameter values depending on prevailing 222 // conditions. 223 void NormalizeDisposition(browser::NavigateParams* params) { 224 // Calculate the WindowOpenDisposition if necessary. 225 if (params->browser->tabstrip_model()->empty() && 226 (params->disposition == NEW_BACKGROUND_TAB || 227 params->disposition == CURRENT_TAB || 228 params->disposition == SINGLETON_TAB)) { 229 params->disposition = NEW_FOREGROUND_TAB; 230 } 231 if (params->browser->profile()->IsOffTheRecord() && 232 params->disposition == OFF_THE_RECORD) { 233 params->disposition = NEW_FOREGROUND_TAB; 234 } 235 236 switch (params->disposition) { 237 case NEW_BACKGROUND_TAB: 238 // Disposition trumps add types. ADD_ACTIVE is a default, so we need to 239 // remove it if disposition implies the tab is going to open in the 240 // background. 241 params->tabstrip_add_types &= ~TabStripModel::ADD_ACTIVE; 242 break; 243 244 case NEW_WINDOW: 245 case NEW_POPUP: 246 // Code that wants to open a new window typically expects it to be shown 247 // automatically. 248 if (params->window_action == browser::NavigateParams::NO_ACTION) 249 params->window_action = browser::NavigateParams::SHOW_WINDOW; 250 // Fall-through. 251 case NEW_FOREGROUND_TAB: 252 case SINGLETON_TAB: 253 params->tabstrip_add_types |= TabStripModel::ADD_ACTIVE; 254 break; 255 256 default: 257 break; 258 } 259 } 260 261 // Obtain the profile used by the code that originated the Navigate() request. 262 // |source_browser| represents the Browser that was supplied in |params| before 263 // it was modified. 264 Profile* GetSourceProfile(browser::NavigateParams* params, 265 Browser* source_browser) { 266 if (params->source_contents) 267 return params->source_contents->profile(); 268 269 if (source_browser) 270 return source_browser->profile(); 271 272 if (params->profile) 273 return params->profile; 274 275 // We couldn't find one in any of the source metadata, so we'll fall back to 276 // the profile associated with the target browser. 277 return params->browser->profile(); 278 } 279 280 281 // This class makes sure the Browser object held in |params| is made visible 282 // by the time it goes out of scope, provided |params| wants it to be shown. 283 class ScopedBrowserDisplayer { 284 public: 285 explicit ScopedBrowserDisplayer(browser::NavigateParams* params) 286 : params_(params) { 287 } 288 ~ScopedBrowserDisplayer() { 289 if (params_->window_action == browser::NavigateParams::SHOW_WINDOW_INACTIVE) 290 params_->browser->window()->ShowInactive(); 291 else if (params_->window_action == browser::NavigateParams::SHOW_WINDOW) 292 params_->browser->window()->Show(); 293 } 294 private: 295 browser::NavigateParams* params_; 296 DISALLOW_COPY_AND_ASSIGN(ScopedBrowserDisplayer); 297 }; 298 299 // This class manages the lifetime of a TabContents created by the Navigate() 300 // function. When Navigate() creates a TabContents for a URL, an instance of 301 // this class takes ownership of it via TakeOwnership() until the TabContents 302 // is added to a tab strip at which time ownership is relinquished via 303 // ReleaseOwnership(). If this object goes out of scope without being added 304 // to a tab strip, the created TabContents is deleted to avoid a leak and the 305 // params->target_contents field is set to NULL. 306 class ScopedTargetContentsOwner { 307 public: 308 explicit ScopedTargetContentsOwner(browser::NavigateParams* params) 309 : params_(params) { 310 } 311 ~ScopedTargetContentsOwner() { 312 if (target_contents_owner_.get()) 313 params_->target_contents = NULL; 314 } 315 316 // Assumes ownership of |params_|' target_contents until ReleaseOwnership 317 // is called. 318 void TakeOwnership() { 319 target_contents_owner_.reset(params_->target_contents); 320 } 321 322 // Relinquishes ownership of |params_|' target_contents. 323 TabContentsWrapper* ReleaseOwnership() { 324 return target_contents_owner_.release(); 325 } 326 327 private: 328 browser::NavigateParams* params_; 329 scoped_ptr<TabContentsWrapper> target_contents_owner_; 330 DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner); 331 }; 332 333 } // namespace 334 335 namespace browser { 336 337 NavigateParams::NavigateParams( 338 Browser* a_browser, 339 const GURL& a_url, 340 PageTransition::Type a_transition) 341 : url(a_url), 342 target_contents(NULL), 343 source_contents(NULL), 344 disposition(CURRENT_TAB), 345 transition(a_transition), 346 tabstrip_index(-1), 347 tabstrip_add_types(TabStripModel::ADD_ACTIVE), 348 window_action(NO_ACTION), 349 path_behavior(RESPECT), 350 browser(a_browser), 351 profile(NULL) { 352 } 353 354 NavigateParams::NavigateParams(Browser* a_browser, 355 TabContentsWrapper* a_target_contents) 356 : target_contents(a_target_contents), 357 source_contents(NULL), 358 disposition(CURRENT_TAB), 359 transition(PageTransition::LINK), 360 tabstrip_index(-1), 361 tabstrip_add_types(TabStripModel::ADD_ACTIVE), 362 window_action(NO_ACTION), 363 path_behavior(RESPECT), 364 browser(a_browser), 365 profile(NULL) { 366 } 367 368 NavigateParams::~NavigateParams() { 369 } 370 371 void Navigate(NavigateParams* params) { 372 Browser* source_browser = params->browser; 373 AdjustNavigateParamsForURL(params); 374 375 params->browser = GetBrowserForDisposition(params); 376 if (!params->browser) 377 return; 378 // Navigate() must not return early after this point. 379 380 if (GetSourceProfile(params, source_browser) != params->browser->profile()) { 381 // A tab is being opened from a link from a different profile, we must reset 382 // source information that may cause state to be shared. 383 params->source_contents = NULL; 384 params->referrer = GURL(); 385 } 386 387 if (params->window_action == browser::NavigateParams::NO_ACTION && 388 source_browser != params->browser && 389 params->browser->tabstrip_model()->empty()) { 390 // A new window has been created. So it needs to be displayed. 391 params->window_action = browser::NavigateParams::SHOW_WINDOW; 392 } 393 394 // Make sure the Browser is shown if params call for it. 395 ScopedBrowserDisplayer displayer(params); 396 397 // Makes sure any TabContents created by this function is destroyed if 398 // not properly added to a tab strip. 399 ScopedTargetContentsOwner target_contents_owner(params); 400 401 // Some dispositions need coercion to base types. 402 NormalizeDisposition(params); 403 404 // Determine if the navigation was user initiated. If it was, we need to 405 // inform the target TabContents, and we may need to update the UI. 406 PageTransition::Type base_transition = 407 PageTransition::StripQualifier(params->transition); 408 bool user_initiated = base_transition == PageTransition::TYPED || 409 base_transition == PageTransition::AUTO_BOOKMARK; 410 411 // Check if this is a singleton tab that already exists 412 int singleton_index = GetIndexOfSingletonTab(params); 413 414 // If no target TabContents was specified, we need to construct one if we are 415 // supposed to target a new tab; unless it's a singleton that already exists. 416 if (!params->target_contents && singleton_index < 0) { 417 GURL url = params->url.is_empty() ? params->browser->GetHomePage() 418 : params->url; 419 if (params->disposition != CURRENT_TAB) { 420 TabContents* source_contents = params->source_contents ? 421 params->source_contents->tab_contents() : NULL; 422 params->target_contents = 423 Browser::TabContentsFactory( 424 params->browser->profile(), 425 GetSiteInstance(source_contents, params->browser->profile(), url), 426 MSG_ROUTING_NONE, 427 source_contents, 428 NULL); 429 // This function takes ownership of |params->target_contents| until it 430 // is added to a TabStripModel. 431 target_contents_owner.TakeOwnership(); 432 params->target_contents->extension_tab_helper()-> 433 SetExtensionAppById(params->extension_app_id); 434 // TODO(sky): figure out why this is needed. Without it we seem to get 435 // failures in startup tests. 436 // By default, content believes it is not hidden. When adding contents 437 // in the background, tell it that it's hidden. 438 if ((params->tabstrip_add_types & TabStripModel::ADD_ACTIVE) == 0) { 439 // TabStripModel::AddTabContents invokes HideContents if not foreground. 440 params->target_contents->tab_contents()->WasHidden(); 441 } 442 } else { 443 // ... otherwise if we're loading in the current tab, the target is the 444 // same as the source. 445 params->target_contents = params->source_contents; 446 DCHECK(params->target_contents); 447 } 448 449 if (user_initiated) { 450 static_cast<RenderViewHostDelegate*>(params->target_contents-> 451 tab_contents())->OnUserGesture(); 452 } 453 454 // Perform the actual navigation. 455 params->target_contents->controller().LoadURL(url, params->referrer, 456 params->transition); 457 } else { 458 // |target_contents| was specified non-NULL, and so we assume it has already 459 // been navigated appropriately. We need to do nothing more other than 460 // add it to the appropriate tabstrip. 461 } 462 463 if (params->source_contents == params->target_contents) { 464 // The navigation occurred in the source tab. 465 params->browser->UpdateUIForNavigationInTab( 466 params->target_contents, 467 params->transition, 468 user_initiated); 469 } else if (singleton_index == -1) { 470 // If some non-default value is set for the index, we should tell the 471 // TabStripModel to respect it. 472 if (params->tabstrip_index != -1) 473 params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX; 474 475 // The navigation should insert a new tab into the target Browser. 476 params->browser->tabstrip_model()->AddTabContents( 477 params->target_contents, 478 params->tabstrip_index, 479 params->transition, 480 params->tabstrip_add_types); 481 // Now that the |params->target_contents| is safely owned by the target 482 // Browser's TabStripModel, we can release ownership. 483 target_contents_owner.ReleaseOwnership(); 484 } 485 486 if (singleton_index >= 0) { 487 TabContents* target = params->browser->GetTabContentsAt(singleton_index); 488 489 if (params->path_behavior == NavigateParams::IGNORE_AND_NAVIGATE && 490 target->GetURL() != params->url) { 491 target->controller().LoadURL( 492 params->url, params->referrer, params->transition); 493 } 494 495 // If the singleton tab isn't already selected, select it. 496 if (params->source_contents != params->target_contents) 497 params->browser->ActivateTabAt(singleton_index, user_initiated); 498 } 499 } 500 501 } // namespace browser 502