Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MessagePort.h"
7 #include "MessageEvent.h"
8 #include "mozilla/dom/Event.h"
9 #include "mozilla/dom/MessageChannel.h"
10 #include "mozilla/dom/MessagePortBinding.h"
11 #include "mozilla/dom/MessagePortList.h"
12 #include "mozilla/dom/StructuredCloneTags.h"
13 #include "nsContentUtils.h"
14 #include "nsGlobalWindow.h"
15 #include "nsPresContext.h"
17 #include "nsIDocument.h"
18 #include "nsIDOMFile.h"
19 #include "nsIDOMFileList.h"
20 #include "nsIPresShell.h"
22 namespace mozilla {
23 namespace dom {
25 class DispatchEventRunnable : public nsRunnable
26 {
27 friend class MessagePort;
29 public:
30 DispatchEventRunnable(MessagePort* aPort)
31 : mPort(aPort)
32 {
33 }
35 NS_IMETHOD
36 Run()
37 {
38 nsRefPtr<DispatchEventRunnable> mKungFuDeathGrip(this);
40 mPort->mDispatchRunnable = nullptr;
41 mPort->Dispatch();
43 return NS_OK;
44 }
46 private:
47 nsRefPtr<MessagePort> mPort;
48 };
50 class PostMessageRunnable : public nsRunnable
51 {
52 friend class MessagePort;
54 public:
55 NS_DECL_NSIRUNNABLE
57 PostMessageRunnable()
58 {
59 }
61 ~PostMessageRunnable()
62 {
63 }
65 JSAutoStructuredCloneBuffer& Buffer()
66 {
67 return mBuffer;
68 }
70 bool StoreISupports(nsISupports* aSupports)
71 {
72 mSupportsArray.AppendElement(aSupports);
73 return true;
74 }
76 void Dispatch(MessagePort* aPort)
77 {
78 mPort = aPort;
79 NS_DispatchToCurrentThread(this);
80 }
82 private:
83 nsRefPtr<MessagePort> mPort;
84 JSAutoStructuredCloneBuffer mBuffer;
86 nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
87 };
89 namespace {
91 struct StructuredCloneInfo
92 {
93 PostMessageRunnable* mEvent;
94 MessagePort* mPort;
95 nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts;
96 };
98 static JSObject*
99 PostMessageReadStructuredClone(JSContext* cx,
100 JSStructuredCloneReader* reader,
101 uint32_t tag,
102 uint32_t data,
103 void* closure)
104 {
105 if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
106 NS_ASSERTION(!data, "Data should be empty");
108 nsISupports* supports;
109 if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
110 JS::Rooted<JS::Value> val(cx);
111 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
112 return JSVAL_TO_OBJECT(val);
113 }
114 }
115 }
117 const JSStructuredCloneCallbacks* runtimeCallbacks =
118 js::GetContextStructuredCloneCallbacks(cx);
120 if (runtimeCallbacks) {
121 return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
122 }
124 return nullptr;
125 }
127 static bool
128 PostMessageWriteStructuredClone(JSContext* cx,
129 JSStructuredCloneWriter* writer,
130 JS::Handle<JSObject*> obj,
131 void *closure)
132 {
133 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
134 NS_ASSERTION(scInfo, "Must have scInfo!");
136 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
137 nsContentUtils::XPConnect()->
138 GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
139 if (wrappedNative) {
140 uint32_t scTag = 0;
141 nsISupports* supports = wrappedNative->Native();
143 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
144 if (blob) {
145 scTag = SCTAG_DOM_BLOB;
146 }
148 nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
149 if (list) {
150 scTag = SCTAG_DOM_FILELIST;
151 }
153 if (scTag) {
154 return JS_WriteUint32Pair(writer, scTag, 0) &&
155 JS_WriteBytes(writer, &supports, sizeof(supports)) &&
156 scInfo->mEvent->StoreISupports(supports);
157 }
158 }
160 const JSStructuredCloneCallbacks* runtimeCallbacks =
161 js::GetContextStructuredCloneCallbacks(cx);
163 if (runtimeCallbacks) {
164 return runtimeCallbacks->write(cx, writer, obj, nullptr);
165 }
167 return false;
168 }
170 static bool
171 PostMessageReadTransferStructuredClone(JSContext* aCx,
172 JSStructuredCloneReader* reader,
173 uint32_t tag, void* data,
174 uint64_t unused,
175 void* aClosure,
176 JS::MutableHandle<JSObject*> returnObject)
177 {
178 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
179 NS_ASSERTION(scInfo, "Must have scInfo!");
181 if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
182 MessagePort* port = static_cast<MessagePort*>(data);
183 port->BindToOwner(scInfo->mPort->GetOwner());
184 scInfo->mPorts.Put(port, nullptr);
186 JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx));
187 if (JS_WrapObject(aCx, &obj)) {
188 MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
189 returnObject.set(obj);
190 }
191 return true;
192 }
194 return false;
195 }
197 static bool
198 PostMessageTransferStructuredClone(JSContext* aCx,
199 JS::Handle<JSObject*> aObj,
200 void* aClosure,
201 uint32_t* aTag,
202 JS::TransferableOwnership* aOwnership,
203 void** aContent,
204 uint64_t *aExtraData)
205 {
206 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
207 NS_ASSERTION(scInfo, "Must have scInfo!");
209 MessagePortBase *port = nullptr;
210 nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
211 if (NS_SUCCEEDED(rv)) {
212 nsRefPtr<MessagePortBase> newPort;
213 if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) {
214 // No duplicate.
215 return false;
216 }
218 newPort = port->Clone();
219 scInfo->mPorts.Put(port, newPort);
221 *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
222 *aOwnership = JS::SCTAG_TMO_CUSTOM;
223 *aContent = newPort;
224 *aExtraData = 0;
226 return true;
227 }
229 return false;
230 }
232 static void
233 PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
234 void* aData,
235 uint64_t aExtraData,
236 void* aClosure)
237 {
238 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
239 NS_ASSERTION(scInfo, "Must have scInfo!");
241 if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
242 MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM);
243 nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData));
244 scInfo->mPorts.Remove(port);
245 }
246 }
248 JSStructuredCloneCallbacks kPostMessageCallbacks = {
249 PostMessageReadStructuredClone,
250 PostMessageWriteStructuredClone,
251 nullptr,
252 PostMessageReadTransferStructuredClone,
253 PostMessageTransferStructuredClone,
254 PostMessageFreeTransferStructuredClone
255 };
257 } // anonymous namespace
259 static PLDHashOperator
260 PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
261 {
262 nsTArray<nsRefPtr<MessagePortBase> > *array =
263 static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
265 array->AppendElement(aKey);
266 return PL_DHASH_NEXT;
267 }
269 NS_IMETHODIMP
270 PostMessageRunnable::Run()
271 {
272 MOZ_ASSERT(mPort);
274 // Get the JSContext for the target window
275 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mPort->GetOwner());
276 NS_ENSURE_STATE(sgo);
277 nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
278 AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext()
279 : nsContentUtils::GetSafeJSContext());
281 MOZ_ASSERT(cx);
283 // Deserialize the structured clone data
284 JS::Rooted<JS::Value> messageData(cx);
285 StructuredCloneInfo scInfo;
286 scInfo.mEvent = this;
287 scInfo.mPort = mPort;
289 if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
290 return NS_ERROR_DOM_DATA_CLONE_ERR;
291 }
293 // Create the event
294 nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
295 do_QueryInterface(mPort->GetOwner());
296 nsRefPtr<MessageEvent> event =
297 new MessageEvent(eventTarget, nullptr, nullptr);
299 event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */,
300 false /* cancelable */, messageData, EmptyString(),
301 EmptyString(), nullptr);
302 event->SetTrusted(true);
303 event->SetSource(mPort);
305 nsTArray<nsRefPtr<MessagePortBase> > ports;
306 scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports);
307 event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
309 bool status;
310 mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status);
311 return status ? NS_OK : NS_ERROR_FAILURE;
312 }
314 MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
315 : DOMEventTargetHelper(aWindow)
316 {
317 // SetIsDOMBinding() is called by DOMEventTargetHelper's ctor.
318 }
320 MessagePortBase::MessagePortBase()
321 {
322 SetIsDOMBinding();
323 }
325 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
327 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
328 DOMEventTargetHelper)
329 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort)
331 // Custom unlink loop because this array contains nsRunnable objects
332 // which are not cycle colleactable.
333 while (!tmp->mMessageQueue.IsEmpty()) {
334 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort);
335 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray);
336 tmp->mMessageQueue.RemoveElementAt(0);
337 }
339 if (tmp->mDispatchRunnable) {
340 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
341 }
343 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
345 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
346 DOMEventTargetHelper)
347 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort)
349 // Custom unlink loop because this array contains nsRunnable objects
350 // which are not cycle colleactable.
351 for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) {
352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort);
353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray);
354 }
356 if (tmp->mDispatchRunnable) {
357 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
358 }
360 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
362 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
363 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
365 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
366 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
368 MessagePort::MessagePort(nsPIDOMWindow* aWindow)
369 : MessagePortBase(aWindow)
370 , mMessageQueueEnabled(false)
371 {
372 }
374 MessagePort::~MessagePort()
375 {
376 Close();
377 }
379 JSObject*
380 MessagePort::WrapObject(JSContext* aCx)
381 {
382 return MessagePortBinding::Wrap(aCx, this);
383 }
385 void
386 MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
387 const Optional<Sequence<JS::Value>>& aTransferable,
388 ErrorResult& aRv)
389 {
390 nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable();
392 // We *must* clone the data here, or the JS::Value could be modified
393 // by script
394 StructuredCloneInfo scInfo;
395 scInfo.mEvent = event;
396 scInfo.mPort = this;
398 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
399 if (aTransferable.WasPassed()) {
400 const Sequence<JS::Value>& realTransferable = aTransferable.Value();
402 // The input sequence only comes from the generated bindings code, which
403 // ensures it is rooted.
404 JS::HandleValueArray elements =
405 JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
406 realTransferable.Elements());
408 JSObject* array =
409 JS_NewArrayObject(aCx, elements);
410 if (!array) {
411 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
412 return;
413 }
414 transferable.setObject(*array);
415 }
417 if (!event->Buffer().write(aCx, aMessage, transferable,
418 &kPostMessageCallbacks, &scInfo)) {
419 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
420 return;
421 }
423 if (!mEntangledPort) {
424 return;
425 }
427 mEntangledPort->mMessageQueue.AppendElement(event);
428 mEntangledPort->Dispatch();
429 }
431 void
432 MessagePort::Start()
433 {
434 if (mMessageQueueEnabled) {
435 return;
436 }
438 mMessageQueueEnabled = true;
439 Dispatch();
440 }
442 void
443 MessagePort::Dispatch()
444 {
445 if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) {
446 return;
447 }
449 nsRefPtr<PostMessageRunnable> event = mMessageQueue.ElementAt(0);
450 mMessageQueue.RemoveElementAt(0);
452 event->Dispatch(this);
454 mDispatchRunnable = new DispatchEventRunnable(this);
455 NS_DispatchToCurrentThread(mDispatchRunnable);
456 }
458 void
459 MessagePort::Close()
460 {
461 if (!mEntangledPort) {
462 return;
463 }
465 // This avoids loops.
466 nsRefPtr<MessagePort> port = mEntangledPort;
467 mEntangledPort = nullptr;
469 // Let's disentangle the 2 ports symmetrically.
470 port->Close();
471 }
473 EventHandlerNonNull*
474 MessagePort::GetOnmessage()
475 {
476 if (NS_IsMainThread()) {
477 return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
478 }
479 return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
480 }
482 void
483 MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
484 {
485 if (NS_IsMainThread()) {
486 SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
487 } else {
488 SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
489 }
491 // When using onmessage, the call to start() is implied.
492 Start();
493 }
495 void
496 MessagePort::Entangle(MessagePort* aMessagePort)
497 {
498 MOZ_ASSERT(aMessagePort);
499 MOZ_ASSERT(aMessagePort != this);
501 Close();
503 mEntangledPort = aMessagePort;
504 }
506 already_AddRefed<MessagePortBase>
507 MessagePort::Clone()
508 {
509 nsRefPtr<MessagePort> newPort = new MessagePort(nullptr);
511 // Move all the events in the port message queue of original port.
512 newPort->mMessageQueue.SwapElements(mMessageQueue);
514 if (mEntangledPort) {
515 nsRefPtr<MessagePort> port = mEntangledPort;
516 mEntangledPort = nullptr;
518 newPort->Entangle(port);
519 port->Entangle(newPort);
520 }
522 return newPort.forget();
523 }
525 } // namespace dom
526 } // namespace mozilla