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 android.graphics.Bitmap
8 import android.os.SystemClock
9 import android.view.KeyEvent
10 import android.util.Base64
11 import androidx.test.ext.junit.runners.AndroidJUnit4
12 import androidx.test.filters.MediumTest
13 import java.io.ByteArrayOutputStream
14 import java.util.concurrent.ThreadLocalRandom
15 import org.hamcrest.Matchers.*
16 import org.json.JSONObject
17 import org.junit.Assume.assumeThat
18 import org.junit.Ignore
19 import org.junit.Test
20 import org.junit.runner.RunWith
21 import org.mozilla.geckoview.*
22 import org.mozilla.geckoview.GeckoSession.*
23 import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
24 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
25 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.*
26 import org.mozilla.geckoview.test.util.Callbacks
27 import org.mozilla.geckoview.test.util.UiThreadUtils
28 
29 @RunWith(AndroidJUnit4::class)
30 @MediumTest
31 class NavigationDelegateTest : BaseSessionTest() {
32 
33     // Provides getters for Loader
34     class TestLoader : Loader() {
35         var mUri: String? = null
urinull36         override fun uri(uri: String): TestLoader {
37             mUri = uri
38             super.uri(uri)
39             return this
40         }
getUrinull41         fun getUri() : String? {
42             return mUri
43         }
flagsnull44         override fun flags(f: Int): TestLoader {
45             super.flags(f)
46             return this
47         }
48     }
49 
testLoadErrorWithErrorPagenull50     fun testLoadErrorWithErrorPage(testLoader: TestLoader, expectedCategory: Int,
51                                    expectedError: Int,
52                                    errorPageUrl: String?) {
53         sessionRule.delegateDuringNextWait(
54                 object : Callbacks.ProgressDelegate, Callbacks.NavigationDelegate, Callbacks.ContentDelegate {
55                     @AssertCalled(count = 1, order = [1])
56                     override fun onLoadRequest(session: GeckoSession,
57                                                request: LoadRequest):
58                                                GeckoResult<AllowOrDeny>? {
59                         assertThat("URI should be " + testLoader.getUri(), request.uri,
60                                 equalTo(testLoader.getUri()))
61                         assertThat("App requested this load", request.isDirectNavigation,
62                                 equalTo(true))
63                         return null
64                     }
65 
66                     @AssertCalled(count = 1, order = [2])
67                     override fun onPageStart(session: GeckoSession, url: String) {
68                         assertThat("URI should be " + testLoader.getUri(), url,
69                                 equalTo(testLoader.getUri()))
70                     }
71 
72                     @AssertCalled(count = 1, order = [3])
73                     override fun onLoadError(session: GeckoSession, uri: String?,
74                                              error: WebRequestError): GeckoResult<String>? {
75                         assertThat("Error category should match", error.category,
76                                 equalTo(expectedCategory))
77                         assertThat("Error code should match", error.code,
78                                 equalTo(expectedError))
79                         return GeckoResult.fromValue(errorPageUrl)
80                     }
81 
82                     @AssertCalled(count = 1, order = [4])
83                     override fun onPageStop(session: GeckoSession, success: Boolean) {
84                         assertThat("Load should fail", success, equalTo(false))
85                     }
86                 })
87 
88         sessionRule.session.load(testLoader)
89         sessionRule.waitForPageStop()
90 
91         if (errorPageUrl != null) {
92             sessionRule.waitUntilCalled(object : Callbacks.ContentDelegate, Callbacks.NavigationDelegate {
93                 @AssertCalled(count = 1, order = [1])
94                 override fun onLocationChange(session: GeckoSession, url: String?) {
95                     assertThat("URL should match", url, equalTo(testLoader.getUri()))
96                 }
97 
98                 @AssertCalled(count = 1, order = [2])
99                 override fun onTitleChange(session: GeckoSession, title: String?) {
100                     assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
101                 }
102             })
103         }
104     }
105 
testLoadExpectErrornull106     fun testLoadExpectError(testUri: String, expectedCategory: Int,
107                             expectedError: Int) {
108         testLoadExpectError(TestLoader().uri(testUri), expectedCategory, expectedError)
109     }
110 
testLoadExpectErrornull111     fun testLoadExpectError(testLoader: TestLoader, expectedCategory: Int,
112                             expectedError: Int) {
113         testLoadErrorWithErrorPage(testLoader, expectedCategory,
114                 expectedError, createTestUrl(HELLO_HTML_PATH))
115         testLoadErrorWithErrorPage(testLoader, expectedCategory,
116                 expectedError, null)
117     }
118 
testLoadEarlyErrorWithErrorPagenull119     fun testLoadEarlyErrorWithErrorPage(testUri: String, expectedCategory: Int,
120                                         expectedError: Int,
121                                         errorPageUrl: String?) {
122         sessionRule.delegateDuringNextWait(
123                 object : Callbacks.ProgressDelegate, Callbacks.NavigationDelegate, Callbacks.ContentDelegate {
124 
125                     @AssertCalled(false)
126                     override fun onPageStart(session: GeckoSession, url: String) {
127                         assertThat("URI should be " + testUri, url, equalTo(testUri))
128                     }
129 
130                     @AssertCalled(count = 1, order = [1])
131                     override fun onLoadError(session: GeckoSession, uri: String?,
132                                              error: WebRequestError): GeckoResult<String>? {
133                         assertThat("Error category should match", error.category,
134                                 equalTo(expectedCategory))
135                         assertThat("Error code should match", error.code,
136                                 equalTo(expectedError))
137                         return GeckoResult.fromValue(errorPageUrl)
138                     }
139 
140                     @AssertCalled(false)
141                     override fun onPageStop(session: GeckoSession, success: Boolean) {
142                     }
143                 })
144 
145         sessionRule.session.loadUri(testUri)
146         sessionRule.waitUntilCalled(Callbacks.NavigationDelegate::class, "onLoadError")
147 
148         if (errorPageUrl != null) {
149             sessionRule.waitUntilCalled(object: Callbacks.ContentDelegate {
150                 @AssertCalled(count = 1)
151                 override fun onTitleChange(session: GeckoSession, title: String?) {
152                     assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
153                 }
154             })
155         }
156     }
157 
testLoadEarlyErrornull158     fun testLoadEarlyError(testUri: String, expectedCategory: Int,
159                            expectedError: Int) {
160         testLoadEarlyErrorWithErrorPage(testUri, expectedCategory, expectedError, createTestUrl(HELLO_HTML_PATH))
161         testLoadEarlyErrorWithErrorPage(testUri, expectedCategory, expectedError, null)
162     }
163 
loadFileNotFoundnull164     @Test fun loadFileNotFound() {
165         // TODO: Bug 1673954
166         assumeThat(sessionRule.env.isFission, equalTo(false))
167 
168         // TODO: bug 1710943
169         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
170 
171         testLoadExpectError("file:///test.mozilla",
172                 WebRequestError.ERROR_CATEGORY_URI,
173                 WebRequestError.ERROR_FILE_NOT_FOUND)
174 
175         val promise = mainSession.evaluatePromiseJS("document.addCertException(false)")
176         var exceptionCaught = false
177         try {
178             val result = promise.value as Boolean
179             assertThat("Promise should not resolve", result, equalTo(false))
180         } catch (e: GeckoSessionTestRule.RejectedPromiseException) {
181             exceptionCaught = true;
182         }
183         assertThat("document.addCertException failed with exception", exceptionCaught, equalTo(true))
184     }
185 
loadUnknownHostnull186     @Test fun loadUnknownHost() {
187         // TODO: Bug 1673954
188         assumeThat(sessionRule.env.isFission, equalTo(false))
189 
190         // TODO: bug 1710943
191         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
192 
193         testLoadExpectError(UNKNOWN_HOST_URI,
194                 WebRequestError.ERROR_CATEGORY_URI,
195                 WebRequestError.ERROR_UNKNOWN_HOST)
196     }
197 
198     // External loads should not have access to privileged protocols
loadExternalDeniednull199     @Test fun loadExternalDenied() {
200         // TODO: Bug 1673954
201         assumeThat(sessionRule.env.isFission, equalTo(false))
202 
203         // TODO: bug 1710943
204         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
205 
206         testLoadExpectError(TestLoader().uri("file:///").flags(LOAD_FLAGS_EXTERNAL),
207                 WebRequestError.ERROR_CATEGORY_UNKNOWN,
208                 WebRequestError.ERROR_UNKNOWN)
209         testLoadExpectError(TestLoader().uri("resource://gre/").flags(LOAD_FLAGS_EXTERNAL),
210                 WebRequestError.ERROR_CATEGORY_UNKNOWN,
211                 WebRequestError.ERROR_UNKNOWN)
212         testLoadExpectError(TestLoader().uri("about:about").flags(LOAD_FLAGS_EXTERNAL),
213                 WebRequestError.ERROR_CATEGORY_UNKNOWN,
214                 WebRequestError.ERROR_UNKNOWN)
215     }
216 
loadInvalidUrinull217     @Test fun loadInvalidUri() {
218         // TODO: bug 1710943
219         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
220 
221         testLoadEarlyError(INVALID_URI,
222                 WebRequestError.ERROR_CATEGORY_URI,
223                 WebRequestError.ERROR_MALFORMED_URI)
224     }
225 
loadBadPortnull226     @Test fun loadBadPort() {
227         // TODO: bug 1710943
228         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
229 
230         testLoadEarlyError("http://localhost:1/",
231                 WebRequestError.ERROR_CATEGORY_NETWORK,
232                 WebRequestError.ERROR_PORT_BLOCKED)
233     }
234 
loadUntrustednull235     @Test fun loadUntrusted() {
236         // TODO: Bug 1673954
237         assumeThat(sessionRule.env.isFission, equalTo(false))
238         val host = if (sessionRule.env.isAutomation) {
239             "expired.example.com"
240         } else {
241             "expired.badssl.com"
242         }
243         val uri = "https://$host/"
244         testLoadExpectError(uri,
245                 WebRequestError.ERROR_CATEGORY_SECURITY,
246                 WebRequestError.ERROR_SECURITY_BAD_CERT)
247 
248         mainSession.waitForJS("document.addCertException(false)")
249         mainSession.delegateDuringNextWait(
250                 object : Callbacks.ProgressDelegate, Callbacks.NavigationDelegate, Callbacks.ContentDelegate {
251                     @AssertCalled(count = 1, order = [1])
252                     override fun onPageStart(session: GeckoSession, url: String) {
253                         assertThat("URI should be " + uri, url, equalTo(uri))
254                     }
255 
256                     @AssertCalled(count = 1, order = [2])
257                     override fun onPageStop(session: GeckoSession, success: Boolean) {
258                         assertThat("Load should succeed", success, equalTo(true))
259                         sessionRule.removeAllCertOverrides()
260                     }
261                 })
262         mainSession.evaluateJS("location.reload()")
263         mainSession.waitForPageStop()
264     }
265 
loadDeprecatedTlsnull266     @Test fun loadDeprecatedTls() {
267         // TODO: Bug 1673954
268         assumeThat(sessionRule.env.isFission, equalTo(false))
269 
270         // TODO: bug 1710943
271         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
272 
273         // Load an initial generic error page in order to ensure 'allowDeprecatedTls' is false
274         testLoadExpectError(UNKNOWN_HOST_URI,
275                 WebRequestError.ERROR_CATEGORY_URI,
276                 WebRequestError.ERROR_UNKNOWN_HOST)
277         mainSession.evaluateJS("document.allowDeprecatedTls = false")
278 
279         val uri = if (sessionRule.env.isAutomation) {
280             "https://tls1.example.com/"
281         } else {
282             "https://tls-v1-0.badssl.com:1010/"
283         }
284         testLoadExpectError(uri,
285                 WebRequestError.ERROR_CATEGORY_SECURITY,
286                 WebRequestError.ERROR_SECURITY_SSL)
287 
288         mainSession.delegateDuringNextWait(object : Callbacks.ProgressDelegate, Callbacks.NavigationDelegate {
289             @AssertCalled(count = 0)
290             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
291                 return null
292             }
293 
294             @AssertCalled(count = 1)
295             override fun onPageStop(session: GeckoSession, success: Boolean) {
296                 assertThat("Load should be successful", success, equalTo(true))
297             }
298         })
299 
300         mainSession.evaluateJS("document.allowDeprecatedTls = true")
301         mainSession.reload()
302         mainSession.waitForPageStop()
303     }
304 
loadWithHTTPSOnlyModenull305     @Test fun loadWithHTTPSOnlyMode() {
306         sessionRule.runtime.settings.setAllowInsecureConnections(GeckoRuntimeSettings.HTTPS_ONLY)
307 
308         val insecureUri = if (sessionRule.env.isAutomation) {
309             "http://nocert.example.com/"
310         } else {
311             "http://neverssl.com"
312         }
313 
314         val secureUri = if (sessionRule.env.isAutomation) {
315             "http://example.com/"
316         } else {
317             "http://neverssl.com"
318         }
319 
320         mainSession.loadUri(insecureUri)
321         mainSession.waitForPageStop()
322 
323         mainSession.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
324             @AssertCalled(count = 1)
325             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
326                 assertThat("categories should match", error.category, equalTo(WebRequestError.ERROR_CATEGORY_SECURITY))
327                 assertThat("codes should match", error.code, equalTo(WebRequestError.ERROR_SECURITY_BAD_CERT))
328                 return null
329             }
330         })
331 
332         sessionRule.runtime.settings.setAllowInsecureConnections(GeckoRuntimeSettings.ALLOW_ALL)
333 
334         mainSession.loadUri(secureUri)
335         mainSession.waitForPageStop()
336 
337         mainSession.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
338             @AssertCalled(count = 0)
339             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
340                 return null
341             }
342 
343             @AssertCalled(count = 1)
344             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
345                 return null
346             }
347         })
348 
349         val privateSession = sessionRule.createOpenSession(
350                 GeckoSessionSettings.Builder(mainSession.settings)
351                         .usePrivateMode(true)
352                         .build())
353 
354         privateSession.loadUri(secureUri)
355         privateSession.waitForPageStop()
356 
357         var onLoadCalledCounter = 0
358         privateSession.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
359             @AssertCalled(count = 0)
360             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
361                 return null
362             }
363 
364             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
365                 onLoadCalledCounter++
366                 return null
367             }
368         })
369 
370         val httpsFirstPBMPref = "dom.security.https_first_pbm"
371         val httpsFirstPBMPrefValue = (sessionRule.getPrefs(httpsFirstPBMPref)[0] as Boolean)
372         if (httpsFirstPBMPrefValue) {
373             // if https-first is enabled we get two calls to onLoadRequest
374             // (1) http://example.com/ and  (2) https://example.com/
375             assertThat("Assert count privateSession.onLoadRequest", onLoadCalledCounter, equalTo(2))
376         } else {
377             assertThat("Assert count privateSession.onLoadRequest", onLoadCalledCounter, equalTo(1))
378         }
379 
380         sessionRule.runtime.settings.setAllowInsecureConnections(GeckoRuntimeSettings.HTTPS_ONLY_PRIVATE)
381 
382         privateSession.loadUri(insecureUri)
383         privateSession.waitForPageStop()
384 
385         privateSession.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
386             @AssertCalled(count = 1)
387             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
388                 assertThat("categories should match", error.category, equalTo(WebRequestError.ERROR_CATEGORY_SECURITY))
389                 assertThat("codes should match", error.code, equalTo(WebRequestError.ERROR_SECURITY_BAD_CERT))
390                 return null
391             }
392         })
393 
394         mainSession.loadUri(secureUri)
395         mainSession.waitForPageStop()
396 
397         mainSession.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
398             @AssertCalled(count = 0)
399             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
400                 return null
401             }
402 
403             @AssertCalled(count = 1)
404             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
405                 return null
406             }
407         })
408 
409         sessionRule.runtime.settings.setAllowInsecureConnections(GeckoRuntimeSettings.ALLOW_ALL)
410     }
411 
412     @Ignore // Disabled for bug 1619344.
loadUnknownProtocolnull413     @Test fun loadUnknownProtocol() {
414         testLoadEarlyError(UNKNOWN_PROTOCOL_URI,
415                 WebRequestError.ERROR_CATEGORY_URI,
416                 WebRequestError.ERROR_UNKNOWN_PROTOCOL)
417     }
418 
loadUnknownProtocolIframenull419     @Test fun loadUnknownProtocolIframe() {
420         // TODO: bug 1710943
421         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
422 
423         // Should match iframe URI from IFRAME_UNKNOWN_PROTOCOL
424         val iframeUri = "foo://bar"
425         sessionRule.session.loadTestPath(IFRAME_UNKNOWN_PROTOCOL)
426         sessionRule.session.waitForPageStop()
427 
428         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
429             @AssertCalled(count = 1)
430             override fun onLoadRequest(session: GeckoSession, request: LoadRequest) : GeckoResult<AllowOrDeny>? {
431                 assertThat("URI should not be null", request.uri, notNullValue())
432                 assertThat("URI should match", request.uri, endsWith(IFRAME_UNKNOWN_PROTOCOL))
433                 return null
434             }
435 
436             @AssertCalled(count = 1)
437             override fun onSubframeLoadRequest(session: GeckoSession,
438                                                request: LoadRequest):
439                                                GeckoResult<AllowOrDeny>? {
440                 assertThat("URI should not be null", request.uri, notNullValue())
441                 assertThat("URI should match", request.uri, endsWith(iframeUri))
442                 return null
443             }
444         })
445     }
446 
447     @Setting(key = Setting.Key.USE_TRACKING_PROTECTION, value = "true")
448     @Ignore // TODO: Bug 1564373
trackingProtectionnull449     @Test fun trackingProtection() {
450         val category = ContentBlocking.AntiTracking.TEST
451         sessionRule.runtime.settings.contentBlocking.setAntiTracking(category)
452         sessionRule.session.loadTestPath(TRACKERS_PATH)
453 
454         sessionRule.waitUntilCalled(
455                 object : Callbacks.ContentBlockingDelegate {
456             @AssertCalled(count = 3)
457             override fun onContentBlocked(session: GeckoSession,
458                                           event: ContentBlocking.BlockEvent) {
459                 assertThat("Category should be set",
460                            event.antiTrackingCategory,
461                            equalTo(category))
462                 assertThat("URI should not be null", event.uri, notNullValue())
463                 assertThat("URI should match", event.uri, endsWith("tracker.js"))
464             }
465 
466             @AssertCalled(false)
467             override fun onContentLoaded(session: GeckoSession, event: ContentBlocking.BlockEvent) {
468             }
469         })
470 
471         sessionRule.session.settings.useTrackingProtection = false
472 
473         sessionRule.session.reload()
474         sessionRule.session.waitForPageStop()
475 
476         sessionRule.forCallbacksDuringWait(
477                 object : Callbacks.ContentBlockingDelegate {
478             @AssertCalled(false)
479             override fun onContentBlocked(session: GeckoSession,
480                                           event: ContentBlocking.BlockEvent) {
481             }
482 
483             @AssertCalled(count = 3)
484             override fun onContentLoaded(session: GeckoSession, event: ContentBlocking.BlockEvent) {
485                 assertThat("Category should be set",
486                            event.antiTrackingCategory,
487                            equalTo(category))
488                 assertThat("URI should not be null", event.uri, notNullValue())
489                 assertThat("URI should match", event.uri, endsWith("tracker.js"))
490             }
491         })
492     }
493 
redirectLoadnull494     @Test fun redirectLoad() {
495         // TODO: bug 1710943
496         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
497 
498         val redirectUri = if (sessionRule.env.isAutomation) {
499             "http://example.org/tests/junit/hello.html"
500         } else {
501             "http://jigsaw.w3.org/HTTP/300/Overview.html"
502         }
503         val uri = if (sessionRule.env.isAutomation) {
504             "http://example.org/tests/junit/simple_redirect.sjs?$redirectUri"
505         } else {
506             "http://jigsaw.w3.org/HTTP/300/301.html"
507         }
508 
509         sessionRule.session.loadUri(uri)
510         sessionRule.waitForPageStop()
511 
512         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
513             @AssertCalled(count = 2, order = [1, 2])
514             override fun onLoadRequest(session: GeckoSession,
515                                        request: LoadRequest):
516                                        GeckoResult<AllowOrDeny>? {
517                 assertThat("Session should not be null", session, notNullValue())
518                 assertThat("URI should not be null", request.uri, notNullValue())
519                 assertThat("URL should match", request.uri,
520                         equalTo(forEachCall(request.uri, redirectUri)))
521                 assertThat("Trigger URL should be null", request.triggerUri,
522                            nullValue())
523                 assertThat("From app should be correct", request.isDirectNavigation,
524                         equalTo(forEachCall(true, false)))
525                 assertThat("Target should not be null", request.target, notNullValue())
526                 assertThat("Target should match", request.target,
527                         equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
528                 assertThat("Redirect flag is set", request.isRedirect,
529                         equalTo(forEachCall(false, true)))
530                 return null
531             }
532         })
533     }
534 
redirectLoadIframenull535     @Test fun redirectLoadIframe() {
536         // TODO: bug 1710943
537         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
538 
539         val path = if (sessionRule.env.isAutomation) {
540             IFRAME_REDIRECT_AUTOMATION
541         } else {
542             IFRAME_REDIRECT_LOCAL
543         }
544 
545         sessionRule.session.loadTestPath(path)
546         sessionRule.waitForPageStop()
547 
548         // We shouldn't be firing onLoadRequest for iframes, including redirects.
549         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
550             @AssertCalled(count = 1)
551             override fun onLoadRequest(session: GeckoSession,
552                                        request: LoadRequest):
553                     GeckoResult<AllowOrDeny>? {
554                 assertThat("Session should not be null", session, notNullValue())
555                 assertThat("App requested this load", request.isDirectNavigation, equalTo(true))
556                 assertThat("URI should not be null", request.uri, notNullValue())
557                 assertThat("URI should match", request.uri, endsWith(path))
558                 assertThat("isRedirect should match", request.isRedirect, equalTo(false))
559                 return null
560             }
561 
562             @AssertCalled(count = 2)
563             override fun onSubframeLoadRequest(session: GeckoSession,
564                                                request: LoadRequest):
565                     GeckoResult<AllowOrDeny>? {
566                 assertThat("Session should not be null", session, notNullValue())
567                 assertThat("App did not request this load", request.isDirectNavigation, equalTo(false))
568                 assertThat("URI should not be null", request.uri, notNullValue())
569                 assertThat("isRedirect should match", request.isRedirect,
570                         equalTo(forEachCall(false, true)))
571                 return null
572             }
573         })
574     }
575 
redirectDenyLoadnull576     @Test fun redirectDenyLoad() {
577         // TODO: Bug 1673954
578         assumeThat(sessionRule.env.isFission, equalTo(false))
579 
580         // TODO: bug 1710943
581         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
582 
583         val redirectUri = if (sessionRule.env.isAutomation) {
584             "http://example.org/tests/junit/hello.html"
585         } else {
586             "http://jigsaw.w3.org/HTTP/300/Overview.html"
587         }
588         val uri = if (sessionRule.env.isAutomation) {
589             "http://example.org/tests/junit/simple_redirect.sjs?$redirectUri"
590         } else {
591             "http://jigsaw.w3.org/HTTP/300/301.html"
592         }
593 
594         sessionRule.delegateDuringNextWait(
595                 object : Callbacks.NavigationDelegate {
596             @AssertCalled(count = 2, order = [1, 2])
597             override fun onLoadRequest(session: GeckoSession,
598                                        request: LoadRequest):
599                                        GeckoResult<AllowOrDeny>? {
600                 assertThat("Session should not be null", session, notNullValue())
601                 assertThat("URI should not be null", request.uri, notNullValue())
602                 assertThat("URL should match", request.uri,
603                         equalTo(forEachCall(request.uri, redirectUri)))
604                 assertThat("Trigger URL should be null", request.triggerUri,
605                            nullValue())
606                 assertThat("From app should be correct", request.isDirectNavigation,
607                         equalTo(forEachCall(true, false)))
608                 assertThat("Target should not be null", request.target, notNullValue())
609                 assertThat("Target should match", request.target,
610                         equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
611                 assertThat("Redirect flag is set", request.isRedirect,
612                         equalTo(forEachCall(false, true)))
613 
614                 return forEachCall(GeckoResult.allow(), GeckoResult.deny())
615             }
616         })
617 
618         sessionRule.session.loadUri(uri)
619         sessionRule.waitForPageStop()
620 
621         sessionRule.forCallbacksDuringWait(
622                 object : Callbacks.ProgressDelegate {
623             @AssertCalled(count = 1, order = [1])
624             override fun onPageStart(session: GeckoSession, url: String) {
625                 assertThat("URL should match", url, equalTo(uri))
626             }
627         })
628     }
629 
redirectIntentLoadnull630     @Test fun redirectIntentLoad() {
631         assumeThat(sessionRule.env.isAutomation, equalTo(true))
632         // TODO: Bug 1673954
633         assumeThat(sessionRule.env.isFission, equalTo(false))
634 
635         // TODO: bug 1710943
636         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
637 
638         val redirectUri = "intent://test"
639         val uri = "http://example.org/tests/junit/simple_redirect.sjs?$redirectUri"
640 
641         sessionRule.session.loadUri(uri)
642         sessionRule.waitForPageStop()
643 
644         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
645             @AssertCalled(count = 2, order = [1, 2])
646             override fun onLoadRequest(session: GeckoSession,
647                                        request: LoadRequest):
648                                        GeckoResult<AllowOrDeny>? {
649                 assertThat("URL should match", request.uri, equalTo(forEachCall(uri, redirectUri)))
650                 assertThat("From app should be correct", request.isDirectNavigation,
651                         equalTo(forEachCall(true, false)))
652                 return null
653             }
654         })
655     }
656 
657 
bypassClassifiernull658     @Test fun bypassClassifier() {
659         // TODO: bug 1710943
660         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
661 
662         val phishingUri = "https://www.itisatrap.org/firefox/its-a-trap.html"
663         val category = ContentBlocking.SafeBrowsing.PHISHING
664 
665         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(category)
666 
667         sessionRule.session.load(Loader()
668             .uri(phishingUri + "?bypass=true")
669             .flags(GeckoSession.LOAD_FLAGS_BYPASS_CLASSIFIER))
670         sessionRule.session.waitForPageStop()
671 
672         sessionRule.forCallbacksDuringWait(
673                 object : Callbacks.NavigationDelegate {
674             @AssertCalled(false)
675             override fun onLoadError(session: GeckoSession, uri: String?,
676                                      error: WebRequestError): GeckoResult<String>? {
677                 return null
678             }
679         })
680     }
681 
safebrowsingPhishingnull682     @Test fun safebrowsingPhishing() {
683         // TODO: Bug 1673954
684         assumeThat(sessionRule.env.isFission, equalTo(false))
685 
686         // TODO: bug 1710943
687         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
688 
689         val phishingUri = "https://www.itisatrap.org/firefox/its-a-trap.html"
690         val category = ContentBlocking.SafeBrowsing.PHISHING
691 
692         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(category)
693 
694         // Add query string to avoid bypassing classifier check because of cache.
695         testLoadExpectError(phishingUri + "?block=true",
696                         WebRequestError.ERROR_CATEGORY_SAFEBROWSING,
697                         WebRequestError.ERROR_SAFEBROWSING_PHISHING_URI)
698 
699         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(ContentBlocking.SafeBrowsing.NONE)
700 
701         sessionRule.session.loadUri(phishingUri + "?block=false")
702         sessionRule.session.waitForPageStop()
703 
704         sessionRule.forCallbacksDuringWait(
705                 object : Callbacks.NavigationDelegate {
706             @AssertCalled(false)
707             override fun onLoadError(session: GeckoSession, uri: String?,
708                                      error: WebRequestError): GeckoResult<String>? {
709                 return null
710             }
711         })
712     }
713 
safebrowsingMalwarenull714     @Test fun safebrowsingMalware() {
715         // TODO: Bug 1673954
716         assumeThat(sessionRule.env.isFission, equalTo(false))
717 
718         // TODO: bug 1710943
719         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
720 
721         val malwareUri = "https://www.itisatrap.org/firefox/its-an-attack.html"
722         val category = ContentBlocking.SafeBrowsing.MALWARE
723 
724         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(category)
725 
726         testLoadExpectError(malwareUri + "?block=true",
727                         WebRequestError.ERROR_CATEGORY_SAFEBROWSING,
728                         WebRequestError.ERROR_SAFEBROWSING_MALWARE_URI)
729 
730         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(ContentBlocking.SafeBrowsing.NONE)
731 
732         sessionRule.session.loadUri(malwareUri + "?block=false")
733         sessionRule.session.waitForPageStop()
734 
735         sessionRule.forCallbacksDuringWait(
736                 object : Callbacks.NavigationDelegate {
737             @AssertCalled(false)
738             override fun onLoadError(session: GeckoSession, uri: String?,
739                                      error: WebRequestError): GeckoResult<String>? {
740                 return null
741             }
742         })
743     }
744 
safebrowsingUnwantednull745     @Test fun safebrowsingUnwanted() {
746         // TODO: Bug 1673954
747         assumeThat(sessionRule.env.isFission, equalTo(false))
748         val unwantedUri = "https://www.itisatrap.org/firefox/unwanted.html"
749         val category = ContentBlocking.SafeBrowsing.UNWANTED
750 
751         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(category)
752 
753         testLoadExpectError(unwantedUri + "?block=true",
754                         WebRequestError.ERROR_CATEGORY_SAFEBROWSING,
755                         WebRequestError.ERROR_SAFEBROWSING_UNWANTED_URI)
756 
757         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(ContentBlocking.SafeBrowsing.NONE)
758 
759         sessionRule.session.loadUri(unwantedUri + "?block=false")
760         sessionRule.session.waitForPageStop()
761 
762         sessionRule.forCallbacksDuringWait(
763                 object : Callbacks.NavigationDelegate {
764             @AssertCalled(false)
765             override fun onLoadError(session: GeckoSession, uri: String?,
766                                      error: WebRequestError): GeckoResult<String>? {
767                 return null
768             }
769         })
770     }
771 
safebrowsingHarmfulnull772     @Test fun safebrowsingHarmful() {
773         // TODO: Bug 1673954
774         assumeThat(sessionRule.env.isFission, equalTo(false))
775 
776         // TODO: bug 1710943
777         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
778 
779         val harmfulUri = "https://www.itisatrap.org/firefox/harmful.html"
780         val category = ContentBlocking.SafeBrowsing.HARMFUL
781 
782         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(category)
783 
784         testLoadExpectError(harmfulUri + "?block=true",
785                         WebRequestError.ERROR_CATEGORY_SAFEBROWSING,
786                         WebRequestError.ERROR_SAFEBROWSING_HARMFUL_URI)
787 
788         sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(ContentBlocking.SafeBrowsing.NONE)
789 
790         sessionRule.session.loadUri(harmfulUri + "?block=false")
791         sessionRule.session.waitForPageStop()
792 
793         sessionRule.forCallbacksDuringWait(
794                 object : Callbacks.NavigationDelegate {
795             @AssertCalled(false)
796             override fun onLoadError(session: GeckoSession, uri: String?,
797                                      error: WebRequestError): GeckoResult<String>? {
798                 return null
799             }
800         })
801     }
802 
803     // Checks that the User Agent matches the user agent built in
804     // nsHttpHandler::BuildUserAgent
defaultUserAgentMatchesActualUserAgentnull805     @Test fun defaultUserAgentMatchesActualUserAgent() {
806         var userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
807         assertThat("Mobile user agent should match the default user agent",
808                 userAgent, equalTo(GeckoSession.getDefaultUserAgent()))
809     }
810 
desktopModenull811     @Test fun desktopMode() {
812         sessionRule.session.loadUri("https://example.com")
813         sessionRule.waitForPageStop()
814 
815         val mobileSubStr = "Mobile"
816         val desktopSubStr = "X11"
817 
818         assertThat("User agent should be set to mobile",
819                    getUserAgent(),
820                    containsString(mobileSubStr))
821 
822         var userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
823         assertThat("User agent should be reported as mobile",
824                     userAgent, containsString(mobileSubStr))
825 
826         sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_DESKTOP
827 
828         sessionRule.session.reload()
829         sessionRule.session.waitForPageStop()
830 
831         assertThat("User agent should be set to desktop",
832                    getUserAgent(),
833                    containsString(desktopSubStr))
834 
835         userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
836         assertThat("User agent should be reported as desktop",
837                     userAgent, containsString(desktopSubStr))
838 
839         sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_MOBILE
840 
841         sessionRule.session.reload()
842         sessionRule.session.waitForPageStop()
843 
844         assertThat("User agent should be set to mobile",
845                    getUserAgent(),
846                    containsString(mobileSubStr))
847 
848         userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
849         assertThat("User agent should be reported as mobile",
850                     userAgent, containsString(mobileSubStr))
851 
852         val vrSubStr = "Mobile VR"
853         sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_VR
854 
855         sessionRule.session.reload()
856         sessionRule.session.waitForPageStop()
857 
858         assertThat("User agent should be set to VR",
859                 getUserAgent(),
860                 containsString(vrSubStr))
861 
862         userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
863         assertThat("User agent should be reported as VR",
864                 userAgent, containsString(vrSubStr))
865 
866     }
867 
getUserAgentnull868     private fun getUserAgent(session: GeckoSession = sessionRule.session): String {
869         return session.evaluateJS("window.navigator.userAgent") as String
870     }
871 
uaOverrideNewSessionnull872     @Test fun uaOverrideNewSession() {
873         // TODO: bug 1710943
874         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
875 
876         val newSession = sessionRule.createClosedSession()
877         newSession.settings.userAgentOverride = "Test user agent override"
878 
879         newSession.open()
880         newSession.loadUri("https://example.com")
881         newSession.waitForPageStop()
882 
883         assertThat("User agent should match override", getUserAgent(newSession),
884                 equalTo("Test user agent override"))
885     }
886 
uaOverridenull887     @Test fun uaOverride() {
888         // TODO: bug 1710943
889         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
890 
891         sessionRule.session.loadUri("https://example.com")
892         sessionRule.waitForPageStop()
893 
894         val mobileSubStr = "Mobile"
895         val vrSubStr = "Mobile VR"
896         val overrideUserAgent = "This is the override user agent"
897 
898         assertThat("User agent should be reported as mobile",
899                 getUserAgent(), containsString(mobileSubStr))
900 
901         sessionRule.session.settings.userAgentOverride = overrideUserAgent
902 
903         sessionRule.session.reload()
904         sessionRule.session.waitForPageStop()
905 
906         assertThat("User agent should be reported as override",
907                 getUserAgent(), equalTo(overrideUserAgent))
908 
909         sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_VR
910 
911         sessionRule.session.reload()
912         sessionRule.session.waitForPageStop()
913 
914         assertThat("User agent should still be reported as override even when USER_AGENT_MODE is set",
915                 getUserAgent(), equalTo(overrideUserAgent))
916 
917         sessionRule.session.settings.userAgentOverride = null
918 
919         sessionRule.session.reload()
920         sessionRule.session.waitForPageStop()
921 
922         assertThat("User agent should now be reported as VR",
923                 getUserAgent(), containsString(vrSubStr))
924 
925         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
926             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
927                 sessionRule.session.settings.userAgentOverride = overrideUserAgent
928                 return null
929             }
930         })
931 
932         sessionRule.session.reload()
933         sessionRule.session.waitForPageStop()
934 
935         assertThat("User agent should be reported as override after being set in onLoadRequest",
936                 getUserAgent(), equalTo(overrideUserAgent))
937 
938         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
939             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
940                 sessionRule.session.settings.userAgentOverride = null
941                 return null
942             }
943         })
944 
945         sessionRule.session.reload()
946         sessionRule.session.waitForPageStop()
947 
948         assertThat("User agent should again be reported as VR after disabling override in onLoadRequest",
949                 getUserAgent(), containsString(vrSubStr))
950     }
951 
952     @WithDisplay(width = 600, height = 200)
viewportModenull953     @Test fun viewportMode() {
954         // TODO: bug 1710943
955         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
956 
957         sessionRule.session.loadTestPath(VIEWPORT_PATH)
958         sessionRule.waitForPageStop()
959 
960         val desktopInnerWidth = 980.0
961         val physicalWidth = 600.0
962         val pixelRatio = sessionRule.session.evaluateJS("window.devicePixelRatio") as Double
963         val mobileInnerWidth = physicalWidth / pixelRatio
964         val innerWidthJs = "window.innerWidth"
965 
966         var innerWidth = sessionRule.session.evaluateJS(innerWidthJs) as Double
967         assertThat("innerWidth should be equal to $mobileInnerWidth",
968                 innerWidth, closeTo(mobileInnerWidth, 0.1))
969 
970         sessionRule.session.settings.viewportMode = GeckoSessionSettings.VIEWPORT_MODE_DESKTOP
971 
972         sessionRule.session.reload()
973         sessionRule.session.waitForPageStop()
974 
975         innerWidth = sessionRule.session.evaluateJS(innerWidthJs) as Double
976         assertThat("innerWidth should be equal to $desktopInnerWidth", innerWidth,
977                 closeTo(desktopInnerWidth, 0.1))
978 
979         sessionRule.session.settings.viewportMode = GeckoSessionSettings.VIEWPORT_MODE_MOBILE
980 
981         sessionRule.session.reload()
982         sessionRule.session.waitForPageStop()
983 
984         innerWidth = sessionRule.session.evaluateJS(innerWidthJs) as Double
985         assertThat("innerWidth should be equal to $mobileInnerWidth again",
986                 innerWidth, closeTo(mobileInnerWidth, 0.1))
987     }
988 
loadnull989     @Test fun load() {
990         // TODO: bug 1710943
991         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
992 
993         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO_HTML_PATH")
994         sessionRule.waitForPageStop()
995 
996         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
997             @AssertCalled(count = 1, order = [1])
998             override fun onLoadRequest(session: GeckoSession,
999                                        request: LoadRequest):
1000                                        GeckoResult<AllowOrDeny>? {
1001                 assertThat("Session should not be null", session, notNullValue())
1002                 assertThat("URI should not be null", request.uri, notNullValue())
1003                 assertThat("URI should match", request.uri, endsWith(HELLO_HTML_PATH))
1004                 assertThat("Trigger URL should be null", request.triggerUri,
1005                            nullValue())
1006                 assertThat("App requested this load", request.isDirectNavigation,
1007                         equalTo(true))
1008                 assertThat("Target should not be null", request.target, notNullValue())
1009                 assertThat("Target should match", request.target,
1010                            equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
1011                 assertThat("Redirect flag is not set", request.isRedirect, equalTo(false))
1012                 assertThat("Should not have a user gesture", request.hasUserGesture, equalTo(false))
1013                 return null
1014             }
1015 
1016             @AssertCalled(count = 1, order = [2])
1017             override fun onLocationChange(session: GeckoSession, url: String?) {
1018                 assertThat("Session should not be null", session, notNullValue())
1019                 assertThat("URL should not be null", url, notNullValue())
1020                 assertThat("URL should match", url, endsWith(HELLO_HTML_PATH))
1021             }
1022 
1023             @AssertCalled(count = 1, order = [2])
1024             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
1025                 assertThat("Session should not be null", session, notNullValue())
1026                 assertThat("Cannot go back", canGoBack, equalTo(false))
1027             }
1028 
1029             @AssertCalled(count = 1, order = [2])
1030             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
1031                 assertThat("Session should not be null", session, notNullValue())
1032                 assertThat("Cannot go forward", canGoForward, equalTo(false))
1033             }
1034 
1035             @AssertCalled(false)
1036             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1037                 return null
1038             }
1039         })
1040     }
1041 
load_dataUrinull1042     @Test fun load_dataUri() {
1043         // TODO: bug 1710943
1044         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1045 
1046         val dataUrl = "data:,Hello%2C%20World!"
1047         sessionRule.session.loadUri(dataUrl)
1048         sessionRule.waitForPageStop()
1049 
1050         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
1051             @AssertCalled(count = 1)
1052             override fun onLocationChange(session: GeckoSession, url: String?) {
1053                 assertThat("URL should match the provided data URL", url, equalTo(dataUrl))
1054             }
1055 
1056             @AssertCalled(count = 1)
1057             override fun onPageStop(session: GeckoSession, success: Boolean) {
1058                 assertThat("Page should load successfully", success, equalTo(true))
1059             }
1060         })
1061     }
1062 
1063     @NullDelegate(GeckoSession.NavigationDelegate::class)
load_withoutNavigationDelegatenull1064     @Test fun load_withoutNavigationDelegate() {
1065         // TODO: bug 1710943
1066         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1067 
1068         // Test that when navigation delegate is disabled, we can still perform loads.
1069         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1070         sessionRule.session.waitForPageStop()
1071 
1072         sessionRule.session.reload()
1073         sessionRule.session.waitForPageStop()
1074     }
1075 
1076     @NullDelegate(GeckoSession.NavigationDelegate::class)
load_canUnsetNavigationDelegatenull1077     @Test fun load_canUnsetNavigationDelegate() {
1078         // TODO: bug 1710943
1079         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1080 
1081         // Test that if we unset the navigation delegate during a load, the load still proceeds.
1082         var onLocationCount = 0
1083         sessionRule.session.navigationDelegate = object : Callbacks.NavigationDelegate {
1084             override fun onLocationChange(session: GeckoSession, url: String?) {
1085                 onLocationCount++
1086             }
1087         }
1088         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1089         sessionRule.session.waitForPageStop()
1090 
1091         assertThat("Should get callback for first load",
1092                    onLocationCount, equalTo(1))
1093 
1094         sessionRule.session.reload()
1095         sessionRule.session.navigationDelegate = null
1096         sessionRule.session.waitForPageStop()
1097 
1098         assertThat("Should not get callback for second load",
1099                    onLocationCount, equalTo(1))
1100     }
1101 
loadStringnull1102     @Test fun loadString() {
1103         // TODO: bug 1710943
1104         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1105 
1106         val dataString = "<html><head><title>TheTitle</title></head><body>TheBody</body></html>"
1107         val mimeType = "text/html"
1108         sessionRule.session.load(Loader().data(dataString, mimeType))
1109         sessionRule.waitForPageStop()
1110 
1111         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate, Callbacks.ContentDelegate {
1112             @AssertCalled
1113             override fun onTitleChange(session: GeckoSession, title: String?) {
1114                 assertThat("Title should match", title, equalTo("TheTitle"))
1115             }
1116 
1117             @AssertCalled(count = 1)
1118             override fun onLocationChange(session: GeckoSession, url: String?) {
1119                 assertThat("URL should be a data URL", url,
1120                            equalTo(createDataUri(dataString, mimeType)))
1121             }
1122 
1123             @AssertCalled(count = 1)
1124             override fun onPageStop(session: GeckoSession, success: Boolean) {
1125                 assertThat("Page should load successfully", success, equalTo(true))
1126             }
1127         })
1128     }
1129 
loadString_noMimeTypenull1130     @Test fun loadString_noMimeType() {
1131         sessionRule.session.load(Loader().data("Hello, World!", null))
1132         sessionRule.waitForPageStop()
1133 
1134         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
1135             @AssertCalled(count = 1)
1136             override fun onLocationChange(session: GeckoSession, url: String?) {
1137                 assertThat("URL should be a data URL", url, startsWith("data:"))
1138             }
1139 
1140             @AssertCalled(count = 1)
1141             override fun onPageStop(session: GeckoSession, success: Boolean) {
1142                 assertThat("Page should load successfully", success, equalTo(true))
1143             }
1144         })
1145     }
1146 
loadData_htmlnull1147     @Test fun loadData_html() {
1148         // TODO: bug 1710943
1149         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1150 
1151         val bytes = getTestBytes(HELLO_HTML_PATH)
1152         assertThat("test html should have data", bytes.size, greaterThan(0))
1153 
1154         sessionRule.session.load(Loader().data(bytes, "text/html"))
1155         sessionRule.waitForPageStop()
1156 
1157         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate, Callbacks.ContentDelegate {
1158             @AssertCalled(count = 1)
1159             override fun onTitleChange(session: GeckoSession, title: String?) {
1160                 assertThat("Title should match", title, equalTo("Hello, world!"))
1161             }
1162 
1163             @AssertCalled(count = 1)
1164             override fun onLocationChange(session: GeckoSession, url: String?) {
1165                 assertThat("URL should match", url, equalTo(createDataUri(bytes, "text/html")))
1166             }
1167 
1168             @AssertCalled(count = 1)
1169             override fun onPageStop(session: GeckoSession, success: Boolean) {
1170                 assertThat("Page should load successfully", success, equalTo(true))
1171             }
1172         })
1173     }
1174 
createDataUrinull1175     private fun createDataUri(data: String,
1176                               mimeType: String?): String {
1177         return String.format("data:%s,%s", mimeType ?: "", data)
1178     }
1179 
createDataUrinull1180     private fun createDataUri(bytes: ByteArray,
1181                               mimeType: String?): String {
1182         return String.format("data:%s;base64,%s", mimeType ?: "",
1183                 Base64.encodeToString(bytes, Base64.NO_WRAP))
1184     }
1185 
loadDataHelpernull1186     fun loadDataHelper(assetPath: String, mimeType: String? = null) {
1187         val bytes = getTestBytes(assetPath)
1188         assertThat("test data should have bytes", bytes.size, greaterThan(0))
1189 
1190         sessionRule.session.load(Loader().data(bytes, mimeType))
1191         sessionRule.waitForPageStop()
1192 
1193         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
1194             @AssertCalled(count = 1)
1195             override fun onLocationChange(session: GeckoSession, url: String?) {
1196                 assertThat("URL should match", url, equalTo(createDataUri(bytes, mimeType)))
1197             }
1198 
1199             @AssertCalled(count = 1)
1200             override fun onPageStop(session: GeckoSession, success: Boolean) {
1201                 assertThat("Page should load successfully", success, equalTo(true))
1202             }
1203         })
1204     }
1205 
1206 
loadDatanull1207     @Test fun loadData() {
1208         loadDataHelper("/assets/www/images/test.gif", "image/gif")
1209     }
1210 
loadData_noMimeTypenull1211     @Test fun loadData_noMimeType() {
1212         loadDataHelper("/assets/www/images/test.gif")
1213     }
1214 
reloadnull1215     @Test fun reload() {
1216         // TODO: bug 1710943
1217         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1218 
1219         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO_HTML_PATH")
1220         sessionRule.waitForPageStop()
1221 
1222         sessionRule.session.reload()
1223         sessionRule.waitForPageStop()
1224 
1225         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
1226             @AssertCalled(count = 1, order = [1])
1227             override fun onLoadRequest(session: GeckoSession,
1228                                        request: LoadRequest):
1229                                        GeckoResult<AllowOrDeny>? {
1230                 assertThat("URI should match", request.uri, endsWith(HELLO_HTML_PATH))
1231                 assertThat("Trigger URL should be null", request.triggerUri,
1232                            nullValue())
1233                 assertThat("Target should match", request.target,
1234                            equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
1235                 assertThat("Load should not be direct", request.isDirectNavigation,
1236                         equalTo(false))
1237                 return null
1238             }
1239 
1240             @AssertCalled(count = 1, order = [2])
1241             override fun onLocationChange(session: GeckoSession, url: String?) {
1242                 assertThat("URL should match", url, endsWith(HELLO_HTML_PATH))
1243             }
1244 
1245             @AssertCalled(count = 1, order = [2])
1246             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
1247                 assertThat("Cannot go back", canGoBack, equalTo(false))
1248             }
1249 
1250             @AssertCalled(count = 1, order = [2])
1251             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
1252                 assertThat("Cannot go forward", canGoForward, equalTo(false))
1253             }
1254 
1255             @AssertCalled(false)
1256             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1257                 return null
1258             }
1259         })
1260     }
1261 
goBackAndForwardnull1262     @Test fun goBackAndForward() {
1263         // TODO: bug 1710943
1264         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1265 
1266         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO_HTML_PATH")
1267         sessionRule.waitForPageStop()
1268 
1269         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO2_HTML_PATH")
1270         sessionRule.waitForPageStop()
1271 
1272         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
1273             @AssertCalled(count = 1)
1274             override fun onLocationChange(session: GeckoSession, url: String?, perms : MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
1275                 assertThat("URL should match", url, endsWith(HELLO2_HTML_PATH))
1276             }
1277         })
1278 
1279         sessionRule.session.goBack()
1280         sessionRule.waitForPageStop()
1281 
1282         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
1283             @AssertCalled(count = 0, order = [1])
1284             override fun onLoadRequest(session: GeckoSession,
1285                                        request: LoadRequest):
1286                                        GeckoResult<AllowOrDeny>? {
1287                 assertThat("Load should not be direct", request.isDirectNavigation,
1288                         equalTo(false))
1289                 return null
1290             }
1291 
1292             @AssertCalled(count = 1, order = [2])
1293             override fun onLocationChange(session: GeckoSession, url: String?, perms : MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
1294                 assertThat("URL should match", url, endsWith(HELLO_HTML_PATH))
1295             }
1296 
1297             @AssertCalled(count = 1, order = [2])
1298             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
1299                 assertThat("Cannot go back", canGoBack, equalTo(false))
1300             }
1301 
1302             @AssertCalled(count = 1, order = [2])
1303             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
1304                 assertThat("Can go forward", canGoForward, equalTo(true))
1305             }
1306 
1307             @AssertCalled(false)
1308             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1309                 return null
1310             }
1311         })
1312 
1313         sessionRule.session.goForward()
1314         sessionRule.waitForPageStop()
1315 
1316         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
1317             @AssertCalled(count = 0, order = [1])
1318             override fun onLoadRequest(session: GeckoSession,
1319                                        request: LoadRequest):
1320                                        GeckoResult<AllowOrDeny>? {
1321                 assertThat("Load should not be direct", request.isDirectNavigation,
1322                         equalTo(false))
1323                 return null
1324             }
1325 
1326             @AssertCalled(count = 1, order = [2])
1327             override fun onLocationChange(session: GeckoSession, url: String?, perms : MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
1328                 assertThat("URL should match", url, endsWith(HELLO2_HTML_PATH))
1329             }
1330 
1331             @AssertCalled(count = 1, order = [2])
1332             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
1333                 assertThat("Can go back", canGoBack, equalTo(true))
1334             }
1335 
1336             @AssertCalled(count = 1, order = [2])
1337             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
1338                 assertThat("Cannot go forward", canGoForward, equalTo(false))
1339             }
1340 
1341             @AssertCalled(false)
1342             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1343                 return null
1344             }
1345         })
1346     }
1347 
onLoadUri_returnTrueCancelsLoadnull1348     @Test fun onLoadUri_returnTrueCancelsLoad() {
1349         // TODO: bug 1710943
1350         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1351 
1352         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
1353             @AssertCalled(count = 2)
1354             override fun onLoadRequest(session: GeckoSession,
1355                                        request: LoadRequest):
1356                                        GeckoResult<AllowOrDeny>? {
1357                 if (request.uri.endsWith(HELLO_HTML_PATH)) {
1358                     return GeckoResult.deny()
1359                 } else {
1360                     return GeckoResult.allow()
1361                 }
1362             }
1363         })
1364 
1365         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1366         sessionRule.session.loadTestPath(HELLO2_HTML_PATH)
1367         sessionRule.waitForPageStop()
1368 
1369         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1370             @AssertCalled(count = 1, order = [1])
1371             override fun onPageStart(session: GeckoSession, url: String) {
1372                 assertThat("URL should match", url, endsWith(HELLO2_HTML_PATH))
1373             }
1374 
1375             @AssertCalled(count = 1, order = [2])
1376             override fun onPageStop(session: GeckoSession, success: Boolean) {
1377                 assertThat("Load should succeed", success, equalTo(true))
1378             }
1379         })
1380     }
1381 
onNewSession_calledForWindowOpennull1382     @Test fun onNewSession_calledForWindowOpen() {
1383         // TODO: bug 1710943
1384         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1385 
1386         // Disable popup blocker.
1387         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1388 
1389         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1390         sessionRule.session.waitForPageStop()
1391 
1392         sessionRule.session.evaluateJS("window.open('newSession_child.html', '_blank')")
1393 
1394         sessionRule.session.waitUntilCalled(object : Callbacks.NavigationDelegate {
1395             @AssertCalled(count = 1, order = [1])
1396             override fun onLoadRequest(session: GeckoSession,
1397                                        request: LoadRequest):
1398                                        GeckoResult<AllowOrDeny>? {
1399                 assertThat("URI should be correct", request.uri, endsWith(NEW_SESSION_CHILD_HTML_PATH))
1400                 assertThat("Trigger URL should match", request.triggerUri,
1401                            endsWith(NEW_SESSION_HTML_PATH))
1402                 assertThat("Target should be correct", request.target,
1403                            equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW))
1404                 assertThat("Load should not be direct", request.isDirectNavigation,
1405                         equalTo(false))
1406                 return null
1407             }
1408 
1409             @AssertCalled(count = 1, order = [2])
1410             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1411                 assertThat("URI should be correct", uri, endsWith(NEW_SESSION_CHILD_HTML_PATH))
1412                 return null
1413             }
1414         })
1415     }
1416 
1417     @Test(expected = GeckoSessionTestRule.RejectedPromiseException::class)
onNewSession_rejectLocalnull1418     fun onNewSession_rejectLocal() {
1419         // TODO: bug 1710943
1420         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1421 
1422         // Disable popup blocker.
1423         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1424 
1425         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1426         sessionRule.session.waitForPageStop()
1427 
1428         sessionRule.session.evaluateJS("window.open('file:///data/local/tmp', '_blank')")
1429     }
1430 
onNewSession_calledForTargetBlankLinknull1431     @Test fun onNewSession_calledForTargetBlankLink() {
1432         // Disable popup blocker.
1433         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1434 
1435         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1436         sessionRule.session.waitForPageStop()
1437 
1438         sessionRule.session.evaluateJS("document.querySelector('#targetBlankLink').click()")
1439 
1440         sessionRule.session.waitUntilCalled(object : Callbacks.NavigationDelegate {
1441             // We get two onLoadRequest calls for the link click,
1442             // one when loading the URL and one when opening a new window.
1443             @AssertCalled(count = 1, order = [1])
1444             override fun onLoadRequest(session: GeckoSession,
1445                                        request: LoadRequest):
1446                                        GeckoResult<AllowOrDeny>? {
1447                 assertThat("URI should be correct", request.uri, endsWith(NEW_SESSION_CHILD_HTML_PATH))
1448                 assertThat("Trigger URL should be null", request.triggerUri,
1449                            endsWith(NEW_SESSION_HTML_PATH))
1450                 assertThat("Target should be correct", request.target,
1451                            equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW))
1452                 return null
1453             }
1454 
1455             @AssertCalled(count = 1, order = [2])
1456             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1457                 assertThat("URI should be correct", uri, endsWith(NEW_SESSION_CHILD_HTML_PATH))
1458                 return null
1459             }
1460         })
1461     }
1462 
delegateNewSessionnull1463     private fun delegateNewSession(settings: GeckoSessionSettings = mainSession.settings): GeckoSession {
1464         val newSession = sessionRule.createClosedSession(settings)
1465 
1466         sessionRule.session.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
1467             @AssertCalled(count = 1)
1468             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession> {
1469                 return GeckoResult.fromValue(newSession)
1470             }
1471         })
1472 
1473         return newSession
1474     }
1475 
onNewSession_childShouldLoadnull1476     @Test fun onNewSession_childShouldLoad() {
1477         // TODO: bug 1710943
1478         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1479 
1480         // Disable popup blocker.
1481         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1482 
1483         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1484         sessionRule.session.waitForPageStop()
1485 
1486         val newSession = delegateNewSession()
1487         sessionRule.session.evaluateJS("document.querySelector('#targetBlankLink').click()")
1488         // Initial about:blank
1489         newSession.waitForPageStop()
1490         // NEW_SESSION_CHILD_HTML_PATH
1491         newSession.waitForPageStop()
1492 
1493         newSession.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1494             @AssertCalled(count = 1)
1495             override fun onPageStart(session: GeckoSession, url: String) {
1496                 assertThat("URL should match", url, endsWith(NEW_SESSION_CHILD_HTML_PATH))
1497             }
1498 
1499             @AssertCalled(count = 1)
1500             override fun onPageStop(session: GeckoSession, success: Boolean) {
1501                 assertThat("Load should succeed", success, equalTo(true))
1502             }
1503         })
1504     }
1505 
onNewSession_setWindowOpenernull1506     @Test fun onNewSession_setWindowOpener() {
1507         // TODO: bug 1710943
1508         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1509 
1510         // Disable popup blocker.
1511         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1512 
1513         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1514         sessionRule.session.waitForPageStop()
1515 
1516         val newSession = delegateNewSession()
1517         sessionRule.session.evaluateJS("document.querySelector('#targetBlankLink').click()")
1518         newSession.waitForPageStop()
1519 
1520         assertThat("window.opener should be set",
1521                    newSession.evaluateJS("window.opener.location.pathname") as String,
1522                    equalTo(NEW_SESSION_HTML_PATH))
1523     }
1524 
onNewSession_supportNoOpenernull1525     @Test fun onNewSession_supportNoOpener() {
1526         // TODO: bug 1710943
1527         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1528 
1529         // Disable popup blocker.
1530         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1531 
1532         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1533         sessionRule.session.waitForPageStop()
1534 
1535         val newSession = delegateNewSession()
1536         sessionRule.session.evaluateJS("document.querySelector('#noOpenerLink').click()")
1537         newSession.waitForPageStop()
1538 
1539         assertThat("window.opener should not be set",
1540                    newSession.evaluateJS("window.opener"),
1541                    equalTo(JSONObject.NULL))
1542     }
1543 
onNewSession_notCalledForHandledLoadsnull1544     @Test fun onNewSession_notCalledForHandledLoads() {
1545         // TODO: bug 1710943
1546         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1547 
1548         // Disable popup blocker.
1549         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1550 
1551         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1552         sessionRule.session.waitForPageStop()
1553 
1554         sessionRule.session.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
1555             override fun onLoadRequest(session: GeckoSession,
1556                                        request: LoadRequest):
1557                                        GeckoResult<AllowOrDeny>? {
1558                 // Pretend we handled the target="_blank" link click.
1559                 if (request.uri.endsWith(NEW_SESSION_CHILD_HTML_PATH)) {
1560                     return GeckoResult.deny()
1561                 } else {
1562                     return GeckoResult.allow()
1563                 }
1564             }
1565         })
1566 
1567         sessionRule.session.evaluateJS("document.querySelector('#targetBlankLink').click()")
1568 
1569         sessionRule.session.reload()
1570         sessionRule.session.waitForPageStop()
1571 
1572         // Assert that onNewSession was not called for the link click.
1573         sessionRule.session.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
1574             @AssertCalled(count = 2)
1575             override fun onLoadRequest(session: GeckoSession,
1576                                        request: LoadRequest):
1577                                        GeckoResult<AllowOrDeny>? {
1578                 assertThat("URI must match", request.uri,
1579                            endsWith(forEachCall(NEW_SESSION_CHILD_HTML_PATH, NEW_SESSION_HTML_PATH)))
1580                 assertThat("Load should not be direct", request.isDirectNavigation,
1581                         equalTo(false))
1582                 return null
1583             }
1584 
1585             @AssertCalled(count = 0)
1586             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
1587                 return null
1588             }
1589         })
1590     }
1591 
onNewSession_submitFormWithTargetBlanknull1592     @Test fun onNewSession_submitFormWithTargetBlank() {
1593         // TODO: bug 1710943
1594         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1595 
1596         sessionRule.session.loadTestPath(FORM_BLANK_HTML_PATH)
1597         sessionRule.waitForPageStop()
1598 
1599         sessionRule.session.evaluateJS("""
1600             document.querySelector('input[type=text]').focus()
1601         """)
1602         sessionRule.session.waitUntilCalled(GeckoSession.TextInputDelegate::class,
1603                                             "restartInput")
1604 
1605         val time = SystemClock.uptimeMillis()
1606         val keyEvent = KeyEvent(time, time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0)
1607         sessionRule.session.textInput.onKeyDown(KeyEvent.KEYCODE_ENTER, keyEvent)
1608         sessionRule.session.textInput.onKeyUp(KeyEvent.KEYCODE_ENTER,
1609                                               KeyEvent.changeAction(keyEvent,
1610                                                                     KeyEvent.ACTION_UP))
1611 
1612         sessionRule.session.waitUntilCalled(object : Callbacks.NavigationDelegate {
1613             @AssertCalled(count = 1, order = [1])
1614             override fun onLoadRequest(session: GeckoSession, request: LoadRequest):
1615                                        GeckoResult<AllowOrDeny>? {
1616                 assertThat("URL should be correct", request.uri,
1617                            endsWith("form_blank.html?"))
1618                 assertThat("Trigger URL should match", request.triggerUri,
1619                            endsWith("form_blank.html"))
1620                 assertThat("Target should be correct", request.target,
1621                            equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW))
1622                 return null
1623             }
1624 
1625             @AssertCalled(count = 1, order = [2])
1626             override fun onNewSession(session: GeckoSession, uri: String):
1627                                       GeckoResult<GeckoSession>? {
1628                 assertThat("URL should be correct", uri, endsWith("form_blank.html?"))
1629                 return null
1630             }
1631         })
1632     }
1633 
loadUriReferrernull1634     @Test fun loadUriReferrer() {
1635         // TODO: bug 1710943
1636         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1637 
1638         val uri = "https://example.com"
1639         val referrer = "https://foo.org/"
1640 
1641         sessionRule.session.load(Loader()
1642             .uri(uri)
1643             .referrer(referrer)
1644             .flags(GeckoSession.LOAD_FLAGS_NONE))
1645         sessionRule.session.waitForPageStop()
1646 
1647         assertThat("Referrer should match",
1648                    sessionRule.session.evaluateJS("document.referrer") as String,
1649                    equalTo(referrer))
1650     }
1651 
loadUriReferrerSessionnull1652     @Test fun loadUriReferrerSession() {
1653         // TODO: bug 1710943
1654         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1655 
1656         val uri = "https://example.com/bar"
1657         val referrer = "https://example.org/"
1658 
1659         sessionRule.session.loadUri(referrer)
1660         sessionRule.session.waitForPageStop()
1661 
1662         val newSession = sessionRule.createOpenSession()
1663         newSession.load(Loader()
1664             .uri(uri)
1665             .referrer(sessionRule.session)
1666             .flags(GeckoSession.LOAD_FLAGS_NONE))
1667         newSession.waitForPageStop()
1668 
1669         assertThat("Referrer should match",
1670                 newSession.evaluateJS("document.referrer") as String,
1671                 equalTo(referrer))
1672     }
1673 
loadUriReferrerSessionFileUrlnull1674     @Test fun loadUriReferrerSessionFileUrl() {
1675         // TODO: bug 1710943
1676         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1677 
1678         val uri = "file:///system/etc/fonts.xml"
1679         val referrer = "https://example.org"
1680 
1681         sessionRule.session.loadUri(referrer)
1682         sessionRule.session.waitForPageStop()
1683 
1684         val newSession = sessionRule.createOpenSession()
1685         newSession.load(Loader()
1686             .uri(uri)
1687             .referrer(sessionRule.session)
1688             .flags(GeckoSession.LOAD_FLAGS_NONE))
1689         newSession.waitUntilCalled(object : Callbacks.NavigationDelegate {
1690             @AssertCalled
1691             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
1692                 return null
1693             }
1694         })
1695     }
1696 
loadUriHeaderTestnull1697     private fun loadUriHeaderTest(headers: Map<String?,String?>,
1698                                   additional: Map<String?, String?>,
1699                                   filter: Int = GeckoSession.HEADER_FILTER_CORS_SAFELISTED) {
1700         // First collect default headers with no override
1701         sessionRule.session.loadUri("$TEST_ENDPOINT/anything")
1702         sessionRule.session.waitForPageStop()
1703 
1704         val defaultContent = sessionRule.session.evaluateJS("document.body.children[0].innerHTML") as String
1705         val defaultBody = JSONObject(defaultContent)
1706         val defaultHeaders = defaultBody.getJSONObject("headers").asMap<String>()
1707 
1708         val expected = HashMap(additional)
1709         for (key in defaultHeaders.keys) {
1710             expected[key] = defaultHeaders[key]
1711             if (additional.containsKey(key)) {
1712                 // TODO: Bug 1671294, headers should be replaced, not appended
1713                 expected[key] += ", " + additional[key]
1714             }
1715         }
1716 
1717         // Now load the page with the header override
1718         sessionRule.session.load(Loader()
1719             .uri("$TEST_ENDPOINT/anything")
1720             .additionalHeaders(headers)
1721             .headerFilter(filter))
1722         sessionRule.session.waitForPageStop()
1723 
1724         val content = sessionRule.session.evaluateJS("document.body.children[0].innerHTML") as String
1725         val body = JSONObject(content)
1726         val actualHeaders = body.getJSONObject("headers").asMap<String>()
1727 
1728         assertThat("Headers should match", expected as Map<String?, String?>,
1729                 equalTo(actualHeaders))
1730     }
1731 
testLoaderEqualsnull1732     private fun testLoaderEquals(a: Loader, b: Loader, shouldBeEqual: Boolean) {
1733         assertThat("Equal test", a == b, equalTo(shouldBeEqual))
1734         assertThat("HashCode test", a.hashCode() == b.hashCode(),
1735                 equalTo(shouldBeEqual))
1736     }
1737 
loaderEqualsnull1738     @Test fun loaderEquals() {
1739         testLoaderEquals(
1740                 Loader().uri("http://test-uri-equals.com"),
1741                 Loader().uri("http://test-uri-equals.com"),
1742                 true)
1743         testLoaderEquals(
1744                 Loader().uri("http://test-uri-equals.com"),
1745                 Loader().uri("http://test-uri-equalsx.com"),
1746                 false)
1747 
1748         testLoaderEquals(
1749                 Loader().uri("http://test-uri-equals.com")
1750                         .flags(LOAD_FLAGS_BYPASS_CLASSIFIER)
1751                         .headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
1752                         .referrer("test-referrer"),
1753                 Loader().uri("http://test-uri-equals.com")
1754                         .flags(LOAD_FLAGS_BYPASS_CLASSIFIER)
1755                         .headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
1756                         .referrer("test-referrer"),
1757                 true)
1758         testLoaderEquals(
1759                 Loader().uri("http://test-uri-equals.com")
1760                         .flags(LOAD_FLAGS_BYPASS_CLASSIFIER)
1761                         .headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
1762                         .referrer(sessionRule.session),
1763                 Loader().uri("http://test-uri-equals.com")
1764                         .flags(LOAD_FLAGS_BYPASS_CLASSIFIER)
1765                         .headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
1766                         .referrer("test-referrer"),
1767                 false)
1768 
1769         testLoaderEquals(
1770                 Loader().referrer(sessionRule.session)
1771                         .data("testtest", "text/plain"),
1772                 Loader().referrer(sessionRule.session)
1773                         .data("testtest", "text/plain"),
1774                 true)
1775         testLoaderEquals(
1776                 Loader().referrer(sessionRule.session)
1777                         .data("testtest", "text/plain"),
1778                 Loader().referrer("test-referrer")
1779                         .data("testtest", "text/plain"),
1780                 false)
1781     }
1782 
loadUriHeadernull1783     @Test fun loadUriHeader() {
1784         // Basic test
1785         loadUriHeaderTest(
1786                 mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
1787                 mapOf()
1788         )
1789         loadUriHeaderTest(
1790                 mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
1791                 mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
1792                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1793         )
1794 
1795         // Empty value headers are ignored
1796         loadUriHeaderTest(
1797                 mapOf("ValueLess1" to "", "ValueLess2" to null),
1798                 mapOf()
1799         )
1800 
1801         // Null key or special headers are ignored
1802         loadUriHeaderTest(
1803                 mapOf(null to "BadNull",
1804                       "Connection" to "BadConnection",
1805                       "Host" to "BadHost"),
1806                 mapOf()
1807         )
1808 
1809         // Key or value cannot contain '\r\n'
1810         loadUriHeaderTest(
1811                 mapOf("Header1" to "Value",
1812                       "Header2" to "Value1, Value2",
1813                       "this\r\nis invalid" to "test value",
1814                       "test key" to "this\r\n is a no-no",
1815                       "what" to "what\r\nhost:amazon.com",
1816                       "Header3" to "Value1, Value2, Value3"
1817                 ),
1818                 mapOf()
1819         )
1820         loadUriHeaderTest(
1821                 mapOf("Header1" to "Value",
1822                         "Header2" to "Value1, Value2",
1823                         "this\r\nis invalid" to "test value",
1824                         "test key" to "this\r\n is a no-no",
1825                         "what" to "what\r\nhost:amazon.com",
1826                         "Header3" to "Value1, Value2, Value3"
1827                 ),
1828                 mapOf("Header1" to "Value",
1829                         "Header2" to "Value1, Value2",
1830                         "Header3" to "Value1, Value2, Value3"),
1831                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1832         )
1833 
1834         loadUriHeaderTest(
1835                 mapOf("Header1" to "Value",
1836                         "Header2" to "Value1, Value2",
1837                         "what" to "what\r\nhost:amazon.com"),
1838                 mapOf()
1839         )
1840         loadUriHeaderTest(
1841                 mapOf("Header1" to "Value",
1842                       "Header2" to "Value1, Value2",
1843                       "what" to "what\r\nhost:amazon.com"),
1844                 mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
1845                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1846         )
1847 
1848         loadUriHeaderTest(
1849                 mapOf("what" to "what\r\nhost:amazon.com"),
1850                 mapOf()
1851         )
1852 
1853         loadUriHeaderTest(
1854                 mapOf("this\r\n" to "yes"),
1855                 mapOf()
1856         )
1857 
1858         // Connection and Host cannot be overriden, no matter the case spelling
1859         loadUriHeaderTest(
1860                 mapOf("Header1" to "Value1", "ConnEction" to "test", "connection" to "test2"),
1861                 mapOf()
1862         )
1863         loadUriHeaderTest(
1864                 mapOf("Header1" to "Value1", "ConnEction" to "test", "connection" to "test2"),
1865                 mapOf("Header1" to "Value1"),
1866                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1867         )
1868 
1869         loadUriHeaderTest(
1870                 mapOf("Header1" to "Value1", "connection" to "test2"),
1871                 mapOf()
1872         )
1873         loadUriHeaderTest(
1874                 mapOf("Header1" to "Value1", "connection" to "test2"),
1875                 mapOf("Header1" to "Value1"),
1876                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1877         )
1878 
1879         loadUriHeaderTest(
1880                 mapOf("Header1   " to "Value1", "host" to "test2"),
1881                 mapOf()
1882         )
1883         loadUriHeaderTest(
1884                 mapOf("Header1   " to "Value1", "host" to "test2"),
1885                 mapOf("Header1" to "Value1"),
1886                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1887         )
1888 
1889         loadUriHeaderTest(
1890                 mapOf("Header1" to "Value1", "host" to "test2"),
1891                 mapOf()
1892         )
1893         loadUriHeaderTest(
1894                 mapOf("Header1" to "Value1", "host" to "test2"),
1895                 mapOf("Header1" to "Value1"),
1896                 GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
1897         )
1898 
1899         // Adding white space at the end of a forbidden header still prevents override
1900         loadUriHeaderTest(
1901                 mapOf("host" to "amazon.com",
1902                       "host " to "amazon.com",
1903                       "host\r" to "amazon.com",
1904                       "host\r\n" to "amazon.com"),
1905                 mapOf()
1906         )
1907 
1908         // '\r' or '\n' are forbidden character even when not following each other
1909         loadUriHeaderTest(
1910                 mapOf("abc\ra\n" to "amazon.com"),
1911                 mapOf()
1912         )
1913 
1914         // CORS Safelist test
1915         loadUriHeaderTest(
1916                 mapOf("Accept-Language" to "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
1917                       "Accept" to "text/html",
1918                       "Content-Language" to "de-DE, en-CA",
1919                       "Content-Type" to "multipart/form-data; boundary=something"),
1920                 mapOf("Accept-Language" to "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
1921                       "Accept" to "text/html",
1922                       "Content-Language" to "de-DE, en-CA",
1923                       "Content-Type" to "multipart/form-data; boundary=something"),
1924                 GeckoSession.HEADER_FILTER_CORS_SAFELISTED
1925         )
1926 
1927         // CORS safelist doesn't allow Content-type image/svg
1928         loadUriHeaderTest(
1929                 mapOf("Accept-Language" to "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
1930                       "Accept" to "text/html",
1931                       "Content-Language" to "de-DE, en-CA",
1932                       "Content-Type" to "image/svg; boundary=something"),
1933                 mapOf("Accept-Language" to "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
1934                       "Accept" to "text/html",
1935                       "Content-Language" to "de-DE, en-CA"),
1936                 GeckoSession.HEADER_FILTER_CORS_SAFELISTED
1937         )
1938     }
1939 
1940     @Test(expected = GeckoResult.UncaughtException::class)
onNewSession_doesNotAllowOpenednull1941     fun onNewSession_doesNotAllowOpened() {
1942         // TODO: bug 1710943
1943         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1944 
1945         // Disable popup blocker.
1946         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
1947 
1948         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
1949         sessionRule.session.waitForPageStop()
1950 
1951         sessionRule.session.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
1952             @AssertCalled(count = 1)
1953             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession> {
1954                 return GeckoResult.fromValue(sessionRule.createOpenSession())
1955             }
1956         })
1957 
1958         sessionRule.session.evaluateJS("document.querySelector('#targetBlankLink').click()")
1959 
1960         sessionRule.session.waitUntilCalled(GeckoSession.NavigationDelegate::class,
1961                                             "onNewSession")
1962         UiThreadUtils.loopUntilIdle(sessionRule.env.defaultTimeoutMillis)
1963     }
1964 
1965     @Test
extensionProcessSwitchingnull1966     fun extensionProcessSwitching() {
1967         // TODO: bug 1710943
1968         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
1969 
1970         sessionRule.setPrefsUntilTestEnd(mapOf(
1971                 "xpinstall.signatures.required" to false,
1972                 "extensions.install.requireBuiltInCerts" to false,
1973                 "extensions.update.requireBuiltInCerts" to false
1974         ))
1975 
1976         val controller = sessionRule.runtime.webExtensionController
1977 
1978         sessionRule.addExternalDelegateUntilTestEnd(
1979                 WebExtensionController.PromptDelegate::class,
1980                 controller::setPromptDelegate,
1981                 { controller.promptDelegate = null },
1982                 object : WebExtensionController.PromptDelegate {
1983             @AssertCalled
1984             override fun onInstallPrompt(extension: WebExtension): GeckoResult<AllowOrDeny> {
1985                 return GeckoResult.allow()
1986             }
1987         })
1988 
1989         val extension = sessionRule.waitForResult(
1990                 controller.install("https://example.org/tests/junit/page-history.xpi"))
1991 
1992         assertThat("baseUrl should be a valid extension URL",
1993                 extension.metaData.baseUrl, startsWith("moz-extension://"))
1994 
1995         val url = extension.metaData.baseUrl + "page.html"
1996         processSwitchingTest(url)
1997 
1998         sessionRule.waitForResult(controller.uninstall(extension))
1999     }
2000 
2001     @Test
mainProcessSwitchingnull2002     fun mainProcessSwitching() {
2003         // TODO: bug 1710943
2004         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
2005         processSwitchingTest("about:config")
2006     }
2007 
processSwitchingTestnull2008     private fun processSwitchingTest(url: String) {
2009         val settings = sessionRule.runtime.settings
2010         val aboutConfigEnabled = settings.aboutConfigEnabled
2011         settings.aboutConfigEnabled = true
2012 
2013         var currentUrl: String? = null
2014         mainSession.delegateUntilTestEnd(object: GeckoSession.NavigationDelegate {
2015             override fun onLocationChange(session: GeckoSession, url: String?) {
2016                 currentUrl = url
2017             }
2018 
2019             override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
2020                 assertThat("Should not get here", false, equalTo(true))
2021                 return null
2022             }
2023         })
2024 
2025         // This will load a page in the child
2026         mainSession.loadTestPath(HELLO2_HTML_PATH)
2027         sessionRule.waitForPageStop()
2028 
2029         assertThat("docShell should start out active", mainSession.active,
2030             equalTo(true))
2031 
2032         // This loads in the parent process
2033         mainSession.loadUri(url)
2034         sessionRule.waitForPageStop()
2035 
2036         assertThat("URL should match", currentUrl!!, equalTo(url))
2037 
2038         // This will load a page in the child
2039         mainSession.loadTestPath(HELLO_HTML_PATH)
2040         sessionRule.waitForPageStop()
2041 
2042         assertThat("URL should match", currentUrl!!, endsWith(HELLO_HTML_PATH))
2043         assertThat("docShell should be active after switching process",
2044                 mainSession.active,
2045                 equalTo(true))
2046 
2047         mainSession.loadUri(url)
2048         sessionRule.waitForPageStop()
2049 
2050         assertThat("URL should match", currentUrl!!, equalTo(url))
2051 
2052         sessionRule.session.goBack()
2053         sessionRule.waitForPageStop()
2054 
2055         assertThat("URL should match", currentUrl!!, endsWith(HELLO_HTML_PATH))
2056         assertThat("docShell should be active after switching process",
2057                 mainSession.active,
2058                 equalTo(true))
2059 
2060         sessionRule.session.goBack()
2061         sessionRule.waitForPageStop()
2062 
2063         assertThat("URL should match", currentUrl!!, equalTo(url))
2064 
2065         sessionRule.session.goBack()
2066         sessionRule.waitForPageStop()
2067 
2068         assertThat("URL should match", currentUrl!!, endsWith(HELLO2_HTML_PATH))
2069         assertThat("docShell should be active after switching process",
2070                 mainSession.active,
2071                 equalTo(true))
2072 
2073         settings.aboutConfigEnabled = aboutConfigEnabled
2074     }
2075 
setLocationHashnull2076     @Test fun setLocationHash() {
2077         // TODO: bug 1710943
2078         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
2079 
2080         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO_HTML_PATH")
2081         sessionRule.waitForPageStop()
2082 
2083         sessionRule.session.evaluateJS("location.hash = 'test1';")
2084 
2085         sessionRule.session.waitUntilCalled(object : Callbacks.NavigationDelegate {
2086             @AssertCalled(count = 0)
2087             override fun onLoadRequest(session: GeckoSession,
2088                                        request: LoadRequest):
2089                                        GeckoResult<AllowOrDeny>? {
2090                 assertThat("Load should not be direct", request.isDirectNavigation,
2091                         equalTo(false))
2092                 return null
2093             }
2094 
2095             @AssertCalled(count = 1)
2096             override fun onLocationChange(session: GeckoSession, url: String?, perms : MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
2097                 assertThat("URI should match", url, endsWith("#test1"))
2098             }
2099         })
2100 
2101         sessionRule.session.evaluateJS("location.hash = 'test2';")
2102 
2103         sessionRule.session.waitUntilCalled(object : Callbacks.NavigationDelegate {
2104             @AssertCalled(count = 0)
2105             override fun onLoadRequest(session: GeckoSession,
2106                                        request: LoadRequest):
2107                                        GeckoResult<AllowOrDeny>? {
2108                 return null
2109             }
2110 
2111             @AssertCalled(count = 1)
2112             override fun onLocationChange(session: GeckoSession, url: String?, perms : MutableList<GeckoSession.PermissionDelegate.ContentPermission>) {
2113                 assertThat("URI should match", url, endsWith("#test2"))
2114             }
2115         })
2116     }
2117 
purgeHistorynull2118     @Test fun purgeHistory() {
2119         // TODO: Bug 1648158
2120         assumeThat(sessionRule.env.isFission, equalTo(false))
2121 
2122         // TODO: bug 1710943
2123         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
2124 
2125         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO_HTML_PATH")
2126         sessionRule.waitUntilCalled(object : Callbacks.NavigationDelegate {
2127             @AssertCalled(count = 1)
2128             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
2129                 assertThat("Session should not be null", session, notNullValue())
2130                 assertThat("Cannot go back", canGoBack, equalTo(false))
2131             }
2132 
2133             @AssertCalled(count = 1)
2134             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
2135                 assertThat("Session should not be null", session, notNullValue())
2136                 assertThat("Cannot go forward", canGoForward, equalTo(false))
2137             }
2138         })
2139         sessionRule.session.loadUri("$TEST_ENDPOINT$HELLO2_HTML_PATH")
2140         sessionRule.waitUntilCalled(object : Callbacks.All {
2141             @AssertCalled(count = 1)
2142             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
2143                 assertThat("Session should not be null", session, notNullValue())
2144                 assertThat("Cannot go back", canGoBack, equalTo(true))
2145             }
2146             @AssertCalled(count = 1)
2147             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
2148                 assertThat("Session should not be null", session, notNullValue())
2149                 assertThat("Cannot go forward", canGoForward, equalTo(false))
2150             }
2151             @AssertCalled(count = 1)
2152             override fun onHistoryStateChange(session: GeckoSession, state: GeckoSession.HistoryDelegate.HistoryList) {
2153                 assertThat("History should have two entries", state.size, equalTo(2))
2154             }
2155         })
2156         sessionRule.session.purgeHistory()
2157         sessionRule.waitUntilCalled(object : Callbacks.All {
2158             @AssertCalled(count = 1)
2159             override fun onHistoryStateChange(session: GeckoSession, state: GeckoSession.HistoryDelegate.HistoryList) {
2160                 assertThat("History should have one entry", state.size, equalTo(1))
2161             }
2162             @AssertCalled(count = 1)
2163             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
2164                 assertThat("Session should not be null", session, notNullValue())
2165                 assertThat("Cannot go back", canGoBack, equalTo(false))
2166             }
2167 
2168             @AssertCalled(count = 1)
2169             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
2170                 assertThat("Session should not be null", session, notNullValue())
2171                 assertThat("Cannot go forward", canGoForward, equalTo(false))
2172             }
2173         })
2174     }
2175 
2176     @WithDisplay(width = 100, height = 100)
userGesturenull2177     @Test fun userGesture() {
2178         // TODO: bug 1710943
2179         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
2180 
2181         mainSession.loadUri("$TEST_ENDPOINT$CLICK_TO_RELOAD_HTML_PATH")
2182         mainSession.waitForPageStop()
2183 
2184         mainSession.synthesizeTap(50, 50)
2185 
2186         sessionRule.waitUntilCalled(object : Callbacks.NavigationDelegate {
2187             @AssertCalled(count = 1)
2188             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
2189                 assertThat("Should have a user gesture", request.hasUserGesture, equalTo(true))
2190                 assertThat("Load should not be direct", request.isDirectNavigation,
2191                         equalTo(false))
2192                 return GeckoResult.allow()
2193             }
2194         })
2195     }
2196 
loadAfterLoadnull2197     @Test fun loadAfterLoad() {
2198         // TODO: Bug 1657028
2199         assumeThat(sessionRule.env.isFission, equalTo(false))
2200 
2201         // TODO: bug 1710943
2202         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
2203 
2204         sessionRule.session.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
2205             @AssertCalled(count = 2)
2206             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
2207                 assertThat("URLs should match", request.uri, endsWith(forEachCall(HELLO_HTML_PATH, HELLO2_HTML_PATH)))
2208                 return GeckoResult.allow()
2209             }
2210         })
2211 
2212         mainSession.loadUri("$TEST_ENDPOINT$HELLO_HTML_PATH")
2213         mainSession.loadUri("$TEST_ENDPOINT$HELLO2_HTML_PATH")
2214         mainSession.waitForPageStop()
2215     }
2216 
2217     @Test
loadLongDataUriToplevelDirectnull2218     fun loadLongDataUriToplevelDirect() {
2219         val dataBytes = ByteArray(3 * 1024 * 1024)
2220         val expectedUri = createDataUri(dataBytes, "*/*")
2221         val loader = Loader().data(dataBytes, "*/*")
2222 
2223         sessionRule.session.delegateUntilTestEnd(object : Callbacks.NavigationDelegate {
2224             @AssertCalled(count = 1, order = [1])
2225             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
2226                 assertThat("URLs should match", request.uri, equalTo(expectedUri))
2227                 return GeckoResult.allow()
2228             }
2229 
2230             @AssertCalled(count = 1, order = [2])
2231             override fun onLoadError(session: GeckoSession, uri: String?,
2232                                      error: WebRequestError): GeckoResult<String>? {
2233                 assertThat("Error category should match", error.category,
2234                         equalTo(WebRequestError.ERROR_CATEGORY_URI))
2235                 assertThat("Error code should match", error.code,
2236                         equalTo(WebRequestError.ERROR_DATA_URI_TOO_LONG))
2237                 assertThat("URLs should match", uri, equalTo(expectedUri))
2238                 return null
2239             }
2240         })
2241 
2242         sessionRule.session.load(loader)
2243         sessionRule.waitUntilCalled(Callbacks.NavigationDelegate::class, "onLoadError")
2244     }
2245 
2246     @Test
loadLongDataUriToplevelIndirectnull2247     fun loadLongDataUriToplevelIndirect() {
2248         // TODO: bug 1710943
2249         assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
2250 
2251         val dataBytes = ByteArray(3 * 1024 * 1024)
2252         val dataUri = createDataUri(dataBytes, "*/*")
2253 
2254         sessionRule.session.loadTestPath(DATA_URI_PATH)
2255         sessionRule.session.waitForPageStop()
2256 
2257         sessionRule.session.delegateUntilTestEnd(object : Callbacks.NavigationDelegate {
2258             @AssertCalled(false)
2259             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
2260                 return GeckoResult.deny()
2261             }
2262         })
2263 
2264         sessionRule.session.evaluateJS("document.querySelector('#largeLink').href = \"$dataUri\"")
2265         sessionRule.session.evaluateJS("document.querySelector('#largeLink').click()")
2266         sessionRule.session.waitForPageStop()
2267     }
2268 
2269     @Test
loadShortDataUriToplevelIndirectnull2270     fun loadShortDataUriToplevelIndirect() {
2271         sessionRule.session.delegateUntilTestEnd(object : Callbacks.NavigationDelegate {
2272             @AssertCalled(count = 2)
2273             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
2274                 return GeckoResult.allow()
2275             }
2276 
2277             @AssertCalled(false)
2278             override fun onLoadError(session: GeckoSession, uri: String?,
2279                                      error: WebRequestError): GeckoResult<String>? {
2280                 return null
2281             }
2282         })
2283 
2284         val dataBytes = this.getTestBytes("/assets/www/images/test.gif")
2285         val uri = createDataUri(dataBytes, "image/*")
2286 
2287         sessionRule.session.loadTestPath(DATA_URI_PATH)
2288         sessionRule.session.waitForPageStop()
2289 
2290         sessionRule.session.evaluateJS("document.querySelector('#smallLink').href = \"$uri\"")
2291         sessionRule.session.evaluateJS("document.querySelector('#smallLink').click()")
2292         sessionRule.session.waitForPageStop()
2293     }
2294 
createLargeHighEntropyImageDataUrinull2295     fun createLargeHighEntropyImageDataUri() : String {
2296         val desiredMinSize = (2 * 1024 * 1024) + 1
2297 
2298         val width = 768;
2299         val height = 768;
2300 
2301         val bitmap = Bitmap.createBitmap(ThreadLocalRandom.current().ints(width.toLong() * height.toLong()).toArray(),
2302                                          width, height, Bitmap.Config.ARGB_8888)
2303 
2304         val stream = ByteArrayOutputStream()
2305         if (!bitmap.compress(Bitmap.CompressFormat.PNG, 0, stream)) {
2306             throw Exception("Error compressing PNG")
2307         }
2308 
2309         val uri = createDataUri(stream.toByteArray(), "image/png")
2310 
2311         if (uri.length < desiredMinSize) {
2312             throw Exception("Test uri is too small, want at least " + desiredMinSize + ", got " + uri.length)
2313         }
2314 
2315         return uri
2316     }
2317 
2318     @Test
loadLongDataUriNonToplevelnull2319     fun loadLongDataUriNonToplevel() {
2320         val dataUri = createLargeHighEntropyImageDataUri()
2321 
2322         sessionRule.session.delegateUntilTestEnd(object : Callbacks.NavigationDelegate {
2323             @AssertCalled(count = 1)
2324             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
2325                 return GeckoResult.allow()
2326             }
2327 
2328             @AssertCalled(false)
2329             override fun onLoadError(session: GeckoSession, uri: String?,
2330                                      error: WebRequestError): GeckoResult<String>? {
2331                 return null
2332             }
2333         })
2334 
2335         sessionRule.session.loadTestPath(DATA_URI_PATH)
2336         sessionRule.session.waitForPageStop()
2337 
2338         sessionRule.session.evaluateJS("document.querySelector('#image').onload = () => { imageLoaded = true; }")
2339         sessionRule.session.evaluateJS("document.querySelector('#image').src = \"$dataUri\"")
2340         UiThreadUtils.waitForCondition({
2341           sessionRule.session.evaluateJS("document.querySelector('#image').complete") as Boolean
2342         }, sessionRule.env.defaultTimeoutMillis)
2343         sessionRule.session.evaluateJS("if (!imageLoaded) throw imageLoaded")
2344     }
2345 }
2346