summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PrintDelegateTest.kt
blob: 2e9bc8f135bd93d76d11c527aad5646bb65f175d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
 * Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

package org.mozilla.geckoview.test

import android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Color.rgb
import android.os.Handler
import android.os.Looper
import android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
import org.hamcrest.CoreMatchers.containsString
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.mozilla.geckoview.Autofill
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.GeckoSession.PrintDelegate
import org.mozilla.geckoview.GeckoView.ActivityContextDelegate
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
import kotlin.math.roundToInt

@RunWith(AndroidJUnit4::class)
@LargeTest
class PrintDelegateTest : BaseSessionTest() {
    private val activityRule = ActivityScenarioRule(GeckoViewTestActivity::class.java)
    private var deviceHeight = 0
    private var deviceWidth = 0
    private var scaledHeight = 0
    private var scaledWidth = 12
    private val instrumentation = InstrumentationRegistry.getInstrumentation()
    private val uiAutomation = instrumentation.getUiAutomation(FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES)

    @get:Rule
    override val rules: RuleChain = RuleChain.outerRule(activityRule).around(sessionRule)

    @Before
    fun setup() {
        activityRule.scenario.onActivity {
            class PrintTestActivityDelegate : ActivityContextDelegate {
                override fun getActivityContext(): Context {
                    return it
                }
            }
            // An activity delegate is required for printing
            it.view.activityContextDelegate = PrintTestActivityDelegate()
            deviceHeight = it.resources.displayMetrics.heightPixels
            deviceWidth = it.resources.displayMetrics.widthPixels
            scaledHeight = (scaledWidth * (deviceHeight / deviceWidth.toDouble())).roundToInt()
        }
    }

