summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads1.js
blob: a8119df3fd7084fd55ff02d7a13031fe08d3ef5d (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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

// Tests getAdjustedQuads works properly in a variety of use cases including
// iframes, scroll and zoom

"use strict";

const TEST_URI = TEST_URI_ROOT + "doc_layoutHelpers_getBoxQuads1.html";

add_task(async function () {
  const tab = await addTab(TEST_URI);

  info("Running tests");

  await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
    // This function allows the Content Task to easily call `FullZoom` API in
    // the parent process.
    function sendCommand(cmd) {
      return SpecialPowers.spawnChrome([cmd], async data => {
        const window = this.browsingContext.topChromeWindow;
        switch (data) {
          case "zoom-enlarge":
            window.FullZoom.enlarge();
            break;
          case "zoom-reset":
            await window.FullZoom.reset();
            break;
          case "zoom-reduce":
            window.FullZoom.reduce();
            break;
        }
      });
    }

    const doc = content.document;

    const { require } = ChromeUtils.importESModule(
      "resource://devtools/shared/loader/Loader.sys.mjs"
    );
    const {
      getAdjustedQuads,
    } = require("resource://devtools/shared/layout/utils.js");

    Assert.strictEqual(
      typeof getAdjustedQuads,
      "function",
      "getAdjustedQuads is defined"
    );

    returnsTheRightDataStructure();
    isEmptyForMissingNode();
    isEmptyForHiddenNodes();
    defaultsToBorderBoxIfNoneProvided();
    returnsLikeGetBoxQuadsInSimpleCase();
    takesIframesOffsetsIntoAccount();
    takesScrollingIntoAccount();
    await takesZoomIntoAccount();
    returnsMultipleItemsForWrappingInlineElements();

    function returnsTheRightDataStructure() {
      info("Checks that the returned data contains bounds and 4 points");

      const node = doc.querySelector("body");
      const [res] = getAdjustedQuads(doc.defaultView, node, "content");

      ok("bounds" in res, "The returned data has a bounds property");
      ok("p1" in res, "The returned data has a p1 property");
      ok("p2" in res, "The returned data has a p2 property");
      ok("p3" in res, "The returned data has a p3 property");
      ok("p4" in res, "The returned data has a p4 property");

      for (const boundProp of [
        "bottom",
        "top",
        "right",
        "left",
        "width",
        "height",
        "x",
        "y",
      ]) {
        ok(
          boundProp in res.bounds,
          "The bounds has a " + boundProp + " property"
        );
      }

      for (const point of ["p1", "p2", "p3", "p4"]) {
        for (const pointProp of ["x", "y", "z", "w"]) {
          ok(
            pointProp in res[point],
            point + " has a " + pointProp + " property"
          );
        }
      }
    }

    function isEmptyForMissingNode() {
      info("Checks that null is returned for invalid nodes");

      for (const input of [null, undefined, "", 0]) {
        is(
          getAdjustedQuads(doc.defaultView, input).length,
          0,
          "A 0-length array is returned for input " + input
        );
      }
    }

    function isEmptyForHiddenNodes() {
      info("Checks that null is returned for nodes that aren't rendered");

      const style = doc.querySelector("#styles");
      is(
        getAdjustedQuads(doc.defaultView, style).length,
        0,
        "null is returned for a <style> node"
      );

      const hidden = doc.querySelector("#hidden-node");
      is(
        getAdjustedQuads(doc.defaultView, hidden).length,
        0,
        "null is returned for a hidden node"
      );
    }

    function defaultsToBorderBoxIfNoneProvided() {
      info(
        "Checks that if no boxtype is passed, then border is the default one"
      );

      const node = doc.querySelector("#simple-node-with-margin-padding-border");
      const [withBoxType] = getAdjustedQuads(doc.defaultView, node, "border");
      const [withoutBoxType] = getAdjustedQuads(doc.defaultView, node);

      for (const boundProp of [
        "bottom",
        "top",
        "right",
        "left",
        "width",
        "height",
        "x",
        "y",
      ]) {
        is(
          withBoxType.bounds[boundProp],
          withoutBoxType.bounds[boundProp],
          boundProp + " bound is equal with or without the border box type"
        );
      }

      for (const point of ["p1", "p2", "p3", "p4"]) {
        for (const pointProp of ["x", "y", "z", "w"]) {
          is(
            withBoxType[point][pointProp],
            withoutBoxType[point][pointProp],
            point +
              "." +
              pointProp +
              " is equal with or without the border box type"
          );
        }
      }
    }

    function returnsLikeGetBoxQuadsInSimpleCase() {
      info(
        "Checks that for an element in the main frame, without scroll nor zoom" +
          "that the returned value is similar to the returned value of getBoxQuads"
      );

      const node = doc.querySelector("#simple-node-with-margin-padding-border");

      for (const region of ["content", "padding", "border", "margin"]) {
        const expected = node.getBoxQuads({
          box: region,
        })[0];
        const [actual] = getAdjustedQuads(doc.defaultView, node, region);

        for (const boundProp of [
          "bottom",
          "top",
          "right",
          "left",
          "width",
          "height",
          "x",
          "y",
        ]) {
          is(
            actual.bounds[boundProp],
            expected.getBounds()[boundProp],
            boundProp +
              " bound is equal to the one returned by getBoxQuads for " +
              region +
              " box"
          );
        }

        for (const point of ["p1", "p2", "p3", "p4"]) {
          for (const pointProp of ["x", "y", "z", "w"]) {
            is(
              actual[point][pointProp],
              expected[point][pointProp],
              point +
                "." +
                pointProp +
                " is equal to the one returned by getBoxQuads for " +
                region +
                " box"
            );
          }
        }
      }
    }

    function takesIframesOffsetsIntoAccount() {
      info(
        "Checks that the quad returned for a node inside iframes that have " +
          "margins takes those offsets into account"
      );

      const rootIframe = doc.querySelector("iframe");
      const subIframe = rootIframe.contentDocument.querySelector("iframe");
      const innerNode = subIframe.contentDocument.querySelector("#inner-node");

      const [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");

      // rootIframe margin + subIframe margin + node margin + node border + node padding
      const p1x = 10 + 10 + 10 + 10 + 10;
      is(quad.p1.x, p1x, "The inner node's p1 x position is correct");

      // Same as p1x + the inner node width
      const p2x = p1x + 100;
      is(quad.p2.x, p2x, "The inner node's p2 x position is correct");
    }

    function takesScrollingIntoAccount() {
      info(
        "Checks that the quad returned for a node inside multiple scrolled " +
          "containers takes the scroll values into account"
      );

      // For info, the container being tested here is absolutely positioned at 0 0
      // to simplify asserting the coordinates

      info("Scroll the container nodes down");
      const scrolledNode = doc.querySelector("#scrolled-node");
      scrolledNode.scrollTop = 100;
      const subScrolledNode = doc.querySelector("#sub-scrolled-node");
      subScrolledNode.scrollTop = 200;
      const innerNode = doc.querySelector("#inner-scrolled-node");

      let [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
      is(
        quad.p1.x,
        0,
        "p1.x of the scrolled node is correct after scrolling down"
      );
      is(
        quad.p1.y,
        -300,
        "p1.y of the scrolled node is correct after scrolling down"
      );

      info("Scrolling back up");
      scrolledNode.scrollTop = 0;
      subScrolledNode.scrollTop = 0;

      [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
      is(
        quad.p1.x,
        0,
        "p1.x of the scrolled node is correct after scrolling up"
      );
      is(
        quad.p1.y,
        0,
        "p1.y of the scrolled node is correct after scrolling up"
      );
    }

    async function takesZoomIntoAccount() {
      info(
        "Checks that if the page is zoomed in/out, the quad returned is correct"
      );

      // Hard-coding coordinates in this zoom test is a bad idea as it can vary
      // depending on the platform, so we simply test that zooming in produces a
      // bigger quad and zooming out produces a smaller quad

      const node = doc.querySelector("#simple-node-with-margin-padding-border");
      const [defaultQuad] = getAdjustedQuads(doc.defaultView, node);

      info("Zoom in");
      await sendCommand("zoom-enlarge");
      const [zoomedInQuad] = getAdjustedQuads(doc.defaultView, node);

      Assert.greater(
        zoomedInQuad.bounds.width,
        defaultQuad.bounds.width,
        "The zoomed in quad is bigger than the default one"
      );
      Assert.greater(
        zoomedInQuad.bounds.height,
        defaultQuad.bounds.height,
        "The zoomed in quad is bigger than the default one"
      );

      info("Zoom out");
      await sendCommand("zoom-reset");
      await sendCommand("zoom-reduce");

      const [zoomedOutQuad] = getAdjustedQuads(doc.defaultView, node);

      Assert.less(
        zoomedOutQuad.bounds.width,
        defaultQuad.bounds.width,
        "The zoomed out quad is smaller than the default one"
      );
      Assert.less(
        zoomedOutQuad.bounds.height,
        defaultQuad.bounds.height,
        "The zoomed out quad is smaller than the default one"
      );

      await sendCommand("zoom-reset");
    }

    function returnsMultipleItemsForWrappingInlineElements() {
      info(
        "Checks that several quads are returned " +
          "for inline elements that span line-breaks"
      );

      const node = doc.querySelector("#inline");
      const quads = getAdjustedQuads(doc.defaultView, node, "content");
      // At least 3 because of the 2 <br />, maybe more depending on the window size.
      Assert.greaterOrEqual(quads.length, 3, "Multiple quads were returned");

      is(
        quads.length,
        node.getBoxQuads().length,
        "The same number of boxes as getBoxQuads was returned"
      );
    }
  });

  gBrowser.removeCurrentTab();
});