| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/threading/platform_thread.h" |
| #include "build/build_config.h" |
| #include "content/browser/generic_sensor/sensor_provider_proxy_impl.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/content_mock_cert_verifier.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "content/shell/browser/shell_javascript_dialog_manager.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/system/buffer.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "services/device/public/cpp/generic_sensor/platform_sensor_configuration.h" |
| #include "services/device/public/cpp/generic_sensor/sensor_reading.h" |
| #include "services/device/public/cpp/test/fake_sensor_and_provider.h" |
| #include "services/device/public/mojom/sensor.mojom.h" |
| #include "services/device/public/mojom/sensor_provider.mojom.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| using device::FakeSensorProvider; |
| |
| class DeviceSensorBrowserTest : public ContentBrowserTest { |
| public: |
| DeviceSensorBrowserTest() { |
| SensorProviderProxyImpl::OverrideSensorProviderBinderForTesting( |
| base::BindRepeating(&DeviceSensorBrowserTest::BindSensorProvider, |
| base::Unretained(this))); |
| } |
| |
| ~DeviceSensorBrowserTest() override { |
| SensorProviderProxyImpl::OverrideSensorProviderBinderForTesting( |
| base::NullCallback()); |
| } |
| |
| void DelayAndQuit(base::TimeDelta delay) { |
| base::PlatformThread::Sleep(delay); |
| base::RunLoop::QuitCurrentWhenIdleDeprecated(); |
| } |
| |
| void WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta delay) { |
| ShellJavaScriptDialogManager* dialog_manager = |
| static_cast<ShellJavaScriptDialogManager*>( |
| shell()->GetJavaScriptDialogManager(shell()->web_contents())); |
| |
| scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner(); |
| dialog_manager->set_dialog_request_callback(base::BindOnce( |
| &DeviceSensorBrowserTest::DelayAndQuit, base::Unretained(this), delay)); |
| runner->Run(); |
| } |
| |
| std::unique_ptr<FakeSensorProvider> sensor_provider_; |
| std::unique_ptr<net::EmbeddedTestServer> https_embedded_test_server_; |
| |
| private: |
| void SetUpOnMainThread() override { |
| ContentBrowserTest::SetUpOnMainThread(); |
| mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); |
| |
| https_embedded_test_server_ = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| // Serve both a.com and b.com (and any other domain). |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(https_embedded_test_server_->InitializeAndListen()); |
| content::SetupCrossSiteRedirector(https_embedded_test_server_.get()); |
| https_embedded_test_server_->ServeFilesFromSourceDirectory( |
| "content/test/data/device_sensors"); |
| https_embedded_test_server_->StartAcceptingConnections(); |
| |
| sensor_provider_ = std::make_unique<FakeSensorProvider>(); |
| sensor_provider_->SetAccelerometerData(4, 5, 6); |
| sensor_provider_->SetLinearAccelerationSensorData(1, 2, 3); |
| sensor_provider_->SetGyroscopeData(7, 8, 9); |
| sensor_provider_->SetRelativeOrientationSensorData(1, 2, 3); |
| sensor_provider_->SetAbsoluteOrientationSensorData(4, 5, 6); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| ContentBrowserTest::SetUpCommandLine(command_line); |
| mock_cert_verifier_.SetUpCommandLine(command_line); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| ContentBrowserTest::SetUpInProcessBrowserTestFixture(); |
| mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); |
| } |
| |
| void TearDownInProcessBrowserTestFixture() override { |
| ContentBrowserTest::TearDownInProcessBrowserTestFixture(); |
| mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); |
| } |
| |
| void BindSensorProvider( |
| mojo::PendingReceiver<device::mojom::SensorProvider> receiver) { |
| sensor_provider_->Bind(std::move(receiver)); |
| } |
| |
| content::ContentMockCertVerifier mock_cert_verifier_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, OrientationTest) { |
| // The test page will register an event handler for orientation events, |
| // expects to get an event with fake values, then removes the event |
| // handler and navigates to #pass. |
| GURL test_url = GetTestUrl("device_sensors", "device_orientation_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| OrientationFallbackToAbsoluteTest) { |
| // The test page will register an event handler for orientation events, |
| // expects to get an event with fake values, then removes the event |
| // handler and navigates to #pass. |
| // |
| // Here the relative orientation sensor is not available, but the absolute |
| // orientation sensor is available, so orientation event will provide the |
| // absolute orientation data. |
| sensor_provider_->set_relative_orientation_sensor_is_available(false); |
| sensor_provider_->set_absolute_orientation_sensor_is_available(true); |
| GURL test_url = GetTestUrl( |
| "device_sensors", "device_orientation_fallback_to_absolute_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, OrientationAbsoluteTest) { |
| // The test page will register an event handler for absolute orientation |
| // events, expects to get an event with fake values, then removes the event |
| // handler and navigates to #pass. |
| GURL test_url = |
| GetTestUrl("device_sensors", "device_orientation_absolute_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, MotionTest) { |
| // The test page will register an event handler for motion events, |
| // expects to get an event with fake values, then removes the event |
| // handler and navigates to #pass. |
| GURL test_url = GetTestUrl("device_sensors", "device_motion_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, OrientationNullTest) { |
| // The test page registers an event handler for orientation events and |
| // expects to get an event with null values, because no sensor data can be |
| // provided. |
| // |
| // Here it needs to set both the relative and absolute orientation sensors |
| // unavailable, since orientation event will fallback to absolute orientation |
| // sensor if it is available. |
| sensor_provider_->set_relative_orientation_sensor_is_available(false); |
| sensor_provider_->set_absolute_orientation_sensor_is_available(false); |
| GURL test_url = |
| GetTestUrl("device_sensors", "device_orientation_null_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, OrientationAbsoluteNullTest) { |
| // The test page registers an event handler for absolute orientation events |
| // and expects to get an event with null values, because no sensor data can be |
| // provided. |
| sensor_provider_->set_absolute_orientation_sensor_is_available(false); |
| GURL test_url = GetTestUrl("device_sensors", |
| "device_orientation_absolute_null_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, MotionNullTest) { |
| // The test page registers an event handler for motion events and |
| // expects to get an event with null values, because no sensor data can be |
| // provided. |
| sensor_provider_->set_accelerometer_is_available(false); |
| sensor_provider_->set_linear_acceleration_sensor_is_available(false); |
| sensor_provider_->set_gyroscope_is_available(false); |
| GURL test_url = GetTestUrl("device_sensors", "device_motion_null_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| // Disabled due to flakiness: https://crbug.com/783891 |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| MotionOnlySomeSensorsAreAvailableTest) { |
| // The test page registers an event handler for motion events and |
| // expects to get an event with only the gyroscope and linear acceleration |
| // sensor values, because no accelerometer values can be provided. |
| sensor_provider_->set_accelerometer_is_available(false); |
| GURL test_url = |
| GetTestUrl("device_sensors", |
| "device_motion_only_some_sensors_are_available_test.html"); |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2); |
| |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, NullTestWithAlert) { |
| // The test page registers an event handlers for motion/orientation events and |
| // expects to get events with null values. The test raises a modal alert |
| // dialog with a delay to test that the one-off null-events still propagate to |
| // window after the alert is dismissed and the callbacks are invoked which |
| // eventually navigate to #pass. |
| sensor_provider_->set_relative_orientation_sensor_is_available(false); |
| sensor_provider_->set_absolute_orientation_sensor_is_available(false); |
| sensor_provider_->set_accelerometer_is_available(false); |
| sensor_provider_->set_linear_acceleration_sensor_is_available(false); |
| sensor_provider_->set_gyroscope_is_available(false); |
| TestNavigationObserver same_tab_observer(shell()->web_contents(), 2); |
| |
| GURL test_url = |
| GetTestUrl("device_sensors", "device_sensors_null_test_with_alert.html"); |
| shell()->LoadURL(test_url); |
| |
| // TODO(timvolodine): investigate if it is possible to test this without |
| // delay, crbug.com/360044. |
| WaitForAlertDialogAndQuitAfterDelay(base::Milliseconds(500)); |
| |
| same_tab_observer.Wait(); |
| EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| DeviceMotionCrossOriginIframeForbiddenTest) { |
| // Main frame is on a.com, iframe is on b.com. |
| GURL main_frame_url = |
| https_embedded_test_server_->GetURL("a.com", "/cross_origin_iframe.html"); |
| GURL iframe_url = https_embedded_test_server_->GetURL( |
| "b.com", "/device_motion_test.html?failure_timeout=100"); |
| |
| // Wait for the main frame, subframe, and the #pass/#fail commits. |
| TestNavigationObserver navigation_observer(shell()->web_contents(), 3); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), main_frame_url)); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), |
| "cross_origin_iframe", iframe_url)); |
| |
| navigation_observer.Wait(); |
| |
| content::RenderFrameHost* iframe = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(iframe); |
| EXPECT_EQ("fail", iframe->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| DeviceMotionCrossOriginIframeAllowedTest) { |
| // Main frame is on a.com, iframe is on b.com. |
| GURL main_frame_url = |
| https_embedded_test_server_->GetURL("a.com", "/cross_origin_iframe.html"); |
| GURL iframe_url = |
| https_embedded_test_server_->GetURL("b.com", "/device_motion_test.html"); |
| |
| // Wait for the main frame, subframe, and the #pass/#fail commits. |
| TestNavigationObserver navigation_observer(shell()->web_contents(), 3); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), main_frame_url)); |
| // Now allow 'accelerometer' and 'gyroscope' policy features. |
| EXPECT_TRUE(ExecJs(shell(), |
| "document.getElementById('cross_origin_iframe')." |
| "allow='accelerometer; gyroscope'")); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), |
| "cross_origin_iframe", iframe_url)); |
| |
| navigation_observer.Wait(); |
| |
| content::RenderFrameHost* iframe = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(iframe); |
| EXPECT_EQ("pass", iframe->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| DeviceOrientationCrossOriginIframeForbiddenTest) { |
| // Main frame is on a.com, iframe is on b.com. |
| GURL main_frame_url = |
| https_embedded_test_server_->GetURL("a.com", "/cross_origin_iframe.html"); |
| GURL iframe_url = https_embedded_test_server_->GetURL( |
| "b.com", "/device_orientation_test.html?failure_timeout=100"); |
| |
| // Wait for the main frame, subframe, and the #pass/#fail commits. |
| TestNavigationObserver navigation_observer(shell()->web_contents(), 3); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), main_frame_url)); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), |
| "cross_origin_iframe", iframe_url)); |
| |
| navigation_observer.Wait(); |
| |
| content::RenderFrameHost* iframe = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(iframe); |
| EXPECT_EQ("fail", iframe->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| DeviceOrientationCrossOriginIframeAllowedTest) { |
| // Main frame is on a.com, iframe is on b.com. |
| GURL main_frame_url = |
| https_embedded_test_server_->GetURL("a.com", "/cross_origin_iframe.html"); |
| GURL iframe_url = https_embedded_test_server_->GetURL( |
| "b.com", "/device_orientation_test.html"); |
| |
| // Wait for the main frame, subframe, and the #pass/#fail commits. |
| TestNavigationObserver navigation_observer(shell()->web_contents(), 3); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), main_frame_url)); |
| // Now allow 'accelerometer' and 'gyroscope' policy features. |
| EXPECT_TRUE(ExecJs(shell(), |
| "document.getElementById('cross_origin_iframe')." |
| "allow='accelerometer; gyroscope'")); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), |
| "cross_origin_iframe", iframe_url)); |
| |
| navigation_observer.Wait(); |
| |
| content::RenderFrameHost* iframe = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(iframe); |
| EXPECT_EQ("pass", iframe->GetLastCommittedURL().ref()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DeviceSensorBrowserTest, |
| DeviceOrientationPermissionsPolicyWarning) { |
| // Main frame is on a.com, iframe is on b.com. |
| GURL main_frame_url = |
| https_embedded_test_server_->GetURL("a.com", "/cross_origin_iframe.html"); |
| GURL iframe_url = https_embedded_test_server_->GetURL( |
| "b.com", "/device_orientation_absolute_test.html"); |
| |
| const char kWarningMessage[] = |
| "The deviceorientationabsolute events are blocked by " |
| "permissions policy. See " |
| "https://github.com/w3c/webappsec-permissions-policy/blob/master/" |
| "features.md#sensor-features"; |
| |
| WebContentsConsoleObserver console_observer(shell()->web_contents()); |
| console_observer.SetPattern(kWarningMessage); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), main_frame_url)); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), |
| "cross_origin_iframe", iframe_url)); |
| |
| console_observer.Wait(); |
| EXPECT_EQ(kWarningMessage, console_observer.GetMessageAt(0u)); |
| } |
| |
| } // namespace |
| |
| } // namespace content |