Chromium Code Reviews| Index: extensions/renderer/display_source_custom_bindings.cc |
| diff --git a/extensions/renderer/display_source_custom_bindings.cc b/extensions/renderer/display_source_custom_bindings.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..637dd2788d1266a3fed44cfddf97dcecd946d152 |
| --- /dev/null |
| +++ b/extensions/renderer/display_source_custom_bindings.cc |
| @@ -0,0 +1,293 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "extensions/renderer/display_source_custom_bindings.h" |
| + |
| +#include "base/bind.h" |
| +#include "content/public/child/v8_value_converter.h" |
| +#include "extensions/renderer/script_context.h" |
| +#include "third_party/WebKit/public/platform/WebMediaStream.h" |
| +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| +#include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h" |
| +#include "v8/include/v8.h" |
| + |
| +namespace extensions { |
| + |
| +using content::V8ValueConverter; |
| + |
| +namespace { |
| +const char kErrorNotSupported[] = "Not supported"; |
| +const char kInvalidSinkIdArg[] = "Invalid sink Id argument"; |
| +const char kInvalidAuthInfoArg[] = "Invalid authentication info argument"; |
| +const char kInvalidStreamArgs[] = "Invalid stream arguments"; |
| +const char kSessionAlreadyStarted[] = "The session has been already started"; |
| +const char kSessionAlreadyTerminating[] = "The session is already terminating"; |
| +const char kSessionNotFound[] = "Session not found"; |
| +} // namespace |
| + |
| +DisplaySourceCustomBindings::DisplaySourceCustomBindings(ScriptContext* context) |
| + : ObjectBackedNativeHandler(context), |
| + weak_factory_(this) { |
| + RouteFunction("StartSession", |
| + base::Bind(&DisplaySourceCustomBindings::CreateSession, |
|
asargent_no_longer_on_chrome
2015/11/26 00:50:07
nit: any reason for the name mismatch between the
Mikhail
2015/11/26 13:00:47
Not really. Renamed.
|
| + weak_factory_.GetWeakPtr())); |
| + RouteFunction("TerminateSession", |
| + base::Bind(&DisplaySourceCustomBindings::TerminateSession, |
| + weak_factory_.GetWeakPtr())); |
| +} |
| + |
| +DisplaySourceCustomBindings::~DisplaySourceCustomBindings() { |
| +} |
| + |
| +void DisplaySourceCustomBindings::Invalidate() { |
| + session_map_.clear(); |
| + terminate_callback_map_.clear(); |
| + weak_factory_.InvalidateWeakPtrs(); |
| + ObjectBackedNativeHandler::Invalidate(); |
| +} |
| + |
| +namespace { |
| + |
| +v8::Local<v8::Value> GetChildValue(v8::Local<v8::Object> value, |
| + const std::string& key_name, |
| + v8::Isolate* isolate) { |
| + v8::Local<v8::Array> property_names(value->GetOwnPropertyNames()); |
| + for (uint32 i = 0; i < property_names->Length(); ++i) { |
| + v8::Local<v8::Value> key(property_names->Get(i)); |
| + if (key_name == *v8::String::Utf8Value(key)) { |
| + v8::TryCatch try_catch; |
|
asargent_no_longer_on_chrome
2015/11/26 00:50:07
nit: according to the comment at https://code.goog
Mikhail
2015/11/26 13:00:47
Done.
|
| + v8::Local<v8::Value> child_v8 = value->Get(key); |
| + if (try_catch.HasCaught()) { |
| + return v8::Null(isolate); |
| + } |
| + return child_v8; |
| + } |
| + } |
| + |
| + return v8::Null(isolate); |
| +} |
| + |
| +} // namespace |
| + |
| +void DisplaySourceCustomBindings::CreateSession( |
| + const v8::FunctionCallbackInfo<v8::Value>& args) { |
| + CHECK_EQ(1, args.Length()); |
| + CHECK(args[0]->IsObject()); |
| + v8::Isolate* isolate = context()->isolate(); |
| + v8::Local<v8::Object> start_info = args[0].As<v8::Object>(); |
| + |
| + v8::Local<v8::Value> sink_id_val = |
| + GetChildValue(start_info, "sinkId", isolate); |
| + if (sink_id_val->IsNull() || sink_id_val->IsUndefined()) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidSinkIdArg))); |
|
asargent_no_longer_on_chrome
2015/11/26 00:50:07
The typical pattern for returning errors from exte
Mikhail
2015/11/26 13:00:47
These are just the arguments sanity checks that ca
asargent_no_longer_on_chrome
2015/12/01 01:54:23
If some errors are reported as exceptions and some
Devlin
2015/12/01 17:48:37
The standard we should be using is chrome.runtime.
|
| + return; |
| + } |
| + const int sink_id = sink_id_val->ToInt32(isolate)->Value(); |
| + |
| + v8::Local<v8::Value> video_stream_val = |
| + GetChildValue(start_info, "videoTrack", isolate); |
| + v8::Local<v8::Value> audio_stream_val = |
| + GetChildValue(start_info, "audioTrack", isolate); |
| + |
| + if ((video_stream_val->IsNull() || video_stream_val->IsUndefined()) && |
| + (audio_stream_val->IsNull() || audio_stream_val->IsUndefined())) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidStreamArgs))); |
| + return; |
| + } |
| + blink::WebMediaStreamTrack audio_track, video_track; |
| + |
| + if (!video_stream_val->IsNull() && !video_stream_val->IsUndefined()) { |
| + CHECK(video_stream_val->IsObject()); |
| + video_track = |
| + blink::WebDOMMediaStreamTrack::fromV8Value( |
| + video_stream_val).component(); |
| + if (video_track.isNull()) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidStreamArgs))); |
| + return; |
| + } |
| + } |
| + if (!audio_stream_val->IsNull() && !audio_stream_val->IsUndefined()) { |
| + CHECK(audio_stream_val->IsObject()); |
| + audio_track = |
| + blink::WebDOMMediaStreamTrack::fromV8Value( |
| + audio_stream_val).component(); |
| + if (audio_track.isNull()) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidStreamArgs))); |
| + return; |
| + } |
| + } |
| + |
| + v8::Local<v8::Value> auth_info_v8_val = |
| + GetChildValue(start_info, "authenticationInfo", isolate); |
| + scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
| + scoped_ptr<base::Value> auth_info_val( |
| + converter->FromV8Value(auth_info_v8_val, context()->v8_context())); |
| + scoped_ptr<DisplaySourceAuthInfo> auth_info; |
| + if (auth_info_val) { |
| + auth_info = DisplaySourceAuthInfo::FromValue(*auth_info_val); |
| + if (!auth_info) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidAuthInfoArg))); |
| + return; |
| + } |
| + } |
| + |
| + if (GetDisplaySession(sink_id)) { |
|
asargent_no_longer_on_chrome
2015/11/26 00:50:07
optional: would it make sense to move this check u
Mikhail
2015/11/26 13:00:47
Makes sense, thus we'll be able to avoid unnecessa
|
| + isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( |
| + isolate, kSessionAlreadyStarted))); |
| + return; |
| + } |
| + |
| + scoped_ptr<DisplaySourceSession> session = |
| + DisplaySourceSessionFactory::CreateSession( |
| + sink_id, video_track, audio_track, std::move(auth_info)); |
| + if (!session) { |
| + isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( |
| + isolate, kErrorNotSupported))); |
| + return; |
| + } |
| + |
| + auto on_started_callback = base::Bind( |
| + &DisplaySourceCustomBindings::OnSessionStarted, |
| + weak_factory_.GetWeakPtr()); |
| + auto on_terminated_callback = base::Bind( |
| + &DisplaySourceCustomBindings::OnSessionTerminated, |
| + weak_factory_.GetWeakPtr()); |
| + auto on_error_callback = base::Bind( |
| + &DisplaySourceCustomBindings::OnSessionError, |
| + weak_factory_.GetWeakPtr()); |
| + session->SetCallbacks(on_started_callback, |
| + on_terminated_callback, |
| + on_error_callback); |
| + session_map_.insert(std::make_pair(sink_id, std::move(session))); |
| + session->Start(); |
| +} |
| + |
| +void DisplaySourceCustomBindings::TerminateSession( |
| + const v8::FunctionCallbackInfo<v8::Value>& args) { |
| + CHECK_EQ(2, args.Length()); |
| + CHECK(args[0]->IsInt32()); |
| + CHECK(args[1]->IsNull() || args[1]->IsFunction()); |
| + |
| + v8::Isolate* isolate = context()->isolate(); |
| + v8::Global<v8::Function> terminate_callback; |
| + if (!args[1]->IsNull()) |
| + terminate_callback.Reset(isolate, args[1].As<v8::Function>()); |
| + |
| + int sink_id = args[0]->ToInt32(args.GetIsolate())->Value(); |
| + DisplaySourceSession* session = GetDisplaySession(sink_id); |
| + if (!session) { |
| + isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( |
| + isolate, kSessionNotFound))); |
| + return; |
| + } |
| + |
| + if (session->state() == DisplaySourceSession::Terminating) { |
| + isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( |
| + isolate, kSessionAlreadyTerminating))); |
| + return; |
| + } |
| + |
| + if (!terminate_callback.IsEmpty()) { |
| + CHECK(terminate_callback_map_.find(sink_id) == |
| + terminate_callback_map_.end()); |
| + terminate_callback_map_.insert( |
| + std::make_pair(sink_id, std::move(terminate_callback))); |
| + } |
| + |
| + session->Terminate(); |
| +} |
| + |
| +void DisplaySourceCustomBindings::DispatchSessionStarted(int sink_id) const { |
| + v8::Isolate* isolate = context()->isolate(); |
| + v8::HandleScope handle_scope(isolate); |
| + v8::Context::Scope context_scope(context()->v8_context()); |
| + v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1); |
| + event_args->Set(0, v8::Integer::New(isolate, sink_id)); |
| + context()->DispatchEvent("displaySource.onSessionStarted", event_args); |
| +} |
| + |
| +void DisplaySourceCustomBindings::DispatchSessionTerminated(int sink_id) const { |
| + v8::Isolate* isolate = context()->isolate(); |
| + v8::HandleScope handle_scope(isolate); |
| + v8::Context::Scope context_scope(context()->v8_context()); |
| + v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1); |
| + event_args->Set(0, v8::Integer::New(isolate, sink_id)); |
| + context()->DispatchEvent("displaySource.onSessionTerminated", event_args); |
| +} |
| + |
| +void DisplaySourceCustomBindings::DispatchSessionError( |
| + int sink_id, |
| + DisplaySourceErrorType type, |
| + const std::string& message) const { |
| + v8::Isolate* isolate = context()->isolate(); |
| + v8::HandleScope handle_scope(isolate); |
| + v8::Context::Scope context_scope(context()->v8_context()); |
| + |
| + api::display_source::ErrorInfo error_info; |
| + error_info.type = type; |
| + if (!message.empty()) |
| + error_info.description.reset(new std::string(message)); |
| + |
| + scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
| + v8::Local<v8::Value> info_arg = |
| + converter->ToV8Value(error_info.ToValue().get(), |
| + context()->v8_context()); |
| + |
| + v8::Local<v8::Array> event_args = v8::Array::New(isolate, 2); |
| + event_args->Set(0, v8::Integer::New(isolate, sink_id)); |
| + event_args->Set(1, info_arg); |
| + context()->DispatchEvent("displaySource.onSessionErrorOccured", event_args); |
| +} |
| + |
| +DisplaySourceSession* DisplaySourceCustomBindings::GetDisplaySession( |
| + int sink_id) const { |
| + auto iter = session_map_.find(sink_id); |
| + if (iter != session_map_.end()) |
| + return iter->second.get(); |
| + return nullptr; |
| +} |
| + |
| +void DisplaySourceCustomBindings::OnSessionStarted(int sink_id) { |
| + DispatchSessionStarted(sink_id); |
| +} |
| + |
| +void DisplaySourceCustomBindings::OnSessionTerminated(int sink_id) { |
| + DisplaySourceSession* session = GetDisplaySession(sink_id); |
| + CHECK(session); |
| + session_map_.erase(sink_id); |
| + |
| + // Call a termination callback if needed. |
| + auto iter = terminate_callback_map_.find(sink_id); |
| + if (iter != terminate_callback_map_.end()) { |
| + v8::Isolate* isolate = context()->isolate(); |
| + v8::Local<v8::Value> callback_args[1]; |
| + callback_args[0] = v8::Integer::New(isolate, sink_id); |
| + context()->CallFunction( |
| + v8::Local<v8::Function>::New(isolate, iter->second), 1, |
| + callback_args); |
| + terminate_callback_map_.erase(iter); |
| + } |
| + |
| + DispatchSessionTerminated(sink_id); |
| +} |
| + |
| +void DisplaySourceCustomBindings::OnSessionError(int sink_id, |
| + DisplaySourceErrorType type, |
| + const std::string& message) { |
| + DisplaySourceSession* session = GetDisplaySession(sink_id); |
| + CHECK(session); |
| + if (session->state() == DisplaySourceSession::Establishing) { |
| + // Error has occured before the session has actually started. |
| + session_map_.erase(sink_id); |
| + } |
| + |
| + DispatchSessionError(sink_id, type, message); |
| +} |
| + |
| +} // extensions |