--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -796,16 +796,25 @@ DOMInterfaces = {
'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
},
'SharedWorkerGlobalScope': {
'headerFile': 'mozilla/dom/WorkerScope.h',
'implicitJSContext': [ 'close' ],
},
+'StreamFilter': {
+ 'nativeType': 'mozilla::extensions::StreamFilter',
+},
+
+'StreamFilterDataEvent': {
+ 'nativeType': 'mozilla::extensions::StreamFilterDataEvent',
+ 'headerFile': 'mozilla/extensions/StreamFilterEvents.h',
+},
+
'StructuredCloneHolder': {
'nativeType': 'mozilla::dom::StructuredCloneBlob',
'wrapperCache': False,
},
'StyleSheet': {
'nativeType': 'mozilla::StyleSheet',
'headerFile': 'mozilla/StyleSheetInlines.h',
new file mode 100644
--- /dev/null
+++ b/dom/webidl/StreamFilter.webidl
@@ -0,0 +1,144 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+/**
+ * This is a Mozilla-specific WebExtension API, which is not available to web
+ * content. It allows monitoring and filtering of HTTP response stream data.
+ *
+ * This API should currently be considered experimental, and is not defined by
+ * any standard.
+ */
+
+enum StreamFilterStatus {
+ /**
+ * The StreamFilter is not fully initialized. No methods may be called until
+ * a "start" event has been received.
+ */
+ "uninitialized",
+ /**
+ * The underlying channel is currently transferring data, which will be
+ * dispatched via "data" events.
+ */
+ "transferringdata",
+ /**
+ * The underlying channel has finished transferring data. Data may still be
+ * written via write() calls at this point.
+ */
+ "finishedtransferringdata",
+ /**
+ * Data transfer is currently suspended. It may be resumed by a call to
+ * resume(). Data may still be written via write() calls in this state.
+ */
+ "suspended",
+ /**
+ * The channel has been closed by a call to close(). No further data wlil be
+ * delivered via "data" events, and no further data may be written via
+ * write() calls.
+ */
+ "closed",
+ /**
+ * The channel has been disconnected by a call to disconnect(). All further
+ * data will be delivered directly, without passing through the filter. No
+ * further events will be dispatched, and no further data may be written by
+ * write() calls.
+ */
+ "disconnected",
+ /**
+ * An error has occurred and the channel is disconnected. The `error`
+ * property contains the details of the error.
+ */
+ "failed",
+};
+
+/**
+ * An interface which allows an extension to intercept, and optionally modify,
+ * response data from an HTTP request.
+ */
+[Exposed=(Window,System),
+ Func="mozilla::extensions::StreamFilter::IsAllowedInContext"]
+interface StreamFilter : EventTarget {
+ /**
+ * Creates a stream filter for the given add-on and the given extension ID.
+ */
+ [ChromeOnly]
+ static StreamFilter create(unsigned long long requestId, DOMString addonId);
+
+ /**
+ * Suspends processing of the request. After this is called, no further data
+ * will be delivered until the request is resumed.
+ */
+ [Throws]
+ void suspend();
+
+ /**
+ * Resumes delivery of data for a suspended request.
+ */
+ [Throws]
+ void resume();
+
+ /**
+ * Closes the request. After this is called, no more data may be written to
+ * the stream, and no further data will be delivered.
+ *
+ * This *must* be called after the consumer is finished writing data, unless
+ * disconnect() has already been called.
+ */
+ [Throws]
+ void close();
+
+ /**
+ * Disconnects the stream filter from the request. After this is called, no
+ * further data will be delivered to the filter, and any unprocessed data
+ * will be written directly to the output stream.
+ */
+ [Throws]
+ void disconnect();
+
+ /**
+ * Writes a chunk of data to the output stream. This may not be called
+ * before the "start" event has been received.
+ */
+ [Throws]
+ void write((ArrayBuffer or Uint8Array) data);
+
+ /**
+ * Returns the current status of the stream.
+ */
+ [Pure]
+ readonly attribute StreamFilterStatus status;
+
+ /**
+ * After an "error" event has been dispatched, this contains a message
+ * describing the error.
+ */
+ [Pure]
+ readonly attribute DOMString error;
+
+ /**
+ * Dispatched with a StreamFilterDataEvent whenever incoming data is
+ * available on the stream. This data will not be delivered to the output
+ * stream unless it is explicitly written via a write() call.
+ */
+ attribute EventHandler ondata;
+
+ /**
+ * Dispatched when the stream is opened, and is about to begin delivering
+ * data.
+ */
+ attribute EventHandler onstart;
+
+ /**
+ * Dispatched when the stream has closed, and has no more data to deliver.
+ * The output stream remains open and writable until close() is called.
+ */
+ attribute EventHandler onstop;
+
+ /**
+ * Dispatched when an error has occurred. No further data may be read or
+ * written after this point.
+ */
+ attribute EventHandler onerror;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/StreamFilterDataEvent.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * This is a Mozilla-specific WebExtension API, which is not available to web
+ * content. It allows monitoring and filtering of HTTP response stream data.
+ *
+ * This API should currently be considered experimental, and is not defined by
+ * any standard.
+ */
+
+[Constructor(DOMString type, optional StreamFilterDataEventInit eventInitDict),
+ Func="mozilla::extensions::StreamFilter::IsAllowedInContext",
+ Exposed=(Window,System)]
+interface StreamFilterDataEvent : Event {
+ /**
+ * Contains a chunk of data read from the input stream.
+ */
+ [Pure]
+ readonly attribute ArrayBuffer data;
+};
+
+dictionary StreamFilterDataEventInit : EventInit {
+ required ArrayBuffer data;
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -293,16 +293,19 @@ with Files("SocketCommon.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("SourceBuffer*"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("StereoPannerNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
+with Files("StreamFilter*"):
+ BUG_COMPONENT = ("Toolkit", "WebExtensions: Request Handling")
+
with Files("Style*"):
BUG_COMPONENT = ("Core", "DOM: CSS Object Model")
with Files("SubtleCrypto.webidl"):
BUG_COMPONENT = ("Core", "DOM: Security")
with Files("TCP*"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
@@ -790,16 +793,18 @@ WEBIDL_FILES = [
'SocketCommon.webidl',
'SourceBuffer.webidl',
'SourceBufferList.webidl',
'StereoPannerNode.webidl',
'Storage.webidl',
'StorageEvent.webidl',
'StorageManager.webidl',
'StorageType.webidl',
+ 'StreamFilter.webidl',
+ 'StreamFilterDataEvent.webidl',
'StructuredCloneHolder.webidl',
'StyleSheet.webidl',
'StyleSheetList.webidl',
'SubtleCrypto.webidl',
'SVGAElement.webidl',
'SVGAngle.webidl',
'SVGAnimatedAngle.webidl',
'SVGAnimatedBoolean.webidl',
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/StreamFilter.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/HoldDropJSObjects.h"
+#include "mozilla/SystemGroup.h"
+#include "mozilla/extensions/StreamFilterChild.h"
+#include "mozilla/extensions/StreamFilterEvents.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsLiteralString.h"
+#include "nsThreadUtils.h"
+#include "nsTArray.h"
+
+using namespace JS;
+using namespace mozilla::dom;
+
+using mozilla::ipc::BackgroundChild;
+using mozilla::ipc::PBackgroundChild;
+
+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);
+
+ mozilla::HoldJSObjects(this);
+
+ ConnectToPBackground();
+};
+
+StreamFilter::~StreamFilter()
+{
+ mozilla::DropJSObjects(this);
+
+ 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::ConnectToPBackground()
+{
+ PBackgroundChild* background = BackgroundChild::GetForCurrentThread();
+ if (background) {
+ ActorCreated(background);
+ } else {
+ bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
+ MOZ_RELEASE_ASSERT(ok);
+ }
+}
+
+void
+StreamFilter::ActorFailed()
+{
+ MOZ_CRASH("Failed to create a PBackgroundChild actor");
+}
+
+void
+StreamFilter::ActorCreated(PBackgroundChild* aBackground)
+{
+ MOZ_ASSERT(aBackground);
+ MOZ_ASSERT(!mActor);
+
+ nsAutoString addonId;
+ mAddonId->ToString(addonId);
+
+ PStreamFilterChild* actor = aBackground->SendPStreamFilterConstructor(mChannelId, addonId);
+ MOZ_ASSERT(actor);
+
+ mActor = static_cast<StreamFilterChild*>(actor);
+ mActor->SetStreamFilter(this);
+}
+
+/*****************************************************************************
+ * Binding methods
+ *****************************************************************************/
+
+template <typename T>
+static inline bool
+ReadTypedArrayData(nsTArray<uint8_t>& aData, const T& aArray, ErrorResult& aRv)
+{
+ aArray.ComputeLengthAndData();
+ if (!aData.SetLength(aArray.Length(), fallible)) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return false;
+ }
+ memcpy(aData.Elements(), aArray.Data(), aArray.Length());
+ return true;
+}
+
+void
+StreamFilter::Write(const ArrayBufferOrUint8Array& aData, ErrorResult& aRv)
+{
+ if (!mActor) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED);
+ }
+
+ nsTArray<uint8_t> data;
+
+ bool ok;
+ if (aData.IsArrayBuffer()) {
+ ok = ReadTypedArrayData(data, aData.GetAsArrayBuffer(), aRv);
+ } else if (aData.IsUint8Array()) {
+ ok = ReadTypedArrayData(data, aData.GetAsUint8Array(), aRv);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Argument should be ArrayBuffer or Uint8Array");
+ return;
+ }
+
+ if (ok) {
+ mActor->Write(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);
+
+ bool defaultPrevented;
+ DispatchEvent(event, &defaultPrevented);
+}
+
+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;
+
+ auto buffer = ArrayBuffer::Create(cx, this, aData.Length(), aData.Elements());
+ if (!buffer) {
+ // TODO: There is no way to recover from this. This chunk of data is lost.
+ FireErrorEvent(NS_LITERAL_STRING("Out of memory"));
+ return;
+ }
+
+ init.mData.Init(buffer);
+
+ RefPtr<StreamFilterDataEvent> event =
+ StreamFilterDataEvent::Constructor(this, NS_LITERAL_STRING("data"), init);
+ event->SetTrusted(true);
+
+ bool defaultPrevented;
+ DispatchEvent(event, &defaultPrevented);
+}
+
+void
+StreamFilter::FireErrorEvent(const nsAString& aError)
+{
+ MOZ_ASSERT(mError.IsEmpty());
+
+ mError = aError;
+ FireEvent(NS_LITERAL_STRING("error"));
+}
+
+/*****************************************************************************
+ * Glue
+ *****************************************************************************/
+
+/* static */ bool
+StreamFilter::IsAllowedInContext(JSContext* aCx, JSObject* /* unused */)
+{
+ return nsContentUtils::CallerHasPermission(aCx, NS_LITERAL_STRING("webRequestBlocking"));
+}
+
+JSObject*
+StreamFilter::WrapObject(JSContext* aCx, HandleObject aGivenProto)
+{
+ return StreamFilterBinding::Wrap(aCx, this, aGivenProto);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(StreamFilter)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(StreamFilter)
+ NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StreamFilter)
+ 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
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/StreamFilter.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef mozilla_extensions_StreamFilter_h
+#define mozilla_extensions_StreamFilter_h
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/StreamFilterBinding.h"
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIAtom.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+
+namespace mozilla {
+namespace extensions {
+
+class StreamFilterChild;
+
+using namespace mozilla::dom;
+
+class StreamFilter : public DOMEventTargetHelper
+ , public nsIIPCBackgroundChildCreateCallback
+{
+ friend class StreamFilterChild;
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(StreamFilter, DOMEventTargetHelper)
+
+ static already_AddRefed<StreamFilter>
+ Create(GlobalObject& global,
+ uint64_t aRequestId,
+ const nsAString& aAddonId);
+
+ explicit StreamFilter(nsIGlobalObject* aParent,
+ uint64_t aRequestId,
+ const nsAString& aAddonId);
+
+ IMPL_EVENT_HANDLER(start);
+ IMPL_EVENT_HANDLER(stop);
+ IMPL_EVENT_HANDLER(data);
+ IMPL_EVENT_HANDLER(error);
+
+ void Write(const ArrayBufferOrUint8Array& aData,
+ ErrorResult& aRv);
+
+ void GetError(nsAString& aError)
+ {
+ aError = mError;
+ }
+
+ StreamFilterStatus Status() const;
+ void Suspend(ErrorResult& aRv);
+ void Resume(ErrorResult& aRv);
+ void Disconnect(ErrorResult& aRv);
+ void Close(ErrorResult& aRv);
+
+ nsISupports* GetParentObject() const { return mParent; }
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ static bool
+ IsAllowedInContext(JSContext* aCx, JSObject* aObj);
+
+protected:
+ virtual ~StreamFilter();
+
+ void FireEvent(const nsAString& aType);
+
+ void FireDataEvent(const nsTArray<uint8_t>& aData);
+
+ void FireErrorEvent(const nsAString& aError);
+
+private:
+ void
+ ConnectToPBackground();
+
+ nsCOMPtr<nsIGlobalObject> mParent;
+ RefPtr<StreamFilterChild> mActor;
+
+ nsString mError;
+
+ const uint64_t mChannelId;
+ const nsCOMPtr<nsIAtom> mAddonId;
+};
+
+} // namespace extensions
+} // namespace mozilla
+
+#endif // mozilla_extensions_StreamFilter_h
--- a/toolkit/components/extensions/webrequest/StreamFilterChild.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilterChild.cpp
@@ -1,22 +1,24 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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()
@@ -219,22 +221,32 @@ StreamFilterChild::SetNextState()
break;
case State::Disconnecting:
mNextState = State::Disconnected;
SendDisconnect();
break;
case State::FinishedTransferringData:
+ mStreamFilter->FireEvent(NS_LITERAL_STRING("stop"));
+ // 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()
{
@@ -245,16 +257,22 @@ StreamFilterChild::MaybeStopRequest()
switch (mState) {
case State::Suspending:
case State::Resuming:
mNextState = State::FinishedTransferringData;
return;
default:
mState = State::FinishedTransferringData;
+ if (mStreamFilter) {
+ mStreamFilter->FireEvent(NS_LITERAL_STRING("stop"));
+ // 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
*****************************************************************************/
@@ -262,16 +280,20 @@ IPCResult
StreamFilterChild::RecvInitialized(const bool& aSuccess)
{
MOZ_ASSERT(mState == State::Uninitialized);
if (aSuccess) {
mState = State::Initialized;
} else {
mState = State::Error;
+ if (mStreamFilter) {
+ mStreamFilter->FireErrorEvent(NS_LITERAL_STRING("Invalid request ID"));
+ mStreamFilter = nullptr;
+ }
}
return IPC_OK();
}
IPCResult
StreamFilterChild::RecvClosed() {
MOZ_DIAGNOSTIC_ASSERT(mState == State::Closing);
@@ -333,27 +355,84 @@ StreamFilterChild::Write(Data&& aData, E
default:
aRv.Throw(NS_ERROR_FAILURE);
return;
}
SendWrite(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;
+ mStreamFilter->FireEvent(NS_LITERAL_STRING("start"));
return IPC_OK();
}
IPCResult
StreamFilterChild::RecvStopRequest(const nsresult& aStatus)
{
mReceivedOnStop = true;
MaybeStopRequest();
@@ -363,16 +442,17 @@ StreamFilterChild::RecvStopRequest(const
/*****************************************************************************
* Incoming request data handling
*****************************************************************************/
void
StreamFilterChild::EmitData(const Data& aData)
{
MOZ_ASSERT(CanFlushData());
+ mStreamFilter->FireDataEvent(aData);
MaybeStopRequest();
}
void
StreamFilterChild::FlushBufferedData()
{
while (!mBufferedData.isEmpty() && CanFlushData()) {
--- a/toolkit/components/extensions/webrequest/StreamFilterChild.h
+++ b/toolkit/components/extensions/webrequest/StreamFilterChild.h
@@ -4,30 +4,36 @@
* 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/. */
#ifndef mozilla_extensions_StreamFilterChild_h
#define mozilla_extensions_StreamFilterChild_h
#include "StreamFilterBase.h"
#include "mozilla/extensions/PStreamFilterChild.h"
+#include "mozilla/extensions/StreamFilter.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/LinkedList.h"
+#include "mozilla/dom/StreamFilterBinding.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace extensions {
+using mozilla::dom::StreamFilterStatus;
using mozilla::ipc::IPCResult;
class StreamFilter;
+
class StreamFilterChild final : public PStreamFilterChild
, public StreamFilterBase
{
+ friend class StreamFilter;
+
public:
NS_INLINE_DECL_REFCOUNTING(StreamFilterChild)
StreamFilterChild()
: mState(State::Uninitialized)
, mReceivedOnStop(false)
{}
@@ -79,30 +85,38 @@ public:
void Write(Data&& aData, ErrorResult& aRv);
State GetState() const
{
return mState;
}
+ StreamFilterStatus Status() const;
+
protected:
virtual IPCResult RecvInitialized(const bool& aSuccess) override;
virtual IPCResult RecvStartRequest() override;
virtual IPCResult RecvData(Data&& data) override;
virtual IPCResult RecvStopRequest(const nsresult& aStatus) override;
virtual IPCResult RecvClosed() override;
virtual IPCResult RecvSuspended() override;
virtual IPCResult RecvResumed() override;
virtual IPCResult RecvFlushData() override;
virtual IPCResult Recv__delete__() override { return IPC_OK(); }
+ void
+ SetStreamFilter(StreamFilter* aStreamFilter)
+ {
+ mStreamFilter = aStreamFilter;
+ }
+
private:
~StreamFilterChild() {}
void SetNextState();
void MaybeStopRequest();
void EmitData(const Data& aData);
@@ -117,14 +131,16 @@ private:
void FlushBufferedData();
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
State mState;
State mNextState;
bool mReceivedOnStop;
+
+ RefPtr<StreamFilter> mStreamFilter;
};
} // namespace extensions
} // namespace mozilla
#endif // mozilla_extensions_StreamFilterChild_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/StreamFilterEvents.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; 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 "mozilla/extensions/StreamFilterEvents.h"
+
+namespace mozilla {
+namespace extensions {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(StreamFilterDataEvent)
+
+NS_IMPL_ADDREF_INHERITED(StreamFilterDataEvent, Event)
+NS_IMPL_RELEASE_INHERITED(StreamFilterDataEvent, Event)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(StreamFilterDataEvent, Event)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(StreamFilterDataEvent, Event)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(StreamFilterDataEvent, Event)
+ tmp->mData = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(StreamFilterDataEvent)
+NS_INTERFACE_MAP_END_INHERITING(Event)
+
+
+/* static */ already_AddRefed<StreamFilterDataEvent>
+StreamFilterDataEvent::Constructor(EventTarget* aEventTarget,
+ const nsAString& aType,
+ const StreamFilterDataEventInit& aParam)
+{
+ RefPtr<StreamFilterDataEvent> event = new StreamFilterDataEvent(aEventTarget);
+
+ bool trusted = event->Init(aEventTarget);
+ event->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
+ event->SetTrusted(trusted);
+ event->SetComposed(aParam.mComposed);
+
+ event->SetData(aParam.mData);
+
+ return event.forget();
+}
+
+JSObject*
+StreamFilterDataEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return StreamFilterDataEventBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace extensions
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/StreamFilterEvents.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef mozilla_extensions_StreamFilterEvents_h
+#define mozilla_extensions_StreamFilterEvents_h
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/StreamFilterDataEventBinding.h"
+
+#include "jsapi.h"
+
+#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/dom/Event.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+
+namespace mozilla {
+namespace extensions {
+
+using namespace JS;
+using namespace mozilla::dom;
+
+class StreamFilterDataEvent : public Event
+{
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(StreamFilterDataEvent, Event)
+
+ explicit StreamFilterDataEvent(EventTarget* aEventTarget)
+ : Event(aEventTarget, nullptr, nullptr)
+ {
+ mozilla::HoldJSObjects(this);
+ }
+
+ static already_AddRefed<StreamFilterDataEvent>
+ Constructor(EventTarget* aEventTarget,
+ const nsAString& aType,
+ const StreamFilterDataEventInit& aParam);
+
+ static already_AddRefed<StreamFilterDataEvent>
+ Constructor(GlobalObject& aGlobal,
+ const nsAString& aType,
+ const StreamFilterDataEventInit& aParam,
+ ErrorResult& aRv)
+ {
+ nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
+ return Constructor(target, aType, aParam);
+ }
+
+ void GetData(JSContext* aCx, JS::MutableHandleObject aResult)
+ {
+ aResult.set(mData);
+ }
+
+ virtual JSObject* WrapObjectInternal(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+protected:
+ virtual ~StreamFilterDataEvent()
+ {
+ mozilla::DropJSObjects(this);
+ }
+
+private:
+ JS::Heap<JSObject*> mData;
+
+ void
+ SetData(const ArrayBuffer& aData)
+ {
+ mData = aData.Obj();
+ }
+};
+
+} // namespace extensions
+} // namespace mozilla
+
+#endif // mozilla_extensions_StreamFilterEvents_h
--- a/toolkit/components/extensions/webrequest/moz.build
+++ b/toolkit/components/extensions/webrequest/moz.build
@@ -8,17 +8,19 @@ XPIDL_SOURCES += [
'mozIWebRequestService.idl',
'nsIWebRequestListener.idl',
]
XPIDL_MODULE = 'webextensions'
UNIFIED_SOURCES += [
'nsWebRequestListener.cpp',
+ 'StreamFilter.cpp',
'StreamFilterChild.cpp',
+ 'StreamFilterEvents.cpp',
'StreamFilterParent.cpp',
'WebRequestService.cpp',
]
IPDL_SOURCES += [
'PStreamFilter.ipdl',
]
@@ -26,18 +28,20 @@ EXPORTS += [
'nsWebRequestListener.h',
]
EXPORTS.mozilla += [
'WebRequestService.h',
]
EXPORTS.mozilla.extensions += [
+ 'StreamFilter.h',
'StreamFilterBase.h',
'StreamFilterChild.h',
+ 'StreamFilterEvents.h',
'StreamFilterParent.h',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
with Files("**"):