michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsWyciwyg.h" michael@0: #include "nsWyciwygChannel.h" michael@0: #include "nsILoadGroup.h" michael@0: #include "nsNetUtil.h" michael@0: #include "LoadContextInfo.h" michael@0: #include "nsICacheService.h" // only to initialize michael@0: #include "nsICacheStorageService.h" michael@0: #include "nsICacheStorage.h" michael@0: #include "nsICacheEntry.h" michael@0: #include "CacheObserver.h" michael@0: #include "nsCharsetSource.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsIInputStreamPump.h" michael@0: #include "nsIOutputStream.h" michael@0: #include "nsIProgressEventSink.h" michael@0: #include "nsIURI.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: typedef mozilla::net::LoadContextInfo LoadContextInfo; michael@0: michael@0: // Must release mChannel on the main thread michael@0: class nsWyciwygAsyncEvent : public nsRunnable { michael@0: public: michael@0: nsWyciwygAsyncEvent(nsWyciwygChannel *aChannel) : mChannel(aChannel) {} michael@0: michael@0: ~nsWyciwygAsyncEvent() michael@0: { michael@0: nsCOMPtr thread = do_GetMainThread(); michael@0: NS_WARN_IF_FALSE(thread, "Couldn't get the main thread!"); michael@0: if (thread) { michael@0: nsIWyciwygChannel *chan = static_cast(mChannel); michael@0: mozilla::unused << mChannel.forget(); michael@0: NS_ProxyRelease(thread, chan); michael@0: } michael@0: } michael@0: protected: michael@0: nsRefPtr mChannel; michael@0: }; michael@0: michael@0: class nsWyciwygSetCharsetandSourceEvent : public nsWyciwygAsyncEvent { michael@0: public: michael@0: nsWyciwygSetCharsetandSourceEvent(nsWyciwygChannel *aChannel) michael@0: : nsWyciwygAsyncEvent(aChannel) {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mChannel->SetCharsetAndSourceInternal(); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: class nsWyciwygWriteEvent : public nsWyciwygAsyncEvent { michael@0: public: michael@0: nsWyciwygWriteEvent(nsWyciwygChannel *aChannel, const nsAString &aData) michael@0: : nsWyciwygAsyncEvent(aChannel), mData(aData) {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mChannel->WriteToCacheEntryInternal(mData); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsString mData; michael@0: }; michael@0: michael@0: class nsWyciwygCloseEvent : public nsWyciwygAsyncEvent { michael@0: public: michael@0: nsWyciwygCloseEvent(nsWyciwygChannel *aChannel, nsresult aReason) michael@0: : nsWyciwygAsyncEvent(aChannel), mReason(aReason) {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mChannel->CloseCacheEntryInternal(mReason); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsresult mReason; michael@0: }; michael@0: michael@0: michael@0: // nsWyciwygChannel methods michael@0: nsWyciwygChannel::nsWyciwygChannel() michael@0: : mMode(NONE), michael@0: mStatus(NS_OK), michael@0: mIsPending(false), michael@0: mCharsetAndSourceSet(false), michael@0: mNeedToWriteCharset(false), michael@0: mCharsetSource(kCharsetUninitialized), michael@0: mContentLength(-1), michael@0: mLoadFlags(LOAD_NORMAL), michael@0: mAppId(NECKO_NO_APP_ID), michael@0: mInBrowser(false) michael@0: { michael@0: } michael@0: michael@0: nsWyciwygChannel::~nsWyciwygChannel() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsWyciwygChannel, michael@0: nsIChannel, michael@0: nsIRequest, michael@0: nsIStreamListener, michael@0: nsIRequestObserver, michael@0: nsICacheEntryOpenCallback, michael@0: nsIWyciwygChannel, michael@0: nsIPrivateBrowsingChannel) michael@0: michael@0: nsresult michael@0: nsWyciwygChannel::Init(nsIURI* uri) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(uri); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (!mozilla::net::CacheObserver::UseNewCache()) { michael@0: // Since nsWyciwygChannel can use the new cache API off the main thread michael@0: // and that API normally does this initiation, we need to take care michael@0: // of initiating the old cache service here manually. Will be removed michael@0: // with bug 913828. michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr service = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: } michael@0: michael@0: mURI = uri; michael@0: mOriginalURI = uri; michael@0: michael@0: nsCOMPtr serv = michael@0: do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = serv->GetIoTarget(getter_AddRefs(mCacheIOTarget)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: // nsIRequest methods: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetName(nsACString &aName) michael@0: { michael@0: return mURI->GetSpec(aName); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::IsPending(bool *aIsPending) michael@0: { michael@0: *aIsPending = mIsPending; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetStatus(nsresult *aStatus) michael@0: { michael@0: if (NS_SUCCEEDED(mStatus) && mPump) michael@0: mPump->GetStatus(aStatus); michael@0: else michael@0: *aStatus = mStatus; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::Cancel(nsresult status) michael@0: { michael@0: mStatus = status; michael@0: if (mPump) michael@0: mPump->Cancel(status); michael@0: // else we're waiting for OnCacheEntryAvailable michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::Suspend() michael@0: { michael@0: if (mPump) michael@0: mPump->Suspend(); michael@0: // XXX else, we'll ignore this ... and that's probably bad! michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::Resume() michael@0: { michael@0: if (mPump) michael@0: mPump->Resume(); michael@0: // XXX else, we'll ignore this ... and that's probably bad! michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) michael@0: { michael@0: *aLoadGroup = mLoadGroup; michael@0: NS_IF_ADDREF(*aLoadGroup); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) michael@0: { michael@0: if (!CanSetLoadGroup(aLoadGroup)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mLoadGroup = aLoadGroup; michael@0: NS_QueryNotificationCallbacks(mCallbacks, michael@0: mLoadGroup, michael@0: NS_GET_IID(nsIProgressEventSink), michael@0: getter_AddRefs(mProgressSink)); michael@0: mPrivateBrowsing = NS_UsePrivateBrowsing(this); michael@0: NS_GetAppInfo(this, &mAppId, &mInBrowser); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetLoadFlags(uint32_t aLoadFlags) michael@0: { michael@0: mLoadFlags = aLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetLoadFlags(uint32_t * aLoadFlags) michael@0: { michael@0: *aLoadFlags = mLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIChannel methods: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetOriginalURI(nsIURI* *aURI) michael@0: { michael@0: *aURI = mOriginalURI; michael@0: NS_ADDREF(*aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetOriginalURI(nsIURI* aURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: mOriginalURI = aURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetURI(nsIURI* *aURI) michael@0: { michael@0: *aURI = mURI; michael@0: NS_IF_ADDREF(*aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetOwner(nsISupports **aOwner) michael@0: { michael@0: NS_PRECONDITION(mOwner, "Must have a principal!"); michael@0: NS_ENSURE_STATE(mOwner); michael@0: michael@0: NS_ADDREF(*aOwner = mOwner); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetOwner(nsISupports* aOwner) michael@0: { michael@0: mOwner = aOwner; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) michael@0: { michael@0: *aCallbacks = mCallbacks.get(); michael@0: NS_IF_ADDREF(*aCallbacks); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) michael@0: { michael@0: if (!CanSetCallbacks(aNotificationCallbacks)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mCallbacks = aNotificationCallbacks; michael@0: NS_QueryNotificationCallbacks(mCallbacks, michael@0: mLoadGroup, michael@0: NS_GET_IID(nsIProgressEventSink), michael@0: getter_AddRefs(mProgressSink)); michael@0: michael@0: mPrivateBrowsing = NS_UsePrivateBrowsing(this); michael@0: NS_GetAppInfo(this, &mAppId, &mInBrowser); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) michael@0: { michael@0: NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetContentType(nsACString &aContentType) michael@0: { michael@0: aContentType.AssignLiteral(WYCIWYG_TYPE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetContentType(const nsACString &aContentType) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetContentCharset(nsACString &aContentCharset) michael@0: { michael@0: aContentCharset.Assign("UTF-16"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetContentCharset(const nsACString &aContentCharset) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetContentDisposition(uint32_t *aContentDisposition) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetContentDisposition(uint32_t aContentDisposition) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetContentLength(int64_t *aContentLength) michael@0: { michael@0: *aContentLength = mContentLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetContentLength(int64_t aContentLength) michael@0: { michael@0: mContentLength = aContentLength; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::Open(nsIInputStream ** aReturn) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) michael@0: { michael@0: LOG(("nsWyciwygChannel::AsyncOpen [this=%p]\n", this)); michael@0: MOZ_ASSERT(mMode == NONE, "nsWyciwygChannel already open"); michael@0: michael@0: NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); michael@0: NS_ENSURE_TRUE(mMode == NONE, NS_ERROR_IN_PROGRESS); michael@0: NS_ENSURE_ARG_POINTER(listener); michael@0: michael@0: mMode = READING; michael@0: michael@0: // open a cache entry for this channel... michael@0: // mIsPending set to true since OnCacheEntryAvailable may be called michael@0: // synchronously and fails when mIsPending found false. michael@0: mIsPending = true; michael@0: nsresult rv = OpenCacheEntry(mURI, nsICacheStorage::OPEN_READONLY | michael@0: nsICacheStorage::CHECK_MULTITHREADED); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("nsWyciwygChannel::OpenCacheEntry failed [rv=%x]\n", rv)); michael@0: mIsPending = false; michael@0: return rv; michael@0: } michael@0: michael@0: // There is no code path that would invoke the listener sooner than michael@0: // we get to this line in case OnCacheEntryAvailable is invoked michael@0: // synchronously. michael@0: mListener = listener; michael@0: mListenerContext = ctx; michael@0: michael@0: if (mLoadGroup) michael@0: mLoadGroup->AddRequest(this, nullptr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // nsIWyciwygChannel michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: nsWyciwygChannel::EnsureWriteCacheEntry() michael@0: { michael@0: MOZ_ASSERT(mMode == WRITING, "nsWyciwygChannel not open for writing"); michael@0: michael@0: if (!mCacheEntry) { michael@0: // OPEN_TRUNCATE will give us the entry instantly michael@0: nsresult rv = OpenCacheEntry(mURI, nsICacheStorage::OPEN_TRUNCATE); michael@0: if (NS_FAILED(rv) || !mCacheEntry) { michael@0: LOG((" could not synchronously open cache entry for write!")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::WriteToCacheEntry(const nsAString &aData) michael@0: { michael@0: if (mMode == READING) { michael@0: LOG(("nsWyciwygChannel::WriteToCacheEntry already open for reading")); michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: mMode = WRITING; michael@0: michael@0: if (mozilla::net::CacheObserver::UseNewCache()) { michael@0: nsresult rv = EnsureWriteCacheEntry(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return mCacheIOTarget->Dispatch(new nsWyciwygWriteEvent(this, aData), michael@0: NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: nsWyciwygChannel::WriteToCacheEntryInternal(const nsAString &aData) michael@0: { michael@0: LOG(("nsWyciwygChannel::WriteToCacheEntryInternal [this=%p]", this)); michael@0: NS_ASSERTION(IsOnCacheIOThread(), "wrong thread"); michael@0: michael@0: nsresult rv; michael@0: michael@0: // With the new cache entry this will just pass as a no-op since we michael@0: // are opening the entry in WriteToCacheEntry. michael@0: rv = EnsureWriteCacheEntry(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) { michael@0: rv = mCacheEntry->SetMetaDataElement("inhibit-persistent-caching", "1"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: if (mSecurityInfo) { michael@0: mCacheEntry->SetSecurityInfo(mSecurityInfo); michael@0: } michael@0: michael@0: if (mNeedToWriteCharset) { michael@0: WriteCharsetAndSourceToCache(mCharsetSource, mCharset); michael@0: mNeedToWriteCharset = false; michael@0: } michael@0: michael@0: uint32_t out; michael@0: if (!mCacheOutputStream) { michael@0: // Get the outputstream from the cache entry. michael@0: rv = mCacheEntry->OpenOutputStream(0, getter_AddRefs(mCacheOutputStream)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Write out a Byte Order Mark, so that we'll know if the data is michael@0: // BE or LE when we go to read it. michael@0: char16_t bom = 0xFEFF; michael@0: rv = mCacheOutputStream->Write((char *)&bom, sizeof(bom), &out); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return mCacheOutputStream->Write((const char *)PromiseFlatString(aData).get(), michael@0: aData.Length() * sizeof(char16_t), &out); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::CloseCacheEntry(nsresult reason) michael@0: { michael@0: return mCacheIOTarget->Dispatch(new nsWyciwygCloseEvent(this, reason), michael@0: NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: nsWyciwygChannel::CloseCacheEntryInternal(nsresult reason) michael@0: { michael@0: NS_ASSERTION(IsOnCacheIOThread(), "wrong thread"); michael@0: michael@0: if (mCacheEntry) { michael@0: LOG(("nsWyciwygChannel::CloseCacheEntryInternal [this=%p ]", this)); michael@0: mCacheOutputStream = 0; michael@0: mCacheInputStream = 0; michael@0: michael@0: if (NS_FAILED(reason)) michael@0: mCacheEntry->AsyncDoom(nullptr); // here we were calling Doom() ... michael@0: michael@0: mCacheEntry = 0; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetSecurityInfo(nsISupports *aSecurityInfo) michael@0: { michael@0: mSecurityInfo = aSecurityInfo; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::SetCharsetAndSource(int32_t aSource, michael@0: const nsACString& aCharset) michael@0: { michael@0: NS_ENSURE_ARG(!aCharset.IsEmpty()); michael@0: michael@0: mCharsetAndSourceSet = true; michael@0: mCharset = aCharset; michael@0: mCharsetSource = aSource; michael@0: michael@0: return mCacheIOTarget->Dispatch(new nsWyciwygSetCharsetandSourceEvent(this), michael@0: NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: nsWyciwygChannel::SetCharsetAndSourceInternal() michael@0: { michael@0: NS_ASSERTION(IsOnCacheIOThread(), "wrong thread"); michael@0: michael@0: if (mCacheEntry) { michael@0: WriteCharsetAndSourceToCache(mCharsetSource, mCharset); michael@0: } else { michael@0: mNeedToWriteCharset = true; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::GetCharsetAndSource(int32_t* aSource, nsACString& aCharset) michael@0: { michael@0: if (mCharsetAndSourceSet) { michael@0: *aSource = mCharsetSource; michael@0: aCharset = mCharset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mCacheEntry) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsXPIDLCString data; michael@0: mCacheEntry->GetMetaDataElement("charset", getter_Copies(data)); michael@0: michael@0: if (data.IsEmpty()) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsXPIDLCString sourceStr; michael@0: mCacheEntry->GetMetaDataElement("charset-source", getter_Copies(sourceStr)); michael@0: michael@0: int32_t source; michael@0: nsresult err; michael@0: source = sourceStr.ToInteger(&err); michael@0: if (NS_FAILED(err) || source == 0) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: *aSource = source; michael@0: aCharset = data; michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // nsICacheEntryOpenCallback michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appCache, michael@0: uint32_t* aResult) michael@0: { michael@0: *aResult = ENTRY_WANTED; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::OnCacheEntryAvailable(nsICacheEntry *aCacheEntry, michael@0: bool aNew, michael@0: nsIApplicationCache* aAppCache, michael@0: nsresult aStatus) michael@0: { michael@0: LOG(("nsWyciwygChannel::OnCacheEntryAvailable [this=%p entry=%p " michael@0: "new=%d status=%x]\n", this, aCacheEntry, aNew, aStatus)); michael@0: michael@0: // if the channel's already fired onStopRequest, michael@0: // then we should ignore this event. michael@0: if (!mIsPending && !aNew) michael@0: return NS_OK; michael@0: michael@0: // otherwise, we have to handle this event. michael@0: if (NS_SUCCEEDED(aStatus)) michael@0: mCacheEntry = aCacheEntry; michael@0: else if (NS_SUCCEEDED(mStatus)) michael@0: mStatus = aStatus; michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (NS_FAILED(mStatus)) { michael@0: LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); michael@0: rv = mStatus; michael@0: } michael@0: else if (!aNew) { // advance to the next state... michael@0: rv = ReadFromCache(); michael@0: } michael@0: michael@0: // a failure from Connect means that we have to abort the channel. michael@0: if (NS_FAILED(rv)) { michael@0: CloseCacheEntry(rv); michael@0: michael@0: if (!aNew) { michael@0: // Since OnCacheEntryAvailable can be called directly from AsyncOpen michael@0: // we must dispatch. michael@0: NS_DispatchToCurrentThread(NS_NewRunnableMethod( michael@0: this, &nsWyciwygChannel::NotifyListener)); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsWyciwygChannel::nsIStreamListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctx, michael@0: nsIInputStream *input, michael@0: uint64_t offset, uint32_t count) michael@0: { michael@0: LOG(("nsWyciwygChannel::OnDataAvailable [this=%p request=%x offset=%llu count=%u]\n", michael@0: this, request, offset, count)); michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = mListener->OnDataAvailable(this, mListenerContext, input, offset, count); michael@0: michael@0: // XXX handle 64-bit stuff for real michael@0: if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND)) michael@0: mProgressSink->OnProgress(this, nullptr, offset + count, michael@0: uint64_t(mContentLength)); michael@0: michael@0: return rv; // let the pump cancel on failure michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // nsIRequestObserver michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::OnStartRequest(nsIRequest *request, nsISupports *ctx) michael@0: { michael@0: LOG(("nsWyciwygChannel::OnStartRequest [this=%p request=%x\n", michael@0: this, request)); michael@0: michael@0: return mListener->OnStartRequest(this, mListenerContext); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsWyciwygChannel::OnStopRequest(nsIRequest *request, nsISupports *ctx, nsresult status) michael@0: { michael@0: LOG(("nsWyciwygChannel::OnStopRequest [this=%p request=%x status=%d\n", michael@0: this, request, status)); michael@0: michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mStatus = status; michael@0: michael@0: mListener->OnStopRequest(this, mListenerContext, mStatus); michael@0: mListener = 0; michael@0: mListenerContext = 0; michael@0: michael@0: if (mLoadGroup) michael@0: mLoadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: michael@0: CloseCacheEntry(mStatus); michael@0: mPump = 0; michael@0: mIsPending = false; michael@0: michael@0: // Drop notification callbacks to prevent cycles. michael@0: mCallbacks = 0; michael@0: mProgressSink = 0; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Helper functions michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: nsWyciwygChannel::OpenCacheEntry(nsIURI *aURI, michael@0: uint32_t aOpenFlags) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr cacheService = michael@0: do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool anonymous = mLoadFlags & LOAD_ANONYMOUS; michael@0: nsRefPtr loadInfo = mozilla::net::GetLoadContextInfo( michael@0: mPrivateBrowsing, mAppId, mInBrowser, anonymous); michael@0: michael@0: nsCOMPtr cacheStorage; michael@0: if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) michael@0: rv = cacheService->MemoryCacheStorage(loadInfo, getter_AddRefs(cacheStorage)); michael@0: else michael@0: rv = cacheService->DiskCacheStorage(loadInfo, false, getter_AddRefs(cacheStorage)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = cacheStorage->AsyncOpenURI(aURI, EmptyCString(), aOpenFlags, this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsWyciwygChannel::ReadFromCache() michael@0: { michael@0: LOG(("nsWyciwygChannel::ReadFromCache [this=%p] ", this)); michael@0: michael@0: NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE); michael@0: nsresult rv; michael@0: michael@0: // Get the stored security info michael@0: mCacheEntry->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); michael@0: michael@0: nsAutoCString tmpStr; michael@0: rv = mCacheEntry->GetMetaDataElement("inhibit-persistent-caching", michael@0: getter_Copies(tmpStr)); michael@0: if (NS_SUCCEEDED(rv) && tmpStr == NS_LITERAL_CSTRING("1")) michael@0: mLoadFlags |= INHIBIT_PERSISTENT_CACHING; michael@0: michael@0: // Get a transport to the cached data... michael@0: rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(mCacheInputStream)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: NS_ENSURE_TRUE(mCacheInputStream, NS_ERROR_UNEXPECTED); michael@0: michael@0: rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mCacheInputStream); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Pump the cache data downstream michael@0: return mPump->AsyncRead(this, nullptr); michael@0: } michael@0: michael@0: void michael@0: nsWyciwygChannel::WriteCharsetAndSourceToCache(int32_t aSource, michael@0: const nsCString& aCharset) michael@0: { michael@0: NS_ASSERTION(IsOnCacheIOThread(), "wrong thread"); michael@0: NS_PRECONDITION(mCacheEntry, "Better have cache entry!"); michael@0: michael@0: mCacheEntry->SetMetaDataElement("charset", aCharset.get()); michael@0: michael@0: nsAutoCString source; michael@0: source.AppendInt(aSource); michael@0: mCacheEntry->SetMetaDataElement("charset-source", source.get()); michael@0: } michael@0: michael@0: void michael@0: nsWyciwygChannel::NotifyListener() michael@0: { michael@0: if (mListener) { michael@0: mListener->OnStartRequest(this, mListenerContext); michael@0: mListener->OnStopRequest(this, mListenerContext, mStatus); michael@0: mListener = 0; michael@0: mListenerContext = 0; michael@0: } michael@0: michael@0: mIsPending = false; michael@0: michael@0: // Remove ourselves from the load group. michael@0: if (mLoadGroup) { michael@0: mLoadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsWyciwygChannel::IsOnCacheIOThread() michael@0: { michael@0: bool correctThread; michael@0: mCacheIOTarget->IsOnCurrentThread(&correctThread); michael@0: return correctThread; michael@0: } michael@0: michael@0: // vim: ts=2 sw=2