[Sync] Rename PSS::IsSyncEnabled to PSS::IsSyncAllowedByFlag.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_tab_util.cc
blobf1783f72ecba294145bea60075ecb628e8dd1d18
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.
5 #include "chrome/browser/extensions/extension_tab_util.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/stringprintf.h"
9 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
10 #include "chrome/browser/extensions/chrome_extension_function.h"
11 #include "chrome/browser/extensions/chrome_extension_function_details.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/browser/extensions/window_controller.h"
14 #include "chrome/browser/extensions/window_controller_list.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/sessions/session_tab_helper.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_finder.h"
19 #include "chrome/browser/ui/browser_iterator.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
22 #include "chrome/browser/ui/singleton_tabs.h"
23 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/common/extensions/api/tabs.h"
26 #include "chrome/common/url_constants.h"
27 #include "components/url_fixer/url_fixer.h"
28 #include "content/public/browser/favicon_status.h"
29 #include "content/public/browser/navigation_entry.h"
30 #include "content/public/browser/web_contents.h"
31 #include "extensions/browser/app_window/app_window.h"
32 #include "extensions/browser/app_window/app_window_registry.h"
33 #include "extensions/common/constants.h"
34 #include "extensions/common/error_utils.h"
35 #include "extensions/common/extension.h"
36 #include "extensions/common/feature_switch.h"
37 #include "extensions/common/manifest_constants.h"
38 #include "extensions/common/manifest_handlers/incognito_info.h"
39 #include "extensions/common/manifest_handlers/options_page_info.h"
40 #include "extensions/common/permissions/api_permission.h"
41 #include "extensions/common/permissions/permissions_data.h"
42 #include "url/gurl.h"
44 using content::NavigationEntry;
45 using content::WebContents;
47 namespace extensions {
49 namespace {
51 namespace keys = tabs_constants;
53 WindowController* GetAppWindowController(const WebContents* contents) {
54 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
55 AppWindowRegistry* registry = AppWindowRegistry::Get(profile);
56 if (!registry)
57 return NULL;
58 AppWindow* app_window =
59 registry->GetAppWindowForRenderViewHost(contents->GetRenderViewHost());
60 if (!app_window)
61 return NULL;
62 return WindowControllerList::GetInstance()->FindWindowById(
63 app_window->session_id().id());
66 // |error_message| can optionally be passed in and will be set with an
67 // appropriate message if the window cannot be found by id.
68 Browser* GetBrowserInProfileWithId(Profile* profile,
69 const int window_id,
70 bool include_incognito,
71 std::string* error_message) {
72 Profile* incognito_profile =
73 include_incognito && profile->HasOffTheRecordProfile()
74 ? profile->GetOffTheRecordProfile()
75 : NULL;
76 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
77 Browser* browser = *it;
78 if ((browser->profile() == profile ||
79 browser->profile() == incognito_profile) &&
80 ExtensionTabUtil::GetWindowId(browser) == window_id &&
81 browser->window()) {
82 return browser;
86 if (error_message)
87 *error_message = ErrorUtils::FormatErrorMessage(
88 keys::kWindowNotFoundError, base::IntToString(window_id));
90 return NULL;
93 Browser* CreateBrowser(ChromeUIThreadExtensionFunction* function,
94 int window_id,
95 std::string* error) {
96 content::WebContents* web_contents = function->GetAssociatedWebContents();
97 chrome::HostDesktopType desktop_type =
98 web_contents && web_contents->GetNativeView()
99 ? chrome::GetHostDesktopTypeForNativeView(
100 web_contents->GetNativeView())
101 : chrome::GetHostDesktopTypeForNativeView(NULL);
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;
109 } // namespace
111 ExtensionTabUtil::OpenTabParams::OpenTabParams()
112 : create_browser_if_needed(false) {
115 ExtensionTabUtil::OpenTabParams::~OpenTabParams() {
118 // Opens a new tab for a given extension. Returns NULL and sets |error| if an
119 // error occurs.
120 base::DictionaryValue* ExtensionTabUtil::OpenTab(
121 ChromeUIThreadExtensionFunction* function,
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;
129 Browser* browser = GetBrowserFromWindowID(function, window_id, error);
130 if (!browser) {
131 if (!params.create_browser_if_needed) {
132 return NULL;
134 browser = CreateBrowser(function, window_id, error);
135 if (!browser)
136 return NULL;
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());
145 if (!browser || !browser->window()) {
146 if (error)
147 *error = keys::kNoCurrentWindowError;
148 return NULL;
151 // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
152 // represents the active tab.
153 WebContents* opener = NULL;
154 if (params.opener_tab_id.get()) {
155 int opener_id = *params.opener_tab_id;
157 if (!ExtensionTabUtil::GetTabById(opener_id,
158 function->GetProfile(),
159 function->include_incognito(),
160 NULL,
161 NULL,
162 &opener,
163 NULL)) {
164 if (error) {
165 *error = ErrorUtils::FormatErrorMessage(keys::kTabNotFoundError,
166 base::IntToString(opener_id));
168 return NULL;
172 // TODO(rafaelw): handle setting remaining tab properties:
173 // -title
174 // -favIconUrl
176 GURL url;
177 if (params.url.get()) {
178 std::string url_string = *params.url;
179 url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
180 function->extension());
181 if (!url.is_valid()) {
182 *error =
183 ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string);
184 return NULL;
186 } else {
187 url = GURL(chrome::kChromeUINewTabURL);
190 // Don't let extensions crash the browser or renderers.
191 if (ExtensionTabUtil::IsCrashURL(url)) {
192 *error = keys::kNoCrashBrowserError;
193 return NULL;
196 // Default to foreground for the new tab. The presence of 'active' property
197 // will override this default.
198 bool active = true;
199 if (params.active.get())
200 active = *params.active;
202 // Default to not pinning the tab. Setting the 'pinned' property to true
203 // will override this default.
204 bool pinned = false;
205 if (params.pinned.get())
206 pinned = *params.pinned;
208 // We can't load extension URLs into incognito windows unless the extension
209 // uses split mode. Special case to fall back to a tabbed window.
210 if (url.SchemeIs(kExtensionScheme) &&
211 !IncognitoInfo::IsSplitMode(function->extension()) &&
212 browser->profile()->IsOffTheRecord()) {
213 Profile* profile = browser->profile()->GetOriginalProfile();
214 chrome::HostDesktopType desktop_type = browser->host_desktop_type();
216 browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
217 if (!browser) {
218 browser = new Browser(
219 Browser::CreateParams(Browser::TYPE_TABBED, profile, desktop_type));
220 browser->window()->Show();
224 // If index is specified, honor the value, but keep it bound to
225 // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
226 int index = -1;
227 if (params.index.get())
228 index = *params.index;
230 TabStripModel* tab_strip = browser->tab_strip_model();
232 index = std::min(std::max(index, -1), tab_strip->count());
234 int add_types = active ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE;
235 add_types |= TabStripModel::ADD_FORCE_INDEX;
236 if (pinned)
237 add_types |= TabStripModel::ADD_PINNED;
238 chrome::NavigateParams navigate_params(
239 browser, url, ui::PAGE_TRANSITION_LINK);
240 navigate_params.disposition =
241 active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
242 navigate_params.tabstrip_index = index;
243 navigate_params.tabstrip_add_types = add_types;
244 chrome::Navigate(&navigate_params);
246 // The tab may have been created in a different window, so make sure we look
247 // at the right tab strip.
248 tab_strip = navigate_params.browser->tab_strip_model();
249 int new_index =
250 tab_strip->GetIndexOfWebContents(navigate_params.target_contents);
251 if (opener)
252 tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
254 if (active)
255 navigate_params.target_contents->SetInitialFocus();
257 // Return data about the newly created tab.
258 return ExtensionTabUtil::CreateTabValue(navigate_params.target_contents,
259 tab_strip,
260 new_index,
261 function->extension());
264 Browser* ExtensionTabUtil::GetBrowserFromWindowID(
265 ChromeUIThreadExtensionFunction* function,
266 int window_id,
267 std::string* error) {
268 if (window_id == extension_misc::kCurrentWindowId) {
269 Browser* result = function->GetCurrentBrowser();
270 if (!result || !result->window()) {
271 if (error)
272 *error = keys::kNoCurrentWindowError;
273 return NULL;
275 return result;
276 } else {
277 return GetBrowserInProfileWithId(function->GetProfile(),
278 window_id,
279 function->include_incognito(),
280 error);
284 Browser* ExtensionTabUtil::GetBrowserFromWindowID(
285 const ChromeExtensionFunctionDetails& details,
286 int window_id,
287 std::string* error) {
288 if (window_id == extension_misc::kCurrentWindowId) {
289 Browser* result = details.GetCurrentBrowser();
290 if (!result || !result->window()) {
291 if (error)
292 *error = keys::kNoCurrentWindowError;
293 return NULL;
295 return result;
296 } else {
297 return GetBrowserInProfileWithId(details.GetProfile(),
298 window_id,
299 details.function()->include_incognito(),
300 error);
304 int ExtensionTabUtil::GetWindowId(const Browser* browser) {
305 return browser->session_id().id();
308 int ExtensionTabUtil::GetWindowIdOfTabStripModel(
309 const TabStripModel* tab_strip_model) {
310 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
311 if (it->tab_strip_model() == tab_strip_model)
312 return GetWindowId(*it);
314 return -1;
317 int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
318 return SessionTabHelper::IdForTab(web_contents);
321 std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
322 return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
325 int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
326 return SessionTabHelper::IdForWindowContainingTab(web_contents);
329 base::DictionaryValue* ExtensionTabUtil::CreateTabValue(
330 WebContents* contents,
331 TabStripModel* tab_strip,
332 int tab_index,
333 const Extension* extension) {
334 // If we have a matching AppWindow with a controller, get the tab value
335 // from its controller instead.
336 WindowController* controller = GetAppWindowController(contents);
337 if (controller &&
338 (!extension || controller->IsVisibleToExtension(extension))) {
339 return controller->CreateTabValue(extension, tab_index);
341 base::DictionaryValue* result =
342 CreateTabValue(contents, tab_strip, tab_index);
343 ScrubTabValueForExtension(contents, extension, result);
344 return result;
347 base::ListValue* ExtensionTabUtil::CreateTabList(
348 const Browser* browser,
349 const Extension* extension) {
350 base::ListValue* tab_list = new base::ListValue();
351 TabStripModel* tab_strip = browser->tab_strip_model();
352 for (int i = 0; i < tab_strip->count(); ++i) {
353 tab_list->Append(CreateTabValue(tab_strip->GetWebContentsAt(i),
354 tab_strip,
356 extension));
359 return tab_list;
362 base::DictionaryValue* ExtensionTabUtil::CreateTabValue(
363 WebContents* contents,
364 TabStripModel* tab_strip,
365 int tab_index) {
366 // If we have a matching AppWindow with a controller, get the tab value
367 // from its controller instead.
368 WindowController* controller = GetAppWindowController(contents);
369 if (controller)
370 return controller->CreateTabValue(NULL, tab_index);
372 if (!tab_strip)
373 ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index);
375 base::DictionaryValue* result = new base::DictionaryValue();
376 bool is_loading = contents->IsLoading();
377 result->SetInteger(keys::kIdKey, GetTabId(contents));
378 result->SetInteger(keys::kIndexKey, tab_index);
379 result->SetInteger(keys::kWindowIdKey, GetWindowIdOfTab(contents));
380 result->SetString(keys::kStatusKey, GetTabStatusText(is_loading));
381 result->SetBoolean(keys::kActiveKey,
382 tab_strip && tab_index == tab_strip->active_index());
383 result->SetBoolean(keys::kSelectedKey,
384 tab_strip && tab_index == tab_strip->active_index());
385 result->SetBoolean(keys::kHighlightedKey,
386 tab_strip && tab_strip->IsTabSelected(tab_index));
387 result->SetBoolean(keys::kPinnedKey,
388 tab_strip && tab_strip->IsTabPinned(tab_index));
389 result->SetBoolean(keys::kIncognitoKey,
390 contents->GetBrowserContext()->IsOffTheRecord());
391 result->SetInteger(keys::kWidthKey,
392 contents->GetContainerBounds().size().width());
393 result->SetInteger(keys::kHeightKey,
394 contents->GetContainerBounds().size().height());
396 // Privacy-sensitive fields: these should be stripped off by
397 // ScrubTabValueForExtension if the extension should not see them.
398 result->SetString(keys::kUrlKey, contents->GetURL().spec());
399 result->SetString(keys::kTitleKey, contents->GetTitle());
400 if (!is_loading) {
401 NavigationEntry* entry = contents->GetController().GetVisibleEntry();
402 if (entry && entry->GetFavicon().valid)
403 result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec());
406 if (tab_strip) {
407 WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
408 if (opener)
409 result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener));
412 return result;
415 void ExtensionTabUtil::ScrubTabValueForExtension(
416 WebContents* contents,
417 const Extension* extension,
418 base::DictionaryValue* tab_info) {
419 bool has_permission = extension &&
420 extension->permissions_data()->HasAPIPermissionForTab(
421 GetTabId(contents), APIPermission::kTab);
423 if (!has_permission) {
424 tab_info->Remove(keys::kUrlKey, NULL);
425 tab_info->Remove(keys::kTitleKey, NULL);
426 tab_info->Remove(keys::kFaviconUrlKey, NULL);
430 void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension,
431 api::tabs::Tab* tab) {
432 bool has_permission =
433 extension &&
434 extension->permissions_data()->HasAPIPermission(APIPermission::kTab);
436 if (!has_permission) {
437 tab->url.reset();
438 tab->title.reset();
439 tab->fav_icon_url.reset();
443 bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents,
444 TabStripModel** tab_strip_model,
445 int* tab_index) {
446 DCHECK(web_contents);
447 DCHECK(tab_strip_model);
448 DCHECK(tab_index);
450 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
451 TabStripModel* tab_strip = it->tab_strip_model();
452 int index = tab_strip->GetIndexOfWebContents(web_contents);
453 if (index != -1) {
454 *tab_strip_model = tab_strip;
455 *tab_index = index;
456 return true;
460 return false;
463 bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
464 WebContents** contents,
465 int* tab_id) {
466 DCHECK(browser);
467 DCHECK(contents);
469 *contents = browser->tab_strip_model()->GetActiveWebContents();
470 if (*contents) {
471 if (tab_id)
472 *tab_id = GetTabId(*contents);
473 return true;
476 return false;
479 bool ExtensionTabUtil::GetTabById(int tab_id,
480 content::BrowserContext* browser_context,
481 bool include_incognito,
482 Browser** browser,
483 TabStripModel** tab_strip,
484 WebContents** contents,
485 int* tab_index) {
486 Profile* profile = Profile::FromBrowserContext(browser_context);
487 Profile* incognito_profile =
488 include_incognito && profile->HasOffTheRecordProfile() ?
489 profile->GetOffTheRecordProfile() : NULL;
490 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
491 Browser* target_browser = *it;
492 if (target_browser->profile() == profile ||
493 target_browser->profile() == incognito_profile) {
494 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
495 for (int i = 0; i < target_tab_strip->count(); ++i) {
496 WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
497 if (SessionTabHelper::IdForTab(target_contents) == tab_id) {
498 if (browser)
499 *browser = target_browser;
500 if (tab_strip)
501 *tab_strip = target_tab_strip;
502 if (contents)
503 *contents = target_contents;
504 if (tab_index)
505 *tab_index = i;
506 return true;
511 return false;
514 GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string,
515 const Extension* extension) {
516 GURL url = GURL(url_string);
517 if (!url.is_valid())
518 url = extension->GetResourceURL(url_string);
520 return url;
523 bool ExtensionTabUtil::IsCrashURL(const GURL& url) {
524 // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
525 GURL fixed_url =
526 url_fixer::FixupURL(url.possibly_invalid_spec(), std::string());
527 return (fixed_url.SchemeIs(content::kChromeUIScheme) &&
528 (fixed_url.host() == content::kChromeUIBrowserCrashHost ||
529 fixed_url.host() == chrome::kChromeUICrashHost));
532 void ExtensionTabUtil::CreateTab(WebContents* web_contents,
533 const std::string& extension_id,
534 WindowOpenDisposition disposition,
535 const gfx::Rect& initial_rect,
536 bool user_gesture) {
537 Profile* profile =
538 Profile::FromBrowserContext(web_contents->GetBrowserContext());
539 chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
540 Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop);
541 const bool browser_created = !browser;
542 if (!browser)
543 browser = new Browser(Browser::CreateParams(profile, active_desktop));
544 chrome::NavigateParams params(browser, web_contents);
546 // The extension_app_id parameter ends up as app_name in the Browser
547 // which causes the Browser to return true for is_app(). This affects
548 // among other things, whether the location bar gets displayed.
549 // TODO(mpcomplete): This seems wrong. What if the extension content is hosted
550 // in a tab?
551 if (disposition == NEW_POPUP)
552 params.extension_app_id = extension_id;
554 params.disposition = disposition;
555 params.window_bounds = initial_rect;
556 params.window_action = chrome::NavigateParams::SHOW_WINDOW;
557 params.user_gesture = user_gesture;
558 chrome::Navigate(&params);
560 // Close the browser if chrome::Navigate created a new one.
561 if (browser_created && (browser != params.browser))
562 browser->window()->Close();
565 // static
566 void ExtensionTabUtil::ForEachTab(
567 const base::Callback<void(WebContents*)>& callback) {
568 for (TabContentsIterator iterator; !iterator.done(); iterator.Next())
569 callback.Run(*iterator);
572 // static
573 WindowController* ExtensionTabUtil::GetWindowControllerOfTab(
574 const WebContents* web_contents) {
575 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
576 if (browser != NULL)
577 return browser->extension_window_controller();
579 return NULL;
582 bool ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
583 Browser* browser) {
584 if (!OptionsPageInfo::HasOptionsPage(extension))
585 return false;
587 // Force the options page to open in non-OTR window, because it won't be
588 // able to save settings from OTR.
589 scoped_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
590 if (browser->profile()->IsOffTheRecord()) {
591 displayer.reset(new chrome::ScopedTabbedBrowserDisplayer(
592 browser->profile()->GetOriginalProfile(),
593 browser->host_desktop_type()));
594 browser = displayer->browser();
597 GURL url_to_navigate;
598 if (OptionsPageInfo::ShouldOpenInTab(extension)) {
599 // Options page tab is simply e.g. chrome-extension://.../options.html.
600 url_to_navigate = OptionsPageInfo::GetOptionsPage(extension);
601 } else {
602 // Options page tab is Extension settings pointed at that Extension's ID,
603 // e.g. chrome://extensions?options=...
604 url_to_navigate = GURL(chrome::kChromeUIExtensionsURL);
605 GURL::Replacements replacements;
606 std::string query =
607 base::StringPrintf("options=%s", extension->id().c_str());
608 replacements.SetQueryStr(query);
609 url_to_navigate = url_to_navigate.ReplaceComponents(replacements);
612 chrome::NavigateParams params(
613 chrome::GetSingletonTabNavigateParams(browser, url_to_navigate));
614 params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
615 params.url = url_to_navigate;
616 chrome::ShowSingletonTabOverwritingNTP(browser, params);
617 return true;
620 } // namespace extensions