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(¶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 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