extension_tab_util.cc revision 558790d6acca3451cf3a6b497803a5f07d0bec58
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 "chrome/browser/extensions/api/tabs/tabs_constants.h" 9#include "chrome/browser/extensions/shell_window_registry.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 33namespace keys = extensions::tabs_constants; 34namespace tabs = extensions::api::tabs; 35 36using apps::ShellWindow; 37using content::NavigationEntry; 38using content::WebContents; 39using extensions::APIPermission; 40using extensions::Extension; 41 42namespace { 43 44extensions::WindowController* GetShellWindowController( 45 const WebContents* contents) { 46 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); 47 extensions::ShellWindowRegistry* registry = 48 extensions::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 61int ExtensionTabUtil::GetWindowId(const Browser* browser) { 62 return browser->session_id().id(); 63} 64 65int 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 74int ExtensionTabUtil::GetTabId(const WebContents* web_contents) { 75 return SessionID::IdForTab(web_contents); 76} 77 78std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) { 79 return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete; 80} 81 82int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) { 83 return SessionID::IdForWindowContainingTab(web_contents); 84} 85 86DictionaryValue* 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 103base::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 118DictionaryValue* 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 167void 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 182void 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 194bool 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 214bool 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 230bool 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 264GURL 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 273bool 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 282void 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(¶ms); 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 316void 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 323extensions::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 332void 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