Home | History | Annotate | Download | only in ui
      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