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
|
<!DOCTYPE html>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/utils.js"></script>
<title>Test that fenced frame notifyEvent() cannot reuse a cached event</title>
<body>
<script>
promise_test(async (t) => {
const fencedframe = await attachFencedFrameContext(
{generator_api: 'fledge'});
let notified_promise = new Promise((resolve) => {
fencedframe.element.addEventListener('fencedtreeclick', () => resolve());
});
await fencedframe.execute(() => {
window.first_click_listener = (e) => {
// Before calling notifyEvent, cache the event for later. After this
// first notifyEvent call fires, we'll attempt to re-use the cached
// event to scam additional notifyEvent calls later.
window.cached_event = e;
window.fence.notifyEvent(e);
};
document.addEventListener('click', window.first_click_listener);
});
await multiClick(10, 10, fencedframe.element);
await notified_promise;
// That notifyEvent call should have consumed user activation.
let frame_has_activation = await fencedframe.execute(() => {
return navigator.userActivation.isActive;
});
assert_false(frame_has_activation);
// Now, let's do another activation, and try to call notifyEvent on
// the cached event.
// If we click again, the frame will receive another activation. If we
// try to call notifyEvent with the cached event instead, the call should
// fail, because even though the trusted click event still exists and the
// frame has activation, the original event has finished dispatching.
let second_notified_promise = new Promise((resolve) => {
fencedframe.element.addEventListener('fencedtreeclick', () => resolve());
});
await fencedframe.execute(() => {
// Unfortunately, a failed assertion in an event handler won't fail the
// whole test. So we have to wrap the handler in a Promise that can
// be awaited and examined from the test code.
document.removeEventListener('click', window.first_click_listener);
window.activation_promise = new Promise((resolve, reject) => {
document.addEventListener('click', (e) => {
try {
assert_equals(window.cached_event.type, 'click');
assert_true(window.cached_event.isTrusted);
assert_true(navigator.userActivation.isActive);
// 0 = NONE, no longer dispatching.
assert_equals(window.cached_event.eventPhase, 0);
window.fence.notifyEvent(window.cached_event);
reject('notifyEvent() should not fire.');
} catch (err) {
if (err.name != 'SecurityError') {
reject('Unexpected error: ' + err.message);
return;
}
resolve('PASS');
}
});
});
});
await multiClick(10, 10, fencedframe.element);
// After sending the mousedown events to reactivate the frame, we have to
// wait for the fenced frame to indicate that the notifyEvent call fails.
// If we get an unexpected result, we'll unwrap the promise into an
// exception, which should fail the test.
await fencedframe.execute(async () => {
await window.activation_promise;
});
// Lastly, we need to make sure the notifyEvent call never reached the
// parent frame.
let result = await Promise.race([
second_notified_promise,
new Promise((resolve) => {
t.step_timeout(() => resolve('timeout'), 2000);
})
]);
assert_equals(result, 'timeout');
}, 'Test that fenced frame notifyEvent() cannot reuse a cached event' +
' after dispatch finishes.');
promise_test(async (t) => {
const fencedframe = await attachFencedFrameContext(
{generator_api: 'fledge'});
await fencedframe.execute(() => {
window.first_click_listener = (e) => {
window.cached_event = e;
};
document.addEventListener('click', window.first_click_listener);
});
await multiClick(10, 10, fencedframe.element);
let notified_promise = new Promise((resolve) => {
fencedframe.element.addEventListener('fencedtreeclick', () => resolve());
});
await fencedframe.execute(() => {
document.removeEventListener('click', window.first_click_listener);
window.activation_promise = new Promise((resolve, reject) => {
document.addEventListener('click', (e) => {
try {
assert_equals(window.cached_event.type, 'click');
assert_true(window.cached_event.isTrusted);
assert_true(navigator.userActivation.isActive);
// 0 = NONE, no longer dispatching.
assert_equals(window.cached_event.eventPhase, 0);
window.fence.notifyEvent(window.cached_event);
reject('notifyEvent() should not fire.');
} catch (err) {
if (err.name != 'SecurityError') {
reject('Unexpected error: ' + err.message);
return;
}
resolve('PASS');
}
});
});
});
await multiClick(10, 10, fencedframe.element);
await fencedframe.execute(async () => {
await window.activation_promise;
});
// Lastly, we need to make sure the notifyEvent call never reached the
// parent frame.
let result = await Promise.race([
notified_promise,
new Promise((resolve) => {
t.step_timeout(() => resolve('timeout'), 2000);
})
]);
assert_equals(result, 'timeout');
}, 'Test that fenced frame notifyEvent() cannot reuse a cached event' +
' after dispatch finishes, even if it has never been notified before.');
promise_test(async (t) => {
const fencedframe = await attachFencedFrameContext(
{generator_api: 'fledge'});
// First, click and cache a click event.
await fencedframe.execute(() => {
window.first_click_listener = (e) => {
window.cached_event = e;
};
document.addEventListener('click', window.first_click_listener);
});
await multiClick(10, 10, fencedframe.element);
// Next, register a new click listener to catch the cached event when it's
// re-dispatched.
await fencedframe.execute(async () => {
document.removeEventListener('click', window.first_click_listener);
window.click_promise = new Promise((resolve, reject) => {
document.addEventListener('click', (e) => {
try {
assert_true(navigator.userActivation.isActive);
window.fence.notifyEvent(e);
reject('notifyEvent() should not fire.')
} catch (err) {
if (err.name != 'SecurityError') {
reject('Unexpected error: ' + err.message);
}
resolve('PASS');
}
});
});
// We need user activation when re-dispatching the event, which will
// ensure that the re-dispatch is the sole reason for notifyEvent()
// failing to fire. We'll simulate a mousedown to provide transient
// activation, and *then* re-dispatch the cached click event, in an
// attempt to scam an additional notifyEvent().
document.addEventListener('mousedown', (e) => {
document.dispatchEvent(window.cached_event);
});
});
// Send a mousedown event to the fenced frame. We can't send a full
// click because it will interfere with the manual event dispatch.
for (let i = 0; i < 3; i++) {
await new test_driver.Actions()
.pointerMove(10, 10, {origin: fencedframe.element})
.pointerDown()
.send();
}
await fencedframe.execute(async () => {
await window.click_promise;
});
}, 'Test that re-dispatching a cached click event does not allow it to be' +
' used with notifyEvent()');
</script>
</body>
|