summaryrefslogtreecommitdiffstats
path: root/dom/base/test/fullscreen/file_fullscreen-api.html
blob: 9661b10de9157f7c09a8fb6fe53b59ae72ff4332 (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
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=545812

Test DOM full-screen API.

-->
<head>
  <title>Test for Bug 545812</title>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="application/javascript" src="file_fullscreen-utils.js"></script>
  <style>
  body {
    background-color: black;
  }
  </style>
</head>
<body>
<div id="fullscreen-element"></div>
<script type="application/javascript">

/** Test for Bug 545812 **/

function ok(condition, msg) {
  opener.ok(condition, "[fullscreen] " + msg);
}

function is(a, b, msg) {
  opener.is(a, b, "[fullscreen] " + msg);
}

/*
<html>
  <body onload='document.body.requestFullscreen();'>
  <iframe id='inner-frame'></iframe>
  </body>
</html>
*/
var iframeContents = "<html><body onload='parent.SimpleTest.waitForFocus(function(){document.body.requestFullscreen();});'><iframe id='inner-frame'></iframe></body></html>";

var iframe = null;
var outOfDocElement = null;
var inDocElement = null;
var container = null;
var button = null;


function sendMouseClick(element) {
  synthesizeMouseAtCenter(element, {});
}

function assertPromiseResolved(promise, msg) {
  let { state, value } = SpecialPowers.PromiseDebugging.getState(promise);
  is(state, "fulfilled", "Promise should have been resolved " + msg);
  is(value, undefined, "Promise should be resolved with undefined " + msg);
}

function assertPromiseRejected(promise, msg) {
  let { state, reason } = SpecialPowers.PromiseDebugging.getState(promise);
  is(state, "rejected", "Promise should have been rejected " + msg);
  // XXX Actually we should be testing "instanceof TypeError", but it
  // doesn't work as expected currently. See bug 1412856.
  is(reason.name, "TypeError",
     "Promise should be rejected with TypeError " + msg);
}

const FULLSCREEN_ELEMENT = document.getElementById("fullscreen-element");
let promise;

function enter1(event) {
  is(event.target, FULLSCREEN_ELEMENT,
     "Event target should be the fullscreen element #1");
  ok(document.fullscreen, "Document should be in fullscreen");
  is(document.fullscreenElement, FULLSCREEN_ELEMENT,
     "Full-screen element should be div element.");
  ok(document.fullscreenElement.matches(":fullscreen"),
     "FSE should match :fullscreen");
  addFullscreenChangeContinuation("exit", exit1);
  FULLSCREEN_ELEMENT.remove();
  is(document.fullscreenElement, null,
     "Full-screen element should be null after removing.");
}

function exit1(event) {
  document.body.appendChild(FULLSCREEN_ELEMENT);
  is(document.fullscreenElement, null,
     "Full-screen element should still be null after re-adding former FSE.");
  is(event.target, document, "Event target should be the document #2");
  ok(!document.fullscreen, "Document should not be in fullscreen");
  is(document.fullscreenElement, null, "Full-screen element should be null.");
  iframe = document.createElement("iframe");
  iframe.allowFullscreen = true;
  addFullscreenChangeContinuation("enter", enter2);
  document.body.appendChild(iframe);
  iframe.srcdoc = iframeContents;
}

function enter2(event) {
  is(event.target, iframe,
     "Event target should be the fullscreen iframe #3");
  is(document.fullscreenElement, iframe,
     "Full-screen element should be iframe element.");
  is(iframe.contentDocument.fullscreenElement, iframe.contentDocument.body,
     "Full-screen element in subframe should be body");
  
  // The iframe's body is full-screen. Cancel full-screen in the subdocument to return
  // the full-screen element to the previous full-screen element. This causes
  // a fullscreenchange event.
  addFullscreenChangeContinuation("exit", exit2);
  promise = document.exitFullscreen();
}

function exit2() {
  is(document.fullscreenElement, null,
     "Full-screen element should have rolled back.");
  is(iframe.contentDocument.fullscreenElement, null,
     "Full-screen element in subframe should be null");
  assertPromiseResolved(promise, "in exit2");
  
  addFullscreenChangeContinuation("enter", enter3);
  promise = FULLSCREEN_ELEMENT.requestFullscreen();
}

function enter3(event) {
  is(event.target, FULLSCREEN_ELEMENT,
     "Event target should be the fullscreen element #3");
  is(document.fullscreenElement, FULLSCREEN_ELEMENT,
     "Full-screen element should be div.");
  assertPromiseResolved(promise, "in enter3");
  
  // Transplant the FSE into subdoc. Should exit full-screen.
  addFullscreenChangeContinuation("exit", exit3);
  var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
  _innerFrame.contentDocument.body.appendChild(FULLSCREEN_ELEMENT);
  is(document.fullscreenElement, null,
     "Full-screen element transplanted, should be null.");
  is(iframe.contentDocument.fullscreenElement, null,
     "Full-screen element in outer frame should be null.");
  is(_innerFrame.contentDocument.fullscreenElement, null,
     "Full-screen element in inner frame should be null.");
}

function exit3(event) {
  document.body.appendChild(FULLSCREEN_ELEMENT);
  is(event.target, document, "Event target should be the document #3");
  is(document.fullscreenElement, null, "Full-screen element should be null.");
  document.body.removeChild(iframe);
  iframe = null;

  // Do a request out of document. It should be denied.
  // Continue test in the following fullscreenerror handler.
  outOfDocElement = document.createElement("div");
  addFullscreenErrorContinuation(error1);    
  promise = outOfDocElement.requestFullscreen();
}

function error1() {
  ok(!document.fullscreenElement,
     "Requests for full-screen from not-in-doc elements should fail.");
  assertPromiseRejected(promise, "in error1");
  container = document.createElement("div");
  inDocElement = document.createElement("div");
  container.appendChild(inDocElement);
  FULLSCREEN_ELEMENT.appendChild(container);

  addFullscreenChangeContinuation("enter", enter4);
  inDocElement.requestFullscreen();
}

function enter4(event) {
  is(event.target, inDocElement,
     "Event target should be the fullscreen element #4");
  is(document.fullscreenElement, inDocElement, "FSE should be inDocElement.");

  // Remove full-screen ancestor element from document, verify it stops being reported as current FSE.
  addFullscreenChangeContinuation("exit", exit_to_arg_test_1);
  container.remove();
  is(document.fullscreenElement, null,
     "Should not have a full-screen element again.");
}

async function exit_to_arg_test_1() {
  ok(!document.fullscreenElement,
     "Should have left full-screen mode (third time).");
  addFullscreenChangeContinuation("enter", enter_from_arg_test_1);
  var threw = false;
  try {
    await FULLSCREEN_ELEMENT.requestFullscreen(123);
  } catch (e) {
    threw = true;
    // trigger normal fullscreen so that we continue
    FULLSCREEN_ELEMENT.requestFullscreen();
  }
  ok(!threw, "requestFullscreen with bogus arg (123) shouldn't throw exception");
}

function enter_from_arg_test_1() {
  ok(document.fullscreenElement,
     "Should have entered full-screen after calling with bogus (ignored) argument (fourth time)");
  addFullscreenChangeContinuation("exit", exit_to_arg_test_2);
  document.exitFullscreen();
}

async function exit_to_arg_test_2() {
  ok(!document.fullscreenElement,
     "Should have left full-screen mode (fourth time).");
  addFullscreenChangeContinuation("enter", enter_from_arg_test_2);
  var threw = false;
  try {
    await FULLSCREEN_ELEMENT.requestFullscreen({ vrDisplay: null });
  } catch (e) {
    threw = true;
    // trigger normal fullscreen so that we continue
    FULLSCREEN_ELEMENT.requestFullscreen();
  }
  ok(!threw, "requestFullscreen with { vrDisplay: null } shouldn't throw exception");
}

function enter_from_arg_test_2() {
  ok(document.fullscreenElement,
     "Should have entered full-screen after calling with vrDisplay null argument (fifth time)");
  addFullscreenChangeContinuation("exit", exit4);
  document.exitFullscreen();
}

function exit4() {
  ok(!document.fullscreenElement,
     "Should be back in non-full-screen mode (fifth time)");
  SpecialPowers.pushPrefEnv({"set":[["full-screen-api.allow-trusted-requests-only", true]]}, function() {
    addFullscreenErrorContinuation(error2);
    FULLSCREEN_ELEMENT.requestFullscreen();
  });
}

function error2() {
  ok(!document.fullscreenElement,
     "Should still be in normal mode, because calling context isn't trusted.");
  button = document.createElement("button");
  button.onclick = function() {
    FULLSCREEN_ELEMENT.requestFullscreen();
  };
  FULLSCREEN_ELEMENT.appendChild(button);
  addFullscreenChangeContinuation("enter", enter5);
  sendMouseClick(button);
}

function enter5() {
  ok(document.fullscreenElement, "Moved to full-screen after mouse click");
  addFullscreenChangeContinuation("exit", exit5);
  document.exitFullscreen();
}

function exit5() {
  ok(!document.fullscreenElement,
     "Should have left full-screen mode (last time).");
  SpecialPowers.pushPrefEnv({
    "set":[["full-screen-api.allow-trusted-requests-only", false],
           ["full-screen-api.enabled", false]]}, function() {
              is(document.fullscreenEnabled, false, "document.fullscreenEnabled should be false if full-screen-api.enabled is false");
              addFullscreenErrorContinuation(error3);
              FULLSCREEN_ELEMENT.requestFullscreen();
  });
}

function error3() {
  ok(!document.fullscreenElement,
     "Should still be in normal mode, because pref is not enabled.");

  SpecialPowers.pushPrefEnv({"set":[["full-screen-api.enabled", true]]}, function() {
    is(document.fullscreenEnabled, true, "document.fullscreenEnabled should be true if full-screen-api.enabled is true");
    opener.nextTest();
  });
}

function begin() {
  testNamespaces(() => {
    addFullscreenChangeContinuation("enter", enter1);
    FULLSCREEN_ELEMENT.requestFullscreen();
  });
}

function testNamespaces(followupTestFn) {
  let tests = [
    {allowed: false, name: "element", ns: "http://www.w3.org/XML/1998/namespace"},
    {allowed: false, name: "element", ns: "http://www.w3.org/1999/xlink"},
    {allowed: false, name: "element", ns: "http://www.w3.org/2000/svg"},
    {allowed: false, name: "element", ns: "http://www.w3.org/1998/Math/MathML"},
    {allowed: false, name: "mathml",  ns: "unknown"},
    {allowed: false, name: "svg",     ns: "unknown"},
    {allowed: true,  name: "element", ns: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"},
    {allowed: true,  name: "element", ns: "http://www.w3.org/1999/xhtml"},
    {allowed: true,  name: "svg",     ns: "http://www.w3.org/1999/xhtml"},
    {allowed: true,  name: "math",    ns: "http://www.w3.org/1999/xhtml"},
    {allowed: true,  name: "svg",     ns: "http://www.w3.org/2000/svg"},
    {allowed: true,  name: "math",    ns: "http://www.w3.org/1998/Math/MathML"},
    {allowed: true,  name: "element"},
  ];

  function runNextNamespaceTest() {
    let test = tests.shift();
    if (!test) {
      followupTestFn();
      return;
    }

    let elem = test.ns ? document.createElementNS(test.ns, test.name) :
                         document.createElement(test.name);
    document.body.appendChild(elem);

    if (test.allowed) {
      addFullscreenChangeContinuation("enter", () => {
        ok(document.fullscreen, "Document should be in fullscreen");
        is(document.fullscreenElement, elem,
           `Element named '${test.name}' in this namespace should be allowed: ${test.ns}`);
        addFullscreenChangeContinuation("exit", () => {
          document.body.removeChild(elem);
          runNextNamespaceTest();
        });
        document.exitFullscreen();
      });
    } else {
      addFullscreenErrorContinuation(() => {
        ok(!document.fullscreenElement,
           `Element named '${test.name}' in this namespace should not be allowed: ${test.ns}`);
        document.body.removeChild(elem);
        runNextNamespaceTest();
      });
    }

    SimpleTest.waitForFocus(() => elem.requestFullscreen());
  }

  runNextNamespaceTest();
}
</script>
</pre>
</body>
</html>