Home | History | Annotate | Download | only in extensions
      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/extensions/extension_tab_util.h"
      6 
      7 #include "base/strings/string_number_conversions.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
     10 #include "chrome/browser/extensions/chrome_extension_function.h"
     11 #include "chrome/browser/extensions/chrome_extension_function_details.h"
     12 #include "chrome/browser/extensions/tab_helper.h"
     13 #include "chrome/browser/extensions/window_controller.h"
     14 #include "chrome/browser/extensions/window_controller_list.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/sessions/session_tab_helper.h"
     17 #include "chrome/browser/ui/browser.h"
     18 #include "chrome/browser/ui/browser_finder.h"
     19 #include "chrome/browser/ui/browser_iterator.h"
     20 #include "chrome/browser/ui/browser_window.h"
     21 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
     22 #include "chrome/browser/ui/singleton_tabs.h"
     23 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
     24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     25 #include "chrome/common/extensions/api/tabs.h"
     26 #include "chrome/common/url_constants.h"
     27 #include "components/url_fixer/url_fixer.h"
     28 #include "content/public/browser/favicon_status.h"
     29 #include "content/public/browser/navigation_entry.h"
     30 #include "content/public/browser/web_contents.h"
     31 #include "extensions/browser/app_window/app_window.h"
     32 #include "extensions/browser/app_window/app_window_registry.h"
     33 #include "extensions/common/constants.h"
     34 #include "extensions/common/error_utils.h"
     35 #include "extensions/common/extension.h"
     36 #include "extensions/common/feature_switch.h"
     37 #include "extensions/common/manifest_constants.h"
     38 #include "extensions/common/manifest_handlers/incognito_info.h"
     39 #include "extensions/common/manifest_handlers/options_page_info.h"
     40 #include "extensions/common/permissions/api_permission.h"
     41 #include "extensions/common/permissions/permissions_data.h"
     42 #include "url/gurl.h"
     43 
     44 using content::NavigationEntry;
     45 using content::WebContents;
     46 
     47 namespace extensions {
     48 
     49 namespace {
     50 
     51 namespace keys = tabs_constants;
     52 
     53 WindowController* GetAppWindowController(const WebContents* contents) {
     54   Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
     55   AppWindowRegistry* registry = AppWindowRegistry::Get(profile);
     56   if (!registry)
     57     return NULL;
     58   AppWindow* app_window =
     59       registry->GetAppWindowForRenderViewHost(contents->GetRenderViewHost());
     60   if (!app_window)
     61     return NULL;
     62   return WindowControllerList::GetInstance()->FindWindowById(
     63       app_window->session_id().id());
     64 }
     65 
     66 // |error_message| can optionally be passed in and will be set with an
     67 // appropriate message if the window cannot be found by id.
     68 Browser* GetBrowserInProfileWithId(Profile* profile,
     69                                    const int window_id,
     70                                    bool include_incognito,
     71                                    std::string* error_message) {
     72   Profile* incognito_profile =
     73       include_incognito && profile->HasOffTheRecordProfile()
     74           ? profile->GetOffTheRecordProfile()
     75           : NULL;
     76   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
     77     Browser* browser = *it;
     78     if ((browser->profile() == profile ||
     79          browser->profile() == incognito_profile) &&
     80         ExtensionTabUtil::GetWindowId(browser) == window_id &&
     81         browser->window()) {
     82       return browser;
     83     }
     84   }
     85 
     86   if (error_message)
     87     *error_message = ErrorUtils::FormatErrorMessage(
     88         keys::kWindowNotFoundError, base::IntToString(window_id));
     89 
     90   return NULL;
     91 }
     92 
     93 Browser* CreateBrowser(ChromeUIThreadExtensionFunction* function,
     94                        int window_id,
     95                        std::string* error) {
     96   content::WebContents* web_contents = function->GetAssociatedWebContents();
     97   chrome::HostDesktopType desktop_type =
     98       web_contents && web_contents->GetNativeView()
     99           ? chrome::GetHostDesktopTypeForNativeView(
    100                 web_contents->GetNativeView())
    101           : chrome::GetHostDesktopTypeForNativeView(NULL);
    102   Browser::CreateParams params(
    103       Browser::TYPE_TABBED, function->GetProfile(), desktop_type);
    104   Browser* browser = new Browser(params);
    105   browser->window()->Show();
    106   return browser;
    107 }
    108 
    109 }  // namespace
    110 
    111 ExtensionTabUtil::OpenTabParams::OpenTabParams()
    112     : create_browser_if_needed(false) {
    113 }
    114 
    115 ExtensionTabUtil::OpenTabParams::~OpenTabParams() {
    116 }
    117 
    118 // Opens a new tab for a given extension. Returns NULL and sets |error| if an
    119 // error occurs.
    120 base::DictionaryValue* ExtensionTabUtil::OpenTab(
    121     ChromeUIThreadExtensionFunction* function,
    122     const OpenTabParams& params,
    123     std::string* error) {
    124   // windowId defaults to "current" window.
    125   int window_id = extension_misc::kCurrentWindowId;
    126   if (params.window_id.get())
    127     window_id = *params.window_id;
    128 
    129   Browser* browser = GetBrowserFromWindowID(function, window_id, error);
    130   if (!browser) {
    131     if (!params.create_browser_if_needed) {
    132       return NULL;
    133     }
    134     browser = CreateBrowser(function, window_id, error);
    135     if (!browser)
    136       return NULL;
    137   }
    138 
    139   // Ensure the selected browser is tabbed.
    140   if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
    141     browser = chrome::FindTabbedBrowser(function->GetProfile(),
    142                                         function->include_incognito(),
    143                                         browser->host_desktop_type());
    144 
    145   if (!browser || !browser->window()) {
    146     // TODO(rpaquay): Error message?
    147     return NULL;
    148   }
    149 
    150   // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
    151   // represents the active tab.
    152   WebContents* opener = NULL;
    153   if (params.opener_tab_id.get()) {
    154     int opener_id = *params.opener_tab_id;
    155 
    156     if (!ExtensionTabUtil::GetTabById(opener_id,
    157                                       function->GetProfile(),
    158                                       function->include_incognito(),
    159                                       NULL,
    160                                       NULL,
    161                                       &opener,
    162                                       NULL)) {
    163       // TODO(rpaquay): Error message?
    164       return NULL;
    165     }
    166   }
    167 
    168   // TODO(rafaelw): handle setting remaining tab properties:
    169   // -title
    170   // -favIconUrl
    171 
    172   GURL url;
    173   if (params.url.get()) {
    174     std::string url_string= *params.url;
    175     url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
    176                                                        function->extension());
    177     if (!url.is_valid()) {
    178       *error =
    179           ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string);
    180       return NULL;
    181     }
    182   } else {
    183     url = GURL(chrome::kChromeUINewTabURL);
    184   }
    185 
    186   // Don't let extensions crash the browser or renderers.
    187   if (ExtensionTabUtil::IsCrashURL(url)) {
    188     *error = keys::kNoCrashBrowserError;
    189     return NULL;
    190   }
    191 
    192   // Default to foreground for the new tab. The presence of 'active' property
    193   // will override this default.
    194   bool active = true;
    195   if (params.active.get())
    196     active = *params.active;
    197 
    198   // Default to not pinning the tab. Setting the 'pinned' property to true
    199   // will override this default.
    200   bool pinned = false;
    201   if (params.pinned.get())
    202     pinned = *params.pinned;
    203 
    204   // We can't load extension URLs into incognito windows unless the extension
    205   // uses split mode. Special case to fall back to a tabbed window.
    206   if (url.SchemeIs(kExtensionScheme) &&
    207       !IncognitoInfo::IsSplitMode(function->extension()) &&
    208       browser->profile()->IsOffTheRecord()) {
    209     Profile* profile = browser->profile()->GetOriginalProfile();
    210     chrome::HostDesktopType desktop_type = browser->host_desktop_type();
    211 
    212     browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
    213     if (!browser) {
    214       browser = new Browser(
    215           Browser::CreateParams(Browser::TYPE_TABBED, profile, desktop_type));
    216       browser->window()->Show();
    217     }
    218   }
    219 
    220   // If index is specified, honor the value, but keep it bound to
    221   // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
    222   int index = -1;
    223   if (params.index.get())
    224     index = *params.index;
    225 
    226   TabStripModel* tab_strip = browser->tab_strip_model();
    227 
    228   index = std::min(std::max(index, -1), tab_strip->count());
    229 
    230   int add_types = active ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE;
    231   add_types |= TabStripModel::ADD_FORCE_INDEX;
    232   if (pinned)
    233     add_types |= TabStripModel::ADD_PINNED;
    234   chrome::NavigateParams navigate_params(
    235       browser, url, ui::PAGE_TRANSITION_LINK);
    236   navigate_params.disposition =
    237       active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
    238   navigate_params.tabstrip_index = index;
    239   navigate_params.tabstrip_add_types = add_types;
    240   chrome::Navigate(&navigate_params);
    241 
    242   // The tab may have been created in a different window, so make sure we look
    243   // at the right tab strip.
    244   tab_strip = navigate_params.browser->tab_strip_model();
    245   int new_index =
    246       tab_strip->GetIndexOfWebContents(navigate_params.target_contents);
    247   if (opener)
    248     tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
    249 
    250   if (active)
    251     navigate_params.target_contents->SetInitialFocus();
    252 
    253   // Return data about the newly created tab.
    254   return ExtensionTabUtil::CreateTabValue(navigate_params.target_contents,
    255                                           tab_strip,
    256                                           new_index,
    257                                           function->extension());
    258 }
    259 
    260 Browser* ExtensionTabUtil::GetBrowserFromWindowID(
    261     ChromeUIThreadExtensionFunction* function,
    262     int window_id,
    263     std::string* error) {
    264   if (window_id == extension_misc::kCurrentWindowId) {
    265     Browser* result = function->GetCurrentBrowser();
    266     if (!result || !result->window()) {
    267       if (error)
    268         *error = keys::kNoCurrentWindowError;
    269       return NULL;
    270     }
    271     return result;
    272   } else {
    273     return GetBrowserInProfileWithId(function->GetProfile(),
    274                                      window_id,
    275                                      function->include_incognito(),
    276                                      error);
    277   }
    278 }
    279 
    280 Browser* ExtensionTabUtil::GetBrowserFromWindowID(
    281     const ChromeExtensionFunctionDetails& details,
    282     int window_id,
    283     std::string* error) {
    284   if (window_id == extension_misc::kCurrentWindowId) {
    285     Browser* result = details.GetCurrentBrowser();
    286     if (!result || !result->window()) {
    287       if (error)
    288         *error = keys::kNoCurrentWindowError;
    289       return NULL;
    290     }
    291     return result;
    292   } else {
    293     return GetBrowserInProfileWithId(details.GetProfile(),
    294                                      window_id,
    295                                      details.function()->include_incognito(),
    296                                      error);
    297   }
    298 }
    299 
    300 int ExtensionTabUtil::GetWindowId(const Browser* browser) {
    301   return browser->session_id().id();
    302 }
    303 
    304 int ExtensionTabUtil::GetWindowIdOfTabStripModel(
    305     const TabStripModel* tab_strip_model) {
    306   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    307     if (it->tab_strip_model() == tab_strip_model)
    308       return GetWindowId(*it);
    309   }
    310   return -1;
    311 }
    312 
    313 int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
    314   return SessionTabHelper::IdForTab(web_contents);
    315 }
    316 
    317 std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
    318   return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
    319 }
    320 
    321 int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
    322   return SessionTabHelper::IdForWindowContainingTab(web_contents);
    323 }
    324 
    325 base::DictionaryValue* ExtensionTabUtil::CreateTabValue(
    326     WebContents* contents,
    327     TabStripModel* tab_strip,
    328     int tab_index,
    329     const Extension* extension) {
    330   // If we have a matching AppWindow with a controller, get the tab value
    331   // from its controller instead.
    332   WindowController* controller = GetAppWindowController(contents);
    333   if (controller &&
    334       (!extension || controller->IsVisibleToExtension(extension))) {
    335     return controller->CreateTabValue(extension, tab_index);
    336   }
    337   base::DictionaryValue* result =
    338       CreateTabValue(contents, tab_strip, tab_index);
    339   ScrubTabValueForExtension(contents, extension, result);
    340   return result;
    341 }
    342 
    343 base::ListValue* ExtensionTabUtil::CreateTabList(
    344     const Browser* browser,
    345     const Extension* extension) {
    346   base::ListValue* tab_list = new base::ListValue();
    347   TabStripModel* tab_strip = browser->tab_strip_model();
    348   for (int i = 0; i < tab_strip->count(); ++i) {
    349     tab_list->Append(CreateTabValue(tab_strip->GetWebContentsAt(i),
    350                                     tab_strip,
    351                                     i,
    352                                     extension));
    353   }
    354 
    355   return tab_list;
    356 }
    357 
    358 base::DictionaryValue* ExtensionTabUtil::CreateTabValue(
    359     WebContents* contents,
    360     TabStripModel* tab_strip,
    361     int tab_index) {
    362   // If we have a matching AppWindow with a controller, get the tab value
    363   // from its controller instead.
    364   WindowController* controller = GetAppWindowController(contents);
    365   if (controller)
    366     return controller->CreateTabValue(NULL, tab_index);
    367 
    368   if (!tab_strip)
    369     ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index);
    370 
    371   base::DictionaryValue* result = new base::DictionaryValue();
    372   bool is_loading = contents->IsLoading();
    373   result->SetInteger(keys::kIdKey, GetTabId(contents));
    374   result->SetInteger(keys::kIndexKey, tab_index);
    375   result->SetInteger(keys::kWindowIdKey, GetWindowIdOfTab(contents));
    376   result->SetString(keys::kStatusKey, GetTabStatusText(is_loading));
    377   result->SetBoolean(keys::kActiveKey,
    378                      tab_strip && tab_index == tab_strip->active_index());
    379   result->SetBoolean(keys::kSelectedKey,
    380                      tab_strip && tab_index == tab_strip->active_index());
    381   result->SetBoolean(keys::kHighlightedKey,
    382                    tab_strip && tab_strip->IsTabSelected(tab_index));
    383   result->SetBoolean(keys::kPinnedKey,
    384                      tab_strip && tab_strip->IsTabPinned(tab_index));
    385   result->SetBoolean(keys::kIncognitoKey,
    386                      contents->GetBrowserContext()->IsOffTheRecord());
    387   result->SetInteger(keys::kWidthKey,
    388                      contents->GetContainerBounds().size().width());
    389   result->SetInteger(keys::kHeightKey,
    390                      contents->GetContainerBounds().size().height());
    391 
    392   // Privacy-sensitive fields: these should be stripped off by
    393   // ScrubTabValueForExtension if the extension should not see them.
    394   result->SetString(keys::kUrlKey, contents->GetURL().spec());
    395   result->SetString(keys::kTitleKey, contents->GetTitle());
    396   if (!is_loading) {
    397     NavigationEntry* entry = contents->GetController().GetVisibleEntry();
    398     if (entry && entry->GetFavicon().valid)
    399       result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec());
    400   }
    401 
    402   if (tab_strip) {
    403     WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
    404     if (opener)
    405       result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener));
    406   }
    407 
    408   return result;
    409 }
    410 
    411 void ExtensionTabUtil::ScrubTabValueForExtension(
    412     WebContents* contents,
    413     const Extension* extension,
    414     base::DictionaryValue* tab_info) {
    415   bool has_permission = extension &&
    416                         extension->permissions_data()->HasAPIPermissionForTab(
    417                             GetTabId(contents), APIPermission::kTab);
    418 
    419   if (!has_permission) {
    420     tab_info->Remove(keys::kUrlKey, NULL);
    421     tab_info->Remove(keys::kTitleKey, NULL);
    422     tab_info->Remove(keys::kFaviconUrlKey, NULL);
    423   }
    424 }
    425 
    426 void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension,
    427                                             api::tabs::Tab* tab) {
    428   bool has_permission =
    429       extension &&
    430       extension->permissions_data()->HasAPIPermission(APIPermission::kTab);
    431 
    432   if (!has_permission) {
    433     tab->url.reset();
    434     tab->title.reset();
    435     tab->fav_icon_url.reset();
    436   }
    437 }
    438 
    439 bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents,
    440                                         TabStripModel** tab_strip_model,
    441                                         int* tab_index) {
    442   DCHECK(web_contents);
    443   DCHECK(tab_strip_model);
    444   DCHECK(tab_index);
    445 
    446   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    447     TabStripModel* tab_strip = it->tab_strip_model();
    448     int index = tab_strip->GetIndexOfWebContents(web_contents);
    449     if (index != -1) {
    450       *tab_strip_model = tab_strip;
    451       *tab_index = index;
    452       return true;
    453     }
    454   }
    455 
    456   return false;
    457 }
    458 
    459 bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
    460                                      WebContents** contents,
    461                                      int* tab_id) {
    462   DCHECK(browser);
    463   DCHECK(contents);
    464 
    465   *contents = browser->tab_strip_model()->GetActiveWebContents();
    466   if (*contents) {
    467     if (tab_id)
    468       *tab_id = GetTabId(*contents);
    469     return true;
    470   }
    471 
    472   return false;
    473 }
    474 
    475 bool ExtensionTabUtil::GetTabById(int tab_id,
    476                                   content::BrowserContext* browser_context,
    477                                   bool include_incognito,
    478                                   Browser** browser,
    479                                   TabStripModel** tab_strip,
    480                                   WebContents** contents,
    481                                   int* tab_index) {
    482   Profile* profile = Profile::FromBrowserContext(browser_context);
    483   Profile* incognito_profile =
    484       include_incognito && profile->HasOffTheRecordProfile() ?
    485           profile->GetOffTheRecordProfile() : NULL;
    486   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    487     Browser* target_browser = *it;
    488     if (target_browser->profile() == profile ||
    489         target_browser->profile() == incognito_profile) {
    490       TabStripModel* target_tab_strip = target_browser->tab_strip_model();
    491       for (int i = 0; i < target_tab_strip->count(); ++i) {
    492         WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
    493         if (SessionTabHelper::IdForTab(target_contents) == tab_id) {
    494           if (browser)
    495             *browser = target_browser;
    496           if (tab_strip)
    497             *tab_strip = target_tab_strip;
    498           if (contents)
    499             *contents = target_contents;
    500           if (tab_index)
    501             *tab_index = i;
    502           return true;
    503         }
    504       }
    505     }
    506   }
    507   return false;
    508 }
    509 
    510 GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string,
    511                                                   const Extension* extension) {
    512   GURL url = GURL(url_string);
    513   if (!url.is_valid())
    514     url = extension->GetResourceURL(url_string);
    515 
    516   return url;
    517 }
    518 
    519 bool ExtensionTabUtil::IsCrashURL(const GURL& url) {
    520   // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
    521   GURL fixed_url =
    522       url_fixer::FixupURL(url.possibly_invalid_spec(), std::string());
    523   return (fixed_url.SchemeIs(content::kChromeUIScheme) &&
    524           (fixed_url.host() == content::kChromeUIBrowserCrashHost ||
    525            fixed_url.host() == chrome::kChromeUICrashHost));
    526 }
    527 
    528 void ExtensionTabUtil::CreateTab(WebContents* web_contents,
    529                                  const std::string& extension_id,
    530                                  WindowOpenDisposition disposition,
    531                                  const gfx::Rect& initial_pos,
    532                                  bool user_gesture) {
    533   Profile* profile =
    534       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    535   chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
    536   Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop);
    537   const bool browser_created = !browser;
    538   if (!browser)
    539     browser = new Browser(Browser::CreateParams(profile, active_desktop));
    540   chrome::NavigateParams params(browser, web_contents);
    541 
    542   // The extension_app_id parameter ends up as app_name in the Browser
    543   // which causes the Browser to return true for is_app().  This affects
    544   // among other things, whether the location bar gets displayed.
    545   // TODO(mpcomplete): This seems wrong. What if the extension content is hosted
    546   // in a tab?
    547   if (disposition == NEW_POPUP)
    548     params.extension_app_id = extension_id;
    549 
    550   params.disposition = disposition;
    551   params.window_bounds = initial_pos;
    552   params.window_action = chrome::NavigateParams::SHOW_WINDOW;
    553   params.user_gesture = user_gesture;
    554   chrome::Navigate(&params);
    555 
    556   // Close the browser if chrome::Navigate created a new one.
    557   if (browser_created && (browser != params.browser))
    558     browser->window()->Close();
    559 }
    560 
    561 // static
    562 void ExtensionTabUtil::ForEachTab(
    563     const base::Callback<void(WebContents*)>& callback) {
    564   for (TabContentsIterator iterator; !iterator.done(); iterator.Next())
    565     callback.Run(*iterator);
    566 }
    567 
    568 // static
    569 WindowController* ExtensionTabUtil::GetWindowControllerOfTab(
    570     const WebContents* web_contents) {
    571   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
    572   if (browser != NULL)
    573     return browser->extension_window_controller();
    574 
    575   return NULL;
    576 }
    577 
    578 void ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
    579                                        Browser* browser) {
    580   DCHECK(OptionsPageInfo::HasOptionsPage(extension));
    581 
    582   // Force the options page to open in non-OTR window, because it won't be
    583   // able to save settings from OTR.
    584   scoped_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
    585   if (browser->profile()->IsOffTheRecord()) {
    586     displayer.reset(new chrome::ScopedTabbedBrowserDisplayer(
    587         browser->profile()->GetOriginalProfile(),
    588         browser->host_desktop_type()));
    589     browser = displayer->browser();
    590   }
    591 
    592   if (!OptionsPageInfo::ShouldOpenInTab(extension)) {
    593     // If we should embed the options page for this extension, open
    594     // chrome://extensions in a new tab and show the extension options in an
    595     // embedded popup.
    596     chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
    597         browser, GURL(chrome::kChromeUIExtensionsURL)));
    598     params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
    599 
    600     GURL::Replacements replacements;
    601     std::string query =
    602         base::StringPrintf("options=%s", extension->id().c_str());
    603     replacements.SetQueryStr(query);
    604     params.url = params.url.ReplaceComponents(replacements);
    605 
    606     chrome::ShowSingletonTabOverwritingNTP(browser, params);
    607   } else {
    608     // Otherwise open a new tab with the extension's options page
    609     content::OpenURLParams params(OptionsPageInfo::GetOptionsPage(extension),
    610                                   content::Referrer(),
    611                                   SINGLETON_TAB,
    612                                   ui::PAGE_TRANSITION_LINK,
    613                                   false);
    614     browser->OpenURL(params);
    615     browser->window()->Show();
    616     WebContents* web_contents =
    617         browser->tab_strip_model()->GetActiveWebContents();
    618     web_contents->GetDelegate()->ActivateContents(web_contents);
    619   }
    620 }
    621 
    622 }  // namespace extensions
    623