diff options
author | allanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 12:21:22 +0000 |
---|---|---|
committer | allanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 12:21:22 +0000 |
commit | f9f23a78dc32653b41b716110d3cc7e6d0363607 (patch) | |
tree | 7746cc6ab899dd80512bee312bf1ef9b814a4a09 | |
parent | c61a6b6d9e4315174806366425fece0da631cc7c (diff) | |
download | chromium_src-f9f23a78dc32653b41b716110d3cc7e6d0363607.zip chromium_src-f9f23a78dc32653b41b716110d3cc7e6d0363607.tar.gz chromium_src-f9f23a78dc32653b41b716110d3cc7e6d0363607.tar.bz2 |
TBR: I didn't upload the six files that I added last time round see here http://src.chromium.org/viewvc/chrome?view=rev&revision=56332
BUG=45535
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56334 0039d316-1c4b-4281-b951-d872f2087c98
6 files changed, 837 insertions, 0 deletions
diff --git a/chrome/browser/geolocation/win7_location_api_unittest_win.cc b/chrome/browser/geolocation/win7_location_api_unittest_win.cc new file mode 100644 index 0000000..b76e8dd --- /dev/null +++ b/chrome/browser/geolocation/win7_location_api_unittest_win.cc @@ -0,0 +1,335 @@ +// Copyright (c) 2010 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 <algorithm> +#include <cmath> +#include <Objbase.h> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/win_util.h" +#include "chrome/common/geoposition.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/geolocation/win7_location_api_win.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::AtLeast; +using testing::DoDefault; +using testing::Invoke; +using testing::Return; + +class MockLatLongReport : public ILatLongReport { +public: + MockLatLongReport() : ref_count_(1) { + ON_CALL(*this, GetAltitude(_)) + .WillByDefault(Invoke(this, &MockLatLongReport::GetAltitudeValid)); + ON_CALL(*this, GetAltitudeError(_)) + .WillByDefault(Invoke(this, + &MockLatLongReport::GetAltitudeErrorValid)); + ON_CALL(*this, GetErrorRadius(_)) + .WillByDefault(Invoke(this, &MockLatLongReport::GetErrorRadiusValid)); + ON_CALL(*this, GetLatitude(_)) + .WillByDefault(Invoke(this, &MockLatLongReport::GetLatitudeValid)); + ON_CALL(*this, GetLongitude(_)) + .WillByDefault(Invoke(this, &MockLatLongReport::GetLongitudeValid)); + ON_CALL(*this, GetValue(_,_)) + .WillByDefault(Invoke(this, &MockLatLongReport::GetValueValid)); + ON_CALL(*this, Release()) + .WillByDefault(Invoke(this, &MockLatLongReport::ReleaseInternal)); + ON_CALL(*this, AddRef()) + .WillByDefault(Invoke(this, &MockLatLongReport::AddRefInternal)); + } + + // ILatLongReport + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetAltitude, + HRESULT(DOUBLE*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetAltitudeError, + HRESULT(DOUBLE*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetErrorRadius, + HRESULT(DOUBLE*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetLatitude, + HRESULT(DOUBLE*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetLongitude, + HRESULT(DOUBLE*)); + // ILocationReport + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetSensorID, + HRESULT(SENSOR_ID*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetTimestamp, + HRESULT(SYSTEMTIME*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetValue, + HRESULT(REFPROPERTYKEY, PROPVARIANT*)); + // IUnknown + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + QueryInterface, + HRESULT(REFIID, void**)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + AddRef, + ULONG()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + Release, + ULONG()); + + HRESULT GetAltitudeValid(DOUBLE* altitude) { + *altitude = 20.5; + return S_OK; + } + HRESULT GetAltitudeErrorValid(DOUBLE* altitude_error) { + *altitude_error = 10.0; + return S_OK; + } + HRESULT GetErrorRadiusValid(DOUBLE* error) { + *error = 5.0; + return S_OK; + } + HRESULT GetLatitudeValid(DOUBLE* latitude) { + *latitude = 51.0; + return S_OK; + } + HRESULT GetLongitudeValid(DOUBLE* longitude) { + *longitude = -0.1; + return S_OK; + } + HRESULT GetValueValid(REFPROPERTYKEY prop_key, PROPVARIANT* prop) { + prop->dblVal = 10.0; + return S_OK; + } + + private: + ~MockLatLongReport() {} + + ULONG AddRefInternal() { + return InterlockedIncrement(&ref_count_); + } + ULONG ReleaseInternal() { + LONG new_ref_count = InterlockedDecrement(&ref_count_); + if (0 == new_ref_count) + delete this; + return new_ref_count; + } + + LONG ref_count_; +}; + +class MockReport : public ILocationReport { + public: + MockReport() : ref_count_(1) { + mock_lat_long_report_ = + new MockLatLongReport(); + ON_CALL(*this, QueryInterface(_, _)) + .WillByDefault(Invoke(this, &MockReport::QueryInterfaceValid)); + ON_CALL(*this, Release()) + .WillByDefault(Invoke(this, &MockReport::ReleaseInternal)); + ON_CALL(*this, AddRef()) + .WillByDefault(Invoke(this, &MockReport::AddRefInternal)); + } + + // ILocationReport + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetSensorID, + HRESULT(SENSOR_ID*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetTimestamp, + HRESULT(SYSTEMTIME*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetValue, + HRESULT(REFPROPERTYKEY, PROPVARIANT*)); + // IUnknown + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + QueryInterface, + HRESULT(REFIID, void**)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + AddRef, + ULONG()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + Release, + ULONG()); + + MockLatLongReport* mock_lat_long_report_; + +private: + ~MockReport() { + mock_lat_long_report_->Release(); + } + + ULONG AddRefInternal() { + return InterlockedIncrement(&ref_count_); + } + ULONG ReleaseInternal() { + LONG new_ref_count = InterlockedDecrement(&ref_count_); + if (0 == new_ref_count) + delete this; + return new_ref_count; + } + HRESULT QueryInterfaceValid(REFIID id, void** report) { + EXPECT_TRUE(id == IID_ILatLongReport); + *report = reinterpret_cast<ILatLongReport*>(mock_lat_long_report_); + mock_lat_long_report_->AddRef(); + return S_OK; + } + + LONG ref_count_; +}; + +class MockLocation : public ILocation { + public: + MockLocation() : ref_count_(1) { + mock_report_ = new MockReport(); + ON_CALL(*this, SetDesiredAccuracy(_, _)) + .WillByDefault(Return(S_OK)); + ON_CALL(*this, GetReport(_, _)) + .WillByDefault(Invoke(this, &MockLocation::GetReportValid)); + ON_CALL(*this, RequestPermissions(_, _, _, _)) + .WillByDefault(Return(S_OK)); + ON_CALL(*this, AddRef()) + .WillByDefault(Invoke(this, &MockLocation::AddRefInternal)); + ON_CALL(*this, Release()) + .WillByDefault(Invoke(this, &MockLocation::ReleaseInternal)); + } + + // ILocation + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetDesiredAccuracy, + HRESULT(REFIID, LOCATION_DESIRED_ACCURACY*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetReport, + HRESULT(REFIID, ILocationReport**)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetReportInterval, + HRESULT(REFIID, DWORD*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetReportStatus, + HRESULT(REFIID, LOCATION_REPORT_STATUS*)); + MOCK_METHOD4_WITH_CALLTYPE(STDMETHODCALLTYPE, + RequestPermissions, + HRESULT(HWND, IID*, ULONG, BOOL)); + MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, + RegisterForReport, + HRESULT(ILocationEvents*, REFIID, DWORD)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetDesiredAccuracy, + HRESULT(REFIID, LOCATION_DESIRED_ACCURACY)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetReportInterval, + HRESULT(REFIID, DWORD)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + UnregisterForReport, + HRESULT(REFIID)); + // IUnknown + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + QueryInterface, + HRESULT(REFIID, void**)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + AddRef, + ULONG()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + Release, + ULONG()); + + MockReport* mock_report_; + + private: + ~MockLocation() { + mock_report_->Release(); + } + + HRESULT GetReportValid(REFIID report_type, + ILocationReport** location_report) { + *location_report = reinterpret_cast<ILocationReport*>(mock_report_); + mock_report_->AddRef(); + return S_OK; + } + ULONG AddRefInternal() { + return InterlockedIncrement(&ref_count_); + } + ULONG ReleaseInternal() { + LONG new_ref_count = InterlockedDecrement(&ref_count_); + if (0 == new_ref_count) + delete this; + return new_ref_count; + } + + LONG ref_count_; +}; + + +HRESULT __stdcall MockPropVariantToDoubleFunction(REFPROPVARIANT propvarIn, + DOUBLE *pdblRet) { + CHECK_EQ(10.0, propvarIn.dblVal); + *pdblRet = 10.0; + return S_OK; +} + +// TODO(allanwoj): Either make mock classes into NiceMock classes +// or check every mock method call. +class GeolocationApiWin7Tests : public testing::Test { + public: + GeolocationApiWin7Tests() { + } + virtual void SetUp() { + api_.reset(CreateMock()); + report_ = locator_->mock_report_; + lat_long_report_ = report_->mock_lat_long_report_; + } + virtual void TearDown() { + locator_->Release(); + api_.reset(); + } + ~GeolocationApiWin7Tests(){ + } + protected: + Win7LocationApi* CreateMock() { + MockLocation* locator = new MockLocation(); + locator_ = locator; + return new Win7LocationApi(NULL, + &MockPropVariantToDoubleFunction, + locator); + } + + scoped_ptr<Win7LocationApi> api_; + MockLatLongReport* lat_long_report_; + MockLocation* locator_; + MockReport* report_; +}; + +TEST_F(GeolocationApiWin7Tests, PermissionDenied) { + EXPECT_CALL(*locator_, GetReport(_, _)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(E_ACCESSDENIED)); + Geoposition position; + api_->GetPosition(&position); + EXPECT_EQ(Geoposition::ERROR_CODE_PERMISSION_DENIED, + position.error_code); +} + +TEST_F(GeolocationApiWin7Tests, GetValidPosition) { + EXPECT_CALL(*locator_, GetReport(_, _)) + .Times(AtLeast(1)); + Geoposition position; + api_->GetPosition(&position); + EXPECT_TRUE(position.IsValidFix()); +} + +TEST_F(GeolocationApiWin7Tests, GetInvalidPosition) { + EXPECT_CALL(*lat_long_report_, GetLatitude(_)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(HRESULT_FROM_WIN32(ERROR_NO_DATA))); + EXPECT_CALL(*locator_, GetReport(_, _)) + .Times(AtLeast(1)); + Geoposition position; + api_->GetPosition(&position); + EXPECT_FALSE(position.IsValidFix()); +} diff --git a/chrome/browser/geolocation/win7_location_api_win.cc b/chrome/browser/geolocation/win7_location_api_win.cc new file mode 100644 index 0000000..18bda1b --- /dev/null +++ b/chrome/browser/geolocation/win7_location_api_win.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2010 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 "chrome/browser/geolocation/win7_location_api_win.h" + +#include "base/base_paths_win.h" +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/geoposition.h" + +namespace { +const double kKnotsToMetresPerSecondConversionFactor = 0.5144; + +void ConvertKnotsToMetresPerSecond(double* knots) { + *knots *= kKnotsToMetresPerSecondConversionFactor; +} + +HINSTANCE LoadWin7Library(const string16& lib_name) { + FilePath sys_dir; + PathService::Get(base::DIR_SYSTEM, &sys_dir); + return LoadLibrary(sys_dir.Append(lib_name).value().c_str()); +} +} + +Win7LocationApi::Win7LocationApi( + HINSTANCE prop_library, + PropVariantToDoubleFunction PropVariantToDouble_function, + ILocation* locator) + : prop_lib_(prop_library), + PropVariantToDouble_function_(PropVariantToDouble_function), + locator_(locator) { +} + +Win7LocationApi::~Win7LocationApi() { + if (prop_lib_ != NULL) + FreeLibrary(prop_lib_); +} + +Win7LocationApi* Win7LocationApi::Create() { + if (!CommandLine::ForCurrentProcess() + ->HasSwitch(switches::kEnableWin7Location)) + return NULL; + // Load probsys.dll + string16 lib_needed = L"propsys.dll"; + HINSTANCE prop_lib = LoadWin7Library(lib_needed); + if (!prop_lib) + return NULL; + // Get pointer to function. + PropVariantToDoubleFunction PropVariantToDouble_function; + PropVariantToDouble_function = + reinterpret_cast<PropVariantToDoubleFunction>( + GetProcAddress(prop_lib, "PropVariantToDouble")); + if (!PropVariantToDouble_function) { + FreeLibrary(prop_lib); + return NULL; + } + // Create the ILocation object that receives location reports. + HRESULT result_type; + CComPtr<ILocation> locator; + result_type = CoCreateInstance( + CLSID_Location, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&locator)); + if (!SUCCEEDED(result_type)) { + FreeLibrary(prop_lib); + return NULL; + } + IID reports_needed[] = { IID_ILatLongReport }; + result_type = locator->RequestPermissions(NULL, reports_needed, 1, TRUE); + return new Win7LocationApi(prop_lib, + PropVariantToDouble_function, + locator); +} + +void Win7LocationApi::GetPosition(Geoposition* position) { + DCHECK(position); + position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + if (!locator_) + return; + // Try to get a position fix + if (!GetPositionIfFixed(position)) + return; + position->error_code = Geoposition::ERROR_CODE_NONE; + if (!position->IsValidFix()) { + // GetPositionIfFixed returned true, yet we've not got a valid fix. + // This shouldn't happen; something went wrong in the conversion. + NOTREACHED() << "Invalid position from GetPositionIfFixed: lat,long " + << position->latitude << "," << position->longitude + << " accuracy " << position->accuracy << " time " + << position->timestamp.ToDoubleT(); + position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + position->error_message = "Bad fix from Win7 provider"; + } +} + +bool Win7LocationApi::GetPositionIfFixed(Geoposition* position) { + HRESULT result_type; + CComPtr<ILocationReport> location_report; + CComPtr<ILatLongReport> lat_long_report; + result_type = locator_->GetReport(IID_ILatLongReport, &location_report); + // Checks to see if location access is allowed. + if (result_type == E_ACCESSDENIED) + position->error_code = Geoposition::ERROR_CODE_PERMISSION_DENIED; + // Checks for any other errors while requesting a location report. + if(!SUCCEEDED(result_type)) + return false; + result_type = location_report->QueryInterface(&lat_long_report); + if(!SUCCEEDED(result_type)) + return false; + result_type = lat_long_report->GetLatitude(&position->latitude); + if(!SUCCEEDED(result_type)) + return false; + result_type = lat_long_report->GetLongitude(&position->longitude); + if(!SUCCEEDED(result_type)) + return false; + result_type = lat_long_report->GetErrorRadius(&position->accuracy); + if (!SUCCEEDED(result_type) || position->accuracy <= 0) + return false; + double temp_dbl; + result_type = lat_long_report->GetAltitude(&temp_dbl); + if (SUCCEEDED(result_type)) + position->altitude = temp_dbl; + result_type = lat_long_report->GetAltitudeError(&temp_dbl); + if (SUCCEEDED(result_type)) + position->altitude_accuracy = temp_dbl; + PROPVARIANT heading; + PropVariantInit(&heading); + result_type = lat_long_report->GetValue( + SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES, &heading); + if (SUCCEEDED(result_type)) + PropVariantToDouble_function_(heading, &position->heading); + PROPVARIANT speed; + PropVariantInit(&speed); + result_type = lat_long_report->GetValue( + SENSOR_DATA_TYPE_SPEED_KNOTS, &speed); + if (SUCCEEDED(result_type)) { + PropVariantToDouble_function_(speed, &position->speed); + ConvertKnotsToMetresPerSecond(&position->speed); + } + position->timestamp = base::Time::Now(); + return true; +} + +bool Win7LocationApi::SetHighAccuracy(bool acc) { + HRESULT result_type; + result_type = locator_->SetDesiredAccuracy(IID_ILatLongReport, + acc ? LOCATION_DESIRED_ACCURACY_HIGH : + LOCATION_DESIRED_ACCURACY_DEFAULT); + return SUCCEEDED(result_type); +} diff --git a/chrome/browser/geolocation/win7_location_api_win.h b/chrome/browser/geolocation/win7_location_api_win.h new file mode 100644 index 0000000..bbbd30e --- /dev/null +++ b/chrome/browser/geolocation/win7_location_api_win.h @@ -0,0 +1,60 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_GEOLOCATION_WIN7_LOCATION_API_WIN_H_ +#define CHROME_BROWSER_GEOLOCATION_WIN7_LOCATION_API_WIN_H_ + +#include <atlbase.h> +#include <atlcom.h> +#include <locationapi.h> +#include <sensors.h> +#include <vector> +#include <Windows.h> + +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/win_util.h" + +struct Geoposition; + +// PropVariantToDouble +typedef HRESULT (WINAPI* PropVariantToDoubleFunction) + (REFPROPVARIANT propvarIn, DOUBLE *pdblRet); + +class Win7LocationApi { + public: + // Public for testing. See Create() below for normal usage. + Win7LocationApi(HINSTANCE prop_library, + PropVariantToDoubleFunction PropVariantToDouble_function, + ILocation* locator); + virtual ~Win7LocationApi(); + + // Attempts to load propsys.dll, initialise |location_| and requests the user + // for access to location information. Creates and returns ownership of an + // instance of Win7LocationApi if all succeed. + static Win7LocationApi* Create(); + // Gives the best available position. + // Returns false if no valid position is available. + virtual void GetPosition(Geoposition* position); + // Changes the "accuracy" needed. Affects power levels of devices. + virtual bool SetHighAccuracy(bool acc); + + private: + // Provides the best position fix if one is available. + // Does this by requesting a location report and querying it to obtain + // location information. + virtual bool GetPositionIfFixed(Geoposition* position); + + // ILocation object that lets us communicate with the Location and + // Sensors platform. + CComPtr<ILocation> locator_; + // Holds the opened propsys.dll library that is passed on construction. + // This class is responsible for closing it. + HINSTANCE prop_lib_; + PropVariantToDoubleFunction PropVariantToDouble_function_; + + DISALLOW_COPY_AND_ASSIGN(Win7LocationApi); +}; + +#endif // CHROME_BROWSER_GEOLOCATION_WIN7_LOCATION_API_WIN_H_ diff --git a/chrome/browser/geolocation/win7_location_provider_unittest_win.cc b/chrome/browser/geolocation/win7_location_provider_unittest_win.cc new file mode 100644 index 0000000..2b5c3c3 --- /dev/null +++ b/chrome/browser/geolocation/win7_location_provider_unittest_win.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2010 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 "chrome/browser/chrome_thread.h" +#include "chrome/browser/geolocation/win7_location_provider_win.h" +#include "chrome/browser/geolocation/win7_location_api_win.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +struct Geoposition; + +using testing::_; +using testing::AtLeast; +using testing::DoDefault; +using testing::Invoke; +using testing::Return; + + +class MockWin7LocationApi : public Win7LocationApi { + public: + static MockWin7LocationApi* CreateMock() { + return new MockWin7LocationApi(); + } + + // Used to signal when the destructor is called. + MOCK_METHOD0(Die, void()); + // Win7LocationApi + MOCK_METHOD1(GetPosition, void(Geoposition*)); + MOCK_METHOD1(SetHighAccuracy, bool(bool)); + + virtual ~MockWin7LocationApi() { + Die(); + } + + void GetPositionValid(Geoposition* position) { + position->latitude = 4.5; + position->longitude = -34.1; + position->accuracy = 0.5; + position->timestamp = base::Time::FromDoubleT(200); + position->error_code = Geoposition::ERROR_CODE_NONE; + } + void GetPositionInvalid(Geoposition* position) { + position->latitude = 4.5; + position->longitude = -340000.1; + position->accuracy = 0.5; + position->timestamp = base::Time::FromDoubleT(200); + position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + } + + private: + MockWin7LocationApi() : Win7LocationApi(NULL, NULL, NULL) { + ON_CALL(*this, GetPosition(_)) + .WillByDefault(Invoke(this, + &MockWin7LocationApi::GetPositionValid)); + ON_CALL(*this, SetHighAccuracy(true)) + .WillByDefault(Return(true)); + ON_CALL(*this, SetHighAccuracy(false)) + .WillByDefault(Return(false)); + } +}; + +class LocationProviderListenerLoopQuitter + : public LocationProviderBase::ListenerInterface { + public: + explicit LocationProviderListenerLoopQuitter(MessageLoop* message_loop) + : message_loop_to_quit_(message_loop) { + CHECK(message_loop_to_quit_ != NULL); + } + virtual void LocationUpdateAvailable(LocationProviderBase* provider) { + EXPECT_EQ(MessageLoop::current(), message_loop_to_quit_); + provider_ = provider; + message_loop_to_quit_->Quit(); + } + + MessageLoop* message_loop_to_quit_; + LocationProviderBase* provider_; +}; + +class GeolocationProviderWin7Tests : public testing::Test { + public: + GeolocationProviderWin7Tests(): location_listener_(&main_message_loop_) { + } + + virtual void SetUp() { + api_ = MockWin7LocationApi::CreateMock(); + provider_ = new Win7LocationProvider(api_); + provider_->RegisterListener(&location_listener_); + } + virtual void TearDown() { + provider_->UnregisterListener(&location_listener_); + provider_->StopProvider(); + delete provider_; + main_message_loop_.RunAllPending(); + } + + protected: + MockWin7LocationApi* api_; + LocationProviderListenerLoopQuitter location_listener_; + MessageLoop main_message_loop_; + Win7LocationProvider* provider_; +}; + +TEST_F(GeolocationProviderWin7Tests, StartStop) { + EXPECT_CALL(*api_, SetHighAccuracy(true)) + .WillOnce(Return(true)); + EXPECT_TRUE(provider_->StartProvider(true)); + provider_->StopProvider(); + EXPECT_CALL(*api_, SetHighAccuracy(false)) + .WillOnce(Return(true)); + EXPECT_TRUE(provider_->StartProvider(false)); +} + +TEST_F(GeolocationProviderWin7Tests, GetValidPosition) { + EXPECT_CALL(*api_, GetPosition(_)) + .Times(AtLeast(1)); + EXPECT_CALL(*api_, SetHighAccuracy(true)) + .WillOnce(Return(true)); + EXPECT_TRUE(provider_->StartProvider(true)); + main_message_loop_.Run(); + Geoposition position; + provider_->GetPosition(&position); + EXPECT_TRUE(position.IsValidFix()); +} + +TEST_F(GeolocationProviderWin7Tests, GetInvalidPosition) { + EXPECT_CALL(*api_, GetPosition(_)) + .Times(AtLeast(1)) + .WillRepeatedly(Invoke(api_, + &MockWin7LocationApi::GetPositionInvalid)); + EXPECT_CALL(*api_, SetHighAccuracy(true)) + .WillOnce(Return(true)); + EXPECT_TRUE(provider_->StartProvider(true)); + main_message_loop_.Run(); + Geoposition position; + provider_->GetPosition(&position); + EXPECT_FALSE(position.IsValidFix()); +} diff --git a/chrome/browser/geolocation/win7_location_provider_win.cc b/chrome/browser/geolocation/win7_location_provider_win.cc new file mode 100644 index 0000000..aa400c4 --- /dev/null +++ b/chrome/browser/geolocation/win7_location_provider_win.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2010 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 "chrome/browser/geolocation/win7_location_provider_win.h" + +#include <algorithm> +#include <cmath> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_loop.h" + +namespace{ +const int kPollPeriodMovingMillis = 500; +// Poll less frequently whilst stationary. +const int kPollPeriodStationaryMillis = kPollPeriodMovingMillis * 3; +// Reading must differ by more than this amount to be considered movement. +const int kMovementThresholdMeters = 20; + +// This algorithm is reused from the corresponding code in the gears project +// and is also used in gps_location_provider_linux.cc +// The arbitrary delta is decreased (gears used 100 meters); if we need to +// decrease it any further we'll likely want to do some smarter filtering to +// remove GPS location jitter noise. +bool PositionsDifferSiginificantly(const Geoposition& position_1, + const Geoposition& position_2) { + const bool pos_1_valid = position_1.IsValidFix(); + if (pos_1_valid != position_2.IsValidFix()) + return true; + if (!pos_1_valid) { + DCHECK(!position_2.IsValidFix()); + return false; + } + double delta = std::sqrt( + std::pow(std::fabs(position_1.latitude - position_2.latitude), 2) + + std::pow(std::fabs(position_1.longitude - position_2.longitude), 2)); + // Convert to meters. 1 minute of arc of latitude (or longitude at the + // equator) is 1 nautical mile or 1852m. + delta *= 60 * 1852; + return delta > kMovementThresholdMeters; +} +} + +Win7LocationProvider::Win7LocationProvider(Win7LocationApi* api) + : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { + DCHECK(api != NULL); + api_.reset(api); +} + +Win7LocationProvider::~Win7LocationProvider() { + api_.reset(); +} + +bool Win7LocationProvider::StartProvider(bool high_accuracy){ + if (api_ == NULL) + return false; + api_->SetHighAccuracy(high_accuracy); + if (task_factory_.empty()) + ScheduleNextPoll(0); + return true; +} + +void Win7LocationProvider::StopProvider() { + task_factory_.RevokeAll(); +} + +void Win7LocationProvider::GetPosition(Geoposition* position) { + DCHECK(position); + *position = position_; +} + +void Win7LocationProvider::UpdatePosition() { + ScheduleNextPoll(0); +} + +void Win7LocationProvider::OnPermissionGranted( + const GURL& requesting_frame) { +} + +void Win7LocationProvider::DoPollTask() { + Geoposition new_position; + api_->GetPosition(&new_position); + const bool differ = PositionsDifferSiginificantly(position_, new_position); + ScheduleNextPoll(differ ? kPollPeriodMovingMillis : + kPollPeriodStationaryMillis); + if (differ || new_position.error_code != Geoposition::ERROR_CODE_NONE) { + // Update if the new location is interesting or we have an error to report + position_ = new_position; + UpdateListeners(); + } +} + +void Win7LocationProvider::ScheduleNextPoll(int interval) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + task_factory_.NewRunnableMethod(&Win7LocationProvider::DoPollTask), + interval); +} + +LocationProviderBase* NewSystemLocationProvider() { + Win7LocationApi* api = Win7LocationApi::Create(); + if (api == NULL) + return NULL; // API not supported on this machine. + return new Win7LocationProvider(api); +} diff --git a/chrome/browser/geolocation/win7_location_provider_win.h b/chrome/browser/geolocation/win7_location_provider_win.h new file mode 100644 index 0000000..7cc8d9c --- /dev/null +++ b/chrome/browser/geolocation/win7_location_provider_win.h @@ -0,0 +1,46 @@ +// Copyright (c) 2010 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. + + +#ifndef CHROME_BROWSER_GEOLOCATION_WIN7_LOCATION_PROVIDER_WIN_H_ +#define CHROME_BROWSER_GEOLOCATION_WIN7_LOCATION_PROVIDER_WIN_H_ + +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "chrome/browser/geolocation/location_provider.h" +#include "chrome/browser/geolocation/win7_location_api_win.h" +#include "chrome/common/geoposition.h" + +// Location provider for Windows 7 that uses the Location and Sensors platform +// to obtain position fixes. +// TODO(allanwoj): Remove polling and let the api signal when a new location. +// TODO(allanwoj): Possibly derive this class and the linux gps provider class +// from a single SystemLocationProvider class as their implementation is very +// similar. +class Win7LocationProvider : public LocationProviderBase { + public: + Win7LocationProvider(Win7LocationApi* api); + virtual ~Win7LocationProvider(); + + // LocationProvider. + virtual bool StartProvider(bool high_accuracy); + virtual void StopProvider(); + virtual void GetPosition(Geoposition* position); + virtual void UpdatePosition(); + virtual void OnPermissionGranted(const GURL& requesting_frame); + + private: + // Task which runs in the child thread. + void DoPollTask(); + // Will schedule a poll; i.e. enqueue DoPollTask deferred task. + void ScheduleNextPoll(int interval); + + scoped_ptr<Win7LocationApi> api_; + Geoposition position_; + // Holder for the tasks which run on the thread; takes care of cleanup. + ScopedRunnableMethodFactory<Win7LocationProvider> task_factory_; +}; + + +#endif // CHROME_BROWSER_GEOLOCATION_WIN7_LOCATION_PROVIDER_WIN_H_ |