OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "extensions/renderer/display_source_custom_bindings.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "content/public/child/v8_value_converter.h" | |
9 #include "extensions/renderer/script_context.h" | |
10 #include "third_party/WebKit/public/platform/WebMediaStream.h" | |
11 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" | |
12 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h" | |
13 #include "v8/include/v8.h" | |
14 | |
15 namespace extensions { | |
16 | |
17 using content::V8ValueConverter; | |
18 | |
19 namespace { | |
20 const char kErrorNotSupported[] = "Not supported"; | |
21 const char kInvalidSinkIdArg[] = "Invalid sink Id argument"; | |
22 const char kInvalidAuthInfoArg[] = "Invalid authentication info argument"; | |
23 const char kInvalidStreamArgs[] = "Invalid stream arguments"; | |
24 const char kSessionAlreadyStarted[] = "The session has been already started"; | |
25 const char kSessionAlreadyTerminating[] = "The session is already terminating"; | |
26 const char kSessionNotFound[] = "Session not found"; | |
27 } // namespace | |
28 | |
29 DisplaySourceCustomBindings::DisplaySourceCustomBindings(ScriptContext* context) | |
30 : ObjectBackedNativeHandler(context), | |
31 weak_factory_(this) { | |
32 RouteFunction("StartSession", | |
33 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.
| |
34 weak_factory_.GetWeakPtr())); | |
35 RouteFunction("TerminateSession", | |
36 base::Bind(&DisplaySourceCustomBindings::TerminateSession, | |
37 weak_factory_.GetWeakPtr())); | |
38 } | |
39 | |
40 DisplaySourceCustomBindings::~DisplaySourceCustomBindings() { | |
41 } | |
42 | |
43 void DisplaySourceCustomBindings::Invalidate() { | |
44 session_map_.clear(); | |
45 terminate_callback_map_.clear(); | |
46 weak_factory_.InvalidateWeakPtrs(); | |
47 ObjectBackedNativeHandler::Invalidate(); | |
48 } | |
49 | |
50 namespace { | |
51 | |
52 v8::Local<v8::Value> GetChildValue(v8::Local<v8::Object> value, | |
53 const std::string& key_name, | |
54 v8::Isolate* isolate) { | |
55 v8::Local<v8::Array> property_names(value->GetOwnPropertyNames()); | |
56 for (uint32 i = 0; i < property_names->Length(); ++i) { | |
57 v8::Local<v8::Value> key(property_names->Get(i)); | |
58 if (key_name == *v8::String::Utf8Value(key)) { | |
59 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.
| |
60 v8::Local<v8::Value> child_v8 = value->Get(key); | |
61 if (try_catch.HasCaught()) { | |
62 return v8::Null(isolate); | |
63 } | |
64 return child_v8; | |
65 } | |
66 } | |
67 | |
68 return v8::Null(isolate); | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 void DisplaySourceCustomBindings::CreateSession( | |
74 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
75 CHECK_EQ(1, args.Length()); | |
76 CHECK(args[0]->IsObject()); | |
77 v8::Isolate* isolate = context()->isolate(); | |
78 v8::Local<v8::Object> start_info = args[0].As<v8::Object>(); | |
79 | |
80 v8::Local<v8::Value> sink_id_val = | |
81 GetChildValue(start_info, "sinkId", isolate); | |
82 if (sink_id_val->IsNull() || sink_id_val->IsUndefined()) { | |
83 isolate->ThrowException(v8::Exception::Error( | |
84 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.
| |
85 return; | |
86 } | |
87 const int sink_id = sink_id_val->ToInt32(isolate)->Value(); | |
88 | |
89 v8::Local<v8::Value> video_stream_val = | |
90 GetChildValue(start_info, "videoTrack", isolate); | |
91 v8::Local<v8::Value> audio_stream_val = | |
92 GetChildValue(start_info, "audioTrack", isolate); | |
93 | |
94 if ((video_stream_val->IsNull() || video_stream_val->IsUndefined()) && | |
95 (audio_stream_val->IsNull() || audio_stream_val->IsUndefined())) { | |
96 isolate->ThrowException(v8::Exception::Error( | |
97 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs))); | |
98 return; | |
99 } | |
100 blink::WebMediaStreamTrack audio_track, video_track; | |
101 | |
102 if (!video_stream_val->IsNull() && !video_stream_val->IsUndefined()) { | |
103 CHECK(video_stream_val->IsObject()); | |
104 video_track = | |
105 blink::WebDOMMediaStreamTrack::fromV8Value( | |
106 video_stream_val).component(); | |
107 if (video_track.isNull()) { | |
108 isolate->ThrowException(v8::Exception::Error( | |
109 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs))); | |
110 return; | |
111 } | |
112 } | |
113 if (!audio_stream_val->IsNull() && !audio_stream_val->IsUndefined()) { | |
114 CHECK(audio_stream_val->IsObject()); | |
115 audio_track = | |
116 blink::WebDOMMediaStreamTrack::fromV8Value( | |
117 audio_stream_val).component(); | |
118 if (audio_track.isNull()) { | |
119 isolate->ThrowException(v8::Exception::Error( | |
120 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs))); | |
121 return; | |
122 } | |
123 } | |
124 | |
125 v8::Local<v8::Value> auth_info_v8_val = | |
126 GetChildValue(start_info, "authenticationInfo", isolate); | |
127 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
128 scoped_ptr<base::Value> auth_info_val( | |
129 converter->FromV8Value(auth_info_v8_val, context()->v8_context())); | |
130 scoped_ptr<DisplaySourceAuthInfo> auth_info; | |
131 if (auth_info_val) { | |
132 auth_info = DisplaySourceAuthInfo::FromValue(*auth_info_val); | |
133 if (!auth_info) { | |
134 isolate->ThrowException(v8::Exception::Error( | |
135 v8::String::NewFromUtf8(isolate, kInvalidAuthInfoArg))); | |
136 return; | |
137 } | |
138 } | |
139 | |
140 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
| |
141 isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( | |
142 isolate, kSessionAlreadyStarted))); | |
143 return; | |
144 } | |
145 | |
146 scoped_ptr<DisplaySourceSession> session = | |
147 DisplaySourceSessionFactory::CreateSession( | |
148 sink_id, video_track, audio_track, std::move(auth_info)); | |
149 if (!session) { | |
150 isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( | |
151 isolate, kErrorNotSupported))); | |
152 return; | |
153 } | |
154 | |
155 auto on_started_callback = base::Bind( | |
156 &DisplaySourceCustomBindings::OnSessionStarted, | |
157 weak_factory_.GetWeakPtr()); | |
158 auto on_terminated_callback = base::Bind( | |
159 &DisplaySourceCustomBindings::OnSessionTerminated, | |
160 weak_factory_.GetWeakPtr()); | |
161 auto on_error_callback = base::Bind( | |
162 &DisplaySourceCustomBindings::OnSessionError, | |
163 weak_factory_.GetWeakPtr()); | |
164 session->SetCallbacks(on_started_callback, | |
165 on_terminated_callback, | |
166 on_error_callback); | |
167 session_map_.insert(std::make_pair(sink_id, std::move(session))); | |
168 session->Start(); | |
169 } | |
170 | |
171 void DisplaySourceCustomBindings::TerminateSession( | |
172 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
173 CHECK_EQ(2, args.Length()); | |
174 CHECK(args[0]->IsInt32()); | |
175 CHECK(args[1]->IsNull() || args[1]->IsFunction()); | |
176 | |
177 v8::Isolate* isolate = context()->isolate(); | |
178 v8::Global<v8::Function> terminate_callback; | |
179 if (!args[1]->IsNull()) | |
180 terminate_callback.Reset(isolate, args[1].As<v8::Function>()); | |
181 | |
182 int sink_id = args[0]->ToInt32(args.GetIsolate())->Value(); | |
183 DisplaySourceSession* session = GetDisplaySession(sink_id); | |
184 if (!session) { | |
185 isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( | |
186 isolate, kSessionNotFound))); | |
187 return; | |
188 } | |
189 | |
190 if (session->state() == DisplaySourceSession::Terminating) { | |
191 isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( | |
192 isolate, kSessionAlreadyTerminating))); | |
193 return; | |
194 } | |
195 | |
196 if (!terminate_callback.IsEmpty()) { | |
197 CHECK(terminate_callback_map_.find(sink_id) == | |
198 terminate_callback_map_.end()); | |
199 terminate_callback_map_.insert( | |
200 std::make_pair(sink_id, std::move(terminate_callback))); | |
201 } | |
202 | |
203 session->Terminate(); | |
204 } | |
205 | |
206 void DisplaySourceCustomBindings::DispatchSessionStarted(int sink_id) const { | |
207 v8::Isolate* isolate = context()->isolate(); | |
208 v8::HandleScope handle_scope(isolate); | |
209 v8::Context::Scope context_scope(context()->v8_context()); | |
210 v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1); | |
211 event_args->Set(0, v8::Integer::New(isolate, sink_id)); | |
212 context()->DispatchEvent("displaySource.onSessionStarted", event_args); | |
213 } | |
214 | |
215 void DisplaySourceCustomBindings::DispatchSessionTerminated(int sink_id) const { | |
216 v8::Isolate* isolate = context()->isolate(); | |
217 v8::HandleScope handle_scope(isolate); | |
218 v8::Context::Scope context_scope(context()->v8_context()); | |
219 v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1); | |
220 event_args->Set(0, v8::Integer::New(isolate, sink_id)); | |
221 context()->DispatchEvent("displaySource.onSessionTerminated", event_args); | |
222 } | |
223 | |
224 void DisplaySourceCustomBindings::DispatchSessionError( | |
225 int sink_id, | |
226 DisplaySourceErrorType type, | |
227 const std::string& message) const { | |
228 v8::Isolate* isolate = context()->isolate(); | |
229 v8::HandleScope handle_scope(isolate); | |
230 v8::Context::Scope context_scope(context()->v8_context()); | |
231 | |
232 api::display_source::ErrorInfo error_info; | |
233 error_info.type = type; | |
234 if (!message.empty()) | |
235 error_info.description.reset(new std::string(message)); | |
236 | |
237 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
238 v8::Local<v8::Value> info_arg = | |
239 converter->ToV8Value(error_info.ToValue().get(), | |
240 context()->v8_context()); | |
241 | |
242 v8::Local<v8::Array> event_args = v8::Array::New(isolate, 2); | |
243 event_args->Set(0, v8::Integer::New(isolate, sink_id)); | |
244 event_args->Set(1, info_arg); | |
245 context()->DispatchEvent("displaySource.onSessionErrorOccured", event_args); | |
246 } | |
247 | |
248 DisplaySourceSession* DisplaySourceCustomBindings::GetDisplaySession( | |
249 int sink_id) const { | |
250 auto iter = session_map_.find(sink_id); | |
251 if (iter != session_map_.end()) | |
252 return iter->second.get(); | |
253 return nullptr; | |
254 } | |
255 | |
256 void DisplaySourceCustomBindings::OnSessionStarted(int sink_id) { | |
257 DispatchSessionStarted(sink_id); | |
258 } | |
259 | |
260 void DisplaySourceCustomBindings::OnSessionTerminated(int sink_id) { | |
261 DisplaySourceSession* session = GetDisplaySession(sink_id); | |
262 CHECK(session); | |
263 session_map_.erase(sink_id); | |
264 | |
265 // Call a termination callback if needed. | |
266 auto iter = terminate_callback_map_.find(sink_id); | |
267 if (iter != terminate_callback_map_.end()) { | |
268 v8::Isolate* isolate = context()->isolate(); | |
269 v8::Local<v8::Value> callback_args[1]; | |
270 callback_args[0] = v8::Integer::New(isolate, sink_id); | |
271 context()->CallFunction( | |
272 v8::Local<v8::Function>::New(isolate, iter->second), 1, | |
273 callback_args); | |
274 terminate_callback_map_.erase(iter); | |
275 } | |
276 | |
277 DispatchSessionTerminated(sink_id); | |
278 } | |
279 | |
280 void DisplaySourceCustomBindings::OnSessionError(int sink_id, | |
281 DisplaySourceErrorType type, | |
282 const std::string& message) { | |
283 DisplaySourceSession* session = GetDisplaySession(sink_id); | |
284 CHECK(session); | |
285 if (session->state() == DisplaySourceSession::Establishing) { | |
286 // Error has occured before the session has actually started. | |
287 session_map_.erase(sink_id); | |
288 } | |
289 | |
290 DispatchSessionError(sink_id, type, message); | |
291 } | |
292 | |
293 } // extensions | |
OLD | NEW |