diff options
Diffstat (limited to 'toolkit/components/extensions/webrequest/StreamFilterChild.cpp')
-rw-r--r-- | toolkit/components/extensions/webrequest/StreamFilterChild.cpp | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webrequest/StreamFilterChild.cpp b/toolkit/components/extensions/webrequest/StreamFilterChild.cpp new file mode 100644 index 0000000000..955460f834 --- /dev/null +++ b/toolkit/components/extensions/webrequest/StreamFilterChild.cpp @@ -0,0 +1,516 @@ +/* -*- 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 |