| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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/browser_navigator.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "chrome/browser/browser.h" | |
| 9 #include "chrome/browser/browser_list.h" | |
| 10 #include "chrome/browser/browser_url_handler.h" | |
| 11 #include "chrome/browser/browser_window.h" | |
| 12 #include "chrome/browser/location_bar.h" | |
| 13 #include "chrome/browser/profile.h" | |
| 14 #include "chrome/browser/renderer_host/site_instance.h" | |
| 15 #include "chrome/browser/status_bubble.h" | |
| 16 #include "chrome/browser/tabs/tab_strip_model.h" | |
| 17 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 18 #include "chrome/common/chrome_switches.h" | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Returns the SiteInstance for |source_contents| if it represents the same | |
| 23 // website as |url|, or NULL otherwise. |source_contents| cannot be NULL. | |
| 24 SiteInstance* GetSiteInstance(TabContents* source_contents, const GURL& url) { | |
| 25 if (!source_contents) | |
| 26 return NULL; | |
| 27 | |
| 28 // Don't use this logic when "--process-per-tab" is specified. | |
| 29 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab) && | |
| 30 SiteInstance::IsSameWebSite(source_contents->profile(), | |
| 31 source_contents->GetURL(), | |
| 32 url)) { | |
| 33 return source_contents->GetSiteInstance(); | |
| 34 } | |
| 35 return NULL; | |
| 36 } | |
| 37 | |
| 38 // Returns true if the specified Browser can open tabs. Not all Browsers support | |
| 39 // multiple tabs, such as app frames and popups. This function returns false for | |
| 40 // those types of Browser. | |
| 41 bool WindowCanOpenTabs(Browser* browser) { | |
| 42 return browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP) || | |
| 43 browser->tabstrip_model()->empty(); | |
| 44 } | |
| 45 | |
| 46 // Finds an existing Browser compatible with |profile|, making a new one if no | |
| 47 // such Browser is located. | |
| 48 Browser* GetOrCreateBrowser(Profile* profile) { | |
| 49 Browser* browser = BrowserList::FindBrowserWithType(profile, | |
| 50 Browser::TYPE_NORMAL, | |
| 51 false); | |
| 52 return browser ? browser : Browser::Create(profile); | |
| 53 } | |
| 54 | |
| 55 // Returns true if two URLs are equal ignoring their ref (hash fragment). | |
| 56 bool CompareURLsIgnoreRef(const GURL& url, const GURL& other) { | |
| 57 if (url == other) | |
| 58 return true; | |
| 59 // If neither has a ref than there is no point in stripping the refs and | |
| 60 // the URLs are different since the comparison failed in the previous if | |
| 61 // statement. | |
| 62 if (!url.has_ref() && !other.has_ref()) | |
| 63 return false; | |
| 64 url_canon::Replacements<char> replacements; | |
| 65 replacements.ClearRef(); | |
| 66 GURL url_no_ref = url.ReplaceComponents(replacements); | |
| 67 GURL other_no_ref = other.ReplaceComponents(replacements); | |
| 68 return url_no_ref == other_no_ref; | |
| 69 } | |
| 70 | |
| 71 // Returns the index of an existing singleton tab in |params->browser| matching | |
| 72 // the URL specified in |params|. | |
| 73 int GetIndexOfSingletonTab(browser::NavigateParams* params) { | |
| 74 if (params->disposition != SINGLETON_TAB) | |
| 75 return -1; | |
| 76 | |
| 77 // In case the URL was rewritten by the BrowserURLHandler we need to ensure | |
| 78 // that we do not open another URL that will get redirected to the rewritten | |
| 79 // URL. | |
| 80 GURL rewritten_url(params->url); | |
| 81 bool reverse_on_redirect = false; | |
| 82 BrowserURLHandler::RewriteURLIfNecessary(&rewritten_url, | |
| 83 params->browser->profile(), | |
| 84 &reverse_on_redirect); | |
| 85 | |
| 86 for (int i = 0; i < params->browser->tab_count(); ++i) { | |
| 87 TabContents* tab = params->browser->GetTabContentsAt(i); | |
| 88 if (CompareURLsIgnoreRef(tab->GetURL(), params->url) || | |
| 89 CompareURLsIgnoreRef(tab->GetURL(), rewritten_url)) { | |
| 90 params->target_contents = tab; | |
| 91 return i; | |
| 92 } | |
| 93 } | |
| 94 return -1; | |
| 95 } | |
| 96 | |
| 97 // Returns a Browser that can host the navigation or tab addition specified in | |
| 98 // |params|. This might just return the same Browser specified in |params|, or | |
| 99 // some other if that Browser is deemed incompatible. | |
| 100 Browser* GetBrowserForDisposition(browser::NavigateParams* params) { | |
| 101 // If no source TabContents was specified, we use the selected one from the | |
| 102 // target browser. This must happen first, before GetBrowserForDisposition() | |
| 103 // has a chance to replace |params->browser| with another one. | |
| 104 if (!params->source_contents && params->browser) | |
| 105 params->source_contents = params->browser->GetSelectedTabContents(); | |
| 106 | |
| 107 Profile* profile = | |
| 108 params->browser ? params->browser->profile() : params->profile; | |
| 109 | |
| 110 switch (params->disposition) { | |
| 111 case CURRENT_TAB: | |
| 112 if (!params->browser && profile) { | |
| 113 // We specified a profile instead of a browser; find or create one. | |
| 114 params->browser = Browser::GetOrCreateTabbedBrowser(profile); | |
| 115 } | |
| 116 return params->browser; | |
| 117 case SINGLETON_TAB: | |
| 118 case NEW_FOREGROUND_TAB: | |
| 119 case NEW_BACKGROUND_TAB: | |
| 120 // See if we can open the tab in the window this navigator is bound to. | |
| 121 if (params->browser && WindowCanOpenTabs(params->browser)) | |
| 122 return params->browser; | |
| 123 // Find a compatible window and re-execute this command in it. Otherwise | |
| 124 // re-run with NEW_WINDOW. | |
| 125 if (profile) | |
| 126 return GetOrCreateBrowser(profile); | |
| 127 return NULL; | |
| 128 case NEW_POPUP: { | |
| 129 // Make a new popup window. Coerce app-style if |params->browser| or the | |
| 130 // |source| represents an app. | |
| 131 Browser::Type type = Browser::TYPE_POPUP; | |
| 132 if ((params->browser && params->browser->type() == Browser::TYPE_APP) || | |
| 133 (params->source_contents && params->source_contents->is_app())) { | |
| 134 type = Browser::TYPE_APP_POPUP; | |
| 135 } | |
| 136 if (profile) { | |
| 137 Browser* browser = new Browser(type, profile); | |
| 138 browser->set_override_bounds(params->window_bounds); | |
| 139 browser->CreateBrowserWindow(); | |
| 140 return browser; | |
| 141 } | |
| 142 return NULL; | |
| 143 } | |
| 144 case NEW_WINDOW: | |
| 145 // Make a new normal browser window. | |
| 146 if (profile) { | |
| 147 Browser* browser = new Browser(Browser::TYPE_NORMAL, profile); | |
| 148 browser->CreateBrowserWindow(); | |
| 149 return browser; | |
| 150 } | |
| 151 return NULL; | |
| 152 case OFF_THE_RECORD: | |
| 153 // Make or find an incognito window. | |
| 154 if (profile) | |
| 155 return GetOrCreateBrowser(profile->GetOffTheRecordProfile()); | |
| 156 return NULL; | |
| 157 // The following types all result in no navigation. | |
| 158 case SUPPRESS_OPEN: | |
| 159 case SAVE_TO_DISK: | |
| 160 case IGNORE_ACTION: | |
| 161 return NULL; | |
| 162 default: | |
| 163 NOTREACHED(); | |
| 164 } | |
| 165 return NULL; | |
| 166 } | |
| 167 | |
| 168 // Fix disposition and other parameter values depending on prevailing | |
| 169 // conditions. | |
| 170 void NormalizeDisposition(browser::NavigateParams* params) { | |
| 171 // Calculate the WindowOpenDisposition if necessary. | |
| 172 if (params->browser->tabstrip_model()->empty() && | |
| 173 (params->disposition == NEW_BACKGROUND_TAB || | |
| 174 params->disposition == CURRENT_TAB || | |
| 175 params->disposition == SINGLETON_TAB)) { | |
| 176 params->disposition = NEW_FOREGROUND_TAB; | |
| 177 } | |
| 178 if (params->browser->profile()->IsOffTheRecord() && | |
| 179 params->disposition == OFF_THE_RECORD) { | |
| 180 params->disposition = NEW_FOREGROUND_TAB; | |
| 181 } | |
| 182 | |
| 183 // Disposition trumps add types. ADD_SELECTED is a default, so we need to | |
| 184 // remove it if disposition implies the tab is going to open in the | |
| 185 // background. | |
| 186 if (params->disposition == NEW_BACKGROUND_TAB) | |
| 187 params->tabstrip_add_types &= ~TabStripModel::ADD_SELECTED; | |
| 188 else if (params->disposition == NEW_FOREGROUND_TAB) | |
| 189 params->tabstrip_add_types |= TabStripModel::ADD_SELECTED; | |
| 190 | |
| 191 // Code that wants to open a new window typically expects it to be shown | |
| 192 // automatically. | |
| 193 if (params->disposition == NEW_WINDOW || params->disposition == NEW_POPUP) { | |
| 194 params->show_window = true; | |
| 195 params->tabstrip_add_types |= TabStripModel::ADD_SELECTED; | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // This class makes sure the Browser object held in |params| is made visible | |
| 200 // by the time it goes out of scope, provided |params| wants it to be shown. | |
| 201 class ScopedBrowserDisplayer { | |
| 202 public: | |
| 203 explicit ScopedBrowserDisplayer(browser::NavigateParams* params) | |
| 204 : params_(params) { | |
| 205 } | |
| 206 ~ScopedBrowserDisplayer() { | |
| 207 if (params_->show_window) | |
| 208 params_->browser->window()->Show(); | |
| 209 } | |
| 210 private: | |
| 211 browser::NavigateParams* params_; | |
| 212 DISALLOW_COPY_AND_ASSIGN(ScopedBrowserDisplayer); | |
| 213 }; | |
| 214 | |
| 215 // This class manages the lifetime of a TabContents created by the Navigate() | |
| 216 // function. When Navigate() creates a TabContents for a URL, an instance of | |
| 217 // this class takes ownership of it via TakeOwnership() until the TabContents | |
| 218 // is added to a tab strip at which time ownership is relinquished via | |
| 219 // ReleaseOwnership(). If this object goes out of scope without being added | |
| 220 // to a tab strip, the created TabContents is deleted to avoid a leak and the | |
| 221 // params->target_contents field is set to NULL. | |
| 222 class ScopedTargetContentsOwner { | |
| 223 public: | |
| 224 explicit ScopedTargetContentsOwner(browser::NavigateParams* params) | |
| 225 : params_(params) { | |
| 226 } | |
| 227 ~ScopedTargetContentsOwner() { | |
| 228 if (target_contents_owner_.get()) | |
| 229 params_->target_contents = NULL; | |
| 230 } | |
| 231 | |
| 232 // Assumes ownership of |params_|' target_contents until ReleaseOwnership | |
| 233 // is called. | |
| 234 void TakeOwnership() { | |
| 235 target_contents_owner_.reset(params_->target_contents); | |
| 236 } | |
| 237 | |
| 238 // Relinquishes ownership of |params_|' target_contents. | |
| 239 TabContents* ReleaseOwnership() { | |
| 240 return target_contents_owner_.release(); | |
| 241 } | |
| 242 | |
| 243 private: | |
| 244 browser::NavigateParams* params_; | |
| 245 scoped_ptr<TabContents> target_contents_owner_; | |
| 246 DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner); | |
| 247 }; | |
| 248 | |
| 249 } // namespace | |
| 250 | |
| 251 namespace browser { | |
| 252 | |
| 253 NavigateParams::NavigateParams( | |
| 254 Browser* a_browser, | |
| 255 const GURL& a_url, | |
| 256 PageTransition::Type a_transition) | |
| 257 : url(a_url), | |
| 258 target_contents(NULL), | |
| 259 source_contents(NULL), | |
| 260 disposition(CURRENT_TAB), | |
| 261 transition(a_transition), | |
| 262 tabstrip_index(-1), | |
| 263 tabstrip_add_types(TabStripModel::ADD_SELECTED), | |
| 264 show_window(false), | |
| 265 browser(a_browser), | |
| 266 profile(NULL) { | |
| 267 } | |
| 268 | |
| 269 NavigateParams::NavigateParams(Browser* a_browser, | |
| 270 TabContents* a_target_contents) | |
| 271 : target_contents(a_target_contents), | |
| 272 source_contents(NULL), | |
| 273 disposition(CURRENT_TAB), | |
| 274 transition(PageTransition::LINK), | |
| 275 tabstrip_index(-1), | |
| 276 tabstrip_add_types(TabStripModel::ADD_SELECTED), | |
| 277 show_window(false), | |
| 278 browser(a_browser), | |
| 279 profile(NULL) { | |
| 280 } | |
| 281 | |
| 282 NavigateParams::~NavigateParams() { | |
| 283 } | |
| 284 | |
| 285 void Navigate(NavigateParams* params) { | |
| 286 params->browser = GetBrowserForDisposition(params); | |
| 287 if (!params->browser) | |
| 288 return; | |
| 289 // Navigate() must not return early after this point. | |
| 290 | |
| 291 // Make sure the Browser is shown if params call for it. | |
| 292 ScopedBrowserDisplayer displayer(params); | |
| 293 | |
| 294 // Makes sure any TabContents created by this function is destroyed if | |
| 295 // not properly added to a tab strip. | |
| 296 ScopedTargetContentsOwner target_contents_owner(params); | |
| 297 | |
| 298 // Some dispositions need coercion to base types. | |
| 299 NormalizeDisposition(params); | |
| 300 | |
| 301 // Determine if the navigation was user initiated. If it was, we need to | |
| 302 // inform the target TabContents, and we may need to update the UI. | |
| 303 PageTransition::Type base_transition = | |
| 304 PageTransition::StripQualifier(params->transition); | |
| 305 bool user_initiated = base_transition == PageTransition::TYPED || | |
| 306 base_transition == PageTransition::AUTO_BOOKMARK; | |
| 307 | |
| 308 // If no target TabContents was specified, we need to construct one if we are | |
| 309 // supposed to target a new tab. | |
| 310 if (!params->target_contents) { | |
| 311 if (params->disposition != CURRENT_TAB) { | |
| 312 params->target_contents = | |
| 313 new TabContents(params->browser->profile(), | |
| 314 GetSiteInstance(params->source_contents, params->url), | |
| 315 MSG_ROUTING_NONE, | |
| 316 params->source_contents, | |
| 317 NULL); | |
| 318 // This function takes ownership of |params->target_contents| until it | |
| 319 // is added to a TabStripModel. | |
| 320 target_contents_owner.TakeOwnership(); | |
| 321 params->target_contents->SetExtensionAppById(params->extension_app_id); | |
| 322 // TODO(sky): figure out why this is needed. Without it we seem to get | |
| 323 // failures in startup tests. | |
| 324 // By default, content believes it is not hidden. When adding contents | |
| 325 // in the background, tell it that it's hidden. | |
| 326 if ((params->tabstrip_add_types & TabStripModel::ADD_SELECTED) == 0) { | |
| 327 // TabStripModel::AddTabContents invokes HideContents if not foreground. | |
| 328 params->target_contents->WasHidden(); | |
| 329 } | |
| 330 } else { | |
| 331 // ... otherwise if we're loading in the current tab, the target is the | |
| 332 // same as the source. | |
| 333 params->target_contents = params->source_contents; | |
| 334 DCHECK(params->target_contents); | |
| 335 } | |
| 336 | |
| 337 if (user_initiated) { | |
| 338 RenderViewHostDelegate::BrowserIntegration* integration = | |
| 339 params->target_contents; | |
| 340 integration->OnUserGesture(); | |
| 341 } | |
| 342 | |
| 343 // Perform the actual navigation. | |
| 344 GURL url = params->url.is_empty() ? params->browser->GetHomePage() | |
| 345 : params->url; | |
| 346 params->target_contents->controller().LoadURL(url, params->referrer, | |
| 347 params->transition); | |
| 348 } else { | |
| 349 // |target_contents| was specified non-NULL, and so we assume it has already | |
| 350 // been navigated appropriately. We need to do nothing more other than | |
| 351 // add it to the appropriate tabstrip. | |
| 352 } | |
| 353 | |
| 354 if (params->source_contents == params->target_contents) { | |
| 355 // The navigation occurred in the source tab, so update the UI. | |
| 356 params->browser->UpdateUIForNavigationInTab(params->target_contents, | |
| 357 params->transition, | |
| 358 user_initiated); | |
| 359 } else { | |
| 360 // The navigation occurred in some other tab. | |
| 361 int singleton_index = GetIndexOfSingletonTab(params); | |
| 362 if (params->disposition == SINGLETON_TAB && singleton_index >= 0) { | |
| 363 // The navigation should re-select an existing tab in the target Browser. | |
| 364 params->browser->SelectTabContentsAt(singleton_index, user_initiated); | |
| 365 } else { | |
| 366 // If some non-default value is set for the index, we should tell the | |
| 367 // TabStripModel to respect it. | |
| 368 if (params->tabstrip_index != -1) | |
| 369 params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX; | |
| 370 | |
| 371 // The navigation should insert a new tab into the target Browser. | |
| 372 params->browser->tabstrip_model()->AddTabContents( | |
| 373 params->target_contents, | |
| 374 params->tabstrip_index, | |
| 375 params->transition, | |
| 376 params->tabstrip_add_types); | |
| 377 // Now that the |params->target_contents| is safely owned by the target | |
| 378 // Browser's TabStripModel, we can release ownership. | |
| 379 target_contents_owner.ReleaseOwnership(); | |
| 380 } | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 } // namespace browser | |
| OLD | NEW |