summaryrefslogtreecommitdiffstats
path: root/uriloader/base
diff options
context:
space:
mode:
Diffstat (limited to 'uriloader/base')
-rw-r--r--uriloader/base/moz.build33
-rw-r--r--uriloader/base/nsCURILoader.idl49
-rw-r--r--uriloader/base/nsDocLoader.cpp1516
-rw-r--r--uriloader/base/nsDocLoader.h335
-rw-r--r--uriloader/base/nsIContentHandler.idl35
-rw-r--r--uriloader/base/nsIDocumentLoader.idl36
-rw-r--r--uriloader/base/nsITransfer.idl106
-rw-r--r--uriloader/base/nsIURIContentListener.idl135
-rw-r--r--uriloader/base/nsIURILoader.idl140
-rw-r--r--uriloader/base/nsIWebProgress.idl153
-rw-r--r--uriloader/base/nsIWebProgressListener.idl425
-rw-r--r--uriloader/base/nsIWebProgressListener2.idl69
-rw-r--r--uriloader/base/nsURILoader.cpp966
-rw-r--r--uriloader/base/nsURILoader.h59
14 files changed, 4057 insertions, 0 deletions
diff --git a/uriloader/base/moz.build b/uriloader/base/moz.build
new file mode 100644
index 000000000..0d0f9a1c6
--- /dev/null
+++ b/uriloader/base/moz.build
@@ -0,0 +1,33 @@
+# -*- 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/.
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+XPIDL_SOURCES += [
+ 'nsCURILoader.idl',
+ 'nsIContentHandler.idl',
+ 'nsIDocumentLoader.idl',
+ 'nsITransfer.idl',
+ 'nsIURIContentListener.idl',
+ 'nsIURILoader.idl',
+ 'nsIWebProgress.idl',
+ 'nsIWebProgressListener.idl',
+ 'nsIWebProgressListener2.idl',
+]
+
+XPIDL_MODULE = 'uriloader'
+
+EXPORTS += [
+ 'nsDocLoader.h',
+ 'nsURILoader.h',
+]
+
+UNIFIED_SOURCES += [
+ 'nsDocLoader.cpp',
+ 'nsURILoader.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/uriloader/base/nsCURILoader.idl b/uriloader/base/nsCURILoader.idl
new file mode 100644
index 000000000..5d9d8f013
--- /dev/null
+++ b/uriloader/base/nsCURILoader.idl
@@ -0,0 +1,49 @@
+/* -*- Mode: IDL; tab-width: 3; 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 "nsIURILoader.idl"
+
+/*
+nsCURILoader implements:
+-------------------------
+nsIURILoader
+*/
+
+%{ C++
+// {9F6D5D40-90E7-11d3-AF93-00A024FFC08C} -
+#define NS_URI_LOADER_CID \
+{ 0x9f6d5d40, 0x90e7, 0x11d3, { 0xaf, 0x80, 0x00, 0xa0, 0x24, 0xff, 0xc0, 0x8c } }
+#define NS_URI_LOADER_CONTRACTID \
+"@mozilla.org/uriloader;1"
+
+/* 057b04d0-0ccf-11d2-beba-00805f8a66dc */
+#define NS_DOCUMENTLOADER_SERVICE_CID \
+ { 0x057b04d0, 0x0ccf, 0x11d2,{0xbe, 0xba, 0x00, 0x80, 0x5f, 0x8a, 0x66, 0xdc}}
+
+#define NS_DOCUMENTLOADER_SERVICE_CONTRACTID \
+"@mozilla.org/docloaderservice;1"
+
+#define NS_CONTENT_HANDLER_CONTRACTID "@mozilla.org/uriloader/content-handler;1"
+#define NS_CONTENT_HANDLER_CONTRACTID_PREFIX NS_CONTENT_HANDLER_CONTRACTID "?type="
+
+/**
+ * A category where content listeners can register. The name of the entry must
+ * be the content that this listener wants to handle, the value must be a
+ * contract ID for the listener. It will be created using createInstance (not
+ * getService).
+ *
+ * Listeners added this way are tried after the initial target of the load and
+ * after explicitly registered listeners (nsIURILoader::registerContentListener).
+ *
+ * These listeners must implement at least nsIURIContentListener (and
+ * nsISupports).
+ *
+ * @see nsICategoryManager
+ * @see nsIURIContentListener
+ */
+#define NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY "external-uricontentlisteners"
+
+%}
diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp
new file mode 100644
index 000000000..69885b93f
--- /dev/null
+++ b/uriloader/base/nsDocLoader.cpp
@@ -0,0 +1,1516 @@
+/* -*- 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 "nspr.h"
+#include "mozilla/Logging.h"
+
+#include "nsDocLoader.h"
+#include "nsCURILoader.h"
+#include "nsNetUtil.h"
+#include "nsIHttpChannel.h"
+#include "nsIWebProgressListener2.h"
+
+#include "nsIServiceManager.h"
+#include "nsXPIDLString.h"
+
+#include "nsIURL.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsWeakPtr.h"
+#include "nsAutoPtr.h"
+#include "nsQueryObject.h"
+
+#include "nsIDOMWindow.h"
+
+#include "nsIStringBundle.h"
+#include "nsIScriptSecurityManager.h"
+
+#include "nsITransport.h"
+#include "nsISocketTransport.h"
+#include "nsIDocShell.h"
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+#include "nsPresContext.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+
+using mozilla::DebugOnly;
+using mozilla::LogLevel;
+
+static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
+
+//
+// Log module for nsIDocumentLoader logging...
+//
+// To enable logging (see mozilla/Logging.h for full details):
+//
+// set MOZ_LOG=DocLoader:5
+// set MOZ_LOG_FILE=debug.log
+//
+// this enables LogLevel::Debug level information and places all output in
+// the file 'debug.log'.
+//
+mozilla::LazyLogModule gDocLoaderLog("DocLoader");
+
+
+#if defined(DEBUG)
+void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
+{
+ if (request)
+ request->GetName(name);
+ else
+ name.AssignLiteral("???");
+}
+#endif /* DEBUG */
+
+
+
+void
+nsDocLoader::RequestInfoHashInitEntry(PLDHashEntryHdr* entry,
+ const void* key)
+{
+ // Initialize the entry with placement new
+ new (entry) nsRequestInfo(key);
+}
+
+void
+nsDocLoader::RequestInfoHashClearEntry(PLDHashTable* table,
+ PLDHashEntryHdr* entry)
+{
+ nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
+ info->~nsRequestInfo();
+}
+
+// this is used for mListenerInfoList.Contains()
+template <>
+class nsDefaultComparator <nsDocLoader::nsListenerInfo, nsIWebProgressListener*> {
+ public:
+ bool Equals(const nsDocLoader::nsListenerInfo& aInfo,
+ nsIWebProgressListener* const& aListener) const {
+ nsCOMPtr<nsIWebProgressListener> listener =
+ do_QueryReferent(aInfo.mWeakListener);
+ return aListener == listener;
+ }
+};
+
+/* static */ const PLDHashTableOps nsDocLoader::sRequestInfoHashOps =
+{
+ PLDHashTable::HashVoidPtrKeyStub,
+ PLDHashTable::MatchEntryStub,
+ PLDHashTable::MoveEntryStub,
+ nsDocLoader::RequestInfoHashClearEntry,
+ nsDocLoader::RequestInfoHashInitEntry
+};
+
+nsDocLoader::nsDocLoader()
+ : mParent(nullptr),
+ mCurrentSelfProgress(0),
+ mMaxSelfProgress(0),
+ mCurrentTotalProgress(0),
+ mMaxTotalProgress(0),
+ mRequestInfoHash(&sRequestInfoHashOps, sizeof(nsRequestInfo)),
+ mCompletedTotalProgress(0),
+ mIsLoadingDocument(false),
+ mIsRestoringDocument(false),
+ mDontFlushLayout(false),
+ mIsFlushingLayout(false)
+{
+ ClearInternalProgress();
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: created.\n", this));
+}
+
+nsresult
+nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
+{
+ mParent = aParent;
+ return NS_OK;
+}
+
+nsresult
+nsDocLoader::Init()
+{
+ nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
+ if (NS_FAILED(rv)) return rv;
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
+
+ return NS_OK;
+}
+
+nsDocLoader::~nsDocLoader()
+{
+ /*
+ |ClearWeakReferences()| here is intended to prevent people holding weak references
+ from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
+ subsequent |Release()| will try to destroy me. At this point there should be only
+ weak references remaining (otherwise, we wouldn't be getting destroyed).
+
+ An alternative would be incrementing our refcount (consider it a compressed flag
+ saying "Don't re-destroy."). I haven't yet decided which is better. [scc]
+ */
+ // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
+ // this needed?
+ ClearWeakReferences();
+
+ Destroy();
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: deleted.\n", this));
+}
+
+
+/*
+ * Implementation of ISupports methods...
+ */
+NS_IMPL_ADDREF(nsDocLoader)
+NS_IMPL_RELEASE(nsDocLoader)
+
+NS_INTERFACE_MAP_BEGIN(nsDocLoader)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
+ NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
+ if (aIID.Equals(kThisImplCID))
+ foundInterface = static_cast<nsIDocumentLoader *>(this);
+ else
+NS_INTERFACE_MAP_END
+
+
+/*
+ * Implementation of nsIInterfaceRequestor methods...
+ */
+NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
+{
+ nsresult rv = NS_ERROR_NO_INTERFACE;
+
+ NS_ENSURE_ARG_POINTER(aSink);
+
+ if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
+ *aSink = mLoadGroup;
+ NS_IF_ADDREF((nsISupports*)*aSink);
+ rv = NS_OK;
+ } else {
+ rv = QueryInterface(aIID, aSink);
+ }
+
+ return rv;
+}
+
+/* static */
+already_AddRefed<nsDocLoader>
+nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
+{
+ RefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
+ return ret.forget();
+}
+
+/* static */
+nsresult
+nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
+{
+ nsresult rv;
+ nsCOMPtr<nsIDocumentLoader> docLoaderService =
+ do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
+ NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
+
+ return rootDocLoader->AddChildLoader(aDocLoader);
+}
+
+NS_IMETHODIMP
+nsDocLoader::Stop(void)
+{
+ nsresult rv = NS_OK;
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: Stop() called\n", this));
+
+ NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, Stop, ());
+
+ if (mLoadGroup)
+ rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
+
+ // Don't report that we're flushing layout so IsBusy returns false after a
+ // Stop call.
+ mIsFlushingLayout = false;
+
+ // Clear out mChildrenInOnload. We want to make sure to fire our
+ // onload at this point, and there's no issue with mChildrenInOnload
+ // after this, since mDocumentRequest will be null after the
+ // DocLoaderIsEmpty() call.
+ mChildrenInOnload.Clear();
+
+ // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
+ // etc, as needed. We could be getting into here from a subframe onload, in
+ // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
+ // happened yet, Canceling the loadgroup did nothing (because it was already
+ // empty), and we're about to start a new load (which is what triggered this
+ // Stop() call).
+
+ // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
+ // we wouldn't need the call here....
+
+ NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
+ DocLoaderIsEmpty(false);
+
+ return rv;
+}
+
+
+bool
+nsDocLoader::IsBusy()
+{
+ nsresult rv;
+
+ //
+ // A document loader is busy if either:
+ //
+ // 1. One of its children is in the middle of an onload handler. Note that
+ // the handler may have already removed this child from mChildList!
+ // 2. It is currently loading a document and either has parts of it still
+ // loading, or has a busy child docloader.
+ // 3. It's currently flushing layout in DocLoaderIsEmpty().
+ //
+
+ if (mChildrenInOnload.Count() || mIsFlushingLayout) {
+ return true;
+ }
+
+ /* Is this document loader busy? */
+ if (!mIsLoadingDocument) {
+ return false;
+ }
+
+ bool busy;
+ rv = mLoadGroup->IsPending(&busy);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ if (busy) {
+ return true;
+ }
+
+ /* check its child document loaders... */
+ uint32_t count = mChildList.Length();
+ for (uint32_t i=0; i < count; i++) {
+ nsIDocumentLoader* loader = ChildAt(i);
+
+ // This is a safe cast, because we only put nsDocLoader objects into the
+ // array
+ if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
+ return true;
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetContainer(nsISupports** aResult)
+{
+ NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
+{
+ nsresult rv = NS_OK;
+
+ if (nullptr == aResult) {
+ rv = NS_ERROR_NULL_POINTER;
+ } else {
+ *aResult = mLoadGroup;
+ NS_IF_ADDREF(*aResult);
+ }
+ return rv;
+}
+
+void
+nsDocLoader::Destroy()
+{
+ Stop();
+
+ // Remove the document loader from the parent list of loaders...
+ if (mParent)
+ {
+ DebugOnly<nsresult> rv = mParent->RemoveChildLoader(this);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveChildLoader failed");
+ }
+
+ // Release all the information about network requests...
+ ClearRequestInfoHash();
+
+ mListenerInfoList.Clear();
+ mListenerInfoList.Compact();
+
+ mDocumentRequest = nullptr;
+
+ if (mLoadGroup)
+ mLoadGroup->SetGroupObserver(nullptr);
+
+ DestroyChildren();
+}
+
+void
+nsDocLoader::DestroyChildren()
+{
+ uint32_t count = mChildList.Length();
+ // if the doc loader still has children...we need to enumerate the
+ // children and make them null out their back ptr to the parent doc
+ // loader
+ for (uint32_t i=0; i < count; i++)
+ {
+ nsIDocumentLoader* loader = ChildAt(i);
+
+ if (loader) {
+ // This is a safe cast, as we only put nsDocLoader objects into the
+ // array
+ DebugOnly<nsresult> rv =
+ static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetDocLoaderParent failed");
+ }
+ }
+ mChildList.Clear();
+}
+
+NS_IMETHODIMP
+nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
+{
+ // called each time a request is added to the group.
+
+ if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
+ nsAutoCString name;
+ request->GetName(name);
+
+ uint32_t count = 0;
+ if (mLoadGroup)
+ mLoadGroup->GetActiveCount(&count);
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
+ this, request, name.get(),
+ (mIsLoadingDocument ? "true" : "false"),
+ count));
+ }
+
+ bool bJustStartedLoading = false;
+
+ nsLoadFlags loadFlags = 0;
+ request->GetLoadFlags(&loadFlags);
+
+ if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
+ bJustStartedLoading = true;
+ mIsLoadingDocument = true;
+ ClearInternalProgress(); // only clear our progress if we are starting a new load....
+ }
+
+ //
+ // Create a new nsRequestInfo for the request that is starting to
+ // load...
+ //
+ AddRequestInfo(request);
+
+ //
+ // Only fire a doStartDocumentLoad(...) if the document loader
+ // has initiated a load... Otherwise, this notification has
+ // resulted from a request being added to the load group.
+ //
+ if (mIsLoadingDocument) {
+ if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
+ //
+ // Make sure that the document channel is null at this point...
+ // (unless its been redirected)
+ //
+ NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
+ !(mDocumentRequest.get()),
+ "Overwriting an existing document channel!");
+
+ // This request is associated with the entire document...
+ mDocumentRequest = request;
+ mLoadGroup->SetDefaultLoadRequest(request);
+
+ // Only fire the start document load notification for the first
+ // document URI... Do not fire it again for redirections
+ //
+ if (bJustStartedLoading) {
+ // Update the progress status state
+ mProgressStateFlags = nsIWebProgressListener::STATE_START;
+
+ // Fire the start document load notification
+ doStartDocumentLoad();
+ return NS_OK;
+ }
+ }
+ }
+
+ NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
+ "mDocumentRequest MUST be set for the duration of a page load!");
+
+ doStartURLLoad(request);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocLoader::OnStopRequest(nsIRequest *aRequest,
+ nsISupports *aCtxt,
+ nsresult aStatus)
+{
+ nsresult rv = NS_OK;
+
+ if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
+ nsAutoCString name;
+ aRequest->GetName(name);
+
+ uint32_t count = 0;
+ if (mLoadGroup)
+ mLoadGroup->GetActiveCount(&count);
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
+ this, aRequest, name.get(),
+ aStatus, (mIsLoadingDocument ? "true" : "false"),
+ count));
+ }
+
+ bool bFireTransferring = false;
+
+ //
+ // Set the Maximum progress to the same value as the current progress.
+ // Since the URI has finished loading, all the data is there. Also,
+ // this will allow a more accurate estimation of the max progress (in case
+ // the old value was unknown ie. -1)
+ //
+ nsRequestInfo *info = GetRequestInfo(aRequest);
+ if (info) {
+ // Null out mLastStatus now so we don't find it when looking for
+ // status from now on. This destroys the nsStatusInfo and hence
+ // removes it from our list.
+ info->mLastStatus = nullptr;
+
+ int64_t oldMax = info->mMaxProgress;
+
+ info->mMaxProgress = info->mCurrentProgress;
+
+ //
+ // If a request whose content-length was previously unknown has just
+ // finished loading, then use this new data to try to calculate a
+ // mMaxSelfProgress...
+ //
+ if ((oldMax < int64_t(0)) && (mMaxSelfProgress < int64_t(0))) {
+ mMaxSelfProgress = CalculateMaxProgress();
+ }
+
+ // As we know the total progress of this request now, save it to be part
+ // of CalculateMaxProgress() result. We need to remove the info from the
+ // hash, see bug 480713.
+ mCompletedTotalProgress += info->mMaxProgress;
+
+ //
+ // Determine whether a STATE_TRANSFERRING notification should be
+ // 'synthesized'.
+ //
+ // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
+ // nsRequestInfo::mCurrentProgress are both 0, then the
+ // STATE_TRANSFERRING notification has not been fired yet...
+ //
+ if ((oldMax == 0) && (info->mCurrentProgress == 0)) {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+
+ // Only fire a TRANSFERRING notification if the request is also a
+ // channel -- data transfer requires a nsIChannel!
+ //
+ if (channel) {
+ if (NS_SUCCEEDED(aStatus)) {
+ bFireTransferring = true;
+ }
+ //
+ // If the request failed (for any reason other than being
+ // redirected or retargeted), the TRANSFERRING notification can
+ // still be fired if a HTTP connection was established to a server.
+ //
+ else if (aStatus != NS_BINDING_REDIRECTED &&
+ aStatus != NS_BINDING_RETARGETED) {
+ //
+ // Only if the load has been targeted (see bug 268483)...
+ //
+ uint32_t lf;
+ channel->GetLoadFlags(&lf);
+ if (lf & nsIChannel::LOAD_TARGETED) {
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
+ if (httpChannel) {
+ uint32_t responseCode;
+ rv = httpChannel->GetResponseStatus(&responseCode);
+ if (NS_SUCCEEDED(rv)) {
+ //
+ // A valid server status indicates that a connection was
+ // established to the server... So, fire the notification
+ // even though a failure occurred later...
+ //
+ bFireTransferring = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (bFireTransferring) {
+ // Send a STATE_TRANSFERRING notification for the request.
+ int32_t flags;
+
+ flags = nsIWebProgressListener::STATE_TRANSFERRING |
+ nsIWebProgressListener::STATE_IS_REQUEST;
+ //
+ // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
+ //
+ if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
+ mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
+
+ // Send STATE_TRANSFERRING for the document too...
+ flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
+ }
+
+ FireOnStateChange(this, aRequest, flags, NS_OK);
+ }
+
+ //
+ // Fire the OnStateChange(...) notification for stop request
+ //
+ doStopURLLoad(aRequest, aStatus);
+
+ // Clear this request out of the hash to avoid bypass of FireOnStateChange
+ // when address of the request is reused.
+ RemoveRequestInfo(aRequest);
+
+ //
+ // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
+ // load. This will handle removing the request from our hashtable as needed.
+ //
+ if (mIsLoadingDocument) {
+ nsCOMPtr<nsIDocShell> ds = do_QueryInterface(static_cast<nsIRequestObserver*>(this));
+ bool doNotFlushLayout = false;
+ if (ds) {
+ // Don't do unexpected layout flushes while we're in process of restoring
+ // a document from the bfcache.
+ ds->GetRestoringDocument(&doNotFlushLayout);
+ }
+ DocLoaderIsEmpty(!doNotFlushLayout);
+ }
+
+ return NS_OK;
+}
+
+
+nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
+{
+ nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
+ if (NS_SUCCEEDED(rv)) {
+ rv = aChild->SetDocLoaderParent(nullptr);
+ }
+ return rv;
+}
+
+nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
+{
+ nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ if (NS_SUCCEEDED(rv)) {
+ rv = aChild->SetDocLoaderParent(this);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
+{
+ if (!mDocumentRequest) {
+ *aChannel = nullptr;
+ return NS_OK;
+ }
+
+ return CallQueryInterface(mDocumentRequest, aChannel);
+}
+
+
+void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
+{
+ if (mIsLoadingDocument) {
+ /* In the unimagineably rude circumstance that onload event handlers
+ triggered by this function actually kill the window ... ok, it's
+ not unimagineable; it's happened ... this deathgrip keeps this object
+ alive long enough to survive this function call. */
+ nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
+
+ // Don't flush layout if we're still busy.
+ if (IsBusy()) {
+ return;
+ }
+
+ NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
+ NS_ASSERTION(mDocumentRequest, "No Document Request!");
+
+ // The load group for this DocumentLoader is idle. Flush if we need to.
+ if (aFlushLayout && !mDontFlushLayout) {
+ nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ if (doc) {
+ // We start loads from style resolution, so we need to flush out style
+ // no matter what. If we have user fonts, we also need to flush layout,
+ // since the reflow is what starts font loads.
+ mozFlushType flushType = Flush_Style;
+ nsIPresShell* shell = doc->GetShell();
+ if (shell) {
+ // Be safe in case this presshell is in teardown now
+ nsPresContext* presContext = shell->GetPresContext();
+ if (presContext && presContext->GetUserFontSet()) {
+ flushType = Flush_Layout;
+ }
+ }
+ mDontFlushLayout = mIsFlushingLayout = true;
+ doc->FlushPendingNotifications(flushType);
+ mDontFlushLayout = mIsFlushingLayout = false;
+ }
+ }
+
+ // And now check whether we're really busy; that might have changed with
+ // the layout flush.
+ // Note, mDocumentRequest can be null if the flushing above re-entered this
+ // method.
+ if (!IsBusy() && mDocumentRequest) {
+ // Clear out our request info hash, now that our load really is done and
+ // we don't need it anymore to CalculateMaxProgress().
+ ClearInternalProgress();
+
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: Is now idle...\n", this));
+
+ nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
+
+ mDocumentRequest = nullptr;
+ mIsLoadingDocument = false;
+
+ // Update the progress status state - the document is done
+ mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
+
+
+ nsresult loadGroupStatus = NS_OK;
+ mLoadGroup->GetStatus(&loadGroupStatus);
+
+ //
+ // New code to break the circular reference between
+ // the load group and the docloader...
+ //
+ mLoadGroup->SetDefaultLoadRequest(nullptr);
+
+ // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
+ // it even if our onload handler removes us from the docloader tree.
+ RefPtr<nsDocLoader> parent = mParent;
+
+ // Note that if calling ChildEnteringOnload() on the parent returns false
+ // then calling our onload handler is not safe. That can only happen on
+ // OOM, so that's ok.
+ if (!parent || parent->ChildEnteringOnload(this)) {
+ // Do nothing with our state after firing the
+ // OnEndDocumentLoad(...). The document loader may be loading a *new*
+ // document - if LoadDocument() was called from a handler!
+ //
+ doStopDocumentLoad(docRequest, loadGroupStatus);
+
+ if (parent) {
+ parent->ChildDoneWithOnload(this);
+ }
+ }
+ }
+ }
+}
+
+void nsDocLoader::doStartDocumentLoad(void)
+{
+
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(mDocumentRequest, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
+ "\tURI: %s \n",
+ this, buffer.get()));
+#endif /* DEBUG */
+
+ // Fire an OnStatus(...) notification STATE_START. This indicates
+ // that the document represented by mDocumentRequest has started to
+ // load...
+ FireOnStateChange(this,
+ mDocumentRequest,
+ nsIWebProgressListener::STATE_START |
+ nsIWebProgressListener::STATE_IS_DOCUMENT |
+ nsIWebProgressListener::STATE_IS_REQUEST |
+ nsIWebProgressListener::STATE_IS_WINDOW |
+ nsIWebProgressListener::STATE_IS_NETWORK,
+ NS_OK);
+}
+
+void nsDocLoader::doStartURLLoad(nsIRequest *request)
+{
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(request, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
+ "\tURI: %s\n",
+ this, buffer.get()));
+#endif /* DEBUG */
+
+ FireOnStateChange(this,
+ request,
+ nsIWebProgressListener::STATE_START |
+ nsIWebProgressListener::STATE_IS_REQUEST,
+ NS_OK);
+}
+
+void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
+{
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(request, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
+ "\tURI: %s status=%x\n",
+ this, buffer.get(), aStatus));
+#endif /* DEBUG */
+
+ FireOnStateChange(this,
+ request,
+ nsIWebProgressListener::STATE_STOP |
+ nsIWebProgressListener::STATE_IS_REQUEST,
+ aStatus);
+
+ // Fire a status change message for the most recent unfinished
+ // request to make sure that the displayed status is not outdated.
+ if (!mStatusInfoList.isEmpty()) {
+ nsStatusInfo* statusInfo = mStatusInfoList.getFirst();
+ FireOnStatusChange(this, statusInfo->mRequest,
+ statusInfo->mStatusCode,
+ statusInfo->mStatusMessage.get());
+ }
+}
+
+void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
+ nsresult aStatus)
+{
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(request, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
+ "\tURI: %s Status=%x\n",
+ this, buffer.get(), aStatus));
+#endif /* DEBUG */
+
+ // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
+ // Grab our parent chain before doing that so we can still dispatch
+ // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
+ // the onload handlers rearrange the docshell tree.
+ WebProgressList list;
+ GatherAncestorWebProgresses(list);
+
+ //
+ // Fire an OnStateChange(...) notification indicating the the
+ // current document has finished loading...
+ //
+ int32_t flags = nsIWebProgressListener::STATE_STOP |
+ nsIWebProgressListener::STATE_IS_DOCUMENT;
+ for (uint32_t i = 0; i < list.Length(); ++i) {
+ list[i]->DoFireOnStateChange(this, request, flags, aStatus);
+ }
+
+ //
+ // Fire a final OnStateChange(...) notification indicating the the
+ // current document has finished loading...
+ //
+ flags = nsIWebProgressListener::STATE_STOP |
+ nsIWebProgressListener::STATE_IS_WINDOW |
+ nsIWebProgressListener::STATE_IS_NETWORK;
+ for (uint32_t i = 0; i < list.Length(); ++i) {
+ list[i]->DoFireOnStateChange(this, request, flags, aStatus);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// The following section contains support for nsIWebProgress and related stuff
+////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
+ uint32_t aNotifyMask)
+{
+ if (mListenerInfoList.Contains(aListener)) {
+ // The listener is already registered!
+ return NS_ERROR_FAILURE;
+ }
+
+ nsWeakPtr listener = do_GetWeakReference(aListener);
+ if (!listener) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return mListenerInfoList.AppendElement(nsListenerInfo(listener, aNotifyMask)) ?
+ NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
+{
+ return mListenerInfoList.RemoveElement(aListener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetDOMWindow(mozIDOMWindowProxy **aResult)
+{
+ return CallGetInterface(this, aResult);
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetDOMWindowID(uint64_t *aResult)
+{
+ *aResult = 0;
+
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ nsresult rv = GetDOMWindow(getter_AddRefs(window));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
+ NS_ENSURE_STATE(piwindow);
+
+ MOZ_ASSERT(piwindow->IsOuterWindow());
+ *aResult = piwindow->WindowID();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetIsTopLevel(bool *aResult)
+{
+ *aResult = false;
+
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ GetDOMWindow(getter_AddRefs(window));
+ if (window) {
+ nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
+ NS_ENSURE_STATE(piwindow);
+
+ nsCOMPtr<nsPIDOMWindowOuter> topWindow = piwindow->GetTop();
+ *aResult = piwindow == topWindow;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
+{
+ *aIsLoadingDocument = mIsLoadingDocument;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocLoader::GetLoadType(uint32_t *aLoadType)
+{
+ *aLoadType = 0;
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+int64_t nsDocLoader::GetMaxTotalProgress()
+{
+ int64_t newMaxTotal = 0;
+
+ uint32_t count = mChildList.Length();
+ for (uint32_t i=0; i < count; i++)
+ {
+ int64_t individualProgress = 0;
+ nsIDocumentLoader* docloader = ChildAt(i);
+ if (docloader)
+ {
+ // Cast is safe since all children are nsDocLoader too
+ individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
+ }
+ if (individualProgress < int64_t(0)) // if one of the elements doesn't know it's size
+ // then none of them do
+ {
+ newMaxTotal = int64_t(-1);
+ break;
+ }
+ else
+ newMaxTotal += individualProgress;
+ }
+
+ int64_t progress = -1;
+ if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
+ progress = newMaxTotal + mMaxSelfProgress;
+
+ return progress;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// The following section contains support for nsIProgressEventSink which is used to
+// pass progress and status between the actual request and the doc loader. The doc loader
+// then turns around and makes the right web progress calls based on this information.
+////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt,
+ int64_t aProgress, int64_t aProgressMax)
+{
+ int64_t progressDelta = 0;
+
+ //
+ // Update the RequestInfo entry with the new progress data
+ //
+ if (nsRequestInfo* info = GetRequestInfo(aRequest)) {
+ // Update info->mCurrentProgress before we call FireOnStateChange,
+ // since that can make the "info" pointer invalid.
+ int64_t oldCurrentProgress = info->mCurrentProgress;
+ progressDelta = aProgress - oldCurrentProgress;
+ info->mCurrentProgress = aProgress;
+
+ // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
+ if (!info->mUploading && (int64_t(0) == oldCurrentProgress) && (int64_t(0) == info->mMaxProgress)) {
+ //
+ // If we receive an OnProgress event from a toplevel channel that the URI Loader
+ // has not yet targeted, then we must suppress the event. This is necessary to
+ // ensure that webprogresslisteners do not get confused when the channel is
+ // finally targeted. See bug 257308.
+ //
+ nsLoadFlags lf = 0;
+ aRequest->GetLoadFlags(&lf);
+ if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
+ return NS_OK;
+ }
+
+ //
+ // This is the first progress notification for the entry. If
+ // (aMaxProgress != -1) then the content-length of the data is known,
+ // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
+ // that the content-length is no longer known.
+ //
+ if (aProgressMax != -1) {
+ mMaxSelfProgress += aProgressMax;
+ info->mMaxProgress = aProgressMax;
+ } else {
+ mMaxSelfProgress = int64_t(-1);
+ info->mMaxProgress = int64_t(-1);
+ }
+
+ // Send a STATE_TRANSFERRING notification for the request.
+ int32_t flags;
+
+ flags = nsIWebProgressListener::STATE_TRANSFERRING |
+ nsIWebProgressListener::STATE_IS_REQUEST;
+ //
+ // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
+ //
+ if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
+ mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
+
+ // Send STATE_TRANSFERRING for the document too...
+ flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
+ }
+
+ FireOnStateChange(this, aRequest, flags, NS_OK);
+ }
+
+ // Update our overall current progress count.
+ mCurrentSelfProgress += progressDelta;
+ }
+ //
+ // The request is not part of the load group, so ignore its progress
+ // information...
+ //
+ else {
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(aRequest, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p OOPS - No Request Info for: %s\n",
+ this, buffer.get()));
+#endif /* DEBUG */
+
+ return NS_OK;
+ }
+
+ //
+ // Fire progress notifications out to any registered nsIWebProgressListeners
+ //
+ FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
+ mCurrentTotalProgress, mMaxTotalProgress);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
+ nsresult aStatus, const char16_t* aStatusArg)
+{
+ //
+ // Fire progress notifications out to any registered nsIWebProgressListeners
+ //
+ if (aStatus != NS_OK) {
+ // Remember the current status for this request
+ nsRequestInfo *info;
+ info = GetRequestInfo(aRequest);
+ if (info) {
+ bool uploading = (aStatus == NS_NET_STATUS_WRITING ||
+ aStatus == NS_NET_STATUS_SENDING_TO);
+ // If switching from uploading to downloading (or vice versa), then we
+ // need to reset our progress counts. This is designed with HTTP form
+ // submission in mind, where an upload is performed followed by download
+ // of possibly several documents.
+ if (info->mUploading != uploading) {
+ mCurrentSelfProgress = mMaxSelfProgress = 0;
+ mCurrentTotalProgress = mMaxTotalProgress = 0;
+ mCompletedTotalProgress = 0;
+ info->mUploading = uploading;
+ info->mCurrentProgress = 0;
+ info->mMaxProgress = 0;
+ }
+ }
+
+ nsCOMPtr<nsIStringBundleService> sbs =
+ mozilla::services::GetStringBundleService();
+ if (!sbs)
+ return NS_ERROR_FAILURE;
+ nsXPIDLString msg;
+ nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
+ getter_Copies(msg));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Keep around the message. In case a request finishes, we need to make sure
+ // to send the status message of another request to our user to that we
+ // don't display, for example, "Transferring" messages for requests that are
+ // already done.
+ if (info) {
+ if (!info->mLastStatus) {
+ info->mLastStatus = new nsStatusInfo(aRequest);
+ } else {
+ // We're going to move it to the front of the list, so remove
+ // it from wherever it is now.
+ info->mLastStatus->remove();
+ }
+ info->mLastStatus->mStatusMessage = msg;
+ info->mLastStatus->mStatusCode = aStatus;
+ // Put the info at the front of the list
+ mStatusInfoList.insertFront(info->mLastStatus);
+ }
+ FireOnStatusChange(this, aRequest, aStatus, msg);
+ }
+ return NS_OK;
+}
+
+void nsDocLoader::ClearInternalProgress()
+{
+ ClearRequestInfoHash();
+
+ mCurrentSelfProgress = mMaxSelfProgress = 0;
+ mCurrentTotalProgress = mMaxTotalProgress = 0;
+ mCompletedTotalProgress = 0;
+
+ mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
+}
+
+/**
+ * |_code| is executed for every listener matching |_flag|
+ * |listener| should be used inside |_code| as the nsIWebProgressListener var.
+ */
+#define NOTIFY_LISTENERS(_flag, _code) \
+PR_BEGIN_MACRO \
+ nsCOMPtr<nsIWebProgressListener> listener; \
+ ListenerArray::BackwardIterator iter(mListenerInfoList); \
+ while (iter.HasMore()) { \
+ nsListenerInfo &info = iter.GetNext(); \
+ if (!(info.mNotifyMask & (_flag))) { \
+ continue; \
+ } \
+ listener = do_QueryReferent(info.mWeakListener); \
+ if (!listener) { \
+ iter.Remove(); \
+ continue; \
+ } \
+ _code \
+ } \
+ mListenerInfoList.Compact(); \
+PR_END_MACRO
+
+void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
+ nsIRequest *request,
+ int64_t aProgress,
+ int64_t aProgressMax,
+ int64_t aProgressDelta,
+ int64_t aTotalProgress,
+ int64_t aMaxTotalProgress)
+{
+ if (mIsLoadingDocument) {
+ mCurrentTotalProgress += aProgressDelta;
+ mMaxTotalProgress = GetMaxTotalProgress();
+
+ aTotalProgress = mCurrentTotalProgress;
+ aMaxTotalProgress = mMaxTotalProgress;
+ }
+
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(request, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
+ this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
+#endif /* DEBUG */
+
+ NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_PROGRESS,
+ // XXX truncates 64-bit to 32-bit
+ listener->OnProgressChange(aLoadInitiator,request,
+ int32_t(aProgress), int32_t(aProgressMax),
+ int32_t(aTotalProgress), int32_t(aMaxTotalProgress));
+ );
+
+ // Pass the notification up to the parent...
+ if (mParent) {
+ mParent->FireOnProgressChange(aLoadInitiator, request,
+ aProgress, aProgressMax,
+ aProgressDelta,
+ aTotalProgress, aMaxTotalProgress);
+ }
+}
+
+void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
+{
+ for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
+ aList.AppendElement(loader);
+ }
+}
+
+void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
+ nsIRequest *aRequest,
+ int32_t aStateFlags,
+ nsresult aStatus)
+{
+ WebProgressList list;
+ GatherAncestorWebProgresses(list);
+ for (uint32_t i = 0; i < list.Length(); ++i) {
+ list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
+ }
+}
+
+void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
+ nsIRequest * const aRequest,
+ int32_t &aStateFlags,
+ const nsresult aStatus)
+{
+ //
+ // Remove the STATE_IS_NETWORK bit if necessary.
+ //
+ // The rule is to remove this bit, if the notification has been passed
+ // up from a child WebProgress, and the current WebProgress is already
+ // active...
+ //
+ if (mIsLoadingDocument &&
+ (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
+ (this != aProgress)) {
+ aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
+ }
+
+ // Add the STATE_RESTORING bit if necessary.
+ if (mIsRestoringDocument)
+ aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
+
+#if defined(DEBUG)
+ nsAutoCString buffer;
+
+ GetURIStringFromRequest(aRequest, buffer);
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: Status (%s): code: %x\n",
+ this, buffer.get(), aStateFlags));
+#endif /* DEBUG */
+
+ NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
+
+ NOTIFY_LISTENERS(((aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL),
+ listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
+ );
+}
+
+
+
+void
+nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI *aUri,
+ uint32_t aFlags)
+{
+ NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_LOCATION,
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug, ("DocLoader [%p] calling %p->OnLocationChange", this, listener.get()));
+ listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags);
+ );
+
+ // Pass the notification up to the parent...
+ if (mParent) {
+ mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
+ }
+}
+
+void
+nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_STATUS,
+ listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
+ );
+
+ // Pass the notification up to the parent...
+ if (mParent) {
+ mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
+ }
+}
+
+bool
+nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
+ nsIURI *aURI,
+ int32_t aDelay,
+ bool aSameURI)
+{
+ /*
+ * Returns true if the refresh may proceed,
+ * false if the refresh should be blocked.
+ */
+ bool allowRefresh = true;
+
+ NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_REFRESH,
+ nsCOMPtr<nsIWebProgressListener2> listener2 =
+ do_QueryReferent(info.mWeakListener);
+ if (!listener2)
+ continue;
+
+ bool listenerAllowedRefresh;
+ nsresult listenerRV = listener2->OnRefreshAttempted(
+ aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
+ if (NS_FAILED(listenerRV))
+ continue;
+
+ allowRefresh = allowRefresh && listenerAllowedRefresh;
+ );
+
+ // Pass the notification up to the parent...
+ if (mParent) {
+ allowRefresh = allowRefresh &&
+ mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
+ }
+
+ return allowRefresh;
+}
+
+nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
+{
+ if (!mRequestInfoHash.Add(aRequest, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
+{
+ mRequestInfoHash.Remove(aRequest);
+}
+
+nsDocLoader::nsRequestInfo* nsDocLoader::GetRequestInfo(nsIRequest* aRequest)
+{
+ return static_cast<nsRequestInfo*>(mRequestInfoHash.Search(aRequest));
+}
+
+void nsDocLoader::ClearRequestInfoHash(void)
+{
+ mRequestInfoHash.Clear();
+}
+
+int64_t nsDocLoader::CalculateMaxProgress()
+{
+ int64_t max = mCompletedTotalProgress;
+ for (auto iter = mRequestInfoHash.Iter(); !iter.Done(); iter.Next()) {
+ auto info = static_cast<const nsRequestInfo*>(iter.Get());
+
+ if (info->mMaxProgress < info->mCurrentProgress) {
+ return int64_t(-1);
+ }
+ max += info->mMaxProgress;
+ }
+ return max;
+}
+
+NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+ nsIChannel *aNewChannel,
+ uint32_t aFlags,
+ nsIAsyncVerifyRedirectCallback *cb)
+{
+ if (aOldChannel)
+ {
+ nsLoadFlags loadFlags = 0;
+ int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
+ nsIWebProgressListener::STATE_IS_REQUEST;
+
+ aOldChannel->GetLoadFlags(&loadFlags);
+ // If the document channel is being redirected, then indicate that the
+ // document is being redirected in the notification...
+ if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
+ {
+ stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
+
+#if defined(DEBUG)
+ nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
+ NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
+#endif /* DEBUG */
+ }
+
+ OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
+ FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
+ }
+
+ cb->OnRedirectVerifyCallback(NS_OK);
+ return NS_OK;
+}
+
+/*
+ * Implementation of nsISecurityEventSink method...
+ */
+
+NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
+ uint32_t aState)
+{
+ //
+ // Fire progress notifications out to any registered nsIWebProgressListeners.
+ //
+
+ nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
+ nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
+
+ NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_SECURITY,
+ listener->OnSecurityChange(webProgress, request, aState);
+ );
+
+ // Pass the notification up to the parent...
+ if (mParent) {
+ mParent->OnSecurityChange(aContext, aState);
+ }
+ return NS_OK;
+}
+
+/*
+ * Implementation of nsISupportsPriority methods...
+ *
+ * The priority of the DocLoader _is_ the priority of its LoadGroup.
+ *
+ * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
+ * go away.
+ */
+
+NS_IMETHODIMP nsDocLoader::GetPriority(int32_t *aPriority)
+{
+ nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
+ if (p)
+ return p->GetPriority(aPriority);
+
+ *aPriority = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocLoader::SetPriority(int32_t aPriority)
+{
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
+
+ nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
+ if (p)
+ p->SetPriority(aPriority);
+
+ NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
+ SetPriority, (aPriority));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocLoader::AdjustPriority(int32_t aDelta)
+{
+ MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
+ ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
+
+ nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
+ if (p)
+ p->AdjustPriority(aDelta);
+
+ NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
+ AdjustPriority, (aDelta));
+
+ return NS_OK;
+}
+
+
+
+
+#if 0
+void nsDocLoader::DumpChannelInfo()
+{
+ nsChannelInfo *info;
+ int32_t i, count;
+ int32_t current=0, max=0;
+
+
+ printf("==== DocLoader=%x\n", this);
+
+ count = mChannelInfoList.Count();
+ for(i=0; i<count; i++) {
+ info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
+
+#if defined(DEBUG)
+ nsAutoCString buffer;
+ nsresult rv = NS_OK;
+ if (info->mURI) {
+ rv = info->mURI->GetSpec(buffer);
+ }
+
+ printf(" [%d] current=%d max=%d [%s]\n", i,
+ info->mCurrentProgress,
+ info->mMaxProgress, buffer.get());
+#endif /* DEBUG */
+
+ current += info->mCurrentProgress;
+ if (max >= 0) {
+ if (info->mMaxProgress < info->mCurrentProgress) {
+ max = -1;
+ } else {
+ max += info->mMaxProgress;
+ }
+ }
+ }
+
+ printf("\nCurrent=%d Total=%d\n====\n", current, max);
+}
+#endif /* 0 */
diff --git a/uriloader/base/nsDocLoader.h b/uriloader/base/nsDocLoader.h
new file mode 100644
index 000000000..481b1397b
--- /dev/null
+++ b/uriloader/base/nsDocLoader.h
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 2; 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 nsDocLoader_h__
+#define nsDocLoader_h__
+
+#include "nsIDocumentLoader.h"
+#include "nsIWebProgress.h"
+#include "nsIWebProgressListener.h"
+#include "nsIRequestObserver.h"
+#include "nsWeakReference.h"
+#include "nsILoadGroup.h"
+#include "nsCOMArray.h"
+#include "nsTObserverArray.h"
+#include "nsString.h"
+#include "nsIChannel.h"
+#include "nsIProgressEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIChannelEventSink.h"
+#include "nsISecurityEventSink.h"
+#include "nsISupportsPriority.h"
+#include "nsCOMPtr.h"
+#include "PLDHashTable.h"
+#include "nsAutoPtr.h"
+
+#include "mozilla/LinkedList.h"
+
+/****************************************************************************
+ * nsDocLoader implementation...
+ ****************************************************************************/
+
+#define NS_THIS_DOCLOADER_IMPL_CID \
+ { /* b4ec8387-98aa-4c08-93b6-6d23069c06f2 */ \
+ 0xb4ec8387, \
+ 0x98aa, \
+ 0x4c08, \
+ {0x93, 0xb6, 0x6d, 0x23, 0x06, 0x9c, 0x06, 0xf2} \
+ }
+
+class nsDocLoader : public nsIDocumentLoader,
+ public nsIRequestObserver,
+ public nsSupportsWeakReference,
+ public nsIProgressEventSink,
+ public nsIWebProgress,
+ public nsIInterfaceRequestor,
+ public nsIChannelEventSink,
+ public nsISecurityEventSink,
+ public nsISupportsPriority
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_THIS_DOCLOADER_IMPL_CID)
+
+ nsDocLoader();
+
+ virtual MOZ_MUST_USE nsresult Init();
+
+ static already_AddRefed<nsDocLoader> GetAsDocLoader(nsISupports* aSupports);
+ // Needed to deal with ambiguous inheritance from nsISupports...
+ static nsISupports* GetAsSupports(nsDocLoader* aDocLoader) {
+ return static_cast<nsIDocumentLoader*>(aDocLoader);
+ }
+
+ // Add aDocLoader as a child to the docloader service.
+ static MOZ_MUST_USE nsresult AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOCUMENTLOADER
+
+ // nsIProgressEventSink
+ NS_DECL_NSIPROGRESSEVENTSINK
+
+ NS_DECL_NSISECURITYEVENTSINK
+
+ // nsIRequestObserver methods: (for observing the load group)
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSIWEBPROGRESS
+
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSICHANNELEVENTSINK
+ NS_DECL_NSISUPPORTSPRIORITY
+
+ // Implementation specific methods...
+
+ // Remove aChild from our childlist. This nulls out the child's mParent
+ // pointer.
+ MOZ_MUST_USE nsresult RemoveChildLoader(nsDocLoader *aChild);
+ // Add aChild to our child list. This will set aChild's mParent pointer to
+ // |this|.
+ MOZ_MUST_USE nsresult AddChildLoader(nsDocLoader* aChild);
+ nsDocLoader* GetParent() const { return mParent; }
+
+ struct nsListenerInfo {
+ nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask)
+ : mWeakListener(aListener),
+ mNotifyMask(aNotifyMask)
+ {
+ }
+
+ // Weak pointer for the nsIWebProgressListener...
+ nsWeakPtr mWeakListener;
+
+ // Mask indicating which notifications the listener wants to receive.
+ unsigned long mNotifyMask;
+ };
+
+protected:
+ virtual ~nsDocLoader();
+
+ virtual MOZ_MUST_USE nsresult SetDocLoaderParent(nsDocLoader * aLoader);
+
+ bool IsBusy();
+
+ void Destroy();
+ virtual void DestroyChildren();
+
+ nsIDocumentLoader* ChildAt(int32_t i) {
+ return mChildList.SafeElementAt(i, nullptr);
+ }
+
+ void FireOnProgressChange(nsDocLoader* aLoadInitiator,
+ nsIRequest *request,
+ int64_t aProgress,
+ int64_t aProgressMax,
+ int64_t aProgressDelta,
+ int64_t aTotalProgress,
+ int64_t aMaxTotalProgress);
+
+ // This should be at least 2 long since we'll generally always
+ // have the current page and the global docloader on the ancestor
+ // list. But to deal with frames it's better to make it a bit
+ // longer, and it's always a stack temporary so there's no real
+ // reason not to.
+ typedef AutoTArray<RefPtr<nsDocLoader>, 8> WebProgressList;
+ void GatherAncestorWebProgresses(WebProgressList& aList);
+
+ void FireOnStateChange(nsIWebProgress *aProgress,
+ nsIRequest* request,
+ int32_t aStateFlags,
+ nsresult aStatus);
+
+ // The guts of FireOnStateChange, but does not call itself on our ancestors.
+ // The arguments that are const are const so that we can detect cases when
+ // DoFireOnStateChange wants to propagate changes to the next web progress
+ // at compile time. The ones that are not, are references so that such
+ // changes can be propagated.
+ void DoFireOnStateChange(nsIWebProgress * const aProgress,
+ nsIRequest* const request,
+ int32_t &aStateFlags,
+ const nsresult aStatus);
+
+ void FireOnStatusChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage);
+
+ void FireOnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI *aUri,
+ uint32_t aFlags);
+
+ MOZ_MUST_USE bool RefreshAttempted(nsIWebProgress* aWebProgress,
+ nsIURI *aURI,
+ int32_t aDelay,
+ bool aSameURI);
+
+ // this function is overridden by the docshell, it is provided so that we
+ // can pass more information about redirect state (the normal OnStateChange
+ // doesn't get the new channel).
+ // @param aRedirectFlags The flags being sent to OnStateChange that
+ // indicate the type of redirect.
+ // @param aStateFlags The channel flags normally sent to OnStateChange.
+ virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
+ nsIChannel* aNewChannel,
+ uint32_t aRedirectFlags,
+ uint32_t aStateFlags) {}
+
+ void doStartDocumentLoad();
+ void doStartURLLoad(nsIRequest *request);
+ void doStopURLLoad(nsIRequest *request, nsresult aStatus);
+ void doStopDocumentLoad(nsIRequest *request, nsresult aStatus);
+
+ // Inform a parent docloader that aChild is about to call its onload
+ // handler.
+ MOZ_MUST_USE bool ChildEnteringOnload(nsIDocumentLoader* aChild) {
+ // It's ok if we're already in the list -- we'll just be in there twice
+ // and then the RemoveObject calls from ChildDoneWithOnload will remove
+ // us.
+ return mChildrenInOnload.AppendObject(aChild);
+ }
+
+ // Inform a parent docloader that aChild is done calling its onload
+ // handler.
+ void ChildDoneWithOnload(nsIDocumentLoader* aChild) {
+ mChildrenInOnload.RemoveObject(aChild);
+ DocLoaderIsEmpty(true);
+ }
+
+protected:
+ struct nsStatusInfo : public mozilla::LinkedListElement<nsStatusInfo>
+ {
+ nsString mStatusMessage;
+ nsresult mStatusCode;
+ // Weak mRequest is ok; we'll be told if it decides to go away.
+ nsIRequest * const mRequest;
+
+ explicit nsStatusInfo(nsIRequest* aRequest) :
+ mRequest(aRequest)
+ {
+ MOZ_COUNT_CTOR(nsStatusInfo);
+ }
+ ~nsStatusInfo()
+ {
+ MOZ_COUNT_DTOR(nsStatusInfo);
+ }
+ };
+
+ struct nsRequestInfo : public PLDHashEntryHdr
+ {
+ explicit nsRequestInfo(const void* key)
+ : mKey(key), mCurrentProgress(0), mMaxProgress(0), mUploading(false)
+ , mLastStatus(nullptr)
+ {
+ MOZ_COUNT_CTOR(nsRequestInfo);
+ }
+
+ ~nsRequestInfo()
+ {
+ MOZ_COUNT_DTOR(nsRequestInfo);
+ }
+
+ nsIRequest* Request() {
+ return static_cast<nsIRequest*>(const_cast<void*>(mKey));
+ }
+
+ const void* mKey; // Must be first for the PLDHashTable stubs to work
+ int64_t mCurrentProgress;
+ int64_t mMaxProgress;
+ bool mUploading;
+
+ nsAutoPtr<nsStatusInfo> mLastStatus;
+ };
+
+ static void RequestInfoHashInitEntry(PLDHashEntryHdr* entry, const void* key);
+ static void RequestInfoHashClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry);
+
+ // IMPORTANT: The ownership implicit in the following member
+ // variables has been explicitly checked and set using nsCOMPtr
+ // for owning pointers and raw COM interface pointers for weak
+ // (ie, non owning) references. If you add any members to this
+ // class, please make the ownership explicit (pinkerton, scc).
+
+ nsCOMPtr<nsIRequest> mDocumentRequest; // [OWNER] ???compare with document
+
+ nsDocLoader* mParent; // [WEAK]
+
+ typedef nsAutoTObserverArray<nsListenerInfo, 8> ListenerArray;
+ ListenerArray mListenerInfoList;
+
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ // We hold weak refs to all our kids
+ nsTObserverArray<nsDocLoader*> mChildList;
+
+ // The following member variables are related to the new nsIWebProgress
+ // feedback interfaces that travis cooked up.
+ int32_t mProgressStateFlags;
+
+ int64_t mCurrentSelfProgress;
+ int64_t mMaxSelfProgress;
+
+ int64_t mCurrentTotalProgress;
+ int64_t mMaxTotalProgress;
+
+ PLDHashTable mRequestInfoHash;
+ int64_t mCompletedTotalProgress;
+
+ mozilla::LinkedList<nsStatusInfo> mStatusInfoList;
+
+ /*
+ * This flag indicates that the loader is loading a document. It is set
+ * from the call to LoadDocument(...) until the OnConnectionsComplete(...)
+ * notification is fired...
+ */
+ bool mIsLoadingDocument;
+
+ /* Flag to indicate that we're in the process of restoring a document. */
+ bool mIsRestoringDocument;
+
+ /* Flag to indicate that we're in the process of flushing layout
+ under DocLoaderIsEmpty() and should not do another flush. */
+ bool mDontFlushLayout;
+
+ /* Flag to indicate whether we should consider ourselves as currently
+ flushing layout for the purposes of IsBusy. For example, if Stop has
+ been called then IsBusy should return false even if we are still
+ flushing. */
+ bool mIsFlushingLayout;
+
+private:
+ static const PLDHashTableOps sRequestInfoHashOps;
+
+ // A list of kids that are in the middle of their onload calls and will let
+ // us know once they're done. We don't want to fire onload for "normal"
+ // DocLoaderIsEmpty calls (those coming from requests finishing in our
+ // loadgroup) unless this is empty.
+ nsCOMArray<nsIDocumentLoader> mChildrenInOnload;
+
+ // DocLoaderIsEmpty should be called whenever the docloader may be empty.
+ // This method is idempotent and does nothing if the docloader is not in
+ // fact empty. This method _does_ make sure that layout is flushed if our
+ // loadgroup has no active requests before checking for "real" emptiness if
+ // aFlushLayout is true.
+ void DocLoaderIsEmpty(bool aFlushLayout);
+
+ int64_t GetMaxTotalProgress();
+
+ nsresult AddRequestInfo(nsIRequest* aRequest);
+ void RemoveRequestInfo(nsIRequest* aRequest);
+ nsRequestInfo *GetRequestInfo(nsIRequest* aRequest);
+ void ClearRequestInfoHash();
+ int64_t CalculateMaxProgress();
+/// void DumpChannelInfo(void);
+
+ // used to clear our internal progress state between loads...
+ void ClearInternalProgress();
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsDocLoader, NS_THIS_DOCLOADER_IMPL_CID)
+
+#endif /* nsDocLoader_h__ */
diff --git a/uriloader/base/nsIContentHandler.idl b/uriloader/base/nsIContentHandler.idl
new file mode 100644
index 000000000..31ef87a8b
--- /dev/null
+++ b/uriloader/base/nsIContentHandler.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "nsISupports.idl"
+interface nsIRequest;
+interface nsIInterfaceRequestor;
+
+[scriptable, uuid(49439df2-b3d2-441c-bf62-866bdaf56fd2)]
+interface nsIContentHandler : nsISupports
+{
+ /**
+ * Tells the content handler to take over handling the content. If this
+ * function succeeds, the URI Loader will leave this request alone, ignoring
+ * progress notifications. Failure of this method will cause the request to be
+ * cancelled, unless the error code is NS_ERROR_WONT_HANDLE_CONTENT (see
+ * below).
+ *
+ * @param aWindowContext
+ * Window context, used to get things like the current nsIDOMWindow
+ * for this request. May be null.
+ * @param aContentType
+ * The content type of aRequest
+ * @param aRequest
+ * A request whose content type is already known.
+ *
+ * @throw NS_ERROR_WONT_HANDLE_CONTENT Indicates that this handler does not
+ * want to handle this content. A different way for handling this
+ * content should be tried.
+ */
+ void handleContent(in string aContentType,
+ in nsIInterfaceRequestor aWindowContext,
+ in nsIRequest aRequest);
+};
diff --git a/uriloader/base/nsIDocumentLoader.idl b/uriloader/base/nsIDocumentLoader.idl
new file mode 100644
index 000000000..3bd960ac8
--- /dev/null
+++ b/uriloader/base/nsIDocumentLoader.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "nsISupports.idl"
+interface nsILoadGroup;
+interface nsIChannel;
+interface nsIURI;
+interface nsIWebProgress;
+interface nsIRequest;
+
+/**
+ * An nsIDocumentLoader is an interface responsible for tracking groups of
+ * loads that belong together (images, external scripts, etc) and subdocuments
+ * (<iframe>, <frame>, etc). It is also responsible for sending
+ * nsIWebProgressListener notifications.
+ * XXXbz this interface should go away, we think...
+ */
+[scriptable, uuid(bbe961ee-59e9-42bb-be50-0331979bb79f)]
+interface nsIDocumentLoader : nsISupports
+{
+ // Stop all loads in the loadgroup of this docloader
+ void stop();
+
+ // XXXbz is this needed? For embedding? What this does is does is not
+ // defined by this interface!
+ readonly attribute nsISupports container;
+
+ // The loadgroup associated with this docloader
+ readonly attribute nsILoadGroup loadGroup;
+
+ // The defaultLoadRequest of the loadgroup associated with this docloader
+ readonly attribute nsIChannel documentChannel;
+};
+
diff --git a/uriloader/base/nsITransfer.idl b/uriloader/base/nsITransfer.idl
new file mode 100644
index 000000000..da34d4ac4
--- /dev/null
+++ b/uriloader/base/nsITransfer.idl
@@ -0,0 +1,106 @@
+/* -*- 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/. */
+
+#include "nsIWebProgressListener2.idl"
+
+interface nsIArray;
+interface nsIURI;
+interface nsICancelable;
+interface nsIMIMEInfo;
+interface nsIFile;
+
+[scriptable, uuid(37ec75d3-97ad-4da8-afaa-eabe5b4afd73)]
+interface nsITransfer : nsIWebProgressListener2 {
+
+ /**
+ * Initializes the transfer with certain properties. This function must
+ * be called prior to accessing any properties on this interface.
+ *
+ * @param aSource The source URI of the transfer. Must not be null.
+ *
+ * @param aTarget The target URI of the transfer. Must not be null.
+ *
+ * @param aDisplayName The user-readable description of the transfer.
+ * Can be empty.
+ *
+ * @param aMIMEInfo The MIME info associated with the target,
+ * including MIME type and helper app when appropriate.
+ * This parameter is optional.
+ *
+ * @param startTime Time when the download started (ie, when the first
+ * response from the server was received)
+ * XXX presumably wbp and exthandler do this differently
+ *
+ * @param aTempFile The location of a temporary file; i.e. a file in which
+ * the received data will be stored, but which is not
+ * equal to the target file. (will be moved to the real
+ * target by the caller, when the download is finished)
+ * May be null.
+ *
+ * @param aCancelable An object that can be used to abort the download.
+ * Must not be null.
+ * Implementations are expected to hold a strong
+ * reference to this object until the download is
+ * finished, at which point they should release the
+ * reference.
+ *
+ * @param aIsPrivate Used to determine the privacy status of the new transfer.
+ * If true, indicates that the transfer was initiated from
+ * a source that desires privacy.
+ */
+ void init(in nsIURI aSource,
+ in nsIURI aTarget,
+ in AString aDisplayName,
+ in nsIMIMEInfo aMIMEInfo,
+ in PRTime startTime,
+ in nsIFile aTempFile,
+ in nsICancelable aCancelable,
+ in boolean aIsPrivate);
+
+ /*
+ * Used to notify the transfer object of the hash of the downloaded file.
+ * Must be called on the main thread, only after the download has finished
+ * successfully.
+ * @param aHash The SHA-256 hash in raw bytes of the downloaded file.
+ */
+ void setSha256Hash(in ACString aHash);
+
+ /*
+ * Used to notify the transfer object of the signature of the downloaded
+ * file. Must be called on the main thread, only after the download has
+ * finished successfully.
+ * @param aSignatureInfo The nsIArray of nsIX509CertList of nsIX509Cert
+ * certificates of the downloaded file.
+ */
+ void setSignatureInfo(in nsIArray aSignatureInfo);
+
+ /*
+ * Used to notify the transfer object of the redirects associated with the
+ * channel that terminated in the downloaded file. Must be called on the
+ * main thread, only after the download has finished successfully.
+ * @param aRedirects The nsIArray of nsIPrincipal of redirected URIs
+ * associated with the downloaded file.
+ */
+ void setRedirects(in nsIArray aRedirects);
+};
+
+%{C++
+/**
+ * A component with this contract ID will be created each time a download is
+ * started, and nsITransfer::Init will be called on it and an observer will be set.
+ *
+ * Notifications of the download progress will happen via
+ * nsIWebProgressListener/nsIWebProgressListener2.
+ *
+ * INTERFACES THAT MUST BE IMPLEMENTED:
+ * nsITransfer
+ * nsIWebProgressListener
+ * nsIWebProgressListener2
+ *
+ * XXX move this to nsEmbedCID.h once the interfaces (and the contract ID) are
+ * frozen.
+ */
+#define NS_TRANSFER_CONTRACTID "@mozilla.org/transfer;1"
+%}
diff --git a/uriloader/base/nsIURIContentListener.idl b/uriloader/base/nsIURIContentListener.idl
new file mode 100644
index 000000000..9008cb61e
--- /dev/null
+++ b/uriloader/base/nsIURIContentListener.idl
@@ -0,0 +1,135 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIRequest;
+interface nsIStreamListener;
+interface nsIURI;
+
+/**
+ * nsIURIContentListener is an interface used by components which
+ * want to know (and have a chance to handle) a particular content type.
+ * Typical usage scenarios will include running applications which register
+ * a nsIURIContentListener for each of its content windows with the uri
+ * dispatcher service.
+ */
+[scriptable, uuid(10a28f38-32e8-4c63-8aa1-12eaaebc369a)]
+interface nsIURIContentListener : nsISupports
+{
+ /**
+ * Gives the original content listener first crack at stopping a load before
+ * it happens.
+ *
+ * @param aURI URI that is being opened.
+ *
+ * @return <code>false</code> if the load can continue;
+ * <code>true</code> if the open should be aborted.
+ */
+ boolean onStartURIOpen(in nsIURI aURI);
+
+ /**
+ * Notifies the content listener to hook up an nsIStreamListener capable of
+ * consuming the data stream.
+ *
+ * @param aContentType Content type of the data.
+ * @param aIsContentPreferred Indicates whether the content should be
+ * preferred by this listener.
+ * @param aRequest Request that is providing the data.
+ * @param aContentHandler nsIStreamListener that will consume the data.
+ * This should be set to <code>nullptr</code> if
+ * this content listener can't handle the content
+ * type; in this case, doContent should also fail
+ * (i.e., return failure nsresult).
+ *
+ * @return <code>true</code> if the load should
+ * be aborted and consumer wants to
+ * handle the load completely by itself. This
+ * causes the URI Loader do nothing else...
+ * <code>false</code> if the URI Loader should
+ * continue handling the load and call the
+ * returned streamlistener's methods.
+ */
+ boolean doContent(in ACString aContentType,
+ in boolean aIsContentPreferred,
+ in nsIRequest aRequest,
+ out nsIStreamListener aContentHandler);
+
+ /**
+ * When given a uri to dispatch, if the URI is specified as 'preferred
+ * content' then the uri loader tries to find a preferred content handler
+ * for the content type. The thought is that many content listeners may
+ * be able to handle the same content type if they have to. i.e. the mail
+ * content window can handle text/html just like a browser window content
+ * listener. However, if the user clicks on a link with text/html content,
+ * then the browser window should handle that content and not the mail
+ * window where the user may have clicked the link. This is the difference
+ * between isPreferred and canHandleContent.
+ *
+ * @param aContentType Content type of the data.
+ * @param aDesiredContentType Indicates that aContentType must be converted
+ * to aDesiredContentType before processing the
+ * data. This causes a stream converted to be
+ * inserted into the nsIStreamListener chain.
+ * This argument can be <code>nullptr</code> if
+ * the content should be consumed directly as
+ * aContentType.
+ *
+ * @return <code>true</code> if this is a preferred
+ * content handler for aContentType;
+ * <code>false<code> otherwise.
+ */
+ boolean isPreferred(in string aContentType, out string aDesiredContentType);
+
+ /**
+ * When given a uri to dispatch, if the URI is not specified as 'preferred
+ * content' then the uri loader calls canHandleContent to see if the content
+ * listener is capable of handling the content.
+ *
+ * @param aContentType Content type of the data.
+ * @param aIsContentPreferred Indicates whether the content should be
+ * preferred by this listener.
+ * @param aDesiredContentType Indicates that aContentType must be converted
+ * to aDesiredContentType before processing the
+ * data. This causes a stream converted to be
+ * inserted into the nsIStreamListener chain.
+ * This argument can be <code>nullptr</code> if
+ * the content should be consumed directly as
+ * aContentType.
+ *
+ * @return <code>true</code> if the data can be consumed.
+ * <code>false</code> otherwise.
+ *
+ * Note: I really envision canHandleContent as a method implemented
+ * by the docshell as the implementation is generic to all doc
+ * shells. The isPreferred decision is a decision made by a top level
+ * application content listener that sits at the top of the docshell
+ * hierarchy.
+ */
+ boolean canHandleContent(in string aContentType,
+ in boolean aIsContentPreferred,
+ out string aDesiredContentType);
+
+ /**
+ * The load context associated with a particular content listener.
+ * The URI Loader stores and accesses this value as needed.
+ */
+ attribute nsISupports loadCookie;
+
+ /**
+ * The parent content listener if this particular listener is part of a chain
+ * of content listeners (i.e. a docshell!)
+ *
+ * @note If this attribute is set to an object that implements
+ * nsISupportsWeakReference, the implementation should get the
+ * nsIWeakReference and hold that. Otherwise, the implementation
+ * should not refcount this interface; it should assume that a non
+ * null value is always valid. In that case, the caller is
+ * responsible for explicitly setting this value back to null if the
+ * parent content listener is destroyed.
+ */
+ attribute nsIURIContentListener parentContentListener;
+};
+
diff --git a/uriloader/base/nsIURILoader.idl b/uriloader/base/nsIURILoader.idl
new file mode 100644
index 000000000..74f21e363
--- /dev/null
+++ b/uriloader/base/nsIURILoader.idl
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIURIContentListener;
+interface nsIURI;
+interface nsILoadGroup;
+interface nsIProgressEventSink;
+interface nsIChannel;
+interface nsIRequest;
+interface nsIStreamListener;
+interface nsIInputStream;
+interface nsIInterfaceRequestor;
+
+/**
+ * The uri dispatcher is responsible for taking uri's, determining
+ * the content and routing the opened url to the correct content
+ * handler.
+ *
+ * When you encounter a url you want to open, you typically call
+ * openURI, passing it the content listener for the window the uri is
+ * originating from. The uri dispatcher opens the url to discover the
+ * content type. It then gives the content listener first crack at
+ * handling the content. If it doesn't want it, the dispatcher tries
+ * to hand it off one of the registered content listeners. This allows
+ * running applications the chance to jump in and handle the content.
+ *
+ * If that also fails, then the uri dispatcher goes to the registry
+ * looking for the preferred content handler for the content type
+ * of the uri. The content handler may create an app instance
+ * or it may hand the contents off to a platform specific plugin
+ * or helper app. Or it may hand the url off to an OS registered
+ * application.
+ */
+[scriptable, uuid(8762c4e7-be35-4958-9b81-a05685bb516d)]
+interface nsIURILoader : nsISupports
+{
+ /**
+ * @name Flags for opening URIs.
+ */
+ /* @{ */
+ /**
+ * Should the content be displayed in a container that prefers the
+ * content-type, or will any container do.
+ */
+ const unsigned long IS_CONTENT_PREFERRED = 1 << 0;
+ /**
+ * If this flag is set, only the listener of the specified window context will
+ * be considered for content handling; if it refuses the load, an error will
+ * be indicated.
+ */
+ const unsigned long DONT_RETARGET = 1 << 1;
+ /* @} */
+
+ /**
+ * As applications such as messenger and the browser are instantiated,
+ * they register content listener's with the uri dispatcher corresponding
+ * to content windows within that application.
+ *
+ * Note to self: we may want to optimize things a bit more by requiring
+ * the content types the registered content listener cares about.
+ *
+ * @param aContentListener
+ * The listener to register. This listener must implement
+ * nsISupportsWeakReference.
+ *
+ * @see the nsIURILoader class description
+ */
+ void registerContentListener (in nsIURIContentListener aContentListener);
+ void unRegisterContentListener (in nsIURIContentListener aContentListener);
+
+ /**
+ * OpenURI requires the following parameters.....
+ * @param aChannel
+ * The channel that should be opened. This must not be asyncOpen'd yet!
+ * If a loadgroup is set on the channel, it will get replaced with a
+ * different one.
+ * @param aFlags
+ * Combination (bitwise OR) of the flags specified above. 0 indicates
+ * default handling.
+ * @param aWindowContext
+ * If you are running the url from a doc shell or a web shell, this is
+ * your window context. If you have a content listener you want to
+ * give first crack to, the uri loader needs to be able to get it
+ * from the window context. We will also be using the window context
+ * to get at the progress event sink interface.
+ * <b>Must not be null!</b>
+ */
+ void openURI(in nsIChannel aChannel,
+ in unsigned long aFlags,
+ in nsIInterfaceRequestor aWindowContext);
+
+ /**
+ * Loads data from a channel. This differs from openURI in that the channel
+ * may already be opened, and that it returns a stream listener into which the
+ * caller should pump data. The caller is responsible for opening the channel
+ * and pumping the channel's data into the returned stream listener.
+ *
+ * Note: If the channel already has a loadgroup, it will be replaced with the
+ * window context's load group, or null if the context doesn't have one.
+ *
+ * If the window context's nsIURIContentListener refuses the load immediately
+ * (e.g. in nsIURIContentListener::onStartURIOpen), this method will return
+ * NS_ERROR_WONT_HANDLE_CONTENT. At that point, the caller should probably
+ * cancel the channel if it's already open (this method will not cancel the
+ * channel).
+ *
+ * If flags include DONT_RETARGET, and the content listener refuses the load
+ * during onStartRequest (e.g. in canHandleContent/isPreferred), then the
+ * returned stream listener's onStartRequest method will return
+ * NS_ERROR_WONT_HANDLE_CONTENT.
+ *
+ * @param aChannel
+ * The channel that should be loaded. The channel may already be
+ * opened. It must not be closed (i.e. this must be called before the
+ * channel calls onStopRequest on its stream listener).
+ * @param aFlags
+ * Combination (bitwise OR) of the flags specified above. 0 indicates
+ * default handling.
+ * @param aWindowContext
+ * If you are running the url from a doc shell or a web shell, this is
+ * your window context. If you have a content listener you want to
+ * give first crack to, the uri loader needs to be able to get it
+ * from the window context. We will also be using the window context
+ * to get at the progress event sink interface.
+ * <b>Must not be null!</b>
+ */
+ nsIStreamListener openChannel(in nsIChannel aChannel,
+ in unsigned long aFlags,
+ in nsIInterfaceRequestor aWindowContext);
+
+ /**
+ * Stops an in progress load
+ */
+ void stop(in nsISupports aLoadCookie);
+};
+
diff --git a/uriloader/base/nsIWebProgress.idl b/uriloader/base/nsIWebProgress.idl
new file mode 100644
index 000000000..9d17d0a4d
--- /dev/null
+++ b/uriloader/base/nsIWebProgress.idl
@@ -0,0 +1,153 @@
+/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
+
+interface mozIDOMWindowProxy;
+interface nsIWebProgressListener;
+
+/**
+ * The nsIWebProgress interface is used to add or remove nsIWebProgressListener
+ * instances to observe the loading of asynchronous requests (usually in the
+ * context of a DOM window).
+ *
+ * nsIWebProgress instances may be arranged in a parent-child configuration,
+ * corresponding to the parent-child configuration of their respective DOM
+ * windows. However, in some cases a nsIWebProgress instance may not have an
+ * associated DOM window. The parent-child relationship of nsIWebProgress
+ * instances is not made explicit by this interface, but the relationship may
+ * exist in some implementations.
+ *
+ * A nsIWebProgressListener instance receives notifications for the
+ * nsIWebProgress instance to which it added itself, and it may also receive
+ * notifications from any nsIWebProgress instances that are children of that
+ * nsIWebProgress instance.
+ */
+[scriptable, uuid(c4d64640-b332-4db6-a2a5-e08566000dc9)]
+interface nsIWebProgress : nsISupports
+{
+ /**
+ * The following flags may be combined to form the aNotifyMask parameter for
+ * the addProgressListener method. They limit the set of events that are
+ * delivered to an nsIWebProgressListener instance.
+ */
+
+ /**
+ * These flags indicate the state transistions to observe, corresponding to
+ * nsIWebProgressListener::onStateChange.
+ *
+ * NOTIFY_STATE_REQUEST
+ * Only receive the onStateChange event if the aStateFlags parameter
+ * includes nsIWebProgressListener::STATE_IS_REQUEST.
+ *
+ * NOTIFY_STATE_DOCUMENT
+ * Only receive the onStateChange event if the aStateFlags parameter
+ * includes nsIWebProgressListener::STATE_IS_DOCUMENT.
+ *
+ * NOTIFY_STATE_NETWORK
+ * Only receive the onStateChange event if the aStateFlags parameter
+ * includes nsIWebProgressListener::STATE_IS_NETWORK.
+ *
+ * NOTIFY_STATE_WINDOW
+ * Only receive the onStateChange event if the aStateFlags parameter
+ * includes nsIWebProgressListener::STATE_IS_WINDOW.
+ *
+ * NOTIFY_STATE_ALL
+ * Receive all onStateChange events.
+ */
+ const unsigned long NOTIFY_STATE_REQUEST = 0x00000001;
+ const unsigned long NOTIFY_STATE_DOCUMENT = 0x00000002;
+ const unsigned long NOTIFY_STATE_NETWORK = 0x00000004;
+ const unsigned long NOTIFY_STATE_WINDOW = 0x00000008;
+ const unsigned long NOTIFY_STATE_ALL = 0x0000000f;
+
+ /**
+ * These flags indicate the other events to observe, corresponding to the
+ * other four methods defined on nsIWebProgressListener.
+ *
+ * NOTIFY_PROGRESS
+ * Receive onProgressChange events.
+ *
+ * NOTIFY_STATUS
+ * Receive onStatusChange events.
+ *
+ * NOTIFY_SECURITY
+ * Receive onSecurityChange events.
+ *
+ * NOTIFY_LOCATION
+ * Receive onLocationChange events.
+ *
+ * NOTIFY_REFRESH
+ * Receive onRefreshAttempted events.
+ * This is defined on nsIWebProgressListener2.
+ */
+ const unsigned long NOTIFY_PROGRESS = 0x00000010;
+ const unsigned long NOTIFY_STATUS = 0x00000020;
+ const unsigned long NOTIFY_SECURITY = 0x00000040;
+ const unsigned long NOTIFY_LOCATION = 0x00000080;
+ const unsigned long NOTIFY_REFRESH = 0x00000100;
+
+ /**
+ * This flag enables all notifications.
+ */
+ const unsigned long NOTIFY_ALL = 0x000001ff;
+
+ /**
+ * Registers a listener to receive web progress events.
+ *
+ * @param aListener
+ * The listener interface to be called when a progress event occurs.
+ * This object must also implement nsISupportsWeakReference.
+ * @param aNotifyMask
+ * The types of notifications to receive.
+ *
+ * @throw NS_ERROR_INVALID_ARG
+ * Indicates that aListener was either null or that it does not
+ * support weak references.
+ * @throw NS_ERROR_FAILURE
+ * Indicates that aListener was already registered.
+ */
+ void addProgressListener(in nsIWebProgressListener aListener,
+ in unsigned long aNotifyMask);
+
+ /**
+ * Removes a previously registered listener of progress events.
+ *
+ * @param aListener
+ * The listener interface previously registered with a call to
+ * addProgressListener.
+ *
+ * @throw NS_ERROR_FAILURE
+ * Indicates that aListener was not registered.
+ */
+ void removeProgressListener(in nsIWebProgressListener aListener);
+
+ /**
+ * The DOM window associated with this nsIWebProgress instance.
+ *
+ * @throw NS_ERROR_FAILURE
+ * Indicates that there is no associated DOM window.
+ */
+ readonly attribute mozIDOMWindowProxy DOMWindow;
+ readonly attribute uint64_t DOMWindowID;
+
+ /**
+ * Indicates whether DOMWindow.top == DOMWindow.
+ */
+ readonly attribute boolean isTopLevel;
+
+ /**
+ * Indicates whether or not a document is currently being loaded
+ * in the context of this nsIWebProgress instance.
+ */
+ readonly attribute boolean isLoadingDocument;
+
+ /**
+ * Contains a load type as specified by the load* constants in
+ * nsIDocShellLoadInfo.idl.
+ */
+ readonly attribute unsigned long loadType;
+};
diff --git a/uriloader/base/nsIWebProgressListener.idl b/uriloader/base/nsIWebProgressListener.idl
new file mode 100644
index 000000000..714b931a3
--- /dev/null
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -0,0 +1,425 @@
+/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
+
+interface nsIWebProgress;
+interface nsIRequest;
+interface nsIURI;
+
+/**
+ * The nsIWebProgressListener interface is implemented by clients wishing to
+ * listen in on the progress associated with the loading of asynchronous
+ * requests in the context of a nsIWebProgress instance as well as any child
+ * nsIWebProgress instances. nsIWebProgress.idl describes the parent-child
+ * relationship of nsIWebProgress instances.
+ */
+[scriptable, uuid(a9df523b-efe2-421e-9d8e-3d7f807dda4c)]
+interface nsIWebProgressListener : nsISupports
+{
+ /**
+ * State Transition Flags
+ *
+ * These flags indicate the various states that requests may transition
+ * through as they are being loaded. These flags are mutually exclusive.
+ *
+ * For any given request, onStateChange is called once with the STATE_START
+ * flag, zero or more times with the STATE_TRANSFERRING flag or once with the
+ * STATE_REDIRECTING flag, and then finally once with the STATE_STOP flag.
+ * NOTE: For document requests, a second STATE_STOP is generated (see the
+ * description of STATE_IS_WINDOW for more details).
+ *
+ * STATE_START
+ * This flag indicates the start of a request. This flag is set when a
+ * request is initiated. The request is complete when onStateChange is
+ * called for the same request with the STATE_STOP flag set.
+ *
+ * STATE_REDIRECTING
+ * This flag indicates that a request is being redirected. The request
+ * passed to onStateChange is the request that is being redirected. When a
+ * redirect occurs, a new request is generated automatically to process the
+ * new request. Expect a corresponding STATE_START event for the new
+ * request, and a STATE_STOP for the redirected request.
+ *
+ * STATE_TRANSFERRING
+ * This flag indicates that data for a request is being transferred to an
+ * end consumer. This flag indicates that the request has been targeted,
+ * and that the user may start seeing content corresponding to the request.
+ *
+ * STATE_NEGOTIATING
+ * This flag is not used.
+ *
+ * STATE_STOP
+ * This flag indicates the completion of a request. The aStatus parameter
+ * to onStateChange indicates the final status of the request.
+ */
+ const unsigned long STATE_START = 0x00000001;
+ const unsigned long STATE_REDIRECTING = 0x00000002;
+ const unsigned long STATE_TRANSFERRING = 0x00000004;
+ const unsigned long STATE_NEGOTIATING = 0x00000008;
+ const unsigned long STATE_STOP = 0x00000010;
+
+
+ /**
+ * State Type Flags
+ *
+ * These flags further describe the entity for which the state transition is
+ * occuring. These flags are NOT mutually exclusive (i.e., an onStateChange
+ * event may indicate some combination of these flags).
+ *
+ * STATE_IS_REQUEST
+ * This flag indicates that the state transition is for a request, which
+ * includes but is not limited to document requests. (See below for a
+ * description of document requests.) Other types of requests, such as
+ * requests for inline content (e.g., images and stylesheets) are
+ * considered normal requests.
+ *
+ * STATE_IS_DOCUMENT
+ * This flag indicates that the state transition is for a document request.
+ * This flag is set in addition to STATE_IS_REQUEST. A document request
+ * supports the nsIChannel interface and its loadFlags attribute includes
+ * the nsIChannel::LOAD_DOCUMENT_URI flag.
+ *
+ * A document request does not complete until all requests associated with
+ * the loading of its corresponding document have completed. This includes
+ * other document requests (e.g., corresponding to HTML <iframe> elements).
+ * The document corresponding to a document request is available via the
+ * DOMWindow attribute of onStateChange's aWebProgress parameter.
+ *
+ * STATE_IS_NETWORK
+ * This flag indicates that the state transition corresponds to the start
+ * or stop of activity in the indicated nsIWebProgress instance. This flag
+ * is accompanied by either STATE_START or STATE_STOP, and it may be
+ * combined with other State Type Flags.
+ *
+ * Unlike STATE_IS_WINDOW, this flag is only set when activity within the
+ * nsIWebProgress instance being observed starts or stops. If activity
+ * only occurs in a child nsIWebProgress instance, then this flag will be
+ * set to indicate the start and stop of that activity.
+ *
+ * For example, in the case of navigation within a single frame of a HTML
+ * frameset, a nsIWebProgressListener instance attached to the
+ * nsIWebProgress of the frameset window will receive onStateChange calls
+ * with the STATE_IS_NETWORK flag set to indicate the start and stop of
+ * said navigation. In other words, an observer of an outer window can
+ * determine when activity, that may be constrained to a child window or
+ * set of child windows, starts and stops.
+ *
+ * STATE_IS_WINDOW
+ * This flag indicates that the state transition corresponds to the start
+ * or stop of activity in the indicated nsIWebProgress instance. This flag
+ * is accompanied by either STATE_START or STATE_STOP, and it may be
+ * combined with other State Type Flags.
+ *
+ * This flag is similar to STATE_IS_DOCUMENT. However, when a document
+ * request completes, two onStateChange calls with STATE_STOP are
+ * generated. The document request is passed as aRequest to both calls.
+ * The first has STATE_IS_REQUEST and STATE_IS_DOCUMENT set, and the second
+ * has the STATE_IS_WINDOW flag set (and possibly the STATE_IS_NETWORK flag
+ * set as well -- see above for a description of when the STATE_IS_NETWORK
+ * flag may be set). This second STATE_STOP event may be useful as a way
+ * to partition the work that occurs when a document request completes.
+ */
+ const unsigned long STATE_IS_REQUEST = 0x00010000;
+ const unsigned long STATE_IS_DOCUMENT = 0x00020000;
+ const unsigned long STATE_IS_NETWORK = 0x00040000;
+ const unsigned long STATE_IS_WINDOW = 0x00080000;
+
+
+ /**
+ * State Modifier Flags
+ *
+ * These flags further describe the transition which is occuring. These
+ * flags are NOT mutually exclusive (i.e., an onStateChange event may
+ * indicate some combination of these flags).
+ *
+ * STATE_RESTORING
+ * This flag indicates that the state transition corresponds to the start
+ * or stop of activity for restoring a previously-rendered presentation.
+ * As such, there is no actual network activity associated with this
+ * request, and any modifications made to the document or presentation
+ * when it was originally loaded will still be present.
+ */
+ const unsigned long STATE_RESTORING = 0x01000000;
+
+ /**
+ * State Security Flags
+ *
+ * These flags describe the security state reported by a call to the
+ * onSecurityChange method. These flags are mutually exclusive.
+ *
+ * STATE_IS_INSECURE
+ * This flag indicates that the data corresponding to the request
+ * was received over an insecure channel.
+ *
+ * STATE_IS_BROKEN
+ * This flag indicates an unknown security state. This may mean that the
+ * request is being loaded as part of a page in which some content was
+ * received over an insecure channel.
+ *
+ * STATE_IS_SECURE
+ * This flag indicates that the data corresponding to the request was
+ * received over a secure channel. The degree of security is expressed by
+ * STATE_SECURE_HIGH, STATE_SECURE_MED, or STATE_SECURE_LOW.
+ */
+ const unsigned long STATE_IS_INSECURE = 0x00000004;
+ const unsigned long STATE_IS_BROKEN = 0x00000001;
+ const unsigned long STATE_IS_SECURE = 0x00000002;
+
+ /**
+ * Mixed active content flags
+ *
+ * May be set in addition to the State Security Flags, to indicate that
+ * mixed active content has been encountered.
+ *
+ * STATE_BLOCKED_MIXED_ACTIVE_CONTENT
+ * Mixed active content has been blocked from loading.
+ *
+ * STATE_LOADED_MIXED_ACTIVE_CONTENT
+ * Mixed active content has been loaded. State should be STATE_IS_BROKEN.
+ */
+ const unsigned long STATE_BLOCKED_MIXED_ACTIVE_CONTENT = 0x00000010;
+ const unsigned long STATE_LOADED_MIXED_ACTIVE_CONTENT = 0x00000020;
+
+ /**
+ * Mixed display content flags
+ *
+ * May be set in addition to the State Security Flags, to indicate that
+ * mixed display content has been encountered.
+ *
+ * STATE_BLOCKED_MIXED_DISPLAY_CONTENT
+ * Mixed display content has been blocked from loading.
+ *
+ * STATE_LOADED_MIXED_DISPLAY_CONTENT
+ * Mixed display content has been loaded. State should be STATE_IS_BROKEN.
+ */
+ const unsigned long STATE_BLOCKED_MIXED_DISPLAY_CONTENT = 0x00000100;
+ const unsigned long STATE_LOADED_MIXED_DISPLAY_CONTENT = 0x00000200;
+
+ /**
+ * Tracking content flags
+ *
+ * May be set in addition to the State security Flags, to indicate that
+ * tracking content has been encountered.
+ *
+ * STATE_BLOCKED_TRACKING_CONTENT
+ * Tracking content has been blocked from loading.
+ *
+ * STATE_LOADED_TRACKING_CONTENT
+ * Tracking content has been loaded.
+ */
+ const unsigned long STATE_BLOCKED_TRACKING_CONTENT = 0x00001000;
+ const unsigned long STATE_LOADED_TRACKING_CONTENT = 0x00002000;
+
+ /**
+ * Security Strength Flags
+ *
+ * These flags describe the security strength and accompany STATE_IS_SECURE
+ * in a call to the onSecurityChange method. These flags are mutually
+ * exclusive.
+ *
+ * These flags are not meant to provide a precise description of data
+ * transfer security. These are instead intended as a rough indicator that
+ * may be used to, for example, color code a security indicator or otherwise
+ * provide basic data transfer security feedback to the user.
+ *
+ * STATE_SECURE_HIGH
+ * This flag indicates a high degree of security.
+ *
+ * STATE_SECURE_MED
+ * This flag indicates a medium degree of security.
+ *
+ * STATE_SECURE_LOW
+ * This flag indicates a low degree of security.
+ */
+ const unsigned long STATE_SECURE_HIGH = 0x00040000;
+ const unsigned long STATE_SECURE_MED = 0x00010000;
+ const unsigned long STATE_SECURE_LOW = 0x00020000;
+
+ /**
+ * State bits for EV == Extended Validation == High Assurance
+ *
+ * These flags describe the level of identity verification
+ * in a call to the onSecurityChange method.
+ *
+ * STATE_IDENTITY_EV_TOPLEVEL
+ * The topmost document uses an EV cert.
+ * NOTE: Available since Gecko 1.9
+ */
+
+ const unsigned long STATE_IDENTITY_EV_TOPLEVEL = 0x00100000;
+
+ /**
+ * Broken state flags
+ *
+ * These flags describe the reason of the broken state.
+ *
+ * STATE_USES_SSL_3
+ * The topmost document uses SSL 3.0.
+ *
+ * STATE_USES_WEAK_CRYPTO
+ * The topmost document uses a weak cipher suite such as RC4.
+ *
+ * STATE_CERT_USER_OVERRIDDEN
+ * The user has added a security exception for the site.
+ */
+ const unsigned long STATE_USES_SSL_3 = 0x01000000;
+ const unsigned long STATE_USES_WEAK_CRYPTO = 0x02000000;
+ const unsigned long STATE_CERT_USER_OVERRIDDEN = 0x04000000;
+
+ /**
+ * Notification indicating the state has changed for one of the requests
+ * associated with aWebProgress.
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification
+ * @param aRequest
+ * The nsIRequest that has changed state.
+ * @param aStateFlags
+ * Flags indicating the new state. This value is a combination of one
+ * of the State Transition Flags and one or more of the State Type
+ * Flags defined above. Any undefined bits are reserved for future
+ * use.
+ * @param aStatus
+ * Error status code associated with the state change. This parameter
+ * should be ignored unless aStateFlags includes the STATE_STOP bit.
+ * The status code indicates success or failure of the request
+ * associated with the state change. NOTE: aStatus may be a success
+ * code even for server generated errors, such as the HTTP 404 error.
+ * In such cases, the request itself should be queried for extended
+ * error information (e.g., for HTTP requests see nsIHttpChannel).
+ */
+ void onStateChange(in nsIWebProgress aWebProgress,
+ in nsIRequest aRequest,
+ in unsigned long aStateFlags,
+ in nsresult aStatus);
+
+ /**
+ * Notification that the progress has changed for one of the requests
+ * associated with aWebProgress. Progress totals are reset to zero when all
+ * requests in aWebProgress complete (corresponding to onStateChange being
+ * called with aStateFlags including the STATE_STOP and STATE_IS_WINDOW
+ * flags).
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification.
+ * @param aRequest
+ * The nsIRequest that has new progress.
+ * @param aCurSelfProgress
+ * The current progress for aRequest.
+ * @param aMaxSelfProgress
+ * The maximum progress for aRequest.
+ * @param aCurTotalProgress
+ * The current progress for all requests associated with aWebProgress.
+ * @param aMaxTotalProgress
+ * The total progress for all requests associated with aWebProgress.
+ *
+ * NOTE: If any progress value is unknown, or if its value would exceed the
+ * maximum value of type long, then its value is replaced with -1.
+ *
+ * NOTE: If the object also implements nsIWebProgressListener2 and the caller
+ * knows about that interface, this function will not be called. Instead,
+ * nsIWebProgressListener2::onProgressChange64 will be called.
+ */
+ void onProgressChange(in nsIWebProgress aWebProgress,
+ in nsIRequest aRequest,
+ in long aCurSelfProgress,
+ in long aMaxSelfProgress,
+ in long aCurTotalProgress,
+ in long aMaxTotalProgress);
+
+ /**
+ * Flags for onLocationChange
+ *
+ * LOCATION_CHANGE_SAME_DOCUMENT
+ * This flag is on when |aWebProgress| did not load a new document.
+ * For example, the location change is due to an anchor scroll or a
+ * pushState/popState/replaceState.
+ *
+ * LOCATION_CHANGE_ERROR_PAGE
+ * This flag is on when |aWebProgress| redirected from the requested
+ * contents to an internal page to show error status, such as
+ * <about:neterror>, <about:certerror> and so on.
+ *
+ * Generally speaking, |aURI| and |aRequest| are the original data. DOM
+ * |window.location.href| is also the original location, while
+ * |document.documentURI| is the redirected location. Sometimes |aURI| is
+ * <about:blank> and |aRequest| is null when the original data does not
+ + remain.
+ *
+ * |aWebProgress| does NOT set this flag when it did not try to load a new
+ * document. In this case, it should set LOCATION_CHANGE_SAME_DOCUMENT.
+ */
+ const unsigned long LOCATION_CHANGE_SAME_DOCUMENT = 0x00000001;
+ const unsigned long LOCATION_CHANGE_ERROR_PAGE = 0x00000002;
+
+ /**
+ * Called when the location of the window being watched changes. This is not
+ * when a load is requested, but rather once it is verified that the load is
+ * going to occur in the given window. For instance, a load that starts in a
+ * window might send progress and status messages for the new site, but it
+ * will not send the onLocationChange until we are sure that we are loading
+ * this new page here.
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification.
+ * @param aRequest
+ * The associated nsIRequest. This may be null in some cases.
+ * @param aLocation
+ * The URI of the location that is being loaded.
+ * @param aFlags
+ * This is a value which explains the situation or the reason why
+ * the location has changed.
+ */
+ void onLocationChange(in nsIWebProgress aWebProgress,
+ in nsIRequest aRequest,
+ in nsIURI aLocation,
+ [optional] in unsigned long aFlags);
+
+ /**
+ * Notification that the status of a request has changed. The status message
+ * is intended to be displayed to the user (e.g., in the status bar of the
+ * browser).
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification.
+ * @param aRequest
+ * The nsIRequest that has new status.
+ * @param aStatus
+ * This value is not an error code. Instead, it is a numeric value
+ * that indicates the current status of the request. This interface
+ * does not define the set of possible status codes. NOTE: Some
+ * status values are defined by nsITransport and nsISocketTransport.
+ * @param aMessage
+ * Localized text corresponding to aStatus.
+ */
+ void onStatusChange(in nsIWebProgress aWebProgress,
+ in nsIRequest aRequest,
+ in nsresult aStatus,
+ in wstring aMessage);
+
+ /**
+ * Notification called for security progress. This method will be called on
+ * security transitions (eg HTTP -> HTTPS, HTTPS -> HTTP, FOO -> HTTPS) and
+ * after document load completion. It might also be called if an error
+ * occurs during network loading.
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification.
+ * @param aRequest
+ * The nsIRequest that has new security state.
+ * @param aState
+ * A value composed of the Security State Flags and the Security
+ * Strength Flags listed above. Any undefined bits are reserved for
+ * future use.
+ *
+ * NOTE: These notifications will only occur if a security package is
+ * installed.
+ */
+ void onSecurityChange(in nsIWebProgress aWebProgress,
+ in nsIRequest aRequest,
+ in unsigned long aState);
+};
diff --git a/uriloader/base/nsIWebProgressListener2.idl b/uriloader/base/nsIWebProgressListener2.idl
new file mode 100644
index 000000000..87701f8d2
--- /dev/null
+++ b/uriloader/base/nsIWebProgressListener2.idl
@@ -0,0 +1,69 @@
+/* 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 "nsIWebProgressListener.idl"
+
+/**
+ * An extended version of nsIWebProgressListener.
+ */
+[scriptable, uuid(dde39de0-e4e0-11da-8ad9-0800200c9a66)]
+interface nsIWebProgressListener2 : nsIWebProgressListener {
+ /**
+ * Notification that the progress has changed for one of the requests
+ * associated with aWebProgress. Progress totals are reset to zero when all
+ * requests in aWebProgress complete (corresponding to onStateChange being
+ * called with aStateFlags including the STATE_STOP and STATE_IS_WINDOW
+ * flags).
+ *
+ * This function is identical to nsIWebProgressListener::onProgressChange,
+ * except that this function supports 64-bit values.
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification.
+ * @param aRequest
+ * The nsIRequest that has new progress.
+ * @param aCurSelfProgress
+ * The current progress for aRequest.
+ * @param aMaxSelfProgress
+ * The maximum progress for aRequest.
+ * @param aCurTotalProgress
+ * The current progress for all requests associated with aWebProgress.
+ * @param aMaxTotalProgress
+ * The total progress for all requests associated with aWebProgress.
+ *
+ * NOTE: If any progress value is unknown, then its value is replaced with -1.
+ *
+ * @see nsIWebProgressListener2::onProgressChange64
+ */
+ void onProgressChange64(in nsIWebProgress aWebProgress,
+ in nsIRequest aRequest,
+ in long long aCurSelfProgress,
+ in long long aMaxSelfProgress,
+ in long long aCurTotalProgress,
+ in long long aMaxTotalProgress);
+
+ /**
+ * Notification that a refresh or redirect has been requested in aWebProgress
+ * For example, via a <meta http-equiv="refresh"> or an HTTP Refresh: header
+ *
+ * @param aWebProgress
+ * The nsIWebProgress instance that fired the notification.
+ * @param aRefreshURI
+ * The new URI that aWebProgress has requested redirecting to.
+ * @param aMillis
+ * The delay (in milliseconds) before refresh.
+ * @param aSameURI
+ * True if aWebProgress is requesting a refresh of the
+ * current URI.
+ * False if aWebProgress is requesting a redirection to
+ * a different URI.
+ *
+ * @return True if the refresh may proceed.
+ * False if the refresh should be aborted.
+ */
+ boolean onRefreshAttempted(in nsIWebProgress aWebProgress,
+ in nsIURI aRefreshURI,
+ in long aMillis,
+ in boolean aSameURI);
+};
diff --git a/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp
new file mode 100644
index 000000000..69475d68f
--- /dev/null
+++ b/uriloader/base/nsURILoader.cpp
@@ -0,0 +1,966 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sts=2 sw=2 et cin: */
+/* 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 "nsURILoader.h"
+#include "nsAutoPtr.h"
+#include "nsIURIContentListener.h"
+#include "nsIContentHandler.h"
+#include "nsILoadGroup.h"
+#include "nsIDocumentLoader.h"
+#include "nsIWebProgress.h"
+#include "nsIWebProgressListener.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsIStreamListener.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIProgressEventSink.h"
+#include "nsIInputStream.h"
+#include "nsIStreamConverterService.h"
+#include "nsWeakReference.h"
+#include "nsIHttpChannel.h"
+#include "nsIMultiPartChannel.h"
+#include "netCore.h"
+#include "nsCRT.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIThreadRetargetableStreamListener.h"
+
+#include "nsXPIDLString.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsReadableUtils.h"
+#include "nsError.h"
+
+#include "nsICategoryManager.h"
+#include "nsCExternalHandlerService.h" // contains contractids for the helper app service
+
+#include "nsIMIMEHeaderParam.h"
+#include "nsNetCID.h"
+
+#include "nsMimeTypes.h"
+
+#include "nsDocLoader.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Preferences.h"
+#include "nsContentUtils.h"
+
+mozilla::LazyLogModule nsURILoader::mLog("URILoader");
+
+#define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
+#define LOG_ERROR(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
+
+#define NS_PREF_DISABLE_BACKGROUND_HANDLING \
+ "security.exthelperapp.disable_background_handling"
+
+/**
+ * The nsDocumentOpenInfo contains the state required when a single
+ * document is being opened in order to discover the content type...
+ * Each instance remains alive until its target URL has been loaded
+ * (or aborted).
+ */
+class nsDocumentOpenInfo final : public nsIStreamListener
+ , public nsIThreadRetargetableStreamListener
+{
+public:
+ // Needed for nsCOMPtr to work right... Don't call this!
+ nsDocumentOpenInfo();
+
+ // Real constructor
+ // aFlags is a combination of the flags on nsIURILoader
+ nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
+ uint32_t aFlags,
+ nsURILoader* aURILoader);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /**
+ * Prepares this object for receiving data. The stream
+ * listener methods of this class must not be called before calling this
+ * method.
+ */
+ nsresult Prepare();
+
+ // Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
+ // take the data off our hands.
+ nsresult DispatchContent(nsIRequest *request, nsISupports * aCtxt);
+
+ // Call this if we need to insert a stream converter from aSrcContentType to
+ // aOutContentType into the StreamListener chain. DO NOT call it if the two
+ // types are the same, since no conversion is needed in that case.
+ nsresult ConvertData(nsIRequest *request,
+ nsIURIContentListener *aListener,
+ const nsACString & aSrcContentType,
+ const nsACString & aOutContentType);
+
+ /**
+ * Function to attempt to use aListener to handle the load. If
+ * true is returned, nothing else needs to be done; if false
+ * is returned, then a different way of handling the load should be
+ * tried.
+ */
+ bool TryContentListener(nsIURIContentListener* aListener,
+ nsIChannel* aChannel);
+
+ // nsIRequestObserver methods:
+ NS_DECL_NSIREQUESTOBSERVER
+
+ // nsIStreamListener methods:
+ NS_DECL_NSISTREAMLISTENER
+
+ // nsIThreadRetargetableStreamListener
+ NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
+protected:
+ ~nsDocumentOpenInfo();
+
+protected:
+ /**
+ * The first content listener to try dispatching data to. Typically
+ * the listener associated with the entity that originated the load.
+ */
+ nsCOMPtr<nsIURIContentListener> m_contentListener;
+
+ /**
+ * The stream listener to forward nsIStreamListener notifications
+ * to. This is set once the load is dispatched.
+ */
+ nsCOMPtr<nsIStreamListener> m_targetStreamListener;
+
+ /**
+ * A pointer to the entity that originated the load. We depend on getting
+ * things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
+ */
+ nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
+
+ /**
+ * IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
+ * (also determines whether we use CanHandleContent or IsPreferred).
+ * DONT_RETARGET means that we will only try m_originalContext, no other
+ * listeners.
+ */
+ uint32_t mFlags;
+
+ /**
+ * The type of the data we will be trying to dispatch.
+ */
+ nsCString mContentType;
+
+ /**
+ * Reference to the URILoader service so we can access its list of
+ * nsIURIContentListeners.
+ */
+ RefPtr<nsURILoader> mURILoader;
+};
+
+NS_IMPL_ADDREF(nsDocumentOpenInfo)
+NS_IMPL_RELEASE(nsDocumentOpenInfo)
+
+NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+ NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+nsDocumentOpenInfo::nsDocumentOpenInfo()
+{
+ NS_NOTREACHED("This should never be called\n");
+}
+
+nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
+ uint32_t aFlags,
+ nsURILoader* aURILoader)
+ : m_originalContext(aWindowContext),
+ mFlags(aFlags),
+ mURILoader(aURILoader)
+{
+}
+
+nsDocumentOpenInfo::~nsDocumentOpenInfo()
+{
+}
+
+nsresult nsDocumentOpenInfo::Prepare()
+{
+ LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
+
+ nsresult rv;
+
+ // ask our window context if it has a uri content listener...
+ m_contentListener = do_GetInterface(m_originalContext, &rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
+{
+ LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
+ MOZ_ASSERT(request);
+ if (!request) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = NS_OK;
+
+ //
+ // Deal with "special" HTTP responses:
+ //
+ // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
+ // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
+ // the request. This has the effect of ensuring that the DocLoader does
+ // not try to interpret this as a real request.
+ //
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv));
+
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t responseCode = 0;
+
+ rv = httpChannel->GetResponseStatus(&responseCode);
+
+ if (NS_FAILED(rv)) {
+ LOG_ERROR((" Failed to get HTTP response status"));
+
+ // behave as in the canceled case
+ return NS_OK;
+ }
+
+ LOG((" HTTP response status: %d", responseCode));
+
+ if (204 == responseCode || 205 == responseCode) {
+ return NS_BINDING_ABORTED;
+ }
+
+ static bool sLargeAllocationHeaderEnabled = false;
+ static bool sCachedLargeAllocationPref = false;
+ if (!sCachedLargeAllocationPref) {
+ sCachedLargeAllocationPref = true;
+ mozilla::Preferences::AddBoolVarCache(&sLargeAllocationHeaderEnabled,
+ "dom.largeAllocationHeader.enabled");
+ }
+
+ if (sLargeAllocationHeaderEnabled) {
+ // If we have a Large-Allocation header, let's check if we should perform a process switch.
+ nsAutoCString largeAllocationHeader;
+ rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Large-Allocation"), largeAllocationHeader);
+ if (NS_SUCCEEDED(rv) && nsContentUtils::AttemptLargeAllocationLoad(httpChannel)) {
+ return NS_BINDING_ABORTED;
+ }
+ }
+ }
+
+ //
+ // Make sure that the transaction has succeeded, so far...
+ //
+ nsresult status;
+
+ rv = request->GetStatus(&status);
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!");
+ if (NS_FAILED(rv)) return rv;
+
+ if (NS_FAILED(status)) {
+ LOG_ERROR((" Request failed, status: 0x%08X", rv));
+
+ //
+ // The transaction has already reported an error - so it will be torn
+ // down. Therefore, it is not necessary to return an error code...
+ //
+ return NS_OK;
+ }
+
+ rv = DispatchContent(request, aCtxt);
+
+ LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener.get(), rv));
+
+ NS_ASSERTION(NS_SUCCEEDED(rv) || !m_targetStreamListener,
+ "Must not have an m_targetStreamListener with a failure return!");
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (m_targetStreamListener)
+ rv = m_targetStreamListener->OnStartRequest(request, aCtxt);
+
+ LOG((" OnStartRequest returning: 0x%08X", rv));
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocumentOpenInfo::CheckListenerChain()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+ do_QueryInterface(m_targetStreamListener, &rv);
+ if (retargetableListener) {
+ rv = retargetableListener->CheckListenerChain();
+ }
+ LOG(("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv %x",
+ this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
+ (nsIStreamListener*)m_targetStreamListener, rv));
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
+ nsIInputStream * inStr,
+ uint64_t sourceOffset, uint32_t count)
+{
+ // if we have retarged to the end stream listener, then forward the call....
+ // otherwise, don't do anything
+
+ nsresult rv = NS_OK;
+
+ if (m_targetStreamListener)
+ rv = m_targetStreamListener->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count);
+ return rv;
+}
+
+NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest *request, nsISupports *aCtxt,
+ nsresult aStatus)
+{
+ LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
+
+ if ( m_targetStreamListener)
+ {
+ nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
+
+ // If this is a multipart stream, we could get another
+ // OnStartRequest after this... reset state.
+ m_targetStreamListener = nullptr;
+ mContentType.Truncate();
+ listener->OnStopRequest(request, aCtxt, aStatus);
+ }
+
+ // Remember...
+ // In the case of multiplexed streams (such as multipart/x-mixed-replace)
+ // these stream listener methods could be called again :-)
+ //
+ return NS_OK;
+}
+
+nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * aCtxt)
+{
+ LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType.get()));
+
+ NS_PRECONDITION(!m_targetStreamListener,
+ "Why do we already have a target stream listener?");
+
+ nsresult rv;
+ nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
+ if (!aChannel) {
+ LOG_ERROR((" Request is not a channel. Bailing."));
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_NAMED_LITERAL_CSTRING(anyType, "*/*");
+ if (mContentType.IsEmpty() || mContentType == anyType) {
+ rv = aChannel->GetContentType(mContentType);
+ if (NS_FAILED(rv)) return rv;
+ LOG((" Got type from channel: '%s'", mContentType.get()));
+ }
+
+ bool isGuessFromExt =
+ mContentType.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT);
+ if (isGuessFromExt) {
+ // Reset to application/octet-stream for now; no one other than the
+ // external helper app service should see APPLICATION_GUESS_FROM_EXT.
+ mContentType = APPLICATION_OCTET_STREAM;
+ aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
+ }
+
+ // Check whether the data should be forced to be handled externally. This
+ // could happen because the Content-Disposition header is set so, or, in the
+ // future, because the user has specified external handling for the MIME
+ // type.
+ bool forceExternalHandling = false;
+ uint32_t disposition;
+ rv = aChannel->GetContentDisposition(&disposition);
+
+ bool allowContentDispositionToForceExternalHandling = true;
+
+#ifdef MOZ_B2G
+
+ // On B2G, OMA content files should never be handled by an external handler
+ // (even if the server specifies Content-Disposition: attachment) because the
+ // data should never be stored on an unencrypted form.
+ allowContentDispositionToForceExternalHandling =
+ !mContentType.LowerCaseEqualsASCII("application/vnd.oma.drm.message");
+
+#endif
+
+ if (NS_SUCCEEDED(rv) && (disposition == nsIChannel::DISPOSITION_ATTACHMENT) &&
+ allowContentDispositionToForceExternalHandling) {
+ forceExternalHandling = true;
+ }
+
+ LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
+
+ // The type or data the contentListener wants.
+ nsXPIDLCString desiredContentType;
+
+ if (!forceExternalHandling)
+ {
+ //
+ // First step: See whether m_contentListener wants to handle this
+ // content type.
+ //
+ if (m_contentListener && TryContentListener(m_contentListener, aChannel)) {
+ LOG((" Success! Our default listener likes this type"));
+ // All done here
+ return NS_OK;
+ }
+
+ // If we aren't allowed to try other listeners, just skip through to
+ // trying to convert the data.
+ if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
+
+ //
+ // Second step: See whether some other registered listener wants
+ // to handle this content type.
+ //
+ int32_t count = mURILoader->m_listeners.Count();
+ nsCOMPtr<nsIURIContentListener> listener;
+ for (int32_t i = 0; i < count; i++) {
+ listener = do_QueryReferent(mURILoader->m_listeners[i]);
+ if (listener) {
+ if (TryContentListener(listener, aChannel)) {
+ LOG((" Found listener registered on the URILoader"));
+ return NS_OK;
+ }
+ } else {
+ // remove from the listener list, reset i and update count
+ mURILoader->m_listeners.RemoveObjectAt(i--);
+ --count;
+ }
+ }
+
+ //
+ // Third step: Try to find a content listener that has not yet had
+ // the chance to register, as it is contained in a not-yet-loaded
+ // module, but which has registered a contract ID.
+ //
+ nsCOMPtr<nsICategoryManager> catman =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (catman) {
+ nsXPIDLCString contractidString;
+ rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
+ mContentType.get(),
+ getter_Copies(contractidString));
+ if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
+ LOG((" Listener contractid for '%s' is '%s'",
+ mContentType.get(), contractidString.get()));
+
+ listener = do_CreateInstance(contractidString);
+ LOG((" Listener from category manager: 0x%p", listener.get()));
+
+ if (listener && TryContentListener(listener, aChannel)) {
+ LOG((" Listener from category manager likes this type"));
+ return NS_OK;
+ }
+ }
+ }
+
+ //
+ // Fourth step: try to find an nsIContentHandler for our type.
+ //
+ nsAutoCString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
+ handlerContractID += mContentType;
+
+ nsCOMPtr<nsIContentHandler> contentHandler =
+ do_CreateInstance(handlerContractID.get());
+ if (contentHandler) {
+ LOG((" Content handler found"));
+ rv = contentHandler->HandleContent(mContentType.get(),
+ m_originalContext, request);
+ // XXXbz returning an error code to represent handling the
+ // content is just bizarre!
+ if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
+ if (NS_FAILED(rv)) {
+ // The content handler has unexpectedly failed. Cancel the request
+ // just in case the handler didn't...
+ LOG((" Content handler failed. Aborting load"));
+ request->Cancel(rv);
+ }
+ else {
+ LOG((" Content handler taking over load"));
+ }
+
+ return rv;
+ }
+ }
+ } else {
+ LOG((" DONT_RETARGET flag set, so skipped over random other content "
+ "listeners and content handlers"));
+ }
+
+ //
+ // Fifth step: If no listener prefers this type, see if any stream
+ // converters exist to transform this content type into
+ // some other.
+ //
+ // Don't do this if the server sent us a MIME type of "*/*" because they saw
+ // it in our Accept header and got confused.
+ // XXXbz have to be careful here; may end up in some sort of bizarre infinite
+ // decoding loop.
+ if (mContentType != anyType) {
+ rv = ConvertData(request, m_contentListener, mContentType, anyType);
+ if (NS_FAILED(rv)) {
+ m_targetStreamListener = nullptr;
+ } else if (m_targetStreamListener) {
+ // We found a converter for this MIME type. We'll just pump data into it
+ // and let the downstream nsDocumentOpenInfo handle things.
+ LOG((" Converter taking over now"));
+ return NS_OK;
+ }
+ }
+ }
+
+ NS_ASSERTION(!m_targetStreamListener,
+ "If we found a listener, why are we not using it?");
+
+ if (mFlags & nsIURILoader::DONT_RETARGET) {
+ LOG((" External handling forced or (listener not interested and no "
+ "stream converter exists), and retargeting disallowed -> aborting"));
+ return NS_ERROR_WONT_HANDLE_CONTENT;
+ }
+
+ // Before dispatching to the external helper app service, check for an HTTP
+ // error page. If we got one, we don't want to handle it with a helper app,
+ // really.
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
+ if (httpChannel) {
+ bool requestSucceeded;
+ httpChannel->GetRequestSucceeded(&requestSucceeded);
+ if (!requestSucceeded) {
+ // returning error from OnStartRequest will cancel the channel
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+ }
+
+ // Sixth step:
+ //
+ // All attempts to dispatch this content have failed. Just pass it off to
+ // the helper app service.
+ //
+
+ //
+ // Optionally, we may want to disable background handling by the external
+ // helper application service.
+ //
+ if (mozilla::Preferences::GetBool(NS_PREF_DISABLE_BACKGROUND_HANDLING,
+ false)) {
+ // First, we will ensure that the parent docshell is in an active
+ // state as we will disallow all external application handling unless it is
+ // in the foreground.
+ nsCOMPtr<nsIDocShell> docShell(do_GetInterface(m_originalContext));
+ if (!docShell) {
+ // If we can't perform our security check we definitely don't want to go
+ // any further!
+ LOG(("Failed to get DocShell to ensure it is active before anding off to "
+ "helper app service. Aborting."));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Ensure the DocShell is active before continuing.
+ bool isActive = false;
+ docShell->GetIsActive(&isActive);
+ if (!isActive) {
+ LOG((" Check for active DocShell returned false. Aborting hand off to "
+ "helper app service."));
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ }
+
+ nsCOMPtr<nsIExternalHelperAppService> helperAppService =
+ do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
+ if (helperAppService) {
+ LOG((" Passing load off to helper app service"));
+
+ // Set these flags to indicate that the channel has been targeted and that
+ // we are not using the original consumer.
+ nsLoadFlags loadFlags = 0;
+ request->GetLoadFlags(&loadFlags);
+ request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
+ | nsIChannel::LOAD_TARGETED);
+
+ if (isGuessFromExt) {
+ mContentType = APPLICATION_GUESS_FROM_EXT;
+ aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
+ }
+
+ rv = helperAppService->DoContent(mContentType,
+ request,
+ m_originalContext,
+ false,
+ nullptr,
+ getter_AddRefs(m_targetStreamListener));
+ if (NS_FAILED(rv)) {
+ request->SetLoadFlags(loadFlags);
+ m_targetStreamListener = nullptr;
+ }
+ }
+
+ NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
+ "There is no way we should be successful at this point without a m_targetStreamListener");
+ return rv;
+}
+
+nsresult
+nsDocumentOpenInfo::ConvertData(nsIRequest *request,
+ nsIURIContentListener* aListener,
+ const nsACString& aSrcContentType,
+ const nsACString& aOutContentType)
+{
+ LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
+ PromiseFlatCString(aSrcContentType).get(),
+ PromiseFlatCString(aOutContentType).get()));
+
+ NS_PRECONDITION(aSrcContentType != aOutContentType,
+ "ConvertData called when the two types are the same!");
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIStreamConverterService> StreamConvService =
+ do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ LOG((" Got converter service"));
+
+ // When applying stream decoders, it is necessary to "insert" an
+ // intermediate nsDocumentOpenInfo instance to handle the targeting of
+ // the "final" stream or streams.
+ //
+ // For certain content types (ie. multi-part/x-mixed-replace) the input
+ // stream is split up into multiple destination streams. This
+ // intermediate instance is used to target these "decoded" streams...
+ //
+ RefPtr<nsDocumentOpenInfo> nextLink =
+ new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
+
+ LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
+
+ // Make sure nextLink starts with the contentListener that said it wanted the
+ // results of this decode.
+ nextLink->m_contentListener = aListener;
+ // Also make sure it has to look for a stream listener to pump data into.
+ nextLink->m_targetStreamListener = nullptr;
+
+ // Make sure that nextLink treats the data as aOutContentType when
+ // dispatching; that way even if the stream converters don't change the type
+ // on the channel we will still do the right thing. If aOutContentType is
+ // */*, that's OK -- that will just indicate to nextLink that it should get
+ // the type off the channel.
+ nextLink->mContentType = aOutContentType;
+
+ // The following call sets m_targetStreamListener to the input end of the
+ // stream converter and sets the output end of the stream converter to
+ // nextLink. As we pump data into m_targetStreamListener the stream
+ // converter will convert it and pass the converted data to nextLink.
+ return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(),
+ PromiseFlatCString(aOutContentType).get(),
+ nextLink,
+ request,
+ getter_AddRefs(m_targetStreamListener));
+}
+
+bool
+nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
+ nsIChannel* aChannel)
+{
+ LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
+ this, mFlags));
+
+ NS_PRECONDITION(aListener, "Must have a non-null listener");
+ NS_PRECONDITION(aChannel, "Must have a channel");
+
+ bool listenerWantsContent = false;
+ nsXPIDLCString typeToUse;
+
+ if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
+ aListener->IsPreferred(mContentType.get(),
+ getter_Copies(typeToUse),
+ &listenerWantsContent);
+ } else {
+ aListener->CanHandleContent(mContentType.get(), false,
+ getter_Copies(typeToUse),
+ &listenerWantsContent);
+ }
+ if (!listenerWantsContent) {
+ LOG((" Listener is not interested"));
+ return false;
+ }
+
+ if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
+ // Need to do a conversion here.
+
+ nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
+
+ if (NS_FAILED(rv)) {
+ // No conversion path -- we don't want this listener, if we got one
+ m_targetStreamListener = nullptr;
+ }
+
+ LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
+
+ // m_targetStreamListener is now the input end of the converter, and we can
+ // just pump the data in there, if it exists. If it does not, we need to
+ // try other nsIURIContentListeners.
+ return m_targetStreamListener != nullptr;
+ }
+
+ // At this point, aListener wants data of type mContentType. Let 'em have
+ // it. But first, if we are retargeting, set an appropriate flag on the
+ // channel
+ nsLoadFlags loadFlags = 0;
+ aChannel->GetLoadFlags(&loadFlags);
+
+ // Set this flag to indicate that the channel has been targeted at a final
+ // consumer. This load flag is tested in nsDocLoader::OnProgress.
+ nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;
+
+ nsCOMPtr<nsIURIContentListener> originalListener =
+ do_GetInterface(m_originalContext);
+ if (originalListener != aListener) {
+ newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
+ }
+ aChannel->SetLoadFlags(loadFlags | newLoadFlags);
+
+ bool abort = false;
+ bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
+ nsresult rv = aListener->DoContent(mContentType,
+ isPreferred,
+ aChannel,
+ getter_AddRefs(m_targetStreamListener),
+ &abort);
+
+ if (NS_FAILED(rv)) {
+ LOG_ERROR((" DoContent failed"));
+
+ // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
+ aChannel->SetLoadFlags(loadFlags);
+ m_targetStreamListener = nullptr;
+ return false;
+ }
+
+ if (abort) {
+ // Nothing else to do here -- aListener is handling it all. Make
+ // sure m_targetStreamListener is null so we don't do anything
+ // after this point.
+ LOG((" Listener has aborted the load"));
+ m_targetStreamListener = nullptr;
+ }
+
+ NS_ASSERTION(abort || m_targetStreamListener, "DoContent returned no listener?");
+
+ // aListener is handling the load from this point on.
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Implementation of nsURILoader
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+nsURILoader::nsURILoader()
+{
+}
+
+nsURILoader::~nsURILoader()
+{
+}
+
+NS_IMPL_ADDREF(nsURILoader)
+NS_IMPL_RELEASE(nsURILoader)
+
+NS_INTERFACE_MAP_BEGIN(nsURILoader)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
+ NS_INTERFACE_MAP_ENTRY(nsIURILoader)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP nsURILoader::RegisterContentListener(nsIURIContentListener * aContentListener)
+{
+ nsresult rv = NS_OK;
+
+ nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
+ NS_ASSERTION(weakListener, "your URIContentListener must support weak refs!\n");
+
+ if (weakListener)
+ m_listeners.AppendObject(weakListener);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsURILoader::UnRegisterContentListener(nsIURIContentListener * aContentListener)
+{
+ nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
+ if (weakListener)
+ m_listeners.RemoveObject(weakListener);
+
+ return NS_OK;
+
+}
+
+NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
+ uint32_t aFlags,
+ nsIInterfaceRequestor *aWindowContext)
+{
+ NS_ENSURE_ARG_POINTER(channel);
+
+ if (LOG_ENABLED()) {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ nsAutoCString spec;
+ uri->GetAsciiSpec(spec);
+ LOG(("nsURILoader::OpenURI for %s", spec.get()));
+ }
+
+ nsCOMPtr<nsIStreamListener> loader;
+ nsresult rv = OpenChannel(channel,
+ aFlags,
+ aWindowContext,
+ false,
+ getter_AddRefs(loader));
+
+ if (NS_SUCCEEDED(rv)) {
+ // this method is not complete!!! Eventually, we should first go
+ // to the content listener and ask them for a protocol handler...
+ // if they don't give us one, we need to go to the registry and get
+ // the preferred protocol handler.
+
+ // But for now, I'm going to let necko do the work for us....
+ rv = channel->AsyncOpen(loader, nullptr);
+
+ // no content from this load - that's OK.
+ if (rv == NS_ERROR_NO_CONTENT) {
+ LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
+ rv = NS_OK;
+ }
+ } else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
+ // Not really an error, from this method's point of view
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+nsresult nsURILoader::OpenChannel(nsIChannel* channel,
+ uint32_t aFlags,
+ nsIInterfaceRequestor* aWindowContext,
+ bool aChannelIsOpen,
+ nsIStreamListener** aListener)
+{
+ NS_ASSERTION(channel, "Trying to open a null channel!");
+ NS_ASSERTION(aWindowContext, "Window context must not be null");
+
+ if (LOG_ENABLED()) {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ nsAutoCString spec;
+ uri->GetAsciiSpec(spec);
+ LOG(("nsURILoader::OpenChannel for %s", spec.get()));
+ }
+
+ // Let the window context's uriListener know that the open is starting. This
+ // gives that window a chance to abort the load process.
+ nsCOMPtr<nsIURIContentListener> winContextListener(do_GetInterface(aWindowContext));
+ if (winContextListener) {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri) {
+ bool doAbort = false;
+ winContextListener->OnStartURIOpen(uri, &doAbort);
+
+ if (doAbort) {
+ LOG((" OnStartURIOpen aborted load"));
+ return NS_ERROR_WONT_HANDLE_CONTENT;
+ }
+ }
+ }
+
+ // we need to create a DocumentOpenInfo object which will go ahead and open
+ // the url and discover the content type....
+ RefPtr<nsDocumentOpenInfo> loader =
+ new nsDocumentOpenInfo(aWindowContext, aFlags, this);
+
+ // Set the correct loadgroup on the channel
+ nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
+
+ if (!loadGroup) {
+ // XXXbz This context is violating what we'd like to be the new uriloader
+ // api.... Set up a nsDocLoader to handle the loadgroup for this context.
+ // This really needs to go away!
+ nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
+ if (listener) {
+ nsCOMPtr<nsISupports> cookie;
+ listener->GetLoadCookie(getter_AddRefs(cookie));
+ if (!cookie) {
+ RefPtr<nsDocLoader> newDocLoader = new nsDocLoader();
+ nsresult rv = newDocLoader->Init();
+ if (NS_FAILED(rv))
+ return rv;
+ rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader);
+ if (NS_FAILED(rv))
+ return rv;
+ cookie = nsDocLoader::GetAsSupports(newDocLoader);
+ listener->SetLoadCookie(cookie);
+ }
+ loadGroup = do_GetInterface(cookie);
+ }
+ }
+
+ // If the channel is pending, then we need to remove it from its current
+ // loadgroup
+ nsCOMPtr<nsILoadGroup> oldGroup;
+ channel->GetLoadGroup(getter_AddRefs(oldGroup));
+ if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) {
+ // It is important to add the channel to the new group before
+ // removing it from the old one, so that the load isn't considered
+ // done as soon as the request is removed.
+ loadGroup->AddRequest(channel, nullptr);
+
+ if (oldGroup) {
+ oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED);
+ }
+ }
+
+ channel->SetLoadGroup(loadGroup);
+
+ // prepare the loader for receiving data
+ nsresult rv = loader->Prepare();
+ if (NS_SUCCEEDED(rv))
+ NS_ADDREF(*aListener = loader);
+ return rv;
+}
+
+NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel,
+ uint32_t aFlags,
+ nsIInterfaceRequestor* aWindowContext,
+ nsIStreamListener** aListener)
+{
+ bool pending;
+ if (NS_FAILED(channel->IsPending(&pending))) {
+ pending = false;
+ }
+
+ return OpenChannel(channel, aFlags, aWindowContext, pending, aListener);
+}
+
+NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie)
+{
+ nsresult rv;
+ nsCOMPtr<nsIDocumentLoader> docLoader;
+
+ NS_ENSURE_ARG_POINTER(aLoadCookie);
+
+ docLoader = do_GetInterface(aLoadCookie, &rv);
+ if (docLoader) {
+ rv = docLoader->Stop();
+ }
+ return rv;
+}
+
diff --git a/uriloader/base/nsURILoader.h b/uriloader/base/nsURILoader.h
new file mode 100644
index 000000000..2c5648dba
--- /dev/null
+++ b/uriloader/base/nsURILoader.h
@@ -0,0 +1,59 @@
+/* -*- 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/. */
+
+#ifndef nsURILoader_h__
+#define nsURILoader_h__
+
+#include "nsCURILoader.h"
+#include "nsISupportsUtils.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsString.h"
+#include "nsIWeakReference.h"
+#include "mozilla/Attributes.h"
+
+#include "mozilla/Logging.h"
+
+class nsDocumentOpenInfo;
+
+class nsURILoader final : public nsIURILoader
+{
+public:
+ NS_DECL_NSIURILOADER
+ NS_DECL_ISUPPORTS
+
+ nsURILoader();
+
+protected:
+ ~nsURILoader();
+
+ /**
+ * Equivalent to nsIURILoader::openChannel, but allows specifying whether the
+ * channel is opened already.
+ */
+ MOZ_MUST_USE nsresult OpenChannel(nsIChannel* channel,
+ uint32_t aFlags,
+ nsIInterfaceRequestor* aWindowContext,
+ bool aChannelOpen,
+ nsIStreamListener** aListener);
+
+ /**
+ * we shouldn't need to have an owning ref count on registered
+ * content listeners because they are supposed to unregister themselves
+ * when they go away. This array stores weak references
+ */
+ nsCOMArray<nsIWeakReference> m_listeners;
+
+ /**
+ * Logging. The module is called "URILoader"
+ */
+ static mozilla::LazyLogModule mLog;
+
+ friend class nsDocumentOpenInfo;
+};
+
+#endif /* nsURILoader_h__ */