• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetSiteInstance(TabContents * source_contents,Profile * profile,const GURL & url)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.
WindowCanOpenTabs(Browser * 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.
GetOrCreateBrowser(Profile * profile)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.
CompareURLsWithReplacements(const GURL & url,const GURL & other,const url_canon::Replacements<char> & replacements)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|.
GetIndexOfSingletonTab(browser::NavigateParams * 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.
AdjustNavigateParamsForURL(browser::NavigateParams * params)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.
GetBrowserForDisposition(browser::NavigateParams * params)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.
NormalizeDisposition(browser::NavigateParams * params)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.
GetSourceProfile(browser::NavigateParams * params,Browser * source_browser)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:
ScopedBrowserDisplayer(browser::NavigateParams * params)285   explicit ScopedBrowserDisplayer(browser::NavigateParams* params)
286       : params_(params) {
287   }
~ScopedBrowserDisplayer()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:
ScopedTargetContentsOwner(browser::NavigateParams * params)308   explicit ScopedTargetContentsOwner(browser::NavigateParams* params)
309       : params_(params) {
310   }
~ScopedTargetContentsOwner()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.
TakeOwnership()318   void TakeOwnership() {
319     target_contents_owner_.reset(params_->target_contents);
320   }
321 
322   // Relinquishes ownership of |params_|' target_contents.
ReleaseOwnership()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 
NavigateParams(Browser * a_browser,const GURL & a_url,PageTransition::Type a_transition)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 
NavigateParams(Browser * a_browser,TabContentsWrapper * a_target_contents)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 
~NavigateParams()368 NavigateParams::~NavigateParams() {
369 }
370 
Navigate(NavigateParams * params)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