1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/base/MessagePort.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,526 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "MessagePort.h" 1.10 +#include "MessageEvent.h" 1.11 +#include "mozilla/dom/Event.h" 1.12 +#include "mozilla/dom/MessageChannel.h" 1.13 +#include "mozilla/dom/MessagePortBinding.h" 1.14 +#include "mozilla/dom/MessagePortList.h" 1.15 +#include "mozilla/dom/StructuredCloneTags.h" 1.16 +#include "nsContentUtils.h" 1.17 +#include "nsGlobalWindow.h" 1.18 +#include "nsPresContext.h" 1.19 + 1.20 +#include "nsIDocument.h" 1.21 +#include "nsIDOMFile.h" 1.22 +#include "nsIDOMFileList.h" 1.23 +#include "nsIPresShell.h" 1.24 + 1.25 +namespace mozilla { 1.26 +namespace dom { 1.27 + 1.28 +class DispatchEventRunnable : public nsRunnable 1.29 +{ 1.30 + friend class MessagePort; 1.31 + 1.32 + public: 1.33 + DispatchEventRunnable(MessagePort* aPort) 1.34 + : mPort(aPort) 1.35 + { 1.36 + } 1.37 + 1.38 + NS_IMETHOD 1.39 + Run() 1.40 + { 1.41 + nsRefPtr<DispatchEventRunnable> mKungFuDeathGrip(this); 1.42 + 1.43 + mPort->mDispatchRunnable = nullptr; 1.44 + mPort->Dispatch(); 1.45 + 1.46 + return NS_OK; 1.47 + } 1.48 + 1.49 + private: 1.50 + nsRefPtr<MessagePort> mPort; 1.51 +}; 1.52 + 1.53 +class PostMessageRunnable : public nsRunnable 1.54 +{ 1.55 + friend class MessagePort; 1.56 + 1.57 + public: 1.58 + NS_DECL_NSIRUNNABLE 1.59 + 1.60 + PostMessageRunnable() 1.61 + { 1.62 + } 1.63 + 1.64 + ~PostMessageRunnable() 1.65 + { 1.66 + } 1.67 + 1.68 + JSAutoStructuredCloneBuffer& Buffer() 1.69 + { 1.70 + return mBuffer; 1.71 + } 1.72 + 1.73 + bool StoreISupports(nsISupports* aSupports) 1.74 + { 1.75 + mSupportsArray.AppendElement(aSupports); 1.76 + return true; 1.77 + } 1.78 + 1.79 + void Dispatch(MessagePort* aPort) 1.80 + { 1.81 + mPort = aPort; 1.82 + NS_DispatchToCurrentThread(this); 1.83 + } 1.84 + 1.85 + private: 1.86 + nsRefPtr<MessagePort> mPort; 1.87 + JSAutoStructuredCloneBuffer mBuffer; 1.88 + 1.89 + nsTArray<nsCOMPtr<nsISupports> > mSupportsArray; 1.90 +}; 1.91 + 1.92 +namespace { 1.93 + 1.94 +struct StructuredCloneInfo 1.95 +{ 1.96 + PostMessageRunnable* mEvent; 1.97 + MessagePort* mPort; 1.98 + nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts; 1.99 +}; 1.100 + 1.101 +static JSObject* 1.102 +PostMessageReadStructuredClone(JSContext* cx, 1.103 + JSStructuredCloneReader* reader, 1.104 + uint32_t tag, 1.105 + uint32_t data, 1.106 + void* closure) 1.107 +{ 1.108 + if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) { 1.109 + NS_ASSERTION(!data, "Data should be empty"); 1.110 + 1.111 + nsISupports* supports; 1.112 + if (JS_ReadBytes(reader, &supports, sizeof(supports))) { 1.113 + JS::Rooted<JS::Value> val(cx); 1.114 + if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) { 1.115 + return JSVAL_TO_OBJECT(val); 1.116 + } 1.117 + } 1.118 + } 1.119 + 1.120 + const JSStructuredCloneCallbacks* runtimeCallbacks = 1.121 + js::GetContextStructuredCloneCallbacks(cx); 1.122 + 1.123 + if (runtimeCallbacks) { 1.124 + return runtimeCallbacks->read(cx, reader, tag, data, nullptr); 1.125 + } 1.126 + 1.127 + return nullptr; 1.128 +} 1.129 + 1.130 +static bool 1.131 +PostMessageWriteStructuredClone(JSContext* cx, 1.132 + JSStructuredCloneWriter* writer, 1.133 + JS::Handle<JSObject*> obj, 1.134 + void *closure) 1.135 +{ 1.136 + StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure); 1.137 + NS_ASSERTION(scInfo, "Must have scInfo!"); 1.138 + 1.139 + nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative; 1.140 + nsContentUtils::XPConnect()-> 1.141 + GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative)); 1.142 + if (wrappedNative) { 1.143 + uint32_t scTag = 0; 1.144 + nsISupports* supports = wrappedNative->Native(); 1.145 + 1.146 + nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports); 1.147 + if (blob) { 1.148 + scTag = SCTAG_DOM_BLOB; 1.149 + } 1.150 + 1.151 + nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports); 1.152 + if (list) { 1.153 + scTag = SCTAG_DOM_FILELIST; 1.154 + } 1.155 + 1.156 + if (scTag) { 1.157 + return JS_WriteUint32Pair(writer, scTag, 0) && 1.158 + JS_WriteBytes(writer, &supports, sizeof(supports)) && 1.159 + scInfo->mEvent->StoreISupports(supports); 1.160 + } 1.161 + } 1.162 + 1.163 + const JSStructuredCloneCallbacks* runtimeCallbacks = 1.164 + js::GetContextStructuredCloneCallbacks(cx); 1.165 + 1.166 + if (runtimeCallbacks) { 1.167 + return runtimeCallbacks->write(cx, writer, obj, nullptr); 1.168 + } 1.169 + 1.170 + return false; 1.171 +} 1.172 + 1.173 +static bool 1.174 +PostMessageReadTransferStructuredClone(JSContext* aCx, 1.175 + JSStructuredCloneReader* reader, 1.176 + uint32_t tag, void* data, 1.177 + uint64_t unused, 1.178 + void* aClosure, 1.179 + JS::MutableHandle<JSObject*> returnObject) 1.180 +{ 1.181 + StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure); 1.182 + NS_ASSERTION(scInfo, "Must have scInfo!"); 1.183 + 1.184 + if (tag == SCTAG_DOM_MAP_MESSAGEPORT) { 1.185 + MessagePort* port = static_cast<MessagePort*>(data); 1.186 + port->BindToOwner(scInfo->mPort->GetOwner()); 1.187 + scInfo->mPorts.Put(port, nullptr); 1.188 + 1.189 + JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx)); 1.190 + if (JS_WrapObject(aCx, &obj)) { 1.191 + MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner()); 1.192 + returnObject.set(obj); 1.193 + } 1.194 + return true; 1.195 + } 1.196 + 1.197 + return false; 1.198 +} 1.199 + 1.200 +static bool 1.201 +PostMessageTransferStructuredClone(JSContext* aCx, 1.202 + JS::Handle<JSObject*> aObj, 1.203 + void* aClosure, 1.204 + uint32_t* aTag, 1.205 + JS::TransferableOwnership* aOwnership, 1.206 + void** aContent, 1.207 + uint64_t *aExtraData) 1.208 +{ 1.209 + StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure); 1.210 + NS_ASSERTION(scInfo, "Must have scInfo!"); 1.211 + 1.212 + MessagePortBase *port = nullptr; 1.213 + nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port); 1.214 + if (NS_SUCCEEDED(rv)) { 1.215 + nsRefPtr<MessagePortBase> newPort; 1.216 + if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) { 1.217 + // No duplicate. 1.218 + return false; 1.219 + } 1.220 + 1.221 + newPort = port->Clone(); 1.222 + scInfo->mPorts.Put(port, newPort); 1.223 + 1.224 + *aTag = SCTAG_DOM_MAP_MESSAGEPORT; 1.225 + *aOwnership = JS::SCTAG_TMO_CUSTOM; 1.226 + *aContent = newPort; 1.227 + *aExtraData = 0; 1.228 + 1.229 + return true; 1.230 + } 1.231 + 1.232 + return false; 1.233 +} 1.234 + 1.235 +static void 1.236 +PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership, 1.237 + void* aData, 1.238 + uint64_t aExtraData, 1.239 + void* aClosure) 1.240 +{ 1.241 + StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure); 1.242 + NS_ASSERTION(scInfo, "Must have scInfo!"); 1.243 + 1.244 + if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) { 1.245 + MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM); 1.246 + nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData)); 1.247 + scInfo->mPorts.Remove(port); 1.248 + } 1.249 +} 1.250 + 1.251 +JSStructuredCloneCallbacks kPostMessageCallbacks = { 1.252 + PostMessageReadStructuredClone, 1.253 + PostMessageWriteStructuredClone, 1.254 + nullptr, 1.255 + PostMessageReadTransferStructuredClone, 1.256 + PostMessageTransferStructuredClone, 1.257 + PostMessageFreeTransferStructuredClone 1.258 +}; 1.259 + 1.260 +} // anonymous namespace 1.261 + 1.262 +static PLDHashOperator 1.263 +PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure) 1.264 +{ 1.265 + nsTArray<nsRefPtr<MessagePortBase> > *array = 1.266 + static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure); 1.267 + 1.268 + array->AppendElement(aKey); 1.269 + return PL_DHASH_NEXT; 1.270 +} 1.271 + 1.272 +NS_IMETHODIMP 1.273 +PostMessageRunnable::Run() 1.274 +{ 1.275 + MOZ_ASSERT(mPort); 1.276 + 1.277 + // Get the JSContext for the target window 1.278 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mPort->GetOwner()); 1.279 + NS_ENSURE_STATE(sgo); 1.280 + nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext(); 1.281 + AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext() 1.282 + : nsContentUtils::GetSafeJSContext()); 1.283 + 1.284 + MOZ_ASSERT(cx); 1.285 + 1.286 + // Deserialize the structured clone data 1.287 + JS::Rooted<JS::Value> messageData(cx); 1.288 + StructuredCloneInfo scInfo; 1.289 + scInfo.mEvent = this; 1.290 + scInfo.mPort = mPort; 1.291 + 1.292 + if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) { 1.293 + return NS_ERROR_DOM_DATA_CLONE_ERR; 1.294 + } 1.295 + 1.296 + // Create the event 1.297 + nsCOMPtr<mozilla::dom::EventTarget> eventTarget = 1.298 + do_QueryInterface(mPort->GetOwner()); 1.299 + nsRefPtr<MessageEvent> event = 1.300 + new MessageEvent(eventTarget, nullptr, nullptr); 1.301 + 1.302 + event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */, 1.303 + false /* cancelable */, messageData, EmptyString(), 1.304 + EmptyString(), nullptr); 1.305 + event->SetTrusted(true); 1.306 + event->SetSource(mPort); 1.307 + 1.308 + nsTArray<nsRefPtr<MessagePortBase> > ports; 1.309 + scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports); 1.310 + event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports)); 1.311 + 1.312 + bool status; 1.313 + mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status); 1.314 + return status ? NS_OK : NS_ERROR_FAILURE; 1.315 +} 1.316 + 1.317 +MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow) 1.318 + : DOMEventTargetHelper(aWindow) 1.319 +{ 1.320 + // SetIsDOMBinding() is called by DOMEventTargetHelper's ctor. 1.321 +} 1.322 + 1.323 +MessagePortBase::MessagePortBase() 1.324 +{ 1.325 + SetIsDOMBinding(); 1.326 +} 1.327 + 1.328 +NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort) 1.329 + 1.330 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort, 1.331 + DOMEventTargetHelper) 1.332 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort) 1.333 + 1.334 + // Custom unlink loop because this array contains nsRunnable objects 1.335 + // which are not cycle colleactable. 1.336 + while (!tmp->mMessageQueue.IsEmpty()) { 1.337 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort); 1.338 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray); 1.339 + tmp->mMessageQueue.RemoveElementAt(0); 1.340 + } 1.341 + 1.342 + if (tmp->mDispatchRunnable) { 1.343 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort); 1.344 + } 1.345 + 1.346 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.347 + 1.348 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort, 1.349 + DOMEventTargetHelper) 1.350 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort) 1.351 + 1.352 + // Custom unlink loop because this array contains nsRunnable objects 1.353 + // which are not cycle colleactable. 1.354 + for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) { 1.355 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort); 1.356 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray); 1.357 + } 1.358 + 1.359 + if (tmp->mDispatchRunnable) { 1.360 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort); 1.361 + } 1.362 + 1.363 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.364 + 1.365 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort) 1.366 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.367 + 1.368 +NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper) 1.369 +NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper) 1.370 + 1.371 +MessagePort::MessagePort(nsPIDOMWindow* aWindow) 1.372 + : MessagePortBase(aWindow) 1.373 + , mMessageQueueEnabled(false) 1.374 +{ 1.375 +} 1.376 + 1.377 +MessagePort::~MessagePort() 1.378 +{ 1.379 + Close(); 1.380 +} 1.381 + 1.382 +JSObject* 1.383 +MessagePort::WrapObject(JSContext* aCx) 1.384 +{ 1.385 + return MessagePortBinding::Wrap(aCx, this); 1.386 +} 1.387 + 1.388 +void 1.389 +MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage, 1.390 + const Optional<Sequence<JS::Value>>& aTransferable, 1.391 + ErrorResult& aRv) 1.392 +{ 1.393 + nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable(); 1.394 + 1.395 + // We *must* clone the data here, or the JS::Value could be modified 1.396 + // by script 1.397 + StructuredCloneInfo scInfo; 1.398 + scInfo.mEvent = event; 1.399 + scInfo.mPort = this; 1.400 + 1.401 + JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); 1.402 + if (aTransferable.WasPassed()) { 1.403 + const Sequence<JS::Value>& realTransferable = aTransferable.Value(); 1.404 + 1.405 + // The input sequence only comes from the generated bindings code, which 1.406 + // ensures it is rooted. 1.407 + JS::HandleValueArray elements = 1.408 + JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(), 1.409 + realTransferable.Elements()); 1.410 + 1.411 + JSObject* array = 1.412 + JS_NewArrayObject(aCx, elements); 1.413 + if (!array) { 1.414 + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 1.415 + return; 1.416 + } 1.417 + transferable.setObject(*array); 1.418 + } 1.419 + 1.420 + if (!event->Buffer().write(aCx, aMessage, transferable, 1.421 + &kPostMessageCallbacks, &scInfo)) { 1.422 + aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); 1.423 + return; 1.424 + } 1.425 + 1.426 + if (!mEntangledPort) { 1.427 + return; 1.428 + } 1.429 + 1.430 + mEntangledPort->mMessageQueue.AppendElement(event); 1.431 + mEntangledPort->Dispatch(); 1.432 +} 1.433 + 1.434 +void 1.435 +MessagePort::Start() 1.436 +{ 1.437 + if (mMessageQueueEnabled) { 1.438 + return; 1.439 + } 1.440 + 1.441 + mMessageQueueEnabled = true; 1.442 + Dispatch(); 1.443 +} 1.444 + 1.445 +void 1.446 +MessagePort::Dispatch() 1.447 +{ 1.448 + if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) { 1.449 + return; 1.450 + } 1.451 + 1.452 + nsRefPtr<PostMessageRunnable> event = mMessageQueue.ElementAt(0); 1.453 + mMessageQueue.RemoveElementAt(0); 1.454 + 1.455 + event->Dispatch(this); 1.456 + 1.457 + mDispatchRunnable = new DispatchEventRunnable(this); 1.458 + NS_DispatchToCurrentThread(mDispatchRunnable); 1.459 +} 1.460 + 1.461 +void 1.462 +MessagePort::Close() 1.463 +{ 1.464 + if (!mEntangledPort) { 1.465 + return; 1.466 + } 1.467 + 1.468 + // This avoids loops. 1.469 + nsRefPtr<MessagePort> port = mEntangledPort; 1.470 + mEntangledPort = nullptr; 1.471 + 1.472 + // Let's disentangle the 2 ports symmetrically. 1.473 + port->Close(); 1.474 +} 1.475 + 1.476 +EventHandlerNonNull* 1.477 +MessagePort::GetOnmessage() 1.478 +{ 1.479 + if (NS_IsMainThread()) { 1.480 + return GetEventHandler(nsGkAtoms::onmessage, EmptyString()); 1.481 + } 1.482 + return GetEventHandler(nullptr, NS_LITERAL_STRING("message")); 1.483 +} 1.484 + 1.485 +void 1.486 +MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) 1.487 +{ 1.488 + if (NS_IsMainThread()) { 1.489 + SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback); 1.490 + } else { 1.491 + SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback); 1.492 + } 1.493 + 1.494 + // When using onmessage, the call to start() is implied. 1.495 + Start(); 1.496 +} 1.497 + 1.498 +void 1.499 +MessagePort::Entangle(MessagePort* aMessagePort) 1.500 +{ 1.501 + MOZ_ASSERT(aMessagePort); 1.502 + MOZ_ASSERT(aMessagePort != this); 1.503 + 1.504 + Close(); 1.505 + 1.506 + mEntangledPort = aMessagePort; 1.507 +} 1.508 + 1.509 +already_AddRefed<MessagePortBase> 1.510 +MessagePort::Clone() 1.511 +{ 1.512 + nsRefPtr<MessagePort> newPort = new MessagePort(nullptr); 1.513 + 1.514 + // Move all the events in the port message queue of original port. 1.515 + newPort->mMessageQueue.SwapElements(mMessageQueue); 1.516 + 1.517 + if (mEntangledPort) { 1.518 + nsRefPtr<MessagePort> port = mEntangledPort; 1.519 + mEntangledPort = nullptr; 1.520 + 1.521 + newPort->Entangle(port); 1.522 + port->Entangle(newPort); 1.523 + } 1.524 + 1.525 + return newPort.forget(); 1.526 +} 1.527 + 1.528 +} // namespace dom 1.529 +} // namespace mozilla