dom/base/MessagePort.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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

mercurial