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