diff options
author | cschuet <cschuet@chromium.org> | 2015-02-03 02:23:39 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-03 10:24:31 +0000 |
commit | b34ff556a7902fe52fe17e63c2e05faa616c8380 (patch) | |
tree | db8ba3ebd46ba7198e7387a943207dc20a8cd594 /extensions/browser/api/networking_config | |
parent | 6bc9a331e82e4f94ca9e0f24eb0efa45ffc1e472 (diff) | |
download | chromium_src-b34ff556a7902fe52fe17e63c2e05faa616c8380.zip chromium_src-b34ff556a7902fe52fe17e63c2e05faa616c8380.tar.gz chromium_src-b34ff556a7902fe52fe17e63c2e05faa616c8380.tar.bz2 |
Implementation of the NetworkingConfigService
Implementation of the NetworkingConfigService and boiler plate code for the
networking.config API.
Reviewers:
Please see
1) https://docs.google.com/document/d/1V8YGouKC477iC11L4PL8H_HU1Ru3R5kMhXppuoIVNeo for the design document.
2) https://docs.google.com/drawings/d/12Hai1LdaPzwtkrQUdCjSpt1s7InRNNBDeWiW5ujfIRg for a flow diagram.
Review URL: https://codereview.chromium.org/880073002
Cr-Commit-Position: refs/heads/master@{#314303}
Diffstat (limited to 'extensions/browser/api/networking_config')
8 files changed, 600 insertions, 0 deletions
diff --git a/extensions/browser/api/networking_config/OWNERS b/extensions/browser/api/networking_config/OWNERS new file mode 100644 index 0000000..5dc3191 --- /dev/null +++ b/extensions/browser/api/networking_config/OWNERS @@ -0,0 +1,3 @@ +cschuet@chromium.org +pneubeck@chromium.org +stevenjb@chromium.org diff --git a/extensions/browser/api/networking_config/networking_config_api.cc b/extensions/browser/api/networking_config/networking_config_api.cc new file mode 100644 index 0000000..bf37436 --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_api.cc @@ -0,0 +1,128 @@ +// Copyright 2015 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 <string> + +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "extensions/browser/api/networking_config/networking_config_api.h" +#include "extensions/browser/api/networking_config/networking_config_service.h" +#include "extensions/browser/api/networking_config/networking_config_service_factory.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +namespace { + +const char kAuthenticationResultFailed[] = + "Failed to set AuthenticationResult."; +const char kMalformedFilterDescription[] = "Malformed filter description."; +const char kMalformedFilterDescriptionWithSSID[] = + "Malformed filter description. Failed to register network with SSID " + "(hex): *"; +const char kUnsupportedNetworkType[] = "Unsupported network type."; + +} // namespace + +NetworkingConfigSetNetworkFilterFunction:: + NetworkingConfigSetNetworkFilterFunction() { +} + +ExtensionFunction::ResponseAction +NetworkingConfigSetNetworkFilterFunction::Run() { + parameters_ = + core_api::networking_config::SetNetworkFilter::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + + NetworkingConfigService* service = + NetworkingConfigServiceFactory::GetForBrowserContext(browser_context()); + DCHECK(service); + + // Remove previously registered networks. + service->UnregisterExtension(extension_id()); + + for (linked_ptr<core_api::networking_config::NetworkInfo>& ni : + parameters_->networks) { + // |Type| field must be set to |WiFi| + if (ni->type != core_api::networking_config::NETWORK_TYPE_WIFI) + return RespondNow(Error(kUnsupportedNetworkType)); + + // Either |ssid| or |hex_ssid| must be set. + if (!ni->ssid.get() && !ni->hex_ssid.get()) + return RespondNow(Error(kMalformedFilterDescription)); + + std::string hex_ssid; + if (ni->ssid.get()) { + auto ssid_field = ni->ssid.get(); + hex_ssid = base::HexEncode(ssid_field->c_str(), ssid_field->size()); + } + if (ni->hex_ssid.get()) + hex_ssid = *ni->hex_ssid.get(); + + if (!service->RegisterHexSsid(hex_ssid, extension_id())) + return RespondNow(Error(kMalformedFilterDescriptionWithSSID, hex_ssid)); + } + + return RespondNow(NoArguments()); +} + +NetworkingConfigSetNetworkFilterFunction:: + ~NetworkingConfigSetNetworkFilterFunction() { +} + +NetworkingConfigFinishAuthenticationFunction:: + NetworkingConfigFinishAuthenticationFunction() { +} + +ExtensionFunction::ResponseAction +NetworkingConfigFinishAuthenticationFunction::Run() { + parameters_ = + core_api::networking_config::FinishAuthentication::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + + NetworkingConfigService* service = + NetworkingConfigServiceFactory::GetForBrowserContext(browser_context()); + DCHECK(service); + + const NetworkingConfigService::AuthenticationResult& last_result = + service->GetAuthenticationResult(); + if (last_result.authentication_state != NetworkingConfigService::NOTRY || + last_result.guid != parameters_->guid) { + RespondNow(Error(kAuthenticationResultFailed)); + } + + // Populate NetworkingCaptivePortalAPI::AuthenticationResult. + NetworkingConfigService::AuthenticationResult authentication_result = { + extension_id(), parameters_->guid, NetworkingConfigService::FAILED, + }; + switch (parameters_->result) { + case core_api::networking_config::AUTHENTICATION_RESULT_NONE: + NOTREACHED(); + break; + case core_api::networking_config::AUTHENTICATION_RESULT_UNHANDLED: + authentication_result.authentication_state = + NetworkingConfigService::FAILED; + break; + case core_api::networking_config::AUTHENTICATION_RESULT_REJECTED: + authentication_result.authentication_state = + NetworkingConfigService::REJECTED; + break; + case core_api::networking_config::AUTHENTICATION_RESULT_FAILED: + authentication_result.authentication_state = + NetworkingConfigService::FAILED; + break; + case core_api::networking_config::AUTHENTICATION_RESULT_SUCCEEDED: + authentication_result.authentication_state = + NetworkingConfigService::SUCCESS; + break; + } + service->SetAuthenticationResult(authentication_result); + return RespondNow(NoArguments()); +} + +NetworkingConfigFinishAuthenticationFunction:: + ~NetworkingConfigFinishAuthenticationFunction() { +} + +} // namespace extensions diff --git a/extensions/browser/api/networking_config/networking_config_api.h b/extensions/browser/api/networking_config/networking_config_api.h new file mode 100644 index 0000000..863fb08 --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_api.h @@ -0,0 +1,55 @@ +// Copyright 2015 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 EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_API_H_ +#define EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_API_H_ + +#include "extensions/common/api/networking_config.h" +#include "extensions/browser/extension_function.h" + +namespace extensions { + +class NetworkingConfigSetNetworkFilterFunction + : public UIThreadExtensionFunction { + public: + NetworkingConfigSetNetworkFilterFunction(); + + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("networking.config.setNetworkFilter", + NETWORKING_CONFIG_SETNETWORKFILTER); + + protected: + ~NetworkingConfigSetNetworkFilterFunction() override; + + scoped_ptr<core_api::networking_config::SetNetworkFilter::Params> + parameters_; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkingConfigSetNetworkFilterFunction); +}; + +class NetworkingConfigFinishAuthenticationFunction + : public UIThreadExtensionFunction { + public: + NetworkingConfigFinishAuthenticationFunction(); + + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("networking.config.finishAuthenticationFunction", + NETWORKING_CONFIG_FINISHAUTHENTICATION); + + protected: + ~NetworkingConfigFinishAuthenticationFunction() override; + + scoped_ptr<core_api::networking_config::FinishAuthentication::Params> + parameters_; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkingConfigFinishAuthenticationFunction); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_API_H_ diff --git a/extensions/browser/api/networking_config/networking_config_service.cc b/extensions/browser/api/networking_config/networking_config_service.cc new file mode 100644 index 0000000..54a8ecd --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_service.cc @@ -0,0 +1,112 @@ +// Copyright 2015 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 "base/lazy_instance.h" +#include "base/strings/string_util.h" +#include "extensions/browser/api/networking_config/networking_config_service.h" + +namespace extensions { + +namespace { + +bool IsValidNonEmptyHexString(const std::string& input) { + size_t count = input.size(); + if (count == 0 || (count % 2) != 0) + return false; + for (const char& c : input) + if (!IsHexDigit<char>(c)) + return false; + return true; +} + +} // namespace + +NetworkingConfigService::AuthenticationResult::AuthenticationResult() + : authentication_state(NetworkingConfigService::NOTRY) { +} + +NetworkingConfigService::AuthenticationResult::AuthenticationResult( + ExtensionId extension_id, + std::string guid, + AuthenticationState authentication_state) + : extension_id(extension_id), + guid(guid), + authentication_state(authentication_state) { +} + +NetworkingConfigService::NetworkingConfigService( + scoped_ptr<EventDelegate> event_delegate, + ExtensionRegistry* extension_registry) + : registry_observer_(this), event_delegate_(event_delegate.Pass()) { + registry_observer_.Add(extension_registry); +} + +NetworkingConfigService::~NetworkingConfigService() { +} + +void NetworkingConfigService::OnExtensionUnloaded( + content::BrowserContext* browser_context, + const Extension* extension, + UnloadedExtensionInfo::Reason reason) { + UnregisterExtension(extension->id()); +} + +std::string NetworkingConfigService::LookupExtensionIdForHexSsid( + std::string hex_ssid) const { + // Transform hex_ssid to uppercase. + transform(hex_ssid.begin(), hex_ssid.end(), hex_ssid.begin(), toupper); + + const auto it = hex_ssid_to_extension_id_.find(hex_ssid); + if (it == hex_ssid_to_extension_id_.end()) + return std::string(); + return it->second; +} + +bool NetworkingConfigService::IsRegisteredForCaptivePortalEvent( + std::string extension_id) const { + return event_delegate_->HasExtensionRegisteredForEvent(extension_id); +} + +bool NetworkingConfigService::RegisterHexSsid(std::string hex_ssid, + const std::string& extension_id) { + if (!IsValidNonEmptyHexString(hex_ssid)) { + LOG(ERROR) << "\'" << hex_ssid << "\' is not a valid hex encoded string."; + return false; + } + + // Transform hex_ssid to uppercase. + transform(hex_ssid.begin(), hex_ssid.end(), hex_ssid.begin(), toupper); + + return hex_ssid_to_extension_id_.insert(make_pair(hex_ssid, extension_id)) + .second; +} + +void NetworkingConfigService::UnregisterExtension( + const std::string& extension_id) { + for (auto it = hex_ssid_to_extension_id_.begin(); + it != hex_ssid_to_extension_id_.end();) { + if (it->second == extension_id) + hex_ssid_to_extension_id_.erase(it++); + else + ++it; + } +} + +const NetworkingConfigService::AuthenticationResult& +NetworkingConfigService::GetAuthenticationResult() const { + return authentication_result_; +} + +void NetworkingConfigService::ResetAuthenticationResult() { + authentication_result_ = AuthenticationResult(); +} + +void NetworkingConfigService::SetAuthenticationResult( + const AuthenticationResult& authentication_result) { + authentication_result_ = authentication_result; +} + +} // namespace extensions diff --git a/extensions/browser/api/networking_config/networking_config_service.h b/extensions/browser/api/networking_config/networking_config_service.h new file mode 100644 index 0000000..236b992 --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_service.h @@ -0,0 +1,107 @@ +// Copyright 2015 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 EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_SERVICE_H_ +#define EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_SERVICE_H_ + +#include <map> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/scoped_observer.h" +#include "components/keyed_service/core/keyed_service.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_registry_observer.h" + +namespace extensions { + +// This class provides the session-scoped storage backing the networking config +// extension API. Currently only the parts relevant for captive portal handling +// are implemented. +class NetworkingConfigService : public ExtensionRegistryObserver, + public KeyedService { + public: + class EventDelegate { + public: + EventDelegate() {} + virtual ~EventDelegate() {} + virtual bool HasExtensionRegisteredForEvent( + const std::string& extension_id) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(EventDelegate); + }; + + // Indicates the authentication state of the portal. + enum AuthenticationState { NOTRY, TRYING, SUCCESS, REJECTED, FAILED }; + + // Provides information about the current authentication state of the portal. + struct AuthenticationResult { + AuthenticationResult(); + AuthenticationResult(ExtensionId extension_id, + std::string guid, + AuthenticationState authentication_state); + ExtensionId extension_id; + std::string guid; + AuthenticationState authentication_state; + }; + + // Note: |extension_registry| must outlive this class. + NetworkingConfigService(scoped_ptr<EventDelegate> event_delegate, + ExtensionRegistry* extension_registry); + ~NetworkingConfigService() override; + + // ExtensionRegistryObserver + void OnExtensionUnloaded(content::BrowserContext* browser_context, + const Extension* extension, + UnloadedExtensionInfo::Reason reason) override; + + // Returns the extension id registered for |hex_ssid|. If no extension is + // registered for this |hex_ssid|, the function returns an empty string. + // |hex_ssid|: SSID in hex encoding. + std::string LookupExtensionIdForHexSsid(std::string hex_ssid) const; + + // Returns true if the extension with id |extension_id| registered for + // |onCaptivePortalDetected| events, otherwise false. + bool IsRegisteredForCaptivePortalEvent(std::string extension_id) const; + + // Registers |hex_ssid| as being handled by the extension with extension ID + // |extension_id|. Returns true on success and false if another extension + // already registered for |hex_ssid|. + // |hex_ssid|: SSID in hex encoding of the network to be registered. + // |extension_id|: Extension ID of the extension handling the network + // configuration for this network. + bool RegisterHexSsid(std::string hex_ssid, const std::string& extension_id); + + // Unregisters extension with the ID |extension_id| removing all associated + // HexSSIDs from the map. + // |extension_id|: ID identifying the extenion to be removed + void UnregisterExtension(const std::string& extensionId); + + // Returns the current AuthenticationResult. + const AuthenticationResult& GetAuthenticationResult() const; + + // Sets the authentication_state to NOTRY and clears all other fields. + void ResetAuthenticationResult(); + + // Sets the current AuthenticationResult. + void SetAuthenticationResult( + const AuthenticationResult& authentication_result); + + private: + AuthenticationResult authentication_result_; + + ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> + registry_observer_; + + scoped_ptr<EventDelegate> event_delegate_; + + // This map associates a given hex encoded SSID to an extension entry. + std::map<std::string, std::string> hex_ssid_to_extension_id_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_SERVICE_H_ diff --git a/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc b/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc new file mode 100644 index 0000000..9990d74 --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc @@ -0,0 +1,71 @@ +// 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 "extensions/browser/api/networking_config/networking_config_service.h" +#include "extensions/browser/api_unittest.h" +#include "extensions/browser/extension_registry.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { + +const char kExtensionId[] = "necdpnkfgondfageiompbacibhgmfebg"; +const char kHexSsid[] = "54657374535349445F5A5A5A5A"; +const char kHexSsidLower[] = "54657374535349445f5a5a5a5a"; + +class MockEventDelegate : public NetworkingConfigService::EventDelegate { + public: + MockEventDelegate() : extension_registered_(false) {} + ~MockEventDelegate() override {} + + bool HasExtensionRegisteredForEvent( + const std::string& extension_id) const override { + return extension_registered_; + } + + void SetExtensionRegisteredForEvent(bool extension_registered) { + extension_registered_ = extension_registered; + } + + private: + bool extension_registered_; +}; + +} // namespace + +class NetworkingConfigServiceTest : public ApiUnitTest { + public: + NetworkingConfigServiceTest() {} + ~NetworkingConfigServiceTest() override {} + + void SetUp() override { + ApiUnitTest::SetUp(); + extension_registry_ = scoped_ptr<ExtensionRegistry>( + new ExtensionRegistry(browser_context())); + scoped_ptr<MockEventDelegate> mock_event_delegate = + scoped_ptr<MockEventDelegate>(new MockEventDelegate()); + service_ = scoped_ptr<NetworkingConfigService>(new NetworkingConfigService( + mock_event_delegate.Pass(), extension_registry_.get())); + DCHECK(service_); + } + + protected: + scoped_ptr<ExtensionRegistry> extension_registry_; + scoped_ptr<NetworkingConfigService> service_; +}; + +TEST_F(NetworkingConfigServiceTest, BasicRegisterHexSsid) { + EXPECT_TRUE(service_->RegisterHexSsid(kHexSsid, kExtensionId)); + EXPECT_EQ(kExtensionId, service_->LookupExtensionIdForHexSsid(kHexSsid)); + EXPECT_EQ(kExtensionId, service_->LookupExtensionIdForHexSsid(kHexSsidLower)); +} + +TEST_F(NetworkingConfigServiceTest, BasicRegisterHexSsidLower) { + EXPECT_TRUE(service_->RegisterHexSsid(kHexSsidLower, kExtensionId)); + EXPECT_EQ(kExtensionId, service_->LookupExtensionIdForHexSsid(kHexSsid)); + EXPECT_EQ(kExtensionId, service_->LookupExtensionIdForHexSsid(kHexSsidLower)); +} + +} // namespace extensions diff --git a/extensions/browser/api/networking_config/networking_config_service_factory.cc b/extensions/browser/api/networking_config/networking_config_service_factory.cc new file mode 100644 index 0000000..62bc8f1 --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_service_factory.cc @@ -0,0 +1,83 @@ +// Copyright 2015 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 "extensions/browser/api/networking_config/networking_config_service_factory.h" + +#include <string> + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/api/networking_config/networking_config_service.h" +#include "extensions/browser/extension_registry_factory.h" +#include "extensions/browser/extension_system_provider.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/common/api/networking_config.h" + +namespace extensions { + +namespace { + +class DefaultEventDelegate : public NetworkingConfigService::EventDelegate { + public: + explicit DefaultEventDelegate(content::BrowserContext* context); + ~DefaultEventDelegate() override; + + bool HasExtensionRegisteredForEvent( + const std::string& extension_id) const override; + + private: + content::BrowserContext* const context_; +}; + +DefaultEventDelegate::DefaultEventDelegate(content::BrowserContext* context) + : context_(context) { +} + +DefaultEventDelegate::~DefaultEventDelegate() { +} + +bool DefaultEventDelegate::HasExtensionRegisteredForEvent( + const std::string& extension_id) const { + return EventRouter::Get(context_)->ExtensionHasEventListener( + extension_id, + core_api::networking_config::OnCaptivePortalDetected::kEventName); +} + +} // namespace + +// static +NetworkingConfigService* NetworkingConfigServiceFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<NetworkingConfigService*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +NetworkingConfigServiceFactory* NetworkingConfigServiceFactory::GetInstance() { + return Singleton<NetworkingConfigServiceFactory>::get(); +} + +NetworkingConfigServiceFactory::NetworkingConfigServiceFactory() + : BrowserContextKeyedServiceFactory( + "NetworkingConfigService", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); + DependsOn(extensions::ExtensionRegistryFactory::GetInstance()); +} + +NetworkingConfigServiceFactory::~NetworkingConfigServiceFactory() { +} + +KeyedService* NetworkingConfigServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new NetworkingConfigService( + make_scoped_ptr(new DefaultEventDelegate(context)), + ExtensionRegistry::Get(context)); +} + +content::BrowserContext* NetworkingConfigServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +} // namespace extensions diff --git a/extensions/browser/api/networking_config/networking_config_service_factory.h b/extensions/browser/api/networking_config/networking_config_service_factory.h new file mode 100644 index 0000000..58e5632 --- /dev/null +++ b/extensions/browser/api/networking_config/networking_config_service_factory.h @@ -0,0 +1,41 @@ +// Copyright 2015 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 EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_SERVICE_FACTORY_H_ +#define EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace extensions { +class NetworkingConfigService; + +class NetworkingConfigServiceFactory + : public BrowserContextKeyedServiceFactory { + public: + static NetworkingConfigService* GetForBrowserContext( + content::BrowserContext* context); + + static NetworkingConfigServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<NetworkingConfigServiceFactory>; + + NetworkingConfigServiceFactory(); + ~NetworkingConfigServiceFactory() override; + + // BrowserContextKeyedBaseFactory + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_NETWORKING_CONFIG_NETWORKING_CONFIG_SERVICE_FACTORY_H_ |