Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "StreamFilterChild.h"
#include "StreamFilter.h"
#include "mozilla/Assertions.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace extensions {
using mozilla::dom::StreamFilterStatus;
using mozilla::ipc::IPCResult;
/*****************************************************************************
* Initialization and cleanup
*****************************************************************************/
void StreamFilterChild::Cleanup() {
switch (mState) {
case State::Closing:
case State::Closed:
case State::Error:
case State::Disconnecting:
case State::Disconnected:
break;
default:
ErrorResult rv;
Disconnect(rv);
break;
}
}
/*****************************************************************************
* State change methods
*****************************************************************************/
void StreamFilterChild::Suspend(ErrorResult& aRv) {
switch (mState) {
case State::TransferringData:
mState = State::Suspending;
mNextState = State::Suspended;
SendSuspend();
break;
case State::Suspending:
switch (mNextState) {
case State::Suspended:
case State::Resuming:
mNextState = State::Suspended;
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
break;
case State::Resuming:
switch (mNextState) {
case State::TransferringData:
case State::Suspending:
mNextState = State::Suspending;
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
break;
case State::Suspended:
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
break;
}
}
void StreamFilterChild::Resume(ErrorResult& aRv) {
switch (mState) {
case State::Suspended:
mState = State::Resuming;
mNextState = State::TransferringData;
SendResume();
break;
case State::Suspending:
switch (mNextState) {
case State::Suspended:
case State::Resuming:
mNextState = State::Resuming;
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
break;
case State::Resuming:
case State::TransferringData:
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
FlushBufferedData();
}
void StreamFilterChild::Disconnect(ErrorResult& aRv) {
switch (mState) {
case State::Suspended:
case State::TransferringData:
case State::FinishedTransferringData:
mState = State::Disconnecting;
mNextState = State::Disconnected;
WriteBufferedData();
SendDisconnect();
break;
case State::Suspending:
case State::Resuming:
switch (mNextState) {
case State::Suspended:
case State::Resuming:
case State::Disconnecting:
mNextState = State::Disconnecting;
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
break;
case State::Disconnecting:
case State::Disconnected:
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
void StreamFilterChild::Close(ErrorResult& aRv) {
switch (mState) {
case State::Suspended:
case State::TransferringData:
case State::FinishedTransferringData:
mState = State::Closing;
mNextState = State::Closed;
SendClose();
break;
case State::Suspending:
case State::Resuming:
mNextState = State::Closing;
break;
case State::Closing:
MOZ_DIAGNOSTIC_ASSERT(mNextState == State::Closed);
break;
case State::Closed:
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
mBufferedData.clear();
}
/*****************************************************************************
* Internal state management
*****************************************************************************/
void StreamFilterChild::SetNextState() {
mState = mNextState;
switch (mNextState) {
case State::Suspending:
mNextState = State::Suspended;
SendSuspend();
break;
case State::Resuming:
mNextState = State::TransferringData;
SendResume();
break;
case State::Closing:
mNextState = State::Closed;
SendClose();
break;
case State::Disconnecting:
mNextState = State::Disconnected;
SendDisconnect();
break;
case State::FinishedTransferringData:
if (mStreamFilter) {
mStreamFilter->FireEvent(u"stop"_ns);
// We don't need access to the stream filter after this point, so break
// our reference cycle, so that it can be collected if we're the last
// reference.
mStreamFilter = nullptr;
}
break;
case State::TransferringData:
FlushBufferedData();
break;
case State::Closed:
case State::Disconnected:
case State::Error:
mStreamFilter = nullptr;
break;
default:
break;
}
}
void StreamFilterChild::MaybeStopRequest() {
if (!mReceivedOnStop || !mBufferedData.isEmpty()) {
return;
}
if (mStreamFilter) {
Unused << mStreamFilter->CheckAlive();
}
switch (mState) {
case State::Suspending:
case State::Resuming:
mNextState = State::FinishedTransferringData;
return;
case State::Disconnecting:
case State::Closing:
case State::Closed:
break;
default:
mState = State::FinishedTransferringData;
if (mStreamFilter) {
mStreamFilter->FireEvent(u"stop"_ns);
// We don't need access to the stream filter after this point, so break
// our reference cycle, so that it can be collected if we're the last
// reference.
mStreamFilter = nullptr;
}
break;
}
}
/*****************************************************************************
* State change acknowledgment callbacks
*****************************************************************************/
void StreamFilterChild::RecvInitialized(bool aSuccess) {
MOZ_ASSERT(mState == State::Uninitialized);
if (aSuccess) {
mState = State::Initialized;
} else {
mState = State::Error;
if (mStreamFilter) {
mStreamFilter->FireErrorEvent(u"Invalid request ID"_ns);
mStreamFilter = nullptr;
}
}
}
IPCResult StreamFilterChild::RecvError(const nsCString& aError) {
mState = State::Error;
if (mStreamFilter) {
mStreamFilter->FireErrorEvent(NS_ConvertUTF8toUTF16(aError));
mStreamFilter = nullptr;
}
SendDestroy();
return IPC_OK();
}
IPCResult StreamFilterChild::RecvClosed() {
MOZ_DIAGNOSTIC_ASSERT(mState == State::Closing);
SetNextState();
return IPC_OK();
}
IPCResult StreamFilterChild::RecvSuspended() {
MOZ_DIAGNOSTIC_ASSERT(mState == State::Suspending);
SetNextState();
return IPC_OK();
}
IPCResult StreamFilterChild::RecvResumed() {
MOZ_DIAGNOSTIC_ASSERT(mState == State::Resuming);
SetNextState();
return IPC_OK();
}
IPCResult StreamFilterChild::RecvFlushData() {
MOZ_DIAGNOSTIC_ASSERT(mState == State::Disconnecting);
SendFlushedData();
SetNextState();
return IPC_OK();
}
/*****************************************************************************
* Other binding methods
*****************************************************************************/
void StreamFilterChild::Write(Data&& aData, ErrorResult& aRv) {
switch (mState) {
case State::Suspending:
case State::Resuming:
switch (mNextState) {
case State::Suspended:
case State::TransferringData:
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
break;
case State::Suspended:
case State::TransferringData:
case State::FinishedTransferringData:
break;
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
SendWrite(std::move(aData));
}
StreamFilterStatus StreamFilterChild::Status() const {
switch (mState) {
case State::Uninitialized:
case State::Initialized:
return StreamFilterStatus::Uninitialized;
case State::TransferringData:
return StreamFilterStatus::Transferringdata;
case State::Suspended:
return StreamFilterStatus::Suspended;
case State::FinishedTransferringData:
return StreamFilterStatus::Finishedtransferringdata;
case State::Resuming:
case State::Suspending:
switch (mNextState) {
case State::TransferringData:
case State::Resuming:
return StreamFilterStatus::Transferringdata;
case State::Suspended:
case State::Suspending:
return StreamFilterStatus::Suspended;
case State::Closing:
return StreamFilterStatus::Closed;
case State::Disconnecting:
return StreamFilterStatus::Disconnected;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected next state");
return StreamFilterStatus::Suspended;
}
break;
case State::Closing:
case State::Closed:
return StreamFilterStatus::Closed;
case State::Disconnecting:
case State::Disconnected:
return StreamFilterStatus::Disconnected;
case State::Error:
return StreamFilterStatus::Failed;
};
MOZ_ASSERT_UNREACHABLE("Not reached");
return StreamFilterStatus::Failed;
}
/*****************************************************************************
* Request state notifications
*****************************************************************************/
IPCResult StreamFilterChild::RecvStartRequest() {
MOZ_ASSERT(mState == State::Initialized);
mState = State::TransferringData;
if (mStreamFilter) {
mStreamFilter->FireEvent(u"start"_ns);
Unused << mStreamFilter->CheckAlive();
}
return IPC_OK();
}
IPCResult StreamFilterChild::RecvStopRequest(const nsresult& aStatus) {
mReceivedOnStop = true;
MaybeStopRequest();
return IPC_OK();
}
/*****************************************************************************
* Incoming request data handling
*****************************************************************************/
void StreamFilterChild::EmitData(const Data& aData) {
MOZ_ASSERT(CanFlushData());
if (mStreamFilter) {
mStreamFilter->FireDataEvent(aData);
}
MaybeStopRequest();
}
void StreamFilterChild::FlushBufferedData() {
while (!mBufferedData.isEmpty() && CanFlushData()) {
UniquePtr<BufferedData> data(mBufferedData.popFirst());
EmitData(data->mData);
}
}
void StreamFilterChild::WriteBufferedData() {
while (!mBufferedData.isEmpty()) {
UniquePtr<BufferedData> data(mBufferedData.popFirst());
SendWrite(data->mData);
}
}
IPCResult StreamFilterChild::RecvData(Data&& aData) {
MOZ_ASSERT(!mReceivedOnStop);
if (mStreamFilter) {
Unused << mStreamFilter->CheckAlive();
}
switch (mState) {
case State::TransferringData:
case State::Resuming:
EmitData(aData);
break;
case State::FinishedTransferringData:
MOZ_ASSERT_UNREACHABLE("Received data in unexpected state");
EmitData(aData);
break;
case State::Suspending:
case State::Suspended:
BufferData(std::move(aData));
break;
case State::Disconnecting:
SendWrite(std::move(aData));
break;
case State::Closing:
break;
default:
MOZ_ASSERT_UNREACHABLE("Received data in unexpected state");
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
/*****************************************************************************
* Glue
*****************************************************************************/
void StreamFilterChild::ActorDestroy(ActorDestroyReason aWhy) {
mStreamFilter = nullptr;
}
} // namespace extensions
} // namespace mozilla