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