async function send_message_to_iframe(iframe, message, reply) { if (reply === undefined) { reply = 'success'; } return new Promise((resolve, reject) => { let messageHandler = e => { if (e.data.command !== message.command) { return; } window.removeEventListener('message', messageHandler); if (e.data.result === reply) { resolve(); } else { reject(); } } window.addEventListener('message', messageHandler); iframe.contentWindow.postMessage(message, '*'); }); } function run_generic_sensor_iframe_tests(sensorName) { const sensorType = self[sensorName]; const featurePolicies = get_feature_policies_for_sensor(sensorName); sensor_test(async t => { assert_true(sensorName in self); const iframe = document.createElement('iframe'); iframe.allow = featurePolicies.join(';') + ';'; iframe.src = 'https://{{domains[www1]}}:{{ports[https][0]}}/generic-sensor/resources/iframe_sensor_handler.html'; // Create sensor inside cross-origin nested browsing context. const iframeLoadWatcher = new EventWatcher(t, iframe, 'load'); document.body.appendChild(iframe); await iframeLoadWatcher.wait_for('load'); await send_message_to_iframe(iframe, {command: 'create_sensor', type: sensorName}); // Focus on the main frame and test that sensor receives readings. window.focus(); const sensor = new sensorType(); const sensorWatcher = new EventWatcher(t, sensor, ['reading', 'error']); sensor.start(); await sensorWatcher.wait_for('reading'); const cachedTimeStamp = sensor.timestamp; // Focus on the cross-origin frame and verify that sensor reading updates in // the top level browsing context are suspended. iframe.contentWindow.focus(); await send_message_to_iframe(iframe, {command: 'start_sensor'}); assert_equals(cachedTimeStamp, sensor.timestamp); // Focus on the main frame, verify that sensor reading updates are resumed. window.focus(); await sensorWatcher.wait_for('reading'); assert_greater_than(sensor.timestamp, cachedTimeStamp); sensor.stop(); // Verify that sensor in cross-origin frame is suspended. await send_message_to_iframe(iframe, {command: 'is_sensor_suspended'}, true); await send_message_to_iframe(iframe, {command: 'reset_sensor_backend'}); // Remove iframe from main document. iframe.parentNode.removeChild(iframe); }, `${sensorName}: sensor is suspended and resumed when focus traverses from\ to cross-origin frame`); sensor_test(async t => { assert_true(sensorName in self); const iframe = document.createElement('iframe'); iframe.allow = featurePolicies.join(';') + ';'; iframe.src = 'https://{{host}}:{{ports[https][0]}}/generic-sensor/resources/iframe_sensor_handler.html'; // Create sensor inside same-origin nested browsing context. const iframeLoadWatcher = new EventWatcher(t, iframe, 'load'); document.body.appendChild(iframe); await iframeLoadWatcher.wait_for('load'); await send_message_to_iframe(iframe, {command: 'create_sensor', type: sensorName}); // Focus on main frame and test that sensor receives readings. window.focus(); const sensor = new sensorType({ // generic_sensor_mocks.js uses a default frequency of 5Hz for sensors. // We deliberately use a higher frequency here to make it easier to spot // spurious, unexpected 'reading' events caused by the main frame's // sensor not stopping early enough. // TODO(rakuco): Create a constant with the 5Hz default frequency instead // of using magic numbers. frequency: 15 }); const sensorWatcher = new EventWatcher(t, sensor, ['reading', 'error']); sensor.start(); await sensorWatcher.wait_for('reading'); let cachedTimeStamp = sensor.timestamp; // Stop sensor in main frame, so that sensorWatcher would not receive // readings while sensor in iframe is started. Sensors that are active and // belong to the same-origin context are not suspended automatically when // focus changes to another same-origin iframe, so if we do not explicitly // stop them we may receive extra 'reading' events that cause the test to // fail (see e.g. https://crbug.com/857520). sensor.stop(); iframe.contentWindow.focus(); await send_message_to_iframe(iframe, {command: 'start_sensor'}); // Start sensor on main frame, verify that readings are updated. window.focus(); sensor.start(); await sensorWatcher.wait_for('reading'); assert_greater_than(sensor.timestamp, cachedTimeStamp); cachedTimeStamp = sensor.timestamp; sensor.stop(); // Verify that sensor in nested browsing context is not suspended. await send_message_to_iframe(iframe, {command: 'is_sensor_suspended'}, false); // Verify that sensor in top level browsing context is receiving readings. iframe.contentWindow.focus(); sensor.start(); await sensorWatcher.wait_for('reading'); assert_greater_than(sensor.timestamp, cachedTimeStamp); sensor.stop(); await send_message_to_iframe(iframe, {command: 'reset_sensor_backend'}); // Remove iframe from main document. iframe.parentNode.removeChild(iframe); }, `${sensorName}: sensor is not suspended when focus traverses from\ to same-origin frame`); sensor_test(async t => { assert_true(sensorName in self); const iframe = document.createElement('iframe'); iframe.allow = featurePolicies.join(';') + ';'; iframe.src = 'https://{{host}}:{{ports[https][0]}}/generic-sensor/resources/iframe_sensor_handler.html'; // Create sensor in the iframe (we do not care whether this is a // cross-origin nested context in this test). const iframeLoadWatcher = new EventWatcher(t, iframe, 'load'); document.body.appendChild(iframe); await iframeLoadWatcher.wait_for('load'); await send_message_to_iframe(iframe, {command: 'create_sensor', type: sensorName}); iframe.contentWindow.focus(); await send_message_to_iframe(iframe, {command: 'start_sensor'}); // Remove iframe from main document and change focus. When focus changes, // we need to determine whether a sensor must have its execution suspended // or resumed (section 4.2.3, "Focused Area" of the Generic Sensor API // spec). In Blink, this involves querying a frame, which might no longer // exist at the time of the check. // Note that we cannot send the "reset_sensor_backend" command because the // iframe is discarded with the removeChild call. iframe.parentNode.removeChild(iframe); window.focus(); }, `${sensorName}: losing a document's frame with an active sensor does not crash`); }