diff options
Diffstat (limited to 'toolkit/components/extensions/webrequest/StreamFilter.cpp')
-rw-r--r-- | toolkit/components/extensions/webrequest/StreamFilter.cpp | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webrequest/StreamFilter.cpp b/toolkit/components/extensions/webrequest/StreamFilter.cpp new file mode 100644 index 0000000000..abfdabb3d5 --- /dev/null +++ b/toolkit/components/extensions/webrequest/StreamFilter.cpp @@ -0,0 +1,267 @@ +/* -*- 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 "StreamFilter.h" + +#include "jsapi.h" +#include "jsfriendapi.h" +#include "xpcpublic.h" + +#include "mozilla/AbstractThread.h" +#include "mozilla/extensions/StreamFilterChild.h" +#include "mozilla/extensions/StreamFilterEvents.h" +#include "mozilla/extensions/StreamFilterParent.h" +#include "mozilla/dom/AutoEntryScript.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/RootedDictionary.h" +#include "mozilla/ipc/Endpoint.h" +#include "nsContentUtils.h" +#include "nsCycleCollectionParticipant.h" +#include "nsLiteralString.h" +#include "nsThreadUtils.h" +#include "nsTArray.h" + +using namespace JS; +using namespace mozilla::dom; + +namespace mozilla { +namespace extensions { + +/***************************************************************************** + * Initialization + *****************************************************************************/ + +StreamFilter::StreamFilter(nsIGlobalObject* aParent, uint64_t aRequestId, + const nsAString& aAddonId) + : mParent(aParent), mChannelId(aRequestId), mAddonId(NS_Atomize(aAddonId)) { + MOZ_ASSERT(aParent); + + Connect(); +}; + +StreamFilter::~StreamFilter() { ForgetActor(); } + +void StreamFilter::ForgetActor() { + if (mActor) { + mActor->Cleanup(); + mActor->SetStreamFilter(nullptr); + } +} + +/* static */ +already_AddRefed<StreamFilter> StreamFilter::Create(GlobalObject& aGlobal, + uint64_t aRequestId, + const nsAString& aAddonId) { + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_ASSERT(global); + + RefPtr<StreamFilter> filter = new StreamFilter(global, aRequestId, aAddonId); + return filter.forget(); +} + +/***************************************************************************** + * Actor allocation + *****************************************************************************/ + +void StreamFilter::Connect() { + MOZ_ASSERT(!mActor); + + mActor = new StreamFilterChild(); + mActor->SetStreamFilter(this); + + nsAutoString addonId; + mAddonId->ToString(addonId); + + ContentChild* cc = ContentChild::GetSingleton(); + RefPtr<StreamFilter> self(this); + if (cc) { + cc->SendInitStreamFilter(mChannelId, addonId) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self](mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) { + self->FinishConnect(std::move(aEndpoint)); + }, + [self](mozilla::ipc::ResponseRejectReason&& aReason) { + self->mActor->RecvInitialized(false); + }); + } else { + StreamFilterParent::Create(nullptr, mChannelId, addonId) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self](mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) { + self->FinishConnect(std::move(aEndpoint)); + }, + [self](bool aDummy) { self->mActor->RecvInitialized(false); }); + } +} + +void StreamFilter::FinishConnect( + mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) { + if (aEndpoint.IsValid()) { + MOZ_RELEASE_ASSERT(aEndpoint.Bind(mActor)); + mActor->RecvInitialized(true); + } else { + mActor->RecvInitialized(false); + } +} + +bool StreamFilter::CheckAlive() { + // Check whether the global that owns this StreamFitler is still scriptable + // and, if not, disconnect the actor so that it can be cleaned up. + JSObject* wrapper = GetWrapperPreserveColor(); + if (!wrapper || !xpc::Scriptability::Get(wrapper).Allowed()) { + ForgetActor(); + return false; + } + return true; +} + +/***************************************************************************** + * Binding methods + *****************************************************************************/ + +void StreamFilter::Write(const ArrayBufferOrUint8Array& aData, + ErrorResult& aRv) { + if (!mActor) { + aRv.Throw(NS_ERROR_NOT_INITIALIZED); + return; + } + + nsTArray<uint8_t> data; + if (!AppendTypedArrayDataTo(aData, data)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + mActor->Write(std::move(data), aRv); +} + +StreamFilterStatus StreamFilter::Status() const { + if (!mActor) { + return StreamFilterStatus::Uninitialized; + } + return mActor->Status(); +} + +void StreamFilter::Suspend(ErrorResult& aRv) { + if (mActor) { + mActor->Suspend(aRv); + } else { + aRv.Throw(NS_ERROR_NOT_INITIALIZED); + } +} + +void StreamFilter::Resume(ErrorResult& aRv) { + if (mActor) { + mActor->Resume(aRv); + } else { + aRv.Throw(NS_ERROR_NOT_INITIALIZED); + } +} + +void StreamFilter::Disconnect(ErrorResult& aRv) { + if (mActor) { + mActor->Disconnect(aRv); + } else { + aRv.Throw(NS_ERROR_NOT_INITIALIZED); + } +} + +void StreamFilter::Close(ErrorResult& aRv) { + if (mActor) { + mActor->Close(aRv); + } else { + aRv.Throw(NS_ERROR_NOT_INITIALIZED); + } +} + +/***************************************************************************** + * Event emitters + *****************************************************************************/ + +void StreamFilter::FireEvent(const nsAString& aType) { + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + RefPtr<Event> event = Event::Constructor(this, aType, init); + event->SetTrusted(true); + + DispatchEvent(*event); +} + +void StreamFilter::FireDataEvent(const nsTArray<uint8_t>& aData) { + AutoEntryScript aes(mParent, "StreamFilter data event"); + JSContext* cx = aes.cx(); + + RootedDictionary<StreamFilterDataEventInit> init(cx); + init.mBubbles = false; + init.mCancelable = false; + + ErrorResult error; + auto buffer = ArrayBuffer::Create(cx, aData, error); + if (error.Failed()) { + // TODO: There is no way to recover from this. This chunk of data is lost. + error.SuppressException(); + FireErrorEvent(u"Out of memory"_ns); + return; + } + + init.mData.Init(buffer); + + RefPtr<StreamFilterDataEvent> event = + StreamFilterDataEvent::Constructor(this, u"data"_ns, init); + event->SetTrusted(true); + + DispatchEvent(*event); +} + +void StreamFilter::FireErrorEvent(const nsAString& aError) { + MOZ_ASSERT(mError.IsEmpty()); + + mError = aError; + FireEvent(u"error"_ns); +} + +/***************************************************************************** + * Glue + *****************************************************************************/ + +/* static */ +bool StreamFilter::IsAllowedInContext(JSContext* aCx, JSObject* /* unused */) { + return nsContentUtils::CallerHasPermission(aCx, + nsGkAtoms::webRequestBlocking); +} + +JSObject* StreamFilter::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return StreamFilter_Binding::Wrap(aCx, this, aGivenProto); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(StreamFilter) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StreamFilter) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(StreamFilter, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(StreamFilter, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(StreamFilter, + DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_ADDREF_INHERITED(StreamFilter, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(StreamFilter, DOMEventTargetHelper) + +} // namespace extensions +} // namespace mozilla |