1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/image_capture/image_capture_impl.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/unguessable_token.h"
12 #include "content/browser/browser_main_loop.h"
13 #include "content/browser/media/media_devices_permission_checker.h"
14 #include "content/browser/renderer_host/media/media_stream_manager.h"
15 #include "content/browser/renderer_host/media/video_capture_manager.h"
16 #include "content/public/browser/browser_task_traits.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/common/content_features.h"
21 #include "media/base/bind_to_current_loop.h"
22 #include "media/capture/mojom/image_capture_types.h"
23 #include "media/capture/video/video_capture_device.h"
24 #include "mojo/public/cpp/bindings/callback_helpers.h"
25 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
26 
27 namespace content {
28 
29 namespace {
30 
GetPhotoStateOnIOThread(const std::string & source_id,MediaStreamManager * media_stream_manager,ImageCaptureImpl::GetPhotoStateCallback callback)31 void GetPhotoStateOnIOThread(const std::string& source_id,
32                              MediaStreamManager* media_stream_manager,
33                              ImageCaptureImpl::GetPhotoStateCallback callback) {
34   DCHECK_CURRENTLY_ON(BrowserThread::IO);
35 
36   const base::UnguessableToken session_id =
37       media_stream_manager->VideoDeviceIdToSessionId(source_id);
38   if (session_id.is_empty())
39     return;
40 
41   media_stream_manager->video_capture_manager()->GetPhotoState(
42       session_id, std::move(callback));
43 }
44 
SetOptionsOnIOThread(const std::string & source_id,MediaStreamManager * media_stream_manager,media::mojom::PhotoSettingsPtr settings,ImageCaptureImpl::SetOptionsCallback callback)45 void SetOptionsOnIOThread(const std::string& source_id,
46                           MediaStreamManager* media_stream_manager,
47                           media::mojom::PhotoSettingsPtr settings,
48                           ImageCaptureImpl::SetOptionsCallback callback) {
49   DCHECK_CURRENTLY_ON(BrowserThread::IO);
50 
51   const base::UnguessableToken session_id =
52       media_stream_manager->VideoDeviceIdToSessionId(source_id);
53   if (session_id.is_empty())
54     return;
55   media_stream_manager->video_capture_manager()->SetPhotoOptions(
56       session_id, std::move(settings), std::move(callback));
57 }
58 
TakePhotoOnIOThread(const std::string & source_id,MediaStreamManager * media_stream_manager,ImageCaptureImpl::TakePhotoCallback callback)59 void TakePhotoOnIOThread(const std::string& source_id,
60                          MediaStreamManager* media_stream_manager,
61                          ImageCaptureImpl::TakePhotoCallback callback) {
62   DCHECK_CURRENTLY_ON(BrowserThread::IO);
63   TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
64                        "image_capture_impl.cc::TakePhotoOnIOThread",
65                        TRACE_EVENT_SCOPE_PROCESS);
66 
67   const base::UnguessableToken session_id =
68       media_stream_manager->VideoDeviceIdToSessionId(source_id);
69   if (session_id.is_empty())
70     return;
71 
72   media_stream_manager->video_capture_manager()->TakePhoto(session_id,
73                                                            std::move(callback));
74 }
75 
76 }  // anonymous namespace
77 
78 // static
Create(RenderFrameHost * render_frame_host,mojo::PendingReceiver<media::mojom::ImageCapture> receiver)79 void ImageCaptureImpl::Create(
80     RenderFrameHost* render_frame_host,
81     mojo::PendingReceiver<media::mojom::ImageCapture> receiver) {
82   DCHECK(render_frame_host);
83   // ImageCaptureImpl owns itself. It will self-destruct when a Mojo interface
84   // error occurs, the render frame host is deleted, or the render frame host
85   // navigates to a new document.
86   new ImageCaptureImpl(render_frame_host, std::move(receiver));
87 }
88 
GetPhotoState(const std::string & source_id,GetPhotoStateCallback callback)89 void ImageCaptureImpl::GetPhotoState(const std::string& source_id,
90                                      GetPhotoStateCallback callback) {
91   DCHECK_CURRENTLY_ON(BrowserThread::UI);
92   TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
93                        "ImageCaptureImpl::GetPhotoState",
94                        TRACE_EVENT_SCOPE_PROCESS);
95 
96   GetPhotoStateCallback scoped_callback =
97       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
98           media::BindToCurrentLoop(
99               base::BindOnce(&ImageCaptureImpl::OnGetPhotoState,
100                              weak_factory_.GetWeakPtr(), std::move(callback))),
101           mojo::CreateEmptyPhotoState());
102   GetIOThreadTaskRunner({})->PostTask(
103       FROM_HERE,
104       base::BindOnce(&GetPhotoStateOnIOThread, source_id,
105                      BrowserMainLoop::GetInstance()->media_stream_manager(),
106                      std::move(scoped_callback)));
107 }
108 
SetOptions(const std::string & source_id,media::mojom::PhotoSettingsPtr settings,SetOptionsCallback callback)109 void ImageCaptureImpl::SetOptions(const std::string& source_id,
110                                   media::mojom::PhotoSettingsPtr settings,
111                                   SetOptionsCallback callback) {
112   DCHECK_CURRENTLY_ON(BrowserThread::UI);
113   TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
114                        "ImageCaptureImpl::SetOptions",
115                        TRACE_EVENT_SCOPE_PROCESS);
116 
117   if ((settings->has_pan || settings->has_tilt || settings->has_zoom) &&
118       !HasPanTiltZoomPermissionGranted()) {
119     std::move(callback).Run(false);
120     return;
121   }
122 
123   SetOptionsCallback scoped_callback =
124       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
125           media::BindToCurrentLoop(std::move(callback)), false);
126   GetIOThreadTaskRunner({})->PostTask(
127       FROM_HERE,
128       base::BindOnce(&SetOptionsOnIOThread, source_id,
129                      BrowserMainLoop::GetInstance()->media_stream_manager(),
130                      std::move(settings), std::move(scoped_callback)));
131 }
132 
TakePhoto(const std::string & source_id,TakePhotoCallback callback)133 void ImageCaptureImpl::TakePhoto(const std::string& source_id,
134                                  TakePhotoCallback callback) {
135   DCHECK_CURRENTLY_ON(BrowserThread::UI);
136   TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
137                        "ImageCaptureImpl::TakePhoto",
138                        TRACE_EVENT_SCOPE_PROCESS);
139 
140   TakePhotoCallback scoped_callback =
141       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
142           media::BindToCurrentLoop(std::move(callback)),
143           media::mojom::Blob::New());
144   GetIOThreadTaskRunner({})->PostTask(
145       FROM_HERE,
146       base::BindOnce(&TakePhotoOnIOThread, source_id,
147                      BrowserMainLoop::GetInstance()->media_stream_manager(),
148                      std::move(scoped_callback)));
149 }
150 
ImageCaptureImpl(RenderFrameHost * render_frame_host,mojo::PendingReceiver<media::mojom::ImageCapture> receiver)151 ImageCaptureImpl::ImageCaptureImpl(
152     RenderFrameHost* render_frame_host,
153     mojo::PendingReceiver<media::mojom::ImageCapture> receiver)
154     : FrameServiceBase(render_frame_host, std::move(receiver)) {}
155 
156 ImageCaptureImpl::~ImageCaptureImpl() = default;
157 
OnGetPhotoState(GetPhotoStateCallback callback,media::mojom::PhotoStatePtr state)158 void ImageCaptureImpl::OnGetPhotoState(GetPhotoStateCallback callback,
159                                        media::mojom::PhotoStatePtr state) {
160   DCHECK_CURRENTLY_ON(BrowserThread::UI);
161   if (!HasPanTiltZoomPermissionGranted()) {
162     state->pan = media::mojom::Range::New();
163     state->tilt = media::mojom::Range::New();
164     state->zoom = media::mojom::Range::New();
165   }
166   std::move(callback).Run(std::move(state));
167 }
168 
HasPanTiltZoomPermissionGranted()169 bool ImageCaptureImpl::HasPanTiltZoomPermissionGranted() {
170   DCHECK_CURRENTLY_ON(BrowserThread::UI);
171 
172   return MediaDevicesPermissionChecker::
173       HasPanTiltZoomPermissionGrantedOnUIThread(
174           render_frame_host()->GetProcess()->GetID(),
175           render_frame_host()->GetRoutingID());
176 }
177 }  // namespace content
178