blob: a92dcf7b2f9b34cf6d5b52c202cc4594f728dda4 [file] [log] [blame]
[email protected]327640a2012-01-24 21:57:591// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ac84431b2011-09-27 17:26:112// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]ac84431b2011-09-27 17:26:115#include "chrome/browser/extensions/extension_tab_util.h"
[email protected]41d9faf2012-02-28 23:46:026
avia2f4804a2015-12-24 23:11:137#include <stddef.h>
Devlin Cronin7050f8e2018-02-07 19:52:048#include <algorithm>
Ivan Sandrke2b20c62018-09-10 16:23:539#include <utility>
avia2f4804a2015-12-24 23:11:1310
Ivan Sandrke2b20c62018-09-10 16:23:5311#include "base/no_destructor.h"
Avi Drissman5f0fb8c2018-12-25 23:20:4912#include "base/stl_util.h"
[email protected]98528302014-05-02 00:34:0813#include "base/strings/string_number_conversions.h"
ericzengf97b7c22014-08-26 03:07:3014#include "base/strings/stringprintf.h"
limasdf6dcdc442016-02-26 04:58:2615#include "base/strings/utf_string_conversions.h"
andersoncss12caff12016-07-14 14:43:4116#include "chrome/browser/browser_process.h"
[email protected]b19451b2012-06-08 17:36:1917#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
Devlin Croninad230bb2018-05-30 18:41:4618#include "chrome/browser/extensions/browser_extension_window_controller.h"
[email protected]98528302014-05-02 00:34:0819#include "chrome/browser/extensions/chrome_extension_function.h"
lfg185333072014-09-09 20:16:1120#include "chrome/browser/extensions/chrome_extension_function_details.h"
[email protected]e9570fdf2012-07-18 20:01:2121#include "chrome/browser/extensions/tab_helper.h"
Ivan Sandrke0e92452019-03-21 09:32:4822#include "chrome/browser/platform_util.h"
[email protected]b56e2e32012-05-11 21:18:0423#include "chrome/browser/profiles/profile.h"
Francois Doray77be0212018-04-05 14:26:2624#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
[email protected]e3f90c602014-08-18 12:41:5925#include "chrome/browser/sessions/session_tab_helper.h"
[email protected]b56e2e32012-05-11 21:18:0426#include "chrome/browser/ui/browser.h"
[email protected]73c1a6842012-07-13 17:39:0427#include "chrome/browser/ui/browser_finder.h"
thestige80821242015-09-30 23:46:0828#include "chrome/browser/ui/browser_navigator_params.h"
[email protected]b56e2e32012-05-11 21:18:0429#include "chrome/browser/ui/browser_window.h"
Chris Hamilton20139652018-06-01 15:55:0130#include "chrome/browser/ui/recently_audible_helper.h"
[email protected]0edcdec2013-10-31 06:43:0831#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
ericzengf97b7c22014-08-26 03:07:3032#include "chrome/browser/ui/singleton_tabs.h"
[email protected]44e329a2012-07-14 01:13:0633#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
[email protected]b56e2e32012-05-11 21:18:0434#include "chrome/browser/ui/tabs/tab_strip_model.h"
jared.sohn09a3ccc2015-07-09 03:47:3035#include "chrome/browser/ui/tabs/tab_utils.h"
[email protected]760f6032014-06-30 23:18:1936#include "chrome/common/extensions/api/tabs.h"
[email protected]45c75e62012-03-21 19:56:3537#include "chrome/common/url_constants.h"
rsleevi24f64dc22015-08-07 21:39:2138#include "components/url_formatter/url_fixer.h"
[email protected]ad23a092011-12-28 07:02:0439#include "content/public/browser/favicon_status.h"
40#include "content/public/browser/navigation_entry.h"
[email protected]6acde6352012-01-04 16:52:2041#include "content/public/browser/web_contents.h"
[email protected]98528302014-05-02 00:34:0842#include "extensions/common/constants.h"
43#include "extensions/common/error_utils.h"
[email protected]e4452d32013-11-15 23:07:4144#include "extensions/common/extension.h"
ericzengf97b7c22014-08-26 03:07:3045#include "extensions/common/feature_switch.h"
[email protected]0c3c9732013-09-16 08:53:4146#include "extensions/common/manifest_constants.h"
[email protected]98528302014-05-02 00:34:0847#include "extensions/common/manifest_handlers/incognito_info.h"
ericzeng09a7e002014-09-09 21:43:4748#include "extensions/common/manifest_handlers/options_page_info.h"
[email protected]793964a2013-10-08 00:47:1949#include "extensions/common/permissions/api_permission.h"
[email protected]e4452d32013-11-15 23:07:4150#include "extensions/common/permissions/permissions_data.h"
[email protected]a6483d22013-07-03 22:11:0051#include "url/gurl.h"
[email protected]ac84431b2011-09-27 17:26:1152
[email protected]10f417c52011-12-28 21:04:2353using content::NavigationEntry;
[email protected]26b5e322011-12-23 01:36:4754using content::WebContents;
[email protected]1c4fbc02013-11-13 02:52:4255
56namespace extensions {
[email protected]26b5e322011-12-23 01:36:4757
[email protected]f1c102b2013-02-15 07:44:1258namespace {
59
[email protected]98528302014-05-02 00:34:0860// |error_message| can optionally be passed in and will be set with an
61// appropriate message if the window cannot be found by id.
62Browser* GetBrowserInProfileWithId(Profile* profile,
63 const int window_id,
Ramin Halavatiacdd17f12018-06-06 07:05:1264 bool match_incognito_profile,
[email protected]98528302014-05-02 00:34:0865 std::string* error_message) {
66 Profile* incognito_profile =
Ramin Halavatiacdd17f12018-06-06 07:05:1267 match_incognito_profile && profile->HasOffTheRecordProfile()
[email protected]98528302014-05-02 00:34:0868 ? profile->GetOffTheRecordProfile()
Catherine Mullings0524e2042017-06-26 23:09:4569 : nullptr;
scottmg8abbff832016-01-28 22:57:3770 for (auto* browser : *BrowserList::GetInstance()) {
[email protected]98528302014-05-02 00:34:0871 if ((browser->profile() == profile ||
72 browser->profile() == incognito_profile) &&
73 ExtensionTabUtil::GetWindowId(browser) == window_id &&
74 browser->window()) {
75 return browser;
76 }
77 }
78
79 if (error_message)
80 *error_message = ErrorUtils::FormatErrorMessage(
Raul Tambrefff51b752019-02-04 13:09:4781 tabs_constants::kWindowNotFoundError, base::NumberToString(window_id));
[email protected]98528302014-05-02 00:34:0882
Catherine Mullings0524e2042017-06-26 23:09:4583 return nullptr;
[email protected]98528302014-05-02 00:34:0884}
85
erg7b01d692017-02-22 21:57:3586Browser* CreateBrowser(Profile* profile,
87 int window_id,
88 bool user_gesture,
89 std::string* error) {
Joel Hockey2687ab52019-08-14 23:59:4690 Browser::CreateParams params(Browser::TYPE_NORMAL, profile, user_gesture);
Toni Barzic00705742019-02-25 22:09:4191 Browser* browser = Browser::Create(params);
92 if (!browser) {
93 *error = tabs_constants::kBrowserWindowNotAllowed;
94 return nullptr;
95 }
[email protected]98528302014-05-02 00:34:0896 browser->window()->Show();
97 return browser;
98}
99
lionel.g.landwerlin85ad2042015-07-23 11:26:11100// Use this function for reporting a tab id to an extension. It will
101// take care of setting the id to TAB_ID_NONE if necessary (for
102// example with devtools).
103int GetTabIdForExtensions(const WebContents* web_contents) {
104 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
105 if (browser && !ExtensionTabUtil::BrowserSupportsTabs(browser))
106 return -1;
Mikel Astiz2f127c422018-04-05 19:10:27107 return SessionTabHelper::IdForTab(web_contents).id();
lionel.g.landwerlin85ad2042015-07-23 11:26:11108}
109
Daniel Bratellbe89e3442018-09-11 15:44:57110std::unique_ptr<ExtensionTabUtil::Delegate>&
111GetExtensionTabUtilDelegateWrapper() {
Ivan Sandrke2b20c62018-09-10 16:23:53112 static base::NoDestructor<std::unique_ptr<ExtensionTabUtil::Delegate>>
113 delegate_wrapper;
114 return *delegate_wrapper;
115}
116
Daniel Bratellbe89e3442018-09-11 15:44:57117ExtensionTabUtil::Delegate* GetExtensionTabUtilDelegate() {
118 return GetExtensionTabUtilDelegateWrapper().get();
Ivan Sandrke2b20c62018-09-10 16:23:53119}
isandrke0951132017-04-24 17:53:31120
John Lee76e40a32019-08-20 17:52:40121ExtensionTabUtil::ScrubTabBehavior GetScrubTabBehaviorImpl(
122 const Extension* extension,
John Leebfbb37cb2019-08-26 22:23:18123 Feature::Context context,
John Lee76e40a32019-08-20 17:52:40124 const GURL& url,
125 int tab_id) {
John Leebfbb37cb2019-08-26 22:23:18126 if (context == Feature::Context::WEBUI_CONTEXT) {
127 return ExtensionTabUtil::kDontScrubTab;
128 }
129
John Lee76e40a32019-08-20 17:52:40130 bool has_permission = false;
131
132 if (extension) {
133 bool api_permission = false;
134 if (tab_id == api::tabs::TAB_ID_NONE) {
135 api_permission =
136 extension->permissions_data()->HasAPIPermission(APIPermission::kTab);
137 } else {
138 api_permission = extension->permissions_data()->HasAPIPermissionForTab(
139 tab_id, APIPermission::kTab);
140 }
141
142 bool host_permission = extension->permissions_data()
143 ->active_permissions()
144 .HasExplicitAccessToOrigin(url);
145 has_permission = api_permission || host_permission;
146 }
147
148 if (!has_permission) {
149 return ExtensionTabUtil::kScrubTabFully;
150 }
151
152 if (GetExtensionTabUtilDelegate()) {
153 return GetExtensionTabUtilDelegate()->GetScrubTabBehavior(extension);
154 }
155
156 return ExtensionTabUtil::kDontScrubTab;
157}
158
[email protected]f1c102b2013-02-15 07:44:12159} // namespace
160
[email protected]98528302014-05-02 00:34:08161ExtensionTabUtil::OpenTabParams::OpenTabParams()
162 : create_browser_if_needed(false) {
163}
164
165ExtensionTabUtil::OpenTabParams::~OpenTabParams() {
166}
167
Catherine Mullings0524e2042017-06-26 23:09:45168// Opens a new tab for a given extension. Returns nullptr and sets |error| if an
[email protected]98528302014-05-02 00:34:08169// error occurs.
Clark DuVallfd4db3d2019-07-30 19:10:43170base::DictionaryValue* ExtensionTabUtil::OpenTab(ExtensionFunction* function,
171 const OpenTabParams& params,
172 bool user_gesture,
173 std::string* error) {
rdevlin.cronin3223122d2016-09-07 21:27:26174 ChromeExtensionFunctionDetails chrome_details(function);
175 Profile* profile = chrome_details.GetProfile();
[email protected]98528302014-05-02 00:34:08176 // windowId defaults to "current" window.
177 int window_id = extension_misc::kCurrentWindowId;
178 if (params.window_id.get())
179 window_id = *params.window_id;
180
rdevlin.cronin3223122d2016-09-07 21:27:26181 Browser* browser = GetBrowserFromWindowID(chrome_details, window_id, error);
[email protected]98528302014-05-02 00:34:08182 if (!browser) {
183 if (!params.create_browser_if_needed) {
Catherine Mullings0524e2042017-06-26 23:09:45184 return nullptr;
[email protected]98528302014-05-02 00:34:08185 }
erg7b01d692017-02-22 21:57:35186 browser = CreateBrowser(profile, window_id, user_gesture, error);
[email protected]98528302014-05-02 00:34:08187 if (!browser)
Catherine Mullings0524e2042017-06-26 23:09:45188 return nullptr;
[email protected]98528302014-05-02 00:34:08189 }
190
Joel Hockey2687ab52019-08-14 23:59:46191 // Ensure the selected browser is normal.
192 if (!browser->is_type_normal() && browser->IsAttemptingToCloseBrowser())
Ramin Halavatiacdd17f12018-06-06 07:05:12193 browser = chrome::FindTabbedBrowser(
194 profile, function->include_incognito_information());
[email protected]98528302014-05-02 00:34:08195 if (!browser || !browser->window()) {
deepak.m121895c012015-05-11 08:11:19196 if (error)
oscarjohansson77164012018-06-11 07:00:11197 *error = tabs_constants::kNoCurrentWindowError;
Catherine Mullings0524e2042017-06-26 23:09:45198 return nullptr;
[email protected]98528302014-05-02 00:34:08199 }
200
201 // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
202 // represents the active tab.
Catherine Mullings0524e2042017-06-26 23:09:45203 WebContents* opener = nullptr;
204 Browser* opener_browser = nullptr;
[email protected]98528302014-05-02 00:34:08205 if (params.opener_tab_id.get()) {
206 int opener_id = *params.opener_tab_id;
207
Catherine Mullings0524e2042017-06-26 23:09:45208 if (!ExtensionTabUtil::GetTabById(
Ramin Halavatiacdd17f12018-06-06 07:05:12209 opener_id, profile, function->include_incognito_information(),
210 &opener_browser, nullptr, &opener, nullptr)) {
deepak.m121895c012015-05-11 08:11:19211 if (error) {
oscarjohansson77164012018-06-11 07:00:11212 *error = ErrorUtils::FormatErrorMessage(
Raul Tambrefff51b752019-02-04 13:09:47213 tabs_constants::kTabNotFoundError, base::NumberToString(opener_id));
deepak.m121895c012015-05-11 08:11:19214 }
Catherine Mullings0524e2042017-06-26 23:09:45215 return nullptr;
[email protected]98528302014-05-02 00:34:08216 }
217 }
218
219 // TODO(rafaelw): handle setting remaining tab properties:
220 // -title
221 // -favIconUrl
222
[email protected]98528302014-05-02 00:34:08223 GURL url;
224 if (params.url.get()) {
bokan107a47f2015-02-03 23:23:39225 std::string url_string = *params.url;
[email protected]eba8f7d2014-07-28 22:09:23226 url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
227 function->extension());
[email protected]98528302014-05-02 00:34:08228 if (!url.is_valid()) {
oscarjohansson77164012018-06-11 07:00:11229 *error = ErrorUtils::FormatErrorMessage(tabs_constants::kInvalidUrlError,
230 url_string);
Catherine Mullings0524e2042017-06-26 23:09:45231 return nullptr;
[email protected]98528302014-05-02 00:34:08232 }
[email protected]f5ec3c922014-05-22 15:04:03233 } else {
234 url = GURL(chrome::kChromeUINewTabURL);
[email protected]98528302014-05-02 00:34:08235 }
236
237 // Don't let extensions crash the browser or renderers.
kalmandfefe1a2015-07-13 22:27:54238 if (ExtensionTabUtil::IsKillURL(url)) {
oscarjohansson77164012018-06-11 07:00:11239 *error = tabs_constants::kNoCrashBrowserError;
Catherine Mullings0524e2042017-06-26 23:09:45240 return nullptr;
[email protected]98528302014-05-02 00:34:08241 }
242
243 // Default to foreground for the new tab. The presence of 'active' property
244 // will override this default.
245 bool active = true;
246 if (params.active.get())
247 active = *params.active;
248
249 // Default to not pinning the tab. Setting the 'pinned' property to true
250 // will override this default.
251 bool pinned = false;
252 if (params.pinned.get())
253 pinned = *params.pinned;
254
255 // We can't load extension URLs into incognito windows unless the extension
256 // uses split mode. Special case to fall back to a tabbed window.
257 if (url.SchemeIs(kExtensionScheme) &&
calamity862d90322017-05-22 07:14:28258 (!function->extension() ||
259 !IncognitoInfo::IsSplitMode(function->extension())) &&
[email protected]98528302014-05-02 00:34:08260 browser->profile()->IsOffTheRecord()) {
261 Profile* profile = browser->profile()->GetOriginalProfile();
[email protected]98528302014-05-02 00:34:08262
scottmg34c5dd882016-02-03 05:21:54263 browser = chrome::FindTabbedBrowser(profile, false);
[email protected]98528302014-05-02 00:34:08264 if (!browser) {
erg7b01d692017-02-22 21:57:35265 Browser::CreateParams params =
Joel Hockey2687ab52019-08-14 23:59:46266 Browser::CreateParams(Browser::TYPE_NORMAL, profile, user_gesture);
Toni Barzic00705742019-02-25 22:09:41267 browser = Browser::Create(params);
268 if (!browser) {
269 *error = tabs_constants::kBrowserWindowNotAllowed;
270 return nullptr;
271 }
[email protected]98528302014-05-02 00:34:08272 browser->window()->Show();
273 }
274 }
275
Catherine Mullings0524e2042017-06-26 23:09:45276 if (opener_browser && browser != opener_browser) {
277 if (error) {
278 *error = "Tab opener must be in the same window as the updated tab.";
279 }
280 return nullptr;
281 }
282
[email protected]98528302014-05-02 00:34:08283 // If index is specified, honor the value, but keep it bound to
284 // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
285 int index = -1;
286 if (params.index.get())
287 index = *params.index;
288
289 TabStripModel* tab_strip = browser->tab_strip_model();
290
291 index = std::min(std::max(index, -1), tab_strip->count());
292
293 int add_types = active ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE;
294 add_types |= TabStripModel::ADD_FORCE_INDEX;
295 if (pinned)
296 add_types |= TabStripModel::ADD_PINNED;
cm.sanchi2522bc92017-12-04 08:04:13297 NavigateParams navigate_params(browser, url, ui::PAGE_TRANSITION_LINK);
nick3b04f322016-08-31 19:29:19298 navigate_params.disposition = active
299 ? WindowOpenDisposition::NEW_FOREGROUND_TAB
300 : WindowOpenDisposition::NEW_BACKGROUND_TAB;
[email protected]98528302014-05-02 00:34:08301 navigate_params.tabstrip_index = index;
302 navigate_params.tabstrip_add_types = add_types;
cm.sanchi2522bc92017-12-04 08:04:13303 Navigate(&navigate_params);
[email protected]98528302014-05-02 00:34:08304
305 // The tab may have been created in a different window, so make sure we look
306 // at the right tab strip.
307 tab_strip = navigate_params.browser->tab_strip_model();
erikchen38fa4022018-04-26 20:37:52308 int new_index = tab_strip->GetIndexOfWebContents(
309 navigate_params.navigated_or_inserted_contents);
rdevlin.cronin5b3827b62017-03-08 02:38:09310 if (opener) {
311 // Only set the opener if the opener tab is in the same tab strip as the
312 // new tab.
rdevlin.cronin5b3827b62017-03-08 02:38:09313 if (tab_strip->GetIndexOfWebContents(opener) != TabStripModel::kNoTab)
314 tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
315 }
[email protected]98528302014-05-02 00:34:08316
317 if (active)
erikchen38fa4022018-04-26 20:37:52318 navigate_params.navigated_or_inserted_contents->SetInitialFocus();
[email protected]98528302014-05-02 00:34:08319
John Lee76e40a32019-08-20 17:52:40320 ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
321 ExtensionTabUtil::GetScrubTabBehavior(
John Leebfbb37cb2019-08-26 22:23:18322 function->extension(), function->source_context_type(),
John Lee76e40a32019-08-20 17:52:40323 navigate_params.navigated_or_inserted_contents);
324
[email protected]98528302014-05-02 00:34:08325 // Return data about the newly created tab.
erikchen38fa4022018-04-26 20:37:52326 return ExtensionTabUtil::CreateTabObject(
John Lee76e40a32019-08-20 17:52:40327 navigate_params.navigated_or_inserted_contents, scrub_tab_behavior,
erikchen38fa4022018-04-26 20:37:52328 function->extension(), tab_strip, new_index)
limasdf6dcdc442016-02-26 04:58:26329 ->ToValue()
330 .release();
[email protected]98528302014-05-02 00:34:08331}
332
333Browser* ExtensionTabUtil::GetBrowserFromWindowID(
lfg185333072014-09-09 20:16:11334 const ChromeExtensionFunctionDetails& details,
335 int window_id,
336 std::string* error) {
337 if (window_id == extension_misc::kCurrentWindowId) {
338 Browser* result = details.GetCurrentBrowser();
339 if (!result || !result->window()) {
340 if (error)
oscarjohansson77164012018-06-11 07:00:11341 *error = tabs_constants::kNoCurrentWindowError;
Catherine Mullings0524e2042017-06-26 23:09:45342 return nullptr;
lfg185333072014-09-09 20:16:11343 }
344 return result;
345 } else {
Ramin Halavatiacdd17f12018-06-06 07:05:12346 return GetBrowserInProfileWithId(
347 details.GetProfile(), window_id,
348 details.function()->include_incognito_information(), error);
lfg185333072014-09-09 20:16:11349 }
350}
351
[email protected]ac84431b2011-09-27 17:26:11352int ExtensionTabUtil::GetWindowId(const Browser* browser) {
353 return browser->session_id().id();
354}
355
[email protected]8c3495c2011-09-28 03:32:30356int ExtensionTabUtil::GetWindowIdOfTabStripModel(
357 const TabStripModel* tab_strip_model) {
scottmg8abbff832016-01-28 22:57:37358 for (auto* browser : *BrowserList::GetInstance()) {
359 if (browser->tab_strip_model() == tab_strip_model)
360 return GetWindowId(browser);
[email protected]8c3495c2011-09-28 03:32:30361 }
362 return -1;
363}
364
[email protected]d2bd5fde2014-05-29 02:24:31365int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
Mikel Astiz2f127c422018-04-05 19:10:27366 return SessionTabHelper::IdForTab(web_contents).id();
[email protected]ac84431b2011-09-27 17:26:11367}
368
369std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
oscarjohansson77164012018-06-11 07:00:11370 return is_loading ? tabs_constants::kStatusValueLoading
371 : tabs_constants::kStatusValueComplete;
[email protected]ac84431b2011-09-27 17:26:11372}
373
[email protected]ea049a02011-12-25 21:37:09374int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
Mikel Astiz2f127c422018-04-05 19:10:27375 return SessionTabHelper::IdForWindowContainingTab(web_contents).id();
[email protected]ac84431b2011-09-27 17:26:11376}
377
limasdf6dcdc442016-02-26 04:58:26378// static
Devlin Cronin7050f8e2018-02-07 19:52:04379std::string ExtensionTabUtil::GetBrowserWindowTypeText(const Browser& browser) {
Joel Hockey2687ab52019-08-14 23:59:46380 if (browser.is_type_devtools())
oscarjohansson77164012018-06-11 07:00:11381 return tabs_constants::kWindowTypeValueDevTools;
Joel Hockey2687ab52019-08-14 23:59:46382 // TODO(crbug.com/990158): We return 'popup' for both popup and app since
383 // chrome.tabs.create({type: 'popup'}) uses
384 // Browser::CreateParams::CreateForApp.
385 if (browser.is_type_popup() || browser.is_type_app())
oscarjohansson77164012018-06-11 07:00:11386 return tabs_constants::kWindowTypeValuePopup;
oscarjohansson77164012018-06-11 07:00:11387 return tabs_constants::kWindowTypeValueNormal;
Devlin Cronin7050f8e2018-02-07 19:52:04388}
389
390// static
dchengc963c7142016-04-08 03:55:22391std::unique_ptr<api::tabs::Tab> ExtensionTabUtil::CreateTabObject(
[email protected]fc2b46b2014-05-03 16:33:45392 WebContents* contents,
Devlin Cronin1980f44d2018-02-08 23:14:01393 ScrubTabBehavior scrub_tab_behavior,
394 const Extension* extension,
[email protected]f34706be2012-09-04 07:32:09395 TabStripModel* tab_strip,
[email protected]304fd152013-01-12 16:54:44396 int tab_index) {
[email protected]0c9f3262012-09-17 05:59:06397 if (!tab_strip)
398 ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index);
[email protected]ac84431b2011-09-27 17:26:11399 bool is_loading = contents->IsLoading();
Jinho Bangb5216cec2018-01-17 19:43:11400 auto tab_object = std::make_unique<api::tabs::Tab>();
401 tab_object->id = std::make_unique<int>(GetTabIdForExtensions(contents));
limasdf6dcdc442016-02-26 04:58:26402 tab_object->index = tab_index;
403 tab_object->window_id = GetWindowIdOfTab(contents);
lazyboy8b1df302017-04-26 23:37:41404 tab_object->status =
Jinho Bangb5216cec2018-01-17 19:43:11405 std::make_unique<std::string>(GetTabStatusText(is_loading));
limasdf6dcdc442016-02-26 04:58:26406 tab_object->active = tab_strip && tab_index == tab_strip->active_index();
407 tab_object->selected = tab_strip && tab_index == tab_strip->active_index();
408 tab_object->highlighted = tab_strip && tab_strip->IsTabSelected(tab_index);
409 tab_object->pinned = tab_strip && tab_strip->IsTabPinned(tab_index);
Chris Hamilton20139652018-06-01 15:55:01410 auto* audible_helper = RecentlyAudibleHelper::FromWebContents(contents);
Chris Hamilton737e22b42018-06-04 18:33:14411 bool audible = false;
412 if (audible_helper) {
413 // WebContents in a tab strip have RecentlyAudible helpers. They endow the
414 // tab with a notion of audibility that has a timeout for quiet periods. Use
415 // that if available.
416 audible = audible_helper->WasRecentlyAudible();
417 } else {
418 // Otherwise use the instantaneous notion of audibility.
419 audible = contents->IsCurrentlyAudible();
420 }
421 tab_object->audible = std::make_unique<bool>(audible);
Francois Doray77be0212018-04-05 14:26:26422 auto* tab_lifeycle_unit_external =
423 resource_coordinator::TabLifecycleUnitExternal::FromWebContents(contents);
andersoncssfd947a42016-08-12 14:10:42424 tab_object->discarded =
Francois Doray77be0212018-04-05 14:26:26425 tab_lifeycle_unit_external && tab_lifeycle_unit_external->IsDiscarded();
andersoncssfd947a42016-08-12 14:10:42426 tab_object->auto_discardable =
Francois Doray77be0212018-04-05 14:26:26427 !tab_lifeycle_unit_external ||
428 tab_lifeycle_unit_external->IsAutoDiscardable();
limasdf6dcdc442016-02-26 04:58:26429 tab_object->muted_info = CreateMutedInfo(contents);
430 tab_object->incognito = contents->GetBrowserContext()->IsOffTheRecord();
lazyboy8b1df302017-04-26 23:37:41431 gfx::Size contents_size = contents->GetContainerBounds().size();
Jinho Bangb5216cec2018-01-17 19:43:11432 tab_object->width = std::make_unique<int>(contents_size.width());
433 tab_object->height = std::make_unique<int>(contents_size.height());
[email protected]ac84431b2011-09-27 17:26:11434
Jinho Bangb5216cec2018-01-17 19:43:11435 tab_object->url = std::make_unique<std::string>(contents->GetURL().spec());
lazyboy8b1df302017-04-26 23:37:41436 tab_object->title =
Jinho Bangb5216cec2018-01-17 19:43:11437 std::make_unique<std::string>(base::UTF16ToUTF8(contents->GetTitle()));
limasdf1f071892016-02-09 10:18:00438 NavigationEntry* entry = contents->GetController().GetVisibleEntry();
lazyboy8b1df302017-04-26 23:37:41439 if (entry && entry->GetFavicon().valid) {
440 tab_object->fav_icon_url =
Jinho Bangb5216cec2018-01-17 19:43:11441 std::make_unique<std::string>(entry->GetFavicon().url.spec());
lazyboy8b1df302017-04-26 23:37:41442 }
[email protected]327640a2012-01-24 21:57:59443 if (tab_strip) {
[email protected]67075402012-10-26 08:26:25444 WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
lazyboy8b1df302017-04-26 23:37:41445 if (opener) {
446 tab_object->opener_tab_id =
Jinho Bangb5216cec2018-01-17 19:43:11447 std::make_unique<int>(GetTabIdForExtensions(opener));
lazyboy8b1df302017-04-26 23:37:41448 }
[email protected]ac84431b2011-09-27 17:26:11449 }
450
John Lee76e40a32019-08-20 17:52:40451 if (scrub_tab_behavior != kDontScrubTab)
452 ScrubTabForExtension(extension, contents, tab_object.get(),
453 scrub_tab_behavior);
limasdf6dcdc442016-02-26 04:58:26454 return tab_object;
[email protected]ac84431b2011-09-27 17:26:11455}
456
Devlin Cronin1980f44d2018-02-08 23:14:01457std::unique_ptr<base::ListValue> ExtensionTabUtil::CreateTabList(
458 const Browser* browser,
John Leebfbb37cb2019-08-26 22:23:18459 const Extension* extension,
460 Feature::Context context) {
Devlin Cronin1980f44d2018-02-08 23:14:01461 std::unique_ptr<base::ListValue> tab_list(new base::ListValue());
462 TabStripModel* tab_strip = browser->tab_strip_model();
463 for (int i = 0; i < tab_strip->count(); ++i) {
John Lee76e40a32019-08-20 17:52:40464 WebContents* web_contents = tab_strip->GetWebContentsAt(i);
465 ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
John Leebfbb37cb2019-08-26 22:23:18466 ExtensionTabUtil::GetScrubTabBehavior(extension, context, web_contents);
John Lee76e40a32019-08-20 17:52:40467 tab_list->Append(CreateTabObject(web_contents, scrub_tab_behavior,
Devlin Cronin1980f44d2018-02-08 23:14:01468 extension, tab_strip, i)
469 ->ToValue());
470 }
471
472 return tab_list;
473}
474
miu8b605f22015-08-15 02:56:56475// static
Devlin Cronin7050f8e2018-02-07 19:52:04476std::unique_ptr<base::DictionaryValue>
477ExtensionTabUtil::CreateWindowValueForExtension(
478 const Browser& browser,
479 const Extension* extension,
John Leebfbb37cb2019-08-26 22:23:18480 PopulateTabBehavior populate_tab_behavior,
481 Feature::Context context) {
Devlin Cronin7050f8e2018-02-07 19:52:04482 auto result = std::make_unique<base::DictionaryValue>();
483
oscarjohansson77164012018-06-11 07:00:11484 result->SetInteger(tabs_constants::kIdKey, browser.session_id().id());
485 result->SetString(tabs_constants::kWindowTypeKey,
486 GetBrowserWindowTypeText(browser));
Devlin Cronin7050f8e2018-02-07 19:52:04487 ui::BaseWindow* window = browser.window();
oscarjohansson77164012018-06-11 07:00:11488 result->SetBoolean(tabs_constants::kFocusedKey, window->IsActive());
Devlin Cronin7050f8e2018-02-07 19:52:04489 const Profile* profile = browser.profile();
oscarjohansson77164012018-06-11 07:00:11490 result->SetBoolean(tabs_constants::kIncognitoKey, profile->IsOffTheRecord());
Avi Drissman9e6533282019-07-17 17:25:12491 result->SetBoolean(
492 tabs_constants::kAlwaysOnTopKey,
493 window->GetZOrderLevel() == ui::ZOrderLevel::kFloatingWindow);
Devlin Cronin7050f8e2018-02-07 19:52:04494
495 std::string window_state;
496 if (window->IsMinimized()) {
oscarjohansson77164012018-06-11 07:00:11497 window_state = tabs_constants::kShowStateValueMinimized;
Devlin Cronin7050f8e2018-02-07 19:52:04498 } else if (window->IsFullscreen()) {
oscarjohansson77164012018-06-11 07:00:11499 window_state = tabs_constants::kShowStateValueFullscreen;
Ivan Sandrke0e92452019-03-21 09:32:48500 if (platform_util::IsBrowserLockedFullscreen(&browser))
oscarjohansson77164012018-06-11 07:00:11501 window_state = tabs_constants::kShowStateValueLockedFullscreen;
Devlin Cronin7050f8e2018-02-07 19:52:04502 } else if (window->IsMaximized()) {
oscarjohansson77164012018-06-11 07:00:11503 window_state = tabs_constants::kShowStateValueMaximized;
Devlin Cronin7050f8e2018-02-07 19:52:04504 } else {
oscarjohansson77164012018-06-11 07:00:11505 window_state = tabs_constants::kShowStateValueNormal;
Devlin Cronin7050f8e2018-02-07 19:52:04506 }
oscarjohansson77164012018-06-11 07:00:11507 result->SetString(tabs_constants::kShowStateKey, window_state);
Devlin Cronin7050f8e2018-02-07 19:52:04508
509 gfx::Rect bounds;
510 if (window->IsMinimized())
511 bounds = window->GetRestoredBounds();
512 else
513 bounds = window->GetBounds();
oscarjohansson77164012018-06-11 07:00:11514 result->SetInteger(tabs_constants::kLeftKey, bounds.x());
515 result->SetInteger(tabs_constants::kTopKey, bounds.y());
516 result->SetInteger(tabs_constants::kWidthKey, bounds.width());
517 result->SetInteger(tabs_constants::kHeightKey, bounds.height());
Devlin Cronin7050f8e2018-02-07 19:52:04518
519 if (populate_tab_behavior == kPopulateTabs)
John Leebfbb37cb2019-08-26 22:23:18520 result->Set(tabs_constants::kTabsKey,
521 CreateTabList(&browser, extension, context));
Devlin Cronin7050f8e2018-02-07 19:52:04522
523 return result;
524}
525
526// static
dchengc963c7142016-04-08 03:55:22527std::unique_ptr<api::tabs::MutedInfo> ExtensionTabUtil::CreateMutedInfo(
miu8b605f22015-08-15 02:56:56528 content::WebContents* contents) {
529 DCHECK(contents);
Peter Kasting479cc1752018-09-28 22:34:37530 auto info = std::make_unique<api::tabs::MutedInfo>();
limasdf6dcdc442016-02-26 04:58:26531 info->muted = contents->IsAudioMuted();
miu8b605f22015-08-15 02:56:56532 switch (chrome::GetTabAudioMutedReason(contents)) {
ortuno71c3f142016-03-30 18:26:58533 case TabMutedReason::NONE:
miu8b605f22015-08-15 02:56:56534 break;
Tommy Steimela8c3757e2017-08-24 00:15:46535 case TabMutedReason::CONTENT_SETTING:
Tommy Steimel371129502017-11-29 19:41:45536 case TabMutedReason::CONTENT_SETTING_CHROME:
Tommy Steimela8c3757e2017-08-24 00:15:46537 case TabMutedReason::CONTEXT_MENU:
limasdf6dcdc442016-02-26 04:58:26538 info->reason = api::tabs::MUTED_INFO_REASON_USER;
miu8b605f22015-08-15 02:56:56539 break;
ortuno71c3f142016-03-30 18:26:58540 case TabMutedReason::MEDIA_CAPTURE:
limasdf6dcdc442016-02-26 04:58:26541 info->reason = api::tabs::MUTED_INFO_REASON_CAPTURE;
miu8b605f22015-08-15 02:56:56542 break;
ortuno71c3f142016-03-30 18:26:58543 case TabMutedReason::EXTENSION:
limasdf6dcdc442016-02-26 04:58:26544 info->reason = api::tabs::MUTED_INFO_REASON_EXTENSION;
Peter Kasting479cc1752018-09-28 22:34:37545 info->extension_id = std::make_unique<std::string>(
546 LastMuteMetadata::FromWebContents(contents)->extension_id);
547 DCHECK(!info->extension_id->empty());
miu8b605f22015-08-15 02:56:56548 break;
549 }
limasdf6dcdc442016-02-26 04:58:26550 return info;
miu8b605f22015-08-15 02:56:56551}
552
limasdf6dcdc442016-02-26 04:58:26553// static
Ivan Sandrke2b20c62018-09-10 16:23:53554void ExtensionTabUtil::SetPlatformDelegate(std::unique_ptr<Delegate> delegate) {
Daniel Bratellbe89e3442018-09-11 15:44:57555 GetExtensionTabUtilDelegateWrapper() = std::move(delegate);
isandrke0951132017-04-24 17:53:31556}
557
558// static
John Lee76e40a32019-08-20 17:52:40559ExtensionTabUtil::ScrubTabBehavior ExtensionTabUtil::GetScrubTabBehavior(
560 const Extension* extension,
John Leebfbb37cb2019-08-26 22:23:18561 Feature::Context context,
John Lee76e40a32019-08-20 17:52:40562 content::WebContents* contents) {
John Leebfbb37cb2019-08-26 22:23:18563 return GetScrubTabBehaviorImpl(extension, context, contents->GetURL(),
John Lee76e40a32019-08-20 17:52:40564 GetTabId(contents));
565}
566
567// static
568ExtensionTabUtil::ScrubTabBehavior ExtensionTabUtil::GetScrubTabBehavior(
569 const Extension* extension,
John Leebfbb37cb2019-08-26 22:23:18570 Feature::Context context,
John Lee76e40a32019-08-20 17:52:40571 const GURL& url) {
John Leebfbb37cb2019-08-26 22:23:18572 return GetScrubTabBehaviorImpl(extension, context, url,
573 api::tabs::TAB_ID_NONE);
John Lee76e40a32019-08-20 17:52:40574}
575
576// static
577void ExtensionTabUtil::ScrubTabForExtension(
578 const Extension* extension,
579 content::WebContents* contents,
580 api::tabs::Tab* tab,
581 ScrubTabBehavior scrub_tab_behavior) {
582 switch (scrub_tab_behavior) {
583 case kScrubTabFully:
584 tab->url.reset();
585 tab->title.reset();
586 tab->fav_icon_url.reset();
587 break;
588 case kScrubTabUrlToOrigin:
589 tab->url =
590 std::make_unique<std::string>(GURL(*tab->url).GetOrigin().spec());
591 break;
592 case kDontScrubTab:
593 NOTREACHED();
Daniel Bratellbe89e3442018-09-11 15:44:57594 }
[email protected]ab3f61412013-01-29 21:55:07595}
596
Kelvin Jiang56584822019-08-26 22:38:39597// static
[email protected]ea049a02011-12-25 21:37:09598bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents,
[email protected]ac84431b2011-09-27 17:26:11599 TabStripModel** tab_strip_model,
600 int* tab_index) {
[email protected]ea049a02011-12-25 21:37:09601 DCHECK(web_contents);
[email protected]ac84431b2011-09-27 17:26:11602 DCHECK(tab_strip_model);
603 DCHECK(tab_index);
604
scottmg8abbff832016-01-28 22:57:37605 for (auto* browser : *BrowserList::GetInstance()) {
606 TabStripModel* tab_strip = browser->tab_strip_model();
[email protected]e52d0a42012-06-08 22:44:16607 int index = tab_strip->GetIndexOfWebContents(web_contents);
[email protected]ac84431b2011-09-27 17:26:11608 if (index != -1) {
609 *tab_strip_model = tab_strip;
610 *tab_index = index;
611 return true;
612 }
613 }
614
615 return false;
616}
617
618bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
[email protected]72f67972012-10-30 18:53:28619 WebContents** contents,
[email protected]ac84431b2011-09-27 17:26:11620 int* tab_id) {
621 DCHECK(browser);
622 DCHECK(contents);
623
[email protected]617ee962013-01-29 20:49:12624 *contents = browser->tab_strip_model()->GetActiveWebContents();
[email protected]ac84431b2011-09-27 17:26:11625 if (*contents) {
626 if (tab_id)
[email protected]72f67972012-10-30 18:53:28627 *tab_id = GetTabId(*contents);
[email protected]ac84431b2011-09-27 17:26:11628 return true;
629 }
630
631 return false;
632}
633
Istiaque Ahmed406b9182019-07-26 06:28:33634// static
[email protected]ac84431b2011-09-27 17:26:11635bool ExtensionTabUtil::GetTabById(int tab_id,
wjmaclean5b11eee2014-09-05 00:55:14636 content::BrowserContext* browser_context,
[email protected]ac84431b2011-09-27 17:26:11637 bool include_incognito,
638 Browser** browser,
639 TabStripModel** tab_strip,
[email protected]72f67972012-10-30 18:53:28640 WebContents** contents,
[email protected]ac84431b2011-09-27 17:26:11641 int* tab_index) {
lionel.g.landwerlind2315f72015-07-21 14:17:19642 if (tab_id == api::tabs::TAB_ID_NONE)
643 return false;
wjmaclean5b11eee2014-09-05 00:55:14644 Profile* profile = Profile::FromBrowserContext(browser_context);
[email protected]ac84431b2011-09-27 17:26:11645 Profile* incognito_profile =
Catherine Mullings0524e2042017-06-26 23:09:45646 include_incognito && profile->HasOffTheRecordProfile()
647 ? profile->GetOffTheRecordProfile()
648 : nullptr;
scottmg8abbff832016-01-28 22:57:37649 for (auto* target_browser : *BrowserList::GetInstance()) {
[email protected]ac84431b2011-09-27 17:26:11650 if (target_browser->profile() == profile ||
651 target_browser->profile() == incognito_profile) {
[email protected]c0849252012-05-12 13:51:27652 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
[email protected]ac84431b2011-09-27 17:26:11653 for (int i = 0; i < target_tab_strip->count(); ++i) {
[email protected]72f67972012-10-30 18:53:28654 WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
Mikel Astiz2f127c422018-04-05 19:10:27655 if (SessionTabHelper::IdForTab(target_contents).id() == tab_id) {
[email protected]ac84431b2011-09-27 17:26:11656 if (browser)
657 *browser = target_browser;
658 if (tab_strip)
659 *tab_strip = target_tab_strip;
660 if (contents)
661 *contents = target_contents;
662 if (tab_index)
663 *tab_index = i;
664 return true;
665 }
666 }
667 }
668 }
669 return false;
670}
[email protected]45c75e62012-03-21 19:56:35671
Istiaque Ahmed406b9182019-07-26 06:28:33672// static
673bool ExtensionTabUtil::GetTabById(int tab_id,
674 content::BrowserContext* browser_context,
675 bool include_incognito,
676 WebContents** contents) {
677 return GetTabById(tab_id, browser_context, include_incognito, nullptr,
678 nullptr, contents, nullptr);
679}
680
Kelvin Jiang56584822019-08-26 22:38:39681// static
682std::vector<content::WebContents*>
683ExtensionTabUtil::GetAllActiveWebContentsForContext(
684 content::BrowserContext* browser_context,
685 bool include_incognito) {
686 std::vector<content::WebContents*> active_contents;
687
688 Profile* profile = Profile::FromBrowserContext(browser_context);
689 Profile* incognito_profile =
690 include_incognito && profile->HasOffTheRecordProfile()
691 ? profile->GetOffTheRecordProfile()
692 : nullptr;
693 for (auto* target_browser : *BrowserList::GetInstance()) {
694 if (target_browser->profile() == profile ||
695 target_browser->profile() == incognito_profile) {
696 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
697
698 active_contents.push_back(target_tab_strip->GetActiveWebContents());
699 }
700 }
701
702 return active_contents;
703}
704
[email protected]45c75e62012-03-21 19:56:35705GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string,
[email protected]1c4fbc02013-11-13 02:52:42706 const Extension* extension) {
[email protected]45c75e62012-03-21 19:56:35707 GURL url = GURL(url_string);
708 if (!url.is_valid())
709 url = extension->GetResourceURL(url_string);
710
711 return url;
712}
713
kalmandfefe1a2015-07-13 22:27:54714bool ExtensionTabUtil::IsKillURL(const GURL& url) {
Lei Zhang4c28b5282017-08-24 04:26:05715 static const char* const kill_hosts[] = {
716 chrome::kChromeUICrashHost, chrome::kChromeUIDelayedHangUIHost,
717 chrome::kChromeUIHangUIHost, chrome::kChromeUIKillHost,
718 chrome::kChromeUIQuitHost, chrome::kChromeUIRestartHost,
719 content::kChromeUIBrowserCrashHost, content::kChromeUIMemoryExhaustHost,
kalmandfefe1a2015-07-13 22:27:54720 };
721
[email protected]45c75e62012-03-21 19:56:35722 // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
723 GURL fixed_url =
rsleevi24f64dc22015-08-07 21:39:21724 url_formatter::FixupURL(url.possibly_invalid_spec(), std::string());
kalmandfefe1a2015-07-13 22:27:54725 if (!fixed_url.SchemeIs(content::kChromeUIScheme))
726 return false;
727
brettwadc846882015-09-25 01:16:22728 base::StringPiece fixed_host = fixed_url.host_piece();
Avi Drissman5f0fb8c2018-12-25 23:20:49729 for (size_t i = 0; i < base::size(kill_hosts); ++i) {
brettwadc846882015-09-25 01:16:22730 if (fixed_host == kill_hosts[i])
kalmandfefe1a2015-07-13 22:27:54731 return true;
732 }
733
734 return false;
[email protected]45c75e62012-03-21 19:56:35735}
[email protected]73c1a6842012-07-13 17:39:04736
erikchen38fa4022018-04-26 20:37:52737void ExtensionTabUtil::CreateTab(std::unique_ptr<WebContents> web_contents,
[email protected]73c1a6842012-07-13 17:39:04738 const std::string& extension_id,
739 WindowOpenDisposition disposition,
bokan107a47f2015-02-03 23:23:39740 const gfx::Rect& initial_rect,
[email protected]73c1a6842012-07-13 17:39:04741 bool user_gesture) {
[email protected]73c1a6842012-07-13 17:39:04742 Profile* profile =
743 Profile::FromBrowserContext(web_contents->GetBrowserContext());
scottmg34c5dd882016-02-03 05:21:54744 Browser* browser = chrome::FindTabbedBrowser(profile, false);
[email protected]2764cab72012-07-19 17:02:10745 const bool browser_created = !browser;
erg7b01d692017-02-22 21:57:35746 if (!browser) {
747 Browser::CreateParams params = Browser::CreateParams(profile, user_gesture);
Toni Barzic00705742019-02-25 22:09:41748 browser = Browser::Create(params);
erg7b01d692017-02-22 21:57:35749 }
Toni Barzic00705742019-02-25 22:09:41750
751 if (!browser)
752 return;
753
erikchen38fa4022018-04-26 20:37:52754 NavigateParams params(browser, std::move(web_contents));
[email protected]73c1a6842012-07-13 17:39:04755
756 // The extension_app_id parameter ends up as app_name in the Browser
757 // which causes the Browser to return true for is_app(). This affects
758 // among other things, whether the location bar gets displayed.
759 // TODO(mpcomplete): This seems wrong. What if the extension content is hosted
760 // in a tab?
nick3b04f322016-08-31 19:29:19761 if (disposition == WindowOpenDisposition::NEW_POPUP)
[email protected]73c1a6842012-07-13 17:39:04762 params.extension_app_id = extension_id;
763
[email protected]73c1a6842012-07-13 17:39:04764 params.disposition = disposition;
bokan107a47f2015-02-03 23:23:39765 params.window_bounds = initial_rect;
cm.sanchi2522bc92017-12-04 08:04:13766 params.window_action = NavigateParams::SHOW_WINDOW;
[email protected]73c1a6842012-07-13 17:39:04767 params.user_gesture = user_gesture;
cm.sanchi2522bc92017-12-04 08:04:13768 Navigate(&params);
[email protected]2764cab72012-07-19 17:02:10769
cm.sanchi2522bc92017-12-04 08:04:13770 // Close the browser if Navigate created a new one.
[email protected]2764cab72012-07-19 17:02:10771 if (browser_created && (browser != params.browser))
772 browser->window()->Close();
[email protected]73c1a6842012-07-13 17:39:04773}
[email protected]44e329a2012-07-14 01:13:06774
775// static
776void ExtensionTabUtil::ForEachTab(
777 const base::Callback<void(WebContents*)>& callback) {
Avi Drissman2f97ce52018-02-07 23:48:23778 for (auto* web_contents : AllTabContentses())
779 callback.Run(web_contents);
[email protected]44e329a2012-07-14 01:13:06780}
[email protected]e9570fdf2012-07-18 20:01:21781
782// static
[email protected]1c4fbc02013-11-13 02:52:42783WindowController* ExtensionTabUtil::GetWindowControllerOfTab(
[email protected]e9570fdf2012-07-18 20:01:21784 const WebContents* web_contents) {
[email protected]f7b4b9e2012-12-02 07:43:17785 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
Catherine Mullings0524e2042017-06-26 23:09:45786 if (browser != nullptr)
[email protected]e9570fdf2012-07-18 20:01:21787 return browser->extension_window_controller();
788
Catherine Mullings0524e2042017-06-26 23:09:45789 return nullptr;
[email protected]e9570fdf2012-07-18 20:01:21790}
[email protected]695089782013-04-09 16:03:17791
catmullings32ab4eeb2017-02-21 18:23:54792bool ExtensionTabUtil::OpenOptionsPageFromAPI(
793 const Extension* extension,
794 content::BrowserContext* browser_context) {
795 if (!OptionsPageInfo::HasOptionsPage(extension))
796 return false;
797 Profile* profile = Profile::FromBrowserContext(browser_context);
798 // This version of OpenOptionsPage() is only called when the extension
799 // initiated the command via chrome.runtime.openOptionsPage. For a spanning
800 // mode extension, this API could only be called from a regular profile, since
801 // that's the only place it's running.
802 DCHECK(!profile->IsOffTheRecord() || IncognitoInfo::IsSplitMode(extension));
803 Browser* browser = chrome::FindBrowserWithProfile(profile);
804 if (!browser)
Toni Barzic00705742019-02-25 22:09:41805 browser = Browser::Create(Browser::CreateParams(profile, true));
806 if (!browser)
807 return false;
catmullings32ab4eeb2017-02-21 18:23:54808 return extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser);
809}
810
kalmance22c472015-02-19 02:31:43811bool ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
[email protected]695089782013-04-09 16:03:17812 Browser* browser) {
kalmance22c472015-02-19 02:31:43813 if (!OptionsPageInfo::HasOptionsPage(extension))
814 return false;
[email protected]695089782013-04-09 16:03:17815
catmullings32ab4eeb2017-02-21 18:23:54816 // Force the options page to open in non-OTR window if the extension is not
817 // running in split mode, because it won't be able to save settings from OTR.
818 // This version of OpenOptionsPage() can be called from an OTR window via e.g.
819 // the action menu, since that's not initiated by the extension.
dchengc963c7142016-04-08 03:55:22820 std::unique_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
catmullings32ab4eeb2017-02-21 18:23:54821 if (browser->profile()->IsOffTheRecord() &&
822 !IncognitoInfo::IsSplitMode(extension)) {
[email protected]0edcdec2013-10-31 06:43:08823 displayer.reset(new chrome::ScopedTabbedBrowserDisplayer(
scottmg851949002016-02-09 20:09:44824 browser->profile()->GetOriginalProfile()));
[email protected]0edcdec2013-10-31 06:43:08825 browser = displayer->browser();
[email protected]695089782013-04-09 16:03:17826 }
827
kalman4427c252015-03-13 01:55:48828 GURL url_to_navigate;
rdevlin.cronin6ef722e2016-03-16 21:42:27829 bool open_in_tab = OptionsPageInfo::ShouldOpenInTab(extension);
830 if (open_in_tab) {
kalman4427c252015-03-13 01:55:48831 // Options page tab is simply e.g. chrome-extension://.../options.html.
832 url_to_navigate = OptionsPageInfo::GetOptionsPage(extension);
833 } else {
834 // Options page tab is Extension settings pointed at that Extension's ID,
835 // e.g. chrome://extensions?options=...
836 url_to_navigate = GURL(chrome::kChromeUIExtensionsURL);
ericzengf97b7c22014-08-26 03:07:30837 GURL::Replacements replacements;
838 std::string query =
839 base::StringPrintf("options=%s", extension->id().c_str());
840 replacements.SetQueryStr(query);
kalman4427c252015-03-13 01:55:48841 url_to_navigate = url_to_navigate.ReplaceComponents(replacements);
ericzengf97b7c22014-08-26 03:07:30842 }
kalmance22c472015-02-19 02:31:43843
cm.sanchi2522bc92017-12-04 08:04:13844 NavigateParams params(
845 GetSingletonTabNavigateParams(browser, url_to_navigate));
rdevlin.cronin10a6ef012016-02-19 00:26:03846 // We need to respect path differences because we don't want opening the
847 // options page to close a page that might be open to extension content.
rdevlin.cronin6ef722e2016-03-16 21:42:27848 // However, if the options page opens inside the chrome://extensions page, we
849 // can override an existing page.
Kevin Bailey55841fa72018-03-12 22:52:05850 // Note: ref behavior is to ignore.
cm.sanchi2522bc92017-12-04 08:04:13851 params.path_behavior = open_in_tab ? NavigateParams::RESPECT
852 : NavigateParams::IGNORE_AND_NAVIGATE;
kalman4427c252015-03-13 01:55:48853 params.url = url_to_navigate;
erikchen38fa4022018-04-26 20:37:52854 ShowSingletonTabOverwritingNTP(browser, std::move(params));
kalmance22c472015-02-19 02:31:43855 return true;
[email protected]695089782013-04-09 16:03:17856}
[email protected]1c4fbc02013-11-13 02:52:42857
lionel.g.landwerlind2315f72015-07-21 14:17:19858// static
859bool ExtensionTabUtil::BrowserSupportsTabs(Browser* browser) {
Joel Hockey2687ab52019-08-14 23:59:46860 return browser && !browser->is_type_devtools();
lionel.g.landwerlind2315f72015-07-21 14:17:19861}
862
[email protected]1c4fbc02013-11-13 02:52:42863} // namespace extensions