Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / media / cdm / browser_cdm_manager.cc
1 // Copyright 2014 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/media/cdm/browser_cdm_manager.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/lazy_instance.h"
10 #include "base/task_runner.h"
11 #include "content/common/media/cdm_messages.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/content_browser_client.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/common/content_switches.h"
19 #include "media/base/browser_cdm.h"
20 #include "media/base/browser_cdm_factory.h"
21 #include "media/base/media_switches.h"
22
23 namespace content {
24
25 using media::BrowserCdm;
26 using media::MediaKeys;
27
28 // Maximum lengths for various EME API parameters. These are checks to
29 // prevent unnecessarily large parameters from being passed around, and the
30 // lengths are somewhat arbitrary as the EME spec doesn't specify any limits.
31 const size_t kMaxInitDataLength = 64 * 1024;  // 64 KB
32 const size_t kMaxSessionResponseLength = 64 * 1024;  // 64 KB
33 const size_t kMaxKeySystemLength = 256;
34
35 // The ID used in this class is a concatenation of |render_frame_id| and
36 // |cdm_id|, i.e. (render_frame_id << 32) + cdm_id.
37
38 static uint64 GetId(int render_frame_id, int cdm_id) {
39   return (static_cast<uint64>(render_frame_id) << 32) +
40          static_cast<uint64>(cdm_id);
41 }
42
43 static bool IdBelongsToFrame(uint64 id, int render_frame_id) {
44   return (id >> 32) == static_cast<uint64>(render_frame_id);
45 }
46
47 // Render process ID to BrowserCdmManager map.
48 typedef std::map<int, BrowserCdmManager*> BrowserCdmManagerMap;
49 base::LazyInstance<BrowserCdmManagerMap> g_browser_cdm_manager_map =
50     LAZY_INSTANCE_INITIALIZER;
51
52 BrowserCdmManager* BrowserCdmManager::FromProcess(int render_process_id) {
53   DCHECK_CURRENTLY_ON(BrowserThread::UI);
54
55   if (!g_browser_cdm_manager_map.Get().count(render_process_id))
56     return NULL;
57
58   return g_browser_cdm_manager_map.Get()[render_process_id];
59 }
60
61 BrowserCdmManager::BrowserCdmManager(
62     int render_process_id,
63     const scoped_refptr<base::TaskRunner>& task_runner)
64     : BrowserMessageFilter(CdmMsgStart),
65       render_process_id_(render_process_id),
66       task_runner_(task_runner) {
67   DCHECK_CURRENTLY_ON(BrowserThread::UI);
68
69   if (!task_runner_.get()) {
70     task_runner_ =
71         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
72   }
73
74   // This may overwrite an existing entry of |render_process_id| if the
75   // previous process crashed and didn't cleanup its child frames. For example,
76   // see FrameTreeBrowserTest.FrameTreeAfterCrash test.
77   g_browser_cdm_manager_map.Get()[render_process_id] = this;
78 }
79
80 BrowserCdmManager::~BrowserCdmManager() {
81   DCHECK_CURRENTLY_ON(BrowserThread::UI);
82   DCHECK(g_browser_cdm_manager_map.Get().count(render_process_id_));
83
84   g_browser_cdm_manager_map.Get().erase(render_process_id_);
85 }
86
87 // Makes sure BrowserCdmManager is always deleted on the Browser UI thread.
88 void BrowserCdmManager::OnDestruct() const {
89   if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
90     delete this;
91   } else {
92     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
93   }
94 }
95
96 base::TaskRunner* BrowserCdmManager::OverrideTaskRunnerForMessage(
97     const IPC::Message& message) {
98   // Only handles CDM messages.
99   if (IPC_MESSAGE_CLASS(message) != CdmMsgStart)
100     return NULL;
101
102   return task_runner_.get();
103 }
104
105 bool BrowserCdmManager::OnMessageReceived(const IPC::Message& msg) {
106   DCHECK(task_runner_->RunsTasksOnCurrentThread());
107   bool handled = true;
108   IPC_BEGIN_MESSAGE_MAP(BrowserCdmManager, msg)
109     IPC_MESSAGE_HANDLER(CdmHostMsg_InitializeCdm, OnInitializeCdm)
110     IPC_MESSAGE_HANDLER(CdmHostMsg_CreateSession, OnCreateSession)
111     IPC_MESSAGE_HANDLER(CdmHostMsg_UpdateSession, OnUpdateSession)
112     IPC_MESSAGE_HANDLER(CdmHostMsg_ReleaseSession, OnReleaseSession)
113     IPC_MESSAGE_HANDLER(CdmHostMsg_DestroyCdm, OnDestroyCdm)
114     IPC_MESSAGE_UNHANDLED(handled = false)
115   IPC_END_MESSAGE_MAP()
116   return handled;
117 }
118
119 media::BrowserCdm* BrowserCdmManager::GetCdm(int render_frame_id, int cdm_id) {
120   DCHECK(task_runner_->RunsTasksOnCurrentThread());
121   return cdm_map_.get(GetId(render_frame_id, cdm_id));
122 }
123
124 void BrowserCdmManager::RenderFrameDeleted(int render_frame_id) {
125   DCHECK(task_runner_->RunsTasksOnCurrentThread());
126
127   std::vector<uint64> ids_to_remove;
128   for (CdmMap::iterator it = cdm_map_.begin(); it != cdm_map_.end(); ++it) {
129     if (IdBelongsToFrame(it->first, render_frame_id))
130       ids_to_remove.push_back(it->first);
131   }
132
133   for (size_t i = 0; i < ids_to_remove.size(); ++i)
134     RemoveCdm(ids_to_remove[i]);
135 }
136
137 void BrowserCdmManager::OnSessionCreated(int render_frame_id,
138                                          int cdm_id,
139                                          uint32 session_id,
140                                          const std::string& web_session_id) {
141   Send(new CdmMsg_SessionCreated(
142       render_frame_id, cdm_id, session_id, web_session_id));
143 }
144
145 void BrowserCdmManager::OnSessionMessage(int render_frame_id,
146                                          int cdm_id,
147                                          uint32 session_id,
148                                          const std::vector<uint8>& message,
149                                          const GURL& destination_url) {
150   GURL verified_gurl = destination_url;
151   if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) {
152     DLOG(WARNING) << "SessionMessage destination_url is invalid : "
153                   << destination_url.possibly_invalid_spec();
154     verified_gurl = GURL::EmptyGURL();  // Replace invalid destination_url.
155   }
156
157   Send(new CdmMsg_SessionMessage(
158       render_frame_id, cdm_id, session_id, message, verified_gurl));
159 }
160
161 void BrowserCdmManager::OnSessionReady(int render_frame_id,
162                                        int cdm_id,
163                                        uint32 session_id) {
164   Send(new CdmMsg_SessionReady(render_frame_id, cdm_id, session_id));
165 }
166
167 void BrowserCdmManager::OnSessionClosed(int render_frame_id,
168                                         int cdm_id,
169                                         uint32 session_id) {
170   Send(new CdmMsg_SessionClosed(render_frame_id, cdm_id, session_id));
171 }
172
173 void BrowserCdmManager::OnSessionError(int render_frame_id,
174                                        int cdm_id,
175                                        uint32 session_id,
176                                        MediaKeys::KeyError error_code,
177                                        uint32 system_code) {
178   Send(new CdmMsg_SessionError(
179       render_frame_id, cdm_id, session_id, error_code, system_code));
180 }
181
182 void BrowserCdmManager::OnInitializeCdm(int render_frame_id,
183                                         int cdm_id,
184                                         const std::string& key_system,
185                                         const GURL& security_origin) {
186   if (key_system.size() > kMaxKeySystemLength) {
187     // This failure will be discovered and reported by OnCreateSession()
188     // as GetCdm() will return null.
189     NOTREACHED() << "Invalid key system: " << key_system;
190     return;
191   }
192
193   AddCdm(render_frame_id, cdm_id, key_system, security_origin);
194 }
195
196 void BrowserCdmManager::OnCreateSession(
197     int render_frame_id,
198     int cdm_id,
199     uint32 session_id,
200     CdmHostMsg_CreateSession_ContentType content_type,
201     const std::vector<uint8>& init_data) {
202   if (init_data.size() > kMaxInitDataLength) {
203     LOG(WARNING) << "InitData for ID: " << cdm_id
204                  << " too long: " << init_data.size();
205     SendSessionError(render_frame_id, cdm_id, session_id);
206     return;
207   }
208
209   // Convert the session content type into a MIME type. "audio" and "video"
210   // don't matter, so using "video" for the MIME type.
211   // Ref:
212   // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-createsession
213   std::string mime_type;
214   switch (content_type) {
215     case CREATE_SESSION_TYPE_WEBM:
216       mime_type = "video/webm";
217       break;
218     case CREATE_SESSION_TYPE_MP4:
219       mime_type = "video/mp4";
220       break;
221     default:
222       NOTREACHED();
223       return;
224   }
225
226 #if defined(OS_ANDROID)
227   if (CommandLine::ForCurrentProcess()
228       ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) {
229     CreateSessionIfPermitted(
230         render_frame_id, cdm_id, session_id, mime_type, init_data, true);
231     return;
232   }
233 #endif
234
235   BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
236   if (!cdm) {
237     DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
238     SendSessionError(render_frame_id, cdm_id, session_id);
239     return;
240   }
241
242   std::map<uint64, GURL>::const_iterator iter =
243       cdm_security_origin_map_.find(GetId(render_frame_id, cdm_id));
244   if (iter == cdm_security_origin_map_.end()) {
245     NOTREACHED();
246     SendSessionError(render_frame_id, cdm_id, session_id);
247     return;
248   }
249   GURL security_origin = iter->second;
250
251   RenderFrameHost* rfh =
252       RenderFrameHost::FromID(render_process_id_, render_frame_id);
253   WebContents* web_contents = WebContents::FromRenderFrameHost(rfh);
254   DCHECK(web_contents);
255   GetContentClient()->browser()->RequestPermission(
256       content::PERMISSION_PROTECTED_MEDIA,
257       web_contents,
258       0,  // bridge id
259       security_origin,
260       // Only implemented for Android infobars which do not support
261       // user gestures.
262       true,
263       base::Bind(&BrowserCdmManager::CreateSessionIfPermitted,
264                  this,
265                  render_frame_id,
266                  cdm_id,
267                  session_id,
268                  mime_type,
269                  init_data));
270 }
271
272 void BrowserCdmManager::OnUpdateSession(
273     int render_frame_id,
274     int cdm_id,
275     uint32 session_id,
276     const std::vector<uint8>& response) {
277   BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
278   if (!cdm) {
279     DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
280     SendSessionError(render_frame_id, cdm_id, session_id);
281     return;
282   }
283
284   if (response.size() > kMaxSessionResponseLength) {
285     LOG(WARNING) << "Response for ID " << cdm_id
286                  << " is too long: " << response.size();
287     SendSessionError(render_frame_id, cdm_id, session_id);
288     return;
289   }
290
291   cdm->UpdateSession(session_id, &response[0], response.size());
292 }
293
294 void BrowserCdmManager::OnReleaseSession(int render_frame_id,
295                                          int cdm_id,
296                                          uint32 session_id) {
297   BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
298   if (!cdm) {
299     DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
300     SendSessionError(render_frame_id, cdm_id, session_id);
301     return;
302   }
303
304   cdm->ReleaseSession(session_id);
305 }
306
307 void BrowserCdmManager::OnDestroyCdm(int render_frame_id, int cdm_id) {
308   RemoveCdm(GetId(render_frame_id, cdm_id));
309 }
310
311 void BrowserCdmManager::SendSessionError(int render_frame_id,
312                                          int cdm_id,
313                                          uint32 session_id) {
314   OnSessionError(
315         render_frame_id, cdm_id, session_id, MediaKeys::kUnknownError, 0);
316 }
317
318 #define BROWSER_CDM_MANAGER_CB(func) \
319   base::Bind(&BrowserCdmManager::func, this, render_frame_id, cdm_id)
320
321 void BrowserCdmManager::AddCdm(int render_frame_id,
322                                int cdm_id,
323                                const std::string& key_system,
324                                const GURL& security_origin) {
325   DCHECK(task_runner_->RunsTasksOnCurrentThread());
326   DCHECK(!GetCdm(render_frame_id, cdm_id));
327
328   scoped_ptr<BrowserCdm> cdm(
329       media::CreateBrowserCdm(key_system,
330                               BROWSER_CDM_MANAGER_CB(OnSessionCreated),
331                               BROWSER_CDM_MANAGER_CB(OnSessionMessage),
332                               BROWSER_CDM_MANAGER_CB(OnSessionReady),
333                               BROWSER_CDM_MANAGER_CB(OnSessionClosed),
334                               BROWSER_CDM_MANAGER_CB(OnSessionError)));
335
336   if (!cdm) {
337     // This failure will be discovered and reported by OnCreateSession()
338     // as GetCdm() will return null.
339     DVLOG(1) << "failed to create CDM.";
340     return;
341   }
342
343   uint64 id = GetId(render_frame_id, cdm_id);
344   cdm_map_.add(id, cdm.Pass());
345   cdm_security_origin_map_[id] = security_origin;
346 }
347
348 void BrowserCdmManager::RemoveCdm(uint64 id) {
349   DCHECK(task_runner_->RunsTasksOnCurrentThread());
350
351   cdm_map_.erase(id);
352   cdm_security_origin_map_.erase(id);
353   if (cdm_cancel_permission_map_.count(id)) {
354     cdm_cancel_permission_map_[id].Run();
355     cdm_cancel_permission_map_.erase(id);
356   }
357 }
358
359 void BrowserCdmManager::CreateSessionIfPermitted(
360     int render_frame_id,
361     int cdm_id,
362     uint32 session_id,
363     const std::string& content_type,
364     const std::vector<uint8>& init_data,
365     bool permitted) {
366   cdm_cancel_permission_map_.erase(GetId(render_frame_id, cdm_id));
367   if (!permitted) {
368     SendSessionError(render_frame_id, cdm_id, session_id);
369     return;
370   }
371
372   BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
373   if (!cdm) {
374     DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
375     SendSessionError(render_frame_id, cdm_id, session_id);
376     return;
377   }
378
379   // This could fail, in which case a SessionError will be fired.
380   cdm->CreateSession(session_id, content_type, &init_data[0], init_data.size());
381 }
382
383 }  // namespace content