1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsDOMDataChannel.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,517 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsDOMDataChannel.h" 1.11 + 1.12 +#ifdef MOZ_LOGGING 1.13 +#define FORCE_PR_LOG 1.14 +#endif 1.15 + 1.16 +#include "base/basictypes.h" 1.17 +#include "prlog.h" 1.18 + 1.19 +#ifdef PR_LOGGING 1.20 +extern PRLogModuleInfo* GetDataChannelLog(); 1.21 +#endif 1.22 +#undef LOG 1.23 +#define LOG(args) PR_LOG(GetDataChannelLog(), PR_LOG_DEBUG, args) 1.24 + 1.25 + 1.26 +#include "nsDOMDataChannelDeclarations.h" 1.27 +#include "nsDOMDataChannel.h" 1.28 +#include "nsIDOMFile.h" 1.29 +#include "nsIDOMDataChannel.h" 1.30 +#include "nsIDOMMessageEvent.h" 1.31 +#include "mozilla/DOMEventTargetHelper.h" 1.32 + 1.33 +#include "nsError.h" 1.34 +#include "nsAutoPtr.h" 1.35 +#include "nsContentUtils.h" 1.36 +#include "nsCxPusher.h" 1.37 +#include "nsCycleCollectionParticipant.h" 1.38 +#include "nsIScriptObjectPrincipal.h" 1.39 +#include "nsNetUtil.h" 1.40 +#include "nsDOMFile.h" 1.41 + 1.42 +#include "DataChannel.h" 1.43 + 1.44 +// Since we've moved the windows.h include down here, we have to explicitly 1.45 +// undef GetBinaryType, otherwise we'll get really odd conflicts 1.46 +#ifdef GetBinaryType 1.47 +#undef GetBinaryType 1.48 +#endif 1.49 + 1.50 +using namespace mozilla; 1.51 +using namespace mozilla::dom; 1.52 + 1.53 +nsDOMDataChannel::~nsDOMDataChannel() 1.54 +{ 1.55 + // Don't call us anymore! Likely isn't an issue (or maybe just less of 1.56 + // one) once we block GC until all the (appropriate) onXxxx handlers 1.57 + // are dropped. (See WebRTC spec) 1.58 + LOG(("Close()ing %p", mDataChannel.get())); 1.59 + mDataChannel->SetListener(nullptr, nullptr); 1.60 + mDataChannel->Close(); 1.61 +} 1.62 + 1.63 +/* virtual */ JSObject* 1.64 +nsDOMDataChannel::WrapObject(JSContext* aCx) 1.65 +{ 1.66 + return DataChannelBinding::Wrap(aCx, this); 1.67 +} 1.68 + 1.69 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataChannel) 1.70 + 1.71 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataChannel, 1.72 + DOMEventTargetHelper) 1.73 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.74 + 1.75 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataChannel, 1.76 + DOMEventTargetHelper) 1.77 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.78 + 1.79 +NS_IMPL_ADDREF_INHERITED(nsDOMDataChannel, DOMEventTargetHelper) 1.80 +NS_IMPL_RELEASE_INHERITED(nsDOMDataChannel, DOMEventTargetHelper) 1.81 + 1.82 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataChannel) 1.83 + NS_INTERFACE_MAP_ENTRY(nsIDOMDataChannel) 1.84 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.85 + 1.86 +nsDOMDataChannel::nsDOMDataChannel(already_AddRefed<mozilla::DataChannel>& aDataChannel, 1.87 + nsPIDOMWindow* aWindow) 1.88 + : DOMEventTargetHelper(aWindow && aWindow->IsOuterWindow() ? 1.89 + aWindow->GetCurrentInnerWindow() : aWindow) 1.90 + , mDataChannel(aDataChannel) 1.91 + , mBinaryType(DC_BINARY_TYPE_BLOB) 1.92 +{ 1.93 +} 1.94 + 1.95 +nsresult 1.96 +nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow) 1.97 +{ 1.98 + nsresult rv; 1.99 + nsAutoString urlParam; 1.100 + 1.101 + MOZ_ASSERT(mDataChannel); 1.102 + mDataChannel->SetListener(this, nullptr); 1.103 + 1.104 + // Now grovel through the objects to get a usable origin for onMessage 1.105 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDOMWindow); 1.106 + NS_ENSURE_STATE(sgo); 1.107 + nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext(); 1.108 + NS_ENSURE_STATE(scriptContext); 1.109 + 1.110 + nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aDOMWindow)); 1.111 + NS_ENSURE_STATE(scriptPrincipal); 1.112 + nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal(); 1.113 + NS_ENSURE_STATE(principal); 1.114 + 1.115 + // Attempt to kill "ghost" DataChannel (if one can happen): but usually too early for check to fail 1.116 + rv = CheckInnerWindowCorrectness(); 1.117 + NS_ENSURE_SUCCESS(rv,rv); 1.118 + 1.119 + rv = nsContentUtils::GetUTFOrigin(principal,mOrigin); 1.120 + LOG(("%s: origin = %s\n",__FUNCTION__,NS_LossyConvertUTF16toASCII(mOrigin).get())); 1.121 + return rv; 1.122 +} 1.123 + 1.124 +NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, open) 1.125 +NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, error) 1.126 +NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, close) 1.127 +NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, message) 1.128 + 1.129 +NS_IMETHODIMP 1.130 +nsDOMDataChannel::GetLabel(nsAString& aLabel) 1.131 +{ 1.132 + mDataChannel->GetLabel(aLabel); 1.133 + return NS_OK; 1.134 +} 1.135 + 1.136 +NS_IMETHODIMP 1.137 +nsDOMDataChannel::GetProtocol(nsAString& aProtocol) 1.138 +{ 1.139 + mDataChannel->GetProtocol(aProtocol); 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +uint16_t 1.144 +nsDOMDataChannel::Id() const 1.145 +{ 1.146 + return mDataChannel->GetStream(); 1.147 +} 1.148 + 1.149 +NS_IMETHODIMP 1.150 +nsDOMDataChannel::GetId(uint16_t *aId) 1.151 +{ 1.152 + *aId = Id(); 1.153 + return NS_OK; 1.154 +} 1.155 + 1.156 +uint16_t 1.157 +nsDOMDataChannel::Stream() const 1.158 +{ 1.159 + return mDataChannel->GetStream(); 1.160 +} 1.161 + 1.162 +NS_IMETHODIMP 1.163 +nsDOMDataChannel::GetStream(uint16_t *aStream) 1.164 +{ 1.165 + *aStream = Stream(); 1.166 + return NS_OK; 1.167 +} 1.168 + 1.169 +// XXX should be GetType()? Open question for the spec 1.170 +bool 1.171 +nsDOMDataChannel::Reliable() const 1.172 +{ 1.173 + return mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE; 1.174 +} 1.175 + 1.176 +NS_IMETHODIMP 1.177 +nsDOMDataChannel::GetReliable(bool* aReliable) 1.178 +{ 1.179 + *aReliable = Reliable(); 1.180 + return NS_OK; 1.181 +} 1.182 + 1.183 +bool 1.184 +nsDOMDataChannel::Ordered() const 1.185 +{ 1.186 + return mDataChannel->GetOrdered(); 1.187 +} 1.188 + 1.189 +NS_IMETHODIMP 1.190 +nsDOMDataChannel::GetOrdered(bool* aOrdered) 1.191 +{ 1.192 + *aOrdered = Ordered(); 1.193 + return NS_OK; 1.194 +} 1.195 + 1.196 +RTCDataChannelState 1.197 +nsDOMDataChannel::ReadyState() const 1.198 +{ 1.199 + return static_cast<RTCDataChannelState>(mDataChannel->GetReadyState()); 1.200 +} 1.201 + 1.202 + 1.203 +NS_IMETHODIMP 1.204 +nsDOMDataChannel::GetReadyState(nsAString& aReadyState) 1.205 +{ 1.206 + uint16_t readyState = mDataChannel->GetReadyState(); 1.207 + // From the WebRTC spec 1.208 + const char * stateName[] = { 1.209 + "connecting", 1.210 + "open", 1.211 + "closing", 1.212 + "closed" 1.213 + }; 1.214 + MOZ_ASSERT(/*readyState >= mozilla::DataChannel::CONNECTING && */ // Always true due to datatypes 1.215 + readyState <= mozilla::DataChannel::CLOSED); 1.216 + aReadyState.AssignASCII(stateName[readyState]); 1.217 + 1.218 + return NS_OK; 1.219 +} 1.220 + 1.221 +uint32_t 1.222 +nsDOMDataChannel::BufferedAmount() const 1.223 +{ 1.224 + return mDataChannel->GetBufferedAmount(); 1.225 +} 1.226 + 1.227 +NS_IMETHODIMP 1.228 +nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount) 1.229 +{ 1.230 + *aBufferedAmount = BufferedAmount(); 1.231 + return NS_OK; 1.232 +} 1.233 + 1.234 +NS_IMETHODIMP nsDOMDataChannel::GetBinaryType(nsAString & aBinaryType) 1.235 +{ 1.236 + switch (mBinaryType) { 1.237 + case DC_BINARY_TYPE_ARRAYBUFFER: 1.238 + aBinaryType.AssignLiteral("arraybuffer"); 1.239 + break; 1.240 + case DC_BINARY_TYPE_BLOB: 1.241 + aBinaryType.AssignLiteral("blob"); 1.242 + break; 1.243 + default: 1.244 + NS_ERROR("Should not happen"); 1.245 + } 1.246 + return NS_OK; 1.247 +} 1.248 + 1.249 +NS_IMETHODIMP 1.250 +nsDOMDataChannel::SetBinaryType(const nsAString& aBinaryType) 1.251 +{ 1.252 + if (aBinaryType.EqualsLiteral("arraybuffer")) { 1.253 + mBinaryType = DC_BINARY_TYPE_ARRAYBUFFER; 1.254 + } else if (aBinaryType.EqualsLiteral("blob")) { 1.255 + mBinaryType = DC_BINARY_TYPE_BLOB; 1.256 + } else { 1.257 + return NS_ERROR_INVALID_ARG; 1.258 + } 1.259 + return NS_OK; 1.260 +} 1.261 + 1.262 +NS_IMETHODIMP 1.263 +nsDOMDataChannel::Close() 1.264 +{ 1.265 + mDataChannel->Close(); 1.266 + return NS_OK; 1.267 +} 1.268 + 1.269 +// All of the following is copy/pasted from WebSocket.cpp. 1.270 +void 1.271 +nsDOMDataChannel::Send(const nsAString& aData, ErrorResult& aRv) 1.272 +{ 1.273 + NS_ConvertUTF16toUTF8 msgString(aData); 1.274 + Send(nullptr, msgString, msgString.Length(), false, aRv); 1.275 +} 1.276 + 1.277 +void 1.278 +nsDOMDataChannel::Send(nsIDOMBlob* aData, ErrorResult& aRv) 1.279 +{ 1.280 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); 1.281 + 1.282 + nsCOMPtr<nsIInputStream> msgStream; 1.283 + nsresult rv = aData->GetInternalStream(getter_AddRefs(msgStream)); 1.284 + if (NS_FAILED(rv)) { 1.285 + aRv.Throw(rv); 1.286 + return; 1.287 + } 1.288 + 1.289 + uint64_t msgLength; 1.290 + rv = aData->GetSize(&msgLength); 1.291 + if (NS_FAILED(rv)) { 1.292 + aRv.Throw(rv); 1.293 + return; 1.294 + } 1.295 + 1.296 + if (msgLength > UINT32_MAX) { 1.297 + aRv.Throw(NS_ERROR_FILE_TOO_BIG); 1.298 + return; 1.299 + } 1.300 + 1.301 + Send(msgStream, EmptyCString(), msgLength, true, aRv); 1.302 +} 1.303 + 1.304 +void 1.305 +nsDOMDataChannel::Send(const ArrayBuffer& aData, ErrorResult& aRv) 1.306 +{ 1.307 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); 1.308 + 1.309 + aData.ComputeLengthAndData(); 1.310 + 1.311 + static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); 1.312 + 1.313 + uint32_t len = aData.Length(); 1.314 + char* data = reinterpret_cast<char*>(aData.Data()); 1.315 + 1.316 + nsDependentCSubstring msgString(data, len); 1.317 + Send(nullptr, msgString, len, true, aRv); 1.318 +} 1.319 + 1.320 +void 1.321 +nsDOMDataChannel::Send(const ArrayBufferView& aData, ErrorResult& aRv) 1.322 +{ 1.323 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); 1.324 + 1.325 + aData.ComputeLengthAndData(); 1.326 + 1.327 + static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); 1.328 + 1.329 + uint32_t len = aData.Length(); 1.330 + char* data = reinterpret_cast<char*>(aData.Data()); 1.331 + 1.332 + nsDependentCSubstring msgString(data, len); 1.333 + Send(nullptr, msgString, len, true, aRv); 1.334 +} 1.335 + 1.336 +void 1.337 +nsDOMDataChannel::Send(nsIInputStream* aMsgStream, 1.338 + const nsACString& aMsgString, 1.339 + uint32_t aMsgLength, 1.340 + bool aIsBinary, 1.341 + ErrorResult& aRv) 1.342 +{ 1.343 + MOZ_ASSERT(NS_IsMainThread()); 1.344 + uint16_t state = mDataChannel->GetReadyState(); 1.345 + 1.346 + // In reality, the DataChannel protocol allows this, but we want it to 1.347 + // look like WebSockets 1.348 + if (state == mozilla::DataChannel::CONNECTING) { 1.349 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.350 + return; 1.351 + } 1.352 + 1.353 + if (state == mozilla::DataChannel::CLOSING || 1.354 + state == mozilla::DataChannel::CLOSED) { 1.355 + return; 1.356 + } 1.357 + 1.358 + MOZ_ASSERT(state == mozilla::DataChannel::OPEN, 1.359 + "Unknown state in nsDOMDataChannel::Send"); 1.360 + 1.361 + int32_t sent; 1.362 + if (aMsgStream) { 1.363 + sent = mDataChannel->SendBinaryStream(aMsgStream, aMsgLength); 1.364 + } else { 1.365 + if (aIsBinary) { 1.366 + sent = mDataChannel->SendBinaryMsg(aMsgString); 1.367 + } else { 1.368 + sent = mDataChannel->SendMsg(aMsgString); 1.369 + } 1.370 + } 1.371 + if (sent < 0) { 1.372 + aRv.Throw(NS_ERROR_FAILURE); 1.373 + } 1.374 +} 1.375 + 1.376 +nsresult 1.377 +nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData, 1.378 + bool aBinary) 1.379 +{ 1.380 + MOZ_ASSERT(NS_IsMainThread()); 1.381 + 1.382 + LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : "")); 1.383 + 1.384 + nsresult rv = CheckInnerWindowCorrectness(); 1.385 + if (NS_FAILED(rv)) { 1.386 + return NS_OK; 1.387 + } 1.388 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner()); 1.389 + NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE); 1.390 + 1.391 + nsIScriptContext* sc = sgo->GetContext(); 1.392 + NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE); 1.393 + 1.394 + AutoPushJSContext cx(sc->GetNativeContext()); 1.395 + NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); 1.396 + 1.397 + JS::Rooted<JS::Value> jsData(cx); 1.398 + 1.399 + if (aBinary) { 1.400 + if (mBinaryType == DC_BINARY_TYPE_BLOB) { 1.401 + rv = nsContentUtils::CreateBlobBuffer(cx, aData, &jsData); 1.402 + NS_ENSURE_SUCCESS(rv, rv); 1.403 + } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) { 1.404 + JS::Rooted<JSObject*> arrayBuf(cx); 1.405 + rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address()); 1.406 + NS_ENSURE_SUCCESS(rv, rv); 1.407 + jsData = OBJECT_TO_JSVAL(arrayBuf); 1.408 + } else { 1.409 + NS_RUNTIMEABORT("Unknown binary type!"); 1.410 + return NS_ERROR_UNEXPECTED; 1.411 + } 1.412 + } else { 1.413 + NS_ConvertUTF8toUTF16 utf16data(aData); 1.414 + JSString* jsString = JS_NewUCStringCopyN(cx, utf16data.get(), utf16data.Length()); 1.415 + NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE); 1.416 + 1.417 + jsData = STRING_TO_JSVAL(jsString); 1.418 + } 1.419 + 1.420 + nsCOMPtr<nsIDOMEvent> event; 1.421 + rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr); 1.422 + NS_ENSURE_SUCCESS(rv,rv); 1.423 + 1.424 + nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event); 1.425 + rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"), 1.426 + false, false, 1.427 + jsData, mOrigin, EmptyString(), 1.428 + nullptr); 1.429 + NS_ENSURE_SUCCESS(rv,rv); 1.430 + event->SetTrusted(true); 1.431 + 1.432 + LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__)); 1.433 + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.434 + if (NS_FAILED(rv)) { 1.435 + NS_WARNING("Failed to dispatch the message event!!!"); 1.436 + } 1.437 + return rv; 1.438 +} 1.439 + 1.440 +nsresult 1.441 +nsDOMDataChannel::OnMessageAvailable(nsISupports* aContext, 1.442 + const nsACString& aMessage) 1.443 +{ 1.444 + MOZ_ASSERT(NS_IsMainThread()); 1.445 + return DoOnMessageAvailable(aMessage, false); 1.446 +} 1.447 + 1.448 +nsresult 1.449 +nsDOMDataChannel::OnBinaryMessageAvailable(nsISupports* aContext, 1.450 + const nsACString& aMessage) 1.451 +{ 1.452 + MOZ_ASSERT(NS_IsMainThread()); 1.453 + return DoOnMessageAvailable(aMessage, true); 1.454 +} 1.455 + 1.456 +nsresult 1.457 +nsDOMDataChannel::OnSimpleEvent(nsISupports* aContext, const nsAString& aName) 1.458 +{ 1.459 + MOZ_ASSERT(NS_IsMainThread()); 1.460 + 1.461 + nsresult rv = CheckInnerWindowCorrectness(); 1.462 + if (NS_FAILED(rv)) { 1.463 + return NS_OK; 1.464 + } 1.465 + 1.466 + nsCOMPtr<nsIDOMEvent> event; 1.467 + rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); 1.468 + NS_ENSURE_SUCCESS(rv,rv); 1.469 + 1.470 + rv = event->InitEvent(aName, false, false); 1.471 + NS_ENSURE_SUCCESS(rv,rv); 1.472 + 1.473 + event->SetTrusted(true); 1.474 + 1.475 + return DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.476 +} 1.477 + 1.478 +nsresult 1.479 +nsDOMDataChannel::OnChannelConnected(nsISupports* aContext) 1.480 +{ 1.481 + LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__)); 1.482 + 1.483 + return OnSimpleEvent(aContext, NS_LITERAL_STRING("open")); 1.484 +} 1.485 + 1.486 +nsresult 1.487 +nsDOMDataChannel::OnChannelClosed(nsISupports* aContext) 1.488 +{ 1.489 + LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__)); 1.490 + 1.491 + return OnSimpleEvent(aContext, NS_LITERAL_STRING("close")); 1.492 +} 1.493 + 1.494 +void 1.495 +nsDOMDataChannel::AppReady() 1.496 +{ 1.497 + mDataChannel->AppReady(); 1.498 +} 1.499 + 1.500 +/* static */ 1.501 +nsresult 1.502 +NS_NewDOMDataChannel(already_AddRefed<mozilla::DataChannel>&& aDataChannel, 1.503 + nsPIDOMWindow* aWindow, 1.504 + nsIDOMDataChannel** aDomDataChannel) 1.505 +{ 1.506 + nsRefPtr<nsDOMDataChannel> domdc = 1.507 + new nsDOMDataChannel(aDataChannel, aWindow); 1.508 + 1.509 + nsresult rv = domdc->Init(aWindow); 1.510 + NS_ENSURE_SUCCESS(rv,rv); 1.511 + 1.512 + return CallQueryInterface(domdc, aDomDataChannel); 1.513 +} 1.514 + 1.515 +/* static */ 1.516 +void 1.517 +NS_DataChannelAppReady(nsIDOMDataChannel* aDomDataChannel) 1.518 +{ 1.519 + ((nsDOMDataChannel *)aDomDataChannel)->AppReady(); 1.520 +}