1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
2  * Any copyright is dedicated to the Public Domain.
3    http://creativecommons.org/publicdomain/zero/1.0/ */
4 
5 package org.mozilla.geckoview.test
6 
7 import org.mozilla.geckoview.GeckoResult
8 import org.mozilla.geckoview.GeckoSession
9 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
10 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.RejectedPromiseException
11 import org.mozilla.geckoview.test.util.Callbacks
12 
13 import android.Manifest
14 import android.content.pm.PackageManager
15 import android.os.Build
16 import androidx.test.platform.app.InstrumentationRegistry
17 import androidx.test.filters.MediumTest
18 import androidx.test.ext.junit.runners.AndroidJUnit4
19 
20 import org.hamcrest.Matchers.*
21 import org.json.JSONArray
22 import org.junit.Assert.fail
23 import org.junit.Assume.assumeThat
24 import org.junit.Test
25 import org.junit.runner.RunWith
26 import org.junit.Ignore
27 import org.mozilla.geckoview.GeckoSessionSettings
28 
29 @RunWith(AndroidJUnit4::class)
30 @MediumTest
31 class PermissionDelegateTest : BaseSessionTest() {
32 
hasPermissionnull33     private fun hasPermission(permission: String): Boolean {
34         if (Build.VERSION.SDK_INT < 23) {
35             return true
36         }
37         return PackageManager.PERMISSION_GRANTED ==
38                 InstrumentationRegistry.getInstrumentation().targetContext.checkSelfPermission(permission)
39     }
40 
isEmulatornull41     private fun isEmulator(): Boolean {
42         return "generic".equals(Build.DEVICE) || Build.DEVICE.startsWith("generic_")
43     }
44 
medianull45     @Test fun media() {
46         // TODO: needs bug 1700243
47         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
48 
49         assertInAutomationThat("Should have camera permission",
50                 hasPermission(Manifest.permission.CAMERA), equalTo(true))
51 
52         assertInAutomationThat("Should have microphone permission",
53                 hasPermission(Manifest.permission.RECORD_AUDIO),
54                 equalTo(true))
55 
56         mainSession.loadTestPath(HELLO_HTML_PATH)
57         mainSession.waitForPageStop()
58 
59         val devices = mainSession.evaluateJS(
60                 "window.navigator.mediaDevices.enumerateDevices()") as JSONArray
61 
62         var hasVideo = false
63         var hasAudio = false
64         for (i in 0 until devices.length()) {
65             if (devices.getJSONObject(i).getString("kind") == "videoinput") {
66                 hasVideo = true;
67             }
68             if (devices.getJSONObject(i).getString("kind") == "audioinput") {
69                 hasAudio = true;
70             }
71         }
72 
73         assertThat("Device list should contain camera device",
74                 hasVideo, equalTo(true))
75         assertThat("Device list should contain microphone device",
76                 hasAudio, equalTo(true))
77 
78         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
79             @AssertCalled(count = 1)
80             override fun onMediaPermissionRequest(
81                     session: GeckoSession, uri: String,
82                     video: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
83                     audio: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
84                     callback: GeckoSession.PermissionDelegate.MediaCallback) {
85                 assertThat("URI should match", uri, endsWith(HELLO_HTML_PATH))
86                 assertThat("Video source should be valid", video, not(emptyArray()))
87 
88                 if (isEmulator()) {
89                     callback.grant(video!![0], null)
90                 } else {
91                     assertThat("Audio source should be valid", audio, not(emptyArray()))
92                     callback.grant(video!![0], audio!![0])
93                 }
94             }
95         })
96 
97         // Start a video stream, with audio if on a real device.
98         var code: String?
99         if (isEmulator()) {
100             code = """this.stream = window.navigator.mediaDevices.getUserMedia({
101                        video: { width: 320, height: 240, frameRate: 10 },
102                    });"""
103         } else {
104             code = """this.stream = window.navigator.mediaDevices.getUserMedia({
105                        video: { width: 320, height: 240, frameRate: 10 },
106                        audio: true
107                    });"""
108         }
109 
110         // Stop the stream and check active flag and id
111         val isActive = mainSession.waitForJS(
112                 """$code
113                    this.stream.then(stream => {
114                      if (!stream.active || stream.id == '') {
115                        return false;
116                      }
117 
118                      stream.getTracks().forEach(track => track.stop());
119                      return true;
120                    })
121                 """.trimMargin()) as Boolean
122 
123         assertThat("Stream should be active and id should not be empty.", isActive, equalTo(true));
124 
125         // Now test rejecting the request.
126         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
127             @AssertCalled(count = 1)
128             override fun onMediaPermissionRequest(
129                     session: GeckoSession, uri: String,
130                     video: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
131                     audio: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
132                     callback: GeckoSession.PermissionDelegate.MediaCallback) {
133                 callback.reject()
134             }
135         })
136 
137         try {
138             if (isEmulator()) {
139                 mainSession.waitForJS("""
140                         window.navigator.mediaDevices.getUserMedia({ video: true })""")
141             } else {
142                 mainSession.waitForJS("""
143                         window.navigator.mediaDevices.getUserMedia({ audio: true: video: true })""")
144             }
145             fail("Request should have failed")
146         } catch (e: RejectedPromiseException) {
147             assertThat("Error should be correct",
148                     e.reason as String, containsString("NotAllowedError"))
149         }
150     }
151 
geolocationnull152     @Test fun geolocation() {
153         assertInAutomationThat("Should have location permission",
154                 hasPermission(Manifest.permission.ACCESS_FINE_LOCATION),
155                 equalTo(true))
156 
157         val url = createTestUrl(HELLO_HTML_PATH)
158         mainSession.loadUri(url)
159         mainSession.waitForPageStop()
160 
161         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
162             // Ensure the content permission is asked first, before the Android permission.
163             @AssertCalled(count = 1, order = [1])
164             override fun onContentPermissionRequest(
165                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
166                     GeckoResult<Int>? {
167                 assertThat("URI should match", perm.uri, endsWith(url))
168                 assertThat("Type should match", perm.permission,
169                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION))
170                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
171             }
172 
173             @AssertCalled(count = 1, order = [2])
174             override fun onAndroidPermissionsRequest(
175                     session: GeckoSession, permissions: Array<out String>?,
176                     callback: GeckoSession.PermissionDelegate.Callback) {
177                 assertThat("Permissions list should be correct",
178                         listOf(*permissions!!), hasItems(Manifest.permission.ACCESS_FINE_LOCATION))
179                 callback.grant()
180             }
181         })
182 
183         try {
184             val hasPosition = mainSession.waitForJS("""new Promise((resolve, reject) =>
185                     window.navigator.geolocation.getCurrentPosition(
186                         position => resolve(
187                             position.coords.latitude !== undefined &&
188                             position.coords.longitude !== undefined),
189                         error => reject(error.code)))""") as Boolean
190 
191             assertThat("Request should succeed", hasPosition, equalTo(true))
192         } catch (ex: RejectedPromiseException) {
193             assertThat("Error should not because the permission was denied.",
194                     ex.reason as String, not("1"))
195         }
196 
197         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
198 
199         assertThat("Permissions should not be null", perms, notNullValue())
200         var permFound : Boolean = false
201         for (perm in perms) {
202             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION &&
203                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
204                 permFound = true
205             }
206         }
207 
208         assertThat("Geolocation permission should be set to allow", permFound, equalTo(true))
209 
210         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
211             @AssertCalled(count = 1)
212             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
213                 var permFound2 : Boolean = false
214                 for (perm in perms) {
215                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION &&
216                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
217                         permFound2 = true
218                     }
219                 }
220                 assertThat("Geolocation permission must be present on refresh", permFound2, equalTo(true))
221             }
222         })
223         mainSession.reload()
224         mainSession.waitForPageStop()
225     }
226 
geolocation_rejectnull227     @Test fun geolocation_reject() {
228         val url = createTestUrl(HELLO_HTML_PATH)
229         mainSession.loadUri(url)
230         mainSession.waitForPageStop()
231 
232         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
233             @AssertCalled(count = 1)
234             override fun onContentPermissionRequest(
235                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
236                     GeckoResult<Int>? {
237                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY)
238             }
239 
240             @AssertCalled(count = 0)
241             override fun onAndroidPermissionsRequest(
242                     session: GeckoSession, permissions: Array<out String>?,
243                     callback: GeckoSession.PermissionDelegate.Callback) {
244             }
245         })
246 
247         val errorCode = mainSession.waitForJS("""new Promise((resolve, reject) =>
248                 window.navigator.geolocation.getCurrentPosition(reject,
249                   error => resolve(error.code)
250                 ))""")
251 
252         // Error code 1 means permission denied.
253         assertThat("Request should fail", errorCode as Double, equalTo(1.0))
254 
255         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
256 
257         assertThat("Permissions should not be null", perms, notNullValue())
258         var permFound : Boolean = false
259         for (perm in perms) {
260             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION &&
261                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY) {
262                 permFound = true
263             }
264         }
265 
266         assertThat("Geolocation permission should be set to allow", permFound, equalTo(true))
267 
268         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
269             @AssertCalled(count = 1)
270             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
271                 var permFound2 : Boolean = false
272                 for (perm in perms) {
273                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION &&
274                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY) {
275                         permFound2 = true
276                     }
277                 }
278                 assertThat("Geolocation permission must be present on refresh", permFound2, equalTo(true))
279             }
280         })
281         mainSession.reload()
282         mainSession.waitForPageStop()
283     }
284 
notificationnull285     @Test fun notification() {
286         sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.requireuserinteraction" to false))
287         val url = createTestUrl(HELLO_HTML_PATH)
288         mainSession.loadUri(url)
289         mainSession.waitForPageStop()
290 
291         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
292             @AssertCalled(count = 1)
293             override fun onContentPermissionRequest(
294                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
295                     GeckoResult<Int>? {
296                 assertThat("URI should match", perm.uri, endsWith(url))
297                 assertThat("Type should match", perm.permission,
298                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
299                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
300             }
301         })
302 
303         val result = mainSession.waitForJS("Notification.requestPermission()")
304 
305         assertThat("Permission should be granted",
306                 result as String, equalTo("granted"))
307 
308         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
309 
310         assertThat("Permissions should not be null", perms, notNullValue())
311         var permFound : Boolean = false
312         for (perm in perms) {
313             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
314                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
315                 permFound = true
316             }
317         }
318 
319         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
320 
321         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
322             @AssertCalled(count = 1)
323             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
324                 var permFound2 : Boolean = false
325                 for (perm in perms) {
326                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
327                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
328                         permFound2 = true
329                     }
330                 }
331                 assertThat("Notification permission must be present on refresh", permFound2, equalTo(true))
332             }
333         })
334         mainSession.reload()
335         mainSession.waitForPageStop()
336 
337         val result2 = mainSession.waitForJS("Notification.permission")
338 
339         assertThat("Permission should be granted",
340                 result2 as String, equalTo("granted"))
341     }
342 
343     @Ignore("disable test for frequently failing Bug 1542525")
notification_rejectnull344     @Test fun notification_reject() {
345         val url = createTestUrl(HELLO_HTML_PATH)
346         mainSession.loadUri(url)
347         mainSession.waitForPageStop()
348 
349         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
350             @AssertCalled(count = 1)
351             override fun onContentPermissionRequest(
352                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
353                     GeckoResult<Int>? {
354                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY)
355             }
356         })
357 
358         val result = mainSession.waitForJS("Notification.requestPermission()")
359 
360         assertThat("Permission should not be granted",
361                 result as String, equalTo("denied"))
362 
363         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
364 
365         assertThat("Permissions should not be null", perms, notNullValue())
366         var permFound : Boolean = false
367         for (perm in perms) {
368             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
369                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY) {
370                 permFound = true
371             }
372         }
373 
374         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
375 
376         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
377             @AssertCalled(count = 1)
378             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
379                 var permFound2 : Boolean = false
380                 for (perm in perms) {
381                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
382                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY) {
383                         permFound2 = true
384                     }
385                 }
386                 assertThat("Notification permission must be present on refresh", permFound2, equalTo(true))
387             }
388         })
389         mainSession.reload()
390         mainSession.waitForPageStop()
391     }
392 
393     @Test
autoplayRejectnull394     fun autoplayReject() {
395         // The profile used in automation sets this to false, so we need to hack it back to true here.
396         sessionRule.setPrefsUntilTestEnd(mapOf(
397                 "media.geckoview.autoplay.request" to true))
398 
399         mainSession.loadTestPath(AUTOPLAY_PATH)
400 
401         mainSession.waitUntilCalled(object : Callbacks.PermissionDelegate {
402             @AssertCalled(count = 2)
403             override fun onContentPermissionRequest(session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
404                     GeckoResult<Int>? {
405                 val expectedType = if (sessionRule.currentCall.counter == 1) GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY_AUDIBLE else GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY_INAUDIBLE
406                 assertThat("Type should match", perm.permission, equalTo(expectedType))
407                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY)
408             }
409         })
410     }
411 
412     @Test
contextIdnull413     fun contextId() {
414         sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.requireuserinteraction" to false))
415         val url = createTestUrl(HELLO_HTML_PATH)
416         mainSession.loadUri(url)
417         mainSession.waitForPageStop()
418 
419         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
420             @AssertCalled(count = 1)
421             override fun onContentPermissionRequest(
422                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
423                     GeckoResult<Int>? {
424                 assertThat("URI should match", perm.uri, endsWith(url))
425                 assertThat("Type should match", perm.permission,
426                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
427                 assertThat("Context ID should match", perm.contextId, equalTo(mainSession.settings.contextId))
428                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
429             }
430         })
431 
432         val result = mainSession.waitForJS("Notification.requestPermission()")
433 
434         assertThat("Permission should be granted",
435                 result as String, equalTo("granted"))
436 
437         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
438 
439         assertThat("Permissions should not be null", perms, notNullValue())
440         var permFound: Boolean = false
441         for (perm in perms) {
442             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
443                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
444                 permFound = true
445             }
446         }
447 
448         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
449 
450         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
451             @AssertCalled(count = 1)
452             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
453                 var permFound2: Boolean = false
454                 for (perm in perms) {
455                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
456                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
457                         permFound2 = true
458                     }
459                 }
460                 assertThat("Notification permission must be present on refresh", permFound2, equalTo(true))
461             }
462         })
463         mainSession.reload()
464         mainSession.waitForPageStop()
465 
466         val session2 = sessionRule.createOpenSession(
467                 GeckoSessionSettings.Builder()
468                         .contextId("foo")
469                         .build())
470 
471         session2.loadUri(url)
472         session2.waitForPageStop()
473 
474         session2.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
475             @AssertCalled(count = 1)
476             override fun onContentPermissionRequest(
477                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
478                     GeckoResult<Int>? {
479                 assertThat("URI should match", perm.uri, endsWith(url))
480                 assertThat("Type should match", perm.permission,
481                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
482                 assertThat("Context ID should match", perm.contextId,
483                         equalTo(session2.settings.contextId))
484                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
485             }
486         })
487 
488         val result2 = session2.waitForJS("Notification.requestPermission()")
489 
490         assertThat("Permission should be granted",
491                 result2 as String, equalTo("granted"))
492 
493         val perms2 = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
494 
495         assertThat("Permissions should not be null", perms, notNullValue())
496         permFound = false
497         for (perm in perms2) {
498             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
499                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
500                 permFound = true
501             }
502         }
503 
504         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
505 
506         session2.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
507             @AssertCalled(count = 1)
508             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
509                 var permFound2: Boolean = false
510                 for (perm in perms) {
511                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
512                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW &&
513                             perm.contextId == session2.settings.contextId) {
514                         permFound2 = true
515                     }
516                 }
517                 assertThat("Notification permission must be present on refresh", permFound2, equalTo(true))
518             }
519         })
520         session2.reload()
521         session2.waitForPageStop()
522     }
523 
setPermissionAllownull524     @Test fun setPermissionAllow() {
525         sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.requireuserinteraction" to false))
526         val url = createTestUrl(HELLO_HTML_PATH)
527         mainSession.loadUri(url)
528         mainSession.waitForPageStop()
529 
530         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
531             @AssertCalled(count = 1)
532             override fun onContentPermissionRequest(
533                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
534                     GeckoResult<Int>? {
535                 assertThat("URI should match", perm.uri, endsWith(url))
536                 assertThat("Type should match", perm.permission,
537                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
538                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY)
539             }
540         })
541         mainSession.waitForJS("Notification.requestPermission()")
542 
543         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
544 
545         assertThat("Permissions should not be null", perms, notNullValue())
546         var permFound : Boolean = false
547         var notificationPerm : GeckoSession.PermissionDelegate.ContentPermission? = null
548         for (perm in perms) {
549             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
550                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY) {
551                 notificationPerm = perm
552                 permFound = true
553             }
554         }
555 
556         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
557 
558         sessionRule.runtime.storageController.setPermission(notificationPerm!!,
559                 GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
560 
561         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
562             @AssertCalled(count = 1)
563             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
564                 var permFound2 : Boolean = false
565                 for (perm in perms) {
566                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
567                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
568                         permFound2 = true
569                     }
570                 }
571                 assertThat("Notification permission must be present on refresh", permFound2, equalTo(true))
572             }
573         })
574         mainSession.reload()
575         mainSession.waitForPageStop()
576 
577         val result = mainSession.waitForJS("Notification.permission")
578 
579         assertThat("Permission should be granted",
580                 result as String, equalTo("granted"))
581     }
582 
setPermissionDenynull583     @Test fun setPermissionDeny() {
584         sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.requireuserinteraction" to false))
585         val url = createTestUrl(HELLO_HTML_PATH)
586         mainSession.loadUri(url)
587         mainSession.waitForPageStop()
588 
589         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
590             @AssertCalled(count = 1)
591             override fun onContentPermissionRequest(
592                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
593                     GeckoResult<Int>? {
594                 assertThat("URI should match", perm.uri, endsWith(url))
595                 assertThat("Type should match", perm.permission,
596                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
597                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
598             }
599         })
600 
601         val result = mainSession.waitForJS("Notification.requestPermission()")
602 
603         assertThat("Permission should be granted",
604                 result as String, equalTo("granted"))
605 
606         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
607 
608         assertThat("Permissions should not be null", perms, notNullValue())
609         var permFound : Boolean = false
610         var notificationPerm : GeckoSession.PermissionDelegate.ContentPermission? = null
611         for (perm in perms) {
612             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
613                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
614                 notificationPerm = perm
615                 permFound = true
616             }
617         }
618 
619         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
620 
621         sessionRule.runtime.storageController.setPermission(notificationPerm!!,
622                 GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY)
623 
624         mainSession.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
625             @AssertCalled(count = 1)
626             override fun onLocationChange(session: GeckoSession, url: String?, perms: MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
627                 var permFound2 : Boolean = false
628                 for (perm in perms) {
629                     if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
630                             perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_DENY) {
631                         permFound2 = true
632                     }
633                 }
634                 assertThat("Notification permission must be present on refresh", permFound2, equalTo(true))
635             }
636         })
637         mainSession.reload()
638         mainSession.waitForPageStop()
639 
640         val result2 = mainSession.waitForJS("Notification.permission")
641 
642         assertThat("Permission should be denied",
643                 result2 as String, equalTo("denied"))
644     }
645 
setPermissionPromptnull646     @Test fun setPermissionPrompt() {
647         sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.requireuserinteraction" to false))
648         val url = createTestUrl(HELLO_HTML_PATH)
649         mainSession.loadUri(url)
650         mainSession.waitForPageStop()
651 
652         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
653             @AssertCalled(count = 1)
654             override fun onContentPermissionRequest(
655                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
656                     GeckoResult<Int>? {
657                 assertThat("URI should match", perm.uri, endsWith(url))
658                 assertThat("Type should match", perm.permission,
659                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
660                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
661             }
662         })
663 
664         val result = mainSession.waitForJS("Notification.requestPermission()")
665 
666         assertThat("Permission should be granted",
667                 result as String, equalTo("granted"))
668 
669         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
670 
671         assertThat("Permissions should not be null", perms, notNullValue())
672         var permFound : Boolean = false
673         var notificationPerm : GeckoSession.PermissionDelegate.ContentPermission? = null
674         for (perm in perms) {
675             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
676                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
677                 notificationPerm = perm
678                 permFound = true
679             }
680         }
681 
682         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
683 
684         sessionRule.runtime.storageController.setPermission(notificationPerm!!,
685                 GeckoSession.PermissionDelegate.ContentPermission.VALUE_PROMPT)
686 
687         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
688             @AssertCalled(count = 1)
689             override fun onContentPermissionRequest(
690                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
691                     GeckoResult<Int>? {
692                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_PROMPT)
693             }
694         })
695 
696         val result2 = mainSession.waitForJS("Notification.requestPermission()")
697 
698         assertThat("Permission should be default",
699                 result2 as String, equalTo("default"))
700     }
701 
permissionJsonConversionnull702     @Test fun permissionJsonConversion() {
703         sessionRule.setPrefsUntilTestEnd(mapOf("dom.webnotifications.requireuserinteraction" to false))
704         val url = createTestUrl(HELLO_HTML_PATH)
705         mainSession.loadUri(url)
706         mainSession.waitForPageStop()
707 
708         mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
709             @AssertCalled(count = 1)
710             override fun onContentPermissionRequest(
711                     session: GeckoSession, perm: GeckoSession.PermissionDelegate.ContentPermission):
712                     GeckoResult<Int>? {
713                 assertThat("URI should match", perm.uri, endsWith(url))
714                 assertThat("Type should match", perm.permission,
715                         equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION))
716                 return GeckoResult.fromValue(GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW)
717             }
718         })
719 
720         val result = mainSession.waitForJS("Notification.requestPermission()")
721 
722         assertThat("Permission should be granted",
723                 result as String, equalTo("granted"))
724 
725         val perms = sessionRule.waitForResult(sessionRule.runtime.storageController.getPermissions(url))
726 
727         assertThat("Permissions should not be null", perms, notNullValue())
728         var permFound : Boolean = false
729         var notificationPerm : GeckoSession.PermissionDelegate.ContentPermission? = null
730         for (perm in perms) {
731             if (perm.permission == GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION &&
732                     url.startsWith(perm.uri) && perm.value == GeckoSession.PermissionDelegate.ContentPermission.VALUE_ALLOW) {
733                 notificationPerm = perm
734                 permFound = true
735             }
736         }
737 
738         assertThat("Notification permission should be set to allow", permFound, equalTo(true))
739 
740         val jsonPerm = notificationPerm?.toJson()
741         assertThat("JSON export should not be null", jsonPerm, notNullValue())
742 
743         val importedPerm = GeckoSession.PermissionDelegate.ContentPermission.fromJson(jsonPerm!!)
744         assertThat("JSON import should not be null", importedPerm, notNullValue())
745 
746         assertThat("URIs should match", importedPerm?.uri, equalTo(notificationPerm?.uri))
747         assertThat("Types should match", importedPerm?.permission, equalTo(notificationPerm?.permission))
748         assertThat("Values should match", importedPerm?.value, equalTo(notificationPerm?.value))
749         assertThat("Context IDs should match", importedPerm?.contextId, equalTo(notificationPerm?.contextId))
750         assertThat("Private mode should match", importedPerm?.privateMode, equalTo(notificationPerm?.privateMode))
751     }
752 
753     // @Test fun persistentStorage() {
754     //     mainSession.loadTestPath(HELLO_HTML_PATH)
755     //     mainSession.waitForPageStop()
756 
757     //     // Persistent storage can be rejected
758     //     mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
759     //         @AssertCalled(count = 1)
760     //         override fun onContentPermissionRequest(
761     //                 session: GeckoSession, uri: String?, type: Int,
762     //                 callback: GeckoSession.PermissionDelegate.Callback) {
763     //             callback.reject()
764     //         }
765     //     })
766 
767     //     var success = mainSession.waitForJS("""window.navigator.storage.persist()""")
768 
769     //     assertThat("Request should fail",
770     //             success as Boolean, equalTo(false))
771 
772     //     // Persistent storage can be granted
773     //     mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
774     //         // Ensure the content permission is asked first, before the Android permission.
775     //         @AssertCalled(count = 1, order = [1])
776     //         override fun onContentPermissionRequest(
777     //                 session: GeckoSession, uri: String?, type: Int,
778     //                 callback: GeckoSession.PermissionDelegate.Callback) {
779     //             assertThat("URI should match", uri, endsWith(HELLO_HTML_PATH))
780     //             assertThat("Type should match", type,
781     //                     equalTo(GeckoSession.PermissionDelegate.PERMISSION_PERSISTENT_STORAGE))
782     //             callback.grant()
783     //         }
784     //     })
785 
786     //     success = mainSession.waitForJS("""window.navigator.storage.persist()""")
787 
788     //     assertThat("Request should succeed",
789     //             success as Boolean,
790     //             equalTo(true))
791 
792     //     // after permission granted further requests will always return true, regardless of response
793     //     mainSession.delegateDuringNextWait(object : Callbacks.PermissionDelegate {
794     //         @AssertCalled(count = 1)
795     //         override fun onContentPermissionRequest(
796     //                 session: GeckoSession, uri: String?, type: Int,
797     //                 callback: GeckoSession.PermissionDelegate.Callback) {
798     //             callback.reject()
799     //         }
800     //     })
801 
802     //     success = mainSession.waitForJS("""window.navigator.storage.persist()""")
803 
804     //     assertThat("Request should succeed",
805     //             success as Boolean, equalTo(true))
806     // }
807 }
808