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 |