|  | // Copyright 2014 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "extensions/browser/api/webcam_private/webcam_private_api.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "components/media_device_salt/media_device_salt_service.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/media_device_id.h" | 
|  | #include "content/public/browser/resource_context.h" | 
|  | #include "extensions/browser/api/serial/serial_port_manager.h" | 
|  | #include "extensions/browser/api/webcam_private/ip_webcam.h" | 
|  | #include "extensions/browser/api/webcam_private/v4l2_webcam.h" | 
|  | #include "extensions/browser/api/webcam_private/visca_webcam.h" | 
|  | #include "extensions/browser/extensions_browser_client.h" | 
|  | #include "extensions/browser/process_manager.h" | 
|  | #include "extensions/browser/process_manager_factory.h" | 
|  | #include "extensions/common/extension_id.h" | 
|  | #include "mojo/public/cpp/bindings/pending_remote.h" | 
|  | #include "url/origin.h" | 
|  |  | 
|  | namespace webcam_private = extensions::api::webcam_private; | 
|  |  | 
|  | namespace content { | 
|  | class BrowserContext; | 
|  | }  // namespace content | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kPathInUse[] = "Path in use"; | 
|  | const char kUnknownWebcam[] = "Unknown webcam id"; | 
|  | const char kOpenSerialWebcamError[] = "Can't open serial webcam."; | 
|  | const char kGetWebcamPTZError[] = "Can't get web camera pan/tilt/zoom."; | 
|  | const char kSetWebcamPTZError[] = "Can't set web camera pan/tilt/zoom."; | 
|  | const char kResetWebcamError[] = "Can't reset web camera."; | 
|  | const char kSetHomeWebcamError[] = "Can't set home position"; | 
|  | const char kRestorePresetWebcamError[] = "Can't restore preset."; | 
|  | const char kSetPresetWebcamError[] = "Can't set preset."; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | // static | 
|  | WebcamPrivateAPI* WebcamPrivateAPI::Get(content::BrowserContext* context) { | 
|  | return GetFactoryInstance()->Get(context); | 
|  | } | 
|  |  | 
|  | WebcamPrivateAPI::WebcamPrivateAPI(content::BrowserContext* context) | 
|  | : browser_context_(context) { | 
|  | webcam_resource_manager_ = | 
|  | std::make_unique<ApiResourceManager<WebcamResource>>(context); | 
|  | } | 
|  |  | 
|  | WebcamPrivateAPI::~WebcamPrivateAPI() { | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::OnGotDeviceIdOnUIThread( | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& webcam_id, | 
|  | base::OnceCallback<void(Webcam*)> callback, | 
|  | const std::optional<std::string>& device_id) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | if (!device_id) { | 
|  | std::move(callback).Run(nullptr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Webcam* webcam = nullptr; | 
|  |  | 
|  | if (device_id->compare(0, 8, "192.168.") == 0) { | 
|  | webcam = new IpWebcam(device_id.value()); | 
|  | } else { | 
|  | V4L2Webcam* v4l2_webcam = new V4L2Webcam(device_id.value()); | 
|  | if (!v4l2_webcam->Open()) { | 
|  | std::move(callback).Run(nullptr); | 
|  | return; | 
|  | } | 
|  | webcam = v4l2_webcam; | 
|  | } | 
|  |  | 
|  | webcam_resource_manager_->Add( | 
|  | new WebcamResource(extension_id, webcam, webcam_id)); | 
|  |  | 
|  | std::move(callback).Run(webcam); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void WebcamPrivateAPI::GetDeviceIdOnIOThread( | 
|  | std::string salt, | 
|  | url::Origin security_origin, | 
|  | std::string hmac_device_id, | 
|  | base::OnceCallback<void(const std::optional<std::string>&)> callback) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  |  | 
|  | content::GetMediaDeviceIDForHMAC( | 
|  | blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, salt, | 
|  | std::move(security_origin), std::move(hmac_device_id), | 
|  | content::GetUIThreadTaskRunner({}), std::move(callback)); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::GetWebcam(const ExtensionId& extension_id, | 
|  | const std::string& webcam_id, | 
|  | base::OnceCallback<void(Webcam*)> callback) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  |  | 
|  | WebcamResource* webcam_resource = FindWebcamResource(extension_id, webcam_id); | 
|  | if (webcam_resource) { | 
|  | Webcam* webcam = webcam_resource->GetWebcam(); | 
|  | std::move(callback).Run(webcam); | 
|  | return; | 
|  | } | 
|  |  | 
|  | url::Origin security_origin = | 
|  | extensions::Extension::CreateOriginFromExtensionId(extension_id); | 
|  | if (media_device_salt::MediaDeviceSaltService* salt_service = | 
|  | ExtensionsBrowserClient::Get()->GetMediaDeviceSaltService( | 
|  | browser_context_)) { | 
|  | salt_service->GetSalt( | 
|  | blink::StorageKey::CreateFirstParty(security_origin), | 
|  | base::BindOnce(&WebcamPrivateAPI::GetDeviceIdOnUIThread, | 
|  | weak_ptr_factory_.GetWeakPtr(), security_origin, | 
|  | extension_id, webcam_id, std::move(callback))); | 
|  | } else { | 
|  | // If the embedder does not provide a salt service, use the browser | 
|  | // context's unique ID as salt. | 
|  | GetDeviceIdOnUIThread(security_origin, extension_id, webcam_id, | 
|  | std::move(callback), browser_context_->UniqueId()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::GetDeviceIdOnUIThread( | 
|  | const url::Origin& security_origin, | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& webcam_id, | 
|  | base::OnceCallback<void(Webcam*)> webcam_callback, | 
|  | const std::string& salt) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  |  | 
|  | auto got_device_cb = | 
|  | base::BindOnce(&WebcamPrivateAPI::OnGotDeviceIdOnUIThread, | 
|  | weak_ptr_factory_.GetWeakPtr(), extension_id, webcam_id, | 
|  | std::move(webcam_callback)); | 
|  |  | 
|  | content::GetIOThreadTaskRunner({})->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&WebcamPrivateAPI::GetDeviceIdOnIOThread, salt, | 
|  | security_origin, webcam_id, std::move(got_device_cb))); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::OpenSerialWebcam( | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& device_path, | 
|  | const base::RepeatingCallback<void(const std::string&, | 
|  | OpenSerialWebcamResult)>& callback) { | 
|  | GetWebcamId(extension_id, device_path, | 
|  | base::BindOnce(&WebcamPrivateAPI::GotWebcamId, | 
|  | weak_ptr_factory_.GetWeakPtr(), extension_id, | 
|  | device_path, callback)); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::GotWebcamId( | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& device_path, | 
|  | const base::RepeatingCallback<void(const std::string&, | 
|  | OpenSerialWebcamResult)>& callback, | 
|  | const std::string& webcam_id) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | WebcamResource* webcam_resource = FindWebcamResource(extension_id, webcam_id); | 
|  | if (webcam_resource) { | 
|  | callback.Run("", OpenSerialWebcamResult::kInUse); | 
|  | return; | 
|  | } | 
|  |  | 
|  | mojo::PendingRemote<device::mojom::SerialPort> port; | 
|  | auto* port_manager = api::SerialPortManager::Get(browser_context_); | 
|  | DCHECK(port_manager); | 
|  |  | 
|  | auto visca_webcam = base::MakeRefCounted<ViscaWebcam>(); | 
|  | visca_webcam->Open( | 
|  | extension_id, port_manager, device_path, | 
|  | base::BindRepeating(&WebcamPrivateAPI::OnOpenSerialWebcam, | 
|  | weak_ptr_factory_.GetWeakPtr(), webcam_id, | 
|  | extension_id, device_path, visca_webcam, callback)); | 
|  | } | 
|  |  | 
|  | bool WebcamPrivateAPI::CloseWebcam(const ExtensionId& extension_id, | 
|  | const std::string& webcam_id) { | 
|  | if (FindWebcamResource(extension_id, webcam_id)) { | 
|  | RemoveWebcamResource(extension_id, webcam_id); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::OnOpenSerialWebcam( | 
|  | const std::string& webcam_id, | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& device_path, | 
|  | scoped_refptr<Webcam> webcam, | 
|  | const base::RepeatingCallback<void(const std::string&, | 
|  | OpenSerialWebcamResult)>& callback, | 
|  | bool success) { | 
|  | if (success) { | 
|  | webcam_resource_manager_->Add( | 
|  | new WebcamResource(extension_id, webcam.get(), webcam_id)); | 
|  | callback.Run(webcam_id, OpenSerialWebcamResult::kSuccess); | 
|  | } else { | 
|  | callback.Run("", OpenSerialWebcamResult::kError); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::GetWebcamId( | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& device_id, | 
|  | base::OnceCallback<void(const std::string&)> webcam_id_callback) { | 
|  | url::Origin security_origin = | 
|  | extensions::Extension::CreateOriginFromExtensionId(extension_id); | 
|  | if (media_device_salt::MediaDeviceSaltService* salt_service = | 
|  | ExtensionsBrowserClient::Get()->GetMediaDeviceSaltService( | 
|  | browser_context_)) { | 
|  | salt_service->GetSalt( | 
|  | blink::StorageKey::CreateFirstParty(security_origin), | 
|  | base::BindOnce(&WebcamPrivateAPI::FinalizeGetWebcamId, | 
|  | weak_ptr_factory_.GetWeakPtr(), security_origin, | 
|  | device_id, std::move(webcam_id_callback))); | 
|  | } else { | 
|  | // If the embedder does not provide a salt service, use the browser | 
|  | // context's unique ID as salt. | 
|  | FinalizeGetWebcamId(security_origin, device_id, | 
|  | std::move(webcam_id_callback), | 
|  | browser_context_->UniqueId()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WebcamPrivateAPI::FinalizeGetWebcamId( | 
|  | const url::Origin& security_origin, | 
|  | const std::string& device_id, | 
|  | base::OnceCallback<void(const std::string&)> webcam_id_callback, | 
|  | const std::string& device_id_salt) { | 
|  | std::string webcam_id = content::GetHMACForMediaDeviceID( | 
|  | device_id_salt, security_origin, device_id); | 
|  | std::move(webcam_id_callback).Run(webcam_id); | 
|  | } | 
|  |  | 
|  | WebcamResource* WebcamPrivateAPI::FindWebcamResource( | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& webcam_id) const { | 
|  | DCHECK(webcam_resource_manager_); | 
|  |  | 
|  | std::unordered_set<int>* connection_ids = | 
|  | webcam_resource_manager_->GetResourceIds(extension_id); | 
|  | if (!connection_ids) | 
|  | return nullptr; | 
|  |  | 
|  | for (const auto& connection_id : *connection_ids) { | 
|  | WebcamResource* webcam_resource = | 
|  | webcam_resource_manager_->Get(extension_id, connection_id); | 
|  | if (webcam_resource && webcam_resource->GetWebcamId() == webcam_id) | 
|  | return webcam_resource; | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | bool WebcamPrivateAPI::RemoveWebcamResource(const ExtensionId& extension_id, | 
|  | const std::string& webcam_id) { | 
|  | DCHECK(webcam_resource_manager_); | 
|  |  | 
|  | std::unordered_set<int>* connection_ids = | 
|  | webcam_resource_manager_->GetResourceIds(extension_id); | 
|  | if (!connection_ids) | 
|  | return false; | 
|  |  | 
|  | for (const auto& connection_id : *connection_ids) { | 
|  | WebcamResource* webcam_resource = | 
|  | webcam_resource_manager_->Get(extension_id, connection_id); | 
|  | if (webcam_resource && webcam_resource->GetWebcamId() == webcam_id) { | 
|  | webcam_resource_manager_->Remove(extension_id, connection_id); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | WebcamPrivateOpenSerialWebcamFunction::WebcamPrivateOpenSerialWebcamFunction() { | 
|  | } | 
|  |  | 
|  | WebcamPrivateOpenSerialWebcamFunction:: | 
|  | ~WebcamPrivateOpenSerialWebcamFunction() { | 
|  | } | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateOpenSerialWebcamFunction::Run() { | 
|  | std::optional<webcam_private::OpenSerialWebcam::Params> params = | 
|  | webcam_private::OpenSerialWebcam::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->OpenSerialWebcam( | 
|  | extension_id(), params->path, | 
|  | base::BindRepeating( | 
|  | &WebcamPrivateOpenSerialWebcamFunction::OnOpenWebcam, this)); | 
|  | // OpenSerialWebcam responds asynchronously. | 
|  | return RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateOpenSerialWebcamFunction::OnOpenWebcam( | 
|  | const std::string& webcam_id, | 
|  | WebcamPrivateAPI::OpenSerialWebcamResult result) { | 
|  | if (result == WebcamPrivateAPI::OpenSerialWebcamResult::kSuccess) { | 
|  | Respond(WithArguments(webcam_id)); | 
|  | } else if (result == WebcamPrivateAPI::OpenSerialWebcamResult::kError) { | 
|  | Respond(Error(kOpenSerialWebcamError)); | 
|  | } else if (result == WebcamPrivateAPI::OpenSerialWebcamResult::kInUse) { | 
|  | Respond(Error(kPathInUse)); | 
|  | } | 
|  | } | 
|  |  | 
|  | WebcamPrivateCloseWebcamFunction::WebcamPrivateCloseWebcamFunction() { | 
|  | } | 
|  |  | 
|  | WebcamPrivateCloseWebcamFunction::~WebcamPrivateCloseWebcamFunction() { | 
|  | } | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateCloseWebcamFunction::Run() { | 
|  | std::optional<webcam_private::CloseWebcam::Params> params = | 
|  | webcam_private::CloseWebcam::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | const bool success = WebcamPrivateAPI::Get(browser_context()) | 
|  | ->CloseWebcam(extension_id(), params->webcam_id); | 
|  | return RespondNow(success ? NoArguments() : Error(kUnknownErrorDoNotUse)); | 
|  | } | 
|  |  | 
|  | WebcamPrivateSetFunction::WebcamPrivateSetFunction() { | 
|  | } | 
|  |  | 
|  | WebcamPrivateSetFunction::~WebcamPrivateSetFunction() { | 
|  | } | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateSetFunction::Run() { | 
|  | std::optional<webcam_private::Set::Params> params = | 
|  | webcam_private::Set::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | std::string webcam_id = params->webcam_id; | 
|  |  | 
|  | auto on_webcam = base::BindOnce(&WebcamPrivateSetFunction::OnWebcam, this, | 
|  | std::move(params)); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->GetWebcam(extension_id(), webcam_id, std::move(on_webcam)); | 
|  |  | 
|  | return did_respond() ? AlreadyResponded() : RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateSetFunction::OnWebcam( | 
|  | std::optional<webcam_private::Set::Params> params, | 
|  | Webcam* webcam) { | 
|  | if (!webcam) | 
|  | return Respond(Error(kUnknownWebcam)); | 
|  |  | 
|  | int pan_speed = 0; | 
|  | int tilt_speed = 0; | 
|  | if (params->config.pan_speed) | 
|  | pan_speed = *(params->config.pan_speed); | 
|  |  | 
|  | if (params->config.tilt_speed) | 
|  | tilt_speed = *(params->config.tilt_speed); | 
|  |  | 
|  | // Count all of the requests we will send before potentially sending any. | 
|  | pending_num_set_webcam_param_requests_ = 0; | 
|  | if (params->config.pan) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  | if (params->config.pan_direction != webcam_private::PanDirection::kNone) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  | if (params->config.tilt) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  | if (params->config.tilt_direction != webcam_private::TiltDirection::kNone) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  | if (params->config.zoom) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  | if (params->config.autofocus_state != webcam_private::AutofocusState::kNone) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  | if (params->config.focus) { | 
|  | ++pending_num_set_webcam_param_requests_; | 
|  | } | 
|  |  | 
|  | if (params->config.pan) { | 
|  | webcam->SetPan(*(params->config.pan), pan_speed, | 
|  | base::BindRepeating( | 
|  | &WebcamPrivateSetFunction::OnSetWebcamParameters, this)); | 
|  | } | 
|  |  | 
|  | if (params->config.pan_direction != webcam_private::PanDirection::kNone) { | 
|  | Webcam::PanDirection direction = Webcam::PAN_STOP; | 
|  | switch (params->config.pan_direction) { | 
|  | case webcam_private::PanDirection::kNone: | 
|  | case webcam_private::PanDirection::kStop: | 
|  | direction = Webcam::PAN_STOP; | 
|  | break; | 
|  |  | 
|  | case webcam_private::PanDirection::kRight: | 
|  | direction = Webcam::PAN_RIGHT; | 
|  | break; | 
|  |  | 
|  | case webcam_private::PanDirection::kLeft: | 
|  | direction = Webcam::PAN_LEFT; | 
|  | break; | 
|  | } | 
|  | webcam->SetPanDirection( | 
|  | direction, pan_speed, | 
|  | base::BindRepeating(&WebcamPrivateSetFunction::OnSetWebcamParameters, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | if (params->config.tilt) { | 
|  | webcam->SetTilt( | 
|  | *(params->config.tilt), tilt_speed, | 
|  | base::BindRepeating(&WebcamPrivateSetFunction::OnSetWebcamParameters, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | if (params->config.tilt_direction != webcam_private::TiltDirection::kNone) { | 
|  | Webcam::TiltDirection direction = Webcam::TILT_STOP; | 
|  | switch (params->config.tilt_direction) { | 
|  | case webcam_private::TiltDirection::kNone: | 
|  | case webcam_private::TiltDirection::kStop: | 
|  | direction = Webcam::TILT_STOP; | 
|  | break; | 
|  |  | 
|  | case webcam_private::TiltDirection::kUp: | 
|  | direction = Webcam::TILT_UP; | 
|  | break; | 
|  |  | 
|  | case webcam_private::TiltDirection::kDown: | 
|  | direction = Webcam::TILT_DOWN; | 
|  | break; | 
|  | } | 
|  | webcam->SetTiltDirection( | 
|  | direction, tilt_speed, | 
|  | base::BindRepeating(&WebcamPrivateSetFunction::OnSetWebcamParameters, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | if (params->config.zoom) { | 
|  | webcam->SetZoom( | 
|  | *(params->config.zoom), | 
|  | base::BindRepeating(&WebcamPrivateSetFunction::OnSetWebcamParameters, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | if (params->config.autofocus_state != webcam_private::AutofocusState::kNone) { | 
|  | Webcam::AutofocusState state = Webcam::AUTOFOCUS_ON; | 
|  | switch (params->config.autofocus_state) { | 
|  | case webcam_private::AutofocusState::kNone: | 
|  | case webcam_private::AutofocusState::kOff: | 
|  | state = Webcam::AUTOFOCUS_OFF; | 
|  | break; | 
|  |  | 
|  | case webcam_private::AutofocusState::kOn: | 
|  | state = Webcam::AUTOFOCUS_ON; | 
|  | break; | 
|  | } | 
|  | webcam->SetAutofocusState( | 
|  | state, base::BindRepeating( | 
|  | &WebcamPrivateSetFunction::OnSetWebcamParameters, this)); | 
|  | } | 
|  |  | 
|  | if (params->config.focus) { | 
|  | webcam->SetFocus( | 
|  | *(params->config.focus), | 
|  | base::BindRepeating(&WebcamPrivateSetFunction::OnSetWebcamParameters, | 
|  | this)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WebcamPrivateSetFunction::OnSetWebcamParameters(bool success) { | 
|  | failed_ |= !success; | 
|  | --pending_num_set_webcam_param_requests_; | 
|  |  | 
|  | DCHECK_GE(pending_num_set_webcam_param_requests_, 0); | 
|  | if (pending_num_set_webcam_param_requests_ != 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (failed_) { | 
|  | Respond(Error(kSetWebcamPTZError)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Reply with a dummy, empty configuration. | 
|  | webcam_private::WebcamCurrentConfiguration result; | 
|  | Respond(WithArguments(result.ToValue())); | 
|  | } | 
|  |  | 
|  | WebcamPrivateGetFunction::WebcamPrivateGetFunction() | 
|  | : min_pan_(0), | 
|  | max_pan_(0), | 
|  | pan_(0), | 
|  | min_tilt_(0), | 
|  | max_tilt_(0), | 
|  | tilt_(0), | 
|  | min_zoom_(0), | 
|  | max_zoom_(0), | 
|  | zoom_(0), | 
|  | min_focus_(0), | 
|  | max_focus_(0), | 
|  | focus_(0), | 
|  | got_pan_(false), | 
|  | got_tilt_(false), | 
|  | got_zoom_(false), | 
|  | got_focus_(false), | 
|  | success_(false) {} | 
|  |  | 
|  | WebcamPrivateGetFunction::~WebcamPrivateGetFunction() { | 
|  | } | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateGetFunction::Run() { | 
|  | std::optional<webcam_private::Get::Params> params = | 
|  | webcam_private::Get::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | auto on_webcam = base::BindOnce(&WebcamPrivateGetFunction::OnWebcam, this); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->GetWebcam(extension_id(), params->webcam_id, std::move(on_webcam)); | 
|  |  | 
|  | // Might have already responded if webcam_resource_manager_ already has the | 
|  | // Webcam. | 
|  | return did_respond() ? AlreadyResponded() : RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateGetFunction::OnWebcam(Webcam* webcam) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | if (!webcam) { | 
|  | Respond(Error(kUnknownWebcam)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | webcam->GetPan(base::BindRepeating( | 
|  | &WebcamPrivateGetFunction::OnGetWebcamParameters, this, INQUIRY_PAN)); | 
|  | webcam->GetTilt(base::BindRepeating( | 
|  | &WebcamPrivateGetFunction::OnGetWebcamParameters, this, INQUIRY_TILT)); | 
|  | webcam->GetZoom(base::BindRepeating( | 
|  | &WebcamPrivateGetFunction::OnGetWebcamParameters, this, INQUIRY_ZOOM)); | 
|  | webcam->GetFocus(base::BindRepeating( | 
|  | &WebcamPrivateGetFunction::OnGetWebcamParameters, this, INQUIRY_FOCUS)); | 
|  | } | 
|  |  | 
|  | // Retrieve webcam parameters. Will respond a config holding the requested | 
|  | // values if any of the requests succeeds. Otherwise will respond an error. | 
|  | void WebcamPrivateGetFunction::OnGetWebcamParameters(InquiryType type, | 
|  | bool success, | 
|  | int value, | 
|  | int min_value, | 
|  | int max_value) { | 
|  | success_ = success_ || success; | 
|  |  | 
|  | switch (type) { | 
|  | case INQUIRY_PAN: | 
|  | if (success) { | 
|  | min_pan_ = min_value; | 
|  | max_pan_ = max_value; | 
|  | pan_ = value; | 
|  | } | 
|  | got_pan_ = true; | 
|  | break; | 
|  | case INQUIRY_TILT: | 
|  | if (success) { | 
|  | min_tilt_ = min_value; | 
|  | max_tilt_ = max_value; | 
|  | tilt_ = value; | 
|  | } | 
|  | got_tilt_ = true; | 
|  | break; | 
|  | case INQUIRY_ZOOM: | 
|  | if (success) { | 
|  | min_zoom_ = min_value; | 
|  | max_zoom_ = max_value; | 
|  | zoom_ = value; | 
|  | } | 
|  | got_zoom_ = true; | 
|  | break; | 
|  | case INQUIRY_FOCUS: | 
|  | if (success) { | 
|  | min_focus_ = min_value; | 
|  | max_focus_ = max_value; | 
|  | focus_ = value; | 
|  | } | 
|  | got_focus_ = true; | 
|  | break; | 
|  | } | 
|  | if (got_pan_ && got_tilt_ && got_zoom_ && got_focus_) { | 
|  | if (!success_) { | 
|  | Respond(Error(kGetWebcamPTZError)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | webcam_private::WebcamCurrentConfiguration result; | 
|  | if (min_pan_ != max_pan_) { | 
|  | result.pan_range.emplace(); | 
|  | result.pan_range->min = min_pan_; | 
|  | result.pan_range->max = max_pan_; | 
|  | } | 
|  | if (min_tilt_ != max_tilt_) { | 
|  | result.tilt_range.emplace(); | 
|  | result.tilt_range->min = min_tilt_; | 
|  | result.tilt_range->max = max_tilt_; | 
|  | } | 
|  | if (min_zoom_ != max_zoom_) { | 
|  | result.zoom_range.emplace(); | 
|  | result.zoom_range->min = min_zoom_; | 
|  | result.zoom_range->max = max_zoom_; | 
|  | } | 
|  | if (min_focus_ != max_focus_) { | 
|  | result.focus_range.emplace(); | 
|  | result.focus_range->min = min_focus_; | 
|  | result.focus_range->max = max_focus_; | 
|  | } | 
|  |  | 
|  | result.pan = pan_; | 
|  | result.tilt = tilt_; | 
|  | result.zoom = zoom_; | 
|  | result.focus = focus_; | 
|  | Respond(WithArguments(result.ToValue())); | 
|  | } | 
|  | } | 
|  |  | 
|  | WebcamPrivateResetFunction::WebcamPrivateResetFunction() { | 
|  | } | 
|  |  | 
|  | WebcamPrivateResetFunction::~WebcamPrivateResetFunction() { | 
|  | } | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateResetFunction::Run() { | 
|  | std::optional<webcam_private::Reset::Params> params = | 
|  | webcam_private::Reset::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | std::string webcam_id = params->webcam_id; | 
|  |  | 
|  | auto on_webcam = base::BindOnce(&WebcamPrivateResetFunction::OnWebcam, this, | 
|  | std::move(params)); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->GetWebcam(extension_id(), webcam_id, std::move(on_webcam)); | 
|  |  | 
|  | // Might have already responsed if webcam_resource_manager_ already has the | 
|  | // Webcam and WebCam::Reset just runs the callback. | 
|  | return did_respond() ? AlreadyResponded() : RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateResetFunction::OnWebcam( | 
|  | std::optional<webcam_private::Reset::Params> params, | 
|  | Webcam* webcam) { | 
|  | if (!webcam) | 
|  | return Respond(Error(kUnknownWebcam)); | 
|  |  | 
|  | webcam->Reset( | 
|  | params->config.pan.has_value(), params->config.tilt.has_value(), | 
|  | params->config.zoom.has_value(), | 
|  | base::BindRepeating(&WebcamPrivateResetFunction::OnResetWebcam, this)); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateResetFunction::OnResetWebcam(bool success) { | 
|  | if (!success) { | 
|  | Respond(Error(kResetWebcamError)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Reply with a dummy, empty configuration. | 
|  | webcam_private::WebcamCurrentConfiguration result; | 
|  | Respond(WithArguments(result.ToValue())); | 
|  | } | 
|  |  | 
|  | WebcamPrivateSetHomeFunction::WebcamPrivateSetHomeFunction() = default; | 
|  |  | 
|  | WebcamPrivateSetHomeFunction::~WebcamPrivateSetHomeFunction() = default; | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateSetHomeFunction::Run() { | 
|  | std::optional<webcam_private::SetHome::Params> params = | 
|  | webcam_private::SetHome::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | auto on_webcam = | 
|  | base::BindOnce(&WebcamPrivateSetHomeFunction::OnWebcam, this); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->GetWebcam(extension_id(), params->webcam_id, std::move(on_webcam)); | 
|  |  | 
|  | return did_respond() ? AlreadyResponded() : RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateSetHomeFunction::OnWebcam(Webcam* webcam) { | 
|  | if (!webcam) | 
|  | return Respond(Error(kUnknownWebcam)); | 
|  |  | 
|  | webcam->SetHome(base::BindRepeating( | 
|  | &WebcamPrivateSetHomeFunction::OnSetHomeWebcam, this)); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateSetHomeFunction::OnSetHomeWebcam(bool success) { | 
|  | if (!success) { | 
|  | Respond(Error(kSetHomeWebcamError)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Reply with a dummy, empty configuration. | 
|  | webcam_private::WebcamCurrentConfiguration result; | 
|  | Respond(WithArguments(result.ToValue())); | 
|  | } | 
|  |  | 
|  | WebcamPrivateRestoreCameraPresetFunction:: | 
|  | WebcamPrivateRestoreCameraPresetFunction() {} | 
|  |  | 
|  | WebcamPrivateRestoreCameraPresetFunction:: | 
|  | ~WebcamPrivateRestoreCameraPresetFunction() {} | 
|  |  | 
|  | ExtensionFunction::ResponseAction | 
|  | WebcamPrivateRestoreCameraPresetFunction::Run() { | 
|  | std::optional<webcam_private::RestoreCameraPreset::Params> params = | 
|  | webcam_private::RestoreCameraPreset::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | auto on_webcam = | 
|  | base::BindOnce(&WebcamPrivateRestoreCameraPresetFunction::OnWebcam, this, | 
|  | params->preset_number); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->GetWebcam(extension_id(), params->webcam_id, std::move(on_webcam)); | 
|  |  | 
|  | return did_respond() ? AlreadyResponded() : RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateRestoreCameraPresetFunction::OnWebcam(int preset_number, | 
|  | Webcam* webcam) { | 
|  | if (!webcam) { | 
|  | Respond(Error(kUnknownWebcam)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | webcam->RestoreCameraPreset( | 
|  | preset_number, | 
|  | base::BindRepeating(&WebcamPrivateRestoreCameraPresetFunction:: | 
|  | OnRestoreCameraPresetWebcam, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateRestoreCameraPresetFunction::OnRestoreCameraPresetWebcam( | 
|  | bool success) { | 
|  | if (!success) { | 
|  | Respond(Error(kRestorePresetWebcamError)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Reply with a dummy, empty configuration. | 
|  | webcam_private::WebcamCurrentConfiguration result; | 
|  | Respond(WithArguments(result.ToValue())); | 
|  | } | 
|  |  | 
|  | WebcamPrivateSetCameraPresetFunction::WebcamPrivateSetCameraPresetFunction() = | 
|  | default; | 
|  |  | 
|  | WebcamPrivateSetCameraPresetFunction::~WebcamPrivateSetCameraPresetFunction() = | 
|  | default; | 
|  |  | 
|  | ExtensionFunction::ResponseAction WebcamPrivateSetCameraPresetFunction::Run() { | 
|  | std::optional<webcam_private::SetCameraPreset::Params> params = | 
|  | webcam_private::SetCameraPreset::Params::Create(args()); | 
|  | EXTENSION_FUNCTION_VALIDATE(params); | 
|  |  | 
|  | auto on_webcam = | 
|  | base::BindOnce(&WebcamPrivateSetCameraPresetFunction::OnWebcam, this, | 
|  | params->preset_number); | 
|  |  | 
|  | WebcamPrivateAPI::Get(browser_context()) | 
|  | ->GetWebcam(extension_id(), params->webcam_id, std::move(on_webcam)); | 
|  |  | 
|  | return did_respond() ? AlreadyResponded() : RespondLater(); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateSetCameraPresetFunction::OnWebcam(int preset_number, | 
|  | Webcam* webcam) { | 
|  | if (!webcam) { | 
|  | Respond(Error(kUnknownWebcam)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | webcam->SetCameraPreset( | 
|  | preset_number, | 
|  | base::BindRepeating( | 
|  | &WebcamPrivateSetCameraPresetFunction::OnSetCameraPresetWebcam, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | void WebcamPrivateSetCameraPresetFunction::OnSetCameraPresetWebcam( | 
|  | bool success) { | 
|  | if (!success) { | 
|  | Respond(Error(kSetPresetWebcamError)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Reply with a dummy, empty configuration. | 
|  | webcam_private::WebcamCurrentConfiguration result; | 
|  | Respond(WithArguments(result.ToValue())); | 
|  | } | 
|  |  | 
|  | static base::LazyInstance<BrowserContextKeyedAPIFactory<WebcamPrivateAPI>>:: | 
|  | DestructorAtExit g_factory = LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | // static | 
|  | BrowserContextKeyedAPIFactory<WebcamPrivateAPI>* | 
|  | WebcamPrivateAPI::GetFactoryInstance() { | 
|  | return g_factory.Pointer(); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void BrowserContextKeyedAPIFactory<WebcamPrivateAPI> | 
|  | ::DeclareFactoryDependencies() { | 
|  | DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); | 
|  | DependsOn(ProcessManagerFactory::GetInstance()); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |