forked from mirrors/gecko-dev
		
	Automatic update from web-platform-testssensors: Ensure a document without an associated frame does not crash
Commit d1034e1e6 ("sensors: Make SensorProviderProxy supplement Document,
not LocalFrame") tied a sensor's lifetime to a document rather than a frame,
but we continued to assume Document::GetFrame() would never return null.
This is not true, as evidenced by the crash reports in bug 889754, caused by
SensorProxy::ShouldSuspendUpdates() trying to invoke methods on a LocalFrame
that can actually be a nullptr.
The original backtrace in the bug report seems to come from sensor creation,
but it is easier to trigger the same crash with a focus change after
destroying a sensor's document's frame.
Bug: 861675, 889754
Change-Id: Idb9ed5c18a655e113e2fb76cde6615aeefcc544a
Reviewed-on: https://chromium-review.googlesource.com/1256826
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Raphael Kubo da Costa (CET) <raphael.kubo.da.costa@intel.com>
Cr-Commit-Position: refs/heads/master@{#595958}
--
wpt-commits: 27d87552c9a67481fb2d6ca82a71622c24ce7090
wpt-pr: 13316
		
	
			
		
			
				
	
	
		
			160 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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`);
 | 
						|
}
 |