// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_tab_util.h" #include "apps/shell_window.h" #include "apps/shell_window_registry.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/extensions/window_controller.h" #include "chrome/browser/extensions/window_controller_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_id.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "chrome/common/net/url_fixer_upper.h" #include "chrome/common/url_constants.h" #include "content/public/browser/favicon_status.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_view.h" #include "extensions/common/extension.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/permissions_data.h" #include "url/gurl.h" using apps::ShellWindow; using content::NavigationEntry; using content::WebContents; namespace extensions { namespace { namespace keys = tabs_constants; WindowController* GetShellWindowController(const WebContents* contents) { Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); apps::ShellWindowRegistry* registry = apps::ShellWindowRegistry::Get(profile); if (!registry) return NULL; ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost(contents->GetRenderViewHost()); if (!shell_window) return NULL; return WindowControllerList::GetInstance()-> FindWindowById(shell_window->session_id().id()); } } // namespace int ExtensionTabUtil::GetWindowId(const Browser* browser) { return browser->session_id().id(); } int ExtensionTabUtil::GetWindowIdOfTabStripModel( const TabStripModel* tab_strip_model) { for (chrome::BrowserIterator it; !it.done(); it.Next()) { if (it->tab_strip_model() == tab_strip_model) return GetWindowId(*it); } return -1; } int ExtensionTabUtil::GetTabId(const WebContents* web_contents) { return SessionID::IdForTab(web_contents); } std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) { return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete; } int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) { return SessionID::IdForWindowContainingTab(web_contents); } DictionaryValue* ExtensionTabUtil::CreateTabValue( const WebContents* contents, TabStripModel* tab_strip, int tab_index, const Extension* extension) { // If we have a matching ShellWindow with a controller, get the tab value // from its controller instead. WindowController* controller = GetShellWindowController(contents); if (controller && (!extension || controller->IsVisibleToExtension(extension))) { return controller->CreateTabValue(extension, tab_index); } DictionaryValue *result = CreateTabValue(contents, tab_strip, tab_index); ScrubTabValueForExtension(contents, extension, result); return result; } base::ListValue* ExtensionTabUtil::CreateTabList( const Browser* browser, const Extension* extension) { base::ListValue* tab_list = new base::ListValue(); TabStripModel* tab_strip = browser->tab_strip_model(); for (int i = 0; i < tab_strip->count(); ++i) { tab_list->Append(CreateTabValue(tab_strip->GetWebContentsAt(i), tab_strip, i, extension)); } return tab_list; } DictionaryValue* ExtensionTabUtil::CreateTabValue( const WebContents* contents, TabStripModel* tab_strip, int tab_index) { // If we have a matching ShellWindow with a controller, get the tab value // from its controller instead. WindowController* controller = GetShellWindowController(contents); if (controller) return controller->CreateTabValue(NULL, tab_index); if (!tab_strip) ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index); DictionaryValue* result = new DictionaryValue(); bool is_loading = contents->IsLoading(); result->SetInteger(keys::kIdKey, GetTabId(contents)); result->SetInteger(keys::kIndexKey, tab_index); result->SetInteger(keys::kWindowIdKey, GetWindowIdOfTab(contents)); result->SetString(keys::kStatusKey, GetTabStatusText(is_loading)); result->SetBoolean(keys::kActiveKey, tab_strip && tab_index == tab_strip->active_index()); result->SetBoolean(keys::kSelectedKey, tab_strip && tab_index == tab_strip->active_index()); result->SetBoolean(keys::kHighlightedKey, tab_strip && tab_strip->IsTabSelected(tab_index)); result->SetBoolean(keys::kPinnedKey, tab_strip && tab_strip->IsTabPinned(tab_index)); result->SetBoolean(keys::kIncognitoKey, contents->GetBrowserContext()->IsOffTheRecord()); result->SetInteger(keys::kWidthKey, contents->GetView()->GetContainerSize().width()); result->SetInteger(keys::kHeightKey, contents->GetView()->GetContainerSize().height()); // Privacy-sensitive fields: these should be stripped off by // ScrubTabValueForExtension if the extension should not see them. result->SetString(keys::kUrlKey, contents->GetURL().spec()); result->SetString(keys::kTitleKey, contents->GetTitle()); if (!is_loading) { NavigationEntry* entry = contents->GetController().GetVisibleEntry(); if (entry && entry->GetFavicon().valid) result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec()); } if (tab_strip) { WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index); if (opener) result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener)); } return result; } void ExtensionTabUtil::ScrubTabValueForExtension(const WebContents* contents, const Extension* extension, DictionaryValue* tab_info) { bool has_permission = extension && PermissionsData::HasAPIPermissionForTab( extension, GetTabId(contents), APIPermission::kTab); if (!has_permission) { tab_info->Remove(keys::kUrlKey, NULL); tab_info->Remove(keys::kTitleKey, NULL); tab_info->Remove(keys::kFaviconUrlKey, NULL); } } void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension, api::tabs::Tab* tab) { bool has_permission = extension && extension->HasAPIPermission( APIPermission::kTab); if (!has_permission) { tab->url.reset(); tab->title.reset(); tab->fav_icon_url.reset(); } } bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents, TabStripModel** tab_strip_model, int* tab_index) { DCHECK(web_contents); DCHECK(tab_strip_model); DCHECK(tab_index); for (chrome::BrowserIterator it; !it.done(); it.Next()) { TabStripModel* tab_strip = it->tab_strip_model(); int index = tab_strip->GetIndexOfWebContents(web_contents); if (index != -1) { *tab_strip_model = tab_strip; *tab_index = index; return true; } } return false; } bool ExtensionTabUtil::GetDefaultTab(Browser* browser, WebContents** contents, int* tab_id) { DCHECK(browser); DCHECK(contents); *contents = browser->tab_strip_model()->GetActiveWebContents(); if (*contents) { if (tab_id) *tab_id = GetTabId(*contents); return true; } return false; } bool ExtensionTabUtil::GetTabById(int tab_id, Profile* profile, bool include_incognito, Browser** browser, TabStripModel** tab_strip, WebContents** contents, int* tab_index) { Profile* incognito_profile = include_incognito && profile->HasOffTheRecordProfile() ? profile->GetOffTheRecordProfile() : NULL; for (chrome::BrowserIterator it; !it.done(); it.Next()) { Browser* target_browser = *it; if (target_browser->profile() == profile || target_browser->profile() == incognito_profile) { TabStripModel* target_tab_strip = target_browser->tab_strip_model(); for (int i = 0; i < target_tab_strip->count(); ++i) { WebContents* target_contents = target_tab_strip->GetWebContentsAt(i); if (SessionID::IdForTab(target_contents) == tab_id) { if (browser) *browser = target_browser; if (tab_strip) *tab_strip = target_tab_strip; if (contents) *contents = target_contents; if (tab_index) *tab_index = i; return true; } } } } return false; } GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string, const Extension* extension) { GURL url = GURL(url_string); if (!url.is_valid()) url = extension->GetResourceURL(url_string); return url; } bool ExtensionTabUtil::IsCrashURL(const GURL& url) { // Check a fixed-up URL, to normalize the scheme and parse hosts correctly. GURL fixed_url = URLFixerUpper::FixupURL(url.possibly_invalid_spec(), std::string()); return (fixed_url.SchemeIs(chrome::kChromeUIScheme) && (fixed_url.host() == content::kChromeUIBrowserCrashHost || fixed_url.host() == chrome::kChromeUICrashHost)); } void ExtensionTabUtil::CreateTab(WebContents* web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) { Profile* profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop(); Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop); const bool browser_created = !browser; if (!browser) browser = new Browser(Browser::CreateParams(profile, active_desktop)); chrome::NavigateParams params(browser, web_contents); // The extension_app_id parameter ends up as app_name in the Browser // which causes the Browser to return true for is_app(). This affects // among other things, whether the location bar gets displayed. // TODO(mpcomplete): This seems wrong. What if the extension content is hosted // in a tab? if (disposition == NEW_POPUP) params.extension_app_id = extension_id; params.disposition = disposition; params.window_bounds = initial_pos; params.window_action = chrome::NavigateParams::SHOW_WINDOW; params.user_gesture = user_gesture; chrome::Navigate(¶ms); // Close the browser if chrome::Navigate created a new one. if (browser_created && (browser != params.browser)) browser->window()->Close(); } // static void ExtensionTabUtil::ForEachTab( const base::Callback<void(WebContents*)>& callback) { for (TabContentsIterator iterator; !iterator.done(); iterator.Next()) callback.Run(*iterator); } // static WindowController* ExtensionTabUtil::GetWindowControllerOfTab( const WebContents* web_contents) { Browser* browser = chrome::FindBrowserWithWebContents(web_contents); if (browser != NULL) return browser->extension_window_controller(); return NULL; } void ExtensionTabUtil::OpenOptionsPage(const Extension* extension, Browser* browser) { DCHECK(!ManifestURL::GetOptionsPage(extension).is_empty()); // Force the options page to open in non-OTR window, because it won't be // able to save settings from OTR. scoped_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer; if (browser->profile()->IsOffTheRecord()) { displayer.reset(new chrome::ScopedTabbedBrowserDisplayer( browser->profile()->GetOriginalProfile(), browser->host_desktop_type())); browser = displayer->browser(); } content::OpenURLParams params(ManifestURL::GetOptionsPage(extension), content::Referrer(), SINGLETON_TAB, content::PAGE_TRANSITION_LINK, false); browser->OpenURL(params); browser->window()->Show(); WebContents* web_contents = browser->tab_strip_model()->GetActiveWebContents(); web_contents->GetDelegate()->ActivateContents(web_contents); } } // namespace extensions