1// Copyright 2013 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/renderer/media/webrtc_identity_service.h"
6
7#include "content/common/media/webrtc_identity_messages.h"
8#include "content/public/renderer/render_thread.h"
9#include "net/base/net_errors.h"
10
11namespace content {
12
13WebRTCIdentityService::RequestInfo::RequestInfo(
14    int request_id,
15    const GURL& origin,
16    const std::string& identity_name,
17    const std::string& common_name,
18    const SuccessCallback& success_callback,
19    const FailureCallback& failure_callback)
20    : request_id(request_id),
21      origin(origin),
22      identity_name(identity_name),
23      common_name(common_name),
24      success_callback(success_callback),
25      failure_callback(failure_callback) {}
26
27WebRTCIdentityService::RequestInfo::~RequestInfo() {}
28
29WebRTCIdentityService::WebRTCIdentityService() : next_request_id_(1) {
30  // RenderThread::Get() could be NULL in unit tests.
31  if (RenderThread::Get())
32    RenderThread::Get()->AddObserver(this);
33}
34
35WebRTCIdentityService::~WebRTCIdentityService() {
36  // RenderThread::Get() could be NULL in unit tests.
37  if (RenderThread::Get()) {
38    RenderThread::Get()->RemoveObserver(this);
39
40    if (!pending_requests_.empty()) {
41      RenderThread::Get()->Send(new WebRTCIdentityMsg_CancelRequest());
42    }
43  }
44}
45
46int WebRTCIdentityService::RequestIdentity(
47    const GURL& origin,
48    const std::string& identity_name,
49    const std::string& common_name,
50    const SuccessCallback& success_callback,
51    const FailureCallback& failure_callback) {
52  int request_id = next_request_id_++;
53
54  RequestInfo request_info(request_id,
55                           origin,
56                           identity_name,
57                           common_name,
58                           success_callback,
59                           failure_callback);
60
61  pending_requests_.push_back(request_info);
62  if (pending_requests_.size() == 1)
63    SendRequest(request_info);
64
65  return request_id;
66}
67
68void WebRTCIdentityService::CancelRequest(int request_id) {
69  std::deque<RequestInfo>::iterator it;
70  for (it = pending_requests_.begin(); it != pending_requests_.end(); ++it) {
71    if (it->request_id != request_id)
72      continue;
73    if (it != pending_requests_.begin()) {
74      pending_requests_.erase(it);
75    } else {
76      Send(new WebRTCIdentityMsg_CancelRequest());
77      OnOutstandingRequestReturned();
78    }
79    break;
80  }
81}
82
83bool WebRTCIdentityService::Send(IPC::Message* message) {
84  // Unit tests should override this method to avoid null-ptr-deref.
85  return RenderThread::Get()->Send(message);
86}
87
88bool WebRTCIdentityService::OnControlMessageReceived(
89    const IPC::Message& message) {
90  bool handled = true;
91  IPC_BEGIN_MESSAGE_MAP(WebRTCIdentityService, message)
92    IPC_MESSAGE_HANDLER(WebRTCIdentityHostMsg_IdentityReady, OnIdentityReady)
93    IPC_MESSAGE_HANDLER(WebRTCIdentityHostMsg_RequestFailed, OnRequestFailed)
94    IPC_MESSAGE_UNHANDLED(handled = false)
95  IPC_END_MESSAGE_MAP()
96
97  return handled;
98}
99
100void WebRTCIdentityService::OnIdentityReady(int request_id,
101                                            const std::string& certificate,
102                                            const std::string& private_key) {
103  // The browser process may have sent the response before it receives the
104  // message to cancel the request. So we need to check if the returned response
105  // matches the request on the top of the queue.
106  if (pending_requests_.empty() ||
107      pending_requests_.front().request_id != request_id)
108    return;
109
110  pending_requests_.front().success_callback.Run(certificate, private_key);
111  OnOutstandingRequestReturned();
112}
113
114void WebRTCIdentityService::OnRequestFailed(int request_id, int error) {
115  // The browser process may have sent the response before it receives the
116  // message to cancel the request. So we need to check if the returned response
117  // matches the request on the top of the queue.
118  if (pending_requests_.empty() ||
119      pending_requests_.front().request_id != request_id)
120    return;
121
122  pending_requests_.front().failure_callback.Run(error);
123  OnOutstandingRequestReturned();
124}
125
126void WebRTCIdentityService::SendRequest(const RequestInfo& request_info) {
127  if (!Send(new WebRTCIdentityMsg_RequestIdentity(request_info.request_id,
128                                                  request_info.origin,
129                                                  request_info.identity_name,
130                                                  request_info.common_name))) {
131    base::MessageLoop::current()->PostTask(
132        FROM_HERE,
133        base::Bind(&WebRTCIdentityService::OnRequestFailed,
134                   base::Unretained(this),
135                   request_info.request_id,
136                   net::ERR_UNEXPECTED));
137  }
138}
139
140void WebRTCIdentityService::OnOutstandingRequestReturned() {
141  pending_requests_.pop_front();
142
143  if (!pending_requests_.empty())
144    SendRequest(pending_requests_.front());
145}
146
147}  // namespace content
148