Index: chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java |
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 d337bfa5cfa160abd38b16dbc86a7d8ce70e987a..0361e8e01bcb905d36edd7d92f303dce35334e11 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 |
@@ -19,6 +19,13 @@ |
import org.chromium.chrome.R; |
import org.chromium.chrome.browser.ContentSettingsType; |
+import org.chromium.chrome.browser.UrlUtilities; |
+ |
+import java.net.URI; |
+import java.util.ArrayList; |
+import java.util.List; |
+import java.util.Map; |
+import java.util.Set; |
/** |
* Shows a list of HTML5 settings for a single website. |
@@ -26,7 +33,13 @@ |
public class SingleWebsitePreferences extends PreferenceFragment |
implements DialogInterface.OnClickListener, OnPreferenceChangeListener, |
OnPreferenceClickListener { |
+ // SingleWebsitePreferences expects either EXTRA_SITE (a Website) or |
+ // EXTRA_ADDRESS (a WebsiteAddress) to be present (but not both). If |
+ // EXTRA_SITE is present, the fragment will display the permissions in that |
+ // Website object. If EXTRA_ADDRESS is present, the fragment will find all |
+ // permissions for that website address and display those. |
public static final String EXTRA_SITE = "org.chromium.chrome.preferences.site"; |
+ public static final String EXTRA_ADDRESS = "org.chromium.chrome.preferences.address"; |
// Preference keys, see single_website_preferences.xml |
// Headings: |
@@ -51,13 +64,145 @@ |
// The website this page is displaying details about. |
private Website mSite; |
+ |
+ // The address of the site we want to display. Used only if EXTRA_ADDRESS is provided. |
+ private WebsiteAddress mSiteAddress; |
+ |
// A list of possible options for each list preference summary. |
private String[] mListPreferenceSummaries; |
+ private class SingleWebsitePermissionsPopulator |
+ implements WebsitePermissionsFetcher.WebsitePermissionsCallback { |
+ @Override |
+ public void onWebsitePermissionsAvailable( |
+ Map<String, Set<Website>> sitesByOrigin, Map<String, Set<Website>> sitesByHost) { |
+ // This method may be called after the activity has been destroyed. |
+ // In that case, bail out. |
+ if (getActivity() == null) return; |
+ |
+ // TODO(mvanouwerkerk): Do this merge at data retrieval time in C++, instead of now. |
+ List<Set<Website>> allSites = new ArrayList<>(); |
+ allSites.addAll(sitesByOrigin.values()); |
+ allSites.addAll(sitesByHost.values()); |
+ // TODO(mvanouwerkerk): Avoid modifying the outer class from this inner class. |
+ mSite = mergePermissionInfoForTopLevelOrigin(mSiteAddress, allSites); |
+ displaySitePermissions(); |
+ } |
+ } |
+ |
+ /** |
+ * Creates a Bundle with the correct arguments for opening this fragment for |
+ * the website with the given url. |
+ * |
+ * @param url The URL to open the fragment with. This is a complete url including scheme, |
+ * domain, port, path, etc. |
+ * @return The bundle to attach to the preferences intent. |
+ */ |
+ public static Bundle createFragmentArgsForSite(String url) { |
+ Bundle fragmentArgs = new Bundle(); |
+ // TODO(mvanouwerkerk): Define a pure getOrigin method in UrlUtilities that is the |
+ // equivalent of the call below, because this is perfectly fine for non-display purposes. |
+ String origin = UrlUtilities.getOriginForDisplay(URI.create(url), true /* schowScheme */); |
+ fragmentArgs.putSerializable( |
+ SingleWebsitePreferences.EXTRA_ADDRESS, WebsiteAddress.create(origin)); |
+ return fragmentArgs; |
+ } |
+ |
@Override |
public void onActivityCreated(Bundle savedInstanceState) { |
getActivity().setTitle(R.string.prefs_content_settings); |
- mSite = (Website) getArguments().getSerializable(EXTRA_SITE); |
+ Object extraSite = getArguments().getSerializable(EXTRA_SITE); |
+ Object extraAddress = getArguments().getSerializable(EXTRA_ADDRESS); |
+ |
+ if (extraSite != null && extraAddress == null) { |
+ mSite = (Website) extraSite; |
+ displaySitePermissions(); |
+ } else if (extraAddress != null && extraSite == null) { |
+ mSiteAddress = (WebsiteAddress) extraAddress; |
+ WebsitePermissionsFetcher fetcher = |
+ new WebsitePermissionsFetcher(new SingleWebsitePermissionsPopulator()); |
+ fetcher.fetchAllPreferences(); |
+ } else { |
+ assert false : "Exactly one of EXTRA_SITE or EXTRA_SITE_ADDRESS must be provided."; |
+ } |
+ |
+ super.onActivityCreated(savedInstanceState); |
+ } |
+ |
+ /** |
+ * Given an address and a list of sets of websites, returns a new site with the same origin |
+ * as |address| which has merged into it the permissions of the matching input sites. If a |
+ * permission is found more than once, the one found first is used and the latter are ignored. |
+ * This should not drop any relevant data as this method already aggressively filters on an |
+ * exact match of origin and embedder. |
+ * |
+ * @param address The address to search for. |
+ * @param websiteSets The websites to search in. |
+ * @return The merged website. |
+ */ |
+ private static Website mergePermissionInfoForTopLevelOrigin( |
+ WebsiteAddress address, List<Set<Website>> websiteSets) { |
+ String origin = address.getOrigin(); |
+ Website merged = new Website(address); |
+ // This nested loop looks expensive, but the amount of data is likely to be relatively |
+ // small because most sites have very few permissions. |
+ for (Set<Website> websiteSet : websiteSets) { |
+ for (Website other : websiteSet) { |
+ if (merged.getCookieInfo() == null && other.getCookieInfo() != null |
+ && permissionInfoIsForTopLevelOrigin(other.getCookieInfo(), origin)) { |
+ merged.setCookieInfo(other.getCookieInfo()); |
+ } |
+ if (merged.getGeolocationInfo() == null && other.getGeolocationInfo() != null |
+ && permissionInfoIsForTopLevelOrigin(other.getGeolocationInfo(), origin)) { |
+ merged.setGeolocationInfo(other.getGeolocationInfo()); |
+ } |
+ if (merged.getMidiInfo() == null && other.getMidiInfo() != null |
+ && permissionInfoIsForTopLevelOrigin(other.getMidiInfo(), origin)) { |
+ merged.setMidiInfo(other.getMidiInfo()); |
+ } |
+ if (merged.getProtectedMediaIdentifierInfo() == null |
+ && other.getProtectedMediaIdentifierInfo() != null |
+ && permissionInfoIsForTopLevelOrigin( |
+ other.getProtectedMediaIdentifierInfo(), origin)) { |
+ merged.setProtectedMediaIdentifierInfo(other.getProtectedMediaIdentifierInfo()); |
+ } |
+ if (merged.getPushNotificationInfo() == null |
+ && other.getPushNotificationInfo() != null |
+ && permissionInfoIsForTopLevelOrigin( |
+ other.getPushNotificationInfo(), origin)) { |
+ merged.setPushNotificationInfo(other.getPushNotificationInfo()); |
+ } |
+ if (merged.getVoiceAndVideoCaptureInfo() == null |
+ && other.getVoiceAndVideoCaptureInfo() != null) { |
+ if (origin.equals(other.getVoiceAndVideoCaptureInfo().getOrigin()) |
+ && (origin.equals(other.getVoiceAndVideoCaptureInfo().getEmbedderSafe()) |
+ || "*".equals(other.getVoiceAndVideoCaptureInfo() |
+ .getEmbedderSafe()))) { |
+ merged.setVoiceAndVideoCaptureInfo(other.getVoiceAndVideoCaptureInfo()); |
+ } |
+ } |
+ // TODO(mvanouwerkerk): Can VoiceAndVideoCaptureInfo share a base type with the |
+ // others? |
+ // TODO(mvanouwerkerk): Merge in PopupExceptionInfo? It is not a PermissionInfo. |
+ // TODO(mvanouwerkerk): Merge in LocalStorageInfo? It is not a PermissionInfo. |
+ // TODO(mvanouwerkerk): Merge in all instances of StorageInfo? It is not a |
+ // PermissionInfo. |
+ } |
+ } |
+ return merged; |
+ } |
+ |
+ private static boolean permissionInfoIsForTopLevelOrigin(PermissionInfo info, String origin) { |
+ // TODO(mvanouwerkerk): Find a more generic place for this method. |
+ return origin.equals(info.getOrigin()) |
+ && (origin.equals(info.getEmbedderSafe()) || "*".equals(info.getEmbedderSafe())); |
+ } |
+ |
+ /** |
+ * Updates the permissions displayed in the UI by fetching them from mSite. |
+ * Must only be called once mSite is set. |
+ */ |
+ private void displaySitePermissions() { |
addPreferencesFromResource(R.xml.single_website_preferences); |
mListPreferenceSummaries = getActivity().getResources().getStringArray( |
R.array.website_settings_permission_options); |
@@ -91,11 +236,7 @@ public void onActivityCreated(Bundle savedInstanceState) { |
} else if (PREF_PROTECTED_MEDIA_IDENTIFIER_PERMISSION.equals(preference.getKey())) { |
setUpListPreference(preference, mSite.getProtectedMediaIdentifierPermission()); |
} else if (PREF_PUSH_NOTIFICATIONS_PERMISSION.equals(preference.getKey())) { |
- if (ContentPreferences.pushNotificationsSupported()) { |
- setUpListPreference(preference, mSite.getPushNotificationPermission()); |
- } else { |
- getPreferenceScreen().removePreference(preference); |
- } |
+ setUpListPreference(preference, mSite.getPushNotificationPermission()); |
} else if (PREF_VOICE_AND_VIDEO_CAPTURE_PERMISSION.equals(preference.getKey())) { |
configureVoiceAndVideoPreference(preference); |
} |
@@ -111,8 +252,6 @@ public void onActivityCreated(Bundle savedInstanceState) { |
Preference heading = preferenceScreen.findPreference(PREF_PERMISSIONS); |
preferenceScreen.removePreference(heading); |
} |
- |
- super.onActivityCreated(savedInstanceState); |
} |
private boolean hasUsagePreferences() { |