[email protected] | 327640a | 2012-01-24 21:57:59 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 5 | #include "chrome/browser/extensions/extension_tab_util.h" |
[email protected] | 41d9faf | 2012-02-28 23:46:02 | [diff] [blame] | 6 | |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 7 | #include "base/strings/string_number_conversions.h" |
ericzeng | f97b7c2 | 2014-08-26 03:07:30 | [diff] [blame] | 8 | #include "base/strings/stringprintf.h" |
[email protected] | b19451b | 2012-06-08 17:36:19 | [diff] [blame] | 9 | #include "chrome/browser/extensions/api/tabs/tabs_constants.h" |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 10 | #include "chrome/browser/extensions/chrome_extension_function.h" |
[email protected] | e9570fdf | 2012-07-18 20:01:21 | [diff] [blame] | 11 | #include "chrome/browser/extensions/tab_helper.h" |
| 12 | #include "chrome/browser/extensions/window_controller.h" |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 13 | #include "chrome/browser/extensions/window_controller_list.h" |
[email protected] | b56e2e3 | 2012-05-11 21:18:04 | [diff] [blame] | 14 | #include "chrome/browser/profiles/profile.h" |
[email protected] | e3f90c60 | 2014-08-18 12:41:59 | [diff] [blame] | 15 | #include "chrome/browser/sessions/session_tab_helper.h" |
[email protected] | b56e2e3 | 2012-05-11 21:18:04 | [diff] [blame] | 16 | #include "chrome/browser/ui/browser.h" |
[email protected] | 73c1a684 | 2012-07-13 17:39:04 | [diff] [blame] | 17 | #include "chrome/browser/ui/browser_finder.h" |
[email protected] | 3539929f | 2013-02-01 05:59:14 | [diff] [blame] | 18 | #include "chrome/browser/ui/browser_iterator.h" |
[email protected] | b56e2e3 | 2012-05-11 21:18:04 | [diff] [blame] | 19 | #include "chrome/browser/ui/browser_window.h" |
[email protected] | 0edcdec | 2013-10-31 06:43:08 | [diff] [blame] | 20 | #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" |
ericzeng | f97b7c2 | 2014-08-26 03:07:30 | [diff] [blame] | 21 | #include "chrome/browser/ui/singleton_tabs.h" |
[email protected] | 44e329a | 2012-07-14 01:13:06 | [diff] [blame] | 22 | #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
[email protected] | b56e2e3 | 2012-05-11 21:18:04 | [diff] [blame] | 23 | #include "chrome/browser/ui/tabs/tab_strip_model.h" |
[email protected] | 760f603 | 2014-06-30 23:18:19 | [diff] [blame] | 24 | #include "chrome/common/extensions/api/tabs.h" |
[email protected] | 69508978 | 2013-04-09 16:03:17 | [diff] [blame] | 25 | #include "chrome/common/extensions/manifest_url_handler.h" |
[email protected] | 45c75e6 | 2012-03-21 19:56:35 | [diff] [blame] | 26 | #include "chrome/common/url_constants.h" |
[email protected] | 9b5b1d60 | 2014-06-12 14:29:02 | [diff] [blame] | 27 | #include "components/url_fixer/url_fixer.h" |
[email protected] | ad23a09 | 2011-12-28 07:02:04 | [diff] [blame] | 28 | #include "content/public/browser/favicon_status.h" |
| 29 | #include "content/public/browser/navigation_entry.h" |
[email protected] | 6acde635 | 2012-01-04 16:52:20 | [diff] [blame] | 30 | #include "content/public/browser/web_contents.h" |
hashimoto | ad3c687 | 2014-08-29 09:46:57 | [diff] [blame] | 31 | #include "extensions/browser/app_window/app_window.h" |
| 32 | #include "extensions/browser/app_window/app_window_registry.h" |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 33 | #include "extensions/common/constants.h" |
| 34 | #include "extensions/common/error_utils.h" |
[email protected] | e4452d3 | 2013-11-15 23:07:41 | [diff] [blame] | 35 | #include "extensions/common/extension.h" |
ericzeng | f97b7c2 | 2014-08-26 03:07:30 | [diff] [blame] | 36 | #include "extensions/common/feature_switch.h" |
[email protected] | 0c3c973 | 2013-09-16 08:53:41 | [diff] [blame] | 37 | #include "extensions/common/manifest_constants.h" |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 38 | #include "extensions/common/manifest_handlers/incognito_info.h" |
[email protected] | 793964a | 2013-10-08 00:47:19 | [diff] [blame] | 39 | #include "extensions/common/permissions/api_permission.h" |
[email protected] | e4452d3 | 2013-11-15 23:07:41 | [diff] [blame] | 40 | #include "extensions/common/permissions/permissions_data.h" |
[email protected] | a6483d2 | 2013-07-03 22:11:00 | [diff] [blame] | 41 | #include "url/gurl.h" |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 42 | |
[email protected] | 10f417c5 | 2011-12-28 21:04:23 | [diff] [blame] | 43 | using content::NavigationEntry; |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 44 | using content::WebContents; |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 45 | |
| 46 | namespace extensions { |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 47 | |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 48 | namespace { |
| 49 | |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 50 | namespace keys = tabs_constants; |
| 51 | |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 52 | WindowController* GetAppWindowController(const WebContents* contents) { |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 53 | Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); |
hashimoto | ad3c687 | 2014-08-29 09:46:57 | [diff] [blame] | 54 | AppWindowRegistry* registry = AppWindowRegistry::Get(profile); |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 55 | if (!registry) |
| 56 | return NULL; |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 57 | AppWindow* app_window = |
| 58 | registry->GetAppWindowForRenderViewHost(contents->GetRenderViewHost()); |
| 59 | if (!app_window) |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 60 | return NULL; |
[email protected] | 4442454 | 2014-01-29 12:10:31 | [diff] [blame] | 61 | return WindowControllerList::GetInstance()->FindWindowById( |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 62 | app_window->session_id().id()); |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 63 | } |
| 64 | |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 65 | // |error_message| can optionally be passed in and will be set with an |
| 66 | // appropriate message if the window cannot be found by id. |
| 67 | Browser* GetBrowserInProfileWithId(Profile* profile, |
| 68 | const int window_id, |
| 69 | bool include_incognito, |
| 70 | std::string* error_message) { |
| 71 | Profile* incognito_profile = |
| 72 | include_incognito && profile->HasOffTheRecordProfile() |
| 73 | ? profile->GetOffTheRecordProfile() |
| 74 | : NULL; |
| 75 | for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| 76 | Browser* browser = *it; |
| 77 | if ((browser->profile() == profile || |
| 78 | browser->profile() == incognito_profile) && |
| 79 | ExtensionTabUtil::GetWindowId(browser) == window_id && |
| 80 | browser->window()) { |
| 81 | return browser; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | if (error_message) |
| 86 | *error_message = ErrorUtils::FormatErrorMessage( |
| 87 | keys::kWindowNotFoundError, base::IntToString(window_id)); |
| 88 | |
| 89 | return NULL; |
| 90 | } |
| 91 | |
[email protected] | a0c91a9f | 2014-05-03 03:41:43 | [diff] [blame] | 92 | Browser* CreateBrowser(ChromeUIThreadExtensionFunction* function, |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 93 | int window_id, |
| 94 | std::string* error) { |
| 95 | content::WebContents* web_contents = function->GetAssociatedWebContents(); |
| 96 | DCHECK(web_contents); |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 97 | DCHECK(web_contents->GetNativeView()); |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 98 | DCHECK(!chrome::FindBrowserWithWebContents(web_contents)); |
| 99 | |
| 100 | chrome::HostDesktopType desktop_type = |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 101 | chrome::GetHostDesktopTypeForNativeView(web_contents->GetNativeView()); |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 102 | Browser::CreateParams params( |
| 103 | Browser::TYPE_TABBED, function->GetProfile(), desktop_type); |
| 104 | Browser* browser = new Browser(params); |
| 105 | browser->window()->Show(); |
| 106 | return browser; |
| 107 | } |
| 108 | |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 109 | } // namespace |
| 110 | |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 111 | ExtensionTabUtil::OpenTabParams::OpenTabParams() |
| 112 | : create_browser_if_needed(false) { |
| 113 | } |
| 114 | |
| 115 | ExtensionTabUtil::OpenTabParams::~OpenTabParams() { |
| 116 | } |
| 117 | |
| 118 | // Opens a new tab for a given extension. Returns NULL and sets |error| if an |
| 119 | // error occurs. |
| 120 | base::DictionaryValue* ExtensionTabUtil::OpenTab( |
[email protected] | a0c91a9f | 2014-05-03 03:41:43 | [diff] [blame] | 121 | ChromeUIThreadExtensionFunction* function, |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 122 | const OpenTabParams& params, |
| 123 | std::string* error) { |
| 124 | // windowId defaults to "current" window. |
| 125 | int window_id = extension_misc::kCurrentWindowId; |
| 126 | if (params.window_id.get()) |
| 127 | window_id = *params.window_id; |
| 128 | |
| 129 | Browser* browser = GetBrowserFromWindowID(function, window_id, error); |
| 130 | if (!browser) { |
| 131 | if (!params.create_browser_if_needed) { |
| 132 | return NULL; |
| 133 | } |
| 134 | browser = CreateBrowser(function, window_id, error); |
| 135 | if (!browser) |
| 136 | return NULL; |
| 137 | } |
| 138 | |
| 139 | // Ensure the selected browser is tabbed. |
| 140 | if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser()) |
| 141 | browser = chrome::FindTabbedBrowser(function->GetProfile(), |
| 142 | function->include_incognito(), |
| 143 | browser->host_desktop_type()); |
| 144 | |
| 145 | if (!browser || !browser->window()) { |
| 146 | // TODO(rpaquay): Error message? |
| 147 | return NULL; |
| 148 | } |
| 149 | |
| 150 | // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that |
| 151 | // represents the active tab. |
| 152 | WebContents* opener = NULL; |
| 153 | if (params.opener_tab_id.get()) { |
| 154 | int opener_id = *params.opener_tab_id; |
| 155 | |
| 156 | if (!ExtensionTabUtil::GetTabById(opener_id, |
| 157 | function->GetProfile(), |
| 158 | function->include_incognito(), |
| 159 | NULL, |
| 160 | NULL, |
| 161 | &opener, |
| 162 | NULL)) { |
| 163 | // TODO(rpaquay): Error message? |
| 164 | return NULL; |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | // TODO(rafaelw): handle setting remaining tab properties: |
| 169 | // -title |
| 170 | // -favIconUrl |
| 171 | |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 172 | GURL url; |
| 173 | if (params.url.get()) { |
[email protected] | f5ec3c92 | 2014-05-22 15:04:03 | [diff] [blame] | 174 | std::string url_string= *params.url; |
[email protected] | eba8f7d | 2014-07-28 22:09:23 | [diff] [blame] | 175 | url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, |
| 176 | function->extension()); |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 177 | if (!url.is_valid()) { |
| 178 | *error = |
| 179 | ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string); |
| 180 | return NULL; |
| 181 | } |
[email protected] | f5ec3c92 | 2014-05-22 15:04:03 | [diff] [blame] | 182 | } else { |
| 183 | url = GURL(chrome::kChromeUINewTabURL); |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | // Don't let extensions crash the browser or renderers. |
| 187 | if (ExtensionTabUtil::IsCrashURL(url)) { |
| 188 | *error = keys::kNoCrashBrowserError; |
| 189 | return NULL; |
| 190 | } |
| 191 | |
| 192 | // Default to foreground for the new tab. The presence of 'active' property |
| 193 | // will override this default. |
| 194 | bool active = true; |
| 195 | if (params.active.get()) |
| 196 | active = *params.active; |
| 197 | |
| 198 | // Default to not pinning the tab. Setting the 'pinned' property to true |
| 199 | // will override this default. |
| 200 | bool pinned = false; |
| 201 | if (params.pinned.get()) |
| 202 | pinned = *params.pinned; |
| 203 | |
| 204 | // We can't load extension URLs into incognito windows unless the extension |
| 205 | // uses split mode. Special case to fall back to a tabbed window. |
| 206 | if (url.SchemeIs(kExtensionScheme) && |
[email protected] | eba8f7d | 2014-07-28 22:09:23 | [diff] [blame] | 207 | !IncognitoInfo::IsSplitMode(function->extension()) && |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 208 | browser->profile()->IsOffTheRecord()) { |
| 209 | Profile* profile = browser->profile()->GetOriginalProfile(); |
| 210 | chrome::HostDesktopType desktop_type = browser->host_desktop_type(); |
| 211 | |
| 212 | browser = chrome::FindTabbedBrowser(profile, false, desktop_type); |
| 213 | if (!browser) { |
| 214 | browser = new Browser( |
| 215 | Browser::CreateParams(Browser::TYPE_TABBED, profile, desktop_type)); |
| 216 | browser->window()->Show(); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | // If index is specified, honor the value, but keep it bound to |
| 221 | // -1 <= index <= tab_strip->count() where -1 invokes the default behavior. |
| 222 | int index = -1; |
| 223 | if (params.index.get()) |
| 224 | index = *params.index; |
| 225 | |
| 226 | TabStripModel* tab_strip = browser->tab_strip_model(); |
| 227 | |
| 228 | index = std::min(std::max(index, -1), tab_strip->count()); |
| 229 | |
| 230 | int add_types = active ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE; |
| 231 | add_types |= TabStripModel::ADD_FORCE_INDEX; |
| 232 | if (pinned) |
| 233 | add_types |= TabStripModel::ADD_PINNED; |
| 234 | chrome::NavigateParams navigate_params( |
| 235 | browser, url, content::PAGE_TRANSITION_LINK); |
| 236 | navigate_params.disposition = |
| 237 | active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; |
| 238 | navigate_params.tabstrip_index = index; |
| 239 | navigate_params.tabstrip_add_types = add_types; |
| 240 | chrome::Navigate(&navigate_params); |
| 241 | |
| 242 | // The tab may have been created in a different window, so make sure we look |
| 243 | // at the right tab strip. |
| 244 | tab_strip = navigate_params.browser->tab_strip_model(); |
| 245 | int new_index = |
| 246 | tab_strip->GetIndexOfWebContents(navigate_params.target_contents); |
| 247 | if (opener) |
| 248 | tab_strip->SetOpenerOfWebContentsAt(new_index, opener); |
| 249 | |
| 250 | if (active) |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 251 | navigate_params.target_contents->SetInitialFocus(); |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 252 | |
| 253 | // Return data about the newly created tab. |
| 254 | return ExtensionTabUtil::CreateTabValue(navigate_params.target_contents, |
| 255 | tab_strip, |
| 256 | new_index, |
[email protected] | eba8f7d | 2014-07-28 22:09:23 | [diff] [blame] | 257 | function->extension()); |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 258 | } |
| 259 | |
| 260 | Browser* ExtensionTabUtil::GetBrowserFromWindowID( |
[email protected] | a0c91a9f | 2014-05-03 03:41:43 | [diff] [blame] | 261 | ChromeUIThreadExtensionFunction* function, |
[email protected] | 9852830 | 2014-05-02 00:34:08 | [diff] [blame] | 262 | int window_id, |
| 263 | std::string* error) { |
| 264 | if (window_id == extension_misc::kCurrentWindowId) { |
| 265 | Browser* result = function->GetCurrentBrowser(); |
| 266 | if (!result || !result->window()) { |
| 267 | if (error) |
| 268 | *error = keys::kNoCurrentWindowError; |
| 269 | return NULL; |
| 270 | } |
| 271 | return result; |
| 272 | } else { |
| 273 | return GetBrowserInProfileWithId(function->GetProfile(), |
| 274 | window_id, |
| 275 | function->include_incognito(), |
| 276 | error); |
| 277 | } |
| 278 | } |
| 279 | |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 280 | int ExtensionTabUtil::GetWindowId(const Browser* browser) { |
| 281 | return browser->session_id().id(); |
| 282 | } |
| 283 | |
[email protected] | 8c3495c | 2011-09-28 03:32:30 | [diff] [blame] | 284 | int ExtensionTabUtil::GetWindowIdOfTabStripModel( |
| 285 | const TabStripModel* tab_strip_model) { |
[email protected] | 3539929f | 2013-02-01 05:59:14 | [diff] [blame] | 286 | for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| 287 | if (it->tab_strip_model() == tab_strip_model) |
[email protected] | 8c3495c | 2011-09-28 03:32:30 | [diff] [blame] | 288 | return GetWindowId(*it); |
| 289 | } |
| 290 | return -1; |
| 291 | } |
| 292 | |
[email protected] | d2bd5fde | 2014-05-29 02:24:31 | [diff] [blame] | 293 | int ExtensionTabUtil::GetTabId(const WebContents* web_contents) { |
[email protected] | e3f90c60 | 2014-08-18 12:41:59 | [diff] [blame] | 294 | return SessionTabHelper::IdForTab(web_contents); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 295 | } |
| 296 | |
| 297 | std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) { |
| 298 | return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete; |
| 299 | } |
| 300 | |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 301 | int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) { |
[email protected] | e3f90c60 | 2014-08-18 12:41:59 | [diff] [blame] | 302 | return SessionTabHelper::IdForWindowContainingTab(web_contents); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 303 | } |
| 304 | |
[email protected] | 023b3d1 | 2013-12-23 18:46:49 | [diff] [blame] | 305 | base::DictionaryValue* ExtensionTabUtil::CreateTabValue( |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 306 | WebContents* contents, |
[email protected] | 0c9f326 | 2012-09-17 05:59:06 | [diff] [blame] | 307 | TabStripModel* tab_strip, |
| 308 | int tab_index, |
[email protected] | f34706be | 2012-09-04 07:32:09 | [diff] [blame] | 309 | const Extension* extension) { |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 310 | // If we have a matching AppWindow with a controller, get the tab value |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 311 | // from its controller instead. |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 312 | WindowController* controller = GetAppWindowController(contents); |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 313 | if (controller && |
| 314 | (!extension || controller->IsVisibleToExtension(extension))) { |
| 315 | return controller->CreateTabValue(extension, tab_index); |
| 316 | } |
[email protected] | 4442454 | 2014-01-29 12:10:31 | [diff] [blame] | 317 | base::DictionaryValue* result = |
[email protected] | 023b3d1 | 2013-12-23 18:46:49 | [diff] [blame] | 318 | CreateTabValue(contents, tab_strip, tab_index); |
[email protected] | 304fd15 | 2013-01-12 16:54:44 | [diff] [blame] | 319 | ScrubTabValueForExtension(contents, extension, result); |
| 320 | return result; |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 321 | } |
| 322 | |
[email protected] | aeca23f | 2013-06-21 22:34:41 | [diff] [blame] | 323 | base::ListValue* ExtensionTabUtil::CreateTabList( |
[email protected] | f34706be | 2012-09-04 07:32:09 | [diff] [blame] | 324 | const Browser* browser, |
| 325 | const Extension* extension) { |
[email protected] | aeca23f | 2013-06-21 22:34:41 | [diff] [blame] | 326 | base::ListValue* tab_list = new base::ListValue(); |
[email protected] | c084925 | 2012-05-12 13:51:27 | [diff] [blame] | 327 | TabStripModel* tab_strip = browser->tab_strip_model(); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 328 | for (int i = 0; i < tab_strip->count(); ++i) { |
[email protected] | 72f6797 | 2012-10-30 18:53:28 | [diff] [blame] | 329 | tab_list->Append(CreateTabValue(tab_strip->GetWebContentsAt(i), |
| 330 | tab_strip, |
| 331 | i, |
| 332 | extension)); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 333 | } |
| 334 | |
| 335 | return tab_list; |
| 336 | } |
| 337 | |
[email protected] | 023b3d1 | 2013-12-23 18:46:49 | [diff] [blame] | 338 | base::DictionaryValue* ExtensionTabUtil::CreateTabValue( |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 339 | WebContents* contents, |
[email protected] | f34706be | 2012-09-04 07:32:09 | [diff] [blame] | 340 | TabStripModel* tab_strip, |
[email protected] | 304fd15 | 2013-01-12 16:54:44 | [diff] [blame] | 341 | int tab_index) { |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 342 | // If we have a matching AppWindow with a controller, get the tab value |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 343 | // from its controller instead. |
[email protected] | dbb03fb | 2014-02-15 05:36:33 | [diff] [blame] | 344 | WindowController* controller = GetAppWindowController(contents); |
[email protected] | f1c102b | 2013-02-15 07:44:12 | [diff] [blame] | 345 | if (controller) |
| 346 | return controller->CreateTabValue(NULL, tab_index); |
| 347 | |
[email protected] | 0c9f326 | 2012-09-17 05:59:06 | [diff] [blame] | 348 | if (!tab_strip) |
| 349 | ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index); |
| 350 | |
[email protected] | 023b3d1 | 2013-12-23 18:46:49 | [diff] [blame] | 351 | base::DictionaryValue* result = new base::DictionaryValue(); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 352 | bool is_loading = contents->IsLoading(); |
[email protected] | f34706be | 2012-09-04 07:32:09 | [diff] [blame] | 353 | result->SetInteger(keys::kIdKey, GetTabId(contents)); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 354 | result->SetInteger(keys::kIndexKey, tab_index); |
[email protected] | f34706be | 2012-09-04 07:32:09 | [diff] [blame] | 355 | result->SetInteger(keys::kWindowIdKey, GetWindowIdOfTab(contents)); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 356 | result->SetString(keys::kStatusKey, GetTabStatusText(is_loading)); |
[email protected] | 8c3495c | 2011-09-28 03:32:30 | [diff] [blame] | 357 | result->SetBoolean(keys::kActiveKey, |
| 358 | tab_strip && tab_index == tab_strip->active_index()); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 359 | result->SetBoolean(keys::kSelectedKey, |
| 360 | tab_strip && tab_index == tab_strip->active_index()); |
[email protected] | 8c3495c | 2011-09-28 03:32:30 | [diff] [blame] | 361 | result->SetBoolean(keys::kHighlightedKey, |
| 362 | tab_strip && tab_strip->IsTabSelected(tab_index)); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 363 | result->SetBoolean(keys::kPinnedKey, |
| 364 | tab_strip && tab_strip->IsTabPinned(tab_index)); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 365 | result->SetBoolean(keys::kIncognitoKey, |
[email protected] | 627e051 | 2011-12-21 22:55:30 | [diff] [blame] | 366 | contents->GetBrowserContext()->IsOffTheRecord()); |
[email protected] | a16a5a5 | 2013-08-16 17:23:35 | [diff] [blame] | 367 | result->SetInteger(keys::kWidthKey, |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 368 | contents->GetContainerBounds().size().width()); |
[email protected] | a16a5a5 | 2013-08-16 17:23:35 | [diff] [blame] | 369 | result->SetInteger(keys::kHeightKey, |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 370 | contents->GetContainerBounds().size().height()); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 371 | |
[email protected] | 304fd15 | 2013-01-12 16:54:44 | [diff] [blame] | 372 | // Privacy-sensitive fields: these should be stripped off by |
| 373 | // ScrubTabValueForExtension if the extension should not see them. |
| 374 | result->SetString(keys::kUrlKey, contents->GetURL().spec()); |
| 375 | result->SetString(keys::kTitleKey, contents->GetTitle()); |
| 376 | if (!is_loading) { |
[email protected] | afe9aba | 2013-08-16 20:31:34 | [diff] [blame] | 377 | NavigationEntry* entry = contents->GetController().GetVisibleEntry(); |
[email protected] | 304fd15 | 2013-01-12 16:54:44 | [diff] [blame] | 378 | if (entry && entry->GetFavicon().valid) |
| 379 | result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec()); |
[email protected] | f34706be | 2012-09-04 07:32:09 | [diff] [blame] | 380 | } |
| 381 | |
[email protected] | 327640a | 2012-01-24 21:57:59 | [diff] [blame] | 382 | if (tab_strip) { |
[email protected] | 6707540 | 2012-10-26 08:26:25 | [diff] [blame] | 383 | WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index); |
| 384 | if (opener) |
| 385 | result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener)); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 386 | } |
| 387 | |
| 388 | return result; |
| 389 | } |
| 390 | |
[email protected] | 023b3d1 | 2013-12-23 18:46:49 | [diff] [blame] | 391 | void ExtensionTabUtil::ScrubTabValueForExtension( |
[email protected] | fc2b46b | 2014-05-03 16:33:45 | [diff] [blame] | 392 | WebContents* contents, |
[email protected] | 023b3d1 | 2013-12-23 18:46:49 | [diff] [blame] | 393 | const Extension* extension, |
| 394 | base::DictionaryValue* tab_info) { |
[email protected] | a6910e7 | 2014-06-06 05:04:36 | [diff] [blame] | 395 | bool has_permission = extension && |
| 396 | extension->permissions_data()->HasAPIPermissionForTab( |
| 397 | GetTabId(contents), APIPermission::kTab); |
[email protected] | 304fd15 | 2013-01-12 16:54:44 | [diff] [blame] | 398 | |
| 399 | if (!has_permission) { |
| 400 | tab_info->Remove(keys::kUrlKey, NULL); |
| 401 | tab_info->Remove(keys::kTitleKey, NULL); |
| 402 | tab_info->Remove(keys::kFaviconUrlKey, NULL); |
| 403 | } |
| 404 | } |
| 405 | |
[email protected] | ab3f6141 | 2013-01-29 21:55:07 | [diff] [blame] | 406 | void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension, |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 407 | api::tabs::Tab* tab) { |
[email protected] | 076ebeda | 2014-06-06 21:47:26 | [diff] [blame] | 408 | bool has_permission = |
| 409 | extension && |
| 410 | extension->permissions_data()->HasAPIPermission(APIPermission::kTab); |
[email protected] | ab3f6141 | 2013-01-29 21:55:07 | [diff] [blame] | 411 | |
| 412 | if (!has_permission) { |
| 413 | tab->url.reset(); |
| 414 | tab->title.reset(); |
| 415 | tab->fav_icon_url.reset(); |
| 416 | } |
| 417 | } |
| 418 | |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 419 | bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents, |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 420 | TabStripModel** tab_strip_model, |
| 421 | int* tab_index) { |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 422 | DCHECK(web_contents); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 423 | DCHECK(tab_strip_model); |
| 424 | DCHECK(tab_index); |
| 425 | |
[email protected] | 3539929f | 2013-02-01 05:59:14 | [diff] [blame] | 426 | for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| 427 | TabStripModel* tab_strip = it->tab_strip_model(); |
[email protected] | e52d0a4 | 2012-06-08 22:44:16 | [diff] [blame] | 428 | int index = tab_strip->GetIndexOfWebContents(web_contents); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 429 | if (index != -1) { |
| 430 | *tab_strip_model = tab_strip; |
| 431 | *tab_index = index; |
| 432 | return true; |
| 433 | } |
| 434 | } |
| 435 | |
| 436 | return false; |
| 437 | } |
| 438 | |
| 439 | bool ExtensionTabUtil::GetDefaultTab(Browser* browser, |
[email protected] | 72f6797 | 2012-10-30 18:53:28 | [diff] [blame] | 440 | WebContents** contents, |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 441 | int* tab_id) { |
| 442 | DCHECK(browser); |
| 443 | DCHECK(contents); |
| 444 | |
[email protected] | 617ee96 | 2013-01-29 20:49:12 | [diff] [blame] | 445 | *contents = browser->tab_strip_model()->GetActiveWebContents(); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 446 | if (*contents) { |
| 447 | if (tab_id) |
[email protected] | 72f6797 | 2012-10-30 18:53:28 | [diff] [blame] | 448 | *tab_id = GetTabId(*contents); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 449 | return true; |
| 450 | } |
| 451 | |
| 452 | return false; |
| 453 | } |
| 454 | |
| 455 | bool ExtensionTabUtil::GetTabById(int tab_id, |
wjmaclean | 5b11eee | 2014-09-05 00:55:14 | [diff] [blame^] | 456 | content::BrowserContext* browser_context, |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 457 | bool include_incognito, |
| 458 | Browser** browser, |
| 459 | TabStripModel** tab_strip, |
[email protected] | 72f6797 | 2012-10-30 18:53:28 | [diff] [blame] | 460 | WebContents** contents, |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 461 | int* tab_index) { |
wjmaclean | 5b11eee | 2014-09-05 00:55:14 | [diff] [blame^] | 462 | Profile* profile = Profile::FromBrowserContext(browser_context); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 463 | Profile* incognito_profile = |
| 464 | include_incognito && profile->HasOffTheRecordProfile() ? |
| 465 | profile->GetOffTheRecordProfile() : NULL; |
[email protected] | 3539929f | 2013-02-01 05:59:14 | [diff] [blame] | 466 | for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| 467 | Browser* target_browser = *it; |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 468 | if (target_browser->profile() == profile || |
| 469 | target_browser->profile() == incognito_profile) { |
[email protected] | c084925 | 2012-05-12 13:51:27 | [diff] [blame] | 470 | TabStripModel* target_tab_strip = target_browser->tab_strip_model(); |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 471 | for (int i = 0; i < target_tab_strip->count(); ++i) { |
[email protected] | 72f6797 | 2012-10-30 18:53:28 | [diff] [blame] | 472 | WebContents* target_contents = target_tab_strip->GetWebContentsAt(i); |
[email protected] | e3f90c60 | 2014-08-18 12:41:59 | [diff] [blame] | 473 | if (SessionTabHelper::IdForTab(target_contents) == tab_id) { |
[email protected] | ac84431b | 2011-09-27 17:26:11 | [diff] [blame] | 474 | if (browser) |
| 475 | *browser = target_browser; |
| 476 | if (tab_strip) |
| 477 | *tab_strip = target_tab_strip; |
| 478 | if (contents) |
| 479 | *contents = target_contents; |
| 480 | if (tab_index) |
| 481 | *tab_index = i; |
| 482 | return true; |
| 483 | } |
| 484 | } |
| 485 | } |
| 486 | } |
| 487 | return false; |
| 488 | } |
[email protected] | 45c75e6 | 2012-03-21 19:56:35 | [diff] [blame] | 489 | |
| 490 | GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string, |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 491 | const Extension* extension) { |
[email protected] | 45c75e6 | 2012-03-21 19:56:35 | [diff] [blame] | 492 | GURL url = GURL(url_string); |
| 493 | if (!url.is_valid()) |
| 494 | url = extension->GetResourceURL(url_string); |
| 495 | |
| 496 | return url; |
| 497 | } |
| 498 | |
| 499 | bool ExtensionTabUtil::IsCrashURL(const GURL& url) { |
| 500 | // Check a fixed-up URL, to normalize the scheme and parse hosts correctly. |
| 501 | GURL fixed_url = |
[email protected] | 9b5b1d60 | 2014-06-12 14:29:02 | [diff] [blame] | 502 | url_fixer::FixupURL(url.possibly_invalid_spec(), std::string()); |
[email protected] | 2d9748b2 | 2014-02-11 00:17:29 | [diff] [blame] | 503 | return (fixed_url.SchemeIs(content::kChromeUIScheme) && |
[email protected] | 46ed086 | 2013-04-14 02:47:56 | [diff] [blame] | 504 | (fixed_url.host() == content::kChromeUIBrowserCrashHost || |
[email protected] | 45c75e6 | 2012-03-21 19:56:35 | [diff] [blame] | 505 | fixed_url.host() == chrome::kChromeUICrashHost)); |
| 506 | } |
[email protected] | 73c1a684 | 2012-07-13 17:39:04 | [diff] [blame] | 507 | |
| 508 | void ExtensionTabUtil::CreateTab(WebContents* web_contents, |
| 509 | const std::string& extension_id, |
| 510 | WindowOpenDisposition disposition, |
| 511 | const gfx::Rect& initial_pos, |
| 512 | bool user_gesture) { |
[email protected] | 73c1a684 | 2012-07-13 17:39:04 | [diff] [blame] | 513 | Profile* profile = |
| 514 | Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
[email protected] | 435d43e0 | 2012-12-09 09:13:55 | [diff] [blame] | 515 | chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop(); |
[email protected] | 7507273 | 2013-01-09 13:39:25 | [diff] [blame] | 516 | Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop); |
[email protected] | 2764cab7 | 2012-07-19 17:02:10 | [diff] [blame] | 517 | const bool browser_created = !browser; |
| 518 | if (!browser) |
[email protected] | 435d43e0 | 2012-12-09 09:13:55 | [diff] [blame] | 519 | browser = new Browser(Browser::CreateParams(profile, active_desktop)); |
[email protected] | e232c99 | 2012-12-06 12:43:20 | [diff] [blame] | 520 | chrome::NavigateParams params(browser, web_contents); |
[email protected] | 73c1a684 | 2012-07-13 17:39:04 | [diff] [blame] | 521 | |
| 522 | // The extension_app_id parameter ends up as app_name in the Browser |
| 523 | // which causes the Browser to return true for is_app(). This affects |
| 524 | // among other things, whether the location bar gets displayed. |
| 525 | // TODO(mpcomplete): This seems wrong. What if the extension content is hosted |
| 526 | // in a tab? |
| 527 | if (disposition == NEW_POPUP) |
| 528 | params.extension_app_id = extension_id; |
| 529 | |
[email protected] | 73c1a684 | 2012-07-13 17:39:04 | [diff] [blame] | 530 | params.disposition = disposition; |
| 531 | params.window_bounds = initial_pos; |
| 532 | params.window_action = chrome::NavigateParams::SHOW_WINDOW; |
| 533 | params.user_gesture = user_gesture; |
| 534 | chrome::Navigate(¶ms); |
[email protected] | 2764cab7 | 2012-07-19 17:02:10 | [diff] [blame] | 535 | |
| 536 | // Close the browser if chrome::Navigate created a new one. |
| 537 | if (browser_created && (browser != params.browser)) |
| 538 | browser->window()->Close(); |
[email protected] | 73c1a684 | 2012-07-13 17:39:04 | [diff] [blame] | 539 | } |
[email protected] | 44e329a | 2012-07-14 01:13:06 | [diff] [blame] | 540 | |
| 541 | // static |
| 542 | void ExtensionTabUtil::ForEachTab( |
| 543 | const base::Callback<void(WebContents*)>& callback) { |
[email protected] | b031ff8 | 2013-01-29 22:53:16 | [diff] [blame] | 544 | for (TabContentsIterator iterator; !iterator.done(); iterator.Next()) |
[email protected] | f807356 | 2012-12-06 12:43:53 | [diff] [blame] | 545 | callback.Run(*iterator); |
[email protected] | 44e329a | 2012-07-14 01:13:06 | [diff] [blame] | 546 | } |
[email protected] | e9570fdf | 2012-07-18 20:01:21 | [diff] [blame] | 547 | |
| 548 | // static |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 549 | WindowController* ExtensionTabUtil::GetWindowControllerOfTab( |
[email protected] | e9570fdf | 2012-07-18 20:01:21 | [diff] [blame] | 550 | const WebContents* web_contents) { |
[email protected] | f7b4b9e | 2012-12-02 07:43:17 | [diff] [blame] | 551 | Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
[email protected] | e9570fdf | 2012-07-18 20:01:21 | [diff] [blame] | 552 | if (browser != NULL) |
| 553 | return browser->extension_window_controller(); |
| 554 | |
| 555 | return NULL; |
| 556 | } |
[email protected] | 69508978 | 2013-04-09 16:03:17 | [diff] [blame] | 557 | |
| 558 | void ExtensionTabUtil::OpenOptionsPage(const Extension* extension, |
| 559 | Browser* browser) { |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 560 | DCHECK(!ManifestURL::GetOptionsPage(extension).is_empty()); |
[email protected] | 69508978 | 2013-04-09 16:03:17 | [diff] [blame] | 561 | |
| 562 | // Force the options page to open in non-OTR window, because it won't be |
| 563 | // able to save settings from OTR. |
[email protected] | 0edcdec | 2013-10-31 06:43:08 | [diff] [blame] | 564 | scoped_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer; |
[email protected] | 69508978 | 2013-04-09 16:03:17 | [diff] [blame] | 565 | if (browser->profile()->IsOffTheRecord()) { |
[email protected] | 0edcdec | 2013-10-31 06:43:08 | [diff] [blame] | 566 | displayer.reset(new chrome::ScopedTabbedBrowserDisplayer( |
| 567 | browser->profile()->GetOriginalProfile(), |
| 568 | browser->host_desktop_type())); |
| 569 | browser = displayer->browser(); |
[email protected] | 69508978 | 2013-04-09 16:03:17 | [diff] [blame] | 570 | } |
| 571 | |
ericzeng | f97b7c2 | 2014-08-26 03:07:30 | [diff] [blame] | 572 | if (FeatureSwitch::embedded_extension_options()->IsEnabled()) { |
| 573 | // If embedded extension options are enabled, open chrome://extensions |
| 574 | // in a new tab and show the extension options in an embedded popup. |
| 575 | chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams( |
| 576 | browser, GURL(chrome::kChromeUIExtensionsURL))); |
| 577 | params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE; |
| 578 | |
| 579 | GURL::Replacements replacements; |
| 580 | std::string query = |
| 581 | base::StringPrintf("options=%s", extension->id().c_str()); |
| 582 | replacements.SetQueryStr(query); |
| 583 | params.url = params.url.ReplaceComponents(replacements); |
| 584 | |
| 585 | chrome::ShowSingletonTabOverwritingNTP(browser, params); |
| 586 | } else { |
| 587 | // Otherwise open a new tab with the extension's options page |
| 588 | content::OpenURLParams params(ManifestURL::GetOptionsPage(extension), |
| 589 | content::Referrer(), |
| 590 | SINGLETON_TAB, |
| 591 | content::PAGE_TRANSITION_LINK, |
| 592 | false); |
| 593 | browser->OpenURL(params); |
| 594 | browser->window()->Show(); |
| 595 | WebContents* web_contents = |
| 596 | browser->tab_strip_model()->GetActiveWebContents(); |
| 597 | web_contents->GetDelegate()->ActivateContents(web_contents); |
| 598 | } |
[email protected] | 69508978 | 2013-04-09 16:03:17 | [diff] [blame] | 599 | } |
[email protected] | 1c4fbc0 | 2013-11-13 02:52:42 | [diff] [blame] | 600 | |
| 601 | } // namespace extensions |