summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/tests/test_abs_positioner_positioning_elements.html
blob: 45342933bab6dcc2e00bfe6fe29ad0806aecc175 (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
<!DOCTYPE HTML>
<html>
<head>
  <title>Test for positioners of absolute positioned elements</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  <style>
  #target {
    background-color: green;
  }
  </style>
</head>
<body>
<p id="display"></p>
<div id="content" contenteditable style="height: 200px; width: 200px;"></div>
<div id="clickaway" style="position: absolute; top: 250px; width: 10px; height: 10px; z-index: 100;"></div>
<img src="green.png"><!-- for ensuring to load the image at first test of <img> case -->
<pre id="test">
<script type="application/javascript">
"use strict";

SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(async function() {
  document.execCommand("enableAbsolutePositionEditing", false, true);
  ok(document.queryCommandState("enableAbsolutePositionEditing"),
     "Absolute positioned element editor should be enabled by the call of execCommand");

  let outOfEditor = document.getElementById("clickaway");

  function cancel(e) { e.stopPropagation(); }
  let content = document.getElementById("content");
  content.addEventListener("mousedown", cancel);
  content.addEventListener("mousemove", cancel);
  content.addEventListener("mouseup", cancel);

  async function waitForSelectionChange() {
    return new Promise(resolve => {
      document.addEventListener("selectionchange", () => {
        resolve();
      }, {once: true});
    });
  }

  async function doTest(aDescription, aInnerHTML) {
    content.innerHTML = aInnerHTML;
    let description = aDescription + ": ";
    let target = document.getElementById("target");
    target.style.position = "absolute";

    async function testPositioner(aDeltaX, aDeltaY) {
      ok(true, description + "testPositioner(" + [aDeltaX, aDeltaY].join(", ") + ")");

      // Reset the position of the target.
      target.style.top = "50px";
      target.style.left = "50px";

      // Click on the target to show the positioner.
      let promiseSelectionChangeEvent = waitForSelectionChange();
      synthesizeMouseAtCenter(target, {});
      await promiseSelectionChangeEvent;

      let rect = target.getBoundingClientRect();

      ok(target.hasAttribute("_moz_abspos"),
         description + "While enableAbsolutePositionEditing is enabled, the positioner should appear");

      // left is abs positioned element's left + margin-left + border-left-width + 12.
      // XXX Perhaps, we need to add border-left-width here if you add new test to have thick border.
      const kPositionerX = 18;
      // top is abs positioned element's top + margin-top + border-top-width - 14.
      // XXX Perhaps, we need to add border-top-width here if you add new test to have thick border.
      const kPositionerY = -7;

      let beforeInputEventExpected = true;
      let beforeInputFired = false;
      let inputEventExpected = true;
      let inputFired = false;
      function onBeforeInput(aEvent) {
        beforeInputFired = true;
        aEvent.preventDefault();  // For making sure this preventDefault() call does not cancel the operation.
        if (!beforeInputEventExpected) {
          ok(false, '"beforeinput" event should not be fired after stopping resizing');
          return;
        }
        ok(aEvent instanceof InputEvent,
           '"beforeinput" event for position changing of absolute position should be dispatched with InputEvent interface');
        is(aEvent.cancelable, false,
           '"beforeinput" event for position changing of absolute position container should not be cancelable');
        is(aEvent.bubbles, true,
           '"beforeinput" event for position changing of absolute position should always bubble');
        is(aEvent.inputType, "",
           'inputType of "beforeinput" event for position changing of absolute position should be empty string');
        is(aEvent.data, null,
           'data of "beforeinput" event for position changing of absolute position should be null');
        is(aEvent.dataTransfer, null,
           'dataTransfer of "beforeinput" event for position changing of absolute position should be null');
        let targetRanges = aEvent.getTargetRanges();
        let selection = document.getSelection();
        is(targetRanges.length, selection.rangeCount,
           'getTargetRanges() of "beforeinput" event for position changing of absolute position should return selection ranges');
        if (targetRanges.length === selection.rangeCount) {
          for (let i = 0; i < selection.rangeCount; i++) {
            let range = selection.getRangeAt(i);
            is(targetRanges[i].startContainer, range.startContainer,
               `startContainer of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match`);
            is(targetRanges[i].startOffset, range.startOffset,
               `startOffset of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match`);
            is(targetRanges[i].endContainer, range.endContainer,
               `endContainer of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match`);
            is(targetRanges[i].endOffset, range.endOffset,
               `endOffset of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match`);
          }
        }
      }
      function onInput(aEvent) {
        inputFired = true;
        if (!inputEventExpected) {
          ok(false, '"input" event should not be fired after stopping resizing');
          return;
        }
        ok(aEvent instanceof InputEvent,
           '"input" event for position changing of absolute position container should be dispatched with InputEvent interface');
        is(aEvent.cancelable, false,
           '"input" event for position changing of absolute position container should be never cancelable');
        is(aEvent.bubbles, true,
           '"input" event for position changing of absolute position should always bubble');
        is(aEvent.inputType, "",
           'inputType of "input" event for position changing of absolute position should be empty string');
        is(aEvent.data, null,
           'data of "input" event for position changing of absolute position should be null');
        is(aEvent.dataTransfer, null,
           'dataTransfer of "input" event for position changing of absolute position should be null');
        is(aEvent.getTargetRanges().length, 0,
           'getTargetRanges() of "input" event for position changing of absolute position should return empty array');
      }

      content.addEventListener("beforeinput", onBeforeInput);
      content.addEventListener("input", onInput);

      // Click on the positioner.
      synthesizeMouse(target, kPositionerX, kPositionerY, {type: "mousedown"});
      // Drag it delta pixels.
      synthesizeMouse(target, kPositionerX + aDeltaX, kPositionerY + aDeltaY, {type: "mousemove"});
      // Release the mouse button
      synthesizeMouse(target, kPositionerX + aDeltaX, kPositionerY + aDeltaY, {type: "mouseup"});

      ok(beforeInputFired, `${description}"beforeinput" event should be fired by moving absolute position container`);
      ok(inputFired, `${description}"input" event should be fired by moving absolute position container`);

      beforeInputEventExpected = false;
      inputEventExpected = false;

      // Move the mouse delta more pixels to the same direction to make sure that the
      // positioning operation has stopped.
      synthesizeMouse(target, kPositionerX + aDeltaX * 2, kPositionerY + aDeltaY * 2, {type: "mousemove"});
      // Click outside of the image to hide the positioner.
      synthesizeMouseAtCenter(outOfEditor, {});

      content.removeEventListener("beforeinput", onBeforeInput);
      content.removeEventListener("input", onInput);

      // Get the new dimensions for the absolute positioned element.
      let newRect = target.getBoundingClientRect();
      isfuzzy(newRect.x, rect.x + aDeltaX, 1, description + "The left should be increased by " + aDeltaX + " pixels");
      isfuzzy(newRect.y, rect.y + aDeltaY, 1, description + "The top should be increased by " + aDeltaY + "pixels");
    }

    await testPositioner( 10, 10);
    await testPositioner( 10, -10);
    await testPositioner(-10, 10);
    await testPositioner(-10, -10);
  }

  const kTests = [
    { description: "Positioner for <img>",
      innerHTML: "<img id=\"target\" src=\"green.png\">",
    },
    { description: "Positioner for <table>",
      innerHTML: "<table id=\"target\" border><tr><td>cell</td><td>cell</td></tr></table>",
    },
    { description: "Positioner for <div>",
      innerHTML: "<div id=\"target\">div element</div>",
    },
  ];

  for (const kTest of kTests) {
    await doTest(kTest.description, kTest.innerHTML);
  }
  content.innerHTML = "";
  SimpleTest.finish();
});
</script>
</pre>
</body>
</html>