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