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/tab_contents/tab_contents_iterator.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/common/extensions/extension.h"
     22 #include "chrome/common/extensions/extension_manifest_constants.h"
     23 #include "chrome/common/extensions/manifest_url_handler.h"
     24 #include "chrome/common/extensions/permissions/api_permission.h"
     25 #include "chrome/common/extensions/permissions/permissions_data.h"
     26 #include "chrome/common/net/url_fixer_upper.h"
     27 #include "chrome/common/url_constants.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 "url/gurl.h"
     32 
     33 namespace keys = extensions::tabs_constants;
     34 namespace tabs = extensions::api::tabs;
     35 
     36 using apps::ShellWindow;
     37 using content::NavigationEntry;
     38 using content::WebContents;
     39 using extensions::APIPermission;
     40 using extensions::Extension;
     41 
     42 namespace {
     43 
     44 extensions::WindowController* GetShellWindowController(
     45     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 extensions::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   extensions::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   extensions::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 
    148   // Privacy-sensitive fields: these should be stripped off by
    149   // ScrubTabValueForExtension if the extension should not see them.
    150   result->SetString(keys::kUrlKey, contents->GetURL().spec());
    151   result->SetString(keys::kTitleKey, contents->GetTitle());
    152   if (!is_loading) {
    153     NavigationEntry* entry = contents->GetController().GetActiveEntry();
    154     if (entry && entry->GetFavicon().valid)
    155       result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec());
    156   }
    157 
    158   if (tab_strip) {
    159     WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
    160     if (opener)
    161       result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener));
    162   }
    163 
    164   return result;
    165 }
    166 
    167 void ExtensionTabUtil::ScrubTabValueForExtension(const WebContents* contents,
    168                                                  const Extension* extension,
    169                                                  DictionaryValue* tab_info) {
    170   bool has_permission =
    171       extension &&
    172       extensions::PermissionsData::HasAPIPermissionForTab(
    173           extension, GetTabId(contents), APIPermission::kTab);
    174 
    175   if (!has_permission) {
    176     tab_info->Remove(keys::kUrlKey, NULL);
    177     tab_info->Remove(keys::kTitleKey, NULL);
    178     tab_info->Remove(keys::kFaviconUrlKey, NULL);
    179   }
    180 }
    181 
    182 void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension,
    183                                             tabs::Tab* tab) {
    184   bool has_permission = extension && extension->HasAPIPermission(
    185       APIPermission::kTab);
    186 
    187   if (!has_permission) {
    188     tab->url.reset();
    189     tab->title.reset();
    190     tab->fav_icon_url.reset();
    191   }
    192 }
    193 
    194 bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents,
    195                                         TabStripModel** tab_strip_model,
    196                                         int* tab_index) {
    197   DCHECK(web_contents);
    198   DCHECK(tab_strip_model);
    199   DCHECK(tab_index);
    200 
    201   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    202     TabStripModel* tab_strip = it->tab_strip_model();
    203     int index = tab_strip->GetIndexOfWebContents(web_contents);
    204     if (index != -1) {
    205       *tab_strip_model = tab_strip;
    206       *tab_index = index;
    207       return true;
    208     }
    209   }
    210 
    211   return false;
    212 }
    213 
    214 bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
    215                                      WebContents** contents,
    216                                      int* tab_id) {
    217   DCHECK(browser);
    218   DCHECK(contents);
    219 
    220   *contents = browser->tab_strip_model()->GetActiveWebContents();
    221   if (*contents) {
    222     if (tab_id)
    223       *tab_id = GetTabId(*contents);
    224     return true;
    225   }
    226 
    227   return false;
    228 }
    229 
    230 bool ExtensionTabUtil::GetTabById(int tab_id,
    231                                   Profile* profile,
    232                                   bool include_incognito,
    233                                   Browser** browser,
    234                                   TabStripModel** tab_strip,
    235                                   WebContents** contents,
    236                                   int* tab_index) {
    237   Profile* incognito_profile =
    238       include_incognito && profile->HasOffTheRecordProfile() ?
    239           profile->GetOffTheRecordProfile() : NULL;
    240   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    241     Browser* target_browser = *it;
    242     if (target_browser->profile() == profile ||
    243         target_browser->profile() == incognito_profile) {
    244       TabStripModel* target_tab_strip = target_browser->tab_strip_model();
    245       for (int i = 0; i < target_tab_strip->count(); ++i) {
    246         WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
    247         if (SessionID::IdForTab(target_contents) == tab_id) {
    248           if (browser)
    249             *browser = target_browser;
    250           if (tab_strip)
    251             *tab_strip = target_tab_strip;
    252           if (contents)
    253             *contents = target_contents;
    254           if (tab_index)
    255             *tab_index = i;
    256           return true;
    257         }
    258       }
    259     }
    260   }
    261   return false;
    262 }
    263 
    264 GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string,
    265     const extensions::Extension* extension) {
    266   GURL url = GURL(url_string);
    267   if (!url.is_valid())
    268     url = extension->GetResourceURL(url_string);
    269 
    270   return url;
    271 }
    272 
    273 bool ExtensionTabUtil::IsCrashURL(const GURL& url) {
    274   // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
    275   GURL fixed_url =
    276       URLFixerUpper::FixupURL(url.possibly_invalid_spec(), std::string());
    277   return (fixed_url.SchemeIs(chrome::kChromeUIScheme) &&
    278           (fixed_url.host() == content::kChromeUIBrowserCrashHost ||
    279            fixed_url.host() == chrome::kChromeUICrashHost));
    280 }
    281 
    282 void ExtensionTabUtil::CreateTab(WebContents* web_contents,
    283                                  const std::string& extension_id,
    284                                  WindowOpenDisposition disposition,
    285                                  const gfx::Rect& initial_pos,
    286                                  bool user_gesture) {
    287   Profile* profile =
    288       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    289   chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
    290   Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop);
    291   const bool browser_created = !browser;
    292   if (!browser)
    293     browser = new Browser(Browser::CreateParams(profile, active_desktop));
    294   chrome::NavigateParams params(browser, web_contents);
    295 
    296   // The extension_app_id parameter ends up as app_name in the Browser
    297   // which causes the Browser to return true for is_app().  This affects
    298   // among other things, whether the location bar gets displayed.
    299   // TODO(mpcomplete): This seems wrong. What if the extension content is hosted
    300   // in a tab?
    301   if (disposition == NEW_POPUP)
    302     params.extension_app_id = extension_id;
    303 
    304   params.disposition = disposition;
    305   params.window_bounds = initial_pos;
    306   params.window_action = chrome::NavigateParams::SHOW_WINDOW;
    307   params.user_gesture = user_gesture;
    308   chrome::Navigate(&params);
    309 
    310   // Close the browser if chrome::Navigate created a new one.
    311   if (browser_created && (browser != params.browser))
    312     browser->window()->Close();
    313 }
    314 
    315 // static
    316 void ExtensionTabUtil::ForEachTab(
    317     const base::Callback<void(WebContents*)>& callback) {
    318   for (TabContentsIterator iterator; !iterator.done(); iterator.Next())
    319     callback.Run(*iterator);
    320 }
    321 
    322 // static
    323 extensions::WindowController* ExtensionTabUtil::GetWindowControllerOfTab(
    324     const WebContents* web_contents) {
    325   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
    326   if (browser != NULL)
    327     return browser->extension_window_controller();
    328 
    329   return NULL;
    330 }
    331 
    332 void ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
    333                                        Browser* browser) {
    334   DCHECK(!extensions::ManifestURL::GetOptionsPage(extension).is_empty());
    335 
    336   // Force the options page to open in non-OTR window, because it won't be
    337   // able to save settings from OTR.
    338   if (browser->profile()->IsOffTheRecord()) {
    339     browser = chrome::FindOrCreateTabbedBrowser(
    340         browser->profile()->GetOriginalProfile(), browser->host_desktop_type());
    341   }
    342 
    343   content::OpenURLParams params(
    344       extensions::ManifestURL::GetOptionsPage(extension),
    345       content::Referrer(), SINGLETON_TAB,
    346       content::PAGE_TRANSITION_LINK, false);
    347   browser->OpenURL(params);
    348   browser->window()->Show();
    349   WebContents* web_contents =
    350       browser->tab_strip_model()->GetActiveWebContents();
    351   web_contents->GetDelegate()->ActivateContents(web_contents);
    352 }
    353