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 "apps/shell_window.h"
      8 #include "apps/shell_window_registry.h"
      9 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
     10 #include "chrome/browser/extensions/tab_helper.h"
     11 #include "chrome/browser/extensions/window_controller.h"
     12 #include "chrome/browser/extensions/window_controller_list.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/sessions/session_id.h"
     15 #include "chrome/browser/ui/browser.h"
     16 #include "chrome/browser/ui/browser_finder.h"
     17 #include "chrome/browser/ui/browser_iterator.h"
     18 #include "chrome/browser/ui/browser_window.h"
     19 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
     20 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
     21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     22 #include "chrome/common/extensions/manifest_url_handler.h"
     23 #include "chrome/common/net/url_fixer_upper.h"
     24 #include "chrome/common/url_constants.h"
     25 #include "content/public/browser/favicon_status.h"
     26 #include "content/public/browser/navigation_entry.h"
     27 #include "content/public/browser/web_contents.h"
     28 #include "content/public/browser/web_contents_view.h"
     29 #include "extensions/common/extension.h"
     30 #include "extensions/common/manifest_constants.h"
     31 #include "extensions/common/permissions/api_permission.h"
     32 #include "extensions/common/permissions/permissions_data.h"
     33 #include "url/gurl.h"
     34 
     35 using apps::ShellWindow;
     36 using content::NavigationEntry;
     37 using content::WebContents;
     38 
     39 namespace extensions {
     40 
     41 namespace {
     42 
     43 namespace keys = tabs_constants;
     44 
     45 WindowController* GetShellWindowController(const WebContents* contents) {
     46   Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
     47   apps::ShellWindowRegistry* registry =
     48       apps::ShellWindowRegistry::Get(profile);
     49   if (!registry)
     50     return NULL;
     51   ShellWindow* shell_window =
     52       registry->GetShellWindowForRenderViewHost(contents->GetRenderViewHost());
     53   if (!shell_window)
     54     return NULL;
     55   return WindowControllerList::GetInstance()->
     56       FindWindowById(shell_window->session_id().id());
     57 }
     58 
     59 }  // namespace
     60 
     61 int ExtensionTabUtil::GetWindowId(const Browser* browser) {
     62   return browser->session_id().id();
     63 }
     64 
     65 int ExtensionTabUtil::GetWindowIdOfTabStripModel(
     66     const TabStripModel* tab_strip_model) {
     67   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
     68     if (it->tab_strip_model() == tab_strip_model)
     69       return GetWindowId(*it);
     70   }
     71   return -1;
     72 }
     73 
     74 int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
     75   return SessionID::IdForTab(web_contents);
     76 }
     77 
     78 std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
     79   return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
     80 }
     81 
     82 int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
     83   return SessionID::IdForWindowContainingTab(web_contents);
     84 }
     85 
     86 DictionaryValue* ExtensionTabUtil::CreateTabValue(
     87     const WebContents* contents,
     88     TabStripModel* tab_strip,
     89     int tab_index,
     90     const Extension* extension) {
     91   // If we have a matching ShellWindow with a controller, get the tab value
     92   // from its controller instead.
     93   WindowController* controller = GetShellWindowController(contents);
     94   if (controller &&
     95       (!extension || controller->IsVisibleToExtension(extension))) {
     96     return controller->CreateTabValue(extension, tab_index);
     97   }
     98   DictionaryValue *result = CreateTabValue(contents, tab_strip, tab_index);
     99   ScrubTabValueForExtension(contents, extension, result);
    100   return result;
    101 }
    102 
    103 base::ListValue* ExtensionTabUtil::CreateTabList(
    104     const Browser* browser,
    105     const Extension* extension) {
    106   base::ListValue* tab_list = new base::ListValue();
    107   TabStripModel* tab_strip = browser->tab_strip_model();
    108   for (int i = 0; i < tab_strip->count(); ++i) {
    109     tab_list->Append(CreateTabValue(tab_strip->GetWebContentsAt(i),
    110                                     tab_strip,
    111                                     i,
    112                                     extension));
    113   }
    114 
    115   return tab_list;
    116 }
    117 
    118 DictionaryValue* ExtensionTabUtil::CreateTabValue(
    119     const WebContents* contents,
    120     TabStripModel* tab_strip,
    121     int tab_index) {
    122   // If we have a matching ShellWindow with a controller, get the tab value
    123   // from its controller instead.
    124   WindowController* controller = GetShellWindowController(contents);
    125   if (controller)
    126     return controller->CreateTabValue(NULL, tab_index);
    127 
    128   if (!tab_strip)
    129     ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index);
    130 
    131   DictionaryValue* result = new DictionaryValue();
    132   bool is_loading = contents->IsLoading();
    133   result->SetInteger(keys::kIdKey, GetTabId(contents));
    134   result->SetInteger(keys::kIndexKey, tab_index);
    135   result->SetInteger(keys::kWindowIdKey, GetWindowIdOfTab(contents));
    136   result->SetString(keys::kStatusKey, GetTabStatusText(is_loading));
    137   result->SetBoolean(keys::kActiveKey,
    138                      tab_strip && tab_index == tab_strip->active_index());
    139   result->SetBoolean(keys::kSelectedKey,
    140                      tab_strip && tab_index == tab_strip->active_index());
    141   result->SetBoolean(keys::kHighlightedKey,
    142                    tab_strip && tab_strip->IsTabSelected(tab_index));
    143   result->SetBoolean(keys::kPinnedKey,
    144                      tab_strip && tab_strip->IsTabPinned(tab_index));
    145   result->SetBoolean(keys::kIncognitoKey,
    146                      contents->GetBrowserContext()->IsOffTheRecord());
    147   result->SetInteger(keys::kWidthKey,
    148                      contents->GetView()->GetContainerSize().width());
    149   result->SetInteger(keys::kHeightKey,
    150                      contents->GetView()->GetContainerSize().height());
    151 
    152   // Privacy-sensitive fields: these should be stripped off by
    153   // ScrubTabValueForExtension if the extension should not see them.
    154   result->SetString(keys::kUrlKey, contents->GetURL().spec());
    155   result->SetString(keys::kTitleKey, contents->GetTitle());
    156   if (!is_loading) {
    157     NavigationEntry* entry = contents->GetController().GetVisibleEntry();
    158     if (entry && entry->GetFavicon().valid)
    159       result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec());
    160   }
    161 
    162   if (tab_strip) {
    163     WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
    164     if (opener)
    165       result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener));
    166   }
    167 
    168   return result;
    169 }
    170 
    171 void ExtensionTabUtil::ScrubTabValueForExtension(const WebContents* contents,
    172                                                  const Extension* extension,
    173                                                  DictionaryValue* tab_info) {
    174   bool has_permission =
    175       extension &&
    176       PermissionsData::HasAPIPermissionForTab(
    177           extension, GetTabId(contents), APIPermission::kTab);
    178 
    179   if (!has_permission) {
    180     tab_info->Remove(keys::kUrlKey, NULL);
    181     tab_info->Remove(keys::kTitleKey, NULL);
    182     tab_info->Remove(keys::kFaviconUrlKey, NULL);
    183   }
    184 }
    185 
    186 void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension,
    187                                             api::tabs::Tab* tab) {
    188   bool has_permission = extension && extension->HasAPIPermission(
    189       APIPermission::kTab);
    190 
    191   if (!has_permission) {
    192     tab->url.reset();
    193     tab->title.reset();
    194     tab->fav_icon_url.reset();
    195   }
    196 }
    197 
    198 bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents,
    199                                         TabStripModel** tab_strip_model,
    200                                         int* tab_index) {
    201   DCHECK(web_contents);
    202   DCHECK(tab_strip_model);
    203   DCHECK(tab_index);
    204 
    205   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    206     TabStripModel* tab_strip = it->tab_strip_model();
    207     int index = tab_strip->GetIndexOfWebContents(web_contents);
    208     if (index != -1) {
    209       *tab_strip_model = tab_strip;
    210       *tab_index = index;
    211       return true;
    212     }
    213   }
    214 
    215   return false;
    216 }
    217 
    218 bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
    219                                      WebContents** contents,
    220                                      int* tab_id) {
    221   DCHECK(browser);
    222   DCHECK(contents);
    223 
    224   *contents = browser->tab_strip_model()->GetActiveWebContents();
    225   if (*contents) {
    226     if (tab_id)
    227       *tab_id = GetTabId(*contents);
    228     return true;
    229   }
    230 
    231   return false;
    232 }
    233 
    234 bool ExtensionTabUtil::GetTabById(int tab_id,
    235                                   Profile* profile,
    236                                   bool include_incognito,
    237                                   Browser** browser,
    238                                   TabStripModel** tab_strip,
    239                                   WebContents** contents,
    240                                   int* tab_index) {
    241   Profile* incognito_profile =
    242       include_incognito && profile->HasOffTheRecordProfile() ?
    243           profile->GetOffTheRecordProfile() : NULL;
    244   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    245     Browser* target_browser = *it;
    246     if (target_browser->profile() == profile ||
    247         target_browser->profile() == incognito_profile) {
    248       TabStripModel* target_tab_strip = target_browser->tab_strip_model();
    249       for (int i = 0; i < target_tab_strip->count(); ++i) {
    250         WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
    251         if (SessionID::IdForTab(target_contents) == tab_id) {
    252           if (browser)
    253             *browser = target_browser;
    254           if (tab_strip)
    255             *tab_strip = target_tab_strip;
    256           if (contents)
    257             *contents = target_contents;
    258           if (tab_index)
    259             *tab_index = i;
    260           return true;
    261         }
    262       }
    263     }
    264   }
    265   return false;
    266 }
    267 
    268 GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string,
    269                                                   const Extension* extension) {
    270   GURL url = GURL(url_string);
    271   if (!url.is_valid())
    272     url = extension->GetResourceURL(url_string);
    273 
    274   return url;
    275 }
    276 
    277 bool ExtensionTabUtil::IsCrashURL(const GURL& url) {
    278   // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
    279   GURL fixed_url =
    280       URLFixerUpper::FixupURL(url.possibly_invalid_spec(), std::string());
    281   return (fixed_url.SchemeIs(chrome::kChromeUIScheme) &&
    282           (fixed_url.host() == content::kChromeUIBrowserCrashHost ||
    283            fixed_url.host() == chrome::kChromeUICrashHost));
    284 }
    285 
    286 void ExtensionTabUtil::CreateTab(WebContents* web_contents,
    287                                  const std::string& extension_id,
    288                                  WindowOpenDisposition disposition,
    289                                  const gfx::Rect& initial_pos,
    290                                  bool user_gesture) {
    291   Profile* profile =
    292       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    293   chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
    294   Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop);
    295   const bool browser_created = !browser;
    296   if (!browser)
    297     browser = new Browser(Browser::CreateParams(profile, active_desktop));
    298   chrome::NavigateParams params(browser, web_contents);
    299 
    300   // The extension_app_id parameter ends up as app_name in the Browser
    301   // which causes the Browser to return true for is_app().  This affects
    302   // among other things, whether the location bar gets displayed.
    303   // TODO(mpcomplete): This seems wrong. What if the extension content is hosted
    304   // in a tab?
    305   if (disposition == NEW_POPUP)
    306     params.extension_app_id = extension_id;
    307 
    308   params.disposition = disposition;
    309   params.window_bounds = initial_pos;
    310   params.window_action = chrome::NavigateParams::SHOW_WINDOW;
    311   params.user_gesture = user_gesture;
    312   chrome::Navigate(&params);
    313 
    314   // Close the browser if chrome::Navigate created a new one.
    315   if (browser_created && (browser != params.browser))
    316     browser->window()->Close();
    317 }
    318 
    319 // static
    320 void ExtensionTabUtil::ForEachTab(
    321     const base::Callback<void(WebContents*)>& callback) {
    322   for (TabContentsIterator iterator; !iterator.done(); iterator.Next())
    323     callback.Run(*iterator);
    324 }
    325 
    326 // static
    327 WindowController* ExtensionTabUtil::GetWindowControllerOfTab(
    328     const WebContents* web_contents) {
    329   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
    330   if (browser != NULL)
    331     return browser->extension_window_controller();
    332 
    333   return NULL;
    334 }
    335 
    336 void ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
    337                                        Browser* browser) {
    338   DCHECK(!ManifestURL::GetOptionsPage(extension).is_empty());
    339 
    340   // Force the options page to open in non-OTR window, because it won't be
    341   // able to save settings from OTR.
    342   scoped_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
    343   if (browser->profile()->IsOffTheRecord()) {
    344     displayer.reset(new chrome::ScopedTabbedBrowserDisplayer(
    345         browser->profile()->GetOriginalProfile(),
    346         browser->host_desktop_type()));
    347     browser = displayer->browser();
    348   }
    349 
    350   content::OpenURLParams params(ManifestURL::GetOptionsPage(extension),
    351                                 content::Referrer(),
    352                                 SINGLETON_TAB,
    353                                 content::PAGE_TRANSITION_LINK,
    354                                 false);
    355   browser->OpenURL(params);
    356   browser->window()->Show();
    357   WebContents* web_contents =
    358       browser->tab_strip_model()->GetActiveWebContents();
    359   web_contents->GetDelegate()->ActivateContents(web_contents);
    360 }
    361 
    362 }  // namespace extensions
    363