#include "content/browser/media/cdm/browser_cdm_manager.h"
+#include "base/bind.h"
#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/task_runner.h"
#include "content/common/media/cdm_messages.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB
const size_t kMaxKeySystemLength = 256;
-// static
-BrowserCdmManager* BrowserCdmManager::Create(RenderFrameHost* rfh) {
- return new BrowserCdmManager(rfh);
+// The ID used in this class is a concatenation of |render_frame_id| and
+// |cdm_id|, i.e. (render_frame_id << 32) + cdm_id.
+
+static uint64 GetId(int render_frame_id, int cdm_id) {
+ return (static_cast<uint64>(render_frame_id) << 32) +
+ static_cast<uint64>(cdm_id);
+}
+
+static bool IdBelongsToFrame(uint64 id, int render_frame_id) {
+ return (id >> 32) == static_cast<uint64>(render_frame_id);
+}
+
+// Render process ID to BrowserCdmManager map.
+typedef std::map<int, BrowserCdmManager*> BrowserCdmManagerMap;
+base::LazyInstance<BrowserCdmManagerMap> g_browser_cdm_manager_map =
+ LAZY_INSTANCE_INITIALIZER;
+
+BrowserCdmManager* BrowserCdmManager::FromProcess(int render_process_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!g_browser_cdm_manager_map.Get().count(render_process_id))
+ return NULL;
+
+ return g_browser_cdm_manager_map.Get()[render_process_id];
}
-BrowserCdmManager::BrowserCdmManager(RenderFrameHost* render_frame_host)
- : render_frame_host_(render_frame_host),
- web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
- weak_ptr_factory_(this) {
+BrowserCdmManager::BrowserCdmManager(
+ int render_process_id,
+ const scoped_refptr<base::TaskRunner>& task_runner)
+ : BrowserMessageFilter(CdmMsgStart),
+ render_process_id_(render_process_id),
+ task_runner_(task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!task_runner_) {
+ task_runner_ =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
+ }
+
+ // This may overwrite an existing entry of |render_process_id| if the
+ // previous process crashed and didn't cleanup its child frames. For example,
+ // see FrameTreeBrowserTest.FrameTreeAfterCrash test.
+ g_browser_cdm_manager_map.Get()[render_process_id] = this;
}
BrowserCdmManager::~BrowserCdmManager() {
- // During the tear down process, OnDestroyCdm() may or may not be called
- // (e.g. WebContents may be destroyed before the render process is killed). So
- // we cannot DCHECK(cdm_map_.empty()) here. Instead, all CDMs in |cdm_map_|
- // will be destroyed here because they are owned by BrowserCdmManager.
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(g_browser_cdm_manager_map.Get().count(render_process_id_));
+
+ g_browser_cdm_manager_map.Get().erase(render_process_id_);
}
-BrowserCdm* BrowserCdmManager::GetCdm(int cdm_id) {
- return cdm_map_.get(cdm_id);
+// Makes sure BrowserCdmManager is always deleted on the Browser UI thread.
+void BrowserCdmManager::OnDestruct() const {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ delete this;
+ } else {
+ BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
+ }
}
-void BrowserCdmManager::OnSessionCreated(
- int cdm_id,
- uint32 session_id,
- const std::string& web_session_id) {
+base::TaskRunner* BrowserCdmManager::OverrideTaskRunnerForMessage(
+ const IPC::Message& message) {
+ // Only handles CDM messages.
+ if (IPC_MESSAGE_CLASS(message) != CdmMsgStart)
+ return NULL;
+
+ return task_runner_.get();
+}
+
+bool BrowserCdmManager::OnMessageReceived(const IPC::Message& msg) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BrowserCdmManager, msg)
+ IPC_MESSAGE_HANDLER(CdmHostMsg_InitializeCdm, OnInitializeCdm)
+ IPC_MESSAGE_HANDLER(CdmHostMsg_CreateSession, OnCreateSession)
+ IPC_MESSAGE_HANDLER(CdmHostMsg_UpdateSession, OnUpdateSession)
+ IPC_MESSAGE_HANDLER(CdmHostMsg_ReleaseSession, OnReleaseSession)
+ IPC_MESSAGE_HANDLER(CdmHostMsg_DestroyCdm, OnDestroyCdm)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+media::BrowserCdm* BrowserCdmManager::GetCdm(int render_frame_id, int cdm_id) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ return cdm_map_.get(GetId(render_frame_id, cdm_id));
+}
+
+void BrowserCdmManager::RenderFrameDeleted(int render_frame_id) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+
+ std::vector<uint64> ids_to_remove;
+ for (CdmMap::iterator it = cdm_map_.begin(); it != cdm_map_.end(); ++it) {
+ if (IdBelongsToFrame(it->first, render_frame_id))
+ ids_to_remove.push_back(it->first);
+ }
+
+ for (size_t i = 0; i < ids_to_remove.size(); ++i)
+ RemoveCdm(ids_to_remove[i]);
+}
+
+void BrowserCdmManager::OnSessionCreated(int render_frame_id,
+ int cdm_id,
+ uint32 session_id,
+ const std::string& web_session_id) {
Send(new CdmMsg_SessionCreated(
- RoutingID(), cdm_id, session_id, web_session_id));
+ render_frame_id, cdm_id, session_id, web_session_id));
}
-void BrowserCdmManager::OnSessionMessage(
- int cdm_id,
- uint32 session_id,
- const std::vector<uint8>& message,
- const GURL& destination_url) {
+void BrowserCdmManager::OnSessionMessage(int render_frame_id,
+ int cdm_id,
+ uint32 session_id,
+ const std::vector<uint8>& message,
+ const GURL& destination_url) {
GURL verified_gurl = destination_url;
if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) {
DLOG(WARNING) << "SessionMessage destination_url is invalid : "
}
Send(new CdmMsg_SessionMessage(
- RoutingID(), cdm_id, session_id, message, verified_gurl));
+ render_frame_id, cdm_id, session_id, message, verified_gurl));
}
-void BrowserCdmManager::OnSessionReady(int cdm_id, uint32 session_id) {
- Send(new CdmMsg_SessionReady(RoutingID(), cdm_id, session_id));
+void BrowserCdmManager::OnSessionReady(int render_frame_id,
+ int cdm_id,
+ uint32 session_id) {
+ Send(new CdmMsg_SessionReady(render_frame_id, cdm_id, session_id));
}
-void BrowserCdmManager::OnSessionClosed(int cdm_id, uint32 session_id) {
- Send(new CdmMsg_SessionClosed(RoutingID(), cdm_id, session_id));
+void BrowserCdmManager::OnSessionClosed(int render_frame_id,
+ int cdm_id,
+ uint32 session_id) {
+ Send(new CdmMsg_SessionClosed(render_frame_id, cdm_id, session_id));
}
-void BrowserCdmManager::OnSessionError(int cdm_id,
+void BrowserCdmManager::OnSessionError(int render_frame_id,
+ int cdm_id,
uint32 session_id,
MediaKeys::KeyError error_code,
uint32 system_code) {
Send(new CdmMsg_SessionError(
- RoutingID(), cdm_id, session_id, error_code, system_code));
+ render_frame_id, cdm_id, session_id, error_code, system_code));
}
-void BrowserCdmManager::OnInitializeCdm(int cdm_id,
- const std::string& key_system,
- const GURL& security_origin) {
+void BrowserCdmManager::OnInitializeCdm(int render_frame_id,
+ int cdm_id,
+ const std::string& key_system,
+ const GURL& security_origin) {
if (key_system.size() > kMaxKeySystemLength) {
// This failure will be discovered and reported by OnCreateSession()
// as GetCdm() will return null.
return;
}
- AddCdm(cdm_id, key_system, security_origin);
+ AddCdm(render_frame_id, cdm_id, key_system, security_origin);
}
void BrowserCdmManager::OnCreateSession(
+ int render_frame_id,
int cdm_id,
uint32 session_id,
CdmHostMsg_CreateSession_ContentType content_type,
if (init_data.size() > kMaxInitDataLength) {
LOG(WARNING) << "InitData for ID: " << cdm_id
<< " too long: " << init_data.size();
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
#if defined(OS_ANDROID)
if (CommandLine::ForCurrentProcess()
->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) {
- CreateSessionIfPermitted(cdm_id, session_id, mime_type, init_data, true);
+ CreateSessionIfPermitted(
+ render_frame_id, cdm_id, session_id, mime_type, init_data, true);
return;
}
#endif
- BrowserCdm* cdm = GetCdm(cdm_id);
+ BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
if (!cdm) {
- DLOG(WARNING) << "No CDM for ID " << cdm_id << " found";
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
- std::map<int, GURL>::const_iterator iter =
- cdm_security_origin_map_.find(cdm_id);
+ std::map<uint64, GURL>::const_iterator iter =
+ cdm_security_origin_map_.find(GetId(render_frame_id, cdm_id));
if (iter == cdm_security_origin_map_.end()) {
NOTREACHED();
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
+ GURL security_origin = iter->second;
+
+ RenderFrameHost* rfh =
+ RenderFrameHost::FromID(render_process_id_, render_frame_id);
+ WebContents* web_contents = WebContents::FromRenderFrameHost(rfh);
+ DCHECK(web_contents);
base::Closure cancel_callback;
GetContentClient()->browser()->RequestProtectedMediaIdentifierPermission(
- web_contents_,
- iter->second,
+ web_contents,
+ security_origin,
base::Bind(&BrowserCdmManager::CreateSessionIfPermitted,
- weak_ptr_factory_.GetWeakPtr(),
- cdm_id,
- session_id,
- mime_type,
- init_data),
+ this,
+ render_frame_id, cdm_id, session_id,
+ mime_type, init_data),
&cancel_callback);
- if (!cancel_callback.is_null())
- cdm_cancel_permision_map_[cdm_id] = cancel_callback;
+
+ if (cancel_callback.is_null())
+ return;
+ cdm_cancel_permission_map_[GetId(render_frame_id, cdm_id)] = cancel_callback;
}
void BrowserCdmManager::OnUpdateSession(
+ int render_frame_id,
int cdm_id,
uint32 session_id,
const std::vector<uint8>& response) {
- BrowserCdm* cdm = GetCdm(cdm_id);
+ BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
if (!cdm) {
- DLOG(WARNING) << "No CDM for ID " << cdm_id << " found";
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
if (response.size() > kMaxSessionResponseLength) {
LOG(WARNING) << "Response for ID " << cdm_id
<< " is too long: " << response.size();
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
cdm->UpdateSession(session_id, &response[0], response.size());
}
-void BrowserCdmManager::OnReleaseSession(int cdm_id, uint32 session_id) {
- BrowserCdm* cdm = GetCdm(cdm_id);
+void BrowserCdmManager::OnReleaseSession(int render_frame_id,
+ int cdm_id,
+ uint32 session_id) {
+ BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
if (!cdm) {
- DLOG(WARNING) << "No CDM for ID " << cdm_id << " found";
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
cdm->ReleaseSession(session_id);
}
-void BrowserCdmManager::OnDestroyCdm(int cdm_id) {
- BrowserCdm* cdm = GetCdm(cdm_id);
- if (!cdm)
- return;
-
- CancelAllPendingSessionCreations(cdm_id);
- RemoveCdm(cdm_id);
+void BrowserCdmManager::OnDestroyCdm(int render_frame_id, int cdm_id) {
+ RemoveCdm(GetId(render_frame_id, cdm_id));
}
-void BrowserCdmManager::CancelAllPendingSessionCreations(int cdm_id) {
- if (cdm_cancel_permision_map_.count(cdm_id)) {
- cdm_cancel_permision_map_[cdm_id].Run();
- cdm_cancel_permision_map_.erase(cdm_id);
- }
+void BrowserCdmManager::SendSessionError(int render_frame_id,
+ int cdm_id,
+ uint32 session_id) {
+ OnSessionError(
+ render_frame_id, cdm_id, session_id, MediaKeys::kUnknownError, 0);
}
-void BrowserCdmManager::AddCdm(int cdm_id,
+#define BROWSER_CDM_MANAGER_CB(func) \
+ base::Bind(&BrowserCdmManager::func, this, render_frame_id, cdm_id)
+
+void BrowserCdmManager::AddCdm(int render_frame_id,
+ int cdm_id,
const std::string& key_system,
const GURL& security_origin) {
- DCHECK(!GetCdm(cdm_id));
- base::WeakPtr<BrowserCdmManager> weak_this = weak_ptr_factory_.GetWeakPtr();
- scoped_ptr<BrowserCdm> cdm(media::CreateBrowserCdm(
- key_system,
- base::Bind(&BrowserCdmManager::OnSessionCreated, weak_this, cdm_id),
- base::Bind(&BrowserCdmManager::OnSessionMessage, weak_this, cdm_id),
- base::Bind(&BrowserCdmManager::OnSessionReady, weak_this, cdm_id),
- base::Bind(&BrowserCdmManager::OnSessionClosed, weak_this, cdm_id),
- base::Bind(&BrowserCdmManager::OnSessionError, weak_this, cdm_id)));
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!GetCdm(render_frame_id, cdm_id));
+
+ scoped_ptr<BrowserCdm> cdm(
+ media::CreateBrowserCdm(key_system,
+ BROWSER_CDM_MANAGER_CB(OnSessionCreated),
+ BROWSER_CDM_MANAGER_CB(OnSessionMessage),
+ BROWSER_CDM_MANAGER_CB(OnSessionReady),
+ BROWSER_CDM_MANAGER_CB(OnSessionClosed),
+ BROWSER_CDM_MANAGER_CB(OnSessionError)));
if (!cdm) {
// This failure will be discovered and reported by OnCreateSession()
return;
}
- cdm_map_.add(cdm_id, cdm.Pass());
- cdm_security_origin_map_[cdm_id] = security_origin;
+ uint64 id = GetId(render_frame_id, cdm_id);
+ cdm_map_.add(id, cdm.Pass());
+ cdm_security_origin_map_[id] = security_origin;
}
-void BrowserCdmManager::RemoveCdm(int cdm_id) {
- // TODO(xhwang): Detach CDM from the player it's set to. In prefixed
- // EME implementation the current code is fine because we always destroy the
- // player before we destroy the DrmBridge. This will not always be the case
- // in unprefixed EME implementation.
- cdm_map_.erase(cdm_id);
- cdm_security_origin_map_.erase(cdm_id);
- cdm_cancel_permision_map_.erase(cdm_id);
-}
+void BrowserCdmManager::RemoveCdm(uint64 id) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
-int BrowserCdmManager::RoutingID() {
- return render_frame_host_->GetRoutingID();
-}
-
-bool BrowserCdmManager::Send(IPC::Message* msg) {
- return render_frame_host_->Send(msg);
+ cdm_map_.erase(id);
+ cdm_security_origin_map_.erase(id);
+ if (cdm_cancel_permission_map_.count(id)) {
+ cdm_cancel_permission_map_[id].Run();
+ cdm_cancel_permission_map_.erase(id);
+ }
}
void BrowserCdmManager::CreateSessionIfPermitted(
+ int render_frame_id,
int cdm_id,
uint32 session_id,
const std::string& content_type,
const std::vector<uint8>& init_data,
bool permitted) {
- cdm_cancel_permision_map_.erase(cdm_id);
+ cdm_cancel_permission_map_.erase(GetId(render_frame_id, cdm_id));
if (!permitted) {
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}
- BrowserCdm* cdm = GetCdm(cdm_id);
+ BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
if (!cdm) {
- DLOG(WARNING) << "No CDM for ID: " << cdm_id << " found";
- OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
+ SendSessionError(render_frame_id, cdm_id, session_id);
return;
}