diff options
Diffstat (limited to 'netwerk/protocol/wyciwyg')
-rw-r--r-- | netwerk/protocol/wyciwyg/PWyciwygChannel.ipdl | 62 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp | 764 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/WyciwygChannelChild.h | 124 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp | 373 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/WyciwygChannelParent.h | 71 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/moz.build | 36 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsIWyciwygChannel.idl | 45 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsWyciwyg.cpp | 10 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsWyciwyg.h | 43 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp | 808 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsWyciwygChannel.h | 115 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp | 158 | ||||
-rw-r--r-- | netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.h | 23 |
13 files changed, 2632 insertions, 0 deletions
diff --git a/netwerk/protocol/wyciwyg/PWyciwygChannel.ipdl b/netwerk/protocol/wyciwyg/PWyciwygChannel.ipdl new file mode 100644 index 000000000..16f5848fb --- /dev/null +++ b/netwerk/protocol/wyciwyg/PWyciwygChannel.ipdl @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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 protocol PNecko; +include protocol PBrowser; +include URIParams; +include PBackgroundSharedTypes; +include PBrowserOrId; + +using class IPC::SerializedLoadContext from "SerializedLoadContext.h"; + +namespace mozilla { +namespace net { + +//------------------------------------------------------------------- +protocol PWyciwygChannel +{ + manager PNecko; + +parent: + async __delete__(); + + async Init(URIParams uri, + PrincipalInfo requestingPrincipalInfo, + PrincipalInfo triggeringPrincipalInfo, + PrincipalInfo principalToInheritInfo, + uint32_t securityFlags, + uint32_t contentPolicyType); + async AsyncOpen(URIParams originalURI, + uint32_t loadFlags, + SerializedLoadContext loadContext, + PBrowserOrId browser); + async AppData(SerializedLoadContext loadContext, PBrowserOrId browser); + + // methods corresponding to those of nsIWyciwygChannel + async WriteToCacheEntry(nsString data); + async CloseCacheEntry(nsresult reason); + async SetCharsetAndSource(int32_t source, nsCString charset); + async SetSecurityInfo(nsCString securityInfo); + async Cancel(nsresult status); + +child: + async OnStartRequest(nsresult statusCode, + int64_t contentLength, + int32_t source, + nsCString charset, + nsCString securityInfo); + + async OnDataAvailable(nsCString data, + uint64_t offset); + + async OnStopRequest(nsresult statusCode); + + async CancelEarly(nsresult statusCode); +}; + + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp new file mode 100644 index 000000000..2a794df3b --- /dev/null +++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp @@ -0,0 +1,764 @@ +/* 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 "nsWyciwyg.h" + +#include "base/compiler_specific.h" + +#include "mozilla/net/ChannelEventQueue.h" +#include "WyciwygChannelChild.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/ContentChild.h" + +#include "nsCharsetSource.h" +#include "nsContentUtils.h" +#include "nsStringStream.h" +#include "nsNetUtil.h" +#include "nsISerializable.h" +#include "nsSerializationHelper.h" +#include "nsIProgressEventSink.h" +#include "mozilla/ipc/URIUtils.h" +#include "SerializedLoadContext.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "nsProxyRelease.h" +#include "nsContentSecurityManager.h" + +using namespace mozilla::ipc; +using namespace mozilla::dom; + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(WyciwygChannelChild, + nsIRequest, + nsIChannel, + nsIWyciwygChannel, + nsIPrivateBrowsingChannel) + + +WyciwygChannelChild::WyciwygChannelChild() + : mStatus(NS_OK) + , mIsPending(false) + , mCanceled(false) + , mLoadFlags(LOAD_NORMAL) + , mContentLength(-1) + , mCharsetSource(kCharsetUninitialized) + , mState(WCC_NEW) + , mIPCOpen(false) + , mSentAppData(false) +{ + LOG(("Creating WyciwygChannelChild @%x\n", this)); + mEventQ = new ChannelEventQueue(NS_ISUPPORTS_CAST(nsIWyciwygChannel*, this)); +} + +WyciwygChannelChild::~WyciwygChannelChild() +{ + LOG(("Destroying WyciwygChannelChild @%x\n", this)); + if (mLoadInfo) { + NS_ReleaseOnMainThread(mLoadInfo.forget()); + } +} + +void +WyciwygChannelChild::AddIPDLReference() +{ + MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference"); + mIPCOpen = true; + AddRef(); +} + +void +WyciwygChannelChild::ReleaseIPDLReference() +{ + MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference"); + mIPCOpen = false; + Release(); +} + +nsresult +WyciwygChannelChild::Init(nsIURI* uri) +{ + NS_ENSURE_ARG_POINTER(uri); + + mState = WCC_INIT; + + mURI = uri; + mOriginalURI = uri; + + URIParams serializedUri; + SerializeURI(uri, serializedUri); + + // propagate loadInfo + mozilla::ipc::PrincipalInfo requestingPrincipalInfo; + mozilla::ipc::PrincipalInfo triggeringPrincipalInfo; + mozilla::ipc::PrincipalInfo principalToInheritInfo; + uint32_t securityFlags; + uint32_t policyType; + if (mLoadInfo) { + mozilla::ipc::PrincipalToPrincipalInfo(mLoadInfo->LoadingPrincipal(), + &requestingPrincipalInfo); + mozilla::ipc::PrincipalToPrincipalInfo(mLoadInfo->TriggeringPrincipal(), + &triggeringPrincipalInfo); + mozilla::ipc::PrincipalToPrincipalInfo(mLoadInfo->PrincipalToInherit(), + &principalToInheritInfo); + securityFlags = mLoadInfo->GetSecurityFlags(); + policyType = mLoadInfo->InternalContentPolicyType(); + } + else { + // use default values if no loadInfo is provided + mozilla::ipc::PrincipalToPrincipalInfo(nsContentUtils::GetSystemPrincipal(), + &requestingPrincipalInfo); + mozilla::ipc::PrincipalToPrincipalInfo(nsContentUtils::GetSystemPrincipal(), + &triggeringPrincipalInfo); + mozilla::ipc::PrincipalToPrincipalInfo(nsContentUtils::GetSystemPrincipal(), + &principalToInheritInfo); + securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL; + policyType = nsIContentPolicy::TYPE_OTHER; + } + + SendInit(serializedUri, + requestingPrincipalInfo, + triggeringPrincipalInfo, + principalToInheritInfo, + securityFlags, + policyType); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// WyciwygChannelChild::PWyciwygChannelChild +//----------------------------------------------------------------------------- + +class WyciwygStartRequestEvent : public ChannelEvent +{ +public: + WyciwygStartRequestEvent(WyciwygChannelChild* child, + const nsresult& statusCode, + const int64_t& contentLength, + const int32_t& source, + const nsCString& charset, + const nsCString& securityInfo) + : mChild(child), mStatusCode(statusCode), mContentLength(contentLength), + mSource(source), mCharset(charset), mSecurityInfo(securityInfo) {} + void Run() { mChild->OnStartRequest(mStatusCode, mContentLength, mSource, + mCharset, mSecurityInfo); } +private: + WyciwygChannelChild* mChild; + nsresult mStatusCode; + int64_t mContentLength; + int32_t mSource; + nsCString mCharset; + nsCString mSecurityInfo; +}; + +bool +WyciwygChannelChild::RecvOnStartRequest(const nsresult& statusCode, + const int64_t& contentLength, + const int32_t& source, + const nsCString& charset, + const nsCString& securityInfo) +{ + mEventQ->RunOrEnqueue(new WyciwygStartRequestEvent(this, statusCode, + contentLength, source, + charset, securityInfo)); + return true; +} + +void +WyciwygChannelChild::OnStartRequest(const nsresult& statusCode, + const int64_t& contentLength, + const int32_t& source, + const nsCString& charset, + const nsCString& securityInfo) +{ + LOG(("WyciwygChannelChild::RecvOnStartRequest [this=%p]\n", this)); + + mState = WCC_ONSTART; + + mStatus = statusCode; + mContentLength = contentLength; + mCharsetSource = source; + mCharset = charset; + + if (!securityInfo.IsEmpty()) { + NS_DeserializeObject(securityInfo, getter_AddRefs(mSecurityInfo)); + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + + nsresult rv = mListener->OnStartRequest(this, mListenerContext); + if (NS_FAILED(rv)) + Cancel(rv); +} + +class WyciwygDataAvailableEvent : public ChannelEvent +{ +public: + WyciwygDataAvailableEvent(WyciwygChannelChild* child, + const nsCString& data, + const uint64_t& offset) + : mChild(child), mData(data), mOffset(offset) {} + void Run() { mChild->OnDataAvailable(mData, mOffset); } +private: + WyciwygChannelChild* mChild; + nsCString mData; + uint64_t mOffset; +}; + +bool +WyciwygChannelChild::RecvOnDataAvailable(const nsCString& data, + const uint64_t& offset) +{ + mEventQ->RunOrEnqueue(new WyciwygDataAvailableEvent(this, data, offset)); + return true; +} + +void +WyciwygChannelChild::OnDataAvailable(const nsCString& data, + const uint64_t& offset) +{ + LOG(("WyciwygChannelChild::RecvOnDataAvailable [this=%p]\n", this)); + + if (mCanceled) + return; + + mState = WCC_ONDATA; + + // NOTE: the OnDataAvailable contract requires the client to read all the data + // in the inputstream. This code relies on that ('data' will go away after + // this function). Apparently the previous, non-e10s behavior was to actually + // support only reading part of the data, allowing later calls to read the + // rest. + nsCOMPtr<nsIInputStream> stringStream; + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), + data.get(), + data.Length(), + NS_ASSIGNMENT_DEPEND); + if (NS_FAILED(rv)) { + Cancel(rv); + return; + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + + rv = mListener->OnDataAvailable(this, mListenerContext, + stringStream, offset, data.Length()); + if (NS_FAILED(rv)) + Cancel(rv); + + if (mProgressSink && NS_SUCCEEDED(rv)) { + mProgressSink->OnProgress(this, nullptr, offset + data.Length(), + mContentLength); + } +} + +class WyciwygStopRequestEvent : public ChannelEvent +{ +public: + WyciwygStopRequestEvent(WyciwygChannelChild* child, + const nsresult& statusCode) + : mChild(child), mStatusCode(statusCode) {} + void Run() { mChild->OnStopRequest(mStatusCode); } +private: + WyciwygChannelChild* mChild; + nsresult mStatusCode; +}; + +bool +WyciwygChannelChild::RecvOnStopRequest(const nsresult& statusCode) +{ + mEventQ->RunOrEnqueue(new WyciwygStopRequestEvent(this, statusCode)); + return true; +} + +void +WyciwygChannelChild::OnStopRequest(const nsresult& statusCode) +{ + LOG(("WyciwygChannelChild::RecvOnStopRequest [this=%p status=%u]\n", + this, statusCode)); + + { // We need to ensure that all IPDL message dispatching occurs + // before we delete the protocol below + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + + mState = WCC_ONSTOP; + + mIsPending = false; + + if (!mCanceled) + mStatus = statusCode; + + mListener->OnStopRequest(this, mListenerContext, statusCode); + + mListener = nullptr; + mListenerContext = nullptr; + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nullptr, mStatus); + + mCallbacks = nullptr; + mProgressSink = nullptr; + } + + if (mIPCOpen) + PWyciwygChannelChild::Send__delete__(this); +} + +class WyciwygCancelEvent : public ChannelEvent +{ + public: + WyciwygCancelEvent(WyciwygChannelChild* child, const nsresult& status) + : mChild(child) + , mStatus(status) {} + + void Run() { mChild->CancelEarly(mStatus); } + private: + WyciwygChannelChild* mChild; + nsresult mStatus; +}; + +bool +WyciwygChannelChild::RecvCancelEarly(const nsresult& statusCode) +{ + mEventQ->RunOrEnqueue(new WyciwygCancelEvent(this, statusCode)); + return true; +} + +void WyciwygChannelChild::CancelEarly(const nsresult& statusCode) +{ + LOG(("WyciwygChannelChild::CancelEarly [this=%p]\n", this)); + + if (mCanceled) + return; + + mCanceled = true; + mStatus = statusCode; + + mIsPending = false; + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nullptr, mStatus); + + if (mListener) { + mListener->OnStartRequest(this, mListenerContext); + mListener->OnStopRequest(this, mListenerContext, mStatus); + } + mListener = nullptr; + mListenerContext = nullptr; + + if (mIPCOpen) + PWyciwygChannelChild::Send__delete__(this); +} + +//----------------------------------------------------------------------------- +// nsIRequest +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +WyciwygChannelChild::GetName(nsACString & aName) +{ + return mURI->GetSpec(aName); +} + +NS_IMETHODIMP +WyciwygChannelChild::IsPending(bool *aIsPending) +{ + *aIsPending = mIsPending; + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetStatus(nsresult *aStatus) +{ + *aStatus = mStatus; + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::Cancel(nsresult aStatus) +{ + if (mCanceled) + return NS_OK; + + mCanceled = true; + mStatus = aStatus; + if (mIPCOpen) + SendCancel(aStatus); + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::Suspend() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WyciwygChannelChild::Resume() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetLoadGroup(nsILoadGroup * *aLoadGroup) +{ + *aLoadGroup = mLoadGroup; + NS_IF_ADDREF(*aLoadGroup); + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetLoadGroup(nsILoadGroup * aLoadGroup) +{ + if (!CanSetLoadGroup(aLoadGroup)) { + return NS_ERROR_FAILURE; + } + + mLoadGroup = aLoadGroup; + NS_QueryNotificationCallbacks(mCallbacks, + mLoadGroup, + NS_GET_IID(nsIProgressEventSink), + getter_AddRefs(mProgressSink)); + + UpdatePrivateBrowsing(); + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetLoadFlags(nsLoadFlags *aLoadFlags) +{ + *aLoadFlags = mLoadFlags; + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetLoadFlags(nsLoadFlags aLoadFlags) +{ + mLoadFlags = aLoadFlags; + return NS_OK; +} + + +//----------------------------------------------------------------------------- +// nsIChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +WyciwygChannelChild::GetOriginalURI(nsIURI * *aOriginalURI) +{ + *aOriginalURI = mOriginalURI; + NS_ADDREF(*aOriginalURI); + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetOriginalURI(nsIURI * aOriginalURI) +{ + NS_ENSURE_TRUE(mState == WCC_INIT, NS_ERROR_UNEXPECTED); + + NS_ENSURE_ARG_POINTER(aOriginalURI); + mOriginalURI = aOriginalURI; + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetURI(nsIURI * *aURI) +{ + *aURI = mURI; + NS_IF_ADDREF(*aURI); + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetOwner(nsISupports * *aOwner) +{ + NS_IF_ADDREF(*aOwner = mOwner); + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetOwner(nsISupports * aOwner) +{ + mOwner = aOwner; + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetLoadInfo(nsILoadInfo **aLoadInfo) +{ + NS_IF_ADDREF(*aLoadInfo = mLoadInfo); + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::SetLoadInfo(nsILoadInfo* aLoadInfo) +{ + mLoadInfo = aLoadInfo; + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetNotificationCallbacks(nsIInterfaceRequestor * *aCallbacks) +{ + *aCallbacks = mCallbacks; + NS_IF_ADDREF(*aCallbacks); + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetNotificationCallbacks(nsIInterfaceRequestor * aCallbacks) +{ + if (!CanSetCallbacks(aCallbacks)) { + return NS_ERROR_FAILURE; + } + + mCallbacks = aCallbacks; + NS_QueryNotificationCallbacks(mCallbacks, + mLoadGroup, + NS_GET_IID(nsIProgressEventSink), + getter_AddRefs(mProgressSink)); + UpdatePrivateBrowsing(); + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetSecurityInfo(nsISupports * *aSecurityInfo) +{ + NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); + + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetContentType(nsACString & aContentType) +{ + aContentType.AssignLiteral(WYCIWYG_TYPE); + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetContentType(const nsACString & aContentType) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetContentCharset(nsACString & aContentCharset) +{ + aContentCharset.AssignLiteral("UTF-16"); + return NS_OK; +} +NS_IMETHODIMP +WyciwygChannelChild::SetContentCharset(const nsACString & aContentCharset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetContentDisposition(uint32_t *aContentDisposition) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +WyciwygChannelChild::SetContentDisposition(uint32_t aContentDisposition) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetContentDispositionFilename(nsAString &aContentDispositionFilename) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +WyciwygChannelChild::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetContentDispositionHeader(nsACString &aContentDispositionHeader) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetContentLength(int64_t *aContentLength) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP +WyciwygChannelChild::SetContentLength(int64_t aContentLength) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WyciwygChannelChild::Open(nsIInputStream **_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WyciwygChannelChild::Open2(nsIInputStream** aStream) +{ + nsCOMPtr<nsIStreamListener> listener; + nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener); + NS_ENSURE_SUCCESS(rv, rv); + return Open(aStream); +} + +static mozilla::dom::TabChild* +GetTabChild(nsIChannel* aChannel) +{ + nsCOMPtr<nsITabChild> iTabChild; + NS_QueryNotificationCallbacks(aChannel, iTabChild); + return iTabChild ? static_cast<mozilla::dom::TabChild*>(iTabChild.get()) : nullptr; +} + +NS_IMETHODIMP +WyciwygChannelChild::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) +{ + MOZ_ASSERT(!mLoadInfo || + mLoadInfo->GetSecurityMode() == 0 || + mLoadInfo->GetInitialSecurityCheckDone() || + (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL && + nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())), + "security flags in loadInfo but asyncOpen2() not called"); + + LOG(("WyciwygChannelChild::AsyncOpen [this=%p]\n", this)); + + // The only places creating wyciwyg: channels should be + // HTMLDocument::OpenCommon and session history. Both should be setting an + // owner or loadinfo. + NS_PRECONDITION(mOwner || mLoadInfo, "Must have a principal"); + NS_ENSURE_STATE(mOwner || mLoadInfo); + + NS_ENSURE_ARG_POINTER(aListener); + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + + mListener = aListener; + mListenerContext = aContext; + mIsPending = true; + + if (mLoadGroup) { + mLoadGroup->AddRequest(this, nullptr); + } + + URIParams originalURI; + SerializeURI(mOriginalURI, originalURI); + + mozilla::dom::TabChild* tabChild = GetTabChild(this); + if (MissingRequiredTabChild(tabChild, "wyciwyg")) { + return NS_ERROR_ILLEGAL_VALUE; + } + + PBrowserOrId browser = static_cast<ContentChild*>(Manager()->Manager()) + ->GetBrowserOrId(tabChild); + + SendAsyncOpen(originalURI, mLoadFlags, IPC::SerializedLoadContext(this), browser); + + mSentAppData = true; + mState = WCC_OPENED; + + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::AsyncOpen2(nsIStreamListener *aListener) +{ + nsCOMPtr<nsIStreamListener> listener = aListener; + nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener); + NS_ENSURE_SUCCESS(rv, rv); + return AsyncOpen(listener, nullptr); +} + +//----------------------------------------------------------------------------- +// nsIWyciwygChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +WyciwygChannelChild::WriteToCacheEntry(const nsAString & aData) +{ + NS_ENSURE_TRUE((mState == WCC_INIT) || + (mState == WCC_ONWRITE), NS_ERROR_UNEXPECTED); + + if (!mSentAppData) { + mozilla::dom::TabChild* tabChild = GetTabChild(this); + + PBrowserOrId browser = static_cast<ContentChild*>(Manager()->Manager()) + ->GetBrowserOrId(tabChild); + + SendAppData(IPC::SerializedLoadContext(this), browser); + mSentAppData = true; + } + + SendWriteToCacheEntry(PromiseFlatString(aData)); + mState = WCC_ONWRITE; + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::CloseCacheEntry(nsresult reason) +{ + NS_ENSURE_TRUE(mState == WCC_ONWRITE, NS_ERROR_UNEXPECTED); + + SendCloseCacheEntry(reason); + mState = WCC_ONCLOSED; + + if (mIPCOpen) + PWyciwygChannelChild::Send__delete__(this); + + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::SetSecurityInfo(nsISupports *aSecurityInfo) +{ + mSecurityInfo = aSecurityInfo; + + if (mSecurityInfo) { + nsCOMPtr<nsISerializable> serializable = do_QueryInterface(mSecurityInfo); + if (serializable) { + nsCString secInfoStr; + NS_SerializeToString(serializable, secInfoStr); + SendSetSecurityInfo(secInfoStr); + } + else { + NS_WARNING("Can't serialize security info"); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::SetCharsetAndSource(int32_t aSource, const nsACString & aCharset) +{ + // mState == WCC_ONSTART when reading from the channel + // mState == WCC_INIT when writing to the cache + NS_ENSURE_TRUE((mState == WCC_ONSTART) || + (mState == WCC_INIT), NS_ERROR_UNEXPECTED); + + mCharsetSource = aSource; + mCharset = aCharset; + + // TODO ensure that nsWyciwygChannel in the parent has still the cache entry + SendSetCharsetAndSource(mCharsetSource, mCharset); + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelChild::GetCharsetAndSource(int32_t *aSource, nsACString & _retval) +{ + NS_ENSURE_TRUE((mState == WCC_ONSTART) || + (mState == WCC_ONDATA) || + (mState == WCC_ONSTOP), NS_ERROR_NOT_AVAILABLE); + + if (mCharsetSource == kCharsetUninitialized) + return NS_ERROR_NOT_AVAILABLE; + + *aSource = mCharsetSource; + _retval = mCharset; + return NS_OK; +} + +//------------------------------------------------------------------------------ +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.h b/netwerk/protocol/wyciwyg/WyciwygChannelChild.h new file mode 100644 index 000000000..a712c4692 --- /dev/null +++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.h @@ -0,0 +1,124 @@ +/* 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_net_WyciwygChannelChild_h +#define mozilla_net_WyciwygChannelChild_h + +#include "mozilla/net/PWyciwygChannelChild.h" +#include "nsIWyciwygChannel.h" +#include "nsIChannel.h" +#include "nsILoadInfo.h" +#include "PrivateBrowsingChannel.h" + +class nsIProgressEventSink; + +namespace mozilla { +namespace net { + +class ChannelEventQueue; + +// TODO: replace with IPDL states +enum WyciwygChannelChildState { + WCC_NEW, + WCC_INIT, + + // States when reading from the channel + WCC_OPENED, + WCC_ONSTART, + WCC_ONDATA, + WCC_ONSTOP, + + // States when writing to the cache + WCC_ONWRITE, + WCC_ONCLOSED +}; + + +// Header file contents +class WyciwygChannelChild final : public PWyciwygChannelChild + , public nsIWyciwygChannel + , public PrivateBrowsingChannel<WyciwygChannelChild> +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUEST + NS_DECL_NSICHANNEL + NS_DECL_NSIWYCIWYGCHANNEL + + WyciwygChannelChild(); + + void AddIPDLReference(); + void ReleaseIPDLReference(); + + nsresult Init(nsIURI *uri); + + bool IsSuspended(); + +protected: + virtual ~WyciwygChannelChild(); + + bool RecvOnStartRequest(const nsresult& statusCode, + const int64_t& contentLength, + const int32_t& source, + const nsCString& charset, + const nsCString& securityInfo) override; + bool RecvOnDataAvailable(const nsCString& data, + const uint64_t& offset) override; + bool RecvOnStopRequest(const nsresult& statusCode) override; + bool RecvCancelEarly(const nsresult& statusCode) override; + + void OnStartRequest(const nsresult& statusCode, + const int64_t& contentLength, + const int32_t& source, + const nsCString& charset, + const nsCString& securityInfo); + void OnDataAvailable(const nsCString& data, + const uint64_t& offset); + void OnStopRequest(const nsresult& statusCode); + void CancelEarly(const nsresult& statusCode); + + friend class PrivateBrowsingChannel<WyciwygChannelChild>; + +private: + nsresult mStatus; + bool mIsPending; + bool mCanceled; + uint32_t mLoadFlags; + int64_t mContentLength; + int32_t mCharsetSource; + nsCString mCharset; + nsCOMPtr<nsIURI> mURI; + nsCOMPtr<nsIURI> mOriginalURI; + nsCOMPtr<nsISupports> mOwner; + nsCOMPtr<nsILoadInfo> mLoadInfo; + nsCOMPtr<nsIInterfaceRequestor> mCallbacks; + nsCOMPtr<nsIProgressEventSink> mProgressSink; + nsCOMPtr<nsILoadGroup> mLoadGroup; + nsCOMPtr<nsIStreamListener> mListener; + nsCOMPtr<nsISupports> mListenerContext; + nsCOMPtr<nsISupports> mSecurityInfo; + + // FIXME: replace with IPDL states (bug 536319) + enum WyciwygChannelChildState mState; + + bool mIPCOpen; + bool mSentAppData; + RefPtr<ChannelEventQueue> mEventQ; + + friend class WyciwygStartRequestEvent; + friend class WyciwygDataAvailableEvent; + friend class WyciwygStopRequestEvent; + friend class WyciwygCancelEvent; +}; + +inline bool +WyciwygChannelChild::IsSuspended() +{ + return false; +} + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_WyciwygChannelChild_h diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp new file mode 100644 index 000000000..be00c2d86 --- /dev/null +++ b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp @@ -0,0 +1,373 @@ +/* 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 "nsWyciwyg.h" + +#include "mozilla/net/WyciwygChannelParent.h" +#include "nsWyciwygChannel.h" +#include "nsNetUtil.h" +#include "nsCharsetSource.h" +#include "nsISerializable.h" +#include "nsSerializationHelper.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/net/NeckoParent.h" +#include "SerializedLoadContext.h" +#include "nsIContentPolicy.h" +#include "mozilla/ipc/BackgroundUtils.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace net { + +WyciwygChannelParent::WyciwygChannelParent() + : mIPCClosed(false) + , mReceivedAppData(false) +{ +} + +WyciwygChannelParent::~WyciwygChannelParent() +{ +} + +void +WyciwygChannelParent::ActorDestroy(ActorDestroyReason why) +{ + // We may still have refcount>0 if the channel hasn't called OnStopRequest + // yet, but we must not send any more msgs to child. + mIPCClosed = true; + + // We need to force the cycle to break here + if (mChannel) { + mChannel->SetNotificationCallbacks(nullptr); + } +} + +//----------------------------------------------------------------------------- +// WyciwygChannelParent::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(WyciwygChannelParent, + nsIStreamListener, + nsIInterfaceRequestor, + nsIRequestObserver) + +//----------------------------------------------------------------------------- +// WyciwygChannelParent::PWyciwygChannelParent +//----------------------------------------------------------------------------- + +bool +WyciwygChannelParent::RecvInit(const URIParams& aURI, + const ipc::PrincipalInfo& aRequestingPrincipalInfo, + const ipc::PrincipalInfo& aTriggeringPrincipalInfo, + const ipc::PrincipalInfo& aPrincipalToInheritInfo, + const uint32_t& aSecurityFlags, + const uint32_t& aContentPolicyType) +{ + nsresult rv; + + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); + if (!uri) + return false; + + LOG(("WyciwygChannelParent RecvInit [this=%p uri=%s]\n", + this, uri->GetSpecOrDefault().get())); + + nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv)); + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + nsCOMPtr<nsIPrincipal> requestingPrincipal = + mozilla::ipc::PrincipalInfoToPrincipal(aRequestingPrincipalInfo, &rv); + if (NS_FAILED(rv)) { + return SendCancelEarly(rv); + } + + nsCOMPtr<nsIPrincipal> triggeringPrincipal = + mozilla::ipc::PrincipalInfoToPrincipal(aTriggeringPrincipalInfo, &rv); + if (NS_FAILED(rv)) { + return SendCancelEarly(rv); + } + + nsCOMPtr<nsIPrincipal> principalToInherit = + mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalToInheritInfo, &rv); + if (NS_FAILED(rv)) { + return SendCancelEarly(rv); + } + + nsCOMPtr<nsIChannel> chan; + rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(chan), + uri, + requestingPrincipal, + triggeringPrincipal, + aSecurityFlags, + aContentPolicyType, + nullptr, // loadGroup + nullptr, // aCallbacks + nsIRequest::LOAD_NORMAL, + ios); + + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo(); + rv = loadInfo->SetPrincipalToInherit(principalToInherit); + if (NS_FAILED(rv)) { + return SendCancelEarly(rv); + } + + mChannel = do_QueryInterface(chan, &rv); + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + return true; +} + +bool +WyciwygChannelParent::RecvAppData(const IPC::SerializedLoadContext& loadContext, + const PBrowserOrId &parent) +{ + LOG(("WyciwygChannelParent RecvAppData [this=%p]\n", this)); + + if (!SetupAppData(loadContext, parent)) + return false; + + mChannel->SetNotificationCallbacks(this); + return true; +} + +bool +WyciwygChannelParent::SetupAppData(const IPC::SerializedLoadContext& loadContext, + const PBrowserOrId &aParent) +{ + if (!mChannel) + return true; + + const char* error = NeckoParent::CreateChannelLoadContext(aParent, + Manager()->Manager(), + loadContext, + nullptr, + mLoadContext); + if (error) { + printf_stderr("WyciwygChannelParent::SetupAppData: FATAL ERROR: %s\n", + error); + return false; + } + + if (!mLoadContext && loadContext.IsPrivateBitValid()) { + nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(mChannel); + if (pbChannel) + pbChannel->SetPrivate(loadContext.mOriginAttributes.mPrivateBrowsingId > 0); + } + + mReceivedAppData = true; + return true; +} + +bool +WyciwygChannelParent::RecvAsyncOpen(const URIParams& aOriginal, + const uint32_t& aLoadFlags, + const IPC::SerializedLoadContext& loadContext, + const PBrowserOrId &aParent) +{ + nsCOMPtr<nsIURI> original = DeserializeURI(aOriginal); + if (!original) + return false; + + LOG(("WyciwygChannelParent RecvAsyncOpen [this=%p]\n", this)); + + if (!mChannel) + return true; + + nsresult rv; + + rv = mChannel->SetOriginalURI(original); + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + rv = mChannel->SetLoadFlags(aLoadFlags); + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + if (!mReceivedAppData && !SetupAppData(loadContext, aParent)) { + return false; + } + + rv = mChannel->SetNotificationCallbacks(this); + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo(); + if (loadInfo && loadInfo->GetEnforceSecurity()) { + rv = mChannel->AsyncOpen2(this); + } + else { + rv = mChannel->AsyncOpen(this, nullptr); + } + + if (NS_FAILED(rv)) + return SendCancelEarly(rv); + + return true; +} + +bool +WyciwygChannelParent::RecvWriteToCacheEntry(const nsString& data) +{ + if (!mReceivedAppData) { + printf_stderr("WyciwygChannelParent::RecvWriteToCacheEntry: FATAL ERROR: didn't receive app data\n"); + return false; + } + + if (mChannel) + mChannel->WriteToCacheEntry(data); + + return true; +} + +bool +WyciwygChannelParent::RecvCloseCacheEntry(const nsresult& reason) +{ + if (mChannel) { + mChannel->CloseCacheEntry(reason); + } + + return true; +} + +bool +WyciwygChannelParent::RecvSetCharsetAndSource(const int32_t& aCharsetSource, + const nsCString& aCharset) +{ + if (mChannel) + mChannel->SetCharsetAndSource(aCharsetSource, aCharset); + + return true; +} + +bool +WyciwygChannelParent::RecvSetSecurityInfo(const nsCString& aSecurityInfo) +{ + if (mChannel) { + nsCOMPtr<nsISupports> securityInfo; + NS_DeserializeObject(aSecurityInfo, getter_AddRefs(securityInfo)); + mChannel->SetSecurityInfo(securityInfo); + } + + return true; +} + +bool +WyciwygChannelParent::RecvCancel(const nsresult& aStatusCode) +{ + if (mChannel) + mChannel->Cancel(aStatusCode); + return true; +} + +//----------------------------------------------------------------------------- +// WyciwygChannelParent::nsIRequestObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +WyciwygChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) +{ + LOG(("WyciwygChannelParent::OnStartRequest [this=%p]\n", this)); + + nsresult rv; + + nsCOMPtr<nsIWyciwygChannel> chan = do_QueryInterface(aRequest, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsresult status; + chan->GetStatus(&status); + + int64_t contentLength = -1; + chan->GetContentLength(&contentLength); + + int32_t charsetSource = kCharsetUninitialized; + nsAutoCString charset; + chan->GetCharsetAndSource(&charsetSource, charset); + + nsCOMPtr<nsISupports> securityInfo; + chan->GetSecurityInfo(getter_AddRefs(securityInfo)); + nsCString secInfoStr; + if (securityInfo) { + nsCOMPtr<nsISerializable> serializable = do_QueryInterface(securityInfo); + if (serializable) + NS_SerializeToString(serializable, secInfoStr); + else { + NS_ERROR("Can't serialize security info"); + return NS_ERROR_UNEXPECTED; + } + } + + if (mIPCClosed || + !SendOnStartRequest(status, contentLength, charsetSource, charset, secInfoStr)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_IMETHODIMP +WyciwygChannelParent::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatusCode) +{ + LOG(("WyciwygChannelParent::OnStopRequest: [this=%p status=%ul]\n", + this, aStatusCode)); + + if (mIPCClosed || !SendOnStopRequest(aStatusCode)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// WyciwygChannelParent::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +WyciwygChannelParent::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + uint64_t aOffset, + uint32_t aCount) +{ + LOG(("WyciwygChannelParent::OnDataAvailable [this=%p]\n", this)); + + nsCString data; + nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount); + if (NS_FAILED(rv)) + return rv; + + if (mIPCClosed || !SendOnDataAvailable(data, aOffset)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// WyciwygChannelParent::nsIInterfaceRequestor +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +WyciwygChannelParent::GetInterface(const nsIID& uuid, void** result) +{ + // Only support nsILoadContext if child channel's callbacks did too + if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) { + nsCOMPtr<nsILoadContext> copy = mLoadContext; + copy.forget(result); + return NS_OK; + } + + return QueryInterface(uuid, result); +} + + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelParent.h b/netwerk/protocol/wyciwyg/WyciwygChannelParent.h new file mode 100644 index 000000000..be009487b --- /dev/null +++ b/netwerk/protocol/wyciwyg/WyciwygChannelParent.h @@ -0,0 +1,71 @@ +/* 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_net_WyciwygChannelParent_h +#define mozilla_net_WyciwygChannelParent_h + +#include "mozilla/net/PWyciwygChannelParent.h" +#include "mozilla/net/NeckoCommon.h" +#include "nsIStreamListener.h" + +#include "nsIWyciwygChannel.h" +#include "nsIInterfaceRequestor.h" +#include "nsILoadContext.h" + +namespace mozilla { +namespace dom { + class PBrowserParent; +} // namespace dom + +namespace net { + +class WyciwygChannelParent : public PWyciwygChannelParent + , public nsIStreamListener + , public nsIInterfaceRequestor +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIINTERFACEREQUESTOR + + WyciwygChannelParent(); + +protected: + virtual ~WyciwygChannelParent(); + + virtual bool RecvInit(const URIParams& uri, + const ipc::PrincipalInfo& aRequestingPrincipalInfo, + const ipc::PrincipalInfo& aTriggeringPrincipalInfo, + const ipc::PrincipalInfo& aPrincipalToInheritInfo, + const uint32_t& aSecurityFlags, + const uint32_t& aContentPolicyType) override; + virtual bool RecvAsyncOpen(const URIParams& original, + const uint32_t& loadFlags, + const IPC::SerializedLoadContext& loadContext, + const PBrowserOrId &parent) override; + virtual bool RecvWriteToCacheEntry(const nsString& data) override; + virtual bool RecvCloseCacheEntry(const nsresult& reason) override; + virtual bool RecvSetCharsetAndSource(const int32_t& source, + const nsCString& charset) override; + virtual bool RecvSetSecurityInfo(const nsCString& securityInfo) override; + virtual bool RecvCancel(const nsresult& statusCode) override; + virtual bool RecvAppData(const IPC::SerializedLoadContext& loadContext, + const PBrowserOrId &parent) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool SetupAppData(const IPC::SerializedLoadContext& loadContext, + const PBrowserOrId &aParent); + + nsCOMPtr<nsIWyciwygChannel> mChannel; + bool mIPCClosed; + bool mReceivedAppData; + nsCOMPtr<nsILoadContext> mLoadContext; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_WyciwygChannelParent_h diff --git a/netwerk/protocol/wyciwyg/moz.build b/netwerk/protocol/wyciwyg/moz.build new file mode 100644 index 000000000..b043137f7 --- /dev/null +++ b/netwerk/protocol/wyciwyg/moz.build @@ -0,0 +1,36 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + 'nsIWyciwygChannel.idl', +] + +XPIDL_MODULE = 'necko_wyciwyg' + +EXPORTS.mozilla.net += [ + 'WyciwygChannelChild.h', + 'WyciwygChannelParent.h', +] + +UNIFIED_SOURCES += [ + 'nsWyciwyg.cpp', + 'nsWyciwygChannel.cpp', + 'nsWyciwygProtocolHandler.cpp', + 'WyciwygChannelChild.cpp', + 'WyciwygChannelParent.cpp', +] + +IPDL_SOURCES += [ + 'PWyciwygChannel.ipdl', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/netwerk/base', +] diff --git a/netwerk/protocol/wyciwyg/nsIWyciwygChannel.idl b/netwerk/protocol/wyciwyg/nsIWyciwygChannel.idl new file mode 100644 index 000000000..29bcc4d77 --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsIWyciwygChannel.idl @@ -0,0 +1,45 @@ +/* -*- Mode: C++; 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/. */ + +#include "nsIChannel.idl" + +/** + * A channel to manage all cache-related interactions for layout + * when it is dealing with dynamic pages created through + * document.write(). This interface provides methods that will + * help layout save dynamic pages in cache for future retrievals. + */ + +[scriptable, uuid (8b8f3341-46da-40f5-a16f-41a91f5d25dd)] +interface nsIWyciwygChannel : nsIChannel +{ + /** + * Append data to the cache entry; opens the cache entry if necessary. + */ + void writeToCacheEntry(in AString aData); + + /** + * Close the cache entry; subsequent writes have undefined behavior. + */ + void closeCacheEntry(in nsresult reason); + + /** + * Set the wyciwyg channels security info + */ + void setSecurityInfo(in nsISupports aSecurityInfo); + + /** + * Store and read a charset and charset source on the wyciwyg channel. These + * are opaque values to the channel; consumers who set them should know what + * they mean. + */ + void setCharsetAndSource(in long aSource, in ACString aCharset); + /** + * The return value is the charset. Throws if either the charset or the + * source cannot be retrieved. This is guaranteed to return a nonzero source + * and a nonempty charset if it does not throw. + */ + ACString getCharsetAndSource(out long aSource); +}; diff --git a/netwerk/protocol/wyciwyg/nsWyciwyg.cpp b/netwerk/protocol/wyciwyg/nsWyciwyg.cpp new file mode 100644 index 000000000..edc716afd --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsWyciwyg.cpp @@ -0,0 +1,10 @@ +/* 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 "nsWyciwyg.h" +#include "nscore.h" + +mozilla::LazyLogModule gWyciwygLog("nsWyciwygChannel"); + + diff --git a/netwerk/protocol/wyciwyg/nsWyciwyg.h b/netwerk/protocol/wyciwyg/nsWyciwyg.h new file mode 100644 index 000000000..48199a9b9 --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsWyciwyg.h @@ -0,0 +1,43 @@ +/* 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 nsWyciwyg_h__ +#define nsWyciwyg_h__ + +#include "mozilla/net/NeckoChild.h" + +// Get rid of chromium's LOG. +#undef LOG + +#include "mozilla/Logging.h" + +// +// Log module for HTTP Protocol logging... +// +// To enable logging (see prlog.h for full details): +// +// set MOZ_LOG=nsWyciwyg:5 +// set MOZ_LOG_FILE=wyciwyg.log +// +// This enables LogLevel::Debug level information and places all output in +// the file wyciwyg.log. +// +extern mozilla::LazyLogModule gWyciwygLog; + +// http logging +#define LOG1(args) MOZ_LOG(gWyciwygLog, mozilla::LogLevel::Error, args) +#define LOG2(args) MOZ_LOG(gWyciwygLog, mozilla::LogLevel::Warning, args) +#define LOG3(args) MOZ_LOG(gWyciwygLog, mozilla::LogLevel::Info, args) +#define LOG4(args) MOZ_LOG(gWyciwygLog, mozilla::LogLevel::Debug, args) +#define LOG(args) LOG4(args) + +#define LOG1_ENABLED() MOZ_LOG_TEST(gWyciwygLog, mozilla::LogLevel::Error) +#define LOG2_ENABLED() MOZ_LOG_TEST(gWyciwygLog, mozilla::LogLevel::Warning) +#define LOG3_ENABLED() MOZ_LOG_TEST(gWyciwygLog, mozilla::LogLevel::Info) +#define LOG4_ENABLED() MOZ_LOG_TEST(gWyciwygLog, mozilla::LogLevel::Debug) +#define LOG_ENABLED() LOG4_ENABLED() + +#define WYCIWYG_TYPE "text/html" + +#endif // nsWyciwyg_h__ diff --git a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp new file mode 100644 index 000000000..52949b799 --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp @@ -0,0 +1,808 @@ +/* -*- Mode: C++; 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/. */ + +#include "nsWyciwyg.h" +#include "nsWyciwygChannel.h" +#include "nsILoadGroup.h" +#include "nsNetUtil.h" +#include "nsNetCID.h" +#include "LoadContextInfo.h" +#include "nsICacheService.h" // only to initialize +#include "nsICacheStorageService.h" +#include "nsICacheStorage.h" +#include "nsICacheEntry.h" +#include "nsCharsetSource.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" +#include "nsIInputStream.h" +#include "nsIInputStreamPump.h" +#include "nsIOutputStream.h" +#include "nsIProgressEventSink.h" +#include "nsIURI.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Unused.h" +#include "mozilla/BasePrincipal.h" +#include "nsProxyRelease.h" +#include "nsContentSecurityManager.h" +#include "nsContentUtils.h" + +typedef mozilla::net::LoadContextInfo LoadContextInfo; + +// nsWyciwygChannel methods +nsWyciwygChannel::nsWyciwygChannel() + : mMode(NONE), + mStatus(NS_OK), + mIsPending(false), + mNeedToWriteCharset(false), + mCharsetSource(kCharsetUninitialized), + mContentLength(-1), + mLoadFlags(LOAD_NORMAL), + mNeedToSetSecurityInfo(false) +{ +} + +nsWyciwygChannel::~nsWyciwygChannel() +{ + if (mLoadInfo) { + NS_ReleaseOnMainThread(mLoadInfo.forget(), false); + } +} + +NS_IMPL_ISUPPORTS(nsWyciwygChannel, + nsIChannel, + nsIRequest, + nsIStreamListener, + nsIRequestObserver, + nsICacheEntryOpenCallback, + nsIWyciwygChannel, + nsIPrivateBrowsingChannel) + +nsresult +nsWyciwygChannel::Init(nsIURI* uri) +{ + NS_ENSURE_ARG_POINTER(uri); + + mURI = uri; + mOriginalURI = uri; + + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// nsIRequest methods: +/////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsWyciwygChannel::GetName(nsACString &aName) +{ + return mURI->GetSpec(aName); +} + +NS_IMETHODIMP +nsWyciwygChannel::IsPending(bool *aIsPending) +{ + *aIsPending = mIsPending; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetStatus(nsresult *aStatus) +{ + if (NS_SUCCEEDED(mStatus) && mPump) + mPump->GetStatus(aStatus); + else + *aStatus = mStatus; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::Cancel(nsresult status) +{ + mStatus = status; + if (mPump) + mPump->Cancel(status); + // else we're waiting for OnCacheEntryAvailable + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::Suspend() +{ + if (mPump) + mPump->Suspend(); + // XXX else, we'll ignore this ... and that's probably bad! + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::Resume() +{ + if (mPump) + mPump->Resume(); + // XXX else, we'll ignore this ... and that's probably bad! + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) +{ + *aLoadGroup = mLoadGroup; + NS_IF_ADDREF(*aLoadGroup); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) +{ + if (!CanSetLoadGroup(aLoadGroup)) { + return NS_ERROR_FAILURE; + } + + mLoadGroup = aLoadGroup; + NS_QueryNotificationCallbacks(mCallbacks, + mLoadGroup, + NS_GET_IID(nsIProgressEventSink), + getter_AddRefs(mProgressSink)); + UpdatePrivateBrowsing(); + NS_GetOriginAttributes(this, mOriginAttributes); + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetLoadFlags(uint32_t aLoadFlags) +{ + mLoadFlags = aLoadFlags; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetLoadFlags(uint32_t * aLoadFlags) +{ + *aLoadFlags = mLoadFlags; + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIChannel methods: +/////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsWyciwygChannel::GetOriginalURI(nsIURI* *aURI) +{ + *aURI = mOriginalURI; + NS_ADDREF(*aURI); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetOriginalURI(nsIURI* aURI) +{ + NS_ENSURE_ARG_POINTER(aURI); + mOriginalURI = aURI; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetURI(nsIURI* *aURI) +{ + *aURI = mURI; + NS_IF_ADDREF(*aURI); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetOwner(nsISupports **aOwner) +{ + NS_IF_ADDREF(*aOwner = mOwner); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetOwner(nsISupports* aOwner) +{ + mOwner = aOwner; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetLoadInfo(nsILoadInfo **aLoadInfo) +{ + NS_IF_ADDREF(*aLoadInfo = mLoadInfo); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) +{ + mLoadInfo = aLoadInfo; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) +{ + *aCallbacks = mCallbacks.get(); + NS_IF_ADDREF(*aCallbacks); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) +{ + if (!CanSetCallbacks(aNotificationCallbacks)) { + return NS_ERROR_FAILURE; + } + + mCallbacks = aNotificationCallbacks; + NS_QueryNotificationCallbacks(mCallbacks, + mLoadGroup, + NS_GET_IID(nsIProgressEventSink), + getter_AddRefs(mProgressSink)); + + UpdatePrivateBrowsing(); + NS_GetOriginAttributes(this, mOriginAttributes); + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) +{ + NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetContentType(nsACString &aContentType) +{ + aContentType.AssignLiteral(WYCIWYG_TYPE); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetContentType(const nsACString &aContentType) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetContentCharset(nsACString &aContentCharset) +{ + aContentCharset.AssignLiteral("UTF-16"); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetContentCharset(const nsACString &aContentCharset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetContentDisposition(uint32_t *aContentDisposition) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetContentDisposition(uint32_t aContentDisposition) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetContentLength(int64_t *aContentLength) +{ + *aContentLength = mContentLength; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetContentLength(int64_t aContentLength) +{ + mContentLength = aContentLength; + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::Open(nsIInputStream ** aReturn) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsWyciwygChannel::Open2(nsIInputStream** aStream) +{ + nsCOMPtr<nsIStreamListener> listener; + nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener); + NS_ENSURE_SUCCESS(rv, rv); + return Open(aStream); +} + +NS_IMETHODIMP +nsWyciwygChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) +{ + MOZ_ASSERT(!mLoadInfo || + mLoadInfo->GetSecurityMode() == 0 || + mLoadInfo->GetInitialSecurityCheckDone() || + (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL && + nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())), + "security flags in loadInfo but asyncOpen2() not called"); + + LOG(("nsWyciwygChannel::AsyncOpen [this=%p]\n", this)); + MOZ_ASSERT(mMode == NONE, "nsWyciwygChannel already open"); + + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + NS_ENSURE_TRUE(mMode == NONE, NS_ERROR_IN_PROGRESS); + NS_ENSURE_ARG_POINTER(listener); + + mMode = READING; + + // open a cache entry for this channel... + // mIsPending set to true since OnCacheEntryAvailable may be called + // synchronously and fails when mIsPending found false. + mIsPending = true; + nsresult rv = OpenCacheEntryForReading(mURI); + if (NS_FAILED(rv)) { + LOG(("nsWyciwygChannel::OpenCacheEntryForReading failed [rv=%x]\n", rv)); + mIsPending = false; + return rv; + } + + // There is no code path that would invoke the listener sooner than + // we get to this line in case OnCacheEntryAvailable is invoked + // synchronously. + mListener = listener; + mListenerContext = ctx; + + if (mLoadGroup) + mLoadGroup->AddRequest(this, nullptr); + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::AsyncOpen2(nsIStreamListener *aListener) +{ + nsCOMPtr<nsIStreamListener> listener = aListener; + nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener); + NS_ENSURE_SUCCESS(rv, rv); + return AsyncOpen(listener, nullptr); +} + +////////////////////////////////////////////////////////////////////////////// +// nsIWyciwygChannel +////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsWyciwygChannel::WriteToCacheEntry(const nsAString &aData) +{ + LOG(("nsWyciwygChannel::WriteToCacheEntry [this=%p]", this)); + + nsresult rv; + + if (mMode == READING) { + LOG(("nsWyciwygChannel::WriteToCacheEntry already open for reading")); + MOZ_ASSERT(false); + return NS_ERROR_UNEXPECTED; + } + + mMode = WRITING; + + if (!mCacheEntry) { + nsresult rv = OpenCacheEntryForWriting(mURI); + if (NS_FAILED(rv) || !mCacheEntry) { + LOG((" could not synchronously open cache entry for write!")); + return NS_ERROR_FAILURE; + } + } + + if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) { + rv = mCacheEntry->SetMetaDataElement("inhibit-persistent-caching", "1"); + if (NS_FAILED(rv)) return rv; + } + + if (mNeedToSetSecurityInfo) { + mCacheEntry->SetSecurityInfo(mSecurityInfo); + mNeedToSetSecurityInfo = false; + } + + if (mNeedToWriteCharset) { + WriteCharsetAndSourceToCache(mCharsetSource, mCharset); + mNeedToWriteCharset = false; + } + + uint32_t out; + if (!mCacheOutputStream) { + // Get the outputstream from the cache entry. + rv = mCacheEntry->OpenOutputStream(0, getter_AddRefs(mCacheOutputStream)); + if (NS_FAILED(rv)) return rv; + + // Write out a Byte Order Mark, so that we'll know if the data is + // BE or LE when we go to read it. + char16_t bom = 0xFEFF; + rv = mCacheOutputStream->Write((char *)&bom, sizeof(bom), &out); + if (NS_FAILED(rv)) return rv; + } + + return mCacheOutputStream->Write((const char *)PromiseFlatString(aData).get(), + aData.Length() * sizeof(char16_t), &out); +} + + +NS_IMETHODIMP +nsWyciwygChannel::CloseCacheEntry(nsresult reason) +{ + if (mCacheEntry) { + LOG(("nsWyciwygChannel::CloseCacheEntry [this=%p ]", this)); + mCacheOutputStream = nullptr; + mCacheInputStream = nullptr; + + if (NS_FAILED(reason)) { + mCacheEntry->AsyncDoom(nullptr); + } + + mCacheEntry = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetSecurityInfo(nsISupports *aSecurityInfo) +{ + if (mMode == READING) { + MOZ_ASSERT(false); + return NS_ERROR_UNEXPECTED; + } + + mSecurityInfo = aSecurityInfo; + + if (mCacheEntry) { + return mCacheEntry->SetSecurityInfo(mSecurityInfo); + } + + mNeedToSetSecurityInfo = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::SetCharsetAndSource(int32_t aSource, + const nsACString& aCharset) +{ + NS_ENSURE_ARG(!aCharset.IsEmpty()); + + if (mCacheEntry) { + WriteCharsetAndSourceToCache(mCharsetSource, mCharset); + } else { + MOZ_ASSERT(mMode != WRITING, "We must have an entry!"); + if (mMode == READING) { + return NS_ERROR_NOT_AVAILABLE; + } + mNeedToWriteCharset = true; + mCharsetSource = aSource; + mCharset = aCharset; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::GetCharsetAndSource(int32_t* aSource, nsACString& aCharset) +{ + MOZ_ASSERT(mMode == READING); + + if (!mCacheEntry) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsXPIDLCString data; + mCacheEntry->GetMetaDataElement("charset", getter_Copies(data)); + + if (data.IsEmpty()) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsXPIDLCString sourceStr; + mCacheEntry->GetMetaDataElement("charset-source", getter_Copies(sourceStr)); + + int32_t source; + nsresult err; + source = sourceStr.ToInteger(&err); + if (NS_FAILED(err) || source == 0) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aSource = source; + aCharset = data; + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////////// +// nsICacheEntryOpenCallback +////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsWyciwygChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appCache, + uint32_t* aResult) +{ + *aResult = ENTRY_WANTED; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygChannel::OnCacheEntryAvailable(nsICacheEntry *aCacheEntry, + bool aNew, + nsIApplicationCache* aAppCache, + nsresult aStatus) +{ + LOG(("nsWyciwygChannel::OnCacheEntryAvailable [this=%p entry=%p " + "new=%d status=%x]\n", this, aCacheEntry, aNew, aStatus)); + + MOZ_RELEASE_ASSERT(!aNew, "New entry must not be returned when flag " + "OPEN_READONLY is used!"); + + // if the channel's already fired onStopRequest, + // then we should ignore this event. + if (!mIsPending) + return NS_OK; + + if (NS_SUCCEEDED(mStatus)) { + if (NS_SUCCEEDED(aStatus)) { + MOZ_ASSERT(aCacheEntry); + mCacheEntry = aCacheEntry; + nsresult rv = ReadFromCache(); + if (NS_FAILED(rv)) { + mStatus = rv; + } + } else { + mStatus = aStatus; + } + } + + if (NS_FAILED(mStatus)) { + LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); + // Since OnCacheEntryAvailable can be called directly from AsyncOpen + // we must dispatch. + NS_DispatchToCurrentThread(mozilla::NewRunnableMethod( + this, &nsWyciwygChannel::NotifyListener)); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsWyciwygChannel::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsWyciwygChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctx, + nsIInputStream *input, + uint64_t offset, uint32_t count) +{ + LOG(("nsWyciwygChannel::OnDataAvailable [this=%p request=%x offset=%llu count=%u]\n", + this, request, offset, count)); + + nsresult rv; + + nsCOMPtr<nsIStreamListener> listener = mListener; + nsCOMPtr<nsISupports> listenerContext = mListenerContext; + + if (listener) { + rv = listener->OnDataAvailable(this, listenerContext, input, offset, count); + } else { + MOZ_ASSERT(false, "We must have a listener!"); + rv = NS_ERROR_UNEXPECTED; + } + + // XXX handle 64-bit stuff for real + if (mProgressSink && NS_SUCCEEDED(rv)) { + mProgressSink->OnProgress(this, nullptr, offset + count, mContentLength); + } + + return rv; // let the pump cancel on failure +} + +////////////////////////////////////////////////////////////////////////////// +// nsIRequestObserver +////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsWyciwygChannel::OnStartRequest(nsIRequest *request, nsISupports *ctx) +{ + LOG(("nsWyciwygChannel::OnStartRequest [this=%p request=%x\n", + this, request)); + + nsCOMPtr<nsIStreamListener> listener = mListener; + nsCOMPtr<nsISupports> listenerContext = mListenerContext; + + if (listener) { + return listener->OnStartRequest(this, listenerContext); + } + + MOZ_ASSERT(false, "We must have a listener!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsWyciwygChannel::OnStopRequest(nsIRequest *request, nsISupports *ctx, nsresult status) +{ + LOG(("nsWyciwygChannel::OnStopRequest [this=%p request=%x status=%d\n", + this, request, status)); + + if (NS_SUCCEEDED(mStatus)) + mStatus = status; + + mIsPending = false; + + nsCOMPtr<nsIStreamListener> listener; + nsCOMPtr<nsISupports> listenerContext; + listener.swap(mListener); + listenerContext.swap(mListenerContext); + + if (listener) { + listener->OnStopRequest(this, listenerContext, mStatus); + } else { + MOZ_ASSERT(false, "We must have a listener!"); + } + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nullptr, mStatus); + + CloseCacheEntry(mStatus); + mPump = nullptr; + + // Drop notification callbacks to prevent cycles. + mCallbacks = nullptr; + mProgressSink = nullptr; + + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////////// + +nsresult +nsWyciwygChannel::GetCacheStorage(nsICacheStorage **_retval) +{ + nsresult rv; + + nsCOMPtr<nsICacheStorageService> cacheService = + do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + bool anonymous = mLoadFlags & LOAD_ANONYMOUS; + mOriginAttributes.SyncAttributesWithPrivateBrowsing(mPrivateBrowsing); + RefPtr<LoadContextInfo> loadInfo = mozilla::net::GetLoadContextInfo(anonymous, mOriginAttributes); + + if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) { + return cacheService->MemoryCacheStorage(loadInfo, _retval); + } + + return cacheService->DiskCacheStorage(loadInfo, false, _retval); +} + +nsresult +nsWyciwygChannel::OpenCacheEntryForReading(nsIURI *aURI) +{ + nsresult rv; + + nsCOMPtr<nsICacheStorage> cacheStorage; + rv = GetCacheStorage(getter_AddRefs(cacheStorage)); + NS_ENSURE_SUCCESS(rv, rv); + + return cacheStorage->AsyncOpenURI(aURI, EmptyCString(), + nsICacheStorage::OPEN_READONLY | + nsICacheStorage::CHECK_MULTITHREADED, + this); +} + +nsresult +nsWyciwygChannel::OpenCacheEntryForWriting(nsIURI *aURI) +{ + nsresult rv; + + nsCOMPtr<nsICacheStorage> cacheStorage; + rv = GetCacheStorage(getter_AddRefs(cacheStorage)); + NS_ENSURE_SUCCESS(rv, rv); + + return cacheStorage->OpenTruncate(aURI, EmptyCString(), + getter_AddRefs(mCacheEntry)); +} + +nsresult +nsWyciwygChannel::ReadFromCache() +{ + LOG(("nsWyciwygChannel::ReadFromCache [this=%p] ", this)); + + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE); + nsresult rv; + + // Get the stored security info + mCacheEntry->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); + + nsAutoCString tmpStr; + rv = mCacheEntry->GetMetaDataElement("inhibit-persistent-caching", + getter_Copies(tmpStr)); + if (NS_SUCCEEDED(rv) && tmpStr.EqualsLiteral("1")) + mLoadFlags |= INHIBIT_PERSISTENT_CACHING; + + // Get a transport to the cached data... + rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(mCacheInputStream)); + if (NS_FAILED(rv)) + return rv; + NS_ENSURE_TRUE(mCacheInputStream, NS_ERROR_UNEXPECTED); + + rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mCacheInputStream); + if (NS_FAILED(rv)) return rv; + + // Pump the cache data downstream + return mPump->AsyncRead(this, nullptr); +} + +void +nsWyciwygChannel::WriteCharsetAndSourceToCache(int32_t aSource, + const nsCString& aCharset) +{ + NS_PRECONDITION(mCacheEntry, "Better have cache entry!"); + + mCacheEntry->SetMetaDataElement("charset", aCharset.get()); + + nsAutoCString source; + source.AppendInt(aSource); + mCacheEntry->SetMetaDataElement("charset-source", source.get()); +} + +void +nsWyciwygChannel::NotifyListener() +{ + nsCOMPtr<nsIStreamListener> listener; + nsCOMPtr<nsISupports> listenerContext; + + listener.swap(mListener); + listenerContext.swap(mListenerContext); + + if (listener) { + listener->OnStartRequest(this, listenerContext); + mIsPending = false; + listener->OnStopRequest(this, listenerContext, mStatus); + } else { + MOZ_ASSERT(false, "We must have the listener!"); + mIsPending = false; + } + + CloseCacheEntry(mStatus); + + // Remove ourselves from the load group. + if (mLoadGroup) { + mLoadGroup->RemoveRequest(this, nullptr, mStatus); + } +} + +// vim: ts=2 sw=2 diff --git a/netwerk/protocol/wyciwyg/nsWyciwygChannel.h b/netwerk/protocol/wyciwyg/nsWyciwygChannel.h new file mode 100644 index 000000000..26326e2a4 --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * 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 nsWyciwygChannel_h___ +#define nsWyciwygChannel_h___ + +#include "nsString.h" +#include "nsCOMPtr.h" + +#include "nsILoadInfo.h" +#include "nsIWyciwygChannel.h" +#include "nsIStreamListener.h" +#include "nsICacheEntryOpenCallback.h" +#include "PrivateBrowsingChannel.h" +#include "mozilla/BasePrincipal.h" + +class nsICacheEntry; +class nsICacheStorage; +class nsIInputStream; +class nsIInputStreamPump; +class nsILoadGroup; +class nsIOutputStream; +class nsIProgressEventSink; +class nsIURI; + +extern mozilla::LazyLogModule gWyciwygLog; + +//----------------------------------------------------------------------------- + +class nsWyciwygChannel final: public nsIWyciwygChannel, + public nsIStreamListener, + public nsICacheEntryOpenCallback, + public mozilla::net::PrivateBrowsingChannel<nsWyciwygChannel> +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIREQUEST + NS_DECL_NSICHANNEL + NS_DECL_NSIWYCIWYGCHANNEL + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSICACHEENTRYOPENCALLBACK + + // nsWyciwygChannel methods: + nsWyciwygChannel(); + + nsresult Init(nsIURI *uri); + +protected: + virtual ~nsWyciwygChannel(); + + nsresult ReadFromCache(); + nsresult EnsureWriteCacheEntry(); + nsresult GetCacheStorage(nsICacheStorage **_retval); + nsresult OpenCacheEntryForReading(nsIURI *aURI); + nsresult OpenCacheEntryForWriting(nsIURI *aURI); + + void WriteCharsetAndSourceToCache(int32_t aSource, + const nsCString& aCharset); + + void NotifyListener(); + + friend class mozilla::net::PrivateBrowsingChannel<nsWyciwygChannel>; + + enum EMode { + NONE, + WRITING, + READING + }; + + EMode mMode; + nsresult mStatus; + bool mIsPending; + bool mNeedToWriteCharset; + int32_t mCharsetSource; + nsCString mCharset; + int64_t mContentLength; + uint32_t mLoadFlags; + mozilla::NeckoOriginAttributes mOriginAttributes; + nsCOMPtr<nsIURI> mURI; + nsCOMPtr<nsIURI> mOriginalURI; + nsCOMPtr<nsISupports> mOwner; + nsCOMPtr<nsILoadInfo> mLoadInfo; + nsCOMPtr<nsIInterfaceRequestor> mCallbacks; + nsCOMPtr<nsIProgressEventSink> mProgressSink; + nsCOMPtr<nsILoadGroup> mLoadGroup; + nsCOMPtr<nsIStreamListener> mListener; + nsCOMPtr<nsISupports> mListenerContext; + + // reuse as much of this channel implementation as we can + nsCOMPtr<nsIInputStreamPump> mPump; + + // Cache related stuff + nsCOMPtr<nsICacheEntry> mCacheEntry; + nsCOMPtr<nsIOutputStream> mCacheOutputStream; + nsCOMPtr<nsIInputStream> mCacheInputStream; + + bool mNeedToSetSecurityInfo; + nsCOMPtr<nsISupports> mSecurityInfo; +}; + +/** + * Casting nsWyciwygChannel to nsISupports is ambiguous. + * This method handles that. + */ +inline nsISupports* +ToSupports(nsWyciwygChannel* p) +{ + return NS_ISUPPORTS_CAST(nsIStreamListener*, p); +} + +#endif /* nsWyciwygChannel_h___ */ diff --git a/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp b/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp new file mode 100644 index 000000000..4e1f7c22e --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp @@ -0,0 +1,158 @@ +/* -*- Mode: C++; 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/. */ + +#include "nsWyciwyg.h" +#include "nsWyciwygChannel.h" +#include "nsWyciwygProtocolHandler.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "plstr.h" +#include "nsIObserverService.h" +#include "mozIApplicationClearPrivateDataParams.h" +#include "nsIURI.h" + +#include "mozilla/net/NeckoChild.h" + +using namespace mozilla::net; +#include "mozilla/net/WyciwygChannelChild.h" + +//////////////////////////////////////////////////////////////////////////////// + +nsWyciwygProtocolHandler::nsWyciwygProtocolHandler() +{ + LOG(("Creating nsWyciwygProtocolHandler [this=%p].\n", this)); +} + +nsWyciwygProtocolHandler::~nsWyciwygProtocolHandler() +{ + LOG(("Deleting nsWyciwygProtocolHandler [this=%p]\n", this)); +} + +NS_IMPL_ISUPPORTS(nsWyciwygProtocolHandler, + nsIProtocolHandler) + +//////////////////////////////////////////////////////////////////////////////// +// nsIProtocolHandler methods: +//////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsWyciwygProtocolHandler::GetScheme(nsACString &result) +{ + result = "wyciwyg"; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygProtocolHandler::GetDefaultPort(int32_t *result) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsWyciwygProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) +{ + // don't override anything. + *_retval = false; + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygProtocolHandler::NewURI(const nsACString &aSpec, + const char *aCharset, // ignored + nsIURI *aBaseURI, + nsIURI **result) +{ + nsresult rv; + + nsCOMPtr<nsIURI> url = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = url->SetSpec(aSpec); + NS_ENSURE_SUCCESS(rv, rv); + + url.forget(result); + + return rv; +} + +NS_IMETHODIMP +nsWyciwygProtocolHandler::NewChannel2(nsIURI* url, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + if (mozilla::net::IsNeckoChild()) + mozilla::net::NeckoChild::InitNeckoChild(); + + NS_ENSURE_ARG_POINTER(url); + nsresult rv; + + nsCOMPtr<nsIWyciwygChannel> channel; + if (IsNeckoChild()) { + NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); + + WyciwygChannelChild *wcc = static_cast<WyciwygChannelChild *>( + gNeckoChild->SendPWyciwygChannelConstructor()); + if (!wcc) + return NS_ERROR_OUT_OF_MEMORY; + + channel = wcc; + rv = wcc->Init(url); + if (NS_FAILED(rv)) + PWyciwygChannelChild::Send__delete__(wcc); + } else + { + // If original channel used https, make sure PSM is initialized + // (this may be first channel to load during a session restore) + nsAutoCString path; + rv = url->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + int32_t slashIndex = path.FindChar('/', 2); + if (slashIndex == kNotFound) + return NS_ERROR_FAILURE; + if (path.Length() < (uint32_t)slashIndex + 1 + 5) + return NS_ERROR_FAILURE; + if (!PL_strncasecmp(path.get() + slashIndex + 1, "https", 5)) + net_EnsurePSMInit(); + + nsWyciwygChannel *wc = new nsWyciwygChannel(); + channel = wc; + rv = wc->Init(url); + } + + if (NS_FAILED(rv)) + return rv; + + // set the loadInfo on the new channel + rv = channel->SetLoadInfo(aLoadInfo); + if (NS_FAILED(rv)) { + return rv; + } + + channel.forget(result); + return NS_OK; +} + +NS_IMETHODIMP +nsWyciwygProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result) +{ + return NewChannel2(url, nullptr, result); +} + +NS_IMETHODIMP +nsWyciwygProtocolHandler::GetProtocolFlags(uint32_t *result) +{ + // Should this be an an nsINestedURI? We don't really want random webpages + // loading these URIs... + + // Note that using URI_INHERITS_SECURITY_CONTEXT here is OK -- untrusted code + // is not allowed to link to wyciwyg URIs and users shouldn't be able to get + // at them, and nsDocShell::InternalLoad forbids non-history loads of these + // URIs. And when loading from history we end up using the principal from + // the history entry, which we put there ourselves, so all is ok. + *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | + URI_INHERITS_SECURITY_CONTEXT; + return NS_OK; +} diff --git a/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.h b/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.h new file mode 100644 index 000000000..d3dbd5ead --- /dev/null +++ b/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * 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 nsWyciwygProtocolHandler_h___ +#define nsWyciwygProtocolHandler_h___ + +#include "nsIProtocolHandler.h" + +class nsWyciwygProtocolHandler : public nsIProtocolHandler +{ + virtual ~nsWyciwygProtocolHandler(); + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPROTOCOLHANDLER + + nsWyciwygProtocolHandler(); +}; + +#endif /* nsWyciwygProtocolHandler_h___ */ |