mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
b3dd358267
- Bug 1247362 - move mAnonymousGlobalScope tracing into nsMessageManagerScriptExecutor; r=mccr8 (9b33b54bc9)
- Bug 1195881 - Contextual Identity working under e10s. r=tanvi r=sicking r=baku (b3fd69bd92)
- Bug 1174624 - Add the Transferable parameter into SendAsyncMessage of nsFrameMessageManager. r=baku (33911dc6f7)
- Bug 1174624 - Add PortIdentifier copy code in order to communicate with same process. r=baku (d597f24e20)
- Bug 1234176 - Do not send memory pressure events to applications sent into the background. r=dhylands (687f154573)
- Bug 1201394 - Remove unused mLRUPoolSize member variable. r=gsvelto (a109934b8d)
- Bug 1144132 follow up to fix static check build bustage on a CLOSED TREE with r=me (050f49060e)
- Bug 1153394 - make HangMonitorChild::sInstance an atomic variable; r=billm (89e6905f3f)
- Bug 1202952 - Fix directory picking for e10s on Windows by making FilePickerParent use the correct nsIFilePicker API for directory picking. r=roc (a7e964d4fa)
- Bug 1227312 - Avoid calling FinalizeChildData twice in GenerateCompleteMinidump. r=ted (c29e6786ae)
- Bug 1222109 - Initialize mHasGamepadListener in InitializeMembers(); r=cleu (8057137e5d)
- Bug 1231498 - ContentParent::RecvCreateWindow() should fail in opt builds if passed bad chromeflags. r=billm (639fb93101)
- minor indentation (f5dbd8996c)
- fix misspatch (3b306e0084)
- Bug 1101264: Truncate long sourceName messages since they can be massive data: URLs. r=bent (c528048e58)
- Bug 1233497 - Update test_bug1086684.html to not access CPOWs unsafely inside SpecialPowers. r=mrbkap (d5d161eac2)
- align tests (24d98036dc)
- Bug 1232931 Return null instead of throwing if swm.getWorkerByID() cannot find the worker. r=ochameau IGNORE IDL for comment only change (17f293f323)
- Bug 1186812 (part 3) - Replace nsBaseHashtable::EnumerateRead() calls in dom/{ipc,plugins}/. r=jimm. (a944fa4480)
- Bug 1234656 - Add TouchEvent ctor, r=mbrubeck (842245df14)
- Bug 1246854 - Remove unnecessary warning. r=botond (7d0532e516)
- Bug 1245393 - Measure s{,Default}RootBranch in the Preferences memory reporter. r=froydnj. (be200f9ebe)
- Bug 1089232 - Updates nsContentPrefService to take an extra isPrivate argument. r=adw (9ea4fe075d)
- Bug 1229519: Fix toolkit/components/contentprefs to pass eslint checks. r=mconley (e48b64448b)
- Bug 663570 - MetaCSP Part 6: CSP preload changes (r=sicking) (65700820c1)
- Bug 1030936 - [CSP] remove fast-path for certified apps once the C++ backend is activated. r=ckerschb (e9527e9cfc)
- Bug 1228497 - initialize 3 members in class. r=christophkerschbaumer (44414e8429)
- Bug 1208946 - Strip URIs in CSP reports (r=dveditz) (dd6c18a8ff)
- Bug 1247464 - Run CSP report URIs through the URL classifier. r=ckerschb (ebb3570172)
- Bug 1242909, r=ckerschb (569de89b26)
- Bug 1119565: Ensure that a plugin listener's stream type is always set, even when it is STREAM_TYPE_UNKNOWN; r=jimm (43fb9ebdb9)
- Bug 1228116 - Relax Security checks for DTD loads. r=sicking (b77e2c4531)
- Bug 1195173 - Use channel->ascynOpen2 layout/style/Loader.cpp (r=bz) (97de97b864)
- let-var (fb35f8f50c)
- Bug 1226324 - Do not use NS_ENSURCE_SUCCESS(rv, NS_OK) within nsContentSecurityManager. r=tanvi (745ecaf562)
- Bug 1221365 - Tests for "Is origin potentially trustworthy?" logic. r=ckerschb,bkelly (1d520ebcc5)
- Bug 1132211 - Dispatch an event when <input type=password> is added to a document (including outside of a form). r=smaug (3e9acb8bf3)
- Bug 1217766 - All PDFs trigger the insecure password warning. r=MattN,bz (0ea7e35b96)
- Bug 1155471 - Mark some members of nsNodeInfoManager as MOZ_NON_OWNING_REF; r=baku (bd47bcea10)
- Tests for bug 1200856; r=sicking (454ff8048a)
- Bug 1243453 P1 Make nsCORSListenerProxy call UpdateChannel() for internal redirects. r=sicking (f2a45b1997)
- Bug 1243453 P2 Test XHR with a non-intercepting service worker. r=ehsan (d83b31ab3d)
- Bug 1169233 - Get grey (inactive) text color from menu labels. r=karlt (470155483b)
- Bug 1161056 - Gtk3 - use sMozWindowBackground colors for combobox background. r=karlt (4502f5583a)
- Bug 1169232 - [gtk3] Add background class to tooltip window to get correct background color. r=karlt (9421a23b1c)
- Bug 1219717 - Derive text color/background from GtkTextView. r=karlt (a39cd997ee)
- Bug 1241239 - Fix missing 'using mozilla::LogLevel' in nsIdleServiceGTK.cpp. r=karlt (16bacfc530)
- Bug 1209659 - Disable client-side decorations on broken Gtk3 versions (<3.20). r=karlt (d5cbd4c0fb)
- Bug 540078 - Remove assertion annotations that are no longer needed and add crashtest. (89f33bb00c)
- Bug 1168219 - Make nsIWidget::Configuration::mChild a smart pointer on widget/qt too. r=froydnj (0f2f97a31b)
- Bug 1234385: Add downloadable blocklist support for between comparison types, by recognizing driverVersionMax when parsing. r=benwa (87617d0fa1)
- Bug 1112712 - DOM key mapping for soft1 soft2 and call keys r=schien (3f4360e64b)
- Bug 1237691 - Implement Oculus Head Pose Prediction (3f6b0122e3)
- Bug 1041882 - Remove Froyo-specific OMX plugin support. r=snorp (eb2f6dd36a)
- Bug 1205930 - Tighten up warnings handling in media/omx-plugin/. r=gerald. (86845d720a)
- Bug 1153849 - Use MOZ_JPEG_CFLAGS when build libyuv with system jpeg. r=jesup (a38f53057d)
- Bug 1240635 - Interpret glyph x-offsets on SVG vertical text paths in the correct direction. r=longsonr (ce90452da1)
- Bug 1185266 - Look up painting properties on the SVGTextFrame when painting text frames that are direct children of <text>. r=jwatt (9c89ab71eb)
- Bug 1143096 - Init all WebMBufferedParser members - r=kinetik (7df2e4e0c3)
- Bug 1231855 - Avoid inserting out of (timecode) order entries in WebMBufferedParser. r=jya (f7806faec4)
997 lines
26 KiB
C++
997 lines
26 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "MessagePort.h"
|
|
|
|
#include "MessageEvent.h"
|
|
#include "MessagePortChild.h"
|
|
#include "mozilla/dom/BlobBinding.h"
|
|
#include "mozilla/dom/Event.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/dom/MessageChannel.h"
|
|
#include "mozilla/dom/MessagePortBinding.h"
|
|
#include "mozilla/dom/MessagePortChild.h"
|
|
#include "mozilla/dom/MessagePortList.h"
|
|
#include "mozilla/dom/PMessagePort.h"
|
|
#include "mozilla/dom/StructuredCloneTags.h"
|
|
#include "mozilla/dom/WorkerPrivate.h"
|
|
#include "mozilla/dom/WorkerScope.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/ipc/PBackgroundChild.h"
|
|
#include "mozilla/unused.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsGlobalWindow.h"
|
|
#include "nsPresContext.h"
|
|
#include "ScriptSettings.h"
|
|
#include "SharedMessagePortMessage.h"
|
|
|
|
#include "nsIBFCacheEntry.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMFileList.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#ifdef XP_WIN
|
|
#undef PostMessage
|
|
#endif
|
|
|
|
using namespace mozilla::dom::workers;
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
class DispatchEventRunnable final : public nsICancelableRunnable
|
|
{
|
|
friend class MessagePort;
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
explicit DispatchEventRunnable(MessagePort* aPort)
|
|
: mPort(aPort)
|
|
{ }
|
|
|
|
NS_IMETHOD
|
|
Run() override
|
|
{
|
|
MOZ_ASSERT(mPort);
|
|
MOZ_ASSERT(mPort->mDispatchRunnable == this);
|
|
mPort->mDispatchRunnable = nullptr;
|
|
mPort->Dispatch();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Cancel() override
|
|
{
|
|
mPort = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~DispatchEventRunnable()
|
|
{}
|
|
|
|
RefPtr<MessagePort> mPort;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(DispatchEventRunnable, nsICancelableRunnable, nsIRunnable)
|
|
|
|
class PostMessageRunnable final : public nsICancelableRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
|
|
: mPort(aPort)
|
|
, mData(aData)
|
|
{
|
|
MOZ_ASSERT(aPort);
|
|
MOZ_ASSERT(aData);
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run() override
|
|
{
|
|
nsCOMPtr<nsIGlobalObject> globalObject;
|
|
|
|
if (NS_IsMainThread()) {
|
|
globalObject = do_QueryInterface(mPort->GetParentObject());
|
|
} else {
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
globalObject = workerPrivate->GlobalScope();
|
|
}
|
|
|
|
AutoJSAPI jsapi;
|
|
if (!globalObject || !jsapi.Init(globalObject)) {
|
|
NS_WARNING("Failed to initialize AutoJSAPI object.");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
ErrorResult rv;
|
|
JS::Rooted<JS::Value> value(cx);
|
|
|
|
mData->Read(mPort->GetParentObject(), cx, &value, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
// Create the event
|
|
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
|
|
do_QueryInterface(mPort->GetOwner());
|
|
RefPtr<MessageEvent> event =
|
|
new MessageEvent(eventTarget, nullptr, nullptr);
|
|
|
|
event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
|
false /* non-bubbling */,
|
|
false /* cancelable */, value, EmptyString(),
|
|
EmptyString(), nullptr);
|
|
event->SetTrusted(true);
|
|
event->SetSource(mPort);
|
|
|
|
nsTArray<RefPtr<MessagePort>> ports = mData->TakeTransferredPorts();
|
|
|
|
RefPtr<MessagePortList> portList =
|
|
new MessagePortList(static_cast<dom::Event*>(event.get()),
|
|
ports);
|
|
event->SetPorts(portList);
|
|
|
|
bool dummy;
|
|
mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
|
|
|
|
// We must check if we were waiting for this message in order to shutdown
|
|
// the port.
|
|
mPort->UpdateMustKeepAlive();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Cancel() override
|
|
{
|
|
mPort = nullptr;
|
|
mData = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~PostMessageRunnable()
|
|
{}
|
|
|
|
RefPtr<MessagePort> mPort;
|
|
RefPtr<SharedMessagePortMessage> mData;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
|
|
DOMEventTargetHelper)
|
|
if (tmp->mDispatchRunnable) {
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
|
|
DOMEventTargetHelper)
|
|
if (tmp->mDispatchRunnable) {
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
|
|
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
|
|
NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
|
|
|
|
namespace {
|
|
|
|
class MessagePortFeature final : public workers::WorkerFeature
|
|
{
|
|
MessagePort* mPort;
|
|
|
|
public:
|
|
explicit MessagePortFeature(MessagePort* aPort)
|
|
: mPort(aPort)
|
|
{
|
|
MOZ_ASSERT(aPort);
|
|
MOZ_COUNT_CTOR(MessagePortFeature);
|
|
}
|
|
|
|
virtual bool Notify(JSContext* aCx, workers::Status aStatus) override
|
|
{
|
|
if (aStatus > Running) {
|
|
// We cannot process messages anymore because we cannot dispatch new
|
|
// runnables. Let's force a Close().
|
|
mPort->CloseForced();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
~MessagePortFeature()
|
|
{
|
|
MOZ_COUNT_DTOR(MessagePortFeature);
|
|
}
|
|
};
|
|
|
|
class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
static void ForceClose(const MessagePortIdentifier& aIdentifier)
|
|
{
|
|
PBackgroundChild* actor =
|
|
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
|
if (actor) {
|
|
Unused << actor->SendMessagePortForceClose(aIdentifier.uuid(),
|
|
aIdentifier.destinationUuid(),
|
|
aIdentifier.sequenceId());
|
|
return;
|
|
}
|
|
|
|
RefPtr<ForceCloseHelper> helper = new ForceCloseHelper(aIdentifier);
|
|
if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) {
|
|
MOZ_CRASH();
|
|
}
|
|
}
|
|
|
|
private:
|
|
explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier)
|
|
: mIdentifier(aIdentifier)
|
|
{}
|
|
|
|
~ForceCloseHelper() {}
|
|
|
|
void ActorFailed() override
|
|
{
|
|
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
|
}
|
|
|
|
void ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override
|
|
{
|
|
ForceClose(mIdentifier);
|
|
}
|
|
|
|
const MessagePortIdentifier mIdentifier;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
|
|
|
|
} // namespace
|
|
|
|
MessagePort::MessagePort(nsISupports* aSupports)
|
|
: mInnerID(0)
|
|
, mMessageQueueEnabled(false)
|
|
, mIsKeptAlive(false)
|
|
{
|
|
mIdentifier = new MessagePortIdentifier();
|
|
mIdentifier->neutered() = true;
|
|
mIdentifier->sequenceId() = 0;
|
|
|
|
nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aSupports);
|
|
if (NS_WARN_IF(!globalObject)) {
|
|
return;
|
|
}
|
|
BindToOwner(globalObject);
|
|
}
|
|
|
|
MessagePort::~MessagePort()
|
|
{
|
|
CloseForced();
|
|
MOZ_ASSERT(!mWorkerFeature);
|
|
}
|
|
|
|
/* static */ already_AddRefed<MessagePort>
|
|
MessagePort::Create(nsISupports* aSupport, const nsID& aUUID,
|
|
const nsID& aDestinationUUID, ErrorResult& aRv)
|
|
{
|
|
RefPtr<MessagePort> mp = new MessagePort(aSupport);
|
|
mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
|
|
false /* Neutered */, eStateUnshippedEntangled, aRv);
|
|
return mp.forget();
|
|
}
|
|
|
|
/* static */ already_AddRefed<MessagePort>
|
|
MessagePort::Create(nsISupports* aSupport,
|
|
const MessagePortIdentifier& aIdentifier,
|
|
ErrorResult& aRv)
|
|
{
|
|
RefPtr<MessagePort> mp = new MessagePort(aSupport);
|
|
mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
|
|
aIdentifier.sequenceId(), aIdentifier.neutered(),
|
|
eStateEntangling, aRv);
|
|
return mp.forget();
|
|
}
|
|
|
|
void
|
|
MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
|
|
{
|
|
MOZ_ASSERT(aEntangledPort);
|
|
MOZ_ASSERT(!mUnshippedEntangledPort);
|
|
|
|
mUnshippedEntangledPort = aEntangledPort;
|
|
}
|
|
|
|
void
|
|
MessagePort::Initialize(const nsID& aUUID,
|
|
const nsID& aDestinationUUID,
|
|
uint32_t aSequenceID, bool mNeutered,
|
|
State aState, ErrorResult& aRv)
|
|
{
|
|
MOZ_ASSERT(mIdentifier);
|
|
mIdentifier->uuid() = aUUID;
|
|
mIdentifier->destinationUuid() = aDestinationUUID;
|
|
mIdentifier->sequenceId() = aSequenceID;
|
|
|
|
mState = aState;
|
|
|
|
if (mNeutered) {
|
|
// If this port is neutered we don't want to keep it alive artificially nor
|
|
// we want to add listeners or workerFeatures.
|
|
mState = eStateDisentangled;
|
|
return;
|
|
}
|
|
|
|
if (mState == eStateEntangling) {
|
|
ConnectToPBackground();
|
|
} else {
|
|
MOZ_ASSERT(mState == eStateUnshippedEntangled);
|
|
}
|
|
|
|
// The port has to keep itself alive until it's entangled.
|
|
UpdateMustKeepAlive();
|
|
|
|
if (!NS_IsMainThread()) {
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
MOZ_ASSERT(!mWorkerFeature);
|
|
|
|
nsAutoPtr<WorkerFeature> feature(new MessagePortFeature(this));
|
|
JSContext* cx = workerPrivate->GetJSContext();
|
|
if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
mWorkerFeature = Move(feature);
|
|
} else if (GetOwner()) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(GetOwner()->IsInnerWindow());
|
|
mInnerID = GetOwner()->WindowID();
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->AddObserver(this, "inner-window-destroyed", false);
|
|
}
|
|
}
|
|
}
|
|
|
|
JSObject*
|
|
MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return MessagePortBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
void
|
|
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
|
const Optional<Sequence<JS::Value>>& aTransferable,
|
|
ErrorResult& aRv)
|
|
{
|
|
// We *must* clone the data here, or the JS::Value could be modified
|
|
// by script
|
|
|
|
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
|
if (aTransferable.WasPassed()) {
|
|
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
|
|
|
|
// Here we want to check if the transerable object list contains
|
|
// this port. No other checks are done.
|
|
for (const JS::Value& value : realTransferable) {
|
|
if (!value.isObject()) {
|
|
continue;
|
|
}
|
|
|
|
MessagePort* port = nullptr;
|
|
nsresult rv = UNWRAP_OBJECT(MessagePort, &value.toObject(), port);
|
|
if (NS_FAILED(rv)) {
|
|
continue;
|
|
}
|
|
|
|
if (port == this) {
|
|
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// The input sequence only comes from the generated bindings code, which
|
|
// ensures it is rooted.
|
|
JS::HandleValueArray elements =
|
|
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
|
|
realTransferable.Elements());
|
|
|
|
JSObject* array =
|
|
JS_NewArrayObject(aCx, elements);
|
|
if (!array) {
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
|
|
transferable.setObject(*array);
|
|
}
|
|
|
|
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
|
|
|
|
data->Write(aCx, aMessage, transferable, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
// This message has to be ignored.
|
|
if (mState > eStateEntangled) {
|
|
return;
|
|
}
|
|
|
|
// If we are unshipped we are connected to the other port on the same thread.
|
|
if (mState == eStateUnshippedEntangled) {
|
|
MOZ_ASSERT(mUnshippedEntangledPort);
|
|
mUnshippedEntangledPort->mMessages.AppendElement(data);
|
|
mUnshippedEntangledPort->Dispatch();
|
|
return;
|
|
}
|
|
|
|
// Not entangled yet, but already closed/disentangled.
|
|
if (mState == eStateEntanglingForDisentangle ||
|
|
mState == eStateEntanglingForClose) {
|
|
return;
|
|
}
|
|
|
|
RemoveDocFromBFCache();
|
|
|
|
// Not entangled yet.
|
|
if (mState == eStateEntangling) {
|
|
mMessagesForTheOtherPort.AppendElement(data);
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(mActor);
|
|
MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
|
|
|
|
AutoTArray<RefPtr<SharedMessagePortMessage>, 1> array;
|
|
array.AppendElement(data);
|
|
|
|
AutoTArray<MessagePortMessage, 1> messages;
|
|
SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
|
|
mActor->SendPostMessages(messages);
|
|
}
|
|
|
|
void
|
|
MessagePort::Start()
|
|
{
|
|
if (mMessageQueueEnabled) {
|
|
return;
|
|
}
|
|
|
|
mMessageQueueEnabled = true;
|
|
Dispatch();
|
|
}
|
|
|
|
void
|
|
MessagePort::Dispatch()
|
|
{
|
|
if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable) {
|
|
return;
|
|
}
|
|
|
|
switch (mState) {
|
|
case eStateUnshippedEntangled:
|
|
// Everything is fine here. We have messages because the other
|
|
// port populates our queue directly.
|
|
break;
|
|
|
|
case eStateEntangling:
|
|
// Everything is fine here as well. We have messages because the other
|
|
// port populated our queue directly when we were in the
|
|
// eStateUnshippedEntangled state.
|
|
break;
|
|
|
|
case eStateEntanglingForDisentangle:
|
|
// Here we don't want to ship messages because these messages must be
|
|
// delivered by the cloned version of this one. They will be sent in the
|
|
// SendDisentangle().
|
|
return;
|
|
|
|
case eStateEntanglingForClose:
|
|
// We still want to deliver messages if we are closing. These messages
|
|
// are here from the previous eStateUnshippedEntangled state.
|
|
break;
|
|
|
|
case eStateEntangled:
|
|
// This port is up and running.
|
|
break;
|
|
|
|
case eStateDisentangling:
|
|
// If we are in the process to disentangle the port, we cannot dispatch
|
|
// messages. They will be sent to the cloned version of this port via
|
|
// SendDisentangle();
|
|
return;
|
|
|
|
case eStateDisentangled:
|
|
MOZ_CRASH("This cannot happen.");
|
|
// It cannot happen because Disentangle should take off all the pending
|
|
// messages.
|
|
break;
|
|
|
|
case eStateDisentangledForClose:
|
|
// If we are here is because the port has been closed. We can still
|
|
// process the pending messages.
|
|
break;
|
|
}
|
|
|
|
RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
|
|
mMessages.RemoveElementAt(0);
|
|
|
|
RefPtr<PostMessageRunnable> runnable = new PostMessageRunnable(this, data);
|
|
|
|
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
|
|
|
|
mDispatchRunnable = new DispatchEventRunnable(this);
|
|
|
|
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDispatchRunnable)));
|
|
}
|
|
|
|
void
|
|
MessagePort::Close()
|
|
{
|
|
CloseInternal(true /* aSoftly */);
|
|
}
|
|
|
|
void
|
|
MessagePort::CloseForced()
|
|
{
|
|
CloseInternal(false /* aSoftly */);
|
|
}
|
|
|
|
void
|
|
MessagePort::CloseInternal(bool aSoftly)
|
|
{
|
|
// If we have some messages to send but we don't want a 'soft' close, we have
|
|
// to flush them now.
|
|
if (!aSoftly) {
|
|
mMessages.Clear();
|
|
}
|
|
|
|
if (mState == eStateUnshippedEntangled) {
|
|
MOZ_ASSERT(mUnshippedEntangledPort);
|
|
|
|
// This avoids loops.
|
|
RefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
|
|
MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
|
|
|
|
mState = eStateDisentangledForClose;
|
|
port->CloseInternal(aSoftly);
|
|
|
|
UpdateMustKeepAlive();
|
|
return;
|
|
}
|
|
|
|
// Not entangled yet, we have to wait.
|
|
if (mState == eStateEntangling) {
|
|
mState = eStateEntanglingForClose;
|
|
return;
|
|
}
|
|
|
|
// Not entangled but already cloned or closed
|
|
if (mState == eStateEntanglingForDisentangle ||
|
|
mState == eStateEntanglingForClose) {
|
|
return;
|
|
}
|
|
|
|
// Maybe we were already closing the port but softly. In this case we call
|
|
// UpdateMustKeepAlive() to consider the empty pending message queue.
|
|
if (mState == eStateDisentangledForClose && !aSoftly) {
|
|
UpdateMustKeepAlive();
|
|
return;
|
|
}
|
|
|
|
if (mState > eStateEntangled) {
|
|
return;
|
|
}
|
|
|
|
// We don't care about stopping the sending of messages because from now all
|
|
// the incoming messages will be ignored.
|
|
mState = eStateDisentangledForClose;
|
|
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mActor->SendClose();
|
|
mActor->SetPort(nullptr);
|
|
mActor = nullptr;
|
|
|
|
UpdateMustKeepAlive();
|
|
}
|
|
|
|
EventHandlerNonNull*
|
|
MessagePort::GetOnmessage()
|
|
{
|
|
if (NS_IsMainThread()) {
|
|
return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
|
|
}
|
|
return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
|
|
}
|
|
|
|
void
|
|
MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
|
|
{
|
|
if (NS_IsMainThread()) {
|
|
SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
|
|
} else {
|
|
SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
|
|
}
|
|
|
|
// When using onmessage, the call to start() is implied.
|
|
Start();
|
|
}
|
|
|
|
// This method is called when the PMessagePortChild actor is entangled to
|
|
// another actor. It receives a list of messages to be dispatch. It can be that
|
|
// we were waiting for this entangling step in order to disentangle the port or
|
|
// to close it.
|
|
void
|
|
MessagePort::Entangled(nsTArray<MessagePortMessage>& aMessages)
|
|
{
|
|
MOZ_ASSERT(mState == eStateEntangling ||
|
|
mState == eStateEntanglingForDisentangle ||
|
|
mState == eStateEntanglingForClose);
|
|
|
|
State oldState = mState;
|
|
mState = eStateEntangled;
|
|
|
|
// If we have pending messages, these have to be sent.
|
|
if (!mMessagesForTheOtherPort.IsEmpty()) {
|
|
nsTArray<MessagePortMessage> messages;
|
|
SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
|
|
mMessagesForTheOtherPort,
|
|
messages);
|
|
mMessagesForTheOtherPort.Clear();
|
|
mActor->SendPostMessages(messages);
|
|
}
|
|
|
|
// We must convert the messages into SharedMessagePortMessages to avoid leaks.
|
|
FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
|
|
if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
|
|
data))) {
|
|
// OOM, we cannot continue.
|
|
return;
|
|
}
|
|
|
|
// If the next step is to close the port, we do it ignoring the received
|
|
// messages.
|
|
if (oldState == eStateEntanglingForClose) {
|
|
CloseForced();
|
|
return;
|
|
}
|
|
|
|
mMessages.AppendElements(data);
|
|
|
|
// We were waiting for the entangling callback in order to disentangle this
|
|
// port immediately after.
|
|
if (oldState == eStateEntanglingForDisentangle) {
|
|
StartDisentangling();
|
|
return;
|
|
}
|
|
|
|
Dispatch();
|
|
}
|
|
|
|
void
|
|
MessagePort::StartDisentangling()
|
|
{
|
|
MOZ_ASSERT(mActor);
|
|
MOZ_ASSERT(mState == eStateEntangled);
|
|
|
|
mState = eStateDisentangling;
|
|
|
|
// Sending this message we communicate to the parent actor that we don't want
|
|
// to receive any new messages. It is possible that a message has been
|
|
// already sent but not received yet. So we have to collect all of them and
|
|
// we send them in the SendDispatch() request.
|
|
mActor->SendStopSendingData();
|
|
}
|
|
|
|
void
|
|
MessagePort::MessagesReceived(nsTArray<MessagePortMessage>& aMessages)
|
|
{
|
|
MOZ_ASSERT(mState == eStateEntangled ||
|
|
mState == eStateDisentangling ||
|
|
// This last step can happen only if Close() has been called
|
|
// manually. At this point SendClose() is sent but we can still
|
|
// receive something until the Closing request is processed.
|
|
mState == eStateDisentangledForClose);
|
|
MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
|
|
|
|
RemoveDocFromBFCache();
|
|
|
|
FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
|
|
if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
|
|
data))) {
|
|
// OOM, We cannot continue.
|
|
return;
|
|
}
|
|
|
|
mMessages.AppendElements(data);
|
|
|
|
if (mState == eStateEntangled) {
|
|
Dispatch();
|
|
}
|
|
}
|
|
|
|
void
|
|
MessagePort::StopSendingDataConfirmed()
|
|
{
|
|
MOZ_ASSERT(mState == eStateDisentangling);
|
|
MOZ_ASSERT(mActor);
|
|
|
|
Disentangle();
|
|
}
|
|
|
|
void
|
|
MessagePort::Disentangle()
|
|
{
|
|
MOZ_ASSERT(mState == eStateDisentangling);
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mState = eStateDisentangled;
|
|
|
|
nsTArray<MessagePortMessage> messages;
|
|
SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages,
|
|
messages);
|
|
mMessages.Clear();
|
|
mActor->SendDisentangle(messages);
|
|
|
|
mActor->SetPort(nullptr);
|
|
mActor = nullptr;
|
|
|
|
UpdateMustKeepAlive();
|
|
}
|
|
|
|
void
|
|
MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
|
|
{
|
|
MOZ_ASSERT(mIdentifier);
|
|
|
|
// We can clone a port that has already been transfered. In this case, on the
|
|
// otherside will have a neutered port. Here we set neutered to true so that
|
|
// we are safe in case a early return.
|
|
aIdentifier.neutered() = true;
|
|
|
|
if (mState > eStateEntangled) {
|
|
return;
|
|
}
|
|
|
|
// We already have a 'next step'. We have to consider this port as already
|
|
// cloned/closed/disentangled.
|
|
if (mState == eStateEntanglingForDisentangle ||
|
|
mState == eStateEntanglingForClose) {
|
|
return;
|
|
}
|
|
|
|
aIdentifier.uuid() = mIdentifier->uuid();
|
|
aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
|
|
aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
|
|
aIdentifier.neutered() = false;
|
|
|
|
// We have to entangle first.
|
|
if (mState == eStateUnshippedEntangled) {
|
|
MOZ_ASSERT(mUnshippedEntangledPort);
|
|
MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
|
|
|
|
// Disconnect the entangled port and connect it to PBackground.
|
|
mUnshippedEntangledPort->ConnectToPBackground();
|
|
mUnshippedEntangledPort = nullptr;
|
|
|
|
// In this case, we don't need to be connected to the PBackground service.
|
|
if (mMessages.IsEmpty()) {
|
|
aIdentifier.sequenceId() = mIdentifier->sequenceId();
|
|
|
|
mState = eStateDisentangled;
|
|
UpdateMustKeepAlive();
|
|
return;
|
|
}
|
|
|
|
// Register this component to PBackground.
|
|
ConnectToPBackground();
|
|
|
|
mState = eStateEntanglingForDisentangle;
|
|
return;
|
|
}
|
|
|
|
// Not entangled yet, we have to wait.
|
|
if (mState == eStateEntangling) {
|
|
mState = eStateEntanglingForDisentangle;
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eStateEntangled);
|
|
StartDisentangling();
|
|
}
|
|
|
|
void
|
|
MessagePort::Closed()
|
|
{
|
|
if (mState >= eStateDisentangled) {
|
|
return;
|
|
}
|
|
|
|
mState = eStateDisentangledForClose;
|
|
|
|
if (mActor) {
|
|
mActor->SetPort(nullptr);
|
|
mActor = nullptr;
|
|
}
|
|
|
|
UpdateMustKeepAlive();
|
|
}
|
|
|
|
void
|
|
MessagePort::ConnectToPBackground()
|
|
{
|
|
mState = eStateEntangling;
|
|
|
|
PBackgroundChild* actor =
|
|
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
|
if (actor) {
|
|
ActorCreated(actor);
|
|
} else {
|
|
if (NS_WARN_IF(
|
|
!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
|
|
MOZ_CRASH();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MessagePort::ActorFailed()
|
|
{
|
|
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
|
}
|
|
|
|
void
|
|
MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
|
|
{
|
|
MOZ_ASSERT(aActor);
|
|
MOZ_ASSERT(!mActor);
|
|
MOZ_ASSERT(mIdentifier);
|
|
MOZ_ASSERT(mState == eStateEntangling ||
|
|
mState == eStateEntanglingForDisentangle ||
|
|
mState == eStateEntanglingForClose);
|
|
|
|
PMessagePortChild* actor =
|
|
aActor->SendPMessagePortConstructor(mIdentifier->uuid(),
|
|
mIdentifier->destinationUuid(),
|
|
mIdentifier->sequenceId());
|
|
|
|
mActor = static_cast<MessagePortChild*>(actor);
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mActor->SetPort(this);
|
|
}
|
|
|
|
void
|
|
MessagePort::UpdateMustKeepAlive()
|
|
{
|
|
if (mState >= eStateDisentangled &&
|
|
mMessages.IsEmpty() &&
|
|
mIsKeptAlive) {
|
|
mIsKeptAlive = false;
|
|
|
|
if (mWorkerFeature) {
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
workerPrivate->RemoveFeature(workerPrivate->GetJSContext(),
|
|
mWorkerFeature);
|
|
mWorkerFeature = nullptr;
|
|
}
|
|
|
|
if (NS_IsMainThread()) {
|
|
nsCOMPtr<nsIObserverService> obs =
|
|
do_GetService("@mozilla.org/observer-service;1");
|
|
if (obs) {
|
|
obs->RemoveObserver(this, "inner-window-destroyed");
|
|
}
|
|
}
|
|
|
|
Release();
|
|
return;
|
|
}
|
|
|
|
if (mState < eStateDisentangled && !mIsKeptAlive) {
|
|
mIsKeptAlive = true;
|
|
AddRef();
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
MessagePort::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (strcmp(aTopic, "inner-window-destroyed")) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// If the window id destroyed we have to release the reference that we are
|
|
// keeping.
|
|
if (!mIsKeptAlive) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
|
|
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
|
|
|
|
uint64_t innerID;
|
|
nsresult rv = wrapper->GetData(&innerID);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (innerID == mInnerID) {
|
|
CloseForced();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
MessagePort::RemoveDocFromBFCache()
|
|
{
|
|
if (!NS_IsMainThread()) {
|
|
return;
|
|
}
|
|
|
|
nsPIDOMWindow* window = GetOwner();
|
|
if (!window) {
|
|
return;
|
|
}
|
|
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
|
|
if (!bfCacheEntry) {
|
|
return;
|
|
}
|
|
|
|
bfCacheEntry->RemoveFromBFCacheSync();
|
|
}
|
|
|
|
/* static */ void
|
|
MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
|
|
{
|
|
ForceCloseHelper::ForceClose(aIdentifier);
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|