From b2bb2aa95a4da140ad637b3bf1f258bde1c0579e Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 1 Nov 2022 16:49:12 +0000 Subject: [PATCH] Bug 1788720 - Expose private browsing flag in StorageController.getPermissions r=geckoview-reviewers,jonalmeida Differential Revision: https://phabricator.services.mozilla.com/D160424 --- mobile/android/geckoview/api.txt | 4 +- .../geckoview/test/PermissionDelegateTest.kt | 14 ++--- .../geckoview/test/WebNotificationTest.kt | 63 +++++++++++++++++++ .../mozilla/geckoview/StorageController.java | 27 +++++++- .../mozilla/geckoview/WebNotification.java | 7 +++ .../mozilla/geckoview/doc-files/CHANGELOG.md | 2 +- .../geckoview/GeckoViewStorageController.jsm | 17 ++++- widget/android/AndroidAlerts.cpp | 6 +- 8 files changed, 122 insertions(+), 18 deletions(-) diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index f0327bffd799..3469b4fa0a23 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -1904,7 +1904,8 @@ package org.mozilla.geckoview { method @AnyThread @NonNull public GeckoResult clearDataFromHost(@NonNull String, long); method @AnyThread @NonNull public GeckoResult> getAllPermissions(); method @AnyThread @NonNull public GeckoResult> getPermissions(@NonNull String); - method @AnyThread @NonNull public GeckoResult> getPermissions(@NonNull String, @Nullable String); + method @AnyThread @NonNull public GeckoResult> getPermissions(@NonNull String, boolean); + method @AnyThread @NonNull public GeckoResult> getPermissions(@NonNull String, @Nullable String, boolean); method @AnyThread public void setPermission(@NonNull GeckoSession.PermissionDelegate.ContentPermission, int); method @AnyThread public void setPrivateBrowsingPermanentPermission(@NonNull GeckoSession.PermissionDelegate.ContentPermission, int); } @@ -2297,6 +2298,7 @@ package org.mozilla.geckoview { field public static final Parcelable.Creator CREATOR; field @Nullable public final String imageUrl; field @Nullable public final String lang; + field public final boolean privateBrowsing; field @NonNull public final boolean requireInteraction; field public final boolean silent; field @Nullable public final String source; diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PermissionDelegateTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PermissionDelegateTest.kt index e561ff749b11..6d392e697a83 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PermissionDelegateTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PermissionDelegateTest.kt @@ -20,22 +20,18 @@ import org.mozilla.geckoview.test.TrackingPermissionService.TrackingPermissionIn import android.Manifest import android.content.Context import android.content.pm.PackageManager -import android.location.Criteria -import android.location.Location import android.location.LocationManager import android.os.Build -import android.os.SystemClock -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.filters.MediumTest import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.filters.MediumTest +import androidx.test.platform.app.InstrumentationRegistry import org.hamcrest.Matchers.* import org.json.JSONArray import org.junit.Assert.fail import org.junit.Assume.assumeThat +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith -import org.junit.Ignore import org.mozilla.geckoview.GeckoSessionSettings @RunWith(AndroidJUnit4::class) @@ -579,7 +575,7 @@ class PermissionDelegateTest : BaseSessionTest() { assertThat("Permission should be granted", result as String, equalTo("granted")) - val perms = sessionRule.waitForResult(storageController.getPermissions(url)) + val perms = sessionRule.waitForResult(storageController.getPermissions(url, false)) assertThat("Permissions should not be null", perms, notNullValue()) var permFound = false @@ -635,7 +631,7 @@ class PermissionDelegateTest : BaseSessionTest() { assertThat("Permission should be granted", result2 as String, equalTo("granted")) - val perms2 = sessionRule.waitForResult(storageController.getPermissions(url)) + val perms2 = sessionRule.waitForResult(storageController.getPermissions(url, false)) assertThat("Permissions should not be null", perms, notNullValue()) permFound = false diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebNotificationTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebNotificationTest.kt index c36cecedda78..b754453cb5b1 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebNotificationTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebNotificationTest.kt @@ -78,6 +78,12 @@ class WebNotificationTest : BaseSessionTest() { assertThat("Source should match", notification.source, equalTo(createTestUrl(HELLO_HTML_PATH))) } + @GeckoSessionTestRule.Setting.List( + GeckoSessionTestRule.Setting( + key = GeckoSessionTestRule.Setting.Key.USE_PRIVATE_MODE, + value = "true" + ) + ) @Test fun onShowNotification() { sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.vibrate.enabled" to true)) val notificationResult = GeckoResult() @@ -88,6 +94,7 @@ class WebNotificationTest : BaseSessionTest() { @GeckoSessionTestRule.AssertCalled override fun onShowNotification(notification: WebNotification) { assertNotificationData(notification, requireInteraction) + assertThat("privateBrowsing should match", notification.privateBrowsing, equalTo(true)) notificationResult.complete(null) } }) @@ -152,6 +159,7 @@ class WebNotificationTest : BaseSessionTest() { val notification = sessionRule.waitForResult(notificationResult) assertNotificationData(notification, requireInteraction) + assertThat("privateBrowsing should match", notification.privateBrowsing, equalTo(true)) // Test that we can click from a deserialized notification val parcel = Parcel.obtain() @@ -160,6 +168,61 @@ class WebNotificationTest : BaseSessionTest() { val deserialized = WebNotification.CREATOR.createFromParcel(parcel) assertNotificationData(deserialized, requireInteraction) + assertThat("privateBrowsing should match", deserialized.privateBrowsing, equalTo(false)) + + deserialized!!.click() + assertThat("Promise should have been resolved.", promiseResult.value as Double, equalTo(1.0)) + } + + @GeckoSessionTestRule.Setting.List( + GeckoSessionTestRule.Setting( + key = GeckoSessionTestRule.Setting.Key.USE_PRIVATE_MODE, + value = "true" + ) + ) + @Test fun clickPrivateNotificationParceled() { + sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.vibrate.enabled" to true)) + val notificationResult = GeckoResult() + val requireInteraction = + sessionRule.getPrefs("dom.webnotifications.requireinteraction.enabled")[0] as Boolean + + sessionRule.delegateDuringNextWait(object : WebNotificationDelegate { + @GeckoSessionTestRule.AssertCalled + override fun onShowNotification(notification: WebNotification) { + notificationResult.complete(notification) + } + }) + + val promiseResult = mainSession.evaluatePromiseJS(""" + new Promise(resolve => { + const notification = new Notification('The Title', { + body: 'The Text', + cookie: 'Cookie', + icon: 'icon.png', + tag: 'Tag', + dir: 'ltr', + lang: 'en-US', + requireInteraction: true, + vibrate: [1,2,3,4] + }); + notification.onclick = function() { + resolve(1); + } + }); + """.trimIndent()) + + val notification = sessionRule.waitForResult(notificationResult) + assertNotificationData(notification, requireInteraction) + assertThat("privateBrowsing should match", notification.privateBrowsing, equalTo(true)) + + // Test that we can click from a deserialized notification + val parcel = Parcel.obtain() + notification.writeToParcel(parcel, 0) + parcel.setDataPosition(0); + + val deserialized = WebNotification.CREATOR.createFromParcel(parcel) + assertNotificationData(deserialized, requireInteraction) + assertThat("privateBrowsing should match", deserialized.privateBrowsing, equalTo(true)) deserialized!!.click() assertThat("Promise should have been resolved.", promiseResult.value as Double, equalTo(1.0)) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java index fa471665c11c..34d6d08bd909 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java @@ -204,7 +204,9 @@ public final class StorageController { } /** - * Get all currently stored permissions for a given URI and default (unset) context ID. + * Get all currently stored permissions for a given URI and default (unset) context ID, in normal + * mode This API will be deprecated in the future + * https://bugzilla.mozilla.org/show_bug.cgi?id=1797379 * * @param uri A String representing the URI to get permissions for. * @return A {@link GeckoResult} that will complete with a list of all currently stored {@link @@ -212,7 +214,22 @@ public final class StorageController { */ @AnyThread public @NonNull GeckoResult> getPermissions(final @NonNull String uri) { - return getPermissions(uri, null); + return getPermissions(uri, null, false); + } + + /** + * Get all currently stored permissions for a given URI and default (unset) context ID. + * + * @param uri A String representing the URI to get permissions for. + * @param privateMode indicate where the {@link ContentPermission}s should be in private or normal + * mode. + * @return A {@link GeckoResult} that will complete with a list of all currently stored {@link + * ContentPermission}s for the URI. + */ + @AnyThread + public @NonNull GeckoResult> getPermissions( + final @NonNull String uri, final boolean privateMode) { + return getPermissions(uri, null, privateMode); } /** @@ -220,15 +237,19 @@ public final class StorageController { * * @param uri A String representing the URI to get permissions for. * @param contextId A String specifying the context ID. + * @param privateMode indicate where the {@link ContentPermission}s should be in private or normal + * mode * @return A {@link GeckoResult} that will complete with a list of all currently stored {@link * ContentPermission}s for the URI. */ @AnyThread public @NonNull GeckoResult> getPermissions( - final @NonNull String uri, final @Nullable String contextId) { + final @NonNull String uri, final @Nullable String contextId, final boolean privateMode) { final GeckoBundle msg = new GeckoBundle(2); + final int privateBrowsingId = (privateMode) ? 1 : 0; msg.putString("uri", uri); msg.putString("contextId", createSafeSessionContextId(contextId)); + msg.putInt("privateBrowsingId", privateBrowsingId); return EventDispatcher.getInstance() .queryBundle("GeckoView:GetPermissionsByURI", msg) .map( diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebNotification.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebNotification.java index ab1928f6b5e6..c2de231f8042 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebNotification.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebNotification.java @@ -103,6 +103,9 @@ public class WebNotification implements Parcelable { */ public final boolean silent; + /** indicates whether the notification came from private browsing mode or not. */ + public final boolean privateBrowsing; + /** * A vibration pattern to run with the display of the notification. A vibration pattern can be an * array with as few as one member. The values are times in milliseconds where the even indices @@ -126,6 +129,7 @@ public class WebNotification implements Parcelable { @NonNull final boolean requireInteraction, @NonNull final String source, final boolean silent, + final boolean privateBrowsing, @NonNull final int[] vibrate) { this.tag = tag; this.mCookie = cookie; @@ -138,6 +142,7 @@ public class WebNotification implements Parcelable { this.source = "".equals(source) ? null : source; this.silent = silent; this.vibrate = vibrate; + this.privateBrowsing = privateBrowsing; } /** @@ -189,6 +194,7 @@ public class WebNotification implements Parcelable { dest.writeInt(requireInteraction ? 1 : 0); dest.writeString(source); dest.writeInt(silent ? 1 : 0); + dest.writeInt(privateBrowsing ? 1 : 0); dest.writeIntArray(vibrate); } @@ -203,6 +209,7 @@ public class WebNotification implements Parcelable { requireInteraction = in.readInt() == 1; source = in.readString(); silent = in.readInt() == 1; + privateBrowsing = in.readInt() == 1; vibrate = in.createIntArray(); } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md index ab0dc434b20c..df752199dc31 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md @@ -1257,4 +1257,4 @@ to allow adding gecko profiler markers. [65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String) [65.25]: {{javadoc_uri}}/GeckoResult.html -[api-version]: 0996b9058eab189d80b636b07d61bdb7db45c367 +[api-version]: 5d853b0411014ac0c6d1059a5d69e69913f8c4c9 diff --git a/mobile/android/modules/geckoview/GeckoViewStorageController.jsm b/mobile/android/modules/geckoview/GeckoViewStorageController.jsm index e8a548629ed2..36da54c80fcf 100644 --- a/mobile/android/modules/geckoview/GeckoViewStorageController.jsm +++ b/mobile/android/modules/geckoview/GeckoViewStorageController.jsm @@ -133,7 +133,12 @@ const GeckoViewStorageController = { const uri = Services.io.newURI(aData.uri); const principal = Services.scriptSecurityManager.createContentPrincipal( uri, - aData.contextId ? { geckoViewSessionContextId: aData.contextId } : {} + aData.contextId + ? { + geckoViewSessionContextId: aData.contextId, + privateBrowsingId: aData.privateBrowsingId, + } + : { privateBrowsingId: aData.privateBrowsingId } ); const rawPerms = Services.perms.getAllForPrincipal(principal); const permissions = rawPerms.map(p => { @@ -162,17 +167,23 @@ const GeckoViewStorageController = { aData.newValue ); } else { + const expirePolicy = aData.privateMode + ? Ci.nsIPermissionManager.EXPIRE_SESSION + : Ci.nsIPermissionManager.EXPIRE_NEVER; Services.perms.addFromPrincipal( principal, key, aData.newValue, - Ci.nsIPermissionManager.EXPIRE_NEVER + expirePolicy ); } break; } case "GeckoView:SetPermissionByURI": { const uri = Services.io.newURI(aData.uri); + const expirePolicy = aData.privateId + ? Ci.nsIPermissionManager.EXPIRE_SESSION + : Ci.nsIPermissionManager.EXPIRE_NEVER; const principal = Services.scriptSecurityManager.createContentPrincipal( uri, { @@ -184,7 +195,7 @@ const GeckoViewStorageController = { principal, aData.perm, aData.newValue, - Ci.nsIPermissionManager.EXPIRE_NEVER + expirePolicy ); break; } diff --git a/widget/android/AndroidAlerts.cpp b/widget/android/AndroidAlerts.cpp index 683e06f79ed9..badef8a0342e 100644 --- a/widget/android/AndroidAlerts.cpp +++ b/widget/android/AndroidAlerts.cpp @@ -91,6 +91,10 @@ AndroidAlerts::ShowPersistentNotification(const nsAString& aPersistentData, rv = aAlert->GetSilent(&silent); NS_ENSURE_SUCCESS(rv, NS_OK); + bool privateBrowsing; + rv = aAlert->GetInPrivateBrowsing(&privateBrowsing); + NS_ENSURE_SUCCESS(rv, NS_OK); + nsTArray vibrate; rv = aAlert->GetVibrate(vibrate); NS_ENSURE_SUCCESS(rv, NS_OK); @@ -105,7 +109,7 @@ AndroidAlerts::ShowPersistentNotification(const nsAString& aPersistentData, java::WebNotification::LocalRef notification = notification->New( title, name, cookie, text, imageUrl, dir, lang, requireInteraction, spec, - silent, jni::IntArray::From(vibrate)); + silent, privateBrowsing, jni::IntArray::From(vibrate)); java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance(); if (runtime != NULL) { runtime->NotifyOnShow(notification);