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