summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/visual-viewport/viewport_support.js
blob: a82bd2b028010e053199e55bd7bb357361581e6f (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
// Simulates a small pinch-zoom to provide some page scale to make the visual
// viewport able to differ from the layout viewport. The amount of zoom is
// likely to vary between browsers so tests shouldn't rely on an exact scale
// factor.
//
// Simulates two fingers, 100px apart, pulling apart vertically to 200px of
// separation.
function pinchZoomIn() {
  const viewport = window.visualViewport;
  const center_x = Math.floor((viewport.width * viewport.scale) / 2);
  const center_y = Math.floor((viewport.height * viewport.scale) / 2);

  return new test_driver.Actions()
      .setContext(window)
      .addPointer("finger1", "touch")
      .addPointer("finger2", "touch")
      .pointerMove(center_x, center_y-50, {origin: "viewport", sourceName: "finger1"})
      .pointerMove(center_x, center_y+50, {origin: "viewport", sourceName: "finger2"})
      .pointerDown({sourceName: "finger1"})
      .pointerDown({sourceName: "finger2"})
      .pointerMove(center_x, center_y-100, {origin: "viewport", sourceName: "finger1"})
      .pointerMove(center_x, center_y+100, {origin: "viewport", sourceName: "finger2"})
      .pointerUp({sourceName: "finger1"})
      .pointerUp({sourceName: "finger2"})
      .send();
}

// If scrollbars affect layout (i.e. what the CSS Overflow spec calls "classic
// scrollbars", as opposed to overlay scrollbars), return the scrollbar
// thickness in CSS pixels. Returns 0 otherwise.
function calculateScrollbarThickness() {
    var container = document.createElement("div");
    container.style.width = "100px";
    container.style.height = "100px";
    container.style.position = "absolute";
    container.style.visibility = "hidden";
    container.style.overflow = "auto";

    document.body.appendChild(container);

    var widthBefore = container.clientWidth;
    var longContent = document.createElement("div");
    longContent.style.height = "1000px";
    container.appendChild(longContent);

    var widthAfter = container.clientWidth;

    container.remove();

    return widthBefore - widthAfter;
}

// Puts up a widget on screen instructing the user to pinch-zoom in to the
// given scale. The widget is sized such that the given scale is achieved. The
// widget is placed at (x, y) on the page. A button on the widget is used by
// the user to let the widget know that the user has finished. If a callback is
// provided, it will be called when the user dismisses the widget.
function showPinchWidget(scale, x, y, callback) {
    var border = 10;
    var width = window.innerWidth / scale - border;
    var height = window.innerHeight / scale - border;

    var box = document.createElement("div");
    box.style.width = width + "px";
    box.style.height = height + "px";

    // Adjust the x/y coordinates by the border width. We want the box to
    // appear in a place such that if the user gets the window edges exactly on
    // the half-point of the border they end up at x/y
    box.style.left = x - border/2 + "px";
    box.style.top = y - border/2 + "px";

    box.style.position = "absolute";
    box.style.backgroundColor = "coral";
    box.style.border = border + "px solid blue";
    box.style.borderBottom = "0";
    box.style.overflow = "auto";

    var oldDocumentOverflow = document.documentElement.style.overflow;

    var instructions = document.createElement("p");
    instructions.innerText =
        "Pinch-zoom and align this box so that the left, right, and top " +
        "window edges are over the border on each side. When done, click the " +
        "'DONE' button above";
    instructions.style.textAlign = "center";
    instructions.style.fontSize = "medium";

    var button = document.createElement("button");
    button.innerText = "DONE";
    button.style.width = "50%";
    button.style.height = "20%";
    button.style.fontSize = "medium";
    button.style.marginLeft = "25%";
    button.addEventListener("click", function() {
        box.remove();
        document.documentElement.style.overflow = oldDocumentOverflow;
        if (callback)
            callback();
    });

    box.appendChild(button);
    box.appendChild(instructions);

    document.documentElement.style.overflow = "hidden";

    document.body.appendChild(box);
}

// Ends a manual test. Must be called before any async tests are started.
function skipManualTest() {
    test(function() { assert_true(false); }, "Manual Test Skipped");
    done();
}

var stepInstructions = [];
var testNames = [];
var stepFunctions = [];
var steps;
var curStep = 0;

// Adds a manual test step to the test. A test will add a series of steps,
// along with instructions.  Once all the tests steps are added, the test can
// be run by continually running the nextStep() function. All manual test steps
// must be added before calling nextStep.
//
// |func| A function to be executed at the given step. This function can include
//        testharness assertions if |testName| is provided. If this is the last
//        step, the |done()| function (used for manual testharness.js tests)
//        will be called after |func| is executed.
// |testName| If provided, the |func| will be wrapped in a testharness.js
//            async_test with this name. If null, |func| will be executed as a
//            free function.
// |instructions| The text to display to the user. Note, these are shown after
//                step is executed so these should be instructions to setup the
//                checks in the next step.
function addManualTestStep(func, testName, instructions) {
    stepFunctions.push(func);
    testNames.push(testName);
    stepInstructions.push(instructions);
}

// Runs the next step of the test. This must be called only after all test steps
// have been added using |addManualTestStep|.
//
// |callbackFunc| If provided, will be called with a single argument being the
//                instruction string for the current step. Use this to update
//                any necessary UI.
function nextStep(callbackFunc) {
    if (curStep == 0)
      _startManualTest();

    if (typeof(callbackFunc) === 'function')
        callbackFunc(stepInstructions[curStep]);

    steps[curStep]();
    curStep++;
}

function _startManualTest() {
    steps = [];
    for (let i = 0; i < stepFunctions.length; ++i) {
        var stepFunc = stepFunctions[i];
        var testName = testNames[i];
        if (testName) {
            steps.push(async_test(testName).step_func(function() {
                stepFunctions[i]();
                this.done();
                if (i == stepFunctions.length - 1)
                    done();
            }));
        } else {
            steps.push(function() {
                stepFunctions[i]();
                if (i == stepFunctions.length - 1)
                    done();
            });
        }
    }
}