OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "extensions/browser/api/cast_channel/cast_channel_api.h" | 5 #include "extensions/browser/api/cast_channel/cast_channel_api.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <limits> | 10 #include <limits> |
11 #include <memory> | 11 #include <memory> |
12 #include <string> | 12 #include <string> |
13 #include <utility> | 13 #include <utility> |
14 #include <vector> | 14 #include <vector> |
15 | 15 |
16 #include "base/json/json_writer.h" | 16 #include "base/json/json_writer.h" |
17 #include "base/lazy_instance.h" | 17 #include "base/lazy_instance.h" |
18 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
20 #include "base/values.h" | 20 #include "base/values.h" |
21 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
22 #include "extensions/browser/api/cast_channel/cast_channel_type_util.h" | |
22 #include "extensions/browser/api/cast_channel/cast_message_util.h" | 23 #include "extensions/browser/api/cast_channel/cast_message_util.h" |
23 #include "extensions/browser/api/cast_channel/cast_socket.h" | 24 #include "extensions/browser/api/cast_channel/cast_socket.h" |
24 #include "extensions/browser/api/cast_channel/keep_alive_delegate.h" | 25 #include "extensions/browser/api/cast_channel/keep_alive_delegate.h" |
25 #include "extensions/browser/api/cast_channel/logger.h" | 26 #include "extensions/browser/api/cast_channel/logger.h" |
26 #include "extensions/browser/event_router.h" | 27 #include "extensions/browser/event_router.h" |
27 #include "extensions/common/api/cast_channel/cast_channel.pb.h" | 28 #include "extensions/common/api/cast_channel/cast_channel.pb.h" |
28 #include "extensions/common/api/cast_channel/logging.pb.h" | 29 #include "extensions/common/api/cast_channel/logging.pb.h" |
29 #include "net/base/ip_address.h" | 30 #include "net/base/ip_address.h" |
30 #include "net/base/ip_endpoint.h" | 31 #include "net/base/ip_endpoint.h" |
31 #include "net/base/net_errors.h" | 32 #include "net/base/net_errors.h" |
32 | 33 |
33 // Default timeout interval for connection setup. | 34 // Default timeout interval for connection setup. |
34 // Used if not otherwise specified at ConnectInfo::timeout. | 35 // Used if not otherwise specified at ConnectInfo::timeout. |
35 const int kDefaultConnectTimeoutMillis = 5000; // 5 seconds. | 36 const int kDefaultConnectTimeoutMillis = 5000; // 5 seconds. |
36 | 37 |
37 namespace extensions { | 38 namespace extensions { |
38 | 39 |
39 namespace Close = cast_channel::Close; | 40 namespace Close = cast_channel::Close; |
40 namespace OnError = cast_channel::OnError; | 41 namespace OnError = cast_channel::OnError; |
41 namespace OnMessage = cast_channel::OnMessage; | 42 namespace OnMessage = cast_channel::OnMessage; |
42 namespace Open = cast_channel::Open; | 43 namespace Open = cast_channel::Open; |
43 namespace Send = cast_channel::Send; | 44 namespace Send = cast_channel::Send; |
45 using ::cast_channel::ChannelError; | |
44 using cast_channel::CastDeviceCapability; | 46 using cast_channel::CastDeviceCapability; |
45 using cast_channel::CastMessage; | 47 using cast_channel::CastMessage; |
46 using cast_channel::CastSocket; | 48 using cast_channel::CastSocket; |
47 using cast_channel::ChannelAuthType; | 49 using cast_channel::CastSocketImpl; |
48 using cast_channel::ChannelError; | 50 using cast_channel::CastTransport; |
49 using cast_channel::ChannelInfo; | 51 using cast_channel::ChannelInfo; |
50 using cast_channel::ConnectInfo; | 52 using cast_channel::ConnectInfo; |
51 using cast_channel::ErrorInfo; | 53 using cast_channel::ErrorInfo; |
54 using cast_channel::KeepAliveDelegate; | |
52 using cast_channel::LastErrors; | 55 using cast_channel::LastErrors; |
53 using cast_channel::Logger; | 56 using cast_channel::Logger; |
54 using cast_channel::MessageInfo; | 57 using cast_channel::MessageInfo; |
55 using cast_channel::ReadyState; | |
56 using content::BrowserThread; | 58 using content::BrowserThread; |
57 | 59 |
58 namespace { | 60 namespace { |
59 | 61 |
60 // T is an extension dictionary (MessageInfo or ChannelInfo) | 62 // T is an extension dictionary (MessageInfo or ChannelInfo) |
61 template <class T> | 63 template <class T> |
62 std::string ParamToString(const T& info) { | 64 std::string ParamToString(const T& info) { |
63 std::unique_ptr<base::DictionaryValue> dict = info.ToValue(); | 65 std::unique_ptr<base::DictionaryValue> dict = info.ToValue(); |
64 std::string out; | 66 std::string out; |
65 base::JSONWriter::Write(*dict, &out); | 67 base::JSONWriter::Write(*dict, &out); |
66 return out; | 68 return out; |
67 } | 69 } |
68 | 70 |
69 // Fills |channel_info| from the destination and state of |socket|. | 71 // Fills |channel_info| from the destination and state of |socket|. |
70 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) { | 72 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) { |
71 DCHECK(channel_info); | 73 DCHECK(channel_info); |
72 channel_info->channel_id = socket.id(); | 74 channel_info->channel_id = socket.id(); |
73 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint(); | 75 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint(); |
74 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort(); | 76 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort(); |
75 channel_info->connect_info.port = ip_endpoint.port(); | 77 channel_info->connect_info.port = ip_endpoint.port(); |
76 channel_info->connect_info.auth = socket.channel_auth(); | 78 channel_info->connect_info.auth = |
77 channel_info->ready_state = socket.ready_state(); | 79 cast_channel::ToChannelAuthType(socket.channel_auth()); |
78 channel_info->error_state = socket.error_state(); | 80 channel_info->ready_state = cast_channel::ToReadyState(socket.ready_state()); |
81 channel_info->error_state = | |
82 cast_channel::ToChannelError(socket.error_state()); | |
79 channel_info->keep_alive = socket.keep_alive(); | 83 channel_info->keep_alive = socket.keep_alive(); |
80 channel_info->audio_only = socket.audio_only(); | 84 channel_info->audio_only = socket.audio_only(); |
81 } | 85 } |
82 | 86 |
83 // Fills |error_info| from |error_state| and |last_errors|. | 87 // Fills |error_info| from |error_state| and |last_errors|. |
84 void FillErrorInfo(ChannelError error_state, | 88 void FillErrorInfo(cast_channel::ChannelError error_state, |
85 const LastErrors& last_errors, | 89 const LastErrors& last_errors, |
86 ErrorInfo* error_info) { | 90 ErrorInfo* error_info) { |
87 error_info->error_state = error_state; | 91 error_info->error_state = error_state; |
88 if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN) | 92 if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN) |
89 error_info->event_type.reset(new int(last_errors.event_type)); | 93 error_info->event_type.reset(new int(last_errors.event_type)); |
90 if (last_errors.challenge_reply_error_type != | 94 if (last_errors.challenge_reply_error_type != |
91 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) { | 95 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) { |
92 error_info->challenge_reply_error_type.reset( | 96 error_info->challenge_reply_error_type.reset( |
93 new int(last_errors.challenge_reply_error_type)); | 97 new int(last_errors.challenge_reply_error_type)); |
94 } | 98 } |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
202 | 206 |
203 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) { | 207 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) { |
204 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 208 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
205 sockets_->Remove(extension_->id(), channel_id); | 209 sockets_->Remove(extension_->id(), channel_id); |
206 } | 210 } |
207 | 211 |
208 void CastChannelAsyncApiFunction::SetResultFromSocket( | 212 void CastChannelAsyncApiFunction::SetResultFromSocket( |
209 const CastSocket& socket) { | 213 const CastSocket& socket) { |
210 ChannelInfo channel_info; | 214 ChannelInfo channel_info; |
211 FillChannelInfo(socket, &channel_info); | 215 FillChannelInfo(socket, &channel_info); |
212 ChannelError error = socket.error_state(); | 216 cast_channel::ChannelError error = |
217 cast_channel::ToChannelError(socket.error_state()); | |
213 if (error != cast_channel::CHANNEL_ERROR_NONE) { | 218 if (error != cast_channel::CHANNEL_ERROR_NONE) { |
214 SetError("Channel socket error = " + base::IntToString(error)); | 219 SetError("Channel socket error = " + base::IntToString(error)); |
215 } | 220 } |
216 SetResultFromChannelInfo(channel_info); | 221 SetResultFromChannelInfo(channel_info); |
217 } | 222 } |
218 | 223 |
219 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id, | 224 void CastChannelAsyncApiFunction::SetResultFromError( |
220 ChannelError error) { | 225 int channel_id, |
226 cast_channel::ChannelError error) { | |
221 ChannelInfo channel_info; | 227 ChannelInfo channel_info; |
222 channel_info.channel_id = channel_id; | 228 channel_info.channel_id = channel_id; |
223 channel_info.ready_state = cast_channel::READY_STATE_CLOSED; | 229 channel_info.ready_state = cast_channel::READY_STATE_CLOSED; |
224 channel_info.error_state = error; | 230 channel_info.error_state = error; |
225 channel_info.connect_info.ip_address = ""; | 231 channel_info.connect_info.ip_address = ""; |
226 channel_info.connect_info.port = 0; | 232 channel_info.connect_info.port = 0; |
227 channel_info.connect_info.auth = cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED; | 233 channel_info.connect_info.auth = cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED; |
228 SetResultFromChannelInfo(channel_info); | 234 SetResultFromChannelInfo(channel_info); |
229 SetError("Channel error = " + base::IntToString(error)); | 235 SetError("Channel error = " + base::IntToString(error)); |
230 } | 236 } |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
301 | 307 |
302 void CastChannelOpenFunction::AsyncWorkStart() { | 308 void CastChannelOpenFunction::AsyncWorkStart() { |
303 DCHECK(api_); | 309 DCHECK(api_); |
304 DCHECK(ip_endpoint_.get()); | 310 DCHECK(ip_endpoint_.get()); |
305 const ConnectInfo& connect_info = params_->connect_info; | 311 const ConnectInfo& connect_info = params_->connect_info; |
306 CastSocket* socket; | 312 CastSocket* socket; |
307 std::unique_ptr<CastSocket> test_socket = api_->GetSocketForTest(); | 313 std::unique_ptr<CastSocket> test_socket = api_->GetSocketForTest(); |
308 if (test_socket.get()) { | 314 if (test_socket.get()) { |
309 socket = test_socket.release(); | 315 socket = test_socket.release(); |
310 } else { | 316 } else { |
311 socket = new cast_channel::CastSocketImpl( | 317 socket = new CastSocketImpl( |
312 extension_->id(), *ip_endpoint_, channel_auth_, | 318 extension_->id(), *ip_endpoint_, |
319 cast_channel::ToChannelAuthTypeInternal(channel_auth_), | |
imcheng
2017/05/24 22:11:23
Instead of this, can you just use ToChannelAuthTyp
zhaobin
2017/05/25 00:49:23
Done.
| |
313 ExtensionsBrowserClient::Get()->GetNetLog(), | 320 ExtensionsBrowserClient::Get()->GetNetLog(), |
314 base::TimeDelta::FromMilliseconds(connect_info.timeout.get() | 321 base::TimeDelta::FromMilliseconds(connect_info.timeout.get() |
315 ? *connect_info.timeout | 322 ? *connect_info.timeout |
316 : kDefaultConnectTimeoutMillis), | 323 : kDefaultConnectTimeoutMillis), |
317 liveness_timeout_ > base::TimeDelta(), api_->GetLogger(), | 324 liveness_timeout_ > base::TimeDelta(), api_->GetLogger(), |
318 connect_info.capabilities.get() ? *connect_info.capabilities | 325 connect_info.capabilities.get() ? *connect_info.capabilities |
319 : CastDeviceCapability::NONE); | 326 : CastDeviceCapability::NONE); |
320 } | 327 } |
321 new_channel_id_ = AddSocket(socket); | 328 new_channel_id_ = AddSocket(socket); |
322 | 329 |
323 // Construct read delegates. | 330 // Construct read delegates. |
324 std::unique_ptr<api::cast_channel::CastTransport::Delegate> delegate( | 331 std::unique_ptr<CastTransport::Delegate> delegate( |
325 base::MakeUnique<CastMessageHandler>( | 332 base::MakeUnique<CastMessageHandler>( |
326 base::Bind(&CastChannelAPI::SendEvent, api_->AsWeakPtr()), socket, | 333 base::Bind(&CastChannelAPI::SendEvent, api_->AsWeakPtr()), socket, |
327 api_->GetLogger())); | 334 api_->GetLogger())); |
328 if (socket->keep_alive()) { | 335 if (socket->keep_alive()) { |
329 // Wrap read delegate in a KeepAliveDelegate for timeout handling. | 336 // Wrap read delegate in a KeepAliveDelegate for timeout handling. |
330 api::cast_channel::KeepAliveDelegate* keep_alive = | 337 KeepAliveDelegate* keep_alive = |
331 new api::cast_channel::KeepAliveDelegate( | 338 new KeepAliveDelegate(socket, api_->GetLogger(), std::move(delegate), |
332 socket, api_->GetLogger(), std::move(delegate), ping_interval_, | 339 ping_interval_, liveness_timeout_); |
333 liveness_timeout_); | |
334 std::unique_ptr<base::Timer> injected_timer = | 340 std::unique_ptr<base::Timer> injected_timer = |
335 api_->GetInjectedTimeoutTimerForTest(); | 341 api_->GetInjectedTimeoutTimerForTest(); |
336 if (injected_timer) { | 342 if (injected_timer) { |
337 keep_alive->SetTimersForTest(base::MakeUnique<base::Timer>(false, false), | 343 keep_alive->SetTimersForTest(base::MakeUnique<base::Timer>(false, false), |
338 std::move(injected_timer)); | 344 std::move(injected_timer)); |
339 } | 345 } |
340 delegate.reset(keep_alive); | 346 delegate.reset(keep_alive); |
341 } | 347 } |
342 | 348 |
343 socket->Connect(std::move(delegate), | 349 socket->Connect(std::move(delegate), |
344 base::Bind(&CastChannelOpenFunction::OnOpen, this)); | 350 base::Bind(&CastChannelOpenFunction::OnOpen, this)); |
345 } | 351 } |
346 | 352 |
347 void CastChannelOpenFunction::OnOpen(cast_channel::ChannelError result) { | 353 void CastChannelOpenFunction::OnOpen(ChannelError result) { |
348 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 354 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
349 VLOG(1) << "Connect finished, OnOpen invoked."; | 355 VLOG(1) << "Connect finished, OnOpen invoked."; |
350 // TODO: If we failed to open the CastSocket, we may want to clean up here, | 356 // TODO: If we failed to open the CastSocket, we may want to clean up here, |
351 // rather than relying on the extension to call close(). This can be done by | 357 // rather than relying on the extension to call close(). This can be done by |
352 // calling RemoveSocket() and api_->GetLogger()->ClearLastErrors(channel_id). | 358 // calling RemoveSocket() and api_->GetLogger()->ClearLastErrors(channel_id). |
353 if (result != cast_channel::CHANNEL_ERROR_UNKNOWN) { | 359 if (result != ChannelError::UNKNOWN) { |
354 CastSocket* socket = GetSocket(new_channel_id_); | 360 CastSocket* socket = GetSocket(new_channel_id_); |
355 CHECK(socket); | 361 CHECK(socket); |
356 SetResultFromSocket(*socket); | 362 SetResultFromSocket(*socket); |
357 } else { | 363 } else { |
358 // The socket is being destroyed. | 364 // The socket is being destroyed. |
359 SetResultFromError(new_channel_id_, result); | 365 SetResultFromError(new_channel_id_, cast_channel::CHANNEL_ERROR_UNKNOWN); |
360 } | 366 } |
361 | 367 |
362 AsyncWorkCompleted(); | 368 AsyncWorkCompleted(); |
363 } | 369 } |
364 | 370 |
365 CastChannelSendFunction::CastChannelSendFunction() { } | 371 CastChannelSendFunction::CastChannelSendFunction() { } |
366 | 372 |
367 CastChannelSendFunction::~CastChannelSendFunction() { } | 373 CastChannelSendFunction::~CastChannelSendFunction() { } |
368 | 374 |
369 bool CastChannelSendFunction::Prepare() { | 375 bool CastChannelSendFunction::Prepare() { |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
462 SetResultFromSocket(*socket); | 468 SetResultFromSocket(*socket); |
463 // This will delete |socket|. | 469 // This will delete |socket|. |
464 RemoveSocket(channel_id); | 470 RemoveSocket(channel_id); |
465 api_->GetLogger()->ClearLastErrors(channel_id); | 471 api_->GetLogger()->ClearLastErrors(channel_id); |
466 } | 472 } |
467 AsyncWorkCompleted(); | 473 AsyncWorkCompleted(); |
468 } | 474 } |
469 | 475 |
470 CastChannelOpenFunction::CastMessageHandler::CastMessageHandler( | 476 CastChannelOpenFunction::CastMessageHandler::CastMessageHandler( |
471 const EventDispatchCallback& ui_dispatch_cb, | 477 const EventDispatchCallback& ui_dispatch_cb, |
472 cast_channel::CastSocket* socket, | 478 CastSocket* socket, |
473 scoped_refptr<Logger> logger) | 479 scoped_refptr<Logger> logger) |
474 : ui_dispatch_cb_(ui_dispatch_cb), socket_(socket), logger_(logger) { | 480 : ui_dispatch_cb_(ui_dispatch_cb), socket_(socket), logger_(logger) { |
475 DCHECK(socket_); | 481 DCHECK(socket_); |
476 DCHECK(logger_); | 482 DCHECK(logger_); |
477 } | 483 } |
478 | 484 |
479 CastChannelOpenFunction::CastMessageHandler::~CastMessageHandler() { | 485 CastChannelOpenFunction::CastMessageHandler::~CastMessageHandler() { |
480 } | 486 } |
481 | 487 |
482 void CastChannelOpenFunction::CastMessageHandler::OnError( | 488 void CastChannelOpenFunction::CastMessageHandler::OnError( |
483 cast_channel::ChannelError error_state) { | 489 ChannelError error_state) { |
484 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 490 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
485 | 491 |
486 ChannelInfo channel_info; | 492 ChannelInfo channel_info; |
487 FillChannelInfo(*socket_, &channel_info); | 493 FillChannelInfo(*socket_, &channel_info); |
488 channel_info.error_state = error_state; | 494 channel_info.error_state = cast_channel::ToChannelError(error_state); |
489 ErrorInfo error_info; | 495 ErrorInfo error_info; |
490 FillErrorInfo(error_state, logger_->GetLastErrors(socket_->id()), | 496 FillErrorInfo(channel_info.error_state, logger_->GetLastErrors(socket_->id()), |
491 &error_info); | 497 &error_info); |
492 | 498 |
493 std::unique_ptr<base::ListValue> results = | 499 std::unique_ptr<base::ListValue> results = |
494 OnError::Create(channel_info, error_info); | 500 OnError::Create(channel_info, error_info); |
495 std::unique_ptr<Event> event(new Event( | 501 std::unique_ptr<Event> event(new Event( |
496 events::CAST_CHANNEL_ON_ERROR, OnError::kEventName, std::move(results))); | 502 events::CAST_CHANNEL_ON_ERROR, OnError::kEventName, std::move(results))); |
497 BrowserThread::PostTask( | 503 BrowserThread::PostTask( |
498 BrowserThread::UI, FROM_HERE, | 504 BrowserThread::UI, FROM_HERE, |
499 base::Bind(ui_dispatch_cb_, socket_->owner_extension_id(), | 505 base::Bind(ui_dispatch_cb_, socket_->owner_extension_id(), |
500 base::Passed(std::move(event)))); | 506 base::Passed(std::move(event)))); |
501 } | 507 } |
502 | 508 |
503 void CastChannelOpenFunction::CastMessageHandler::OnMessage( | 509 void CastChannelOpenFunction::CastMessageHandler::OnMessage( |
504 const CastMessage& message) { | 510 const CastMessage& message) { |
505 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 511 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
506 | 512 |
507 MessageInfo message_info; | 513 MessageInfo message_info; |
508 cast_channel::CastMessageToMessageInfo(message, &message_info); | 514 CastMessageToMessageInfo(message, &message_info); |
509 ChannelInfo channel_info; | 515 ChannelInfo channel_info; |
510 FillChannelInfo(*socket_, &channel_info); | 516 FillChannelInfo(*socket_, &channel_info); |
511 VLOG(1) << "Received message " << ParamToString(message_info) | 517 VLOG(1) << "Received message " << ParamToString(message_info) |
512 << " on channel " << ParamToString(channel_info); | 518 << " on channel " << ParamToString(channel_info); |
513 | 519 |
514 std::unique_ptr<base::ListValue> results = | 520 std::unique_ptr<base::ListValue> results = |
515 OnMessage::Create(channel_info, message_info); | 521 OnMessage::Create(channel_info, message_info); |
516 std::unique_ptr<Event> event(new Event(events::CAST_CHANNEL_ON_MESSAGE, | 522 std::unique_ptr<Event> event(new Event(events::CAST_CHANNEL_ON_MESSAGE, |
517 OnMessage::kEventName, | 523 OnMessage::kEventName, |
518 std::move(results))); | 524 std::move(results))); |
519 BrowserThread::PostTask( | 525 BrowserThread::PostTask( |
520 BrowserThread::UI, FROM_HERE, | 526 BrowserThread::UI, FROM_HERE, |
521 base::Bind(ui_dispatch_cb_, socket_->owner_extension_id(), | 527 base::Bind(ui_dispatch_cb_, socket_->owner_extension_id(), |
522 base::Passed(std::move(event)))); | 528 base::Passed(std::move(event)))); |
523 } | 529 } |
524 | 530 |
525 void CastChannelOpenFunction::CastMessageHandler::Start() { | 531 void CastChannelOpenFunction::CastMessageHandler::Start() { |
526 } | 532 } |
527 | 533 |
528 } // namespace extensions | 534 } // namespace extensions |
OLD | NEW |