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