blob: 45f6644b776e0b7529a398ae9895f2c80517d565 [file] [log] [blame]
// Copyright 2017 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 "content/browser/geolocation/geolocation_service_impl.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "content/public/browser/permission_manager.h"
#include "content/public/browser/permission_type.h"
#include "content/public/common/content_features.h"
#include "content/public/test/navigation_simulator.h"
#include "content/test/mock_permission_manager.h"
#include "content/test/test_render_frame_host.h"
#include "device/geolocation/geolocation_context.h"
#include "services/device/public/interfaces/geolocation.mojom.h"
#include "services/device/public/interfaces/geoposition.mojom.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/mojom/feature_policy/feature_policy.mojom.h"
using base::test::ScopedFeatureList;
using blink::mojom::PermissionStatus;
using device::GeolocationContext;
using device::mojom::GeolocationPtr;
using device::mojom::GeopositionPtr;
using blink::mojom::GeolocationService;
using blink::mojom::GeolocationServicePtr;
typedef base::Callback<void(PermissionStatus)> PermissionCallback;
namespace content {
namespace {
double kMockLatitude = 1.0;
double kMockLongitude = 10.0;
GURL kMainUrl = GURL("https://www.google.com/maps");
GURL kEmbeddedUrl = GURL("https://embeddables.com/someframe");
class TestPermissionManager : public MockPermissionManager {
public:
TestPermissionManager()
: request_id_(PermissionManager::kNoPendingOperation) {}
~TestPermissionManager() override = default;
int RequestPermission(PermissionType permissions,
RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
bool user_gesture,
const PermissionCallback& callback) override {
EXPECT_EQ(permissions, PermissionType::GEOLOCATION);
EXPECT_TRUE(user_gesture);
request_callback_.Run(callback);
return request_id_;
}
void SetRequestId(int request_id) { request_id_ = request_id; }
void SetRequestCallback(
const base::Callback<void(const PermissionCallback&)>& request_callback) {
request_callback_ = request_callback;
}
private:
int request_id_;
base::Callback<void(const PermissionCallback&)> request_callback_;
};
class GeolocationServiceTest : public RenderViewHostImplTestHarness {
protected:
GeolocationServiceTest() {}
~GeolocationServiceTest() override {}
void SetUp() override {
RenderViewHostImplTestHarness::SetUp();
NavigateAndCommit(kMainUrl);
permission_manager_.reset(new TestPermissionManager);
}
void CreateEmbeddedFrameAndGeolocationService(bool allow_via_feature_policy) {
if (allow_via_feature_policy) {
RenderFrameHostTester::For(main_rfh())
->SimulateFeaturePolicyHeader(
blink::mojom::FeaturePolicyFeature::kGeolocation,
std::vector<url::Origin>{url::Origin::Create(kEmbeddedUrl)});
}
RenderFrameHost* embedded_rfh =
RenderFrameHostTester::For(main_rfh())->AppendChild("");
RenderFrameHostTester::For(embedded_rfh)->InitializeRenderFrameIfNeeded();
auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
kEmbeddedUrl, embedded_rfh);
navigation_simulator->Commit();
embedded_rfh = navigation_simulator->GetFinalRenderFrameHost();
service_.reset(new GeolocationServiceImpl(
&context_, permission_manager_.get(), embedded_rfh));
service_->Bind(mojo::MakeRequest(&service_ptr_));
}
GeolocationServicePtr* service_ptr() { return &service_ptr_; }
GeolocationService* service() { return &*service_ptr_; }
GeolocationContext* context() { return &context_; }
TestPermissionManager* permission_manager() {
return permission_manager_.get();
}
private:
// The |permission_manager_| needs to come before the |service_| since
// GeolocationService calls PermissionManager in its destructor.
std::unique_ptr<TestPermissionManager> permission_manager_;
std::unique_ptr<GeolocationServiceImpl> service_;
GeolocationServicePtr service_ptr_;
GeolocationContext context_;
DISALLOW_COPY_AND_ASSIGN(GeolocationServiceTest);
};
} // namespace
TEST_F(GeolocationServiceTest, PermissionGrantedPolicyViolation) {
// The embedded frame is not whitelisted.
ScopedFeatureList feature_list;
feature_list.InitFromCommandLine(
features::kUseFeaturePolicyForPermissions.name, std::string());
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/false);
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& callback) {
ADD_FAILURE() << "Permissions checked unexpectedly.";
}));
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
base::RunLoop loop;
geolocation.set_connection_error_handler(loop.QuitClosure());
geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
ADD_FAILURE() << "Position updated unexpectedly";
}));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.Run();
}
TEST_F(GeolocationServiceTest, PermissionGrantedNoPolicyViolation) {
// Whitelist the embedded frame.
ScopedFeatureList feature_list;
feature_list.InitFromCommandLine(
features::kUseFeaturePolicyForPermissions.name, std::string());
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& callback) {
callback.Run(PermissionStatus::GRANTED);
}));
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
base::RunLoop loop;
geolocation.set_connection_error_handler(base::BindOnce(
[] { ADD_FAILURE() << "Connection error handler called unexpectedly"; }));
geolocation->QueryNextPosition(base::BindOnce(
[](base::Closure callback, GeopositionPtr geoposition) {
EXPECT_DOUBLE_EQ(kMockLatitude, geoposition->latitude);
EXPECT_DOUBLE_EQ(kMockLongitude, geoposition->longitude);
callback.Run();
},
loop.QuitClosure()));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.Run();
}
TEST_F(GeolocationServiceTest, PermissionGrantedSync) {
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& callback) {
callback.Run(PermissionStatus::GRANTED);
}));
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
base::RunLoop loop;
geolocation.set_connection_error_handler(base::BindOnce(
[] { ADD_FAILURE() << "Connection error handler called unexpectedly"; }));
geolocation->QueryNextPosition(base::BindOnce(
[](base::Closure callback, GeopositionPtr geoposition) {
EXPECT_DOUBLE_EQ(kMockLatitude, geoposition->latitude);
EXPECT_DOUBLE_EQ(kMockLongitude, geoposition->longitude);
callback.Run();
},
loop.QuitClosure()));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.Run();
}
TEST_F(GeolocationServiceTest, PermissionDeniedSync) {
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& callback) {
callback.Run(PermissionStatus::DENIED);
}));
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
base::RunLoop loop;
geolocation.set_connection_error_handler(loop.QuitClosure());
geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
ADD_FAILURE() << "Position updated unexpectedly";
}));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.Run();
}
TEST_F(GeolocationServiceTest, PermissionGrantedAsync) {
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
permission_manager()->SetRequestId(42);
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& permission_callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(
[](const PermissionCallback& callback) {
callback.Run(PermissionStatus::GRANTED);
},
permission_callback));
}));
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
base::RunLoop loop;
geolocation.set_connection_error_handler(base::BindOnce(
[] { ADD_FAILURE() << "Connection error handler called unexpectedly"; }));
geolocation->QueryNextPosition(base::BindOnce(
[](base::Closure callback, GeopositionPtr geoposition) {
EXPECT_DOUBLE_EQ(kMockLatitude, geoposition->latitude);
EXPECT_DOUBLE_EQ(kMockLongitude, geoposition->longitude);
callback.Run();
},
loop.QuitClosure()));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.Run();
}
TEST_F(GeolocationServiceTest, PermissionDeniedAsync) {
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
permission_manager()->SetRequestId(42);
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& permission_callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(
[](const PermissionCallback& callback) {
callback.Run(PermissionStatus::DENIED);
},
permission_callback));
}));
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
base::RunLoop loop;
geolocation.set_connection_error_handler(loop.QuitClosure());
geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
ADD_FAILURE() << "Position updated unexpectedly";
}));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.Run();
}
TEST_F(GeolocationServiceTest, ServiceClosedBeforePermissionResponse) {
CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
permission_manager()->SetRequestId(42);
GeolocationPtr geolocation;
service()->CreateGeolocation(mojo::MakeRequest(&geolocation), true);
// Don't immediately respond to the request.
permission_manager()->SetRequestCallback(
base::Bind([](const PermissionCallback& callback) {}));
base::RunLoop loop;
service_ptr()->reset();
geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
ADD_FAILURE() << "Position updated unexpectedly";
}));
auto mock_geoposition = device::mojom::Geoposition::New();
mock_geoposition->latitude = kMockLatitude;
mock_geoposition->longitude = kMockLongitude;
context()->SetOverride(std::move(mock_geoposition));
loop.RunUntilIdle();
}
} // namespace content