    @After
    fun cleanup() {
        activityRule.scenario.onActivity {
            uiAutomation.setOnAccessibilityEventListener {}
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun printDelegateTest() {
        activityRule.scenario.onActivity {
            var delegateCalled = 0
            sessionRule.delegateUntilTestEnd(object : PrintDelegate {
                @AssertCalled(count = 1)
                override fun onPrint(session: GeckoSession) {
                    delegateCalled++
                }
            })
            mainSession.loadTestPath(COLOR_ORANGE_BACKGROUND_HTML_PATH)
            mainSession.waitForPageStop()
            mainSession.printPageContent()
            assertTrue("Android print delegate called once.", delegateCalled == 1)
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun windowDotPrintAvailableTest() {
        sessionRule.setPrefsUntilTestEnd(mapOf("dom.enable_window_print" to true))
        activityRule.scenario.onActivity {
            mainSession.loadTestPath(COLOR_ORANGE_BACKGROUND_HTML_PATH)
            mainSession.waitForPageStop()
            val response = mainSession.waitForJS("window.print();")
            assertTrue("Window.print(); is available.", response == null)
        }
    }

    // Returns the center pixel color of the the print preview's screenshot
    private fun printCenterPixelColor(): GeckoResult<Int> {
        val pixelResult = GeckoResult<Int>()
        // Listening for Android Print Activity
        uiAutomation.setOnAccessibilityEventListener { event ->
            if (event.packageName == "com.android.printspooler" &&
                event.eventType == TYPE_VIEW_SCROLLED
            ) {
                uiAutomation.setOnAccessibilityEventListener {}
                // Delaying the screenshot to give time for preview to load
                Handler(Looper.getMainLooper()).postDelayed({
                    val bitmap = uiAutomation.takeScreenshot()
                    val scaled = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false)
                    pixelResult.complete(scaled.getPixel(scaledWidth / 2, scaledHeight / 2))
                }, 1500)
            }
        }
        return pixelResult
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun printPreviewRendered() {
        activityRule.scenario.onActivity { activity ->
            // CSS rules render this blue on screen and orange on print
            mainSession.loadTestPath(PRINT_CONTENT_CHANGE)
            mainSession.waitForPageStop()
            // Setting to the default delegate (test rules changed it)
            mainSession.printDelegate = activity.view.printDelegate
            mainSession.printPageContent()
            val orange = rgb(255, 113, 57)
            val centerPixel = printCenterPixelColor()
            assertTrue(
                "Android print opened and rendered.",
                sessionRule.waitForResult(centerPixel) == orange,
            )
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun basicWindowDotPrintTest() {
        sessionRule.setPrefsUntilTestEnd(mapOf("dom.enable_window_print" to true))
        activityRule.scenario.onActivity { activity ->
            // CSS rules render this blue on screen and orange on print
            mainSession.loadTestPath(PRINT_CONTENT_CHANGE)
            mainSession.waitForPageStop()
            // Setting to the default delegate (test rules changed it)
            mainSession.printDelegate = activity.view.printDelegate
            mainSession.evaluateJS("window.print();")
            val centerPixel = printCenterPixelColor()
            val orange = rgb(255, 113, 57)
            assertTrue(
                "Android print opened and rendered.",
                sessionRule.waitForResult(centerPixel) == orange,
            )
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun statusWindowDotPrintTest() {
        sessionRule.setPrefsUntilTestEnd(mapOf("dom.enable_window_print" to true))
        activityRule.scenario.onActivity { activity ->
            // CSS rules render this blue on screen and orange on print
            mainSession.loadTestPath(PRINT_CONTENT_CHANGE)
            mainSession.waitForPageStop()
            // Setting to the default delegate (test rules changed it)
            mainSession.printDelegate = activity.view.printDelegate
            mainSession.evaluateJS("window.print()")
            val centerPixel = printCenterPixelColor()
            val orange = rgb(255, 113, 57)
            assertTrue(
                "Android print opened and rendered.",
                sessionRule.waitForResult(centerPixel) == orange,
            )
            var didCatch = false
            try {
                mainSession.evaluateJS("window.print();")
            } catch (e: GeckoSessionTestRule.RejectedPromiseException) {
                assertThat(
                    "Print status context reported.",
                    e.message,
                    containsString("Window.print: No browsing context"),
                )
                didCatch = true
            }
            assertTrue("Did show print status.", didCatch)
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun staticContextWindowDotPrintTest() {
        sessionRule.setPrefsUntilTestEnd(mapOf("dom.enable_window_print" to true))
        activityRule.scenario.onActivity { activity ->
            // CSS rules render this blue on screen and orange on print
            // Print button removes content after printing to test if it froze a static page for printing
            mainSession.loadTestPath(PRINT_CONTENT_CHANGE)
            mainSession.waitForPageStop()
            // Setting to the default delegate (test rules changed it)
            mainSession.printDelegate = activity.view.printDelegate
            mainSession.evaluateJS("document.getElementById('print-button').click();")
            val centerPixel = printCenterPixelColor()
            val orange = rgb(255, 113, 57)
            assertTrue(
                "Android print opened and rendered static page.",
                sessionRule.waitForResult(centerPixel) == orange,
            )
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun iframeWindowDotPrintTest() {
        sessionRule.setPrefsUntilTestEnd(mapOf("dom.enable_window_print" to true))
        activityRule.scenario.onActivity { activity ->
            // Main frame CSS rules render red on screen and green on print
            // iframe CSS rules render blue on screen and orange on print
            mainSession.loadTestPath(PRINT_IFRAME)
            mainSession.waitForPageStop()
            // Setting to the default delegate (test rules changed it)
            mainSession.printDelegate = activity.view.printDelegate
            // iframe window.print button
            mainSession.evaluateJS("document.getElementById('iframe').contentDocument.getElementById('print-button').click();")
            val centerPixelIframe = printCenterPixelColor()
            val orange = rgb(255, 113, 57)
            sessionRule.waitForResult(centerPixelIframe).let { it ->
                assertTrue("The iframe should not print green. (Printed containing page instead of iframe.)", it != Color.GREEN)
                assertTrue("Printed the iframe correctly.", it == orange)
            }
        }
    }

    @NullDelegate(Autofill.Delegate::class)
    @Test
    fun contentIframeWindowDotPrintTest() {
        sessionRule.setPrefsUntilTestEnd(mapOf("dom.enable_window_print" to true))
        activityRule.scenario.onActivity { activity ->
            // Main frame CSS rules render red on screen and green on print
            // iframe CSS rules render blue on screen and orange on print
            mainSession.loadTestPath(PRINT_IFRAME)
            mainSession.waitForPageStop()
            // Setting to the default delegate (test rules changed it)
            mainSession.printDelegate = activity.view.printDelegate
            // Main page window.print button
            mainSession.evaluateJS("document.getElementById('print-button-page').click();")
            val centerPixelContent = printCenterPixelColor()
            assertTrue("Printed the main content correctly.", sessionRule.waitForResult(centerPixelContent) == Color.GREEN)
        }
    }
}