diff --git a/chrome/android/java/res/xml/single_website_preferences.xml b/chrome/android/java/res/xml/single_website_preferences.xml index 2aa5566321d65..d14950f7525ce 100644 --- a/chrome/android/java/res/xml/single_website_preferences.xml +++ b/chrome/android/java/res/xml/single_website_preferences.xml @@ -49,6 +49,8 @@ android:key="push_notifications_list" /> + diff --git a/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java index 5cc1c7543b0c8..2a776e958b71f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java @@ -691,6 +691,8 @@ public void onClick(View view) { public void run() { Bundle fragmentArguments = SingleWebsitePreferences.createFragmentArgsForSite(mFullUrl); + fragmentArguments.putParcelable(SingleWebsitePreferences.EXTRA_WEB_CONTENTS, + mWebContents); Intent preferencesIntent = PreferencesLauncher.createIntentForSettingsPage( mContext, SingleWebsitePreferences.class.getName()); preferencesIntent.putExtra( diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java index 38b0e9cb7d24f..affb9519f3b6e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java @@ -138,6 +138,12 @@ private static Map getResourceInfo() { new ResourceItem(R.drawable.permission_protected_media, org.chromium.chrome.R.string.protected_content, 0, ContentSetting.ASK, ContentSetting.BLOCK, 0, 0)); + localMap.put(ContentSettingsType.CONTENT_SETTINGS_TYPE_KEYGEN, + new ResourceItem(R.drawable.permission_keygen, + R.string.keygen_permission_title, + R.string.keygen_permission_title, ContentSetting.ALLOW, + ContentSetting.BLOCK, + 0, R.string.website_settings_category_blocked_recommended)); sResourceInfo = localMap; } return sResourceInfo; diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/KeygenInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/KeygenInfo.java new file mode 100644 index 0000000000000..e7068cd4854af --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/KeygenInfo.java @@ -0,0 +1,25 @@ +// Copyright 2015 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. + +package org.chromium.chrome.browser.preferences.website; + +/** + * Keygen information for a given origin. + */ +public class KeygenInfo extends PermissionInfo { + public KeygenInfo(String origin, String embedder, boolean isIncognito) { + super(origin, embedder, isIncognito); + } + + protected int getNativePreferenceValue(String origin, String embedder, boolean isIncognito) { + return WebsitePreferenceBridge.nativeGetKeygenSettingForOrigin( + origin, embedder, isIncognito); + } + + protected void setNativePreferenceValue( + String origin, String embedder, ContentSetting value, boolean isIncognito) { + WebsitePreferenceBridge.nativeSetKeygenSettingForOrigin( + origin, embedder, value.toInt(), isIncognito); + } +} diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java index 67cf2e5fa6dc5..090828545c934 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java @@ -28,6 +28,7 @@ import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; import org.chromium.chrome.browser.search_engines.TemplateUrlService; import org.chromium.chrome.browser.util.UrlUtilities; +import org.chromium.content_public.browser.WebContents; import java.net.URI; import java.util.ArrayList; @@ -52,6 +53,8 @@ public class SingleWebsitePreferences extends PreferenceFragment public static final String EXTRA_ORIGIN = "org.chromium.chrome.preferences.origin"; public static final String EXTRA_LOCATION = "org.chromium.chrome.preferences.location"; + public static final String EXTRA_WEB_CONTENTS = "org.chromium.chrome.preferences.web_contents"; + // Preference keys, see single_website_preferences.xml // Headings: public static final String PREF_SITE_TITLE = "site_title"; @@ -70,6 +73,7 @@ public class SingleWebsitePreferences extends PreferenceFragment public static final String PREF_COOKIES_PERMISSION = "cookies_permission_list"; public static final String PREF_FULLSCREEN_PERMISSION = "fullscreen_permission_list"; public static final String PREF_JAVASCRIPT_PERMISSION = "javascript_permission_list"; + public static final String PREF_KEYGEN_PERMISSION = "keygen_permission_list"; public static final String PREF_LOCATION_ACCESS = "location_access_list"; public static final String PREF_MIC_CAPTURE_PERMISSION = "microphone_permission_list"; public static final String PREF_MIDI_SYSEX_PERMISSION = "midi_sysex_permission_list"; @@ -86,6 +90,7 @@ public class SingleWebsitePreferences extends PreferenceFragment PREF_COOKIES_PERMISSION, PREF_FULLSCREEN_PERMISSION, PREF_JAVASCRIPT_PERMISSION, + PREF_KEYGEN_PERMISSION, PREF_LOCATION_ACCESS, PREF_MIC_CAPTURE_PERMISSION, PREF_MIDI_SYSEX_PERMISSION, @@ -102,6 +107,12 @@ public class SingleWebsitePreferences extends PreferenceFragment private class SingleWebsitePermissionsPopulator implements WebsitePermissionsFetcher.WebsitePermissionsCallback { + private final WebContents mWebContents; + + public SingleWebsitePermissionsPopulator(WebContents webContents) { + mWebContents = webContents; + } + @Override public void onWebsitePermissionsAvailable( Map> sitesByOrigin, Map> sitesByHost) { @@ -115,6 +126,14 @@ public void onWebsitePermissionsAvailable( allSites.addAll(sitesByHost.values()); // TODO(mvanouwerkerk): Avoid modifying the outer class from this inner class. mSite = mergePermissionInfoForTopLevelOrigin(mSiteAddress, allSites); + + // Display Keygen Content Setting if Keygen is blocked. + if (mSite.getKeygenInfo() == null && mWebContents != null + && WebsitePreferenceBridge.getKeygenBlocked(mWebContents)) { + String origin = mSiteAddress.getOrigin(); + mSite.setKeygenInfo(new KeygenInfo(origin, origin, false)); + } + displaySitePermissions(); } } @@ -145,14 +164,17 @@ public void onActivityCreated(Bundle savedInstanceState) { Object extraSite = getArguments().getSerializable(EXTRA_SITE); Object extraOrigin = getArguments().getSerializable(EXTRA_ORIGIN); + getArguments().setClassLoader(WebContents.class.getClassLoader()); + Object webContents = getArguments().get(EXTRA_WEB_CONTENTS); if (extraSite != null && extraOrigin == null) { mSite = (Website) extraSite; displaySitePermissions(); } else if (extraOrigin != null && extraSite == null) { mSiteAddress = WebsiteAddress.create((String) extraOrigin); - WebsitePermissionsFetcher fetcher = - new WebsitePermissionsFetcher(new SingleWebsitePermissionsPopulator()); + WebsitePermissionsFetcher fetcher; + fetcher = new WebsitePermissionsFetcher( + new SingleWebsitePermissionsPopulator((WebContents) webContents)); fetcher.fetchAllPreferences(); } else { assert false : "Exactly one of EXTRA_SITE or EXTRA_SITE_ADDRESS must be provided."; @@ -193,6 +215,10 @@ && permissionInfoIsForTopLevelOrigin(other.getFullscreenInfo(), origin)) { && permissionInfoIsForTopLevelOrigin(other.getGeolocationInfo(), origin)) { merged.setGeolocationInfo(other.getGeolocationInfo()); } + if (merged.getKeygenInfo() == null && other.getKeygenInfo() != null + && permissionInfoIsForTopLevelOrigin(other.getKeygenInfo(), origin)) { + merged.setKeygenInfo(other.getKeygenInfo()); + } if (merged.getMidiInfo() == null && other.getMidiInfo() != null && permissionInfoIsForTopLevelOrigin(other.getMidiInfo(), origin)) { merged.setMidiInfo(other.getMidiInfo()); @@ -284,6 +310,8 @@ private void displaySitePermissions() { setUpListPreference(preference, mSite.getFullscreenPermission()); } else if (PREF_JAVASCRIPT_PERMISSION.equals(preference.getKey())) { setUpListPreference(preference, mSite.getJavaScriptPermission()); + } else if (PREF_KEYGEN_PERMISSION.equals(preference.getKey())) { + setUpListPreference(preference, mSite.getKeygenPermission()); } else if (PREF_LOCATION_ACCESS.equals(preference.getKey())) { setUpLocationPreference(preference); } else if (PREF_MIC_CAPTURE_PERMISSION.equals(preference.getKey())) { @@ -508,6 +536,8 @@ private int getContentSettingsTypeFromPreferenceKey(String preferenceKey) { return ContentSettingsType.CONTENT_SETTINGS_TYPE_FULLSCREEN; case PREF_JAVASCRIPT_PERMISSION: return ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT; + case PREF_KEYGEN_PERMISSION: + return ContentSettingsType.CONTENT_SETTINGS_TYPE_KEYGEN; case PREF_LOCATION_ACCESS: return ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION; case PREF_MIC_CAPTURE_PERMISSION: @@ -566,6 +596,8 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { mSite.setFullscreenPermission(permission); } else if (PREF_JAVASCRIPT_PERMISSION.equals(preference.getKey())) { mSite.setJavaScriptPermission(permission); + } else if (PREF_KEYGEN_PERMISSION.equals(preference.getKey())) { + mSite.setKeygenPermission(permission); } else if (PREF_LOCATION_ACCESS.equals(preference.getKey())) { mSite.setGeolocationPermission(permission); } else if (PREF_MIC_CAPTURE_PERMISSION.equals(preference.getKey())) { @@ -618,6 +650,7 @@ private void resetSite() { mSite.setFullscreenPermission(ContentSetting.DEFAULT); mSite.setGeolocationPermission(ContentSetting.DEFAULT); mSite.setJavaScriptPermission(ContentSetting.DEFAULT); + mSite.setKeygenPermission(ContentSetting.DEFAULT); mSite.setMicrophonePermission(ContentSetting.DEFAULT); mSite.setMidiPermission(ContentSetting.DEFAULT); mSite.setPopupPermission(ContentSetting.DEFAULT); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java index 1d8bfaade49bc..acf76fa368b00 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java @@ -29,6 +29,7 @@ public class Website implements Serializable { private CameraInfo mCameraInfo; private CookieInfo mCookieInfo; private GeolocationInfo mGeolocationInfo; + private KeygenInfo mKeygenInfo; private MicrophoneInfo mMicrophoneInfo; private MidiInfo mMidiInfo; private ContentSettingException mJavaScriptException; @@ -132,6 +133,37 @@ public void setGeolocationPermission(ContentSetting value) { } } + /** + * Sets the KeygenInfo object for this Website. + */ + public void setKeygenInfo(KeygenInfo info) { + mKeygenInfo = info; + WebsiteAddress embedder = WebsiteAddress.create(info.getEmbedder()); + if (embedder != null) { + mSummary = embedder.getTitle(); + } + } + + public KeygenInfo getKeygenInfo() { + return mKeygenInfo; + } + + /** + * Returns what permission governs keygen access. + */ + public ContentSetting getKeygenPermission() { + return mKeygenInfo != null ? mKeygenInfo.getContentSetting() : null; + } + + /** + * Configure keygen access setting for this site. + */ + public void setKeygenPermission(ContentSetting value) { + if (mKeygenInfo != null) { + mKeygenInfo.setContentSetting(value); + } + } + /** * Sets the MidiInfo object for this Website. */ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java index a0f3d7c65296a..1d48e706e83b4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java @@ -63,6 +63,8 @@ public void fetchAllPreferences() { queue.add(new CookieInfoFetcher()); // Fullscreen are stored per-origin. queue.add(new FullscreenInfoFetcher()); + // Keygen permissions are per-origin. + queue.add(new KeygenInfoFetcher()); // Local storage info is per-origin. queue.add(new LocalStorageInfoFetcher()); // Website storage is per-host. @@ -248,6 +250,17 @@ public void run() { } } + private class KeygenInfoFetcher extends Task { + @Override + public void run() { + for (KeygenInfo info : WebsitePreferenceBridge.getKeygenInfo()) { + WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); + if (address == null) continue; + createSiteByOriginAndHost(address).setKeygenInfo(info); + } + } + } + private class CookieInfoFetcher extends Task { @Override public void run() { diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java index f4ebb54545bd6..cf3dc7b7eddae 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java @@ -6,6 +6,7 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.content_public.browser.WebContents; import java.util.ArrayList; import java.util.HashMap; @@ -41,6 +42,30 @@ public interface StorageInfoClearedCallback { public void onStorageInfoCleared(); } + /** + * @return the list of all origins that have keygen permissions in non-incognito mode. + */ + @SuppressWarnings("unchecked") + public static List getKeygenInfo() { + ArrayList list = new ArrayList(); + nativeGetKeygenOrigins(list); + return list; + } + + @CalledByNative + private static void insertKeygenInfoIntoList( + ArrayList list, String origin, String embedder) { + list.add(new KeygenInfo(origin, embedder, false)); + } + + /** + * @return whether we've blocked key generation in the current tab. + */ + @SuppressWarnings("unchecked") + public static boolean getKeygenBlocked(WebContents webContents) { + return nativeGetKeygenBlocked(webContents); + } + /** * @return the list of all origins that have geolocation permissions in non-incognito mode. */ @@ -247,6 +272,12 @@ static native int nativeGetGeolocationSettingForOrigin( String origin, String embedder, boolean isIncognito); public static native void nativeSetGeolocationSettingForOrigin( String origin, String embedder, int value, boolean isIncognito); + private static native void nativeGetKeygenOrigins(Object list); + static native int nativeGetKeygenSettingForOrigin( + String origin, String embedder, boolean isIncognito); + static native void nativeSetKeygenSettingForOrigin( + String origin, String embedder, int value, boolean isIncognito); + private static native boolean nativeGetKeygenBlocked(Object webContents); private static native void nativeGetMidiOrigins(Object list); static native int nativeGetMidiSettingForOrigin( String origin, String embedder, boolean isIncognito); diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java index 047dd81fa5db9..82d93e6522fe0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java @@ -16,6 +16,7 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.infobar.InfoBarContainer; import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference; +import org.chromium.chrome.browser.preferences.ChromeBaseListPreference; import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; import org.chromium.chrome.browser.preferences.LocationSettings; import org.chromium.chrome.browser.preferences.PrefServiceBridge; @@ -123,6 +124,15 @@ private Preferences startSiteSettingsCategory(String category) { return (Preferences) getInstrumentation().startActivitySync(intent); } + private Preferences startSingleWebsitePreferences(Website site) { + Bundle fragmentArgs = new Bundle(); + fragmentArgs.putSerializable(SingleWebsitePreferences.EXTRA_SITE, site); + Intent intent = PreferencesLauncher.createIntentForSettingsPage( + getInstrumentation().getTargetContext(), SingleWebsitePreferences.class.getName()); + intent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs); + return (Preferences) getInstrumentation().startActivitySync(intent); + } + private void setCookiesEnabled(final Preferences preferenceActivity, final boolean enabled) { ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override @@ -254,6 +264,27 @@ public void run() { preferenceActivity.finish(); } + private void setEnableKeygen(final String origin, final boolean enabled) { + Website website = new Website(WebsiteAddress.create(origin)); + website.setKeygenInfo(new KeygenInfo(origin, origin, false)); + final Preferences preferenceActivity = startSingleWebsitePreferences(website); + + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + SingleWebsitePreferences websitePreferences = + (SingleWebsitePreferences) preferenceActivity.getFragmentForTest(); + ChromeBaseListPreference keygen = + (ChromeBaseListPreference) websitePreferences.findPreference( + SingleWebsitePreferences.PREF_KEYGEN_PERMISSION); + websitePreferences.onPreferenceChange(keygen, enabled + ? ContentSetting.ALLOW.toString() + : ContentSetting.BLOCK.toString()); + } + }); + preferenceActivity.finish(); + } + /** * Tests that disabling cookies turns off the third-party cookie toggle. * @throws Exception @@ -350,6 +381,46 @@ public void testPopupsNotBlocked() throws Exception { assertEquals(2, getTabCount()); } + /** + * Sets Allow Keygen Enabled to be false and make sure it is set correctly. + * @throws Exception + */ + @SmallTest + @Feature({"Preferences"}) + public void testKeygenBlocked() throws Exception { + final String origin = "http://example.com/"; + setEnableKeygen(origin, false); + + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + Website site = new Website(WebsiteAddress.create(origin)); + site.setKeygenInfo(new KeygenInfo(origin, origin, false)); + assertEquals(site.getKeygenPermission(), ContentSetting.BLOCK); + } + }); + } + + /** + * Sets Allow Keygen Enabled to be true and make sure it is set correctly. + * @throws Exception + */ + @SmallTest + @Feature({"Preferences"}) + public void testKeygenNotBlocked() throws Exception { + final String origin = "http://example.com/"; + setEnableKeygen(origin, true); + + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + Website site = new Website(WebsiteAddress.create(origin)); + site.setKeygenInfo(new KeygenInfo(origin, origin, false)); + assertEquals(site.getKeygenPermission(), ContentSetting.ALLOW); + } + }); + } + /** * Sets Allow Camera Enabled to be false and make sure it is set correctly. * @throws Exception diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc index fecc1583bf232..4e785c7b61f9f 100644 --- a/chrome/browser/android/preferences/website_preference_bridge.cc +++ b/chrome/browser/android/preferences/website_preference_bridge.cc @@ -17,6 +17,7 @@ #include "chrome/browser/browsing_data/local_data_container.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/content_settings/web_site_settings_uma_util.h" #include "chrome/browser/notifications/desktop_notification_profile_util.h" #include "chrome/browser/profiles/profile.h" @@ -205,6 +206,44 @@ static void SetGeolocationSettingForOrigin( (ContentSetting) value, is_incognito); } +static void GetKeygenOrigins(JNIEnv* env, + const JavaParamRef& clazz, + const JavaParamRef& list) { + GetOrigins(env, CONTENT_SETTINGS_TYPE_KEYGEN, + &Java_WebsitePreferenceBridge_insertKeygenInfoIntoList, list, + false); +} + +static jint GetKeygenSettingForOrigin(JNIEnv* env, + const JavaParamRef& clazz, + const JavaParamRef& origin, + const JavaParamRef& embedder, + jboolean is_incognito) { + return GetSettingForOrigin(env, CONTENT_SETTINGS_TYPE_KEYGEN, origin, + embedder, is_incognito); +} + +static void SetKeygenSettingForOrigin(JNIEnv* env, + const JavaParamRef& clazz, + const JavaParamRef& origin, + const JavaParamRef& embedder, + jint value, + jboolean is_incognito) { + GURL embedder_url(ConvertJavaStringToUTF8(env, embedder)); + SetSettingForOrigin(env, CONTENT_SETTINGS_TYPE_KEYGEN, origin, + ContentSettingsPattern::FromURLNoWildcard(embedder_url), + (ContentSetting) value, is_incognito); +} + +static jboolean GetKeygenBlocked(JNIEnv* env, + const JavaParamRef& clazz, + const JavaParamRef& java_web_contents) { + content::WebContents* web_contents = + content::WebContents::FromJavaWebContents(java_web_contents); + return TabSpecificContentSettings::FromWebContents( + web_contents)->IsContentBlocked(CONTENT_SETTINGS_TYPE_KEYGEN); +} + static void GetMidiOrigins(JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& list) { diff --git a/chrome/browser/ui/android/website_settings_popup_android.cc b/chrome/browser/ui/android/website_settings_popup_android.cc index f91629155a792..4ca14049bf7df 100644 --- a/chrome/browser/ui/android/website_settings_popup_android.cc +++ b/chrome/browser/ui/android/website_settings_popup_android.cc @@ -109,6 +109,7 @@ void WebsiteSettingsPopupAndroid::SetPermissionInfo( permissions_to_display.push_back(CONTENT_SETTINGS_TYPE_IMAGES); permissions_to_display.push_back(CONTENT_SETTINGS_TYPE_JAVASCRIPT); permissions_to_display.push_back(CONTENT_SETTINGS_TYPE_POPUPS); + permissions_to_display.push_back(CONTENT_SETTINGS_TYPE_KEYGEN); std::map user_specified_settings_to_display;