import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _adivertablechannelparent_h_
#define _adivertablechannelparent_h_
#include "nsISupports.h"
class nsIStreamListener;
namespace mozilla {
namespace net {
// To be implemented by a channel's parent actors, e.g. HttpChannelParent
// and FTPChannelParent. Used by ChannelDiverterParent to divert
// nsIStreamListener callbacks from the child process to a new
// listener in the parent process.
class ADivertableParentChannel : public nsISupports
{
public:
// Called by ChannelDiverterParent::DivertTo(nsIStreamListener*).
// The listener should now be used to received nsIStreamListener callbacks,
// i.e. OnStartRequest, OnDataAvailable and OnStopRequest, as if it had been
// passed to AsyncOpen for the channel. A reference to the listener will be
// added and kept until OnStopRequest has completed.
virtual void DivertTo(nsIStreamListener *aListener) = 0;
// Called to suspend parent channel in ChannelDiverterParent constructor.
virtual nsresult SuspendForDiversion() = 0;
// While messages are diverted back from the child to the parent calls to
// suspend/resume the channel must also suspend/resume the message diversion.
// These two functions will be called by nsHttpChannel and nsFtpChannel
// Suspend()/Resume() functions.
virtual nsresult SuspendMessageDiversion() = 0;
virtual nsresult ResumeMessageDiversion() = 0;
};
} // namespace net
} // namespace mozilla
#endif

32
netwerk/base/ARefBase.h Normal file
View file

@ -0,0 +1,32 @@
/* -*- 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 mozilla_net_ARefBase_h
#define mozilla_net_ARefBase_h
#include "nscore.h"
namespace mozilla { namespace net {
// This is an abstract class that can be pointed to by either
// nsCOMPtr or nsRefPtr. nsHttpConnectionMgr uses it for generic
// objects that need to be reference counted - similiar to nsISupports
// but it may or may not be xpcom.
class ARefBase
{
public:
ARefBase() {}
virtual ~ARefBase() {}
NS_IMETHOD_ (MozExternalRefCountType) AddRef() = 0;
NS_IMETHOD_ (MozExternalRefCountType) Release() = 0;
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,120 @@
/* -*- Mode: C++; 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 <algorithm>
#include "ArrayBufferInputStream.h"
#include "nsStreamUtils.h"
#include "jsapi.h"
#include "jsfriendapi.h"
NS_IMPL_ISUPPORTS(ArrayBufferInputStream, nsIArrayBufferInputStream, nsIInputStream);
ArrayBufferInputStream::ArrayBufferInputStream()
: mBufferLength(0)
, mPos(0)
, mClosed(false)
{
}
NS_IMETHODIMP
ArrayBufferInputStream::SetData(JS::Handle<JS::Value> aBuffer,
uint32_t aByteOffset,
uint32_t aLength,
JSContext* aCx)
{
if (!aBuffer.isObject()) {
return NS_ERROR_FAILURE;
}
JS::RootedObject arrayBuffer(aCx, &aBuffer.toObject());
if (!JS_IsArrayBufferObject(arrayBuffer)) {
return NS_ERROR_FAILURE;
}
uint32_t buflen = JS_GetArrayBufferByteLength(arrayBuffer);
uint32_t offset = std::min(buflen, aByteOffset);
mBufferLength = std::min(buflen - offset, aLength);
mArrayBuffer = mozilla::MakeUnique<char[]>(mBufferLength);
JS::AutoCheckCannotGC nogc;
bool isShared;
char* src = (char*) JS_GetArrayBufferData(arrayBuffer, &isShared, nogc) + offset;
memcpy(&mArrayBuffer[0], src, mBufferLength);
return NS_OK;
}
NS_IMETHODIMP
ArrayBufferInputStream::Close()
{
mClosed = true;
return NS_OK;
}
NS_IMETHODIMP
ArrayBufferInputStream::Available(uint64_t* aCount)
{
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
if (mArrayBuffer) {
*aCount = mBufferLength ? mBufferLength - mPos : 0;
} else {
*aCount = 0;
}
return NS_OK;
}
NS_IMETHODIMP
ArrayBufferInputStream::Read(char* aBuf, uint32_t aCount, uint32_t *aReadCount)
{
return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
}
NS_IMETHODIMP
ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
uint32_t aCount, uint32_t *result)
{
NS_ASSERTION(result, "null ptr");
NS_ASSERTION(mBufferLength >= mPos, "bad stream state");
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
MOZ_ASSERT(mArrayBuffer || (mPos == mBufferLength), "stream inited incorrectly");
*result = 0;
while (mPos < mBufferLength) {
uint32_t remaining = mBufferLength - mPos;
MOZ_ASSERT(mArrayBuffer);
uint32_t count = std::min(aCount, remaining);
if (count == 0) {
break;
}
uint32_t written;
nsresult rv = writer(this, closure, &mArrayBuffer[0] + mPos, *result, count, &written);
if (NS_FAILED(rv)) {
// InputStreams do not propagate errors to caller.
return NS_OK;
}
NS_ASSERTION(written <= count,
"writer should not write more than we asked it to write");
mPos += written;
*result += written;
aCount -= written;
}
return NS_OK;
}
NS_IMETHODIMP
ArrayBufferInputStream::IsNonBlocking(bool *aNonBlocking)
{
*aNonBlocking = true;
return NS_OK;
}

View file

@ -0,0 +1,38 @@
/* -*- Mode: C++; 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/. */
#ifndef ArrayBufferInputStream_h
#define ArrayBufferInputStream_h
#include "nsIArrayBufferInputStream.h"
#include "js/Value.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#define NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID "@mozilla.org/io/arraybuffer-input-stream;1"
#define NS_ARRAYBUFFERINPUTSTREAM_CID \
{ /* 3014dde6-aa1c-41db-87d0-48764a3710f6 */ \
0x3014dde6, \
0xaa1c, \
0x41db, \
{0x87, 0xd0, 0x48, 0x76, 0x4a, 0x37, 0x10, 0xf6} \
}
class ArrayBufferInputStream : public nsIArrayBufferInputStream {
public:
ArrayBufferInputStream();
NS_DECL_ISUPPORTS
NS_DECL_NSIARRAYBUFFERINPUTSTREAM
NS_DECL_NSIINPUTSTREAM
private:
virtual ~ArrayBufferInputStream() {}
mozilla::UniquePtr<char[]> mArrayBuffer;
uint32_t mBufferLength;
uint32_t mPos;
bool mClosed;
};
#endif // ArrayBufferInputStream_h

77
netwerk/base/AutoClose.h Normal file
View file

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_AutoClose_h
#define mozilla_net_AutoClose_h
#include "nsCOMPtr.h"
#include "mozilla/Mutex.h"
namespace mozilla { namespace net {
// Like an nsAutoPtr for XPCOM streams (e.g. nsIAsyncInputStream) and other
// refcounted classes that need to have the Close() method called explicitly
// before they are destroyed.
template <typename T>
class AutoClose
{
public:
AutoClose() : mMutex("net::AutoClose.mMutex") { }
~AutoClose(){
CloseAndRelease();
}
explicit operator bool()
{
MutexAutoLock lock(mMutex);
return mPtr;
}
already_AddRefed<T> forget()
{
MutexAutoLock lock(mMutex);
return mPtr.forget();
}
void takeOver(nsCOMPtr<T> & rhs)
{
already_AddRefed<T> other = rhs.forget();
TakeOverInternal(&other);
}
void CloseAndRelease()
{
TakeOverInternal(nullptr);
}
private:
void TakeOverInternal(already_AddRefed<T> *aOther)
{
nsCOMPtr<T> ptr;
{
MutexAutoLock lock(mMutex);
ptr.swap(mPtr);
if (aOther) {
mPtr = *aOther;
}
}
if (ptr) {
ptr->Close();
}
}
void operator=(const AutoClose<T> &) = delete;
AutoClose(const AutoClose<T> &) = delete;
nsCOMPtr<T> mPtr;
Mutex mMutex;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_AutoClose_h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,418 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This file defines two implementations of the nsIBackgroundFileSaver
* interface. See the "test_backgroundfilesaver.js" file for usage examples.
*/
#ifndef BackgroundFileSaver_h__
#define BackgroundFileSaver_h__
#include "ScopedNSSTypes.h"
#include "mozilla/Mutex.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsIAsyncOutputStream.h"
#include "nsIBackgroundFileSaver.h"
#include "nsIStreamListener.h"
#include "nsNSSShutDown.h"
#include "nsStreamUtils.h"
#include "nsString.h"
class nsIAsyncInputStream;
class nsIThread;
class nsIX509CertList;
namespace mozilla {
namespace net {
class DigestOutputStream;
////////////////////////////////////////////////////////////////////////////////
//// BackgroundFileSaver
class BackgroundFileSaver : public nsIBackgroundFileSaver,
public nsNSSShutDownObject
{
public:
NS_DECL_NSIBACKGROUNDFILESAVER
BackgroundFileSaver();
/**
* Initializes the pipe and the worker thread on XPCOM construction.
*
* This is called automatically by the XPCOM infrastructure, and if this
* fails, the factory will delete this object without returning a reference.
*/
nsresult Init();
/**
* Used by nsNSSShutDownList to manage nsNSSShutDownObjects.
*/
void virtualDestroyNSSReference() override;
/**
* Number of worker threads that are currently running.
*/
static uint32_t sThreadCount;
/**
* Maximum number of worker threads reached during the current download session,
* used for telemetry.
*
* When there are no more worker threads running, we consider the download
* session finished, and this counter is reset.
*/
static uint32_t sTelemetryMaxThreadCount;
protected:
virtual ~BackgroundFileSaver();
/**
* Helper function for managing NSS objects (mDigestContext).
*/
void destructorSafeDestroyNSSReference();
/**
* Thread that constructed this object.
*/
nsCOMPtr<nsIThread> mControlThread;
/**
* Thread to which the actual input/output is delegated.
*/
nsCOMPtr<nsIThread> mWorkerThread;
/**
* Stream that receives data from derived classes. The received data will be
* available to the worker thread through mPipeInputStream. This is an
* instance of nsPipeOutputStream, not BackgroundFileSaverOutputStream.
*/
nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream;
/**
* Used during initialization, determines if the pipe is created with an
* infinite buffer. An infinite buffer is required if the derived class
* implements nsIStreamListener, because this interface requires all the
* provided data to be consumed synchronously.
*/
virtual bool HasInfiniteBuffer() = 0;
/**
* Used by derived classes if they need to be called back while copying.
*/
virtual nsAsyncCopyProgressFun GetProgressCallback() = 0;
/**
* Stream used by the worker thread to read the data to be saved.
*/
nsCOMPtr<nsIAsyncInputStream> mPipeInputStream;
private:
friend class NotifyTargetChangeRunnable;
/**
* Matches the nsIBackgroundFileSaver::observer property.
*
* @remarks This is a strong reference so that JavaScript callers don't need
* to worry about keeping another reference to the observer.
*/
nsCOMPtr<nsIBackgroundFileSaverObserver> mObserver;
//////////////////////////////////////////////////////////////////////////////
//// Shared state between control and worker threads
/**
* Protects the shared state between control and worker threads. This mutex
* is always locked for a very short time, never during input/output.
*/
mozilla::Mutex mLock;
/**
* True if the worker thread is already waiting to process a change in state.
*/
bool mWorkerThreadAttentionRequested;
/**
* True if the operation should finish as soon as possibile.
*/
bool mFinishRequested;
/**
* True if the operation completed, with either success or failure.
*/
bool mComplete;
/**
* Holds the current file saver status. This is a success status while the
* object is working correctly, and remains such if the operation completes
* successfully. This becomes an error status when an error occurs on the
* worker thread, or when the operation is canceled.
*/
nsresult mStatus;
/**
* True if we should append data to the initial target file, instead of
* overwriting it.
*/
bool mAppend;
/**
* This is set by the first SetTarget call on the control thread, and contains
* the target file name that will be used by the worker thread, as soon as it
* is possible to update mActualTarget and open the file. This is null if no
* target was ever assigned to this object.
*/
nsCOMPtr<nsIFile> mInitialTarget;
/**
* This is set by the first SetTarget call on the control thread, and
* indicates whether mInitialTarget should be kept as partially completed,
* rather than deleted, if the operation fails or is canceled.
*/
bool mInitialTargetKeepPartial;
/**
* This is set by subsequent SetTarget calls on the control thread, and
* contains the new target file name to which the worker thread will move the
* target file, as soon as it can be done. This is null if SetTarget was
* called only once, or no target was ever assigned to this object.
*
* The target file can be renamed multiple times, though only the most recent
* rename is guaranteed to be processed by the worker thread.
*/
nsCOMPtr<nsIFile> mRenamedTarget;
/**
* This is set by subsequent SetTarget calls on the control thread, and
* indicates whether mRenamedTarget should be kept as partially completed,
* rather than deleted, if the operation fails or is canceled.
*/
bool mRenamedTargetKeepPartial;
/**
* While NS_AsyncCopy is in progress, allows canceling it. Null otherwise.
* This is read by both threads but only written by the worker thread.
*/
nsCOMPtr<nsISupports> mAsyncCopyContext;
/**
* The SHA 256 hash in raw bytes of the downloaded file. This is written
* by the worker thread but can be read on the main thread.
*/
nsCString mSha256;
/**
* Whether or not to compute the hash. Must be set on the main thread before
* setTarget is called.
*/
bool mSha256Enabled;
/**
* Store the signature info.
*/
nsCOMArray<nsIX509CertList> mSignatureInfo;
/**
* Whether or not to extract the signature. Must be set on the main thread
* before setTarget is called.
*/
bool mSignatureInfoEnabled;
//////////////////////////////////////////////////////////////////////////////
//// State handled exclusively by the worker thread
/**
* Current target file associated to the input and output streams.
*/
nsCOMPtr<nsIFile> mActualTarget;
/**
* Indicates whether mActualTarget should be kept as partially completed,
* rather than deleted, if the operation fails or is canceled.
*/
bool mActualTargetKeepPartial;
/**
* Used to calculate the file hash. This keeps state across file renames and
* is lazily initialized in ProcessStateChange.
*/
UniquePK11Context mDigestContext;
//////////////////////////////////////////////////////////////////////////////
//// Private methods
/**
* Called when NS_AsyncCopy completes.
*
* @param aClosure
* Populated with a raw pointer to the BackgroundFileSaver object.
* @param aStatus
* Success or failure status specified when the copy was interrupted.
*/
static void AsyncCopyCallback(void *aClosure, nsresult aStatus);
/**
* Called on the control thread after state changes, to ensure that the worker
* thread will process the state change appropriately.
*
* @param aShouldInterruptCopy
* If true, the current NS_AsyncCopy, if any, is canceled.
*/
nsresult GetWorkerThreadAttention(bool aShouldInterruptCopy);
/**
* Event called on the worker thread to begin processing a state change.
*/
nsresult ProcessAttention();
/**
* Called by ProcessAttention to execute the operations corresponding to the
* state change. If this results in an error, ProcessAttention will force the
* entire operation to be aborted.
*/
nsresult ProcessStateChange();
/**
* Returns true if completion conditions are met on the worker thread. The
* first time this happens, posts the completion event to the control thread.
*/
bool CheckCompletion();
/**
* Event called on the control thread to indicate that file contents will now
* be saved to the specified file.
*/
nsresult NotifyTargetChange(nsIFile *aTarget);
/**
* Event called on the control thread to send the final notification.
*/
nsresult NotifySaveComplete();
/**
* Verifies the signature of the binary at the specified file path and stores
* the signature data in mSignatureInfo. We extract only X.509 certificates,
* since that is what Google's Safebrowsing protocol specifies.
*/
nsresult ExtractSignatureInfo(const nsAString& filePath);
};
////////////////////////////////////////////////////////////////////////////////
//// BackgroundFileSaverOutputStream
class BackgroundFileSaverOutputStream : public BackgroundFileSaver
, public nsIAsyncOutputStream
, public nsIOutputStreamCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
NS_DECL_NSIASYNCOUTPUTSTREAM
NS_DECL_NSIOUTPUTSTREAMCALLBACK
BackgroundFileSaverOutputStream();
protected:
virtual bool HasInfiniteBuffer() override;
virtual nsAsyncCopyProgressFun GetProgressCallback() override;
private:
~BackgroundFileSaverOutputStream();
/**
* Original callback provided to our AsyncWait wrapper.
*/
nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback;
};
////////////////////////////////////////////////////////////////////////////////
//// BackgroundFileSaverStreamListener. This class is instantiated by
// nsExternalHelperAppService, DownloadCore.jsm, and possibly others.
class BackgroundFileSaverStreamListener final : public BackgroundFileSaver
, public nsIStreamListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
BackgroundFileSaverStreamListener();
protected:
virtual bool HasInfiniteBuffer() override;
virtual nsAsyncCopyProgressFun GetProgressCallback() override;
private:
~BackgroundFileSaverStreamListener();
/**
* Protects the state related to whether the request should be suspended.
*/
mozilla::Mutex mSuspensionLock;
/**
* Whether we should suspend the request because we received too much data.
*/
bool mReceivedTooMuchData;
/**
* Request for which we received too much data. This is populated when
* mReceivedTooMuchData becomes true for the first time.
*/
nsCOMPtr<nsIRequest> mRequest;
/**
* Whether mRequest is currently suspended.
*/
bool mRequestSuspended;
/**
* Called while NS_AsyncCopy is copying data.
*/
static void AsyncCopyProgressCallback(void *aClosure, uint32_t aCount);
/**
* Called on the control thread to suspend or resume the request.
*/
nsresult NotifySuspendOrResume();
};
// A wrapper around nsIOutputStream, so that we can compute hashes on the
// stream without copying and without polluting pristine NSS code with XPCOM
// interfaces.
class DigestOutputStream : public nsNSSShutDownObject,
public nsIOutputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
// Constructor. Neither parameter may be null. The caller owns both.
DigestOutputStream(nsIOutputStream* outputStream, PK11Context* aContext);
// We don't own any NSS objects here, so no need to clean up
void virtualDestroyNSSReference() override { }
private:
~DigestOutputStream();
// Calls to write are passed to this stream.
nsCOMPtr<nsIOutputStream> mOutputStream;
// Digest context used to compute the hash, owned by the caller.
PK11Context* mDigestContext;
// Don't accidentally copy construct.
DigestOutputStream(const DigestOutputStream& d);
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,366 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/net/CaptivePortalService.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsXULAppAPI.h"
static const char16_t kInterfaceName[] = u"captive-portal-inteface";
static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
static const char kAbortCaptivePortalLoginEvent[] = "captive-portal-login-abort";
static const char kCaptivePortalLoginSuccessEvent[] = "captive-portal-login-success";
static const uint32_t kDefaultInterval = 60*1000; // check every 60 seconds
namespace mozilla {
namespace net {
static LazyLogModule gCaptivePortalLog("CaptivePortalService");
#undef LOG
#define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
nsISupportsWeakReference, nsITimerCallback,
nsICaptivePortalCallback)
CaptivePortalService::CaptivePortalService()
: mState(UNKNOWN)
, mStarted(false)
, mInitialized(false)
, mRequestInProgress(false)
, mEverBeenCaptive(false)
, mDelay(kDefaultInterval)
, mSlackCount(0)
, mMinInterval(kDefaultInterval)
, mMaxInterval(25*kDefaultInterval)
, mBackoffFactor(5.0)
{
mLastChecked = TimeStamp::Now();
}
CaptivePortalService::~CaptivePortalService()
{
LOG(("CaptivePortalService::~CaptivePortalService isParentProcess:%d\n",
XRE_GetProcessType() == GeckoProcessType_Default));
}
nsresult
CaptivePortalService::PerformCheck()
{
LOG(("CaptivePortalService::PerformCheck mRequestInProgress:%d mInitialized:%d mStarted:%d\n",
mRequestInProgress, mInitialized, mStarted));
// Don't issue another request if last one didn't complete
if (mRequestInProgress || !mInitialized || !mStarted) {
return NS_OK;
}
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
nsresult rv;
if (!mCaptivePortalDetector) {
mCaptivePortalDetector =
do_GetService("@mozilla.org/toolkit/captive-detector;1", &rv);
if (NS_FAILED(rv)) {
LOG(("Unable to get a captive portal detector\n"));
return rv;
}
}
LOG(("CaptivePortalService::PerformCheck - Calling CheckCaptivePortal\n"));
mRequestInProgress = true;
mCaptivePortalDetector->CheckCaptivePortal(kInterfaceName, this);
return NS_OK;
}
nsresult
CaptivePortalService::RearmTimer()
{
LOG(("CaptivePortalService::RearmTimer\n"));
// Start a timer to recheck
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
if (mTimer) {
mTimer->Cancel();
}
// If we have successfully determined the state, and we have never detected
// a captive portal, we don't need to keep polling, but will rely on events
// to trigger detection.
if (mState == NOT_CAPTIVE) {
return NS_OK;
}
if (!mTimer) {
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
}
if (mTimer && mDelay > 0) {
LOG(("CaptivePortalService - Reloading timer with delay %u\n", mDelay));
return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
nsresult
CaptivePortalService::Initialize()
{
if (mInitialized) {
return NS_OK;
}
mInitialized = true;
// Only the main process service should actually do anything. The service in
// the content process only mirrors the CP state in the main process.
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return NS_OK;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true);
observerService->AddObserver(this, kAbortCaptivePortalLoginEvent, true);
observerService->AddObserver(this, kCaptivePortalLoginSuccessEvent, true);
}
LOG(("Initialized CaptivePortalService\n"));
return NS_OK;
}
nsresult
CaptivePortalService::Start()
{
if (!mInitialized) {
return NS_ERROR_NOT_INITIALIZED;
}
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// Doesn't do anything if called in the content process.
return NS_OK;
}
if (mStarted) {
return NS_OK;
}
MOZ_ASSERT(mState == UNKNOWN, "Initial state should be UNKNOWN");
mStarted = true;
mEverBeenCaptive = false;
// Get the delay prefs
Preferences::GetUint("network.captive-portal-service.minInterval", &mMinInterval);
Preferences::GetUint("network.captive-portal-service.maxInterval", &mMaxInterval);
Preferences::GetFloat("network.captive-portal-service.backoffFactor", &mBackoffFactor);
LOG(("CaptivePortalService::Start min:%u max:%u backoff:%.2f\n",
mMinInterval, mMaxInterval, mBackoffFactor));
mSlackCount = 0;
mDelay = mMinInterval;
// When Start is called, perform a check immediately
PerformCheck();
RearmTimer();
return NS_OK;
}
nsresult
CaptivePortalService::Stop()
{
LOG(("CaptivePortalService::Stop\n"));
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// Doesn't do anything when called in the content process.
return NS_OK;
}
if (!mStarted) {
return NS_OK;
}
if (mTimer) {
mTimer->Cancel();
}
mTimer = nullptr;
mRequestInProgress = false;
mStarted = false;
if (mCaptivePortalDetector) {
mCaptivePortalDetector->Abort(kInterfaceName);
}
mCaptivePortalDetector = nullptr;
// Clear the state in case anyone queries the state while detection is off.
mState = UNKNOWN;
return NS_OK;
}
void
CaptivePortalService::SetStateInChild(int32_t aState)
{
// This should only be called in the content process, from ContentChild.cpp
// in order to mirror the captive portal state set in the chrome process.
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
mState = aState;
mLastChecked = TimeStamp::Now();
}
//-----------------------------------------------------------------------------
// CaptivePortalService::nsICaptivePortalService
//-----------------------------------------------------------------------------
NS_IMETHODIMP
CaptivePortalService::GetState(int32_t *aState)
{
*aState = mState;
return NS_OK;
}
NS_IMETHODIMP
CaptivePortalService::RecheckCaptivePortal()
{
LOG(("CaptivePortalService::RecheckCaptivePortal\n"));
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// Doesn't do anything if called in the content process.
return NS_OK;
}
// This is called for user activity. We need to reset the slack count,
// so the checks continue to be quite frequent.
mSlackCount = 0;
mDelay = mMinInterval;
PerformCheck();
RearmTimer();
return NS_OK;
}
NS_IMETHODIMP
CaptivePortalService::GetLastChecked(uint64_t *aLastChecked)
{
double duration = (TimeStamp::Now() - mLastChecked).ToMilliseconds();
*aLastChecked = static_cast<uint64_t>(duration);
return NS_OK;
}
//-----------------------------------------------------------------------------
// CaptivePortalService::nsITimer
// This callback gets called every mDelay miliseconds
// It issues a checkCaptivePortal operation if one isn't already in progress
//-----------------------------------------------------------------------------
NS_IMETHODIMP
CaptivePortalService::Notify(nsITimer *aTimer)
{
LOG(("CaptivePortalService::Notify\n"));
MOZ_ASSERT(aTimer == mTimer);
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
PerformCheck();
// This is needed because we don't want to always make requests very often.
// Every 10 checks, we the delay is increased mBackoffFactor times
// to a maximum delay of mMaxInterval
mSlackCount++;
if (mSlackCount % 10 == 0) {
mDelay = mDelay * mBackoffFactor;
}
if (mDelay > mMaxInterval) {
mDelay = mMaxInterval;
}
// Note - if mDelay is 0, the timer will not be rearmed.
RearmTimer();
return NS_OK;
}
//-----------------------------------------------------------------------------
// CaptivePortalService::nsIObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
CaptivePortalService::Observe(nsISupports *aSubject,
const char * aTopic,
const char16_t * aData)
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// Doesn't do anything if called in the content process.
return NS_OK;
}
LOG(("CaptivePortalService::Observe() topic=%s\n", aTopic));
if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
// A redirect or altered content has been detected.
// The user needs to log in. We are in a captive portal.
mState = LOCKED_PORTAL;
mLastChecked = TimeStamp::Now();
mEverBeenCaptive = true;
} else if (!strcmp(aTopic, kCaptivePortalLoginSuccessEvent)) {
// The user has successfully logged in. We have connectivity.
mState = UNLOCKED_PORTAL;
mLastChecked = TimeStamp::Now();
mSlackCount = 0;
mDelay = mMinInterval;
RearmTimer();
} else if (!strcmp(aTopic, kAbortCaptivePortalLoginEvent)) {
// The login has been aborted
mState = UNKNOWN;
mLastChecked = TimeStamp::Now();
mSlackCount = 0;
}
// Send notification so that the captive portal state is mirrored in the
// content process.
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (observerService) {
nsCOMPtr<nsICaptivePortalService> cps(this);
observerService->NotifyObservers(cps, NS_IPC_CAPTIVE_PORTAL_SET_STATE, nullptr);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// CaptivePortalService::nsICaptivePortalCallback
//-----------------------------------------------------------------------------
NS_IMETHODIMP
CaptivePortalService::Prepare()
{
LOG(("CaptivePortalService::Prepare\n"));
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
// XXX: Finish preparation shouldn't be called until dns and routing is available.
if (mCaptivePortalDetector) {
mCaptivePortalDetector->FinishPreparation(kInterfaceName);
}
return NS_OK;
}
NS_IMETHODIMP
CaptivePortalService::Complete(bool success)
{
LOG(("CaptivePortalService::Complete(success=%d) mState=%d\n", success, mState));
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
mLastChecked = TimeStamp::Now();
// Note: this callback gets called when:
// 1. the request is completed, and content is valid (success == true)
// 2. when the request is aborted or times out (success == false)
if (success) {
if (mEverBeenCaptive) {
mState = UNLOCKED_PORTAL;
} else {
mState = NOT_CAPTIVE;
}
}
mRequestInProgress = false;
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,70 @@
/* 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 CaptivePortalService_h_
#define CaptivePortalService_h_
#include "nsICaptivePortalService.h"
#include "nsICaptivePortalDetector.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsITimer.h"
#include "nsCOMArray.h"
#include "mozilla/TimeStamp.h"
namespace mozilla {
namespace net {
class CaptivePortalService
: public nsICaptivePortalService
, public nsIObserver
, public nsSupportsWeakReference
, public nsITimerCallback
, public nsICaptivePortalCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICAPTIVEPORTALSERVICE
NS_DECL_NSIOBSERVER
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSICAPTIVEPORTALCALLBACK
CaptivePortalService();
nsresult Initialize();
nsresult Start();
nsresult Stop();
// This method is only called in the content process, in order to mirror
// the captive portal state in the parent process.
void SetStateInChild(int32_t aState);
private:
virtual ~CaptivePortalService();
nsresult PerformCheck();
nsresult RearmTimer();
nsCOMPtr<nsICaptivePortalDetector> mCaptivePortalDetector;
int32_t mState;
nsCOMPtr<nsITimer> mTimer;
bool mStarted;
bool mInitialized;
bool mRequestInProgress;
bool mEverBeenCaptive;
uint32_t mDelay;
int32_t mSlackCount;
uint32_t mMinInterval;
uint32_t mMaxInterval;
float mBackoffFactor;
// This holds a timestamp when the last time when the captive portal check
// has changed state.
mozilla::TimeStamp mLastChecked;
};
} // namespace net
} // namespace mozilla
#endif // CaptivePortalService_h_

View file

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/net/ChannelDiverterChild.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/FTPChannelChild.h"
#include "mozilla/net/PHttpChannelChild.h"
#include "mozilla/net/PFTPChannelChild.h"
#include "nsIDivertableChannel.h"
namespace mozilla {
namespace net {
ChannelDiverterChild::ChannelDiverterChild()
{
}
ChannelDiverterChild::~ChannelDiverterChild()
{
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _channeldiverterchild_h_
#define _channeldiverterchild_h_
#include "mozilla/net/PChannelDiverterChild.h"
namespace mozilla {
namespace net {
class ChannelDiverterChild :
public PChannelDiverterChild
{
public:
ChannelDiverterChild();
virtual ~ChannelDiverterChild();
};
} // namespace net
} // namespace mozilla
#endif /* _channeldiverterchild_h_ */

View file

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/net/ChannelDiverterParent.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "mozilla/net/HttpChannelParent.h"
#include "mozilla/net/FTPChannelParent.h"
#include "mozilla/net/PHttpChannelParent.h"
#include "mozilla/net/PFTPChannelParent.h"
#include "ADivertableParentChannel.h"
namespace mozilla {
namespace net {
ChannelDiverterParent::ChannelDiverterParent()
{
}
ChannelDiverterParent::~ChannelDiverterParent()
{
}
bool
ChannelDiverterParent::Init(const ChannelDiverterArgs& aArgs)
{
switch (aArgs.type()) {
case ChannelDiverterArgs::THttpChannelDiverterArgs:
{
auto httpParent = static_cast<HttpChannelParent*>(
aArgs.get_HttpChannelDiverterArgs().mChannelParent());
httpParent->SetApplyConversion(aArgs.get_HttpChannelDiverterArgs().mApplyConversion());
mDivertableChannelParent =
static_cast<ADivertableParentChannel*>(httpParent);
break;
}
case ChannelDiverterArgs::TPFTPChannelParent:
{
mDivertableChannelParent = static_cast<ADivertableParentChannel*>(
static_cast<FTPChannelParent*>(aArgs.get_PFTPChannelParent()));
break;
}
default:
NS_NOTREACHED("unknown ChannelDiverterArgs type");
return false;
}
MOZ_ASSERT(mDivertableChannelParent);
nsresult rv = mDivertableChannelParent->SuspendForDiversion();
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
void
ChannelDiverterParent::DivertTo(nsIStreamListener* newListener)
{
MOZ_ASSERT(newListener);
MOZ_ASSERT(mDivertableChannelParent);
mDivertableChannelParent->DivertTo(newListener);
}
void
ChannelDiverterParent::ActorDestroy(ActorDestroyReason aWhy)
{
// Implement me! Bug 1005179
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _channeldiverterparent_h_
#define _channeldiverterparent_h_
#include "mozilla/net/PChannelDiverterParent.h"
class nsIStreamListener;
namespace mozilla {
namespace net {
class ChannelDiverterArgs;
class ADivertableParentChannel;
class ChannelDiverterParent :
public PChannelDiverterParent
{
public:
ChannelDiverterParent();
virtual ~ChannelDiverterParent();
bool Init(const ChannelDiverterArgs& aArgs);
void DivertTo(nsIStreamListener* newListener);
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:
RefPtr<ADivertableParentChannel> mDivertableChannelParent;
};
} // namespace net
} // namespace mozilla
#endif /* _channeldiverterparent_h_ */

921
netwerk/base/Dashboard.cpp Normal file
View file

@ -0,0 +1,921 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http:mozilla.org/MPL/2.0/. */
#include "mozilla/dom/NetDashboardBinding.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/net/Dashboard.h"
#include "mozilla/net/HttpInfo.h"
#include "nsHttp.h"
#include "nsICancelable.h"
#include "nsIDNSService.h"
#include "nsIDNSRecord.h"
#include "nsIInputStream.h"
#include "nsISocketTransport.h"
#include "nsIThread.h"
#include "nsProxyRelease.h"
#include "nsSocketTransportService2.h"
#include "nsThreadUtils.h"
#include "nsURLHelper.h"
#include "mozilla/Logging.h"
using mozilla::AutoSafeJSContext;
using mozilla::dom::Sequence;
using mozilla::dom::ToJSValue;
namespace mozilla {
namespace net {
class SocketData
: public nsISupports
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
SocketData()
{
mTotalSent = 0;
mTotalRecv = 0;
mThread = nullptr;
}
uint64_t mTotalSent;
uint64_t mTotalRecv;
nsTArray<SocketInfo> mData;
nsMainThreadPtrHandle<NetDashboardCallback> mCallback;
nsIThread *mThread;
private:
virtual ~SocketData()
{
}
};
static void GetErrorString(nsresult rv, nsAString& errorString);
NS_IMPL_ISUPPORTS0(SocketData)
class HttpData
: public nsISupports
{
virtual ~HttpData()
{
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
HttpData()
{
mThread = nullptr;
}
nsTArray<HttpRetParams> mData;
nsMainThreadPtrHandle<NetDashboardCallback> mCallback;
nsIThread *mThread;
};
NS_IMPL_ISUPPORTS0(HttpData)
class WebSocketRequest
: public nsISupports
{
virtual ~WebSocketRequest()
{
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
WebSocketRequest()
{
mThread = nullptr;
}
nsMainThreadPtrHandle<NetDashboardCallback> mCallback;
nsIThread *mThread;
};
NS_IMPL_ISUPPORTS0(WebSocketRequest)
class DnsData
: public nsISupports
{
virtual ~DnsData()
{
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
DnsData()
{
mThread = nullptr;
}
nsTArray<DNSCacheEntries> mData;
nsMainThreadPtrHandle<NetDashboardCallback> mCallback;
nsIThread *mThread;
};
NS_IMPL_ISUPPORTS0(DnsData)
class ConnectionData
: public nsITransportEventSink
, public nsITimerCallback
{
virtual ~ConnectionData()
{
if (mTimer) {
mTimer->Cancel();
}
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSITIMERCALLBACK
void StartTimer(uint32_t aTimeout);
void StopTimer();
explicit ConnectionData(Dashboard *target)
{
mThread = nullptr;
mDashboard = target;
}
nsCOMPtr<nsISocketTransport> mSocket;
nsCOMPtr<nsIInputStream> mStreamIn;
nsCOMPtr<nsITimer> mTimer;
nsMainThreadPtrHandle<NetDashboardCallback> mCallback;
nsIThread *mThread;
Dashboard *mDashboard;
nsCString mHost;
uint32_t mPort;
const char *mProtocol;
uint32_t mTimeout;
nsString mStatus;
};
NS_IMPL_ISUPPORTS(ConnectionData, nsITransportEventSink, nsITimerCallback)
NS_IMETHODIMP
ConnectionData::OnTransportStatus(nsITransport *aTransport, nsresult aStatus,
int64_t aProgress, int64_t aProgressMax)
{
if (aStatus == NS_NET_STATUS_CONNECTED_TO) {
StopTimer();
}
GetErrorString(aStatus, mStatus);
mThread->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>
(mDashboard, &Dashboard::GetConnectionStatus, this),
NS_DISPATCH_NORMAL);
return NS_OK;
}
NS_IMETHODIMP
ConnectionData::Notify(nsITimer *aTimer)
{
MOZ_ASSERT(aTimer == mTimer);
if (mSocket) {
mSocket->Close(NS_ERROR_ABORT);
mSocket = nullptr;
mStreamIn = nullptr;
}
mTimer = nullptr;
mStatus.AssignLiteral(u"NS_ERROR_NET_TIMEOUT");
mThread->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>
(mDashboard, &Dashboard::GetConnectionStatus, this),
NS_DISPATCH_NORMAL);
return NS_OK;
}
void
ConnectionData::StartTimer(uint32_t aTimeout)
{
if (!mTimer) {
mTimer = do_CreateInstance("@mozilla.org/timer;1");
}
mTimer->InitWithCallback(this, aTimeout * 1000,
nsITimer::TYPE_ONE_SHOT);
}
void
ConnectionData::StopTimer()
{
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
}
class LookupHelper;
class LookupArgument
: public nsISupports
{
virtual ~LookupArgument()
{
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
LookupArgument(nsIDNSRecord *aRecord, LookupHelper *aHelper)
{
mRecord = aRecord;
mHelper = aHelper;
}
nsCOMPtr<nsIDNSRecord> mRecord;
RefPtr<LookupHelper> mHelper;
};
NS_IMPL_ISUPPORTS0(LookupArgument)
class LookupHelper
: public nsIDNSListener
{
virtual ~LookupHelper()
{
if (mCancel) {
mCancel->Cancel(NS_ERROR_ABORT);
}
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDNSLISTENER
LookupHelper() {
}
nsresult ConstructAnswer(LookupArgument *aArgument);
public:
nsCOMPtr<nsICancelable> mCancel;
nsMainThreadPtrHandle<NetDashboardCallback> mCallback;
nsIThread *mThread;
nsresult mStatus;
};
NS_IMPL_ISUPPORTS(LookupHelper, nsIDNSListener)
NS_IMETHODIMP
LookupHelper::OnLookupComplete(nsICancelable *aRequest,
nsIDNSRecord *aRecord, nsresult aStatus)
{
MOZ_ASSERT(aRequest == mCancel);
mCancel = nullptr;
mStatus = aStatus;
RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
mThread->Dispatch(NewRunnableMethod<RefPtr<LookupArgument>>
(this, &LookupHelper::ConstructAnswer, arg),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
LookupHelper::ConstructAnswer(LookupArgument *aArgument)
{
nsIDNSRecord *aRecord = aArgument->mRecord;
AutoSafeJSContext cx;
mozilla::dom::DNSLookupDict dict;
dict.mAddress.Construct();
Sequence<nsString> &addresses = dict.mAddress.Value();
if (NS_SUCCEEDED(mStatus)) {
dict.mAnswer = true;
bool hasMore;
aRecord->HasMore(&hasMore);
while (hasMore) {
nsString* nextAddress = addresses.AppendElement(fallible);
if (!nextAddress) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCString nextAddressASCII;
aRecord->GetNextAddrAsString(nextAddressASCII);
CopyASCIItoUTF16(nextAddressASCII, *nextAddress);
aRecord->HasMore(&hasMore);
}
} else {
dict.mAnswer = false;
GetErrorString(mStatus, dict.mError);
}
JS::RootedValue val(cx);
if (!ToJSValue(cx, dict, &val)) {
return NS_ERROR_FAILURE;
}
this->mCallback->OnDashboardDataAvailable(val);
return NS_OK;
}
NS_IMPL_ISUPPORTS(Dashboard, nsIDashboard, nsIDashboardEventNotifier)
Dashboard::Dashboard()
{
mEnableLogging = false;
}
Dashboard::~Dashboard()
{
}
NS_IMETHODIMP
Dashboard::RequestSockets(NetDashboardCallback *aCallback)
{
RefPtr<SocketData> socketData = new SocketData();
socketData->mCallback =
new nsMainThreadPtrHolder<NetDashboardCallback>(aCallback, true);
socketData->mThread = NS_GetCurrentThread();
gSocketTransportService->Dispatch(NewRunnableMethod<RefPtr<SocketData>>
(this, &Dashboard::GetSocketsDispatch, socketData),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetSocketsDispatch(SocketData *aSocketData)
{
RefPtr<SocketData> socketData = aSocketData;
if (gSocketTransportService) {
gSocketTransportService->GetSocketConnections(&socketData->mData);
socketData->mTotalSent = gSocketTransportService->GetSentBytes();
socketData->mTotalRecv = gSocketTransportService->GetReceivedBytes();
}
socketData->mThread->Dispatch(NewRunnableMethod<RefPtr<SocketData>>
(this, &Dashboard::GetSockets, socketData),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetSockets(SocketData *aSocketData)
{
RefPtr<SocketData> socketData = aSocketData;
AutoSafeJSContext cx;
mozilla::dom::SocketsDict dict;
dict.mSockets.Construct();
dict.mSent = 0;
dict.mReceived = 0;
Sequence<mozilla::dom::SocketElement> &sockets = dict.mSockets.Value();
uint32_t length = socketData->mData.Length();
if (!sockets.SetCapacity(length, fallible)) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t i = 0; i < socketData->mData.Length(); i++) {
dom::SocketElement &mSocket = *sockets.AppendElement(fallible);
CopyASCIItoUTF16(socketData->mData[i].host, mSocket.mHost);
mSocket.mPort = socketData->mData[i].port;
mSocket.mActive = socketData->mData[i].active;
mSocket.mTcp = socketData->mData[i].tcp;
mSocket.mSent = (double) socketData->mData[i].sent;
mSocket.mReceived = (double) socketData->mData[i].received;
dict.mSent += socketData->mData[i].sent;
dict.mReceived += socketData->mData[i].received;
}
dict.mSent += socketData->mTotalSent;
dict.mReceived += socketData->mTotalRecv;
JS::RootedValue val(cx);
if (!ToJSValue(cx, dict, &val))
return NS_ERROR_FAILURE;
socketData->mCallback->OnDashboardDataAvailable(val);
return NS_OK;
}
NS_IMETHODIMP
Dashboard::RequestHttpConnections(NetDashboardCallback *aCallback)
{
RefPtr<HttpData> httpData = new HttpData();
httpData->mCallback =
new nsMainThreadPtrHolder<NetDashboardCallback>(aCallback, true);
httpData->mThread = NS_GetCurrentThread();
gSocketTransportService->Dispatch(NewRunnableMethod<RefPtr<HttpData>>
(this, &Dashboard::GetHttpDispatch, httpData),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetHttpDispatch(HttpData *aHttpData)
{
RefPtr<HttpData> httpData = aHttpData;
HttpInfo::GetHttpConnectionData(&httpData->mData);
httpData->mThread->Dispatch(NewRunnableMethod<RefPtr<HttpData>>
(this, &Dashboard::GetHttpConnections, httpData),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetHttpConnections(HttpData *aHttpData)
{
RefPtr<HttpData> httpData = aHttpData;
AutoSafeJSContext cx;
mozilla::dom::HttpConnDict dict;
dict.mConnections.Construct();
using mozilla::dom::HalfOpenInfoDict;
using mozilla::dom::HttpConnectionElement;
using mozilla::dom::HttpConnInfo;
Sequence<HttpConnectionElement> &connections = dict.mConnections.Value();
uint32_t length = httpData->mData.Length();
if (!connections.SetCapacity(length, fallible)) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t i = 0; i < httpData->mData.Length(); i++) {
HttpConnectionElement &connection = *connections.AppendElement(fallible);
CopyASCIItoUTF16(httpData->mData[i].host, connection.mHost);
connection.mPort = httpData->mData[i].port;
connection.mSpdy = httpData->mData[i].spdy;
connection.mSsl = httpData->mData[i].ssl;
connection.mActive.Construct();
connection.mIdle.Construct();
connection.mHalfOpens.Construct();
Sequence<HttpConnInfo> &active = connection.mActive.Value();
Sequence<HttpConnInfo> &idle = connection.mIdle.Value();
Sequence<HalfOpenInfoDict> &halfOpens = connection.mHalfOpens.Value();
if (!active.SetCapacity(httpData->mData[i].active.Length(), fallible) ||
!idle.SetCapacity(httpData->mData[i].idle.Length(), fallible) ||
!halfOpens.SetCapacity(httpData->mData[i].halfOpens.Length(),
fallible)) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t j = 0; j < httpData->mData[i].active.Length(); j++) {
HttpConnInfo &info = *active.AppendElement(fallible);
info.mRtt = httpData->mData[i].active[j].rtt;
info.mTtl = httpData->mData[i].active[j].ttl;
info.mProtocolVersion =
httpData->mData[i].active[j].protocolVersion;
}
for (uint32_t j = 0; j < httpData->mData[i].idle.Length(); j++) {
HttpConnInfo &info = *idle.AppendElement(fallible);
info.mRtt = httpData->mData[i].idle[j].rtt;
info.mTtl = httpData->mData[i].idle[j].ttl;
info.mProtocolVersion = httpData->mData[i].idle[j].protocolVersion;
}
for (uint32_t j = 0; j < httpData->mData[i].halfOpens.Length(); j++) {
HalfOpenInfoDict &info = *halfOpens.AppendElement(fallible);
info.mSpeculative = httpData->mData[i].halfOpens[j].speculative;
}
}
JS::RootedValue val(cx);
if (!ToJSValue(cx, dict, &val)) {
return NS_ERROR_FAILURE;
}
httpData->mCallback->OnDashboardDataAvailable(val);
return NS_OK;
}
NS_IMETHODIMP
Dashboard::GetEnableLogging(bool *value)
{
*value = mEnableLogging;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::SetEnableLogging(const bool value)
{
mEnableLogging = value;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::AddHost(const nsACString& aHost, uint32_t aSerial, bool aEncrypted)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
LogData mData(nsCString(aHost), aSerial, aEncrypted);
if (mWs.data.Contains(mData)) {
return NS_OK;
}
if (!mWs.data.AppendElement(mData)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::RemoveHost(const nsACString& aHost, uint32_t aSerial)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
if (index == -1)
return NS_ERROR_FAILURE;
mWs.data.RemoveElementAt(index);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::NewMsgSent(const nsACString& aHost, uint32_t aSerial, uint32_t aLength)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
if (index == -1)
return NS_ERROR_FAILURE;
mWs.data[index].mMsgSent++;
mWs.data[index].mSizeSent += aLength;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::NewMsgReceived(const nsACString& aHost, uint32_t aSerial, uint32_t aLength)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
if (index == -1)
return NS_ERROR_FAILURE;
mWs.data[index].mMsgReceived++;
mWs.data[index].mSizeReceived += aLength;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::RequestWebsocketConnections(NetDashboardCallback *aCallback)
{
RefPtr<WebSocketRequest> wsRequest = new WebSocketRequest();
wsRequest->mCallback =
new nsMainThreadPtrHolder<NetDashboardCallback>(aCallback, true);
wsRequest->mThread = NS_GetCurrentThread();
wsRequest->mThread->Dispatch(NewRunnableMethod<RefPtr<WebSocketRequest>>
(this, &Dashboard::GetWebSocketConnections, wsRequest),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetWebSocketConnections(WebSocketRequest *aWsRequest)
{
RefPtr<WebSocketRequest> wsRequest = aWsRequest;
AutoSafeJSContext cx;
mozilla::dom::WebSocketDict dict;
dict.mWebsockets.Construct();
Sequence<mozilla::dom::WebSocketElement> &websockets =
dict.mWebsockets.Value();
mozilla::MutexAutoLock lock(mWs.lock);
uint32_t length = mWs.data.Length();
if (!websockets.SetCapacity(length, fallible)) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t i = 0; i < mWs.data.Length(); i++) {
dom::WebSocketElement &websocket = *websockets.AppendElement(fallible);
CopyASCIItoUTF16(mWs.data[i].mHost, websocket.mHostport);
websocket.mMsgsent = mWs.data[i].mMsgSent;
websocket.mMsgreceived = mWs.data[i].mMsgReceived;
websocket.mSentsize = mWs.data[i].mSizeSent;
websocket.mReceivedsize = mWs.data[i].mSizeReceived;
websocket.mEncrypted = mWs.data[i].mEncrypted;
}
JS::RootedValue val(cx);
if (!ToJSValue(cx, dict, &val)) {
return NS_ERROR_FAILURE;
}
wsRequest->mCallback->OnDashboardDataAvailable(val);
return NS_OK;
}
NS_IMETHODIMP
Dashboard::RequestDNSInfo(NetDashboardCallback *aCallback)
{
RefPtr<DnsData> dnsData = new DnsData();
dnsData->mCallback =
new nsMainThreadPtrHolder<NetDashboardCallback>(aCallback, true);
nsresult rv;
dnsData->mData.Clear();
dnsData->mThread = NS_GetCurrentThread();
if (!mDnsService) {
mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
if (NS_FAILED(rv)) {
return rv;
}
}
gSocketTransportService->Dispatch(NewRunnableMethod<RefPtr<DnsData>>
(this, &Dashboard::GetDnsInfoDispatch, dnsData),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetDnsInfoDispatch(DnsData *aDnsData)
{
RefPtr<DnsData> dnsData = aDnsData;
if (mDnsService) {
mDnsService->GetDNSCacheEntries(&dnsData->mData);
}
dnsData->mThread->Dispatch(NewRunnableMethod<RefPtr<DnsData>>
(this, &Dashboard::GetDNSCacheEntries, dnsData),
NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetDNSCacheEntries(DnsData *dnsData)
{
AutoSafeJSContext cx;
mozilla::dom::DNSCacheDict dict;
dict.mEntries.Construct();
Sequence<mozilla::dom::DnsCacheEntry> &entries = dict.mEntries.Value();
uint32_t length = dnsData->mData.Length();
if (!entries.SetCapacity(length, fallible)) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t i = 0; i < dnsData->mData.Length(); i++) {
dom::DnsCacheEntry &entry = *entries.AppendElement(fallible);
entry.mHostaddr.Construct();
Sequence<nsString> &addrs = entry.mHostaddr.Value();
if (!addrs.SetCapacity(dnsData->mData[i].hostaddr.Length(), fallible)) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
CopyASCIItoUTF16(dnsData->mData[i].hostname, entry.mHostname);
entry.mExpiration = dnsData->mData[i].expiration;
for (uint32_t j = 0; j < dnsData->mData[i].hostaddr.Length(); j++) {
nsString* addr = addrs.AppendElement(fallible);
if (!addr) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
CopyASCIItoUTF16(dnsData->mData[i].hostaddr[j], *addr);
}
if (dnsData->mData[i].family == PR_AF_INET6) {
CopyASCIItoUTF16("ipv6", entry.mFamily);
} else {
CopyASCIItoUTF16("ipv4", entry.mFamily);
}
}
JS::RootedValue val(cx);
if (!ToJSValue(cx, dict, &val)) {
return NS_ERROR_FAILURE;
}
dnsData->mCallback->OnDashboardDataAvailable(val);
return NS_OK;
}
NS_IMETHODIMP
Dashboard::RequestDNSLookup(const nsACString &aHost,
NetDashboardCallback *aCallback)
{
nsresult rv;
if (!mDnsService) {
mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
if (NS_FAILED(rv)) {
return rv;
}
}
RefPtr<LookupHelper> helper = new LookupHelper();
helper->mCallback =
new nsMainThreadPtrHolder<NetDashboardCallback>(aCallback, true);
helper->mThread = NS_GetCurrentThread();
rv = mDnsService->AsyncResolve(aHost, 0, helper.get(),
NS_GetCurrentThread(),
getter_AddRefs(helper->mCancel));
return rv;
}
void
HttpConnInfo::SetHTTP1ProtocolVersion(uint8_t pv)
{
switch (pv) {
case NS_HTTP_VERSION_0_9:
protocolVersion.AssignLiteral(u"http/0.9");
break;
case NS_HTTP_VERSION_1_0:
protocolVersion.AssignLiteral(u"http/1.0");
break;
case NS_HTTP_VERSION_1_1:
protocolVersion.AssignLiteral(u"http/1.1");
break;
case NS_HTTP_VERSION_2_0:
protocolVersion.AssignLiteral(u"http/2.0");
break;
default:
protocolVersion.AssignLiteral(u"unknown protocol version");
}
}
void
HttpConnInfo::SetHTTP2ProtocolVersion(uint8_t pv)
{
MOZ_ASSERT (pv == HTTP_VERSION_2);
protocolVersion.Assign(u"h2");
}
NS_IMETHODIMP
Dashboard::GetLogPath(nsACString &aLogPath)
{
aLogPath.SetCapacity(2048);
uint32_t len = LogModule::GetLogFile(aLogPath.BeginWriting(), 2048);
aLogPath.SetLength(len);
return NS_OK;
}
NS_IMETHODIMP
Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort,
const char *aProtocol, uint32_t aTimeout,
NetDashboardCallback *aCallback)
{
nsresult rv;
RefPtr<ConnectionData> connectionData = new ConnectionData(this);
connectionData->mHost = aHost;
connectionData->mPort = aPort;
connectionData->mProtocol = aProtocol;
connectionData->mTimeout = aTimeout;
connectionData->mCallback =
new nsMainThreadPtrHolder<NetDashboardCallback>(aCallback, true);
connectionData->mThread = NS_GetCurrentThread();
rv = TestNewConnection(connectionData);
if (NS_FAILED(rv)) {
mozilla::net::GetErrorString(rv, connectionData->mStatus);
connectionData->mThread->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>
(this, &Dashboard::GetConnectionStatus, connectionData),
NS_DISPATCH_NORMAL);
return rv;
}
return NS_OK;
}
nsresult
Dashboard::GetConnectionStatus(ConnectionData *aConnectionData)
{
RefPtr<ConnectionData> connectionData = aConnectionData;
AutoSafeJSContext cx;
mozilla::dom::ConnStatusDict dict;
dict.mStatus = connectionData->mStatus;
JS::RootedValue val(cx);
if (!ToJSValue(cx, dict, &val))
return NS_ERROR_FAILURE;
connectionData->mCallback->OnDashboardDataAvailable(val);
return NS_OK;
}
nsresult
Dashboard::TestNewConnection(ConnectionData *aConnectionData)
{
RefPtr<ConnectionData> connectionData = aConnectionData;
nsresult rv;
if (!connectionData->mHost.Length() ||
!net_IsValidHostName(connectionData->mHost)) {
return NS_ERROR_UNKNOWN_HOST;
}
if (connectionData->mProtocol &&
NS_LITERAL_STRING("ssl").EqualsASCII(connectionData->mProtocol)) {
rv = gSocketTransportService->CreateTransport(
&connectionData->mProtocol, 1, connectionData->mHost,
connectionData->mPort, nullptr,
getter_AddRefs(connectionData->mSocket));
} else {
rv = gSocketTransportService->CreateTransport(
nullptr, 0, connectionData->mHost,
connectionData->mPort, nullptr,
getter_AddRefs(connectionData->mSocket));
}
if (NS_FAILED(rv)) {
return rv;
}
rv = connectionData->mSocket->SetEventSink(connectionData,
NS_GetCurrentThread());
if (NS_FAILED(rv)) {
return rv;
}
rv = connectionData->mSocket->OpenInputStream(
nsITransport::OPEN_BLOCKING, 0, 0,
getter_AddRefs(connectionData->mStreamIn));
if (NS_FAILED(rv)) {
return rv;
}
connectionData->StartTimer(connectionData->mTimeout);
return rv;
}
typedef struct
{
nsresult key;
const char *error;
} ErrorEntry;
#undef ERROR
#define ERROR(key, val) {key, #key}
ErrorEntry socketTransportStatuses[] = {
ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
};
#undef ERROR
static void
GetErrorString(nsresult rv, nsAString& errorString)
{
for (size_t i = 0; i < ArrayLength(socketTransportStatuses); ++i) {
if (socketTransportStatuses[i].key == rv) {
errorString.AssignASCII(socketTransportStatuses[i].error);
return;
}
}
nsAutoCString errorCString;
mozilla::GetErrorName(rv, errorCString);
CopyUTF8toUTF16(errorCString, errorString);
}
} // namespace net
} // namespace mozilla

105
netwerk/base/Dashboard.h Normal file
View file

@ -0,0 +1,105 @@
/* 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 nsDashboard_h__
#define nsDashboard_h__
#include "mozilla/Mutex.h"
#include "mozilla/net/DashboardTypes.h"
#include "nsIDashboard.h"
#include "nsIDashboardEventNotifier.h"
#include "nsIDNSListener.h"
#include "nsIServiceManager.h"
#include "nsITimer.h"
#include "nsITransport.h"
class nsIDNSService;
namespace mozilla {
namespace net {
class SocketData;
class HttpData;
class DnsData;
class WebSocketRequest;
class ConnectionData;
class Dashboard final
: public nsIDashboard
, public nsIDashboardEventNotifier
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDASHBOARD
NS_DECL_NSIDASHBOARDEVENTNOTIFIER
Dashboard();
static const char *GetErrorString(nsresult rv);
nsresult GetConnectionStatus(ConnectionData *aConnectionData);
private:
struct LogData
{
LogData(nsCString host, uint32_t serial, bool encryption):
mHost(host),
mSerial(serial),
mMsgSent(0),
mMsgReceived(0),
mSizeSent(0),
mSizeReceived(0),
mEncrypted(encryption)
{ }
nsCString mHost;
uint32_t mSerial;
uint32_t mMsgSent;
uint32_t mMsgReceived;
uint64_t mSizeSent;
uint64_t mSizeReceived;
bool mEncrypted;
bool operator==(const LogData& a) const
{
return mHost.Equals(a.mHost) && (mSerial == a.mSerial);
}
};
struct WebSocketData
{
WebSocketData():lock("Dashboard.webSocketData")
{
}
uint32_t IndexOf(nsCString hostname, uint32_t mSerial)
{
LogData temp(hostname, mSerial, false);
return data.IndexOf(temp);
}
nsTArray<LogData> data;
mozilla::Mutex lock;
};
bool mEnableLogging;
WebSocketData mWs;
private:
virtual ~Dashboard();
nsresult GetSocketsDispatch(SocketData *);
nsresult GetHttpDispatch(HttpData *);
nsresult GetDnsInfoDispatch(DnsData *);
nsresult TestNewConnection(ConnectionData *);
/* Helper methods that pass the JSON to the callback function. */
nsresult GetSockets(SocketData *);
nsresult GetHttpConnections(HttpData *);
nsresult GetDNSCacheEntries(DnsData *);
nsresult GetWebSocketConnections(WebSocketRequest *);
nsCOMPtr<nsIDNSService> mDnsService;
};
} // namespace net
} // namespace mozilla
#endif // nsDashboard_h__

View file

@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_DashboardTypes_h_
#define mozilla_net_DashboardTypes_h_
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace net {
struct SocketInfo
{
nsCString host;
uint64_t sent;
uint64_t received;
uint16_t port;
bool active;
bool tcp;
};
struct HalfOpenSockets
{
bool speculative;
};
struct DNSCacheEntries
{
nsCString hostname;
nsTArray<nsCString> hostaddr;
uint16_t family;
int64_t expiration;
nsCString netInterface;
};
struct HttpConnInfo
{
uint32_t ttl;
uint32_t rtt;
nsString protocolVersion;
void SetHTTP1ProtocolVersion(uint8_t pv);
void SetHTTP2ProtocolVersion(uint8_t pv);
};
struct HttpRetParams
{
nsCString host;
nsTArray<HttpConnInfo> active;
nsTArray<HttpConnInfo> idle;
nsTArray<HalfOpenSockets> halfOpens;
uint32_t counter;
uint16_t port;
bool spdy;
bool ssl;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_DashboardTypes_h_

View file

@ -0,0 +1,462 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "EventTokenBucket.h"
#include "nsICancelable.h"
#include "nsIIOService.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsSocketTransportService2.h"
#ifdef DEBUG
#include "MainThreadUtils.h"
#endif
#ifdef XP_WIN
#include <windows.h>
#include <mmsystem.h>
#endif
namespace mozilla {
namespace net {
////////////////////////////////////////////
// EventTokenBucketCancelable
////////////////////////////////////////////
class TokenBucketCancelable : public nsICancelable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICANCELABLE
explicit TokenBucketCancelable(class ATokenBucketEvent *event);
void Fire();
private:
virtual ~TokenBucketCancelable() {}
friend class EventTokenBucket;
ATokenBucketEvent *mEvent;
};
NS_IMPL_ISUPPORTS(TokenBucketCancelable, nsICancelable)
TokenBucketCancelable::TokenBucketCancelable(ATokenBucketEvent *event)
: mEvent(event)
{
}
NS_IMETHODIMP
TokenBucketCancelable::Cancel(nsresult reason)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
mEvent = nullptr;
return NS_OK;
}
void
TokenBucketCancelable::Fire()
{
if (!mEvent)
return;
ATokenBucketEvent *event = mEvent;
mEvent = nullptr;
event->OnTokenBucketAdmitted();
}
////////////////////////////////////////////
// EventTokenBucket
////////////////////////////////////////////
NS_IMPL_ISUPPORTS(EventTokenBucket, nsITimerCallback)
// by default 1hz with no burst
EventTokenBucket::EventTokenBucket(uint32_t eventsPerSecond,
uint32_t burstSize)
: mUnitCost(kUsecPerSec)
, mMaxCredit(kUsecPerSec)
, mCredit(kUsecPerSec)
, mPaused(false)
, mStopped(false)
, mTimerArmed(false)
#ifdef XP_WIN
, mFineGrainTimerInUse(false)
, mFineGrainResetTimerArmed(false)
#endif
{
mLastUpdate = TimeStamp::Now();
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
nsCOMPtr<nsIEventTarget> sts;
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
if (NS_SUCCEEDED(rv))
sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mTimer)
mTimer->SetTarget(sts);
SetRate(eventsPerSecond, burstSize);
}
EventTokenBucket::~EventTokenBucket()
{
SOCKET_LOG(("EventTokenBucket::dtor %p events=%d\n",
this, mEvents.GetSize()));
CleanupTimers();
// Complete any queued events to prevent hangs
while (mEvents.GetSize()) {
RefPtr<TokenBucketCancelable> cancelable =
dont_AddRef(static_cast<TokenBucketCancelable *>(mEvents.PopFront()));
cancelable->Fire();
}
}
void
EventTokenBucket::CleanupTimers()
{
if (mTimer && mTimerArmed) {
mTimer->Cancel();
}
mTimer = nullptr;
mTimerArmed = false;
#ifdef XP_WIN
NormalTimers();
if (mFineGrainResetTimer && mFineGrainResetTimerArmed) {
mFineGrainResetTimer->Cancel();
}
mFineGrainResetTimer = nullptr;
mFineGrainResetTimerArmed = false;
#endif
}
void
EventTokenBucket::SetRate(uint32_t eventsPerSecond,
uint32_t burstSize)
{
SOCKET_LOG(("EventTokenBucket::SetRate %p %u %u\n",
this, eventsPerSecond, burstSize));
if (eventsPerSecond > kMaxHz) {
eventsPerSecond = kMaxHz;
SOCKET_LOG((" eventsPerSecond out of range\n"));
}
if (!eventsPerSecond) {
eventsPerSecond = 1;
SOCKET_LOG((" eventsPerSecond out of range\n"));
}
mUnitCost = kUsecPerSec / eventsPerSecond;
mMaxCredit = mUnitCost * burstSize;
if (mMaxCredit > kUsecPerSec * 60 * 15) {
SOCKET_LOG((" burstSize out of range\n"));
mMaxCredit = kUsecPerSec * 60 * 15;
}
mCredit = mMaxCredit;
mLastUpdate = TimeStamp::Now();
}
void
EventTokenBucket::ClearCredits()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("EventTokenBucket::ClearCredits %p\n", this));
mCredit = 0;
}
uint32_t
EventTokenBucket::BurstEventsAvailable()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
return static_cast<uint32_t>(mCredit / mUnitCost);
}
uint32_t
EventTokenBucket::QueuedEvents()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
return mEvents.GetSize();
}
void
EventTokenBucket::Pause()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("EventTokenBucket::Pause %p\n", this));
if (mPaused || mStopped)
return;
mPaused = true;
if (mTimerArmed) {
mTimer->Cancel();
mTimerArmed = false;
}
}
void
EventTokenBucket::UnPause()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("EventTokenBucket::UnPause %p\n", this));
if (!mPaused || mStopped)
return;
mPaused = false;
DispatchEvents();
UpdateTimer();
}
void
EventTokenBucket::Stop()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("EventTokenBucket::Stop %p armed=%d\n", this, mTimerArmed));
mStopped = true;
CleanupTimers();
// Complete any queued events to prevent hangs
while (mEvents.GetSize()) {
RefPtr<TokenBucketCancelable> cancelable =
dont_AddRef(static_cast<TokenBucketCancelable *>(mEvents.PopFront()));
cancelable->Fire();
}
}
nsresult
EventTokenBucket::SubmitEvent(ATokenBucketEvent *event, nsICancelable **cancelable)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("EventTokenBucket::SubmitEvent %p\n", this));
if (mStopped || !mTimer)
return NS_ERROR_FAILURE;
UpdateCredits();
RefPtr<TokenBucketCancelable> cancelEvent = new TokenBucketCancelable(event);
// When this function exits the cancelEvent needs 2 references, one for the
// mEvents queue and one for the caller of SubmitEvent()
NS_ADDREF(*cancelable = cancelEvent.get());
if (mPaused || !TryImmediateDispatch(cancelEvent.get())) {
// queue it
SOCKET_LOG((" queued\n"));
mEvents.Push(cancelEvent.forget().take());
UpdateTimer();
}
else {
SOCKET_LOG((" dispatched synchronously\n"));
}
return NS_OK;
}
bool
EventTokenBucket::TryImmediateDispatch(TokenBucketCancelable *cancelable)
{
if (mCredit < mUnitCost)
return false;
mCredit -= mUnitCost;
cancelable->Fire();
return true;
}
void
EventTokenBucket::DispatchEvents()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("EventTokenBucket::DispatchEvents %p %d\n", this, mPaused));
if (mPaused || mStopped)
return;
while (mEvents.GetSize() && mUnitCost <= mCredit) {
RefPtr<TokenBucketCancelable> cancelable =
dont_AddRef(static_cast<TokenBucketCancelable *>(mEvents.PopFront()));
if (cancelable->mEvent) {
SOCKET_LOG(("EventTokenBucket::DispachEvents [%p] "
"Dispatching queue token bucket event cost=%lu credit=%lu\n",
this, mUnitCost, mCredit));
mCredit -= mUnitCost;
cancelable->Fire();
}
}
#ifdef XP_WIN
if (!mEvents.GetSize())
WantNormalTimers();
#endif
}
void
EventTokenBucket::UpdateTimer()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mTimerArmed || mPaused || mStopped || !mEvents.GetSize() || !mTimer)
return;
if (mCredit >= mUnitCost)
return;
// determine the time needed to wait to accumulate enough credits to admit
// one more event and set the timer for that point. Always round it
// up because firing early doesn't help.
//
uint64_t deficit = mUnitCost - mCredit;
uint64_t msecWait = (deficit + (kUsecPerMsec - 1)) / kUsecPerMsec;
if (msecWait < 4) // minimum wait
msecWait = 4;
else if (msecWait > 60000) // maximum wait
msecWait = 60000;
#ifdef XP_WIN
FineGrainTimers();
#endif
SOCKET_LOG(("EventTokenBucket::UpdateTimer %p for %dms\n",
this, msecWait));
nsresult rv = mTimer->InitWithCallback(this, static_cast<uint32_t>(msecWait),
nsITimer::TYPE_ONE_SHOT);
mTimerArmed = NS_SUCCEEDED(rv);
}
NS_IMETHODIMP
EventTokenBucket::Notify(nsITimer *timer)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
#ifdef XP_WIN
if (timer == mFineGrainResetTimer) {
FineGrainResetTimerNotify();
return NS_OK;
}
#endif
SOCKET_LOG(("EventTokenBucket::Notify() %p\n", this));
mTimerArmed = false;
if (mStopped)
return NS_OK;
UpdateCredits();
DispatchEvents();
UpdateTimer();
return NS_OK;
}
void
EventTokenBucket::UpdateCredits()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
TimeStamp now = TimeStamp::Now();
TimeDuration elapsed = now - mLastUpdate;
mLastUpdate = now;
mCredit += static_cast<uint64_t>(elapsed.ToMicroseconds());
if (mCredit > mMaxCredit)
mCredit = mMaxCredit;
SOCKET_LOG(("EventTokenBucket::UpdateCredits %p to %lu (%lu each.. %3.2f)\n",
this, mCredit, mUnitCost, (double)mCredit / mUnitCost));
}
#ifdef XP_WIN
void
EventTokenBucket::FineGrainTimers()
{
SOCKET_LOG(("EventTokenBucket::FineGrainTimers %p mFineGrainTimerInUse=%d\n",
this, mFineGrainTimerInUse));
mLastFineGrainTimerUse = TimeStamp::Now();
if (mFineGrainTimerInUse)
return;
if (mUnitCost > kCostFineGrainThreshold)
return;
SOCKET_LOG(("EventTokenBucket::FineGrainTimers %p timeBeginPeriod()\n",
this));
mFineGrainTimerInUse = true;
timeBeginPeriod(1);
}
void
EventTokenBucket::NormalTimers()
{
if (!mFineGrainTimerInUse)
return;
mFineGrainTimerInUse = false;
SOCKET_LOG(("EventTokenBucket::NormalTimers %p timeEndPeriod()\n", this));
timeEndPeriod(1);
}
void
EventTokenBucket::WantNormalTimers()
{
if (!mFineGrainTimerInUse)
return;
if (mFineGrainResetTimerArmed)
return;
TimeDuration elapsed(TimeStamp::Now() - mLastFineGrainTimerUse);
static const TimeDuration fiveSeconds = TimeDuration::FromSeconds(5);
if (elapsed >= fiveSeconds) {
NormalTimers();
return;
}
if (!mFineGrainResetTimer)
mFineGrainResetTimer = do_CreateInstance("@mozilla.org/timer;1");
// if we can't delay the reset, just do it now
if (!mFineGrainResetTimer) {
NormalTimers();
return;
}
// pad the callback out 100ms to avoid having to round trip this again if the
// timer calls back just a tad early.
SOCKET_LOG(("EventTokenBucket::WantNormalTimers %p "
"Will reset timer granularity after delay", this));
mFineGrainResetTimer->InitWithCallback(
this,
static_cast<uint32_t>((fiveSeconds - elapsed).ToMilliseconds()) + 100,
nsITimer::TYPE_ONE_SHOT);
mFineGrainResetTimerArmed = true;
}
void
EventTokenBucket::FineGrainResetTimerNotify()
{
SOCKET_LOG(("EventTokenBucket::FineGrainResetTimerNotify() events = %d\n",
this, mEvents.GetSize()));
mFineGrainResetTimerArmed = false;
// If we are currently processing events then wait for the queue to drain
// before trying to reset back to normal timers again
if (!mEvents.GetSize())
WantNormalTimers();
}
#endif
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,149 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 NetEventTokenBucket_h__
#define NetEventTokenBucket_h__
#include "ARefBase.h"
#include "nsCOMPtr.h"
#include "nsDeque.h"
#include "nsITimer.h"
#include "mozilla/TimeStamp.h"
class nsICancelable;
namespace mozilla {
namespace net {
/* A token bucket is used to govern the maximum rate a series of events
can be executed at. For instance if your event was "eat a piece of cake"
then a token bucket configured to allow "1 piece per day" would spread
the eating of a 8 piece cake over 8 days even if you tried to eat the
whole thing up front. In a practical sense it 'costs' 1 token to execute
an event and tokens are 'earned' at a particular rate as time goes by.
The token bucket can be perfectly smooth or allow a configurable amount of
burstiness. A bursty token bucket allows you to save up unused credits, while
a perfectly smooth one would not. A smooth "1 per day" cake token bucket
would require 9 days to eat that cake if you skipped a slice on day 4
(use the token or lose it), while a token bucket configured with a burst
of 2 would just let you eat 2 slices on day 5 (the credits for day 4 and day
5) and finish the cake in the usual 8 days.
EventTokenBucket(hz=20, burst=5) creates a token bucket with the following properties:
+ events from an infinite stream will be admitted 20 times per second (i.e.
hz=20 means 1 event per 50 ms). Timers will be used to space things evenly down to
5ms gaps (i.e. up to 200hz). Token buckets with rates greater than 200hz will admit
multiple events with 5ms gaps between them. 10000hz is the maximum rate and 1hz is
the minimum rate.
+ The burst size controls the limit of 'credits' that a token bucket can accumulate
when idle. For our (20,5) example each event requires 50ms of credit (again, 20hz = 50ms
per event). a burst size of 5 means that the token bucket can accumulate a
maximum of 250ms (5 * 50ms) for this bucket. If no events have been admitted for the
last full second the bucket can still only accumulate 250ms of credit - but that credit
means that 5 events can be admitted without delay. A burst size of 1 is the minimum.
The EventTokenBucket is created with maximum credits already applied, but they
can be cleared with the ClearCredits() method. The maximum burst size is
15 minutes worth of events.
+ An event is submitted to the token bucket asynchronously through SubmitEvent().
The OnTokenBucketAdmitted() method of the submitted event is used as a callback
when the event is ready to run. A cancelable event is returned to the SubmitEvent() caller
for use in the case they do not wish to wait for the callback.
*/
class EventTokenBucket;
class ATokenBucketEvent
{
public:
virtual void OnTokenBucketAdmitted() = 0;
};
class TokenBucketCancelable;
class EventTokenBucket : public nsITimerCallback, public ARefBase
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
// This should be constructed on the main thread
EventTokenBucket(uint32_t eventsPerSecond, uint32_t burstSize);
// These public methods are all meant to be called from the socket thread
void ClearCredits();
uint32_t BurstEventsAvailable();
uint32_t QueuedEvents();
// a paused token bucket will not process any events, but it will accumulate
// credits. ClearCredits can be used before unpausing if desired.
void Pause();
void UnPause();
void Stop();
// The returned cancelable event can only be canceled from the socket thread
nsresult SubmitEvent(ATokenBucketEvent *event, nsICancelable **cancelable);
private:
virtual ~EventTokenBucket();
void CleanupTimers();
friend class RunNotifyEvent;
friend class SetTimerEvent;
bool TryImmediateDispatch(TokenBucketCancelable *event);
void SetRate(uint32_t eventsPerSecond, uint32_t burstSize);
void DispatchEvents();
void UpdateTimer();
void UpdateCredits();
const static uint64_t kUsecPerSec = 1000000;
const static uint64_t kUsecPerMsec = 1000;
const static uint64_t kMaxHz = 10000;
uint64_t mUnitCost; // usec of credit needed for 1 event (from eventsPerSecond)
uint64_t mMaxCredit; // usec mCredit limit (from busrtSize)
uint64_t mCredit; // usec of accumulated credit.
bool mPaused;
bool mStopped;
nsDeque mEvents;
bool mTimerArmed;
TimeStamp mLastUpdate;
// The timer is created on the main thread, but is armed and executes Notify()
// callbacks on the socket thread in order to maintain low latency of event
// delivery.
nsCOMPtr<nsITimer> mTimer;
#ifdef XP_WIN
// Windows timers are 15ms granularity by default. When we have active events
// that need to be dispatched at 50ms or less granularity we change the OS
// granularity to 1ms. 90 seconds after that need has elapsed we will change it
// back
const static uint64_t kCostFineGrainThreshold = 50 * kUsecPerMsec;
void FineGrainTimers(); // get 1ms granularity
void NormalTimers(); // reset to default granularity
void WantNormalTimers(); // reset after 90 seconds if not needed in interim
void FineGrainResetTimerNotify(); // delayed callback to reset
TimeStamp mLastFineGrainTimerUse;
bool mFineGrainTimerInUse;
bool mFineGrainResetTimerArmed;
nsCOMPtr<nsITimer> mFineGrainResetTimer;
#endif
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,181 @@
/* 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 "LoadContextInfo.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsIChannel.h"
#include "nsILoadContext.h"
#include "nsIWebNavigation.h"
#include "nsNetUtil.h"
using namespace mozilla::dom;
namespace mozilla {
namespace net {
// LoadContextInfo
NS_IMPL_ISUPPORTS(LoadContextInfo, nsILoadContextInfo)
LoadContextInfo::LoadContextInfo(bool aIsAnonymous, NeckoOriginAttributes aOriginAttributes)
: mIsAnonymous(aIsAnonymous)
, mOriginAttributes(aOriginAttributes)
{
}
LoadContextInfo::~LoadContextInfo()
{
}
NS_IMETHODIMP LoadContextInfo::GetIsPrivate(bool *aIsPrivate)
{
*aIsPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
return NS_OK;
}
NS_IMETHODIMP LoadContextInfo::GetIsAnonymous(bool *aIsAnonymous)
{
*aIsAnonymous = mIsAnonymous;
return NS_OK;
}
NeckoOriginAttributes const* LoadContextInfo::OriginAttributesPtr()
{
return &mOriginAttributes;
}
NS_IMETHODIMP LoadContextInfo::GetOriginAttributes(JSContext *aCx,
JS::MutableHandle<JS::Value> aVal)
{
if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
// LoadContextInfoFactory
NS_IMPL_ISUPPORTS(LoadContextInfoFactory, nsILoadContextInfoFactory)
NS_IMETHODIMP LoadContextInfoFactory::GetDefault(nsILoadContextInfo * *aDefault)
{
nsCOMPtr<nsILoadContextInfo> info = GetLoadContextInfo(false, NeckoOriginAttributes());
info.forget(aDefault);
return NS_OK;
}
NS_IMETHODIMP LoadContextInfoFactory::GetPrivate(nsILoadContextInfo * *aPrivate)
{
NeckoOriginAttributes attrs;
attrs.SyncAttributesWithPrivateBrowsing(true);
nsCOMPtr<nsILoadContextInfo> info = GetLoadContextInfo(false, attrs);
info.forget(aPrivate);
return NS_OK;
}
NS_IMETHODIMP LoadContextInfoFactory::GetAnonymous(nsILoadContextInfo * *aAnonymous)
{
nsCOMPtr<nsILoadContextInfo> info = GetLoadContextInfo(true, NeckoOriginAttributes());
info.forget(aAnonymous);
return NS_OK;
}
NS_IMETHODIMP LoadContextInfoFactory::Custom(bool aAnonymous,
JS::HandleValue aOriginAttributes, JSContext *cx,
nsILoadContextInfo * *_retval)
{
NeckoOriginAttributes attrs;
bool status = attrs.Init(cx, aOriginAttributes);
NS_ENSURE_TRUE(status, NS_ERROR_FAILURE);
nsCOMPtr<nsILoadContextInfo> info = GetLoadContextInfo(aAnonymous, attrs);
info.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP LoadContextInfoFactory::FromLoadContext(nsILoadContext *aLoadContext, bool aAnonymous,
nsILoadContextInfo * *_retval)
{
nsCOMPtr<nsILoadContextInfo> info = GetLoadContextInfo(aLoadContext, aAnonymous);
info.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP LoadContextInfoFactory::FromWindow(nsIDOMWindow *aWindow, bool aAnonymous,
nsILoadContextInfo * *_retval)
{
nsCOMPtr<nsILoadContextInfo> info = GetLoadContextInfo(aWindow, aAnonymous);
info.forget(_retval);
return NS_OK;
}
// Helper functions
LoadContextInfo *
GetLoadContextInfo(nsIChannel * aChannel)
{
nsresult rv;
DebugOnly<bool> pb = NS_UsePrivateBrowsing(aChannel);
bool anon = false;
nsLoadFlags loadFlags;
rv = aChannel->GetLoadFlags(&loadFlags);
if (NS_SUCCEEDED(rv)) {
anon = !!(loadFlags & nsIChannel::LOAD_ANONYMOUS);
}
NeckoOriginAttributes oa;
NS_GetOriginAttributes(aChannel, oa);
MOZ_ASSERT(pb == (oa.mPrivateBrowsingId > 0));
return new LoadContextInfo(anon, oa);
}
LoadContextInfo *
GetLoadContextInfo(nsILoadContext *aLoadContext, bool aIsAnonymous)
{
if (!aLoadContext) {
return new LoadContextInfo(aIsAnonymous,
NeckoOriginAttributes(nsILoadContextInfo::NO_APP_ID, false));
}
DebugOnly<bool> pb = aLoadContext->UsePrivateBrowsing();
DocShellOriginAttributes doa;
aLoadContext->GetOriginAttributes(doa);
MOZ_ASSERT(pb == (doa.mPrivateBrowsingId > 0));
NeckoOriginAttributes noa;
noa.InheritFromDocShellToNecko(doa);
return new LoadContextInfo(aIsAnonymous, noa);
}
LoadContextInfo*
GetLoadContextInfo(nsIDOMWindow *aWindow,
bool aIsAnonymous)
{
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
return GetLoadContextInfo(loadContext, aIsAnonymous);
}
LoadContextInfo *
GetLoadContextInfo(nsILoadContextInfo *aInfo)
{
return new LoadContextInfo(aInfo->IsAnonymous(),
*aInfo->OriginAttributesPtr());
}
LoadContextInfo *
GetLoadContextInfo(bool const aIsAnonymous,
NeckoOriginAttributes const &aOriginAttributes)
{
return new LoadContextInfo(aIsAnonymous,
aOriginAttributes);
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,61 @@
/* 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 nsLoadContextInfo_h__
#define nsLoadContextInfo_h__
#include "nsILoadContextInfo.h"
class nsIChannel;
class nsILoadContext;
namespace mozilla {
namespace net {
class LoadContextInfo : public nsILoadContextInfo
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSILOADCONTEXTINFO
LoadContextInfo(bool aIsAnonymous, NeckoOriginAttributes aOriginAttributes);
private:
virtual ~LoadContextInfo();
protected:
bool mIsAnonymous : 1;
NeckoOriginAttributes mOriginAttributes;
};
class LoadContextInfoFactory : public nsILoadContextInfoFactory
{
virtual ~LoadContextInfoFactory() {}
public:
NS_DECL_ISUPPORTS // deliberately not thread-safe
NS_DECL_NSILOADCONTEXTINFOFACTORY
};
LoadContextInfo*
GetLoadContextInfo(nsIChannel *aChannel);
LoadContextInfo*
GetLoadContextInfo(nsILoadContext *aLoadContext,
bool aIsAnonymous);
LoadContextInfo*
GetLoadContextInfo(nsIDOMWindow *aLoadContext,
bool aIsAnonymous);
LoadContextInfo*
GetLoadContextInfo(nsILoadContextInfo *aInfo);
LoadContextInfo*
GetLoadContextInfo(bool const aIsAnonymous,
NeckoOriginAttributes const &aOriginAttributes);
} // namespace net
} // namespace mozilla
#endif

925
netwerk/base/LoadInfo.cpp Normal file
View file

@ -0,0 +1,925 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/LoadInfo.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozIThirdPartyUtil.h"
#include "nsFrameLoader.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIFrameLoader.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsISupportsImpl.h"
#include "nsISupportsUtils.h"
#include "nsContentUtils.h"
#include "nsDocShell.h"
#include "nsGlobalWindow.h"
#include "nsNullPrincipal.h"
using namespace mozilla::dom;
namespace mozilla {
namespace net {
static void
InheritOriginAttributes(nsIPrincipal* aLoadingPrincipal, NeckoOriginAttributes& aAttrs)
{
const PrincipalOriginAttributes attrs =
BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef();
aAttrs.InheritFromDocToNecko(attrs);
}
LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
nsINode* aLoadingContext,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType)
: mLoadingPrincipal(aLoadingContext ?
aLoadingContext->NodePrincipal() : aLoadingPrincipal)
, mTriggeringPrincipal(aTriggeringPrincipal ?
aTriggeringPrincipal : mLoadingPrincipal.get())
, mPrincipalToInherit(nullptr)
, mLoadingContext(do_GetWeakReference(aLoadingContext))
, mSecurityFlags(aSecurityFlags)
, mInternalContentPolicyType(aContentPolicyType)
, mTainting(LoadTainting::Basic)
, mUpgradeInsecureRequests(false)
, mVerifySignedContent(false)
, mEnforceSRI(false)
, mForceInheritPrincipalDropped(false)
, mInnerWindowID(0)
, mOuterWindowID(0)
, mParentOuterWindowID(0)
, mFrameOuterWindowID(0)
, mEnforceSecurity(false)
, mInitialSecurityCheckDone(false)
, mIsThirdPartyContext(false)
, mForcePreflight(false)
, mIsPreflight(false)
, mForceHSTSPriming(false)
, mMixedContentWouldBlock(false)
{
MOZ_ASSERT(mLoadingPrincipal);
MOZ_ASSERT(mTriggeringPrincipal);
#ifdef DEBUG
// TYPE_DOCUMENT loads initiated by javascript tests will go through
// nsIOService and use the wrong constructor. Don't enforce the
// !TYPE_DOCUMENT check in those cases
bool skipContentTypeCheck = false;
skipContentTypeCheck = Preferences::GetBool("network.loadinfo.skip_type_assertion");
#endif
// This constructor shouldn't be used for TYPE_DOCUMENT loads that don't
// have a loadingPrincipal
MOZ_ASSERT(skipContentTypeCheck ||
mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT);
// TODO(bug 1259873): Above, we initialize mIsThirdPartyContext to false meaning
// that consumers of LoadInfo that don't pass a context or pass a context from
// which we can't find a window will default to assuming that they're 1st
// party. It would be nice if we could default "safe" and assume that we are
// 3rd party until proven otherwise.
// if consumers pass both, aLoadingContext and aLoadingPrincipal
// then the loadingPrincipal must be the same as the node's principal
MOZ_ASSERT(!aLoadingContext || !aLoadingPrincipal ||
aLoadingContext->NodePrincipal() == aLoadingPrincipal);
// if the load is sandboxed, we can not also inherit the principal
if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
mSecurityFlags ^= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
mForceInheritPrincipalDropped = true;
}
if (aLoadingContext) {
nsCOMPtr<nsPIDOMWindowOuter> contextOuter = aLoadingContext->OwnerDoc()->GetWindow();
if (contextOuter) {
ComputeIsThirdPartyContext(contextOuter);
mOuterWindowID = contextOuter->WindowID();
nsCOMPtr<nsPIDOMWindowOuter> parent = contextOuter->GetScriptableParent();
mParentOuterWindowID = parent ? parent->WindowID() : mOuterWindowID;
}
mInnerWindowID = aLoadingContext->OwnerDoc()->InnerWindowID();
// When the element being loaded is a frame, we choose the frame's window
// for the window ID and the frame element's window as the parent
// window. This is the behavior that Chrome exposes to add-ons.
// NB: If the frameLoaderOwner doesn't have a frame loader, then the load
// must be coming from an object (such as a plugin) that's loaded into it
// instead of a document being loaded. In that case, treat this object like
// any other non-document-loading element.
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner =
do_QueryInterface(aLoadingContext);
nsCOMPtr<nsIFrameLoader> fl = frameLoaderOwner ?
frameLoaderOwner->GetFrameLoader() : nullptr;
if (fl) {
nsCOMPtr<nsIDocShell> docShell;
if (NS_SUCCEEDED(fl->GetDocShell(getter_AddRefs(docShell))) && docShell) {
nsCOMPtr<nsPIDOMWindowOuter> outerWindow = do_GetInterface(docShell);
if (outerWindow) {
mFrameOuterWindowID = outerWindow->WindowID();
}
}
}
// if the document forces all requests to be upgraded from http to https, then
// we should do that for all requests. If it only forces preloads to be upgraded
// then we should enforce upgrade insecure requests only for preloads.
mUpgradeInsecureRequests =
aLoadingContext->OwnerDoc()->GetUpgradeInsecureRequests(false) ||
(nsContentUtils::IsPreloadType(mInternalContentPolicyType) &&
aLoadingContext->OwnerDoc()->GetUpgradeInsecureRequests(true));
// if owner doc has content signature, we enforce SRI
nsCOMPtr<nsIChannel> channel = aLoadingContext->OwnerDoc()->GetChannel();
if (channel) {
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
if (loadInfo) {
mEnforceSRI = loadInfo->GetVerifySignedContent();
}
}
}
// If CSP requires SRI (require-sri-for), then store that information
// in the loadInfo so we can enforce SRI before loading the subresource.
if (!mEnforceSRI) {
// do not look into the CSP if already true:
// a CSP saying that SRI isn't needed should not
// overrule GetVerifySignedContent
if (aLoadingPrincipal) {
nsCOMPtr<nsIContentSecurityPolicy> csp;
aLoadingPrincipal->GetCsp(getter_AddRefs(csp));
uint32_t externalType =
nsContentUtils::InternalContentPolicyTypeToExternal(aContentPolicyType);
// csp could be null if loading principal is system principal
if (csp) {
csp->RequireSRIForType(externalType, &mEnforceSRI);
}
// if CSP is delivered via a meta tag, it's speculatively available
// as 'preloadCSP'. If we are preloading a script or style, we have
// to apply that speculative 'preloadCSP' for such loads.
if (!mEnforceSRI && nsContentUtils::IsPreloadType(aContentPolicyType)) {
nsCOMPtr<nsIContentSecurityPolicy> preloadCSP;
aLoadingPrincipal->GetPreloadCsp(getter_AddRefs(preloadCSP));
if (preloadCSP) {
preloadCSP->RequireSRIForType(externalType, &mEnforceSRI);
}
}
}
}
InheritOriginAttributes(mLoadingPrincipal, mOriginAttributes);
// We need to do this after inheriting the document's origin attributes
// above, in case the loading principal ends up being the system principal.
if (aLoadingContext) {
nsCOMPtr<nsILoadContext> loadContext =
aLoadingContext->OwnerDoc()->GetLoadContext();
nsCOMPtr<nsIDocShell> docShell = aLoadingContext->OwnerDoc()->GetDocShell();
if (loadContext && docShell &&
docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
bool usePrivateBrowsing;
nsresult rv = loadContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
if (NS_SUCCEEDED(rv)) {
mOriginAttributes.SyncAttributesWithPrivateBrowsing(usePrivateBrowsing);
}
}
}
// For chrome docshell, the mPrivateBrowsingId remains 0 even its
// UsePrivateBrowsing() is true, so we only update the mPrivateBrowsingId in
// origin attributes if the type of the docshell is content.
if (aLoadingContext) {
nsCOMPtr<nsIDocShell> docShell = aLoadingContext->OwnerDoc()->GetDocShell();
if (docShell) {
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
MOZ_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0,
"chrome docshell shouldn't have mPrivateBrowsingId set.");
}
}
}
}
/* Constructor takes an outer window, but no loadingNode or loadingPrincipal.
* This constructor should only be used for TYPE_DOCUMENT loads, since they
* have a null loadingNode and loadingPrincipal.
*/
LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
nsIPrincipal* aTriggeringPrincipal,
nsSecurityFlags aSecurityFlags)
: mLoadingPrincipal(nullptr)
, mTriggeringPrincipal(aTriggeringPrincipal)
, mPrincipalToInherit(nullptr)
, mSecurityFlags(aSecurityFlags)
, mInternalContentPolicyType(nsIContentPolicy::TYPE_DOCUMENT)
, mTainting(LoadTainting::Basic)
, mUpgradeInsecureRequests(false)
, mVerifySignedContent(false)
, mEnforceSRI(false)
, mForceInheritPrincipalDropped(false)
, mInnerWindowID(0)
, mOuterWindowID(0)
, mParentOuterWindowID(0)
, mFrameOuterWindowID(0)
, mEnforceSecurity(false)
, mInitialSecurityCheckDone(false)
, mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
, mForcePreflight(false)
, mIsPreflight(false)
, mForceHSTSPriming(false)
, mMixedContentWouldBlock(false)
{
// Top-level loads are never third-party
// Grab the information we can out of the window.
MOZ_ASSERT(aOuterWindow);
MOZ_ASSERT(mTriggeringPrincipal);
// if the load is sandboxed, we can not also inherit the principal
if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
mSecurityFlags ^= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
mForceInheritPrincipalDropped = true;
}
// NB: Ignore the current inner window since we're navigating away from it.
mOuterWindowID = aOuterWindow->WindowID();
// TODO We can have a parent without a frame element in some cases dealing
// with the hidden window.
nsCOMPtr<nsPIDOMWindowOuter> parent = aOuterWindow->GetScriptableParent();
mParentOuterWindowID = parent ? parent->WindowID() : 0;
// get the docshell from the outerwindow, and then get the originattributes
nsCOMPtr<nsIDocShell> docShell = aOuterWindow->GetDocShell();
MOZ_ASSERT(docShell);
const DocShellOriginAttributes attrs =
nsDocShell::Cast(docShell)->GetOriginAttributes();
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
MOZ_ASSERT(attrs.mPrivateBrowsingId == 0,
"chrome docshell shouldn't have mPrivateBrowsingId set.");
}
mOriginAttributes.InheritFromDocShellToNecko(attrs);
}
LoadInfo::LoadInfo(const LoadInfo& rhs)
: mLoadingPrincipal(rhs.mLoadingPrincipal)
, mTriggeringPrincipal(rhs.mTriggeringPrincipal)
, mPrincipalToInherit(rhs.mPrincipalToInherit)
, mLoadingContext(rhs.mLoadingContext)
, mSecurityFlags(rhs.mSecurityFlags)
, mInternalContentPolicyType(rhs.mInternalContentPolicyType)
, mTainting(rhs.mTainting)
, mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
, mVerifySignedContent(rhs.mVerifySignedContent)
, mEnforceSRI(rhs.mEnforceSRI)
, mForceInheritPrincipalDropped(rhs.mForceInheritPrincipalDropped)
, mInnerWindowID(rhs.mInnerWindowID)
, mOuterWindowID(rhs.mOuterWindowID)
, mParentOuterWindowID(rhs.mParentOuterWindowID)
, mFrameOuterWindowID(rhs.mFrameOuterWindowID)
, mEnforceSecurity(rhs.mEnforceSecurity)
, mInitialSecurityCheckDone(rhs.mInitialSecurityCheckDone)
, mIsThirdPartyContext(rhs.mIsThirdPartyContext)
, mOriginAttributes(rhs.mOriginAttributes)
, mRedirectChainIncludingInternalRedirects(
rhs.mRedirectChainIncludingInternalRedirects)
, mRedirectChain(rhs.mRedirectChain)
, mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
, mForcePreflight(rhs.mForcePreflight)
, mIsPreflight(rhs.mIsPreflight)
, mForceHSTSPriming(rhs.mForceHSTSPriming)
, mMixedContentWouldBlock(rhs.mMixedContentWouldBlock)
{
}
LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType,
LoadTainting aTainting,
bool aUpgradeInsecureRequests,
bool aVerifySignedContent,
bool aEnforceSRI,
bool aForceInheritPrincipalDropped,
uint64_t aInnerWindowID,
uint64_t aOuterWindowID,
uint64_t aParentOuterWindowID,
uint64_t aFrameOuterWindowID,
bool aEnforceSecurity,
bool aInitialSecurityCheckDone,
bool aIsThirdPartyContext,
const NeckoOriginAttributes& aOriginAttributes,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
const nsTArray<nsCString>& aCorsUnsafeHeaders,
bool aForcePreflight,
bool aIsPreflight,
bool aForceHSTSPriming,
bool aMixedContentWouldBlock)
: mLoadingPrincipal(aLoadingPrincipal)
, mTriggeringPrincipal(aTriggeringPrincipal)
, mPrincipalToInherit(aPrincipalToInherit)
, mSecurityFlags(aSecurityFlags)
, mInternalContentPolicyType(aContentPolicyType)
, mTainting(aTainting)
, mUpgradeInsecureRequests(aUpgradeInsecureRequests)
, mVerifySignedContent(aVerifySignedContent)
, mEnforceSRI(aEnforceSRI)
, mForceInheritPrincipalDropped(aForceInheritPrincipalDropped)
, mInnerWindowID(aInnerWindowID)
, mOuterWindowID(aOuterWindowID)
, mParentOuterWindowID(aParentOuterWindowID)
, mFrameOuterWindowID(aFrameOuterWindowID)
, mEnforceSecurity(aEnforceSecurity)
, mInitialSecurityCheckDone(aInitialSecurityCheckDone)
, mIsThirdPartyContext(aIsThirdPartyContext)
, mOriginAttributes(aOriginAttributes)
, mCorsUnsafeHeaders(aCorsUnsafeHeaders)
, mForcePreflight(aForcePreflight)
, mIsPreflight(aIsPreflight)
, mForceHSTSPriming (aForceHSTSPriming)
, mMixedContentWouldBlock(aMixedContentWouldBlock)
{
// Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
MOZ_ASSERT(mTriggeringPrincipal);
mRedirectChainIncludingInternalRedirects.SwapElements(
aRedirectChainIncludingInternalRedirects);
mRedirectChain.SwapElements(aRedirectChain);
}
LoadInfo::~LoadInfo()
{
}
void
LoadInfo::ComputeIsThirdPartyContext(nsPIDOMWindowOuter* aOuterWindow)
{
nsContentPolicyType type =
nsContentUtils::InternalContentPolicyTypeToExternal(mInternalContentPolicyType);
if (type == nsIContentPolicy::TYPE_DOCUMENT) {
// Top-level loads are never third-party.
mIsThirdPartyContext = false;
return;
}
nsCOMPtr<mozIThirdPartyUtil> util(do_GetService(THIRDPARTYUTIL_CONTRACTID));
if (NS_WARN_IF(!util)) {
return;
}
util->IsThirdPartyWindow(aOuterWindow, nullptr, &mIsThirdPartyContext);
}
NS_IMPL_ISUPPORTS(LoadInfo, nsILoadInfo)
already_AddRefed<nsILoadInfo>
LoadInfo::Clone() const
{
RefPtr<LoadInfo> copy(new LoadInfo(*this));
return copy.forget();
}
already_AddRefed<nsILoadInfo>
LoadInfo::CloneWithNewSecFlags(nsSecurityFlags aSecurityFlags) const
{
RefPtr<LoadInfo> copy(new LoadInfo(*this));
copy->mSecurityFlags = aSecurityFlags;
return copy.forget();
}
already_AddRefed<nsILoadInfo>
LoadInfo::CloneForNewRequest() const
{
RefPtr<LoadInfo> copy(new LoadInfo(*this));
copy->mEnforceSecurity = false;
copy->mInitialSecurityCheckDone = false;
copy->mRedirectChainIncludingInternalRedirects.Clear();
copy->mRedirectChain.Clear();
return copy.forget();
}
NS_IMETHODIMP
LoadInfo::GetLoadingPrincipal(nsIPrincipal** aLoadingPrincipal)
{
NS_IF_ADDREF(*aLoadingPrincipal = mLoadingPrincipal);
return NS_OK;
}
nsIPrincipal*
LoadInfo::LoadingPrincipal()
{
return mLoadingPrincipal;
}
NS_IMETHODIMP
LoadInfo::GetTriggeringPrincipal(nsIPrincipal** aTriggeringPrincipal)
{
NS_ADDREF(*aTriggeringPrincipal = mTriggeringPrincipal);
return NS_OK;
}
nsIPrincipal*
LoadInfo::TriggeringPrincipal()
{
return mTriggeringPrincipal;
}
NS_IMETHODIMP
LoadInfo::GetPrincipalToInherit(nsIPrincipal** aPrincipalToInherit)
{
NS_IF_ADDREF(*aPrincipalToInherit = mPrincipalToInherit);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit)
{
MOZ_ASSERT(aPrincipalToInherit, "must be a valid principal to inherit");
mPrincipalToInherit = aPrincipalToInherit;
return NS_OK;
}
nsIPrincipal*
LoadInfo::PrincipalToInherit()
{
return mPrincipalToInherit;
}
NS_IMETHODIMP
LoadInfo::GetLoadingDocument(nsIDOMDocument** aResult)
{
nsCOMPtr<nsINode> node = do_QueryReferent(mLoadingContext);
if (node) {
nsCOMPtr<nsIDOMDocument> context = do_QueryInterface(node->OwnerDoc());
context.forget(aResult);
}
return NS_OK;
}
nsINode*
LoadInfo::LoadingNode()
{
nsCOMPtr<nsINode> node = do_QueryReferent(mLoadingContext);
return node;
}
NS_IMETHODIMP
LoadInfo::GetSecurityFlags(nsSecurityFlags* aResult)
{
*aResult = mSecurityFlags;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetSecurityMode(uint32_t* aFlags)
{
*aFlags = (mSecurityFlags &
(nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED |
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL |
nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS));
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetIsInThirdPartyContext(bool* aIsInThirdPartyContext)
{
*aIsInThirdPartyContext = mIsThirdPartyContext;
return NS_OK;
}
static const uint32_t sCookiePolicyMask =
nsILoadInfo::SEC_COOKIES_DEFAULT |
nsILoadInfo::SEC_COOKIES_INCLUDE |
nsILoadInfo::SEC_COOKIES_SAME_ORIGIN |
nsILoadInfo::SEC_COOKIES_OMIT;
NS_IMETHODIMP
LoadInfo::GetCookiePolicy(uint32_t *aResult)
{
uint32_t policy = mSecurityFlags & sCookiePolicyMask;
if (policy == nsILoadInfo::SEC_COOKIES_DEFAULT) {
policy = (mSecurityFlags & SEC_REQUIRE_CORS_DATA_INHERITS) ?
nsILoadInfo::SEC_COOKIES_SAME_ORIGIN : nsILoadInfo::SEC_COOKIES_INCLUDE;
}
*aResult = policy;
return NS_OK;
}
void
LoadInfo::SetIncludeCookiesSecFlag()
{
MOZ_ASSERT(!mEnforceSecurity,
"Request should not have been opened yet");
MOZ_ASSERT((mSecurityFlags & sCookiePolicyMask) ==
nsILoadInfo::SEC_COOKIES_DEFAULT);
mSecurityFlags = (mSecurityFlags & ~sCookiePolicyMask) |
nsILoadInfo::SEC_COOKIES_INCLUDE;
}
NS_IMETHODIMP
LoadInfo::GetForceInheritPrincipal(bool* aInheritPrincipal)
{
*aInheritPrincipal =
(mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetForceInheritPrincipalOverruleOwner(bool* aInheritPrincipal)
{
*aInheritPrincipal =
(mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetLoadingSandboxed(bool* aLoadingSandboxed)
{
*aLoadingSandboxed = (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetAboutBlankInherits(bool* aResult)
{
*aResult =
(mSecurityFlags & nsILoadInfo::SEC_ABOUT_BLANK_INHERITS);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetAllowChrome(bool* aResult)
{
*aResult =
(mSecurityFlags & nsILoadInfo::SEC_ALLOW_CHROME);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetDisallowScript(bool* aResult)
{
*aResult =
(mSecurityFlags & nsILoadInfo::SEC_DISALLOW_SCRIPT);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetDontFollowRedirects(bool* aResult)
{
*aResult =
(mSecurityFlags & nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetLoadErrorPage(bool* aResult)
{
*aResult =
(mSecurityFlags & nsILoadInfo::SEC_LOAD_ERROR_PAGE);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetExternalContentPolicyType(nsContentPolicyType* aResult)
{
*aResult = nsContentUtils::InternalContentPolicyTypeToExternal(mInternalContentPolicyType);
return NS_OK;
}
nsContentPolicyType
LoadInfo::InternalContentPolicyType()
{
return mInternalContentPolicyType;
}
NS_IMETHODIMP
LoadInfo::GetUpgradeInsecureRequests(bool* aResult)
{
*aResult = mUpgradeInsecureRequests;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetVerifySignedContent(bool aVerifySignedContent)
{
MOZ_ASSERT(mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
"can only verify content for TYPE_DOCUMENT");
mVerifySignedContent = aVerifySignedContent;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetVerifySignedContent(bool* aResult)
{
*aResult = mVerifySignedContent;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetEnforceSRI(bool aEnforceSRI)
{
mEnforceSRI = aEnforceSRI;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetEnforceSRI(bool* aResult)
{
*aResult = mEnforceSRI;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetForceInheritPrincipalDropped(bool* aResult)
{
*aResult = mForceInheritPrincipalDropped;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetInnerWindowID(uint64_t* aResult)
{
*aResult = mInnerWindowID;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetOuterWindowID(uint64_t* aResult)
{
*aResult = mOuterWindowID;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetParentOuterWindowID(uint64_t* aResult)
{
*aResult = mParentOuterWindowID;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetFrameOuterWindowID(uint64_t* aResult)
{
*aResult = mFrameOuterWindowID;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetScriptableOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aOriginAttributes)
{
if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::ResetPrincipalsToNullPrincipal()
{
// take the originAttributes from the LoadInfo and create
// a new NullPrincipal using those origin attributes.
PrincipalOriginAttributes pAttrs;
pAttrs.InheritFromNecko(mOriginAttributes);
nsCOMPtr<nsIPrincipal> newNullPrincipal = nsNullPrincipal::Create(pAttrs);
MOZ_ASSERT(mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT ||
!mLoadingPrincipal,
"LoadingPrincipal should be null for toplevel loads");
// the loadingPrincipal for toplevel loads is always a nullptr;
if (mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT) {
mLoadingPrincipal = newNullPrincipal;
}
mTriggeringPrincipal = newNullPrincipal;
mPrincipalToInherit = newNullPrincipal;
// setting SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER will overrule
// any non null owner set on the channel and will return the principal
// form the loadinfo instead.
mSecurityFlags |= SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetScriptableOriginAttributes(JSContext* aCx,
JS::Handle<JS::Value> aOriginAttributes)
{
NeckoOriginAttributes attrs;
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
mOriginAttributes = attrs;
return NS_OK;
}
nsresult
LoadInfo::GetOriginAttributes(mozilla::NeckoOriginAttributes* aOriginAttributes)
{
NS_ENSURE_ARG(aOriginAttributes);
*aOriginAttributes = mOriginAttributes;
return NS_OK;
}
nsresult
LoadInfo::SetOriginAttributes(const mozilla::NeckoOriginAttributes& aOriginAttributes)
{
mOriginAttributes = aOriginAttributes;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetEnforceSecurity(bool aEnforceSecurity)
{
// Indicates whether the channel was openend using AsyncOpen2. Once set
// to true, it must remain true throughout the lifetime of the channel.
// Setting it to anything else than true will be discarded.
MOZ_ASSERT(aEnforceSecurity, "aEnforceSecurity must be true");
mEnforceSecurity = mEnforceSecurity || aEnforceSecurity;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetEnforceSecurity(bool* aResult)
{
*aResult = mEnforceSecurity;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetInitialSecurityCheckDone(bool aInitialSecurityCheckDone)
{
// Indicates whether the channel was ever evaluated by the
// ContentSecurityManager. Once set to true, this flag must
// remain true throughout the lifetime of the channel.
// Setting it to anything else than true will be discarded.
MOZ_ASSERT(aInitialSecurityCheckDone, "aInitialSecurityCheckDone must be true");
mInitialSecurityCheckDone = mInitialSecurityCheckDone || aInitialSecurityCheckDone;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetInitialSecurityCheckDone(bool* aResult)
{
*aResult = mInitialSecurityCheckDone;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::AppendRedirectedPrincipal(nsIPrincipal* aPrincipal, bool aIsInternalRedirect)
{
NS_ENSURE_ARG(aPrincipal);
MOZ_ASSERT(NS_IsMainThread());
mRedirectChainIncludingInternalRedirects.AppendElement(aPrincipal);
if (!aIsInternalRedirect) {
mRedirectChain.AppendElement(aPrincipal);
}
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetRedirectChainIncludingInternalRedirects(JSContext* aCx, JS::MutableHandle<JS::Value> aChain)
{
if (!ToJSValue(aCx, mRedirectChainIncludingInternalRedirects, aChain)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
const nsTArray<nsCOMPtr<nsIPrincipal>>&
LoadInfo::RedirectChainIncludingInternalRedirects()
{
return mRedirectChainIncludingInternalRedirects;
}
NS_IMETHODIMP
LoadInfo::GetRedirectChain(JSContext* aCx, JS::MutableHandle<JS::Value> aChain)
{
if (!ToJSValue(aCx, mRedirectChain, aChain)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
const nsTArray<nsCOMPtr<nsIPrincipal>>&
LoadInfo::RedirectChain()
{
return mRedirectChain;
}
void
LoadInfo::SetCorsPreflightInfo(const nsTArray<nsCString>& aHeaders,
bool aForcePreflight)
{
MOZ_ASSERT(GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS);
MOZ_ASSERT(!mInitialSecurityCheckDone);
mCorsUnsafeHeaders = aHeaders;
mForcePreflight = aForcePreflight;
}
const nsTArray<nsCString>&
LoadInfo::CorsUnsafeHeaders()
{
return mCorsUnsafeHeaders;
}
NS_IMETHODIMP
LoadInfo::GetForcePreflight(bool* aForcePreflight)
{
*aForcePreflight = mForcePreflight;
return NS_OK;
}
void
LoadInfo::SetIsPreflight()
{
MOZ_ASSERT(GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS);
MOZ_ASSERT(!mInitialSecurityCheckDone);
mIsPreflight = true;
}
NS_IMETHODIMP
LoadInfo::GetIsPreflight(bool* aIsPreflight)
{
*aIsPreflight = mIsPreflight;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetForceHSTSPriming(bool* aForceHSTSPriming)
{
*aForceHSTSPriming = mForceHSTSPriming;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetMixedContentWouldBlock(bool *aMixedContentWouldBlock)
{
*aMixedContentWouldBlock = mMixedContentWouldBlock;
return NS_OK;
}
void
LoadInfo::SetHSTSPriming(bool aMixedContentWouldBlock)
{
mForceHSTSPriming = true;
mMixedContentWouldBlock = aMixedContentWouldBlock;
}
void
LoadInfo::ClearHSTSPriming()
{
mForceHSTSPriming = false;
mMixedContentWouldBlock = false;
}
NS_IMETHODIMP
LoadInfo::GetTainting(uint32_t* aTaintingOut)
{
MOZ_ASSERT(aTaintingOut);
*aTaintingOut = static_cast<uint32_t>(mTainting);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::MaybeIncreaseTainting(uint32_t aTainting)
{
NS_ENSURE_ARG(aTainting <= TAINTING_OPAQUE);
LoadTainting tainting = static_cast<LoadTainting>(aTainting);
if (tainting > mTainting) {
mTainting = tainting;
}
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetIsTopLevelLoad(bool *aResult)
{
*aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID
: mParentOuterWindowID == mOuterWindowID;
return NS_OK;
}
} // namespace net
} // namespace mozilla

163
netwerk/base/LoadInfo.h Normal file
View file

@ -0,0 +1,163 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_LoadInfo_h
#define mozilla_LoadInfo_h
#include "nsIContentPolicy.h"
#include "nsILoadInfo.h"
#include "nsIPrincipal.h"
#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
#include "nsIURI.h"
#include "nsTArray.h"
#include "mozilla/BasePrincipal.h"
class nsINode;
class nsPIDOMWindowOuter;
namespace mozilla {
namespace dom {
class XMLHttpRequestMainThread;
}
namespace net {
class OptionalLoadInfoArgs;
} // namespace net
namespace ipc {
// we have to forward declare that function so we can use it as a friend.
nsresult
LoadInfoArgsToLoadInfo(const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
nsILoadInfo** outLoadInfo);
} // namespace ipc
namespace net {
/**
* Class that provides an nsILoadInfo implementation.
*
* Note that there is no reason why this class should be MOZ_EXPORT, but
* Thunderbird relies on some insane hacks which require this, so we'll leave it
* as is for now, but hopefully we'll be able to remove the MOZ_EXPORT keyword
* from this class at some point. See bug 1149127 for the discussion.
*/
class MOZ_EXPORT LoadInfo final : public nsILoadInfo
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSILOADINFO
// aLoadingPrincipal MUST NOT BE NULL.
LoadInfo(nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
nsINode* aLoadingContext,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType);
// Constructor used for TYPE_DOCUMENT loads which have no reasonable
// loadingNode or loadingPrincipal
LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
nsIPrincipal* aTriggeringPrincipal,
nsSecurityFlags aSecurityFlags);
// create an exact copy of the loadinfo
already_AddRefed<nsILoadInfo> Clone() const;
// hands off!!! don't use CloneWithNewSecFlags unless you know
// exactly what you are doing - it should only be used within
// nsBaseChannel::Redirect()
already_AddRefed<nsILoadInfo>
CloneWithNewSecFlags(nsSecurityFlags aSecurityFlags) const;
// creates a copy of the loadinfo which is appropriate to use for a
// separate request. I.e. not for a redirect or an inner channel, but
// when a separate request is made with the same security properties.
already_AddRefed<nsILoadInfo> CloneForNewRequest() const;
void SetIsPreflight();
private:
// private constructor that is only allowed to be called from within
// HttpChannelParent and FTPChannelParent declared as friends undeneath.
// In e10s we can not serialize nsINode, hence we store the innerWindowID.
// Please note that aRedirectChain uses swapElements.
LoadInfo(nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType,
LoadTainting aTainting,
bool aUpgradeInsecureRequests,
bool aVerifySignedContent,
bool aEnforceSRI,
bool aForceInheritPrincipalDropped,
uint64_t aInnerWindowID,
uint64_t aOuterWindowID,
uint64_t aParentOuterWindowID,
uint64_t aFrameOuterWindowID,
bool aEnforceSecurity,
bool aInitialSecurityCheckDone,
bool aIsThirdPartyRequest,
const NeckoOriginAttributes& aOriginAttributes,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
const nsTArray<nsCString>& aUnsafeHeaders,
bool aForcePreflight,
bool aIsPreflight,
bool aForceHSTSPriming,
bool aMixedContentWouldBlock);
LoadInfo(const LoadInfo& rhs);
friend nsresult
mozilla::ipc::LoadInfoArgsToLoadInfo(
const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
nsILoadInfo** outLoadInfo);
~LoadInfo();
void ComputeIsThirdPartyContext(nsPIDOMWindowOuter* aOuterWindow);
// This function is the *only* function which can change the securityflags
// of a loadinfo. It only exists because of the XHR code. Don't call it
// from anywhere else!
void SetIncludeCookiesSecFlag();
friend class mozilla::dom::XMLHttpRequestMainThread;
// if you add a member, please also update the copy constructor
nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
nsWeakPtr mLoadingContext;
nsSecurityFlags mSecurityFlags;
nsContentPolicyType mInternalContentPolicyType;
LoadTainting mTainting;
bool mUpgradeInsecureRequests;
bool mVerifySignedContent;
bool mEnforceSRI;
bool mForceInheritPrincipalDropped;
uint64_t mInnerWindowID;
uint64_t mOuterWindowID;
uint64_t mParentOuterWindowID;
uint64_t mFrameOuterWindowID;
bool mEnforceSecurity;
bool mInitialSecurityCheckDone;
bool mIsThirdPartyContext;
NeckoOriginAttributes mOriginAttributes;
nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChainIncludingInternalRedirects;
nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChain;
nsTArray<nsCString> mCorsUnsafeHeaders;
bool mForcePreflight;
bool mIsPreflight;
bool mForceHSTSPriming : 1;
bool mMixedContentWouldBlock : 1;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_LoadInfo_h

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_LoadTainting_h
#define mozilla_LoadTainting_h
namespace mozilla {
// Define an enumeration to reflect the concept of response tainting from the
// the fetch spec:
//
// https://fetch.spec.whatwg.org/#concept-request-response-tainting
//
// Roughly the tainting means:
//
// * Basic: the request resulted in a same-origin or non-http load
// * CORS: the request resulted in a cross-origin load with CORS headers
// * Opaque: the request resulted in a cross-origin load without CORS headers
//
// The enumeration is purposefully designed such that more restrictive tainting
// corresponds to a higher integral value.
//
// NOTE: Checking the tainting is not currently adequate. You *must* still
// check the final URL and CORS mode on the channel.
//
// These values are currently only set on the channel LoadInfo when the request
// was initiated through fetch() or when a service worker interception occurs.
// In the future we should set the tainting value within necko so that it is
// consistently applied. Once that is done consumers can replace checks against
// the final URL and CORS mode with checks against tainting.
enum class LoadTainting : uint8_t
{
Basic = 0,
CORS = 1,
Opaque = 2
};
} // namespace mozilla
#endif // mozilla_LoadTainting_h

View file

@ -0,0 +1,92 @@
/* -*- Mode: C++; 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 "MemoryDownloader.h"
#include "mozilla/Assertions.h"
#include "nsIInputStream.h"
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(MemoryDownloader,
nsIStreamListener,
nsIRequestObserver)
MemoryDownloader::MemoryDownloader(IObserver* aObserver)
: mObserver(aObserver)
{
}
MemoryDownloader::~MemoryDownloader()
{
}
NS_IMETHODIMP
MemoryDownloader::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
{
MOZ_ASSERT(!mData);
mData.reset(new FallibleTArray<uint8_t>());
mStatus = NS_OK;
return NS_OK;
}
NS_IMETHODIMP
MemoryDownloader::OnStopRequest(nsIRequest* aRequest,
nsISupports* aCtxt,
nsresult aStatus)
{
MOZ_ASSERT_IF(NS_FAILED(mStatus), NS_FAILED(aStatus));
MOZ_ASSERT(!mData == NS_FAILED(mStatus));
Data data;
data.swap(mData);
RefPtr<IObserver> observer;
observer.swap(mObserver);
observer->OnDownloadComplete(this, aRequest, aCtxt, aStatus,
mozilla::Move(data));
return NS_OK;
}
nsresult
MemoryDownloader::ConsumeData(nsIInputStream* aIn,
void* aClosure,
const char* aFromRawSegment,
uint32_t aToOffset,
uint32_t aCount,
uint32_t* aWriteCount)
{
MemoryDownloader* self = static_cast<MemoryDownloader*>(aClosure);
if (!self->mData->AppendElements(aFromRawSegment, aCount, fallible)) {
// The error returned by ConsumeData isn't propagated to the
// return of ReadSegments, so it has to be passed as state.
self->mStatus = NS_ERROR_OUT_OF_MEMORY;
return NS_ERROR_OUT_OF_MEMORY;
}
*aWriteCount = aCount;
return NS_OK;
}
NS_IMETHODIMP
MemoryDownloader::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aCtxt,
nsIInputStream* aInStr,
uint64_t aSourceOffset,
uint32_t aCount)
{
uint32_t n;
MOZ_ASSERT(mData);
nsresult rv = aInStr->ReadSegments(ConsumeData, this, aCount, &n);
if (NS_SUCCEEDED(mStatus) && NS_FAILED(rv)) {
mStatus = rv;
}
if (NS_WARN_IF(NS_FAILED(mStatus))) {
mData.reset(nullptr);
return mStatus;
}
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,65 @@
/* -*- Mode: C++; 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 mozilla_net_MemoryDownloader_h__
#define mozilla_net_MemoryDownloader_h__
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsIStreamListener.h"
#include "nsTArray.h"
/**
* mozilla::net::MemoryDownloader
*
* This class is similar to nsIDownloader, but stores the downloaded
* stream in memory instead of a file. Ownership of the temporary
* memory is transferred to the observer when download is complete;
* there is no need to retain a reference to the downloader.
*/
namespace mozilla {
namespace net {
class MemoryDownloader final : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
typedef mozilla::UniquePtr<FallibleTArray<uint8_t>> Data;
class IObserver : public nsISupports {
public:
// Note: aData may be null if (and only if) aStatus indicates failure.
virtual void OnDownloadComplete(MemoryDownloader* aDownloader,
nsIRequest* aRequest,
nsISupports* aCtxt,
nsresult aStatus,
Data aData) = 0;
};
explicit MemoryDownloader(IObserver* aObserver);
private:
virtual ~MemoryDownloader();
static nsresult ConsumeData(nsIInputStream *in,
void *closure,
const char *fromRawSegment,
uint32_t toOffset,
uint32_t count,
uint32_t *writeCount);
RefPtr<IObserver> mObserver;
Data mData;
nsresult mStatus;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_MemoryDownloader_h__

View file

@ -0,0 +1,100 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 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/. */
#ifndef NetStatistics_h__
#define NetStatistics_h__
#include "mozilla/Assertions.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsINetworkInterface.h"
#include "nsINetworkManager.h"
#include "nsINetworkStatsServiceProxy.h"
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
namespace mozilla {
namespace net {
// The following members are used for network per-app metering.
const static uint64_t NETWORK_STATS_THRESHOLD = 65536;
const static char NETWORK_STATS_NO_SERVICE_TYPE[] = "";
inline nsresult
GetActiveNetworkInfo(nsCOMPtr<nsINetworkInfo> &aNetworkInfo)
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
nsCOMPtr<nsINetworkManager> networkManager =
do_GetService("@mozilla.org/network/manager;1", &rv);
if (NS_FAILED(rv) || !networkManager) {
aNetworkInfo = nullptr;
return rv;
}
networkManager->GetActiveNetworkInfo(getter_AddRefs(aNetworkInfo));
return NS_OK;
}
class SaveNetworkStatsEvent : public Runnable {
public:
SaveNetworkStatsEvent(uint32_t aAppId,
bool aIsInIsolatedMozBrowser,
nsMainThreadPtrHandle<nsINetworkInfo> &aActiveNetworkInfo,
uint64_t aCountRecv,
uint64_t aCountSent,
bool aIsAccumulative)
: mAppId(aAppId),
mIsInIsolatedMozBrowser(aIsInIsolatedMozBrowser),
mActiveNetworkInfo(aActiveNetworkInfo),
mCountRecv(aCountRecv),
mCountSent(aCountSent),
mIsAccumulative(aIsAccumulative)
{
MOZ_ASSERT(mAppId != NECKO_NO_APP_ID);
MOZ_ASSERT(mActiveNetworkInfo);
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
nsCOMPtr<nsINetworkStatsServiceProxy> mNetworkStatsServiceProxy =
do_GetService("@mozilla.org/networkstatsServiceProxy;1", &rv);
if (NS_FAILED(rv)) {
return rv;
}
// save the network stats through NetworkStatsServiceProxy
mNetworkStatsServiceProxy->SaveAppStats(mAppId,
mIsInIsolatedMozBrowser,
mActiveNetworkInfo,
PR_Now() / 1000,
mCountRecv,
mCountSent,
mIsAccumulative,
nullptr);
return NS_OK;
}
private:
uint32_t mAppId;
bool mIsInIsolatedMozBrowser;
nsMainThreadPtrHandle<nsINetworkInfo> mActiveNetworkInfo;
uint64_t mCountRecv;
uint64_t mCountSent;
bool mIsAccumulative;
};
} // namespace mozilla:net
} // namespace mozilla
#endif // !NetStatistics_h__

461
netwerk/base/NetUtil.jsm Normal file
View file

@ -0,0 +1,461 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
* vim: sw=4 ts=4 sts=4 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
this.EXPORTED_SYMBOLS = [
"NetUtil",
];
/**
* Necko utilities
*/
////////////////////////////////////////////////////////////////////////////////
//// Constants
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
const PR_UINT32_MAX = 0xffffffff;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
////////////////////////////////////////////////////////////////////////////////
//// NetUtil Object
this.NetUtil = {
/**
* Function to perform simple async copying from aSource (an input stream)
* to aSink (an output stream). The copy will happen on some background
* thread. Both streams will be closed when the copy completes.
*
* @param aSource
* The input stream to read from
* @param aSink
* The output stream to write to
* @param aCallback [optional]
* A function that will be called at copy completion with a single
* argument: the nsresult status code for the copy operation.
*
* @return An nsIRequest representing the copy operation (for example, this
* can be used to cancel the copying). The consumer can ignore the
* return value if desired.
*/
asyncCopy: function NetUtil_asyncCopy(aSource, aSink,
aCallback = null)
{
if (!aSource || !aSink) {
let exception = new Components.Exception(
"Must have a source and a sink",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
// make a stream copier
var copier = Cc["@mozilla.org/network/async-stream-copier;1"].
createInstance(Ci.nsIAsyncStreamCopier2);
copier.init(aSource, aSink,
null /* Default event target */,
0 /* Default length */,
true, true /* Auto-close */);
var observer;
if (aCallback) {
observer = {
onStartRequest: function(aRequest, aContext) {},
onStopRequest: function(aRequest, aContext, aStatusCode) {
aCallback(aStatusCode);
}
}
} else {
observer = null;
}
// start the copying
copier.QueryInterface(Ci.nsIAsyncStreamCopier).asyncCopy(observer, null);
return copier;
},
/**
* Asynchronously opens a source and fetches the response. While the fetch
* is asynchronous, I/O may happen on the main thread. When reading from
* a local file, prefer using "OS.File" methods instead.
*
* @param aSource
* This argument can be one of the following:
* - An options object that will be passed to NetUtil.newChannel.
* - An existing nsIChannel.
* - An existing nsIInputStream.
* Using an nsIURI, nsIFile, or string spec directly is deprecated.
* @param aCallback
* The callback function that will be notified upon completion. It
* will get these arguments:
* 1) An nsIInputStream containing the data from aSource, if any.
* 2) The status code from opening the source.
* 3) Reference to the nsIRequest.
*/
asyncFetch: function NetUtil_asyncFetch(aSource, aCallback)
{
if (!aSource || !aCallback) {
let exception = new Components.Exception(
"Must have a source and a callback",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
// Create a pipe that will create our output stream that we can use once
// we have gotten all the data.
let pipe = Cc["@mozilla.org/pipe;1"].
createInstance(Ci.nsIPipe);
pipe.init(true, true, 0, PR_UINT32_MAX, null);
// Create a listener that will give data to the pipe's output stream.
let listener = Cc["@mozilla.org/network/simple-stream-listener;1"].
createInstance(Ci.nsISimpleStreamListener);
listener.init(pipe.outputStream, {
onStartRequest: function(aRequest, aContext) {},
onStopRequest: function(aRequest, aContext, aStatusCode) {
pipe.outputStream.close();
aCallback(pipe.inputStream, aStatusCode, aRequest);
}
});
// Input streams are handled slightly differently from everything else.
if (aSource instanceof Ci.nsIInputStream) {
let pump = Cc["@mozilla.org/network/input-stream-pump;1"].
createInstance(Ci.nsIInputStreamPump);
pump.init(aSource, -1, -1, 0, 0, true);
pump.asyncRead(listener, null);
return;
}
let channel = aSource;
if (!(channel instanceof Ci.nsIChannel)) {
channel = this.newChannel(aSource);
}
try {
// Open the channel using asyncOpen2() if the loadinfo contains one
// of the security mode flags, otherwise fall back to use asyncOpen().
if (channel.loadInfo &&
channel.loadInfo.securityMode != 0) {
channel.asyncOpen2(listener);
}
else {
// Log deprecation warning to console to make sure all channels
// are created providing the correct security flags in the loadinfo.
// See nsILoadInfo for all available security flags and also the API
// of NetUtil.newChannel() for details above.
Cu.reportError("NetUtil.jsm: asyncFetch() requires the channel to have " +
"one of the security flags set in the loadinfo (see nsILoadInfo). " +
"Please create channel using NetUtil.newChannel()");
channel.asyncOpen(listener, null);
}
}
catch (e) {
let exception = new Components.Exception(
"Failed to open input source '" + channel.originalURI.spec + "'",
e.result,
Components.stack.caller,
aSource,
e
);
throw exception;
}
},
/**
* Constructs a new URI for the given spec, character set, and base URI, or
* an nsIFile.
*
* @param aTarget
* The string spec for the desired URI or an nsIFile.
* @param aOriginCharset [optional]
* The character set for the URI. Only used if aTarget is not an
* nsIFile.
* @param aBaseURI [optional]
* The base URI for the spec. Only used if aTarget is not an
* nsIFile.
*
* @return an nsIURI object.
*/
newURI: function NetUtil_newURI(aTarget, aOriginCharset, aBaseURI)
{
if (!aTarget) {
let exception = new Components.Exception(
"Must have a non-null string spec or nsIFile object",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
if (aTarget instanceof Ci.nsIFile) {
return this.ioService.newFileURI(aTarget);
}
return this.ioService.newURI(aTarget, aOriginCharset, aBaseURI);
},
/**
* Constructs a new channel for the given source.
*
* Keep in mind that URIs coming from a webpage should *never* use the
* systemPrincipal as the loadingPrincipal.
*
* @param aWhatToLoad
* This argument used to be a string spec for the desired URI, an
* nsIURI, or an nsIFile. Now it should be an options object with
* the following properties:
* {
* uri:
* The full URI spec string, nsIURI or nsIFile to create the
* channel for.
* Note that this cannot be an nsIFile if you have to specify a
* non-default charset or base URI. Call NetUtil.newURI first if
* you need to construct an URI using those options.
* loadingNode:
* loadingPrincipal:
* triggeringPrincipal:
* securityFlags:
* contentPolicyType:
* These will be used as values for the nsILoadInfo object on the
* created channel. For details, see nsILoadInfo in nsILoadInfo.idl
* loadUsingSystemPrincipal:
* Set this to true to use the system principal as
* loadingPrincipal. This must be omitted if loadingPrincipal or
* loadingNode are present.
* This should be used with care as it skips security checks.
* }
* @param aOriginCharset [deprecated]
* The character set for the URI. Only used if aWhatToLoad is a
* string, which is a deprecated API. Must be undefined otherwise.
* Use NetUtil.newURI if you need to use this option.
* @param aBaseURI [deprecated]
* The base URI for the spec. Only used if aWhatToLoad is a string,
* which is a deprecated API. Must be undefined otherwise. Use
* NetUtil.newURI if you need to use this option.
* @return an nsIChannel object.
*/
newChannel: function NetUtil_newChannel(aWhatToLoad, aOriginCharset, aBaseURI)
{
// Check for the deprecated API first.
if (typeof aWhatToLoad == "string" ||
(aWhatToLoad instanceof Ci.nsIFile) ||
(aWhatToLoad instanceof Ci.nsIURI)) {
let uri = (aWhatToLoad instanceof Ci.nsIURI)
? aWhatToLoad
: this.newURI(aWhatToLoad, aOriginCharset, aBaseURI);
// log deprecation warning for developers.
Services.console.logStringMessage(
"Warning: NetUtil.newChannel(uri) deprecated, please provide argument 'aWhatToLoad'");
// Provide default loadinfo arguments and call the new API.
let systemPrincipal =
Services.scriptSecurityManager.getSystemPrincipal();
return this.ioService.newChannelFromURI2(
uri,
null, // loadingNode
systemPrincipal, // loadingPrincipal
null, // triggeringPrincipal
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
Ci.nsIContentPolicy.TYPE_OTHER);
}
// We are using the updated API, that requires only the options object.
if (typeof aWhatToLoad != "object" ||
aOriginCharset !== undefined ||
aBaseURI !== undefined) {
throw new Components.Exception(
"newChannel requires a single object argument",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
}
let { uri,
loadingNode,
loadingPrincipal,
loadUsingSystemPrincipal,
triggeringPrincipal,
securityFlags,
contentPolicyType } = aWhatToLoad;
if (!uri) {
throw new Components.Exception(
"newChannel requires the 'uri' property on the options object.",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
}
if (typeof uri == "string" || uri instanceof Ci.nsIFile) {
uri = this.newURI(uri);
}
if (!loadingNode && !loadingPrincipal && !loadUsingSystemPrincipal) {
throw new Components.Exception(
"newChannel requires at least one of the 'loadingNode'," +
" 'loadingPrincipal', or 'loadUsingSystemPrincipal'" +
" properties on the options object.",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
}
if (loadUsingSystemPrincipal === true) {
if (loadingNode || loadingPrincipal) {
throw new Components.Exception(
"newChannel does not accept 'loadUsingSystemPrincipal'" +
" if the 'loadingNode' or 'loadingPrincipal' properties" +
" are present on the options object.",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
}
loadingPrincipal = Services.scriptSecurityManager
.getSystemPrincipal();
} else if (loadUsingSystemPrincipal !== undefined) {
throw new Components.Exception(
"newChannel requires the 'loadUsingSystemPrincipal'" +
" property on the options object to be 'true' or 'undefined'.",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
}
if (securityFlags === undefined) {
securityFlags = loadUsingSystemPrincipal
? Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
: Ci.nsILoadInfo.SEC_NORMAL;
}
if (contentPolicyType === undefined) {
if (!loadUsingSystemPrincipal) {
throw new Components.Exception(
"newChannel requires the 'contentPolicyType' property on" +
" the options object unless loading from system principal.",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
}
contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
}
return this.ioService.newChannelFromURI2(uri,
loadingNode || null,
loadingPrincipal || null,
triggeringPrincipal || null,
securityFlags,
contentPolicyType);
},
/**
* Reads aCount bytes from aInputStream into a string.
*
* @param aInputStream
* The input stream to read from.
* @param aCount
* The number of bytes to read from the stream.
* @param aOptions [optional]
* charset
* The character encoding of stream data.
* replacement
* The character to replace unknown byte sequences.
* If unset, it causes an exceptions to be thrown.
*
* @return the bytes from the input stream in string form.
*
* @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream.
* @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would
* block the calling thread (non-blocking mode only).
* @throws NS_ERROR_FAILURE if there are not enough bytes available to read
* aCount amount of data.
* @throws NS_ERROR_ILLEGAL_INPUT if aInputStream has invalid sequences
*/
readInputStreamToString: function NetUtil_readInputStreamToString(aInputStream,
aCount,
aOptions)
{
if (!(aInputStream instanceof Ci.nsIInputStream)) {
let exception = new Components.Exception(
"First argument should be an nsIInputStream",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
if (!aCount) {
let exception = new Components.Exception(
"Non-zero amount of bytes must be specified",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
if (aOptions && "charset" in aOptions) {
let cis = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
try {
// When replacement is set, the character that is unknown sequence
// replaces with aOptions.replacement character.
if (!("replacement" in aOptions)) {
// aOptions.replacement isn't set.
// If input stream has unknown sequences for aOptions.charset,
// throw NS_ERROR_ILLEGAL_INPUT.
aOptions.replacement = 0;
}
cis.init(aInputStream, aOptions.charset, aCount,
aOptions.replacement);
let str = {};
cis.readString(-1, str);
cis.close();
return str.value;
}
catch (e) {
// Adjust the stack so it throws at the caller's location.
throw new Components.Exception(e.message, e.result,
Components.stack.caller, e.data);
}
}
let sis = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
sis.init(aInputStream);
try {
return sis.readBytes(aCount);
}
catch (e) {
// Adjust the stack so it throws at the caller's location.
throw new Components.Exception(e.message, e.result,
Components.stack.caller, e.data);
}
},
/**
* Returns a reference to nsIIOService.
*
* @return a reference to nsIIOService.
*/
get ioService()
{
delete this.ioService;
return this.ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
},
};

View file

@ -0,0 +1,300 @@
/* -*- 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 "NetworkActivityMonitor.h"
#include "prmem.h"
#include "nsIObserverService.h"
#include "nsPISocketTransportService.h"
#include "nsSocketTransportService2.h"
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
#include "prerror.h"
using namespace mozilla::net;
static PRStatus
nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
{
PRStatus ret;
PRErrorCode code;
ret = fd->lower->methods->connect(fd->lower, addr, timeout);
if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR ||
code == PR_IN_PROGRESS_ERROR)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
return ret;
}
static int32_t
nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len)
{
int32_t ret;
ret = fd->lower->methods->read(fd->lower, buf, len);
if (ret >= 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
return ret;
}
static int32_t
nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len)
{
int32_t ret;
ret = fd->lower->methods->write(fd->lower, buf, len);
if (ret > 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
return ret;
}
static int32_t
nsNetMon_Writev(PRFileDesc *fd,
const PRIOVec *iov,
int32_t size,
PRIntervalTime timeout)
{
int32_t ret;
ret = fd->lower->methods->writev(fd->lower, iov, size, timeout);
if (ret > 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
return ret;
}
static int32_t
nsNetMon_Recv(PRFileDesc *fd,
void *buf,
int32_t amount,
int flags,
PRIntervalTime timeout)
{
int32_t ret;
ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout);
if (ret >= 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
return ret;
}
static int32_t
nsNetMon_Send(PRFileDesc *fd,
const void *buf,
int32_t amount,
int flags,
PRIntervalTime timeout)
{
int32_t ret;
ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
if (ret > 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
return ret;
}
static int32_t
nsNetMon_RecvFrom(PRFileDesc *fd,
void *buf,
int32_t amount,
int flags,
PRNetAddr *addr,
PRIntervalTime timeout)
{
int32_t ret;
ret = fd->lower->methods->recvfrom(fd->lower,
buf,
amount,
flags,
addr,
timeout);
if (ret >= 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
return ret;
}
static int32_t
nsNetMon_SendTo(PRFileDesc *fd,
const void *buf,
int32_t amount,
int flags,
const PRNetAddr *addr,
PRIntervalTime timeout)
{
int32_t ret;
ret = fd->lower->methods->sendto(fd->lower,
buf,
amount,
flags,
addr,
timeout);
if (ret > 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
return ret;
}
static int32_t
nsNetMon_AcceptRead(PRFileDesc *listenSock,
PRFileDesc **acceptedSock,
PRNetAddr **peerAddr,
void *buf,
int32_t amount,
PRIntervalTime timeout)
{
int32_t ret;
ret = listenSock->lower->methods->acceptread(listenSock->lower,
acceptedSock,
peerAddr,
buf,
amount,
timeout);
if (ret > 0)
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
return ret;
}
class NotifyNetworkActivity : public mozilla::Runnable {
public:
explicit NotifyNetworkActivity(NetworkActivityMonitor::Direction aDirection)
: mDirection(aDirection)
{}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs)
return NS_ERROR_FAILURE;
obs->NotifyObservers(nullptr,
mDirection == NetworkActivityMonitor::kUpload
? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC
: NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC,
nullptr);
return NS_OK;
}
private:
NetworkActivityMonitor::Direction mDirection;
};
NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr;
static PRDescIdentity sNetActivityMonitorLayerIdentity;
static PRIOMethods sNetActivityMonitorLayerMethods;
static PRIOMethods *sNetActivityMonitorLayerMethodsPtr = nullptr;
NetworkActivityMonitor::NetworkActivityMonitor()
: mBlipInterval(PR_INTERVAL_NO_TIMEOUT)
{
MOZ_COUNT_CTOR(NetworkActivityMonitor);
NS_ASSERTION(gInstance==nullptr,
"multiple NetworkActivityMonitor instances!");
}
NetworkActivityMonitor::~NetworkActivityMonitor()
{
MOZ_COUNT_DTOR(NetworkActivityMonitor);
gInstance = nullptr;
}
nsresult
NetworkActivityMonitor::Init(int32_t blipInterval)
{
nsresult rv;
if (gInstance)
return NS_ERROR_ALREADY_INITIALIZED;
NetworkActivityMonitor * mon = new NetworkActivityMonitor();
rv = mon->Init_Internal(blipInterval);
if (NS_FAILED(rv)) {
delete mon;
return rv;
}
gInstance = mon;
return NS_OK;
}
nsresult
NetworkActivityMonitor::Shutdown()
{
if (!gInstance)
return NS_ERROR_NOT_INITIALIZED;
delete gInstance;
return NS_OK;
}
nsresult
NetworkActivityMonitor::Init_Internal(int32_t blipInterval)
{
if (!sNetActivityMonitorLayerMethodsPtr) {
sNetActivityMonitorLayerIdentity =
PR_GetUniqueIdentity("network activity monitor layer");
sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods();
sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect;
sNetActivityMonitorLayerMethods.read = nsNetMon_Read;
sNetActivityMonitorLayerMethods.write = nsNetMon_Write;
sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev;
sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv;
sNetActivityMonitorLayerMethods.send = nsNetMon_Send;
sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom;
sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo;
sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead;
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
}
mBlipInterval = PR_MillisecondsToInterval(blipInterval);
// Set the last notification times to time that has just expired, so any
// activity even right now will trigger notification.
mLastNotificationTime[kUpload] = PR_IntervalNow() - mBlipInterval;
mLastNotificationTime[kDownload] = mLastNotificationTime[kUpload];
return NS_OK;
}
nsresult
NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd)
{
if (!gInstance)
return NS_OK;
PRFileDesc * layer;
PRStatus status;
layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
sNetActivityMonitorLayerMethodsPtr);
if (!layer) {
return NS_ERROR_FAILURE;
}
status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer);
if (status == PR_FAILURE) {
PR_DELETE(layer);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
NetworkActivityMonitor::DataInOut(Direction direction)
{
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
if (gInstance) {
PRIntervalTime now = PR_IntervalNow();
if ((now - gInstance->mLastNotificationTime[direction]) >
gInstance->mBlipInterval) {
gInstance->mLastNotificationTime[direction] = now;
gInstance->PostNotification(direction);
}
}
return NS_OK;
}
void
NetworkActivityMonitor::PostNotification(Direction direction)
{
nsCOMPtr<nsIRunnable> ev = new NotifyNetworkActivity(direction);
NS_DispatchToMainThread(ev);
}

View file

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NetworkActivityMonitor_h___
#define NetworkActivityMonitor_h___
#include <stdint.h>
#include "nscore.h"
#include "prio.h"
#include "prinrval.h"
namespace mozilla { namespace net {
class NetworkActivityMonitor
{
public:
enum Direction {
kUpload = 0,
kDownload = 1
};
NetworkActivityMonitor();
~NetworkActivityMonitor();
static nsresult Init(int32_t blipInterval);
static nsresult Shutdown();
static nsresult AttachIOLayer(PRFileDesc *fd);
static nsresult DataInOut(Direction direction);
private:
nsresult Init_Internal(int32_t blipInterval);
void PostNotification(Direction direction);
static NetworkActivityMonitor * gInstance;
PRIntervalTime mBlipInterval;
PRIntervalTime mLastNotificationTime[2];
};
} // namespace net
} // namespace mozilla
#endif /* NetworkActivityMonitor_h___ */

View file

@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <netdb.h>
#include "mozilla/DebugOnly.h"
#include "mozilla/ScopeExit.h"
#include "NetworkInfoServiceImpl.h"
namespace mozilla {
namespace net {
static nsresult
ListInterfaceAddresses(int aFd, const char* aIface, AddrMapType& aAddrMap);
nsresult
DoListAddresses(AddrMapType& aAddrMap)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
return NS_ERROR_FAILURE;
}
auto autoCloseSocket = MakeScopeExit([&] {
close(fd);
});
struct ifconf ifconf;
/* 16k of space should be enough to list all interfaces. Worst case, if it's
* not then we will error out and fail to list addresses. This should only
* happen on pathological machines with way too many interfaces.
*/
char buf[16384];
ifconf.ifc_len = sizeof(buf);
ifconf.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifconf) != 0) {
return NS_ERROR_FAILURE;
}
struct ifreq* ifreq = ifconf.ifc_req;
int i = 0;
while (i < ifconf.ifc_len) {
size_t len = IFNAMSIZ + ifreq->ifr_addr.sa_len;
DebugOnly<nsresult> rv =
ListInterfaceAddresses(fd, ifreq->ifr_name, aAddrMap);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ListInterfaceAddresses failed");
ifreq = (struct ifreq*) ((char*)ifreq + len);
i += len;
}
autoCloseSocket.release();
return NS_OK;
}
static nsresult
ListInterfaceAddresses(int aFd, const char* aInterface, AddrMapType& aAddrMap)
{
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(struct ifreq));
strncpy(ifreq.ifr_name, aInterface, IFNAMSIZ - 1);
if (ioctl(aFd, SIOCGIFADDR, &ifreq) != 0) {
return NS_ERROR_FAILURE;
}
char host[128];
int family;
switch(family=ifreq.ifr_addr.sa_family) {
case AF_INET:
case AF_INET6:
getnameinfo(&ifreq.ifr_addr, sizeof(ifreq.ifr_addr), host, sizeof(host), 0, 0, NI_NUMERICHOST);
break;
case AF_UNSPEC:
return NS_OK;
default:
// Unknown family.
return NS_OK;
}
nsCString ifaceStr;
ifaceStr.AssignASCII(aInterface);
nsCString addrStr;
addrStr.AssignASCII(host);
aAddrMap.Put(ifaceStr, addrStr);
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsString.h"
#include "nsDataHashtable.h"
namespace mozilla {
namespace net {
typedef nsDataHashtable<nsCStringHashKey, nsCString> AddrMapType;
nsresult DoListAddresses(AddrMapType& aAddrMap);
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <netdb.h>
#include "mozilla/DebugOnly.h"
#include "mozilla/ScopeExit.h"
#include "NetworkInfoServiceImpl.h"
namespace mozilla {
namespace net {
static nsresult
ListInterfaceAddresses(int aFd, const char* aIface, AddrMapType& aAddrMap);
nsresult
DoListAddresses(AddrMapType& aAddrMap)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
return NS_ERROR_FAILURE;
}
auto autoCloseSocket = MakeScopeExit([&] {
close(fd);
});
struct ifconf ifconf;
/* 16k of space should be enough to list all interfaces. Worst case, if it's
* not then we will error out and fail to list addresses. This should only
* happen on pathological machines with way too many interfaces.
*/
char buf[16384];
ifconf.ifc_len = sizeof(buf);
ifconf.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifconf) != 0) {
return NS_ERROR_FAILURE;
}
struct ifreq* ifreq = ifconf.ifc_req;
int i = 0;
while (i < ifconf.ifc_len) {
size_t len = sizeof(struct ifreq);
DebugOnly<nsresult> rv =
ListInterfaceAddresses(fd, ifreq->ifr_name, aAddrMap);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ListInterfaceAddresses failed");
ifreq = (struct ifreq*) ((char*)ifreq + len);
i += len;
}
autoCloseSocket.release();
return NS_OK;
}
static nsresult
ListInterfaceAddresses(int aFd, const char* aInterface, AddrMapType& aAddrMap)
{
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(struct ifreq));
strncpy(ifreq.ifr_name, aInterface, IFNAMSIZ - 1);
if (ioctl(aFd, SIOCGIFADDR, &ifreq) != 0) {
return NS_ERROR_FAILURE;
}
char host[128];
int family;
switch(family=ifreq.ifr_addr.sa_family) {
case AF_INET:
case AF_INET6:
getnameinfo(&ifreq.ifr_addr, sizeof(ifreq.ifr_addr), host, sizeof(host), 0, 0, NI_NUMERICHOST);
break;
case AF_UNSPEC:
return NS_OK;
default:
// Unknown family.
return NS_OK;
}
nsCString ifaceStr;
ifaceStr.AssignASCII(aInterface);
nsCString addrStr;
addrStr.AssignASCII(host);
aAddrMap.Put(ifaceStr, addrStr);
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <winsock2.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#include "mozilla/UniquePtr.h"
#include "NetworkInfoServiceImpl.h"
namespace mozilla {
namespace net {
nsresult
DoListAddresses(AddrMapType& aAddrMap)
{
UniquePtr<MIB_IPADDRTABLE> ipAddrTable;
DWORD size = sizeof(MIB_IPADDRTABLE);
ipAddrTable.reset((MIB_IPADDRTABLE*) malloc(size));
if (!ipAddrTable) {
return NS_ERROR_FAILURE;
}
DWORD retVal = GetIpAddrTable(ipAddrTable.get(), &size, 0);
if (retVal == ERROR_INSUFFICIENT_BUFFER) {
ipAddrTable.reset((MIB_IPADDRTABLE*) malloc(size));
if (!ipAddrTable) {
return NS_ERROR_FAILURE;
}
retVal = GetIpAddrTable(ipAddrTable.get(), &size, 0);
}
if (retVal != NO_ERROR) {
return NS_ERROR_FAILURE;
}
for (DWORD i = 0; i < ipAddrTable->dwNumEntries; i++) {
int index = ipAddrTable->table[i].dwIndex;
uint32_t addrVal = (uint32_t) ipAddrTable->table[i].dwAddr;
nsCString indexString;
indexString.AppendInt(index, 10);
nsCString addrString;
addrString.AppendPrintf("%d.%d.%d.%d",
(addrVal >> 0) & 0xff, (addrVal >> 8) & 0xff,
(addrVal >> 16) & 0xff, (addrVal >> 24) & 0xff);
aAddrMap.Put(indexString, addrString);
}
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,347 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "nsSocketTransportService2.h"
#include "PollableEvent.h"
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Logging.h"
#include "prerror.h"
#include "prio.h"
#include "private/pprio.h"
#include "prnetdb.h"
#ifdef XP_WIN
#include "ShutdownLayer.h"
#else
#include <fcntl.h>
#define USEPIPE 1
#endif
namespace mozilla {
namespace net {
#ifndef USEPIPE
static PRDescIdentity sPollableEventLayerIdentity;
static PRIOMethods sPollableEventLayerMethods;
static PRIOMethods *sPollableEventLayerMethodsPtr = nullptr;
static void LazyInitSocket()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (sPollableEventLayerMethodsPtr) {
return;
}
sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer");
sPollableEventLayerMethods = *PR_GetDefaultIOMethods();
sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods;
}
static bool NewTCPSocketPair(PRFileDesc *fd[], bool aSetRecvBuff)
{
// this is a replacement for PR_NewTCPSocketPair that manually
// sets the recv buffer to 64K. A windows bug (1248358)
// can result in using an incompatible rwin and window
// scale option on localhost pipes if not set before connect.
SOCKET_LOG(("NewTCPSocketPair %s a recv buffer tuning\n", aSetRecvBuff ? "with" : "without"));
PRFileDesc *listener = nullptr;
PRFileDesc *writer = nullptr;
PRFileDesc *reader = nullptr;
PRSocketOptionData recvBufferOpt;
recvBufferOpt.option = PR_SockOpt_RecvBufferSize;
recvBufferOpt.value.recv_buffer_size = 65535;
PRSocketOptionData nodelayOpt;
nodelayOpt.option = PR_SockOpt_NoDelay;
nodelayOpt.value.no_delay = true;
PRSocketOptionData noblockOpt;
noblockOpt.option = PR_SockOpt_Nonblocking;
noblockOpt.value.non_blocking = true;
listener = PR_OpenTCPSocket(PR_AF_INET);
if (!listener) {
goto failed;
}
if (aSetRecvBuff) {
PR_SetSocketOption(listener, &recvBufferOpt);
}
PR_SetSocketOption(listener, &nodelayOpt);
PRNetAddr listenAddr;
memset(&listenAddr, 0, sizeof(listenAddr));
if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) ||
(PR_Bind(listener, &listenAddr) == PR_FAILURE) ||
(PR_GetSockName(listener, &listenAddr) == PR_FAILURE) || // learn the dynamic port
(PR_Listen(listener, 5) == PR_FAILURE)) {
goto failed;
}
writer = PR_OpenTCPSocket(PR_AF_INET);
if (!writer) {
goto failed;
}
if (aSetRecvBuff) {
PR_SetSocketOption(writer, &recvBufferOpt);
}
PR_SetSocketOption(writer, &nodelayOpt);
PR_SetSocketOption(writer, &noblockOpt);
PRNetAddr writerAddr;
if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port), &writerAddr) == PR_FAILURE) {
goto failed;
}
if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
if ((PR_GetError() != PR_IN_PROGRESS_ERROR) ||
(PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) {
goto failed;
}
}
reader = PR_Accept(listener, &listenAddr, PR_MillisecondsToInterval(200));
if (!reader) {
goto failed;
}
if (aSetRecvBuff) {
PR_SetSocketOption(reader, &recvBufferOpt);
}
PR_SetSocketOption(reader, &nodelayOpt);
PR_SetSocketOption(reader, &noblockOpt);
PR_Close(listener);
fd[0] = reader;
fd[1] = writer;
return true;
failed:
if (listener) {
PR_Close(listener);
}
if (reader) {
PR_Close(reader);
}
if (writer) {
PR_Close(writer);
}
return false;
}
#endif
PollableEvent::PollableEvent()
: mWriteFD(nullptr)
, mReadFD(nullptr)
, mSignaled(false)
{
MOZ_COUNT_CTOR(PollableEvent);
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
// create pair of prfiledesc that can be used as a poll()ble
// signal. on windows use a localhost socket pair, and on
// unix use a pipe.
#ifdef USEPIPE
SOCKET_LOG(("PollableEvent() using pipe\n"));
if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) {
// make the pipe non blocking. NSPR asserts at
// trying to use SockOpt here
PROsfd fd = PR_FileDesc2NativeHandle(mReadFD);
int flags = fcntl(fd, F_GETFL, 0);
(void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
fd = PR_FileDesc2NativeHandle(mWriteFD);
flags = fcntl(fd, F_GETFL, 0);
(void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
} else {
mReadFD = nullptr;
mWriteFD = nullptr;
SOCKET_LOG(("PollableEvent() pipe failed\n"));
}
#else
SOCKET_LOG(("PollableEvent() using socket pair\n"));
PRFileDesc *fd[2];
LazyInitSocket();
// Try with a increased recv buffer first (bug 1248358).
if (NewTCPSocketPair(fd, true)) {
mReadFD = fd[0];
mWriteFD = fd[1];
// If the previous fails try without recv buffer increase (bug 1305436).
} else if (NewTCPSocketPair(fd, false)) {
mReadFD = fd[0];
mWriteFD = fd[1];
// If both fail, try the old version.
} else if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
mReadFD = fd[0];
mWriteFD = fd[1];
PRSocketOptionData socket_opt;
DebugOnly<PRStatus> status;
socket_opt.option = PR_SockOpt_NoDelay;
socket_opt.value.no_delay = true;
PR_SetSocketOption(mWriteFD, &socket_opt);
PR_SetSocketOption(mReadFD, &socket_opt);
socket_opt.option = PR_SockOpt_Nonblocking;
socket_opt.value.non_blocking = true;
status = PR_SetSocketOption(mWriteFD, &socket_opt);
MOZ_ASSERT(status == PR_SUCCESS);
status = PR_SetSocketOption(mReadFD, &socket_opt);
MOZ_ASSERT(status == PR_SUCCESS);
}
if (mReadFD && mWriteFD) {
// compatibility with LSPs such as McAfee that assume a NSPR
// layer for read ala the nspr Pollable Event - Bug 698882. This layer is a nop.
PRFileDesc *topLayer =
PR_CreateIOLayerStub(sPollableEventLayerIdentity,
sPollableEventLayerMethodsPtr);
if (topLayer) {
if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) {
topLayer->dtor(topLayer);
} else {
SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
mReadFD = topLayer;
}
}
} else {
SOCKET_LOG(("PollableEvent() socketpair failed\n"));
}
#endif
if (mReadFD && mWriteFD) {
// prime the system to deal with races invovled in [dc]tor cycle
SOCKET_LOG(("PollableEvent() ctor ok\n"));
mSignaled = true;
PR_Write(mWriteFD, "I", 1);
}
}
PollableEvent::~PollableEvent()
{
MOZ_COUNT_DTOR(PollableEvent);
if (mWriteFD) {
#if defined(XP_WIN)
AttachShutdownLayer(mWriteFD);
#endif
PR_Close(mWriteFD);
}
if (mReadFD) {
#if defined(XP_WIN)
AttachShutdownLayer(mReadFD);
#endif
PR_Close(mReadFD);
}
}
// we do not record signals on the socket thread
// because the socket thread can reliably look at its
// own runnable queue before selecting a poll time
// this is the "service the network without blocking" comment in
// nsSocketTransportService2.cpp
bool
PollableEvent::Signal()
{
SOCKET_LOG(("PollableEvent::Signal\n"));
if (!mWriteFD) {
SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
return false;
}
#ifndef XP_WIN
// On windows poll can hang and this became worse when we introduced the
// patch for bug 698882 (see also bug 1292181), therefore we reverted the
// behavior on windows to be as before bug 698882, e.g. write to the socket
// also if an event dispatch is on the socket thread and writing to the
// socket for each event. See bug 1292181.
if (PR_GetCurrentThread() == gSocketThread) {
SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
return true;
}
#endif
#ifndef XP_WIN
// To wake up the poll writing once is enough, but for Windows that can cause
// hangs so we will write for every event.
// For non-Windows systems it is enough to write just once.
if (mSignaled) {
return true;
}
#endif
mSignaled = true;
int32_t status = PR_Write(mWriteFD, "M", 1);
SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status));
if (status != 1) {
NS_WARNING("PollableEvent::Signal Failed\n");
SOCKET_LOG(("PollableEvent::Signal Failed\n"));
mSignaled = false;
}
return (status == 1);
}
bool
PollableEvent::Clear()
{
// necessary because of the "dont signal on socket thread" optimization
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
SOCKET_LOG(("PollableEvent::Clear\n"));
mSignaled = false;
if (!mReadFD) {
SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
return false;
}
char buf[2048];
int32_t status;
#ifdef XP_WIN
// On Windows we are writing to the socket for each event, to be sure that we
// do not have any deadlock read from the socket as much as we can.
while (true) {
status = PR_Read(mReadFD, buf, 2048);
SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
if (status == 0) {
SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
return false;
}
if (status < 0) {
PRErrorCode code = PR_GetError();
if (code == PR_WOULD_BLOCK_ERROR) {
return true;
} else {
SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
return false;
}
}
}
#else
status = PR_Read(mReadFD, buf, 2048);
SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
if (status == 1) {
return true;
}
if (status == 0) {
SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
return false;
}
if (status > 1) {
MOZ_ASSERT(false);
SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
Clear();
return true;
}
PRErrorCode code = PR_GetError();
if (code == PR_WOULD_BLOCK_ERROR) {
return true;
}
SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
return false;
#endif //XP_WIN
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 PollableEvent_h__
#define PollableEvent_h__
#include "mozilla/Mutex.h"
namespace mozilla {
namespace net {
// class must be called locked
class PollableEvent
{
public:
PollableEvent();
~PollableEvent();
// Signal/Clear return false only if they fail
bool Signal();
bool Clear();
bool Valid() { return mWriteFD && mReadFD; }
PRFileDesc *PollableFD() { return mReadFD; }
private:
PRFileDesc *mWriteFD;
PRFileDesc *mReadFD;
bool mSignaled;
};
} // namespace net
} // namespace mozilla
#endif

2550
netwerk/base/Predictor.cpp Normal file

File diff suppressed because it is too large Load diff

480
netwerk/base/Predictor.h Normal file
View file

@ -0,0 +1,480 @@
/* vim: set ts=2 sts=2 et sw=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 mozilla_net_Predictor_h
#define mozilla_net_Predictor_h
#include "nsINetworkPredictor.h"
#include "nsINetworkPredictorVerifier.h"
#include "nsCOMPtr.h"
#include "nsICacheEntry.h"
#include "nsICacheEntryOpenCallback.h"
#include "nsICacheStorageVisitor.h"
#include "nsIDNSListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsIObserver.h"
#include "nsISpeculativeConnect.h"
#include "nsIStreamListener.h"
#include "mozilla/RefPtr.h"
#include "nsString.h"
#include "nsTArray.h"
#include "mozilla/TimeStamp.h"
class nsICacheStorage;
class nsIDNSService;
class nsIIOService;
class nsILoadContextInfo;
class nsITimer;
namespace mozilla {
namespace net {
class nsHttpRequestHead;
class nsHttpResponseHead;
class Predictor : public nsINetworkPredictor
, public nsIObserver
, public nsISpeculativeConnectionOverrider
, public nsIInterfaceRequestor
, public nsICacheEntryMetaDataVisitor
, public nsINetworkPredictorVerifier
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETWORKPREDICTOR
NS_DECL_NSIOBSERVER
NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICACHEENTRYMETADATAVISITOR
NS_DECL_NSINETWORKPREDICTORVERIFIER
Predictor();
nsresult Init();
void Shutdown();
static nsresult Create(nsISupports *outer, const nsIID& iid, void **result);
// Used to update whether a particular URI was cacheable or not.
// sourceURI and targetURI are the same as the arguments to Learn
// and httpStatus is the status code we got while loading targetURI.
static void UpdateCacheability(nsIURI *sourceURI, nsIURI *targetURI,
uint32_t httpStatus,
nsHttpRequestHead &requestHead,
nsHttpResponseHead *reqponseHead,
nsILoadContextInfo *lci);
private:
virtual ~Predictor();
// Stores callbacks for a child process predictor (for test purposes)
nsCOMPtr<nsINetworkPredictorVerifier> mChildVerifier;
union Reason {
PredictorLearnReason mLearn;
PredictorPredictReason mPredict;
};
class DNSListener : public nsIDNSListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDNSLISTENER
DNSListener()
{ }
private:
virtual ~DNSListener()
{ }
};
class Action : public nsICacheEntryOpenCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICACHEENTRYOPENCALLBACK
Action(bool fullUri, bool predict, Reason reason,
nsIURI *targetURI, nsIURI *sourceURI,
nsINetworkPredictorVerifier *verifier, Predictor *predictor);
Action(bool fullUri, bool predict, Reason reason,
nsIURI *targetURI, nsIURI *sourceURI,
nsINetworkPredictorVerifier *verifier, Predictor *predictor,
uint8_t stackCount);
static const bool IS_FULL_URI = true;
static const bool IS_ORIGIN = false;
static const bool DO_PREDICT = true;
static const bool DO_LEARN = false;
private:
virtual ~Action();
bool mFullUri : 1;
bool mPredict : 1;
union {
PredictorPredictReason mPredictReason;
PredictorLearnReason mLearnReason;
};
nsCOMPtr<nsIURI> mTargetURI;
nsCOMPtr<nsIURI> mSourceURI;
nsCOMPtr<nsINetworkPredictorVerifier> mVerifier;
TimeStamp mStartTime;
uint8_t mStackCount;
RefPtr<Predictor> mPredictor;
};
class CacheabilityAction : public nsICacheEntryOpenCallback
, public nsICacheEntryMetaDataVisitor
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICACHEENTRYOPENCALLBACK
NS_DECL_NSICACHEENTRYMETADATAVISITOR
CacheabilityAction(nsIURI *targetURI, uint32_t httpStatus,
const nsCString &method, Predictor *predictor)
:mTargetURI(targetURI)
,mHttpStatus(httpStatus)
,mMethod(method)
,mPredictor(predictor)
{ }
private:
virtual ~CacheabilityAction() { }
nsCOMPtr<nsIURI> mTargetURI;
uint32_t mHttpStatus;
nsCString mMethod;
RefPtr<Predictor> mPredictor;
nsTArray<nsCString> mKeysToCheck;
nsTArray<nsCString> mValuesToCheck;
};
class Resetter : public nsICacheEntryOpenCallback,
public nsICacheEntryMetaDataVisitor,
public nsICacheStorageVisitor
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICACHEENTRYOPENCALLBACK
NS_DECL_NSICACHEENTRYMETADATAVISITOR
NS_DECL_NSICACHESTORAGEVISITOR
explicit Resetter(Predictor *predictor);
private:
virtual ~Resetter() { }
void Complete();
uint32_t mEntriesToVisit;
nsTArray<nsCString> mKeysToDelete;
RefPtr<Predictor> mPredictor;
nsTArray<nsCOMPtr<nsIURI>> mURIsToVisit;
};
class SpaceCleaner : public nsICacheEntryMetaDataVisitor
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICACHEENTRYMETADATAVISITOR
explicit SpaceCleaner(Predictor *predictor)
:mLRUStamp(0)
,mLRUKeyToDelete(nullptr)
,mPredictor(predictor)
{ }
void Finalize(nsICacheEntry *entry);
private:
virtual ~SpaceCleaner() { }
uint32_t mLRUStamp;
const char *mLRUKeyToDelete;
nsTArray<nsCString> mLongKeysToDelete;
RefPtr<Predictor> mPredictor;
};
class PrefetchListener : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
PrefetchListener(nsINetworkPredictorVerifier *verifier, nsIURI *uri,
Predictor *predictor)
:mVerifier(verifier)
,mURI(uri)
,mPredictor(predictor)
{ }
private:
virtual ~PrefetchListener() { }
nsCOMPtr<nsINetworkPredictorVerifier> mVerifier;
nsCOMPtr<nsIURI> mURI;
RefPtr<Predictor> mPredictor;
TimeStamp mStartTime;
};
// Observer-related stuff
nsresult InstallObserver();
void RemoveObserver();
// Service startup utilities
void MaybeCleanupOldDBFiles();
// The guts of prediction
// This is the top-level driver for doing any prediction that needs
// information from the cache. Returns true if any predictions were queued up
// * reason - What kind of prediction this is/why this prediction is
// happening (pageload, startup)
// * entry - the cache entry with the information we need
// * isNew - whether or not the cache entry is brand new and empty
// * fullUri - whether we are doing predictions based on a full page URI, or
// just the origin of the page
// * targetURI - the URI that we are predicting based upon - IOW, the URI
// that is being loaded or being redirected to
// * verifier - used for testing to verify the expected predictions happen
// * stackCount - used to ensure we don't recurse too far trying to find the
// final redirection in a redirect chain
bool PredictInternal(PredictorPredictReason reason, nsICacheEntry *entry,
bool isNew, bool fullUri, nsIURI *targetURI,
nsINetworkPredictorVerifier *verifier,
uint8_t stackCount);
// Used when predicting because the user's mouse hovered over a link
// * targetURI - the URI target of the link
// * sourceURI - the URI of the page on which the link appears
// * verifier - used for testing to verify the expected predictions happen
void PredictForLink(nsIURI *targetURI,
nsIURI *sourceURI,
nsINetworkPredictorVerifier *verifier);
// Used when predicting because a page is being loaded (which may include
// being the target of a redirect). All arguments are the same as for
// PredictInternal. Returns true if any predictions were queued up.
bool PredictForPageload(nsICacheEntry *entry,
nsIURI *targetURI,
uint8_t stackCount,
bool fullUri,
nsINetworkPredictorVerifier *verifier);
// Used when predicting pages that will be used near browser startup. All
// arguments are the same as for PredictInternal. Returns true if any
// predictions were queued up.
bool PredictForStartup(nsICacheEntry *entry,
bool fullUri,
nsINetworkPredictorVerifier *verifier);
// Utilities related to prediction
// Used to update our rolling load count (how many of the last n loads was a
// partular resource loaded on?)
// * entry - cache entry of page we're loading
// * flags - value that contains our rolling count as the top 20 bits (but
// we may use fewer than those 20 bits for calculations)
// * key - metadata key that we will update on entry
// * hitCount - part of the metadata we need to preserve
// * lastHit - part of the metadata we need to preserve
void UpdateRollingLoadCount(nsICacheEntry *entry, const uint32_t flags,
const char *key, const uint32_t hitCount,
const uint32_t lastHit);
// Used to calculate how much to degrade our confidence for all resources
// on a particular page, because of how long ago the most recent load of that
// page was. Returns a value between 0 (very recent most recent load) and 100
// (very distant most recent load)
// * lastLoad - time stamp of most recent load of a page
int32_t CalculateGlobalDegradation(uint32_t lastLoad);
// Used to calculate how confident we are that a particular resource will be
// used. Returns a value between 0 (no confidence) and 100 (very confident)
// * hitCount - number of times this resource has been seen when loading
// this page
// * hitsPossible - number of times this page has been loaded
// * lastHit - timestamp of the last time this resource was seen when
// loading this page
// * lastPossible - timestamp of the last time this page was loaded
// * globalDegradation - value calculated by CalculateGlobalDegradation for
// this page
int32_t CalculateConfidence(uint32_t hitCount, uint32_t hitsPossible,
uint32_t lastHit, uint32_t lastPossible,
int32_t globalDegradation);
// Used to calculate all confidence values for all resources associated with a
// page.
// * entry - the cache entry with all necessary information about this page
// * referrer - the URI that we are loading (may be null)
// * lastLoad - timestamp of the last time this page was loaded
// * loadCount - number of times this page has been loaded
// * gloablDegradation - value calculated by CalculateGlobalDegradation for
// this page
// * fullUri - whether we're predicting for a full URI or origin-only
void CalculatePredictions(nsICacheEntry *entry, nsIURI *referrer,
uint32_t lastLoad, uint32_t loadCount,
int32_t globalDegradation, bool fullUri);
// Used to prepare any necessary prediction for a resource on a page
// * confidence - value calculated by CalculateConfidence for this resource
// * flags - the flags taken from the resource
// * uri - the URI of the resource
void SetupPrediction(int32_t confidence, uint32_t flags, nsIURI *uri);
// Used to kick off a prefetch from RunPredictions if necessary
// * uri - the URI to prefetch
// * referrer - the URI of the referring page
// * verifier - used for testing to ensure the expected prefetch happens
nsresult Prefetch(nsIURI *uri, nsIURI *referrer, nsINetworkPredictorVerifier *verifier);
// Used to actually perform any predictions set up via SetupPrediction.
// Returns true if any predictions were performed.
// * referrer - the URI we are predicting from
// * verifier - used for testing to ensure the expected predictions happen
bool RunPredictions(nsIURI *referrer, nsINetworkPredictorVerifier *verifier);
// Used to guess whether a page will redirect to another page or not. Returns
// true if a redirection is likely.
// * entry - cache entry with all necessary information about this page
// * loadCount - number of times this page has been loaded
// * lastLoad - timestamp of the last time this page was loaded
// * globalDegradation - value calculated by CalculateGlobalDegradation for
// this page
// * redirectURI - if this returns true, the URI that is likely to be
// redirected to, otherwise null
bool WouldRedirect(nsICacheEntry *entry, uint32_t loadCount,
uint32_t lastLoad, int32_t globalDegradation,
nsIURI **redirectURI);
// The guts of learning information
// This is the top-level driver for doing any updating of our information in
// the cache
// * reason - why this learn is happening (pageload, startup, redirect)
// * entry - the cache entry with the information we need
// * isNew - whether or not the cache entry is brand new and empty
// * fullUri - whether we are doing predictions based on a full page URI, or
// just the origin of the page
// * targetURI - the URI that we are adding to our data - most often a
// resource loaded by a page the user navigated to
// * sourceURI - the URI that caused targetURI to be loaded, usually the
// page the user navigated to
void LearnInternal(PredictorLearnReason reason, nsICacheEntry *entry,
bool isNew, bool fullUri, nsIURI *targetURI,
nsIURI *sourceURI);
// Used when learning about a resource loaded by a page
// * entry - the cache entry with information that needs updating
// * targetURI - the URI of the resource that was loaded by the page
void LearnForSubresource(nsICacheEntry *entry, nsIURI *targetURI);
// Used when learning about a redirect from one page to another
// * entry - the cache entry of the page that was redirected from
// * targetURI - the URI of the redirect target
void LearnForRedirect(nsICacheEntry *entry, nsIURI *targetURI);
// Used to learn about pages loaded close to browser startup. This results in
// LearnForStartup being called if we are, in fact, near browser startup
// * uri - the URI of a page that has been loaded (may not have been near
// browser startup)
// * fullUri - true if this is a full page uri, false if it's an origin
void MaybeLearnForStartup(nsIURI *uri, bool fullUri);
// Used in conjunction with MaybeLearnForStartup to learn about pages loaded
// close to browser startup
// * entry - the cache entry that stores the startup page list
// * targetURI - the URI of a page that was loaded near browser startup
void LearnForStartup(nsICacheEntry *entry, nsIURI *targetURI);
// Used to parse the data we store in cache metadata
// * key - the cache metadata key
// * value - the cache metadata value
// * uri - (out) the URI this metadata entry was about
// * hitCount - (out) the number of times this URI has been seen
// * lastHit - (out) timestamp of the last time this URI was seen
// * flags - (out) flags for this metadata entry
bool ParseMetaDataEntry(const char *key, const char *value, nsIURI **uri,
uint32_t &hitCount, uint32_t &lastHit,
uint32_t &flags);
// Used to update whether a particular URI was cacheable or not.
// sourceURI and targetURI are the same as the arguments to Learn
// and httpStatus is the status code we got while loading targetURI.
void UpdateCacheabilityInternal(nsIURI *sourceURI, nsIURI *targetURI,
uint32_t httpStatus, const nsCString &method);
// Make sure our prefs are in their expected range of values
void SanitizePrefs();
// Our state
bool mInitialized;
bool mEnabled;
bool mEnableHoverOnSSL;
bool mEnablePrefetch;
int32_t mPageDegradationDay;
int32_t mPageDegradationWeek;
int32_t mPageDegradationMonth;
int32_t mPageDegradationYear;
int32_t mPageDegradationMax;
int32_t mSubresourceDegradationDay;
int32_t mSubresourceDegradationWeek;
int32_t mSubresourceDegradationMonth;
int32_t mSubresourceDegradationYear;
int32_t mSubresourceDegradationMax;
int32_t mPrefetchRollingLoadCount;
int32_t mPrefetchMinConfidence;
int32_t mPreconnectMinConfidence;
int32_t mPreresolveMinConfidence;
int32_t mRedirectLikelyConfidence;
int32_t mPrefetchForceValidFor;
int32_t mMaxResourcesPerEntry;
bool mCleanedUp;
nsCOMPtr<nsITimer> mCleanupTimer;
nsTArray<nsCString> mKeysToOperateOn;
nsTArray<nsCString> mValuesToOperateOn;
nsCOMPtr<nsICacheStorage> mCacheDiskStorage;
nsCOMPtr<nsIIOService> mIOService;
nsCOMPtr<nsISpeculativeConnect> mSpeculativeService;
nsCOMPtr<nsIURI> mStartupURI;
uint32_t mStartupTime;
uint32_t mLastStartupTime;
int32_t mStartupCount;
uint32_t mMaxURILength;
nsCOMPtr<nsIDNSService> mDnsService;
RefPtr<DNSListener> mDNSListener;
nsTArray<nsCOMPtr<nsIURI>> mPrefetches;
nsTArray<nsCOMPtr<nsIURI>> mPreconnects;
nsTArray<nsCOMPtr<nsIURI>> mPreresolves;
bool mDoingTests;
static Predictor *sSelf;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_Predictor_h

View file

@ -0,0 +1,132 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sts=4 sw=4 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/. */
#ifndef mozilla_net_PrivateBrowsingChannel_h__
#define mozilla_net_PrivateBrowsingChannel_h__
#include "nsIPrivateBrowsingChannel.h"
#include "nsCOMPtr.h"
#include "nsILoadGroup.h"
#include "nsILoadContext.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIInterfaceRequestor.h"
#include "nsNetUtil.h"
#include "mozilla/Unused.h"
namespace mozilla {
namespace net {
template <class Channel>
class PrivateBrowsingChannel : public nsIPrivateBrowsingChannel
{
public:
PrivateBrowsingChannel() :
mPrivateBrowsingOverriden(false),
mPrivateBrowsing(false)
{
}
NS_IMETHOD SetPrivate(bool aPrivate)
{
// Make sure that we don't have a load context
// This is a fatal error in debug builds, and a runtime error in release
// builds.
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(static_cast<Channel*>(this), loadContext);
MOZ_ASSERT(!loadContext);
if (loadContext) {
return NS_ERROR_FAILURE;
}
mPrivateBrowsingOverriden = true;
mPrivateBrowsing = aPrivate;
return NS_OK;
}
NS_IMETHOD GetIsChannelPrivate(bool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = mPrivateBrowsing;
return NS_OK;
}
NS_IMETHOD IsPrivateModeOverriden(bool* aValue, bool *aResult)
{
NS_ENSURE_ARG_POINTER(aValue);
NS_ENSURE_ARG_POINTER(aResult);
*aResult = mPrivateBrowsingOverriden;
if (mPrivateBrowsingOverriden) {
*aValue = mPrivateBrowsing;
}
return NS_OK;
}
// Must be called every time the channel's callbacks or loadGroup is updated
void UpdatePrivateBrowsing()
{
// once marked as private we never go un-private
if (mPrivateBrowsing) {
return;
}
auto channel = static_cast<Channel*>(this);
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(channel, loadContext);
if (loadContext) {
mPrivateBrowsing = loadContext->UsePrivateBrowsing();
return;
}
nsCOMPtr<nsILoadInfo> loadInfo;
Unused << channel->GetLoadInfo(getter_AddRefs(loadInfo));
if (loadInfo) {
NeckoOriginAttributes attrs = loadInfo->GetOriginAttributes();
mPrivateBrowsing = attrs.mPrivateBrowsingId > 0;
}
}
bool CanSetCallbacks(nsIInterfaceRequestor* aCallbacks) const
{
// Make sure that the private bit override flag is not set.
// This is a fatal error in debug builds, and a runtime error in release
// builds.
if (!aCallbacks) {
return true;
}
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(aCallbacks);
if (!loadContext) {
return true;
}
MOZ_ASSERT(!mPrivateBrowsingOverriden);
return !mPrivateBrowsingOverriden;
}
bool CanSetLoadGroup(nsILoadGroup* aLoadGroup) const
{
// Make sure that the private bit override flag is not set.
// This is a fatal error in debug builds, and a runtime error in release
// builds.
if (!aLoadGroup) {
return true;
}
nsCOMPtr<nsIInterfaceRequestor> callbacks;
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
// From this point on, we just hand off the work to CanSetCallbacks,
// because the logic is exactly the same.
return CanSetCallbacks(callbacks);
}
protected:
bool mPrivateBrowsingOverriden;
bool mPrivateBrowsing;
};
} // namespace net
} // namespace mozilla
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 ProxyAutoConfig_h__
#define ProxyAutoConfig_h__
#include "nsString.h"
#include "nsCOMPtr.h"
class nsITimer;
namespace JS {
class CallArgs;
} // namespace JS
namespace mozilla { namespace net {
class JSContextWrapper;
union NetAddr;
// The ProxyAutoConfig class is meant to be created and run on a
// non main thread. It synchronously resolves PAC files by blocking that
// thread and running nested event loops. GetProxyForURI is not re-entrant.
class ProxyAutoConfig {
public:
ProxyAutoConfig();
~ProxyAutoConfig();
nsresult Init(const nsCString &aPACURI,
const nsCString &aPACScript,
bool aIncludePath);
void SetThreadLocalIndex(uint32_t index);
void Shutdown();
void GC();
bool MyIPAddress(const JS::CallArgs &aArgs);
bool ResolveAddress(const nsCString &aHostName,
NetAddr *aNetAddr, unsigned int aTimeout);
/**
* Get the proxy string for the specified URI. The proxy string is
* given by the following:
*
* result = proxy-spec *( proxy-sep proxy-spec )
* proxy-spec = direct-type | proxy-type LWS proxy-host [":" proxy-port]
* direct-type = "DIRECT"
* proxy-type = "PROXY" | "HTTP" | "HTTPS" | "SOCKS" | "SOCKS4" | "SOCKS5"
* proxy-sep = ";" LWS
* proxy-host = hostname | ipv4-address-literal
* proxy-port = <any 16-bit unsigned integer>
* LWS = *( SP | HT )
* SP = <US-ASCII SP, space (32)>
* HT = <US-ASCII HT, horizontal-tab (9)>
*
* NOTE: direct-type and proxy-type are case insensitive
* NOTE: SOCKS implies SOCKS4
*
* Examples:
* "PROXY proxy1.foo.com:8080; PROXY proxy2.foo.com:8080; DIRECT"
* "SOCKS socksproxy"
* "DIRECT"
*
* XXX add support for IPv6 address literals.
* XXX quote whatever the official standard is for PAC.
*
* @param aTestURI
* The URI as an ASCII string to test.
* @param aTestHost
* The ASCII hostname to test.
*
* @param result
* result string as defined above.
*/
nsresult GetProxyForURI(const nsCString &aTestURI,
const nsCString &aTestHost,
nsACString &result);
private:
// allow 665ms for myipaddress dns queries. That's 95th percentile.
const static unsigned int kTimeout = 665;
// used to compile the PAC file and setup the execution context
nsresult SetupJS();
bool SrcAddress(const NetAddr *remoteAddress, nsCString &localAddress);
bool MyIPAddressTryHost(const nsCString &hostName, unsigned int timeout,
const JS::CallArgs &aArgs, bool* aResult);
JSContextWrapper *mJSContext;
bool mJSNeedsSetup;
bool mShutdown;
nsCString mPACScript;
nsCString mPACURI;
bool mIncludePath;
nsCString mRunningHost;
nsCOMPtr<nsITimer> mTimer;
};
} // namespace net
} // namespace mozilla
#endif // ProxyAutoConfig_h__

View file

@ -0,0 +1,87 @@
/* 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 "RedirectChannelRegistrar.h"
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(RedirectChannelRegistrar, nsIRedirectChannelRegistrar)
RedirectChannelRegistrar::RedirectChannelRegistrar()
: mRealChannels(32)
, mParentChannels(32)
, mId(1)
, mLock("RedirectChannelRegistrar")
{
}
NS_IMETHODIMP
RedirectChannelRegistrar::RegisterChannel(nsIChannel *channel,
uint32_t *_retval)
{
MutexAutoLock lock(mLock);
mRealChannels.Put(mId, channel);
*_retval = mId;
++mId;
// Ensure we always provide positive ids
if (!mId)
mId = 1;
return NS_OK;
}
NS_IMETHODIMP
RedirectChannelRegistrar::GetRegisteredChannel(uint32_t id,
nsIChannel **_retval)
{
MutexAutoLock lock(mLock);
if (!mRealChannels.Get(id, _retval))
return NS_ERROR_NOT_AVAILABLE;
return NS_OK;
}
NS_IMETHODIMP
RedirectChannelRegistrar::LinkChannels(uint32_t id,
nsIParentChannel *channel,
nsIChannel** _retval)
{
MutexAutoLock lock(mLock);
if (!mRealChannels.Get(id, _retval))
return NS_ERROR_NOT_AVAILABLE;
mParentChannels.Put(id, channel);
return NS_OK;
}
NS_IMETHODIMP
RedirectChannelRegistrar::GetParentChannel(uint32_t id,
nsIParentChannel **_retval)
{
MutexAutoLock lock(mLock);
if (!mParentChannels.Get(id, _retval))
return NS_ERROR_NOT_AVAILABLE;
return NS_OK;
}
NS_IMETHODIMP
RedirectChannelRegistrar::DeregisterChannels(uint32_t id)
{
MutexAutoLock lock(mLock);
mRealChannels.Remove(id);
mParentChannels.Remove(id);
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,44 @@
/* 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 RedirectChannelRegistrar_h__
#define RedirectChannelRegistrar_h__
#include "nsIRedirectChannelRegistrar.h"
#include "nsIChannel.h"
#include "nsIParentChannel.h"
#include "nsInterfaceHashtable.h"
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
namespace mozilla {
namespace net {
class RedirectChannelRegistrar final : public nsIRedirectChannelRegistrar
{
NS_DECL_ISUPPORTS
NS_DECL_NSIREDIRECTCHANNELREGISTRAR
RedirectChannelRegistrar();
private:
~RedirectChannelRegistrar() {}
protected:
typedef nsInterfaceHashtable<nsUint32HashKey, nsIChannel>
ChannelHashtable;
typedef nsInterfaceHashtable<nsUint32HashKey, nsIParentChannel>
ParentChannelHashtable;
ChannelHashtable mRealChannels;
ParentChannelHashtable mParentChannels;
uint32_t mId;
Mutex mLock;
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,186 @@
/* 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 ReferrerPolicy_h__
#define ReferrerPolicy_h__
#include "nsStringGlue.h"
#include "nsIHttpChannel.h"
#include "nsUnicharUtils.h"
namespace mozilla { namespace net {
enum ReferrerPolicy {
/* spec tokens: never no-referrer */
RP_No_Referrer = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER,
/* spec tokens: origin */
RP_Origin = nsIHttpChannel::REFERRER_POLICY_ORIGIN,
/* spec tokens: default no-referrer-when-downgrade */
RP_No_Referrer_When_Downgrade = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
RP_Default = nsIHttpChannel::REFERRER_POLICY_DEFAULT,
/* spec tokens: origin-when-cross-origin */
RP_Origin_When_Crossorigin = nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
/* spec tokens: always unsafe-url */
RP_Unsafe_URL = nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL,
/* spec tokens: same-origin */
RP_Same_Origin = nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN,
/* spec tokens: strict-origin */
RP_Strict_Origin = nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN,
/* spec tokens: strict-origin-when-cross-origin */
RP_Strict_Origin_When_Cross_Origin = nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
/* spec tokens: empty string */
/* The empty string "" corresponds to no referrer policy, or unset policy */
RP_Unset = nsIHttpChannel::REFERRER_POLICY_UNSET,
};
/* spec tokens: never no-referrer */
const char kRPS_Never[] = "never";
const char kRPS_No_Referrer[] = "no-referrer";
/* spec tokens: origin */
const char kRPS_Origin[] = "origin";
/* spec tokens: default no-referrer-when-downgrade */
const char kRPS_Default[] = "default";
const char kRPS_No_Referrer_When_Downgrade[] = "no-referrer-when-downgrade";
/* spec tokens: origin-when-cross-origin */
const char kRPS_Origin_When_Cross_Origin[] = "origin-when-cross-origin";
const char kRPS_Origin_When_Crossorigin[] = "origin-when-crossorigin";
/* spec tokens: same-origin */
const char kRPS_Same_Origin[] = "same-origin";
/* spec tokens: strict-origin */
const char kRPS_Strict_Origin[] = "strict-origin";
/* spec tokens: strict-origin-when-cross-origin */
const char kRPS_Strict_Origin_When_Cross_Origin[] = "strict-origin-when-cross-origin";
/* spec tokens: always unsafe-url */
const char kRPS_Always[] = "always";
const char kRPS_Unsafe_URL[] = "unsafe-url";
inline ReferrerPolicy
ReferrerPolicyFromString(const nsAString& content)
{
if (content.IsEmpty()) {
return RP_No_Referrer;
}
nsString lowerContent(content);
ToLowerCase(lowerContent);
// This is implemented step by step as described in the Referrer Policy
// specification, section "Determine token's Policy".
if (lowerContent.EqualsLiteral(kRPS_Never) ||
lowerContent.EqualsLiteral(kRPS_No_Referrer)) {
return RP_No_Referrer;
}
if (lowerContent.EqualsLiteral(kRPS_Origin)) {
return RP_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_Default) ||
lowerContent.EqualsLiteral(kRPS_No_Referrer_When_Downgrade)) {
return RP_No_Referrer_When_Downgrade;
}
if (lowerContent.EqualsLiteral(kRPS_Origin_When_Cross_Origin) ||
lowerContent.EqualsLiteral(kRPS_Origin_When_Crossorigin)) {
return RP_Origin_When_Crossorigin;
}
if (lowerContent.EqualsLiteral(kRPS_Same_Origin)) {
return RP_Same_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_Strict_Origin)) {
return RP_Strict_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_Strict_Origin_When_Cross_Origin)) {
return RP_Strict_Origin_When_Cross_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_Always) ||
lowerContent.EqualsLiteral(kRPS_Unsafe_URL)) {
return RP_Unsafe_URL;
}
// Spec says if none of the previous match, use empty string.
return RP_Unset;
}
inline bool
IsValidReferrerPolicy(const nsAString& content)
{
if (content.IsEmpty()) {
return true;
}
nsString lowerContent(content);
ToLowerCase(lowerContent);
return lowerContent.EqualsLiteral(kRPS_Never)
|| lowerContent.EqualsLiteral(kRPS_No_Referrer)
|| lowerContent.EqualsLiteral(kRPS_Origin)
|| lowerContent.EqualsLiteral(kRPS_Default)
|| lowerContent.EqualsLiteral(kRPS_No_Referrer_When_Downgrade)
|| lowerContent.EqualsLiteral(kRPS_Origin_When_Cross_Origin)
|| lowerContent.EqualsLiteral(kRPS_Origin_When_Crossorigin)
|| lowerContent.EqualsLiteral(kRPS_Same_Origin)
|| lowerContent.EqualsLiteral(kRPS_Strict_Origin)
|| lowerContent.EqualsLiteral(kRPS_Strict_Origin_When_Cross_Origin)
|| lowerContent.EqualsLiteral(kRPS_Always)
|| lowerContent.EqualsLiteral(kRPS_Unsafe_URL);
}
inline ReferrerPolicy
AttributeReferrerPolicyFromString(const nsAString& content)
{
// Specs : https://html.spec.whatwg.org/multipage/infrastructure.html#referrer-policy-attribute
// Spec says the empty string "" corresponds to no referrer policy, or RP_Unset
if (content.IsEmpty()) {
return RP_Unset;
}
nsString lowerContent(content);
ToLowerCase(lowerContent);
if (lowerContent.EqualsLiteral(kRPS_No_Referrer)) {
return RP_No_Referrer;
}
if (lowerContent.EqualsLiteral(kRPS_Origin)) {
return RP_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_No_Referrer_When_Downgrade)) {
return RP_No_Referrer_When_Downgrade;
}
if (lowerContent.EqualsLiteral(kRPS_Origin_When_Cross_Origin)) {
return RP_Origin_When_Crossorigin;
}
if (lowerContent.EqualsLiteral(kRPS_Unsafe_URL)) {
return RP_Unsafe_URL;
}
if (lowerContent.EqualsLiteral(kRPS_Strict_Origin)) {
return RP_Strict_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_Same_Origin)) {
return RP_Same_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_Strict_Origin_When_Cross_Origin)) {
return RP_Strict_Origin_When_Cross_Origin;
}
// Spec says invalid value default is empty string state
// So, return RP_Unset if none of the previous match, return RP_Unset
return RP_Unset;
}
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,217 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ;*; */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAutoPtr.h"
#include "nsIObserverService.h"
#include "nsIUUIDGenerator.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "RequestContextService.h"
#include "mozilla/Atomics.h"
#include "mozilla/Services.h"
#include "mozilla/net/PSpdyPush.h"
namespace mozilla {
namespace net {
// nsIRequestContext
class RequestContext final : public nsIRequestContext
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTCONTEXT
explicit RequestContext(const nsID& id);
private:
virtual ~RequestContext();
nsID mID;
char mCID[NSID_LENGTH];
Atomic<uint32_t> mBlockingTransactionCount;
nsAutoPtr<SpdyPushCache> mSpdyCache;
nsCString mUserAgentOverride;
};
NS_IMPL_ISUPPORTS(RequestContext, nsIRequestContext)
RequestContext::RequestContext(const nsID& aID)
: mBlockingTransactionCount(0)
{
mID = aID;
mID.ToProvidedString(mCID);
}
RequestContext::~RequestContext()
{
}
NS_IMETHODIMP
RequestContext::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount)
{
NS_ENSURE_ARG_POINTER(aBlockingTransactionCount);
*aBlockingTransactionCount = mBlockingTransactionCount;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::AddBlockingTransaction()
{
mBlockingTransactionCount++;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::RemoveBlockingTransaction(uint32_t *outval)
{
NS_ENSURE_ARG_POINTER(outval);
mBlockingTransactionCount--;
*outval = mBlockingTransactionCount;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache)
{
*aSpdyPushCache = mSpdyCache.get();
return NS_OK;
}
NS_IMETHODIMP
RequestContext::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache)
{
mSpdyCache = aSpdyPushCache;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::GetID(nsID *outval)
{
NS_ENSURE_ARG_POINTER(outval);
*outval = mID;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::GetUserAgentOverride(nsACString& aUserAgentOverride)
{
aUserAgentOverride = mUserAgentOverride;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::SetUserAgentOverride(const nsACString& aUserAgentOverride)
{
mUserAgentOverride = aUserAgentOverride;
return NS_OK;
}
//nsIRequestContextService
RequestContextService *RequestContextService::sSelf = nullptr;
NS_IMPL_ISUPPORTS(RequestContextService, nsIRequestContextService, nsIObserver)
RequestContextService::RequestContextService()
{
MOZ_ASSERT(!sSelf, "multiple rcs instances!");
MOZ_ASSERT(NS_IsMainThread());
sSelf = this;
}
RequestContextService::~RequestContextService()
{
MOZ_ASSERT(NS_IsMainThread());
Shutdown();
sSelf = nullptr;
}
nsresult
RequestContextService::Init()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_NOT_AVAILABLE;
}
return obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
void
RequestContextService::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
mTable.Clear();
}
/* static */ nsresult
RequestContextService::Create(nsISupports *aOuter, const nsIID& aIID, void **aResult)
{
MOZ_ASSERT(NS_IsMainThread());
if (aOuter != nullptr) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<RequestContextService> svc = new RequestContextService();
nsresult rv = svc->Init();
NS_ENSURE_SUCCESS(rv, rv);
return svc->QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
RequestContextService::GetRequestContext(const nsID& rcID, nsIRequestContext **rc)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG_POINTER(rc);
*rc = nullptr;
if (!mTable.Get(rcID, rc)) {
nsCOMPtr<nsIRequestContext> newSC = new RequestContext(rcID);
mTable.Put(rcID, newSC);
newSC.swap(*rc);
}
return NS_OK;
}
NS_IMETHODIMP
RequestContextService::NewRequestContextID(nsID *rcID)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mUUIDGen) {
nsresult rv;
mUUIDGen = do_GetService("@mozilla.org/uuid-generator;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
return mUUIDGen->GenerateUUIDInPlace(rcID);
}
NS_IMETHODIMP
RequestContextService::RemoveRequestContext(const nsID& rcID)
{
MOZ_ASSERT(NS_IsMainThread());
mTable.Remove(rcID);
return NS_OK;
}
NS_IMETHODIMP
RequestContextService::Observe(nsISupports *subject, const char *topic,
const char16_t *data_unicode)
{
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
Shutdown();
}
return NS_OK;
}
} // ::mozilla::net
} // ::mozilla

View file

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ;*; */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla__net__RequestContextService_h
#define mozilla__net__RequestContextService_h
#include "nsCOMPtr.h"
#include "nsInterfaceHashtable.h"
#include "nsIObserver.h"
#include "nsIRequestContext.h"
class nsIUUIDGenerator;
namespace mozilla {
namespace net {
class RequestContextService final
: public nsIRequestContextService
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTCONTEXTSERVICE
NS_DECL_NSIOBSERVER
RequestContextService();
nsresult Init();
void Shutdown();
static nsresult Create(nsISupports *outer, const nsIID& iid, void **result);
private:
virtual ~RequestContextService();
static RequestContextService *sSelf;
nsInterfaceHashtable<nsIDHashKey, nsIRequestContext> mTable;
nsCOMPtr<nsIUUIDGenerator> mUUIDGen;
};
} // ::mozilla::net
} // ::mozilla
#endif // mozilla__net__RequestContextService_h

View file

@ -0,0 +1,80 @@
/* -*- 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 "mozilla/Assertions.h"
#include "ShutdownLayer.h"
#include "prerror.h"
#include "private/pprio.h"
#include "prmem.h"
#include <winsock2.h>
static PRDescIdentity sWinSockShutdownLayerIdentity;
static PRIOMethods sWinSockShutdownLayerMethods;
static PRIOMethods *sWinSockShutdownLayerMethodsPtr = nullptr;
namespace mozilla {
namespace net {
extern PRDescIdentity nsNamedPipeLayerIdentity;
} // namespace net
} // namespace mozilla
PRStatus
WinSockClose(PRFileDesc *aFd)
{
MOZ_RELEASE_ASSERT(aFd->identity == sWinSockShutdownLayerIdentity,
"Windows shutdown layer not on the top of the stack");
PROsfd osfd = PR_FileDesc2NativeHandle(aFd);
if (osfd != -1) {
shutdown(osfd, SD_BOTH);
}
aFd->identity = PR_INVALID_IO_LAYER;
if (aFd->lower) {
return aFd->lower->methods->close(aFd->lower);
} else {
return PR_SUCCESS;
}
}
nsresult mozilla::net::AttachShutdownLayer(PRFileDesc *aFd)
{
if (!sWinSockShutdownLayerMethodsPtr) {
sWinSockShutdownLayerIdentity =
PR_GetUniqueIdentity("windows shutdown call layer");
sWinSockShutdownLayerMethods = *PR_GetDefaultIOMethods();
sWinSockShutdownLayerMethods.close = WinSockClose;
sWinSockShutdownLayerMethodsPtr = &sWinSockShutdownLayerMethods;
}
if (mozilla::net::nsNamedPipeLayerIdentity &&
PR_GetIdentitiesLayer(aFd, mozilla::net::nsNamedPipeLayerIdentity)) {
// Do not attach shutdown layer on named pipe layer,
// it is for PR_NSPR_IO_LAYER only.
return NS_OK;
}
PRFileDesc * layer;
PRStatus status;
layer = PR_CreateIOLayerStub(sWinSockShutdownLayerIdentity,
sWinSockShutdownLayerMethodsPtr);
if (!layer) {
return NS_OK;
}
status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
if (status == PR_FAILURE) {
PR_DELETE(layer);
return NS_ERROR_FAILURE;
}
return NS_OK;
}

View file

@ -0,0 +1,22 @@
/* -*- 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 SHUTDOWNLAYER_H___
#define SHUTDOWNLAYER_H___
#include "nscore.h"
#include "prio.h"
namespace mozilla { namespace net {
// This is only for windows. This layer will be attached jus before PR_CLose
// is call and it will only call shutdown(sock).
extern nsresult AttachShutdownLayer(PRFileDesc *fd);
} // namespace net
} // namespace mozilla
#endif /* SHUTDOWN_H___ */

View file

@ -0,0 +1,95 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "SimpleBuffer.h"
#include <algorithm>
namespace mozilla {
namespace net {
SimpleBuffer::SimpleBuffer()
: mStatus(NS_OK)
, mAvailable(0)
{
mOwningThread = PR_GetCurrentThread();
}
nsresult SimpleBuffer::Write(char *src, size_t len)
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
if (NS_FAILED(mStatus)) {
return mStatus;
}
while (len > 0) {
SimpleBufferPage *p = mBufferList.getLast();
if (p && (p->mWriteOffset == SimpleBufferPage::kSimpleBufferPageSize)) {
// no room.. make a new page
p = nullptr;
}
if (!p) {
p = new (fallible) SimpleBufferPage();
if (!p) {
mStatus = NS_ERROR_OUT_OF_MEMORY;
return mStatus;
}
mBufferList.insertBack(p);
}
size_t roomOnPage = SimpleBufferPage::kSimpleBufferPageSize - p->mWriteOffset;
size_t toWrite = std::min(roomOnPage, len);
memcpy(p->mBuffer + p->mWriteOffset, src, toWrite);
src += toWrite;
len -= toWrite;
p->mWriteOffset += toWrite;
mAvailable += toWrite;
}
return NS_OK;
}
size_t SimpleBuffer::Read(char *dest, size_t maxLen)
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
if (NS_FAILED(mStatus)) {
return 0;
}
size_t rv = 0;
for (SimpleBufferPage *p = mBufferList.getFirst();
p && (rv < maxLen); p = mBufferList.getFirst()) {
size_t avail = p->mWriteOffset - p->mReadOffset;
size_t toRead = std::min(avail, (maxLen - rv));
memcpy(dest + rv, p->mBuffer + p->mReadOffset, toRead);
rv += toRead;
p->mReadOffset += toRead;
if (p->mReadOffset == p->mWriteOffset) {
p->remove();
delete p;
}
}
MOZ_ASSERT(mAvailable >= rv);
mAvailable -= rv;
return rv;
}
size_t SimpleBuffer::Available()
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
return NS_SUCCEEDED(mStatus) ? mAvailable : 0;
}
void SimpleBuffer::Clear()
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
SimpleBufferPage *p;
while ((p = mBufferList.popFirst())) {
delete p;
}
mAvailable = 0;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 SimpleBuffer_h__
#define SimpleBuffer_h__
/*
This class is similar to a nsPipe except it does not have any locking, stores
an unbounded amount of data, can only be used on one thread, and has much
simpler result code semantics to deal with.
*/
#include "prtypes.h"
#include "mozilla/LinkedList.h"
#include "nsIThreadInternal.h"
namespace mozilla {
namespace net {
class SimpleBufferPage : public LinkedListElement<SimpleBufferPage>
{
public:
SimpleBufferPage() : mReadOffset(0), mWriteOffset(0) {}
static const size_t kSimpleBufferPageSize = 32000;
private:
friend class SimpleBuffer;
char mBuffer[kSimpleBufferPageSize];
size_t mReadOffset;
size_t mWriteOffset;
};
class SimpleBuffer
{
public:
SimpleBuffer();
~SimpleBuffer() {}
nsresult Write(char *stc, size_t len); // return OK or OUT_OF_MEMORY
size_t Read(char *dest, size_t maxLen); // return bytes read
size_t Available();
void Clear();
private:
PRThread *mOwningThread;
nsresult mStatus;
AutoCleanLinkedList<SimpleBufferPage> mBufferList;
size_t mAvailable;
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,72 @@
/* 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 "base/basictypes.h"
#include "mozilla/ClearOnShutdown.h"
#include "StreamingProtocolService.h"
#include "mozilla/net/NeckoChild.h"
#include "nsIURI.h"
#include "necko-config.h"
#ifdef NECKO_PROTOCOL_rtsp
#include "RtspControllerChild.h"
#include "RtspController.h"
#endif
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(StreamingProtocolControllerService,
nsIStreamingProtocolControllerService)
/* static */
StaticRefPtr<StreamingProtocolControllerService> sSingleton;
/* static */
already_AddRefed<StreamingProtocolControllerService>
StreamingProtocolControllerService::GetInstance()
{
if (!sSingleton) {
sSingleton = new StreamingProtocolControllerService();
ClearOnShutdown(&sSingleton);
}
RefPtr<StreamingProtocolControllerService> service = sSingleton.get();
return service.forget();
}
NS_IMETHODIMP
StreamingProtocolControllerService::Create(nsIChannel *aChannel, nsIStreamingProtocolController **aResult)
{
RefPtr<nsIStreamingProtocolController> mediacontroller;
nsCOMPtr<nsIURI> uri;
nsAutoCString scheme;
NS_ENSURE_ARG_POINTER(aChannel);
aChannel->GetURI(getter_AddRefs(uri));
nsresult rv = uri->GetScheme(scheme);
if (NS_FAILED(rv)) return rv;
#ifdef NECKO_PROTOCOL_rtsp
if (scheme.EqualsLiteral("rtsp")) {
if (IsNeckoChild()) {
mediacontroller = new RtspControllerChild(aChannel);
} else {
mediacontroller = new RtspController(aChannel);
}
}
#endif
if (!mediacontroller) {
return NS_ERROR_NO_INTERFACE;
}
mediacontroller->Init(uri);
mediacontroller.forget(aResult);
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_StreamingProtocolControllerService_h
#define mozilla_net_StreamingProtocolControllerService_h
#include "mozilla/StaticPtr.h"
#include "nsIStreamingProtocolService.h"
#include "nsIStreamingProtocolController.h"
#include "nsCOMPtr.h"
#include "nsIChannel.h"
namespace mozilla {
namespace net {
/**
* This class implements a service to help to create streaming protocol controller.
*/
class StreamingProtocolControllerService : public nsIStreamingProtocolControllerService
{
private:
virtual ~StreamingProtocolControllerService() {};
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMINGPROTOCOLCONTROLLERSERVICE
StreamingProtocolControllerService() {};
static already_AddRefed<StreamingProtocolControllerService> GetInstance();
};
} // namespace net
} // namespace mozilla
#endif //mozilla_net_StreamingProtocolControllerService_h

View file

@ -0,0 +1,515 @@
/* vim:set ts=2 sw=2 et cindent: */
/* 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 "TLSServerSocket.h"
#include "mozilla/net/DNS.h"
#include "nsAutoPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIServerSocket.h"
#include "nsITimer.h"
#include "nsIX509Cert.h"
#include "nsIX509CertDB.h"
#include "nsNetCID.h"
#include "nsProxyRelease.h"
#include "nsServiceManagerUtils.h"
#include "nsSocketTransport2.h"
#include "nsThreadUtils.h"
#include "ScopedNSSTypes.h"
#include "ssl.h"
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// TLSServerSocket
//-----------------------------------------------------------------------------
TLSServerSocket::TLSServerSocket()
: mServerCert(nullptr)
{
}
TLSServerSocket::~TLSServerSocket()
{
}
NS_IMPL_ISUPPORTS_INHERITED(TLSServerSocket,
nsServerSocket,
nsITLSServerSocket)
nsresult
TLSServerSocket::SetSocketDefaults()
{
// Set TLS options on the listening socket
mFD = SSL_ImportFD(nullptr, mFD);
if (NS_WARN_IF(!mFD)) {
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
SSL_OptionSet(mFD, SSL_SECURITY, true);
SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_CLIENT, false);
SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_SERVER, true);
// We don't currently notify the server API consumer of renegotiation events
// (to revalidate peer certs, etc.), so disable it for now.
SSL_OptionSet(mFD, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER);
SetSessionCache(true);
SetSessionTickets(true);
SetRequestClientCertificate(REQUEST_NEVER);
return NS_OK;
}
void
TLSServerSocket::CreateClientTransport(PRFileDesc* aClientFD,
const NetAddr& aClientAddr)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsresult rv;
RefPtr<nsSocketTransport> trans = new nsSocketTransport;
if (NS_WARN_IF(!trans)) {
mCondition = NS_ERROR_OUT_OF_MEMORY;
return;
}
RefPtr<TLSServerConnectionInfo> info = new TLSServerConnectionInfo();
info->mServerSocket = this;
info->mTransport = trans;
nsCOMPtr<nsISupports> infoSupports =
NS_ISUPPORTS_CAST(nsITLSServerConnectionInfo*, info);
rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr, infoSupports);
if (NS_WARN_IF(NS_FAILED(rv))) {
mCondition = rv;
return;
}
// Override the default peer certificate validation, so that server consumers
// can make their own choice after the handshake completes.
SSL_AuthCertificateHook(aClientFD, AuthCertificateHook, nullptr);
// Once the TLS handshake has completed, the server consumer is notified and
// has access to various TLS state details.
// It's safe to pass info here because the socket transport holds it as
// |mSecInfo| which keeps it alive for the lifetime of the socket.
SSL_HandshakeCallback(aClientFD, TLSServerConnectionInfo::HandshakeCallback,
info);
// Notify the consumer of the new client so it can manage the streams.
// Security details aren't known yet. The security observer will be notified
// later when they are ready.
nsCOMPtr<nsIServerSocket> serverSocket =
do_QueryInterface(NS_ISUPPORTS_CAST(nsITLSServerSocket*, this));
mListener->OnSocketAccepted(serverSocket, trans);
}
nsresult
TLSServerSocket::OnSocketListen()
{
if (NS_WARN_IF(!mServerCert)) {
return NS_ERROR_NOT_INITIALIZED;
}
UniqueCERTCertificate cert(mServerCert->GetCert());
if (NS_WARN_IF(!cert)) {
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
UniqueSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert.get(), nullptr));
if (NS_WARN_IF(!key)) {
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
SSLKEAType certKEA = NSS_FindCertKEAType(cert.get());
nsresult rv = MapSECStatus(SSL_ConfigSecureServer(mFD, cert.get(), key.get(),
certKEA));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// static
SECStatus
TLSServerSocket::AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checksig,
PRBool isServer)
{
// Allow any client cert here, server consumer code can decide whether it's
// okay after being notified of the new client socket.
return SECSuccess;
}
//-----------------------------------------------------------------------------
// TLSServerSocket::nsITLSServerSocket
//-----------------------------------------------------------------------------
NS_IMETHODIMP
TLSServerSocket::GetServerCert(nsIX509Cert** aCert)
{
if (NS_WARN_IF(!aCert)) {
return NS_ERROR_INVALID_POINTER;
}
*aCert = mServerCert;
NS_IF_ADDREF(*aCert);
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetServerCert(nsIX509Cert* aCert)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
mServerCert = aCert;
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetSessionCache(bool aEnabled)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
SSL_OptionSet(mFD, SSL_NO_CACHE, !aEnabled);
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetSessionTickets(bool aEnabled)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
SSL_OptionSet(mFD, SSL_ENABLE_SESSION_TICKETS, aEnabled);
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetRequestClientCertificate(uint32_t aMode)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
SSL_OptionSet(mFD, SSL_REQUEST_CERTIFICATE, aMode != REQUEST_NEVER);
switch (aMode) {
case REQUEST_ALWAYS:
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NO_ERROR);
break;
case REQUIRE_FIRST_HANDSHAKE:
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_FIRST_HANDSHAKE);
break;
case REQUIRE_ALWAYS:
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_ALWAYS);
break;
default:
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
}
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetCipherSuites(uint16_t* aCipherSuites, uint32_t aLength)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
for (uint16_t i = 0; i < SSL_NumImplementedCiphers; ++i) {
uint16_t cipher_id = SSL_ImplementedCiphers[i];
if (SSL_CipherPrefSet(mFD, cipher_id, false) != SECSuccess) {
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
}
for (uint32_t i = 0; i < aLength; ++i) {
if (SSL_CipherPrefSet(mFD, aCipherSuites[i], true) != SECSuccess) {
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
}
return NS_OK;
}
NS_IMETHODIMP
TLSServerSocket::SetVersionRange(uint16_t aMinVersion, uint16_t aMaxVersion)
{
// If AsyncListen was already called (and set mListener), it's too late to set
// this.
if (NS_WARN_IF(mListener)) {
return NS_ERROR_IN_PROGRESS;
}
SSLVersionRange range = {aMinVersion, aMaxVersion};
if (SSL_VersionRangeSet(mFD, &range) != SECSuccess) {
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// TLSServerConnectionInfo
//-----------------------------------------------------------------------------
namespace {
class TLSServerSecurityObserverProxy final : public nsITLSServerSecurityObserver
{
~TLSServerSecurityObserverProxy() {}
public:
explicit TLSServerSecurityObserverProxy(nsITLSServerSecurityObserver* aListener)
: mListener(new nsMainThreadPtrHolder<nsITLSServerSecurityObserver>(aListener))
{ }
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITLSSERVERSECURITYOBSERVER
class OnHandshakeDoneRunnable : public Runnable
{
public:
OnHandshakeDoneRunnable(const nsMainThreadPtrHandle<nsITLSServerSecurityObserver>& aListener,
nsITLSServerSocket* aServer,
nsITLSClientStatus* aStatus)
: mListener(aListener)
, mServer(aServer)
, mStatus(aStatus)
{ }
NS_DECL_NSIRUNNABLE
private:
nsMainThreadPtrHandle<nsITLSServerSecurityObserver> mListener;
nsCOMPtr<nsITLSServerSocket> mServer;
nsCOMPtr<nsITLSClientStatus> mStatus;
};
private:
nsMainThreadPtrHandle<nsITLSServerSecurityObserver> mListener;
};
NS_IMPL_ISUPPORTS(TLSServerSecurityObserverProxy,
nsITLSServerSecurityObserver)
NS_IMETHODIMP
TLSServerSecurityObserverProxy::OnHandshakeDone(nsITLSServerSocket* aServer,
nsITLSClientStatus* aStatus)
{
RefPtr<OnHandshakeDoneRunnable> r =
new OnHandshakeDoneRunnable(mListener, aServer, aStatus);
return NS_DispatchToMainThread(r);
}
NS_IMETHODIMP
TLSServerSecurityObserverProxy::OnHandshakeDoneRunnable::Run()
{
mListener->OnHandshakeDone(mServer, mStatus);
return NS_OK;
}
} // namespace
NS_IMPL_ISUPPORTS(TLSServerConnectionInfo,
nsITLSServerConnectionInfo,
nsITLSClientStatus)
TLSServerConnectionInfo::TLSServerConnectionInfo()
: mServerSocket(nullptr)
, mTransport(nullptr)
, mPeerCert(nullptr)
, mTlsVersionUsed(TLS_VERSION_UNKNOWN)
, mKeyLength(0)
, mMacLength(0)
, mLock("TLSServerConnectionInfo.mLock")
, mSecurityObserver(nullptr)
{
}
TLSServerConnectionInfo::~TLSServerConnectionInfo()
{
if (!mSecurityObserver) {
return;
}
RefPtr<nsITLSServerSecurityObserver> observer;
{
MutexAutoLock lock(mLock);
observer = mSecurityObserver.forget();
}
if (observer) {
NS_ReleaseOnMainThread(observer.forget());
}
}
NS_IMETHODIMP
TLSServerConnectionInfo::SetSecurityObserver(nsITLSServerSecurityObserver* aObserver)
{
{
MutexAutoLock lock(mLock);
mSecurityObserver = new TLSServerSecurityObserverProxy(aObserver);
}
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetServerSocket(nsITLSServerSocket** aSocket)
{
if (NS_WARN_IF(!aSocket)) {
return NS_ERROR_INVALID_POINTER;
}
*aSocket = mServerSocket;
NS_IF_ADDREF(*aSocket);
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetStatus(nsITLSClientStatus** aStatus)
{
if (NS_WARN_IF(!aStatus)) {
return NS_ERROR_INVALID_POINTER;
}
*aStatus = this;
NS_IF_ADDREF(*aStatus);
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetPeerCert(nsIX509Cert** aCert)
{
if (NS_WARN_IF(!aCert)) {
return NS_ERROR_INVALID_POINTER;
}
*aCert = mPeerCert;
NS_IF_ADDREF(*aCert);
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetTlsVersionUsed(int16_t* aTlsVersionUsed)
{
if (NS_WARN_IF(!aTlsVersionUsed)) {
return NS_ERROR_INVALID_POINTER;
}
*aTlsVersionUsed = mTlsVersionUsed;
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetCipherName(nsACString& aCipherName)
{
aCipherName.Assign(mCipherName);
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetKeyLength(uint32_t* aKeyLength)
{
if (NS_WARN_IF(!aKeyLength)) {
return NS_ERROR_INVALID_POINTER;
}
*aKeyLength = mKeyLength;
return NS_OK;
}
NS_IMETHODIMP
TLSServerConnectionInfo::GetMacLength(uint32_t* aMacLength)
{
if (NS_WARN_IF(!aMacLength)) {
return NS_ERROR_INVALID_POINTER;
}
*aMacLength = mMacLength;
return NS_OK;
}
// static
void
TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD, void* aArg)
{
RefPtr<TLSServerConnectionInfo> info =
static_cast<TLSServerConnectionInfo*>(aArg);
nsISocketTransport* transport = info->mTransport;
// No longer needed outside this function, so clear the weak ref
info->mTransport = nullptr;
nsresult rv = info->HandshakeCallback(aFD);
if (NS_WARN_IF(NS_FAILED(rv))) {
transport->Close(rv);
}
}
nsresult
TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD)
{
nsresult rv;
UniqueCERTCertificate clientCert(SSL_PeerCertificate(aFD));
if (clientCert) {
nsCOMPtr<nsIX509CertDB> certDB =
do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIX509Cert> clientCertPSM;
rv = certDB->ConstructX509(reinterpret_cast<char*>(clientCert->derCert.data),
clientCert->derCert.len,
getter_AddRefs(clientCertPSM));
if (NS_FAILED(rv)) {
return rv;
}
mPeerCert = clientCertPSM;
}
SSLChannelInfo channelInfo;
rv = MapSECStatus(SSL_GetChannelInfo(aFD, &channelInfo, sizeof(channelInfo)));
if (NS_FAILED(rv)) {
return rv;
}
mTlsVersionUsed = channelInfo.protocolVersion;
SSLCipherSuiteInfo cipherInfo;
rv = MapSECStatus(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
sizeof(cipherInfo)));
if (NS_FAILED(rv)) {
return rv;
}
mCipherName.Assign(cipherInfo.cipherSuiteName);
mKeyLength = cipherInfo.effectiveKeyBits;
mMacLength = cipherInfo.macBits;
if (!mSecurityObserver) {
return NS_OK;
}
// Notify consumer code that handshake is complete
nsCOMPtr<nsITLSServerSecurityObserver> observer;
{
MutexAutoLock lock(mLock);
mSecurityObserver.swap(observer);
}
nsCOMPtr<nsITLSServerSocket> serverSocket;
GetServerSocket(getter_AddRefs(serverSocket));
observer->OnHandshakeDone(serverSocket, this);
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,81 @@
/* vim:set ts=2 sw=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_TLSServerSocket_h
#define mozilla_net_TLSServerSocket_h
#include "nsAutoPtr.h"
#include "nsITLSServerSocket.h"
#include "nsServerSocket.h"
#include "nsString.h"
#include "mozilla/Mutex.h"
#include "seccomon.h"
namespace mozilla {
namespace net {
class TLSServerSocket final : public nsServerSocket
, public nsITLSServerSocket
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_NSISERVERSOCKET(nsServerSocket::)
NS_DECL_NSITLSSERVERSOCKET
// Override methods from nsServerSocket
virtual void CreateClientTransport(PRFileDesc* clientFD,
const NetAddr& clientAddr) override;
virtual nsresult SetSocketDefaults() override;
virtual nsresult OnSocketListen() override;
TLSServerSocket();
private:
virtual ~TLSServerSocket();
static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
PRBool checksig, PRBool isServer);
nsCOMPtr<nsIX509Cert> mServerCert;
};
class TLSServerConnectionInfo : public nsITLSServerConnectionInfo
, public nsITLSClientStatus
{
friend class TLSServerSocket;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITLSSERVERCONNECTIONINFO
NS_DECL_NSITLSCLIENTSTATUS
TLSServerConnectionInfo();
private:
virtual ~TLSServerConnectionInfo();
static void HandshakeCallback(PRFileDesc* aFD, void* aArg);
nsresult HandshakeCallback(PRFileDesc* aFD);
RefPtr<TLSServerSocket> mServerSocket;
// Weak ref to the transport, to avoid cycles since the transport holds a
// reference to the TLSServerConnectionInfo object. This is not handed out to
// anyone, and is only used in HandshakeCallback to close the transport in
// case of an error. After this, it's set to nullptr.
nsISocketTransport* mTransport;
nsCOMPtr<nsIX509Cert> mPeerCert;
int16_t mTlsVersionUsed;
nsCString mCipherName;
uint32_t mKeyLength;
uint32_t mMacLength;
// lock protects access to mSecurityObserver
mozilla::Mutex mLock;
nsCOMPtr<nsITLSServerSecurityObserver> mSecurityObserver;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_TLSServerSocket_h

View file

@ -0,0 +1,392 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ThrottleQueue.h"
#include "nsISeekableStream.h"
#include "nsIAsyncInputStream.h"
#include "nsStreamUtils.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
class ThrottleInputStream final
: public nsIAsyncInputStream
, public nsISeekableStream
{
public:
ThrottleInputStream(nsIInputStream* aStream, ThrottleQueue* aQueue);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIASYNCINPUTSTREAM
void AllowInput();
private:
~ThrottleInputStream();
nsCOMPtr<nsIInputStream> mStream;
RefPtr<ThrottleQueue> mQueue;
nsresult mClosedStatus;
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mEventTarget;
};
NS_IMPL_ISUPPORTS(ThrottleInputStream, nsIAsyncInputStream, nsIInputStream, nsISeekableStream)
ThrottleInputStream::ThrottleInputStream(nsIInputStream *aStream, ThrottleQueue* aQueue)
: mStream(aStream)
, mQueue(aQueue)
, mClosedStatus(NS_OK)
{
MOZ_ASSERT(aQueue != nullptr);
}
ThrottleInputStream::~ThrottleInputStream()
{
Close();
}
NS_IMETHODIMP
ThrottleInputStream::Close()
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
if (mQueue) {
mQueue->DequeueStream(this);
mQueue = nullptr;
mClosedStatus = NS_BASE_STREAM_CLOSED;
}
return mStream->Close();
}
NS_IMETHODIMP
ThrottleInputStream::Available(uint64_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
return mStream->Available(aResult);
}
NS_IMETHODIMP
ThrottleInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
uint32_t realCount;
nsresult rv = mQueue->Available(aCount, &realCount);
if (NS_FAILED(rv)) {
return rv;
}
if (realCount == 0) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
rv = mStream->Read(aBuf, realCount, aResult);
if (NS_SUCCEEDED(rv) && *aResult > 0) {
mQueue->RecordRead(*aResult);
}
return rv;
}
NS_IMETHODIMP
ThrottleInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
uint32_t realCount;
nsresult rv = mQueue->Available(aCount, &realCount);
if (NS_FAILED(rv)) {
return rv;
}
if (realCount == 0) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
rv = mStream->ReadSegments(aWriter, aClosure, realCount, aResult);
if (NS_SUCCEEDED(rv) && *aResult > 0) {
mQueue->RecordRead(*aResult);
}
return rv;
}
NS_IMETHODIMP
ThrottleInputStream::IsNonBlocking(bool* aNonBlocking)
{
*aNonBlocking = true;
return NS_OK;
}
NS_IMETHODIMP
ThrottleInputStream::Seek(int32_t aWhence, int64_t aOffset)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
if (!sstream) {
return NS_ERROR_FAILURE;
}
return sstream->Seek(aWhence, aOffset);
}
NS_IMETHODIMP
ThrottleInputStream::Tell(int64_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
if (!sstream) {
return NS_ERROR_FAILURE;
}
return sstream->Tell(aResult);
}
NS_IMETHODIMP
ThrottleInputStream::SetEOF()
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
if (!sstream) {
return NS_ERROR_FAILURE;
}
return sstream->SetEOF();
}
NS_IMETHODIMP
ThrottleInputStream::CloseWithStatus(nsresult aStatus)
{
if (NS_FAILED(mClosedStatus)) {
// Already closed, ignore.
return NS_OK;
}
if (NS_SUCCEEDED(aStatus)) {
aStatus = NS_BASE_STREAM_CLOSED;
}
mClosedStatus = Close();
if (NS_SUCCEEDED(mClosedStatus)) {
mClosedStatus = aStatus;
}
return NS_OK;
}
NS_IMETHODIMP
ThrottleInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
uint32_t aFlags,
uint32_t aRequestedCount,
nsIEventTarget *aEventTarget)
{
if (aFlags != 0) {
return NS_ERROR_ILLEGAL_VALUE;
}
mCallback = aCallback;
mEventTarget = aEventTarget;
if (mCallback) {
mQueue->QueueStream(this);
} else {
mQueue->DequeueStream(this);
}
return NS_OK;
}
void
ThrottleInputStream::AllowInput()
{
MOZ_ASSERT(mCallback);
nsCOMPtr<nsIInputStreamCallback> callbackEvent =
NS_NewInputStreamReadyEvent(mCallback, mEventTarget);
mCallback = nullptr;
mEventTarget = nullptr;
callbackEvent->OnInputStreamReady(this);
}
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(ThrottleQueue, nsIInputChannelThrottleQueue, nsITimerCallback)
ThrottleQueue::ThrottleQueue()
: mMeanBytesPerSecond(0)
, mMaxBytesPerSecond(0)
, mBytesProcessed(0)
, mTimerArmed(false)
{
nsresult rv;
nsCOMPtr<nsIEventTarget> sts;
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
if (NS_SUCCEEDED(rv))
sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mTimer)
mTimer->SetTarget(sts);
}
ThrottleQueue::~ThrottleQueue()
{
if (mTimer && mTimerArmed) {
mTimer->Cancel();
}
mTimer = nullptr;
}
NS_IMETHODIMP
ThrottleQueue::RecordRead(uint32_t aBytesRead)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
ThrottleEntry entry;
entry.mTime = TimeStamp::Now();
entry.mBytesRead = aBytesRead;
mReadEvents.AppendElement(entry);
mBytesProcessed += aBytesRead;
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::Available(uint32_t aRemaining, uint32_t* aAvailable)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
TimeStamp now = TimeStamp::Now();
TimeStamp oneSecondAgo = now - TimeDuration::FromSeconds(1);
size_t i;
// Remove all stale events.
for (i = 0; i < mReadEvents.Length(); ++i) {
if (mReadEvents[i].mTime >= oneSecondAgo) {
break;
}
}
mReadEvents.RemoveElementsAt(0, i);
uint32_t totalBytes = 0;
for (i = 0; i < mReadEvents.Length(); ++i) {
totalBytes += mReadEvents[i].mBytesRead;
}
uint32_t spread = mMaxBytesPerSecond - mMeanBytesPerSecond;
double prob = static_cast<double>(rand()) / RAND_MAX;
uint32_t thisSliceBytes = mMeanBytesPerSecond - spread +
static_cast<uint32_t>(2 * spread * prob);
if (totalBytes >= thisSliceBytes) {
*aAvailable = 0;
} else {
*aAvailable = thisSliceBytes;
}
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::Init(uint32_t aMeanBytesPerSecond, uint32_t aMaxBytesPerSecond)
{
// Can be called on any thread.
if (aMeanBytesPerSecond == 0 || aMaxBytesPerSecond == 0 || aMaxBytesPerSecond < aMeanBytesPerSecond) {
return NS_ERROR_ILLEGAL_VALUE;
}
mMeanBytesPerSecond = aMeanBytesPerSecond;
mMaxBytesPerSecond = aMaxBytesPerSecond;
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::BytesProcessed(uint64_t* aResult)
{
*aResult = mBytesProcessed;
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::WrapStream(nsIInputStream* aInputStream, nsIAsyncInputStream** aResult)
{
nsCOMPtr<nsIAsyncInputStream> result = new ThrottleInputStream(aInputStream, this);
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::Notify(nsITimer* aTimer)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
// A notified reader may need to push itself back on the queue.
// Swap out the list of readers so that this works properly.
nsTArray<RefPtr<ThrottleInputStream>> events;
events.SwapElements(mAsyncEvents);
// Optimistically notify all the waiting readers, and then let them
// requeue if there isn't enough bandwidth.
for (size_t i = 0; i < events.Length(); ++i) {
events[i]->AllowInput();
}
mTimerArmed = false;
return NS_OK;
}
void
ThrottleQueue::QueueStream(ThrottleInputStream* aStream)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mAsyncEvents.IndexOf(aStream) == mAsyncEvents.NoIndex) {
mAsyncEvents.AppendElement(aStream);
if (!mTimerArmed) {
uint32_t ms = 1000;
if (mReadEvents.Length() > 0) {
TimeStamp t = mReadEvents[0].mTime + TimeDuration::FromSeconds(1);
TimeStamp now = TimeStamp::Now();
if (t > now) {
ms = static_cast<uint32_t>((t - now).ToMilliseconds());
} else {
ms = 1;
}
}
if (NS_SUCCEEDED(mTimer->InitWithCallback(this, ms, nsITimer::TYPE_ONE_SHOT))) {
mTimerArmed = true;
}
}
}
}
void
ThrottleQueue::DequeueStream(ThrottleInputStream* aStream)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
mAsyncEvents.RemoveElement(aStream);
}
}
}

View file

@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_ThrottleQueue_h
#define mozilla_net_ThrottleQueue_h
#include "mozilla/TimeStamp.h"
#include "nsIThrottledInputChannel.h"
#include "nsITimer.h"
namespace mozilla {
namespace net {
class ThrottleInputStream;
/**
* An implementation of nsIInputChannelThrottleQueue that can be used
* to throttle uploads. This class is not thread-safe.
* Initialization and calls to WrapStream may be done on any thread;
* but otherwise, after creation, it can only be used on the socket
* thread. It currently throttles with a one second granularity, so
* may be a bit choppy.
*/
class ThrottleQueue final
: public nsIInputChannelThrottleQueue
, public nsITimerCallback
{
public:
ThrottleQueue();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTCHANNELTHROTTLEQUEUE
NS_DECL_NSITIMERCALLBACK
void QueueStream(ThrottleInputStream* aStream);
void DequeueStream(ThrottleInputStream* aStream);
private:
~ThrottleQueue();
struct ThrottleEntry {
TimeStamp mTime;
uint32_t mBytesRead;
};
nsTArray<ThrottleEntry> mReadEvents;
uint32_t mMeanBytesPerSecond;
uint32_t mMaxBytesPerSecond;
uint64_t mBytesProcessed;
nsTArray<RefPtr<ThrottleInputStream>> mAsyncEvents;
nsCOMPtr<nsITimer> mTimer;
bool mTimerArmed;
};
}
}
#endif // mozilla_net_ThrottleQueue_h

277
netwerk/base/Tickler.cpp Normal file
View file

@ -0,0 +1,277 @@
/* -*- 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 "Tickler.h"
#ifdef MOZ_USE_WIFI_TICKLER
#include "nsComponentManagerUtils.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "prnetdb.h"
#include "mozilla/jni/Utils.h"
#include "GeneratedJNIWrappers.h"
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler)
Tickler::Tickler()
: mLock("Tickler::mLock")
, mActive(false)
, mCanceled(false)
, mEnabled(false)
, mDelay(16)
, mDuration(TimeDuration::FromMilliseconds(400))
, mFD(nullptr)
{
MOZ_ASSERT(NS_IsMainThread());
}
Tickler::~Tickler()
{
// non main thread uses of the tickler should hold weak
// references to it if they must hold a reference at all
MOZ_ASSERT(NS_IsMainThread());
if (mThread) {
mThread->AsyncShutdown();
mThread = nullptr;
}
if (mTimer)
mTimer->Cancel();
if (mFD)
PR_Close(mFD);
}
nsresult
Tickler::Init()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mTimer);
MOZ_ASSERT(!mActive);
MOZ_ASSERT(!mThread);
MOZ_ASSERT(!mFD);
if (jni::IsAvailable()) {
java::GeckoAppShell::EnableNetworkNotifications();
}
mFD = PR_OpenUDPSocket(PR_AF_INET);
if (!mFD)
return NS_ERROR_FAILURE;
// make sure new socket has a ttl of 1
// failure is not fatal.
PRSocketOptionData opt;
opt.option = PR_SockOpt_IpTimeToLive;
opt.value.ip_ttl = 1;
PR_SetSocketOption(mFD, &opt);
nsresult rv = NS_NewNamedThread("wifi tickler",
getter_AddRefs(mThread));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsITimer> tmpTimer(do_CreateInstance(NS_TIMER_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
rv = tmpTimer->SetTarget(mThread);
if (NS_FAILED(rv))
return rv;
mTimer.swap(tmpTimer);
mAddr.inet.family = PR_AF_INET;
mAddr.inet.port = PR_htons (4886);
mAddr.inet.ip = 0;
return NS_OK;
}
void Tickler::Tickle()
{
MutexAutoLock lock(mLock);
MOZ_ASSERT(mThread);
mLastTickle = TimeStamp::Now();
if (!mActive)
MaybeStartTickler();
}
void Tickler::PostCheckTickler()
{
mLock.AssertCurrentThreadOwns();
mThread->Dispatch(NewRunnableMethod(this, &Tickler::CheckTickler),
NS_DISPATCH_NORMAL);
return;
}
void Tickler::MaybeStartTicklerUnlocked()
{
MutexAutoLock lock(mLock);
MaybeStartTickler();
}
void Tickler::MaybeStartTickler()
{
mLock.AssertCurrentThreadOwns();
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(
NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked));
return;
}
if (!mPrefs)
mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (mPrefs) {
int32_t val;
bool boolVal;
if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal)))
mEnabled = boolVal;
if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) {
if (val < 1)
val = 1;
if (val > 100000)
val = 100000;
mDuration = TimeDuration::FromMilliseconds(val);
}
if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) {
if (val < 1)
val = 1;
if (val > 1000)
val = 1000;
mDelay = static_cast<uint32_t>(val);
}
}
PostCheckTickler();
}
void Tickler::CheckTickler()
{
MutexAutoLock lock(mLock);
MOZ_ASSERT(mThread == NS_GetCurrentThread());
bool shouldRun = (!mCanceled) &&
((TimeStamp::Now() - mLastTickle) <= mDuration);
if ((shouldRun && mActive) || (!shouldRun && !mActive))
return; // no change in state
if (mActive)
StopTickler();
else
StartTickler();
}
void Tickler::Cancel()
{
MutexAutoLock lock(mLock);
MOZ_ASSERT(NS_IsMainThread());
mCanceled = true;
if (mThread)
PostCheckTickler();
}
void Tickler::StopTickler()
{
mLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mThread == NS_GetCurrentThread());
MOZ_ASSERT(mTimer);
MOZ_ASSERT(mActive);
mTimer->Cancel();
mActive = false;
}
class TicklerTimer final : public nsITimerCallback
{
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
TicklerTimer(Tickler *aTickler)
{
mTickler = do_GetWeakReference(aTickler);
}
private:
~TicklerTimer() {}
nsWeakPtr mTickler;
};
void Tickler::StartTickler()
{
mLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mThread == NS_GetCurrentThread());
MOZ_ASSERT(!mActive);
MOZ_ASSERT(mTimer);
if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this),
mEnabled ? mDelay : 1000,
nsITimer::TYPE_REPEATING_SLACK)))
mActive = true;
}
// argument should be in network byte order
void Tickler::SetIPV4Address(uint32_t address)
{
mAddr.inet.ip = address;
}
// argument should be in network byte order
void Tickler::SetIPV4Port(uint16_t port)
{
mAddr.inet.port = port;
}
NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback)
NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer)
{
RefPtr<Tickler> tickler = do_QueryReferent(mTickler);
if (!tickler)
return NS_ERROR_FAILURE;
MutexAutoLock lock(tickler->mLock);
if (!tickler->mFD) {
tickler->StopTickler();
return NS_ERROR_FAILURE;
}
if (tickler->mCanceled ||
((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) {
tickler->StopTickler();
return NS_OK;
}
if (!tickler->mEnabled)
return NS_OK;
PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0);
return NS_OK;
}
} // namespace mozilla::net
} // namespace mozilla
#else // not defined MOZ_USE_WIFI_TICKLER
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS0(Tickler)
} // namespace net
} // namespace mozilla
#endif // defined MOZ_USE_WIFI_TICKLER

131
netwerk/base/Tickler.h Normal file
View file

@ -0,0 +1,131 @@
/* -*- 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 mozilla_net_Tickler_h
#define mozilla_net_Tickler_h
// The tickler sends a regular 0 byte UDP heartbeat out to a
// particular address for a short time after it has been touched. This
// is used on some mobile wifi chipsets to mitigate Power Save Polling
// (PSP) Mode when we are anticipating a response packet
// soon. Typically PSP adds 100ms of latency to a read event because
// the packet delivery is not triggered until the 802.11 beacon is
// delivered to the host (100ms is the standard Access Point
// configuration for the beacon interval.) Requesting a frequent
// transmission and getting a CTS frame from the AP at least that
// frequently allows for low latency receives when we have reason to
// expect them (e.g a SYN-ACK).
//
// The tickler is used to allow RTT based phases of web transport to
// complete quickly when on wifi - ARP, DNS, TCP handshake, SSL
// handshake, HTTP headers, and the TCP slow start phase. The
// transaction is given up to 400 miliseconds by default to get
// through those phases before the tickler is disabled.
//
// The tickler only applies to wifi on mobile right now. Hopefully it
// can also be restricted to particular handset models in the future.
#if defined(ANDROID) && !defined(MOZ_B2G)
#define MOZ_USE_WIFI_TICKLER
#endif
#include "mozilla/Attributes.h"
#include "nsISupports.h"
#include <stdint.h>
#ifdef MOZ_USE_WIFI_TICKLER
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "nsAutoPtr.h"
#include "nsISupports.h"
#include "nsIThread.h"
#include "nsITimer.h"
#include "nsWeakReference.h"
#include "prio.h"
class nsIPrefBranch;
#endif
namespace mozilla {
namespace net {
#ifdef MOZ_USE_WIFI_TICKLER
// 8f769ed6-207c-4af9-9f7e-9e832da3754e
#define NS_TICKLER_IID \
{ 0x8f769ed6, 0x207c, 0x4af9, \
{ 0x9f, 0x7e, 0x9e, 0x83, 0x2d, 0xa3, 0x75, 0x4e } }
class Tickler final : public nsSupportsWeakReference
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TICKLER_IID)
// These methods are main thread only
Tickler();
void Cancel();
nsresult Init();
void SetIPV4Address(uint32_t address);
void SetIPV4Port(uint16_t port);
// Tickle the tickler to (re-)start the activity.
// May call from any thread
void Tickle();
private:
~Tickler();
friend class TicklerTimer;
Mutex mLock;
nsCOMPtr<nsIThread> mThread;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIPrefBranch> mPrefs;
bool mActive;
bool mCanceled;
bool mEnabled;
uint32_t mDelay;
TimeDuration mDuration;
PRFileDesc* mFD;
TimeStamp mLastTickle;
PRNetAddr mAddr;
// These functions may be called from any thread
void PostCheckTickler();
void MaybeStartTickler();
void MaybeStartTicklerUnlocked();
// Tickler thread only
void CheckTickler();
void StartTickler();
void StopTickler();
};
NS_DEFINE_STATIC_IID_ACCESSOR(Tickler, NS_TICKLER_IID)
#else // not defined MOZ_USE_WIFI_TICKLER
class Tickler final : public nsISupports
{
~Tickler() { }
public:
NS_DECL_THREADSAFE_ISUPPORTS
Tickler() { }
nsresult Init() { return NS_ERROR_NOT_IMPLEMENTED; }
void Cancel() { }
void SetIPV4Address(uint32_t) { };
void SetIPV4Port(uint16_t) { }
void Tickle() { }
};
#endif // defined MOZ_USE_WIFI_TICKLER
} // namespace net
} // namespace mozilla
#endif // mozilla_net_Tickler_h

316
netwerk/base/moz.build Normal file
View file

@ -0,0 +1,316 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'mozIThirdPartyUtil.idl',
'nsIApplicationCache.idl',
'nsIApplicationCacheChannel.idl',
'nsIApplicationCacheContainer.idl',
'nsIApplicationCacheService.idl',
'nsIArrayBufferInputStream.idl',
'nsIAsyncStreamCopier.idl',
'nsIAsyncStreamCopier2.idl',
'nsIAsyncVerifyRedirectCallback.idl',
'nsIAuthInformation.idl',
'nsIAuthModule.idl',
'nsIAuthPrompt.idl',
'nsIAuthPrompt2.idl',
'nsIAuthPromptAdapterFactory.idl',
'nsIAuthPromptCallback.idl',
'nsIAuthPromptProvider.idl',
'nsIBackgroundFileSaver.idl',
'nsIBufferedStreams.idl',
'nsIByteRangeRequest.idl',
'nsICacheInfoChannel.idl',
'nsICachingChannel.idl',
'nsICancelable.idl',
'nsICaptivePortalService.idl',
'nsIChannel.idl',
'nsIChannelEventSink.idl',
'nsIChannelWithDivertableParentListener.idl',
'nsIChildChannel.idl',
'nsIClassOfService.idl',
'nsIContentSniffer.idl',
'nsICryptoFIPSInfo.idl',
'nsICryptoHash.idl',
'nsICryptoHMAC.idl',
'nsIDashboard.idl',
'nsIDashboardEventNotifier.idl',
'nsIDeprecationWarner.idl',
'nsIDivertableChannel.idl',
'nsIDownloader.idl',
'nsIEncodedChannel.idl',
'nsIExternalProtocolHandler.idl',
'nsIFileStreams.idl',
'nsIFileURL.idl',
'nsIForcePendingChannel.idl',
'nsIFormPOSTActionChannel.idl',
'nsIHttpAuthenticatorCallback.idl',
'nsIHttpPushListener.idl',
'nsIIncrementalDownload.idl',
'nsIIncrementalStreamLoader.idl',
'nsIInputStreamChannel.idl',
'nsIInputStreamPump.idl',
'nsIIOService.idl',
'nsIIOService2.idl',
'nsILoadContextInfo.idl',
'nsILoadGroup.idl',
'nsILoadGroupChild.idl',
'nsILoadInfo.idl',
'nsIMIMEInputStream.idl',
'nsIMultiPartChannel.idl',
'nsINestedURI.idl',
'nsINetAddr.idl',
'nsINetUtil.idl',
'nsINetworkInfoService.idl',
'nsINetworkInterceptController.idl',
'nsINetworkLinkService.idl',
'nsINetworkPredictor.idl',
'nsINetworkPredictorVerifier.idl',
'nsINetworkProperties.idl',
'nsINSSErrorsService.idl',
'nsINullChannel.idl',
'nsIParentChannel.idl',
'nsIParentRedirectingChannel.idl',
'nsIPermission.idl',
'nsIPermissionManager.idl',
'nsIPrivateBrowsingChannel.idl',
'nsIProgressEventSink.idl',
'nsIPrompt.idl',
'nsIProtocolHandler.idl',
'nsIProtocolProxyCallback.idl',
'nsIProtocolProxyFilter.idl',
'nsIProtocolProxyService.idl',
'nsIProtocolProxyService2.idl',
'nsIProxiedChannel.idl',
'nsIProxiedProtocolHandler.idl',
'nsIProxyInfo.idl',
'nsIRandomGenerator.idl',
'nsIRedirectChannelRegistrar.idl',
'nsIRedirectResultListener.idl',
'nsIRequest.idl',
'nsIRequestContext.idl',
'nsIRequestObserver.idl',
'nsIRequestObserverProxy.idl',
'nsIResumableChannel.idl',
'nsISecCheckWrapChannel.idl',
'nsISecureBrowserUI.idl',
'nsISecurityEventSink.idl',
'nsISecurityInfoProvider.idl',
'nsISensitiveInfoHiddenURI.idl',
'nsISerializationHelper.idl',
'nsIServerSocket.idl',
'nsISimpleStreamListener.idl',
'nsISocketFilter.idl',
'nsISocketTransport.idl',
'nsISocketTransportService.idl',
'nsISpeculativeConnect.idl',
'nsIStandardURL.idl',
'nsIStreamingProtocolController.idl',
'nsIStreamingProtocolService.idl',
'nsIStreamListener.idl',
'nsIStreamListenerTee.idl',
'nsIStreamLoader.idl',
'nsIStreamTransportService.idl',
'nsISyncStreamListener.idl',
'nsISystemProxySettings.idl',
'nsIThreadRetargetableRequest.idl',
'nsIThreadRetargetableStreamListener.idl',
'nsIThrottledInputChannel.idl',
'nsITimedChannel.idl',
'nsITLSServerSocket.idl',
'nsITraceableChannel.idl',
'nsITransport.idl',
'nsIUDPSocket.idl',
'nsIUnicharStreamLoader.idl',
'nsIUploadChannel.idl',
'nsIUploadChannel2.idl',
'nsIURI.idl',
'nsIURIClassifier.idl',
'nsIURIWithBlobImpl.idl',
'nsIURIWithPrincipal.idl',
'nsIURIWithQuery.idl',
'nsIURL.idl',
'nsIURLParser.idl',
'nsPILoadGroupInternal.idl',
'nsPISocketTransportService.idl',
]
if CONFIG['MOZ_TOOLKIT_SEARCH']:
XPIDL_SOURCES += [
'nsIBrowserSearchService.idl',
]
XPIDL_MODULE = 'necko'
EXPORTS += [
'netCore.h',
'nsASocketHandler.h',
'nsAsyncRedirectVerifyHelper.h',
'nsFileStreams.h',
'nsInputStreamPump.h',
'nsMIMEInputStream.h',
'nsNetUtil.h',
'nsNetUtilInlines.h',
'nsReadLine.h',
'nsSerializationHelper.h',
'nsSimpleNestedURI.h',
'nsSimpleURI.h',
'nsStreamListenerWrapper.h',
'nsTemporaryFileInputStream.h',
'nsURIHashKey.h',
'nsURLHelper.h',
'nsURLParsers.h',
]
EXPORTS.mozilla += [
'LoadContextInfo.h',
'LoadInfo.h',
'LoadTainting.h',
]
EXPORTS.mozilla.net += [
'CaptivePortalService.h',
'ChannelDiverterChild.h',
'ChannelDiverterParent.h',
'Dashboard.h',
'DashboardTypes.h',
'MemoryDownloader.h',
'Predictor.h',
'ReferrerPolicy.h',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
EXPORTS += [
'NetStatistics.h',
]
UNIFIED_SOURCES += [
'ArrayBufferInputStream.cpp',
'BackgroundFileSaver.cpp',
'CaptivePortalService.cpp',
'ChannelDiverterChild.cpp',
'ChannelDiverterParent.cpp',
'Dashboard.cpp',
'EventTokenBucket.cpp',
'LoadContextInfo.cpp',
'LoadInfo.cpp',
'MemoryDownloader.cpp',
'NetworkActivityMonitor.cpp',
'nsAsyncRedirectVerifyHelper.cpp',
'nsAsyncStreamCopier.cpp',
'nsAuthInformationHolder.cpp',
'nsBase64Encoder.cpp',
'nsBaseChannel.cpp',
'nsBaseContentStream.cpp',
'nsBufferedStreams.cpp',
'nsChannelClassifier.cpp',
'nsDirectoryIndexStream.cpp',
'nsDNSPrefetch.cpp',
'nsDownloader.cpp',
'nsFileStreams.cpp',
'nsIncrementalDownload.cpp',
'nsIncrementalStreamLoader.cpp',
'nsInputStreamChannel.cpp',
'nsInputStreamPump.cpp',
'nsIOService.cpp',
'nsLoadGroup.cpp',
'nsMediaFragmentURIParser.cpp',
'nsMIMEInputStream.cpp',
'nsNetAddr.cpp',
'nsNetUtil.cpp',
'nsPACMan.cpp',
'nsPreloadedStream.cpp',
'nsProtocolProxyService.cpp',
'nsProxyInfo.cpp',
'nsRequestObserverProxy.cpp',
'nsSecCheckWrapChannel.cpp',
'nsSerializationHelper.cpp',
'nsServerSocket.cpp',
'nsSimpleNestedURI.cpp',
'nsSimpleStreamListener.cpp',
'nsSimpleURI.cpp',
'nsSocketTransport2.cpp',
'nsSocketTransportService2.cpp',
'nsStandardURL.cpp',
'nsStreamListenerTee.cpp',
'nsStreamListenerWrapper.cpp',
'nsStreamLoader.cpp',
'nsStreamTransportService.cpp',
'nsSyncStreamListener.cpp',
'nsTemporaryFileInputStream.cpp',
'nsTransportUtils.cpp',
'nsUDPSocket.cpp',
'nsUnicharStreamLoader.cpp',
'nsURLHelper.cpp',
'nsURLParsers.cpp',
'PollableEvent.cpp',
'Predictor.cpp',
'ProxyAutoConfig.cpp',
'RedirectChannelRegistrar.cpp',
'RequestContextService.cpp',
'SimpleBuffer.cpp',
'StreamingProtocolService.cpp',
'ThrottleQueue.cpp',
'Tickler.cpp',
'TLSServerSocket.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'nsURLHelperWin.cpp',
'ShutdownLayer.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'nsURLHelperOSX.cpp',
]
else:
SOURCES += [
'nsURLHelperUnix.cpp',
]
# nsINetworkInfoService support.
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'NetworkInfoServiceWindows.cpp',
'nsNetworkInfoService.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'NetworkInfoServiceCocoa.cpp',
'nsNetworkInfoService.cpp',
]
elif CONFIG['OS_ARCH'] == 'Linux':
SOURCES += [
'NetworkInfoServiceLinux.cpp',
'nsNetworkInfoService.cpp',
]
EXTRA_JS_MODULES += [
'NetUtil.jsm',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/docshell/base',
'/dom/base',
'/netwerk/protocol/http',
'/netwerk/socket',
'/security/pkix/include'
]
if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
LOCAL_INCLUDES += [
'/netwerk/protocol/rtsp/controller',
'/netwerk/protocol/rtsp/rtsp',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -0,0 +1,167 @@
/* 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 nsIURI;
interface mozIDOMWindowProxy;
interface nsIChannel;
/**
* Utility functions for determining whether a given URI, channel, or window
* hierarchy is third party with respect to a known URI.
*/
[scriptable, uuid(fd82700e-ffb4-4932-b7d6-08f0b5697dda)]
interface mozIThirdPartyUtil : nsISupports
{
/**
* isThirdPartyURI
*
* Determine whether two URIs are third party with respect to each other.
* This is determined by computing the base domain for both URIs. If they can
* be determined, and the base domains match, the request is defined as first
* party. If it cannot be determined because one or both URIs do not have a
* base domain (for instance, in the case of IP addresses, host aliases such
* as 'localhost', or a file:// URI), an exact string comparison on host is
* performed.
*
* For example, the URI "http://mail.google.com/" is not third party with
* respect to "http://images.google.com/", but "http://mail.yahoo.com/" and
* "http://192.168.1.1/" are.
*
* @return true if aFirstURI is third party with respect to aSecondURI.
*
* @throws if either URI is null, has a malformed host, or has an empty host
* and is not a file:// URI.
*/
boolean isThirdPartyURI(in nsIURI aFirstURI, in nsIURI aSecondURI);
/**
* isThirdPartyWindow
*
* Determine whether the given window hierarchy is third party. This is done
* as follows:
*
* 1) Obtain the URI of the principal associated with 'aWindow'. Call this the
* 'bottom URI'.
* 2) If 'aURI' is provided, determine if it is third party with respect to
* the bottom URI. If so, return.
* 3) Find the same-type parent window, if there is one, and its URI.
* Determine whether it is third party with respect to the bottom URI. If
* so, return.
*
* Therefore, each level in the window hierarchy is tested. (This means that
* nested iframes with different base domains, even though the bottommost and
* topmost URIs might be equal, will be considered third party.)
*
* @param aWindow
* The bottommost window in the hierarchy.
* @param aURI
* A URI to test against. If null, the URI of the principal
* associated with 'aWindow' will be used.
*
* For example, if 'aURI' is "http://mail.google.com/", 'aWindow' has a URI
* of "http://google.com/", and its parent is the topmost content window with
* a URI of "http://mozilla.com", the result will be true.
*
* @return true if 'aURI' is third party with respect to any of the URIs
* associated with aWindow and its same-type parents.
*
* @throws if aWindow is null; the same-type parent of any window in the
* hierarchy cannot be determined; or the URI associated with any
* window in the hierarchy is null, has a malformed host, or has an
* empty host and is not a file:// URI.
*
* @see isThirdPartyURI
*/
boolean isThirdPartyWindow(in mozIDOMWindowProxy aWindow, [optional] in nsIURI aURI);
/**
* isThirdPartyChannel
*
* Determine whether the given channel and its content window hierarchy is
* third party. This is done as follows:
*
* 1) If 'aChannel' is an nsIHttpChannel and has the
* 'forceAllowThirdPartyCookie' property set, then:
* a) If 'aURI' is null, return false.
* b) Otherwise, find the URI of the channel, determine whether it is
* foreign with respect to 'aURI', and return.
* 2) Find the URI of the channel and determine whether it is third party with
* respect to the URI of the channel. If so, return.
* 3) Obtain the bottommost nsIDOMWindow, and its same-type parent if it
* exists, from the channel's notification callbacks. Then:
* a) If the parent is the same as the bottommost window, and the channel
* has the LOAD_DOCUMENT_URI flag set, return false. This represents the
* case where a toplevel load is occurring and the window's URI has not
* yet been updated. (We have already checked that 'aURI' is not foreign
* with respect to the channel URI.)
* b) Otherwise, return the result of isThirdPartyWindow with arguments
* of the channel's bottommost window and the channel URI, respectively.
*
* Therefore, both the channel's URI and each level in the window hierarchy
* associated with the channel is tested.
*
* @param aChannel
* The channel associated with the load.
* @param aURI
* A URI to test against. If null, the URI of the channel will be used.
*
* For example, if 'aURI' is "http://mail.google.com/", 'aChannel' has a URI
* of "http://google.com/", and its parent is the topmost content window with
* a URI of "http://mozilla.com", the result will be true.
*
* @return true if aURI is third party with respect to the channel URI or any
* of the URIs associated with the same-type window hierarchy of the
* channel.
*
* @throws if 'aChannel' is null; the channel has no notification callbacks or
* an associated window; or isThirdPartyWindow throws.
*
* @see isThirdPartyWindow
*/
boolean isThirdPartyChannel(in nsIChannel aChannel, [optional] in nsIURI aURI);
/**
* getBaseDomain
*
* Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
* "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
* dot may be present. If aHostURI is an IP address, an alias such as
* 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
* be the exact host. The result of this function should only be used in exact
* string comparisons, since substring comparisons will not be valid for the
* special cases elided above.
*
* @param aHostURI
* The URI to analyze.
*
* @return the base domain.
*/
AUTF8String getBaseDomain(in nsIURI aHostURI);
/**
* getURIFromWindow
*
* Returns the URI associated with the script object principal for the
* window.
*/
nsIURI getURIFromWindow(in mozIDOMWindowProxy aWindow);
/**
* getTopWindowForChannel
*
* Returns the top-level window associated with the given channel.
*/
mozIDOMWindowProxy getTopWindowForChannel(in nsIChannel aChannel);
};
%{ C++
/**
* The mozIThirdPartyUtil implementation is an XPCOM service registered
* under the ContractID:
*/
#define THIRDPARTYUTIL_CONTRACTID "@mozilla.org/thirdpartyutil;1"
%}

14
netwerk/base/netCore.h Normal file
View file

@ -0,0 +1,14 @@
/* -*- Mode: C++; 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/. */
#ifndef __netCore_h__
#define __netCore_h__
#include "nsError.h"
// Where most necko status messages come from:
#define NECKO_MSGS_URL "chrome://necko/locale/necko.properties"
#endif // __netCore_h__

View file

@ -0,0 +1,96 @@
/* 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 nsASocketHandler_h__
#define nsASocketHandler_h__
// socket handler used by nsISocketTransportService.
// methods are only called on the socket thread.
class nsASocketHandler : public nsISupports
{
public:
nsASocketHandler()
: mCondition(NS_OK)
, mPollFlags(0)
, mPollTimeout(UINT16_MAX)
, mIsPrivate(false)
{}
//
// this condition variable will be checked to determine if the socket
// handler should be detached. it must only be accessed on the socket
// thread.
//
nsresult mCondition;
//
// these flags can only be modified on the socket transport thread.
// the socket transport service will check these flags before calling
// PR_Poll.
//
uint16_t mPollFlags;
//
// this value specifies the maximum amount of time in seconds that may be
// spent waiting for activity on this socket. if this timeout is reached,
// then OnSocketReady will be called with outFlags = -1.
//
// the default value for this member is UINT16_MAX, which disables the
// timeout error checking. (i.e., a timeout value of UINT16_MAX is
// never reached.)
//
uint16_t mPollTimeout;
bool mIsPrivate;
//
// called to service a socket
//
// params:
// socketRef - socket identifier
// fd - socket file descriptor
// outFlags - value of PR_PollDesc::out_flags after PR_Poll returns
// or -1 if a timeout occurred
//
virtual void OnSocketReady(PRFileDesc *fd, int16_t outFlags) = 0;
//
// called when a socket is no longer under the control of the socket
// transport service. the socket handler may close the socket at this
// point. after this call returns, the handler will no longer be owned
// by the socket transport service.
//
virtual void OnSocketDetached(PRFileDesc *fd) = 0;
//
// called to determine if the socket is for a local peer.
// when used for server sockets, indicates if it only accepts local
// connections.
//
virtual void IsLocal(bool *aIsLocal) = 0;
//
// called to determine if this socket should not be terminated when Gecko
// is turned offline. This is mostly useful for the debugging server
// socket.
//
virtual void KeepWhenOffline(bool *aKeepWhenOffline)
{
*aKeepWhenOffline = false;
}
//
// called when global pref for keepalive has changed.
//
virtual void OnKeepaliveEnabledPrefChange(bool aEnabled) { }
//
// returns the number of bytes sent/transmitted over the socket
//
virtual uint64_t ByteCountSent() = 0;
virtual uint64_t ByteCountReceived() = 0;
};
#endif // !nsASocketHandler_h__

View file

@ -0,0 +1,288 @@
/* -*- 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 "mozilla/Logging.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsIOService.h"
#include "nsIChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsILoadInfo.h"
namespace mozilla {
namespace net {
static LazyLogModule gRedirectLog("nsRedirect");
#undef LOG
#define LOG(args) MOZ_LOG(gRedirectLog, LogLevel::Debug, args)
NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper,
nsIAsyncVerifyRedirectCallback,
nsIRunnable)
class nsAsyncVerifyRedirectCallbackEvent : public Runnable {
public:
nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
nsresult result)
: mCallback(cb), mResult(result) {
}
NS_IMETHOD Run() override
{
LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
"callback to %p with result %x",
mCallback.get(), mResult));
(void) mCallback->OnRedirectVerifyCallback(mResult);
return NS_OK;
}
private:
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
nsresult mResult;
};
nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
: mFlags(0),
mWaitingForRedirectCallback(false),
mCallbackInitiated(false),
mExpectedCallbacks(0),
mResult(NS_OK)
{
}
nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
{
NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
"Did not receive all required callbacks!");
}
nsresult
nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
uint32_t flags, bool synchronize)
{
LOG(("nsAsyncRedirectVerifyHelper::Init() "
"oldChan=%p newChan=%p", oldChan, newChan));
mOldChan = oldChan;
mNewChan = newChan;
mFlags = flags;
mCallbackThread = do_GetCurrentThread();
if (!(flags & (nsIChannelEventSink::REDIRECT_INTERNAL |
nsIChannelEventSink::REDIRECT_STS_UPGRADE))) {
nsCOMPtr<nsILoadInfo> loadInfo = oldChan->GetLoadInfo();
if (loadInfo && loadInfo->GetDontFollowRedirects()) {
ExplicitCallback(NS_BINDING_ABORTED);
return NS_OK;
}
}
if (synchronize)
mWaitingForRedirectCallback = true;
nsresult rv;
rv = NS_DispatchToMainThread(this);
NS_ENSURE_SUCCESS(rv, rv);
if (synchronize) {
nsIThread *thread = NS_GetCurrentThread();
while (mWaitingForRedirectCallback) {
if (!NS_ProcessNextEvent(thread)) {
return NS_ERROR_UNEXPECTED;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
{
LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
"result=%x expectedCBs=%u mResult=%x",
result, mExpectedCallbacks, mResult));
MOZ_DIAGNOSTIC_ASSERT(mExpectedCallbacks > 0,
"OnRedirectVerifyCallback called more times than expected");
if (mExpectedCallbacks <= 0) {
return NS_ERROR_UNEXPECTED;
}
--mExpectedCallbacks;
// If response indicates failure we may call back immediately
if (NS_FAILED(result)) {
// We chose to store the first failure-value (as opposed to the last)
if (NS_SUCCEEDED(mResult))
mResult = result;
// If InitCallback() has been called, just invoke the callback and
// return. Otherwise it will be invoked from InitCallback()
if (mCallbackInitiated) {
ExplicitCallback(mResult);
return NS_OK;
}
}
// If the expected-counter is in balance and InitCallback() was called, all
// sinks have agreed that the redirect is ok and we can invoke our callback
if (mCallbackInitiated && mExpectedCallbacks == 0) {
ExplicitCallback(mResult);
}
return NS_OK;
}
nsresult
nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
nsIChannel *oldChannel,
nsIChannel *newChannel,
uint32_t flags)
{
LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
"sink=%p expectedCBs=%u mResult=%x",
sink, mExpectedCallbacks, mResult));
++mExpectedCallbacks;
if (IsOldChannelCanceled()) {
LOG((" old channel has been canceled, cancel the redirect by "
"emulating OnRedirectVerifyCallback..."));
(void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
return NS_BINDING_ABORTED;
}
nsresult rv =
sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
LOG((" result=%x expectedCBs=%u", rv, mExpectedCallbacks));
// If the sink returns failure from this call the redirect is vetoed. We
// emulate a callback from the sink in this case in order to perform all
// the necessary logic.
if (NS_FAILED(rv)) {
LOG((" emulating OnRedirectVerifyCallback..."));
(void) OnRedirectVerifyCallback(rv);
}
return rv; // Return the actual status since our caller may need it
}
void
nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
{
LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
"result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
result, mExpectedCallbacks, mCallbackInitiated, mResult));
nsCOMPtr<nsIAsyncVerifyRedirectCallback>
callback(do_QueryInterface(mOldChan));
if (!callback || !mCallbackThread) {
LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
"callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get()));
return;
}
mCallbackInitiated = false; // reset to ensure only one callback
mWaitingForRedirectCallback = false;
// Now, dispatch the callback on the event-target which called Init()
nsCOMPtr<nsIRunnable> event =
new nsAsyncVerifyRedirectCallbackEvent(callback, result);
if (!event) {
NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
"failed creating callback event!");
return;
}
nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
"failed dispatching callback event!");
} else {
LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
"dispatched callback event=%p", event.get()));
}
}
void
nsAsyncRedirectVerifyHelper::InitCallback()
{
LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
"expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
mCallbackInitiated = true;
// Invoke the callback if we are done
if (mExpectedCallbacks == 0)
ExplicitCallback(mResult);
}
NS_IMETHODIMP
nsAsyncRedirectVerifyHelper::Run()
{
/* If the channel got canceled after it fired AsyncOnChannelRedirect
* and before we got here, mostly because docloader load has been canceled,
* we must completely ignore this notification and prevent any further
* notification.
*/
if (IsOldChannelCanceled()) {
ExplicitCallback(NS_BINDING_ABORTED);
return NS_OK;
}
// First, the global observer
NS_ASSERTION(gIOService, "Must have an IO service at this point");
LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
mFlags, this);
if (NS_FAILED(rv)) {
ExplicitCallback(rv);
return NS_OK;
}
// Now, the per-channel observers
nsCOMPtr<nsIChannelEventSink> sink;
NS_QueryNotificationCallbacks(mOldChan, sink);
if (sink) {
LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
}
// All invocations to AsyncOnChannelRedirect has been done - call
// InitCallback() to flag this
InitCallback();
return NS_OK;
}
bool
nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
{
bool canceled;
nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
do_QueryInterface(mOldChan);
if (oldChannelInternal) {
oldChannelInternal->GetCanceled(&canceled);
if (canceled) {
return true;
}
} else if (mOldChan) {
// For non-HTTP channels check on the status, failure
// indicates the channel has probably been canceled.
nsresult status = NS_ERROR_FAILURE;
mOldChan->GetStatus(&status);
if (NS_FAILED(status)) {
return true;
}
}
return false;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,129 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAsyncRedirectVerifyHelper_h
#define nsAsyncRedirectVerifyHelper_h
#include "nsIRunnable.h"
#include "nsIThread.h"
#include "nsIChannelEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
class nsIChannel;
namespace mozilla {
namespace net {
/**
* This class simplifies call of OnChannelRedirect of IOService and
* the sink bound with the channel being redirected while the result of
* redirect decision is returned through the callback.
*/
class nsAsyncRedirectVerifyHelper final : public nsIRunnable,
public nsIAsyncVerifyRedirectCallback
{
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
public:
nsAsyncRedirectVerifyHelper();
/*
* Calls AsyncOnChannelRedirect() on the given sink with the given
* channels and flags. Keeps track of number of async callbacks to expect.
*/
nsresult DelegateOnChannelRedirect(nsIChannelEventSink *sink,
nsIChannel *oldChannel,
nsIChannel *newChannel,
uint32_t flags);
/**
* Initialize and run the chain of AsyncOnChannelRedirect calls. OldChannel
* is QI'ed for nsIAsyncVerifyRedirectCallback. The result of the redirect
* decision is passed through this interface back to the oldChannel.
*
* @param oldChan
* channel being redirected, MUST implement
* nsIAsyncVerifyRedirectCallback
* @param newChan
* target of the redirect channel
* @param flags
* redirect flags
* @param synchronize
* set to TRUE if you want the Init method wait synchronously for
* all redirect callbacks
*/
nsresult Init(nsIChannel* oldChan,
nsIChannel* newChan,
uint32_t flags,
bool synchronize = false);
protected:
nsCOMPtr<nsIChannel> mOldChan;
nsCOMPtr<nsIChannel> mNewChan;
uint32_t mFlags;
bool mWaitingForRedirectCallback;
nsCOMPtr<nsIThread> mCallbackThread;
bool mCallbackInitiated;
int32_t mExpectedCallbacks;
nsresult mResult; // value passed to callback
void InitCallback();
/**
* Calls back to |oldChan| as described in Init()
*/
void ExplicitCallback(nsresult result);
private:
~nsAsyncRedirectVerifyHelper();
bool IsOldChannelCanceled();
};
/*
* Helper to make the call-stack handle some control-flow for us
*/
class nsAsyncRedirectAutoCallback
{
public:
explicit nsAsyncRedirectAutoCallback(nsIAsyncVerifyRedirectCallback* aCallback)
: mCallback(aCallback)
{
mResult = NS_OK;
}
~nsAsyncRedirectAutoCallback()
{
if (mCallback)
mCallback->OnRedirectVerifyCallback(mResult);
}
/*
* Call this is you want it to call back with a different result-code
*/
void SetResult(nsresult aRes)
{
mResult = aRes;
}
/*
* Call this is you want to avoid the callback
*/
void DontCallback()
{
mCallback = nullptr;
}
private:
nsIAsyncVerifyRedirectCallback* mCallback;
nsresult mResult;
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -0,0 +1,419 @@
/* 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 "nsAsyncStreamCopier.h"
#include "nsIOService.h"
#include "nsIEventTarget.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsNetCID.h"
#include "nsIBufferedStreams.h"
#include "nsIRequestObserver.h"
#include "mozilla/Logging.h"
using namespace mozilla;
#undef LOG
//
// MOZ_LOG=nsStreamCopier:5
//
static LazyLogModule gStreamCopierLog("nsStreamCopier");
#define LOG(args) MOZ_LOG(gStreamCopierLog, mozilla::LogLevel::Debug, args)
/**
* An event used to perform initialization off the main thread.
*/
class AsyncApplyBufferingPolicyEvent final: public Runnable
{
public:
/**
* @param aCopier
* The nsAsyncStreamCopier requesting the information.
*/
explicit AsyncApplyBufferingPolicyEvent(nsAsyncStreamCopier* aCopier)
: mCopier(aCopier)
, mTarget(NS_GetCurrentThread())
{ }
NS_IMETHOD Run() override
{
nsresult rv = mCopier->ApplyBufferingPolicy();
if (NS_FAILED(rv)) {
mCopier->Cancel(rv);
return NS_OK;
}
rv = mTarget->Dispatch(NewRunnableMethod(mCopier,
&nsAsyncStreamCopier::AsyncCopyInternal),
NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
mCopier->Cancel(rv);
}
return NS_OK;
}
private:
RefPtr<nsAsyncStreamCopier> mCopier;
nsCOMPtr<nsIEventTarget> mTarget;
};
//-----------------------------------------------------------------------------
nsAsyncStreamCopier::nsAsyncStreamCopier()
: mLock("nsAsyncStreamCopier.mLock")
, mMode(NS_ASYNCCOPY_VIA_READSEGMENTS)
, mChunkSize(nsIOService::gDefaultSegmentSize)
, mStatus(NS_OK)
, mIsPending(false)
, mShouldSniffBuffering(false)
{
LOG(("Creating nsAsyncStreamCopier @%x\n", this));
}
nsAsyncStreamCopier::~nsAsyncStreamCopier()
{
LOG(("Destroying nsAsyncStreamCopier @%x\n", this));
}
bool
nsAsyncStreamCopier::IsComplete(nsresult *status)
{
MutexAutoLock lock(mLock);
if (status)
*status = mStatus;
return !mIsPending;
}
nsIRequest*
nsAsyncStreamCopier::AsRequest()
{
return static_cast<nsIRequest*>(static_cast<nsIAsyncStreamCopier*>(this));
}
void
nsAsyncStreamCopier::Complete(nsresult status)
{
LOG(("nsAsyncStreamCopier::Complete [this=%p status=%x]\n", this, status));
nsCOMPtr<nsIRequestObserver> observer;
nsCOMPtr<nsISupports> ctx;
{
MutexAutoLock lock(mLock);
mCopierCtx = nullptr;
if (mIsPending) {
mIsPending = false;
mStatus = status;
// setup OnStopRequest callback and release references...
observer = mObserver;
mObserver = nullptr;
}
}
if (observer) {
LOG((" calling OnStopRequest [status=%x]\n", status));
observer->OnStopRequest(AsRequest(), ctx, status);
}
}
void
nsAsyncStreamCopier::OnAsyncCopyComplete(void *closure, nsresult status)
{
nsAsyncStreamCopier *self = (nsAsyncStreamCopier *) closure;
self->Complete(status);
NS_RELEASE(self); // addref'd in AsyncCopy
}
//-----------------------------------------------------------------------------
// nsISupports
// We cannot use simply NS_IMPL_ISUPPORTSx as both
// nsIAsyncStreamCopier and nsIAsyncStreamCopier2 implement nsIRequest
NS_IMPL_ADDREF(nsAsyncStreamCopier)
NS_IMPL_RELEASE(nsAsyncStreamCopier)
NS_INTERFACE_TABLE_HEAD(nsAsyncStreamCopier)
NS_INTERFACE_TABLE_BEGIN
NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier)
NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier2)
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsIRequest, nsIAsyncStreamCopier)
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsISupports, nsIAsyncStreamCopier)
NS_INTERFACE_TABLE_END
NS_INTERFACE_TABLE_TAIL
//-----------------------------------------------------------------------------
// nsIRequest
NS_IMETHODIMP
nsAsyncStreamCopier::GetName(nsACString &name)
{
name.Truncate();
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::IsPending(bool *result)
{
*result = !IsComplete();
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::GetStatus(nsresult *status)
{
IsComplete(status);
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::Cancel(nsresult status)
{
nsCOMPtr<nsISupports> copierCtx;
{
MutexAutoLock lock(mLock);
if (!mIsPending)
return NS_OK;
copierCtx.swap(mCopierCtx);
}
if (NS_SUCCEEDED(status)) {
NS_WARNING("cancel with non-failure status code");
status = NS_BASE_STREAM_CLOSED;
}
if (copierCtx)
NS_CancelAsyncCopy(copierCtx, status);
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::Suspend()
{
NS_NOTREACHED("nsAsyncStreamCopier::Suspend");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsAsyncStreamCopier::Resume()
{
NS_NOTREACHED("nsAsyncStreamCopier::Resume");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsAsyncStreamCopier::GetLoadFlags(nsLoadFlags *aLoadFlags)
{
*aLoadFlags = LOAD_NORMAL;
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::SetLoadFlags(nsLoadFlags aLoadFlags)
{
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::GetLoadGroup(nsILoadGroup **aLoadGroup)
{
*aLoadGroup = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup *aLoadGroup)
{
return NS_OK;
}
nsresult
nsAsyncStreamCopier::InitInternal(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
uint32_t chunkSize,
bool closeSource,
bool closeSink)
{
NS_ASSERTION(!mSource && !mSink, "Init() called more than once");
if (chunkSize == 0) {
chunkSize = nsIOService::gDefaultSegmentSize;
}
mChunkSize = chunkSize;
mSource = source;
mSink = sink;
mCloseSource = closeSource;
mCloseSink = closeSink;
if (target) {
mTarget = target;
} else {
nsresult rv;
mTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsIAsyncStreamCopier
NS_IMETHODIMP
nsAsyncStreamCopier::Init(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
bool sourceBuffered,
bool sinkBuffered,
uint32_t chunkSize,
bool closeSource,
bool closeSink)
{
NS_ASSERTION(sourceBuffered || sinkBuffered, "at least one stream must be buffered");
mMode = sourceBuffered ? NS_ASYNCCOPY_VIA_READSEGMENTS
: NS_ASYNCCOPY_VIA_WRITESEGMENTS;
return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
}
//-----------------------------------------------------------------------------
// nsIAsyncStreamCopier2
NS_IMETHODIMP
nsAsyncStreamCopier::Init(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
uint32_t chunkSize,
bool closeSource,
bool closeSink)
{
mShouldSniffBuffering = true;
return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
}
/**
* Detect whether the input or the output stream is buffered,
* bufferize one of them if neither is buffered.
*/
nsresult
nsAsyncStreamCopier::ApplyBufferingPolicy()
{
// This function causes I/O, it must not be executed on the main
// thread.
MOZ_ASSERT(!NS_IsMainThread());
if (NS_OutputStreamIsBuffered(mSink)) {
// Sink is buffered, no need to perform additional buffering
mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
return NS_OK;
}
if (NS_InputStreamIsBuffered(mSource)) {
// Source is buffered, no need to perform additional buffering
mMode = NS_ASYNCCOPY_VIA_READSEGMENTS;
return NS_OK;
}
// No buffering, let's buffer the sink
nsresult rv;
nsCOMPtr<nsIBufferedOutputStream> sink =
do_CreateInstance(NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
rv = sink->Init(mSink, mChunkSize);
if (NS_FAILED(rv)) {
return rv;
}
mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
mSink = sink;
return NS_OK;
}
//-----------------------------------------------------------------------------
// Both nsIAsyncStreamCopier and nsIAsyncStreamCopier2
NS_IMETHODIMP
nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx)
{
LOG(("nsAsyncStreamCopier::AsyncCopy [this=%p observer=%x]\n", this, observer));
NS_ASSERTION(mSource && mSink, "not initialized");
nsresult rv;
if (observer) {
// build proxy for observer events
rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), observer, ctx);
if (NS_FAILED(rv)) return rv;
}
// from this point forward, AsyncCopy is going to return NS_OK. any errors
// will be reported via OnStopRequest.
mIsPending = true;
if (mObserver) {
rv = mObserver->OnStartRequest(AsRequest(), nullptr);
if (NS_FAILED(rv))
Cancel(rv);
}
if (!mShouldSniffBuffering) {
// No buffer sniffing required, let's proceed
AsyncCopyInternal();
return NS_OK;
}
if (NS_IsMainThread()) {
// Don't perform buffer sniffing on the main thread
nsCOMPtr<nsIRunnable> event = new AsyncApplyBufferingPolicyEvent(this);
rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
Cancel(rv);
}
return NS_OK;
}
// We're not going to block the main thread, so let's sniff here
rv = ApplyBufferingPolicy();
if (NS_FAILED(rv)) {
Cancel(rv);
}
AsyncCopyInternal();
return NS_OK;
}
// Launch async copy.
// All errors are reported through the observer.
void
nsAsyncStreamCopier::AsyncCopyInternal()
{
MOZ_ASSERT(mMode == NS_ASYNCCOPY_VIA_READSEGMENTS
|| mMode == NS_ASYNCCOPY_VIA_WRITESEGMENTS);
nsresult rv;
// we want to receive progress notifications; release happens in
// OnAsyncCopyComplete.
NS_ADDREF_THIS();
{
MutexAutoLock lock(mLock);
rv = NS_AsyncCopy(mSource, mSink, mTarget, mMode, mChunkSize,
OnAsyncCopyComplete, this, mCloseSource, mCloseSink,
getter_AddRefs(mCopierCtx));
}
if (NS_FAILED(rv)) {
NS_RELEASE_THIS();
Cancel(rv);
}
}

View file

@ -0,0 +1,83 @@
/* 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 nsAsyncStreamCopier_h__
#define nsAsyncStreamCopier_h__
#include "nsIAsyncStreamCopier.h"
#include "nsIAsyncStreamCopier2.h"
#include "mozilla/Mutex.h"
#include "nsStreamUtils.h"
#include "nsCOMPtr.h"
class nsIRequestObserver;
//-----------------------------------------------------------------------------
class nsAsyncStreamCopier final : public nsIAsyncStreamCopier,
nsIAsyncStreamCopier2
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUEST
NS_DECL_NSIASYNCSTREAMCOPIER
// nsIAsyncStreamCopier2
// We declare it by hand instead of NS_DECL_NSIASYNCSTREAMCOPIER2
// as nsIAsyncStreamCopier2 duplicates methods of nsIAsyncStreamCopier
NS_IMETHOD Init(nsIInputStream *aSource,
nsIOutputStream *aSink,
nsIEventTarget *aTarget,
uint32_t aChunkSize,
bool aCloseSource,
bool aCloseSink) override;
nsAsyncStreamCopier();
//-------------------------------------------------------------------------
// these methods may be called on any thread
bool IsComplete(nsresult *status = nullptr);
void Complete(nsresult status);
private:
virtual ~nsAsyncStreamCopier();
nsresult InitInternal(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
uint32_t chunkSize,
bool closeSource,
bool closeSink);
static void OnAsyncCopyComplete(void *, nsresult);
void AsyncCopyInternal();
nsresult ApplyBufferingPolicy();
nsIRequest* AsRequest();
nsCOMPtr<nsIInputStream> mSource;
nsCOMPtr<nsIOutputStream> mSink;
nsCOMPtr<nsIRequestObserver> mObserver;
nsCOMPtr<nsIEventTarget> mTarget;
nsCOMPtr<nsISupports> mCopierCtx;
mozilla::Mutex mLock;
nsAsyncCopyMode mMode;
uint32_t mChunkSize;
nsresult mStatus;
bool mIsPending;
bool mCloseSource;
bool mCloseSink;
bool mShouldSniffBuffering;
friend class ProceedWithAsyncCopy;
friend class AsyncApplyBufferingPolicyEvent;
};
#endif // !nsAsyncStreamCopier_h__

View file

@ -0,0 +1,74 @@
/* 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 "nsAuthInformationHolder.h"
NS_IMPL_ISUPPORTS(nsAuthInformationHolder, nsIAuthInformation)
NS_IMETHODIMP
nsAuthInformationHolder::GetFlags(uint32_t* aFlags)
{
*aFlags = mFlags;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::GetRealm(nsAString& aRealm)
{
aRealm = mRealm;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::GetAuthenticationScheme(nsACString& aScheme)
{
aScheme = mAuthType;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::GetUsername(nsAString& aUserName)
{
aUserName = mUser;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::SetUsername(const nsAString& aUserName)
{
if (!(mFlags & ONLY_PASSWORD))
mUser = aUserName;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::GetPassword(nsAString& aPassword)
{
aPassword = mPassword;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::SetPassword(const nsAString& aPassword)
{
mPassword = aPassword;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::GetDomain(nsAString& aDomain)
{
aDomain = mDomain;
return NS_OK;
}
NS_IMETHODIMP
nsAuthInformationHolder::SetDomain(const nsAString& aDomain)
{
if (mFlags & NEED_DOMAIN)
mDomain = aDomain;
return NS_OK;
}

View file

@ -0,0 +1,48 @@
/* 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 NSAUTHINFORMATIONHOLDER_H_
#define NSAUTHINFORMATIONHOLDER_H_
#include "nsIAuthInformation.h"
#include "nsString.h"
class nsAuthInformationHolder : public nsIAuthInformation {
protected:
virtual ~nsAuthInformationHolder() {}
public:
// aAuthType must be ASCII
nsAuthInformationHolder(uint32_t aFlags, const nsString& aRealm,
const nsCString& aAuthType)
: mFlags(aFlags), mRealm(aRealm), mAuthType(aAuthType) {}
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTHINFORMATION
const nsString& User() const { return mUser; }
const nsString& Password() const { return mPassword; }
const nsString& Domain() const { return mDomain; }
/**
* This method can be used to initialize the username when the
* ONLY_PASSWORD flag is set.
*/
void SetUserInternal(const nsString& aUsername) {
mUser = aUsername;
}
private:
nsString mUser;
nsString mPassword;
nsString mDomain;
uint32_t mFlags;
nsString mRealm;
nsCString mAuthType;
};
#endif

View file

@ -0,0 +1,67 @@
/* 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 "nsBase64Encoder.h"
#include "plbase64.h"
#include "prmem.h"
NS_IMPL_ISUPPORTS(nsBase64Encoder, nsIOutputStream)
NS_IMETHODIMP
nsBase64Encoder::Close()
{
return NS_OK;
}
NS_IMETHODIMP
nsBase64Encoder::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
nsBase64Encoder::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval)
{
mData.Append(aBuf, aCount);
*_retval = aCount;
return NS_OK;
}
NS_IMETHODIMP
nsBase64Encoder::WriteFrom(nsIInputStream* aStream, uint32_t aCount,
uint32_t* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsBase64Encoder::WriteSegments(nsReadSegmentFun aReader,
void* aClosure,
uint32_t aCount,
uint32_t* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsBase64Encoder::IsNonBlocking(bool* aNonBlocking)
{
*aNonBlocking = false;
return NS_OK;
}
nsresult
nsBase64Encoder::Finish(nsCSubstring& result)
{
char* b64 = PL_Base64Encode(mData.get(), mData.Length(), nullptr);
if (!b64)
return NS_ERROR_OUT_OF_MEMORY;
result.Assign(b64);
PR_Free(b64);
// Free unneeded memory and allow reusing the object
mData.Truncate();
return NS_OK;
}

View file

@ -0,0 +1,32 @@
/* 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 NSBASE64ENCODER_H_
#define NSBASE64ENCODER_H_
#include "nsIOutputStream.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
/**
* A base64 encoder. Usage: Instantiate class, write to it using
* Write(), then call Finish() to get the base64-encoded data.
*/
class nsBase64Encoder final : public nsIOutputStream {
public:
nsBase64Encoder() {}
NS_DECL_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
nsresult Finish(nsCSubstring& _result);
private:
~nsBase64Encoder() {}
/// The data written to this stream. nsCString can deal fine with
/// binary data.
nsCString mData;
};
#endif

View file

@ -0,0 +1,937 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 sts=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsBaseChannel.h"
#include "nsContentUtils.h"
#include "nsURLHelper.h"
#include "nsNetCID.h"
#include "nsMimeTypes.h"
#include "nsIContentSniffer.h"
#include "nsIScriptSecurityManager.h"
#include "nsMimeTypes.h"
#include "nsIHttpEventSink.h"
#include "nsIHttpChannel.h"
#include "nsIChannelEventSink.h"
#include "nsIStreamConverterService.h"
#include "nsChannelClassifier.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsProxyRelease.h"
#include "nsXULAppAPI.h"
#include "nsContentSecurityManager.h"
#include "LoadInfo.h"
#include "nsServiceManagerUtils.h"
// This class is used to suspend a request across a function scope.
class ScopedRequestSuspender {
public:
explicit ScopedRequestSuspender(nsIRequest *request)
: mRequest(request) {
if (mRequest && NS_FAILED(mRequest->Suspend())) {
NS_WARNING("Couldn't suspend pump");
mRequest = nullptr;
}
}
~ScopedRequestSuspender() {
if (mRequest)
mRequest->Resume();
}
private:
nsIRequest *mRequest;
};
// Used to suspend data events from mPump within a function scope. This is
// usually needed when a function makes callbacks that could process events.
#define SUSPEND_PUMP_FOR_SCOPE() \
ScopedRequestSuspender pump_suspender__(mPump)
//-----------------------------------------------------------------------------
// nsBaseChannel
nsBaseChannel::nsBaseChannel()
: mLoadFlags(LOAD_NORMAL)
, mQueriedProgressSink(true)
, mSynthProgressEvents(false)
, mAllowThreadRetargeting(true)
, mWaitingOnAsyncRedirect(false)
, mStatus(NS_OK)
, mContentDispositionHint(UINT32_MAX)
, mContentLength(-1)
, mWasOpened(false)
{
mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
}
nsBaseChannel::~nsBaseChannel()
{
NS_ReleaseOnMainThread(mLoadInfo.forget());
}
nsresult
nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
bool openNewChannel)
{
SUSPEND_PUMP_FOR_SCOPE();
// Transfer properties
newChannel->SetLoadGroup(mLoadGroup);
newChannel->SetNotificationCallbacks(mCallbacks);
newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE);
// make a copy of the loadinfo, append to the redirectchain
// and set it on the new channel
if (mLoadInfo) {
nsSecurityFlags secFlags = mLoadInfo->GetSecurityFlags() &
~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
nsCOMPtr<nsILoadInfo> newLoadInfo =
static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->CloneWithNewSecFlags(secFlags);
nsCOMPtr<nsIPrincipal> uriPrincipal;
nsIScriptSecurityManager *sm = nsContentUtils::GetSecurityManager();
sm->GetChannelURIPrincipal(this, getter_AddRefs(uriPrincipal));
bool isInternalRedirect =
(redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
nsIChannelEventSink::REDIRECT_STS_UPGRADE));
newLoadInfo->AppendRedirectedPrincipal(uriPrincipal, isInternalRedirect);
newChannel->SetLoadInfo(newLoadInfo);
}
else {
// the newChannel was created with a dummy loadInfo, we should clear
// it in case the original channel does not have a loadInfo
newChannel->SetLoadInfo(nullptr);
}
// Preserve the privacy bit if it has been overridden
if (mPrivateBrowsingOverriden) {
nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
do_QueryInterface(newChannel);
if (newPBChannel) {
newPBChannel->SetPrivate(mPrivateBrowsing);
}
}
nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel);
if (bag) {
for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
bag->SetProperty(iter.Key(), iter.UserData());
}
}
// Notify consumer, giving chance to cancel redirect. For backwards compat,
// we support nsIHttpEventSink if we are an HTTP channel and if this is not
// an internal redirect.
RefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
new nsAsyncRedirectVerifyHelper();
bool checkRedirectSynchronously = !openNewChannel;
mRedirectChannel = newChannel;
mRedirectFlags = redirectFlags;
mOpenRedirectChannel = openNewChannel;
nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags,
checkRedirectSynchronously);
if (NS_FAILED(rv))
return rv;
if (checkRedirectSynchronously && NS_FAILED(mStatus))
return mStatus;
return NS_OK;
}
nsresult
nsBaseChannel::ContinueRedirect()
{
// Backwards compat for non-internal redirects from a HTTP channel.
// XXX Is our http channel implementation going to derive from nsBaseChannel?
// If not, this code can be removed.
if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface();
if (httpChannel) {
nsCOMPtr<nsIHttpEventSink> httpEventSink;
GetCallback(httpEventSink);
if (httpEventSink) {
nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel);
if (NS_FAILED(rv)) {
return rv;
}
}
}
}
// Make sure to do this _after_ making all the OnChannelRedirect calls
mRedirectChannel->SetOriginalURI(OriginalURI());
// If we fail to open the new channel, then we want to leave this channel
// unaffected, so we defer tearing down our channel until we have succeeded
// with the redirect.
if (mOpenRedirectChannel) {
nsresult rv = NS_OK;
if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
rv = mRedirectChannel->AsyncOpen2(mListener);
}
else {
rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
}
NS_ENSURE_SUCCESS(rv, rv);
}
mRedirectChannel = nullptr;
// close down this channel
Cancel(NS_BINDING_REDIRECTED);
ChannelDone();
return NS_OK;
}
bool
nsBaseChannel::HasContentTypeHint() const
{
NS_ASSERTION(!Pending(), "HasContentTypeHint called too late");
return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE);
}
nsresult
nsBaseChannel::PushStreamConverter(const char *fromType,
const char *toType,
bool invalidatesContentLength,
nsIStreamListener **result)
{
NS_ASSERTION(mListener, "no listener");
nsresult rv;
nsCOMPtr<nsIStreamConverterService> scs =
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIStreamListener> converter;
rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext,
getter_AddRefs(converter));
if (NS_SUCCEEDED(rv)) {
mListener = converter;
if (invalidatesContentLength)
mContentLength = -1;
if (result) {
*result = nullptr;
converter.swap(*result);
}
}
return rv;
}
nsresult
nsBaseChannel::BeginPumpingData()
{
nsCOMPtr<nsIInputStream> stream;
nsCOMPtr<nsIChannel> channel;
nsresult rv = OpenContentStream(true, getter_AddRefs(stream),
getter_AddRefs(channel));
if (NS_FAILED(rv))
return rv;
NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
if (channel) {
rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel));
if (NS_SUCCEEDED(rv))
mWaitingOnAsyncRedirect = true;
return rv;
}
// By assigning mPump, we flag this channel as pending (see Pending). It's
// important that the pending flag is set when we call into the stream (the
// call to AsyncRead results in the stream's AsyncWait method being called)
// and especially when we call into the loadgroup. Our caller takes care to
// release mPump if we return an error.
rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
true);
if (NS_SUCCEEDED(rv))
rv = mPump->AsyncRead(this, nullptr);
return rv;
}
void
nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
{
NS_ASSERTION(!mPump, "Shouldn't have gotten here");
nsresult rv = mStatus;
if (NS_SUCCEEDED(mStatus)) {
rv = Redirect(newChannel,
nsIChannelEventSink::REDIRECT_TEMPORARY,
true);
if (NS_SUCCEEDED(rv)) {
// OnRedirectVerifyCallback will be called asynchronously
return;
}
}
ContinueHandleAsyncRedirect(rv);
}
void
nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result)
{
mWaitingOnAsyncRedirect = false;
if (NS_FAILED(result))
Cancel(result);
if (NS_FAILED(result) && mListener) {
// Notify our consumer ourselves
mListener->OnStartRequest(this, mListenerContext);
mListener->OnStopRequest(this, mListenerContext, mStatus);
ChannelDone();
}
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
// Drop notification callbacks to prevent cycles.
mCallbacks = nullptr;
CallbacksChanged();
}
void
nsBaseChannel::ClassifyURI()
{
// For channels created in the child process, delegate to the parent to
// classify URIs.
if (!XRE_IsParentProcess()) {
return;
}
if (mLoadFlags & LOAD_CLASSIFY_URI) {
RefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
if (classifier) {
classifier->Start(this);
} else {
Cancel(NS_ERROR_OUT_OF_MEMORY);
}
}
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsISupports
NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel,
nsHashPropertyBag,
nsIRequest,
nsIChannel,
nsIThreadRetargetableRequest,
nsIInterfaceRequestor,
nsITransportEventSink,
nsIRequestObserver,
nsIStreamListener,
nsIThreadRetargetableStreamListener,
nsIAsyncVerifyRedirectCallback,
nsIPrivateBrowsingChannel)
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIRequest
NS_IMETHODIMP
nsBaseChannel::GetName(nsACString &result)
{
if (!mURI) {
result.Truncate();
return NS_OK;
}
return mURI->GetSpec(result);
}
NS_IMETHODIMP
nsBaseChannel::IsPending(bool *result)
{
*result = Pending();
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetStatus(nsresult *status)
{
if (mPump && NS_SUCCEEDED(mStatus)) {
mPump->GetStatus(status);
} else {
*status = mStatus;
}
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::Cancel(nsresult status)
{
// Ignore redundant cancelation
if (NS_FAILED(mStatus))
return NS_OK;
mStatus = status;
if (mPump)
mPump->Cancel(status);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::Suspend()
{
NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
return mPump->Suspend();
}
NS_IMETHODIMP
nsBaseChannel::Resume()
{
NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
return mPump->Resume();
}
NS_IMETHODIMP
nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
{
mLoadFlags = aLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
{
NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
{
if (!CanSetLoadGroup(aLoadGroup)) {
return NS_ERROR_FAILURE;
}
mLoadGroup = aLoadGroup;
CallbacksChanged();
UpdatePrivateBrowsing();
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIChannel
NS_IMETHODIMP
nsBaseChannel::GetOriginalURI(nsIURI **aURI)
{
*aURI = OriginalURI();
NS_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetOriginalURI(nsIURI *aURI)
{
NS_ENSURE_ARG_POINTER(aURI);
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetURI(nsIURI **aURI)
{
NS_IF_ADDREF(*aURI = mURI);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetOwner(nsISupports **aOwner)
{
NS_IF_ADDREF(*aOwner = mOwner);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetOwner(nsISupports *aOwner)
{
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
{
mLoadInfo = aLoadInfo;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
{
NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
{
NS_IF_ADDREF(*aCallbacks = mCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
{
if (!CanSetCallbacks(aCallbacks)) {
return NS_ERROR_FAILURE;
}
mCallbacks = aCallbacks;
CallbacksChanged();
UpdatePrivateBrowsing();
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
{
NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetContentType(nsACString &aContentType)
{
aContentType = mContentType;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetContentType(const nsACString &aContentType)
{
// mContentCharset is unchanged if not parsed
bool dummy;
net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetContentCharset(nsACString &aContentCharset)
{
aContentCharset = mContentCharset;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
{
mContentCharset = aContentCharset;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition)
{
// preserve old behavior, fail unless explicitly set.
if (mContentDispositionHint == UINT32_MAX) {
return NS_ERROR_NOT_AVAILABLE;
}
*aContentDisposition = mContentDispositionHint;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition)
{
mContentDispositionHint = aContentDisposition;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
{
if (!mContentDispositionFilename) {
return NS_ERROR_NOT_AVAILABLE;
}
aContentDispositionFilename = *mContentDispositionFilename;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
{
mContentDispositionFilename = new nsString(aContentDispositionFilename);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
nsBaseChannel::GetContentLength(int64_t *aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::SetContentLength(int64_t aContentLength)
{
mContentLength = aContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::Open(nsIInputStream **result)
{
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
nsCOMPtr<nsIChannel> chan;
nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan));
NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
if (NS_SUCCEEDED(rv) && chan) {
rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false);
if (NS_FAILED(rv))
return rv;
rv = chan->Open(result);
} else if (rv == NS_ERROR_NOT_IMPLEMENTED)
return NS_ImplementChannelOpen(this, result);
if (NS_SUCCEEDED(rv)) {
mWasOpened = true;
ClassifyURI();
}
return rv;
}
NS_IMETHODIMP
nsBaseChannel::Open2(nsIInputStream** aStream)
{
nsCOMPtr<nsIStreamListener> listener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return Open(aStream);
}
NS_IMETHODIMP
nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
{
MOZ_ASSERT(!mLoadInfo ||
mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone() ||
(mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
"security flags in loadInfo but asyncOpen2() not called");
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
NS_ENSURE_ARG(listener);
// Skip checking for chrome:// sub-resources.
nsAutoCString scheme;
mURI->GetScheme(scheme);
if (!scheme.EqualsLiteral("file")) {
NS_CompareLoadInfoAndLoadContext(this);
}
// Ensure that this is an allowed port before proceeding.
nsresult rv = NS_CheckPortSafety(mURI);
if (NS_FAILED(rv)) {
mCallbacks = nullptr;
return rv;
}
// Store the listener and context early so that OpenContentStream and the
// stream's AsyncWait method (called by AsyncRead) can have access to them
// via PushStreamConverter and the StreamListener methods. However, since
// this typically introduces a reference cycle between this and the listener,
// we need to be sure to break the reference if this method does not succeed.
mListener = listener;
mListenerContext = ctxt;
// This method assigns mPump as a side-effect. We need to clear mPump if
// this method fails.
rv = BeginPumpingData();
if (NS_FAILED(rv)) {
mPump = nullptr;
ChannelDone();
mCallbacks = nullptr;
return rv;
}
// At this point, we are going to return success no matter what.
mWasOpened = true;
SUSPEND_PUMP_FOR_SCOPE();
if (mLoadGroup)
mLoadGroup->AddRequest(this, nullptr);
ClassifyURI();
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::AsyncOpen2(nsIStreamListener *aListener)
{
nsCOMPtr<nsIStreamListener> listener = aListener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return AsyncOpen(listener, nullptr);
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsITransportEventSink
NS_IMETHODIMP
nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
int64_t progress, int64_t progressMax)
{
// In some cases, we may wish to suppress transport-layer status events.
if (!mPump || NS_FAILED(mStatus)) {
return NS_OK;
}
SUSPEND_PUMP_FOR_SCOPE();
// Lazily fetch mProgressSink
if (!mProgressSink) {
if (mQueriedProgressSink) {
return NS_OK;
}
GetCallback(mProgressSink);
mQueriedProgressSink = true;
if (!mProgressSink) {
return NS_OK;
}
}
if (!HasLoadFlag(LOAD_BACKGROUND)) {
nsAutoString statusArg;
if (GetStatusArg(status, statusArg)) {
mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get());
}
}
if (progress) {
mProgressSink->OnProgress(this, mListenerContext, progress, progressMax);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIInterfaceRequestor
NS_IMETHODIMP
nsBaseChannel::GetInterface(const nsIID &iid, void **result)
{
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result);
return *result ? NS_OK : NS_ERROR_NO_INTERFACE;
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIRequestObserver
static void
CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount)
{
nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
nsAutoCString newType;
NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType);
if (!newType.IsEmpty()) {
chan->SetContentType(newType);
}
}
static void
CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount)
{
nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
nsCOMPtr<nsIContentSniffer> sniffer =
do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER);
if (!sniffer)
return;
nsAutoCString detected;
nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
if (NS_SUCCEEDED(rv))
chan->SetContentType(detected);
}
NS_IMETHODIMP
nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
MOZ_ASSERT(request == mPump);
// If our content type is unknown, use the content type
// sniffer. If the sniffer is not available for some reason, then we just keep
// going as-is.
if (NS_SUCCEEDED(mStatus) &&
mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
}
// Now, the general type sniffers. Skip this if we have none.
if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS)
mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
SUSPEND_PUMP_FOR_SCOPE();
if (mListener) // null in case of redirect
return mListener->OnStartRequest(this, mListenerContext);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
nsresult status)
{
// If both mStatus and status are failure codes, we keep mStatus as-is since
// that is consistent with our GetStatus and Cancel methods.
if (NS_SUCCEEDED(mStatus))
mStatus = status;
// Cause Pending to return false.
mPump = nullptr;
if (mListener) // null in case of redirect
mListener->OnStopRequest(this, mListenerContext, mStatus);
ChannelDone();
// No need to suspend pump in this scope since we will not be receiving
// any more events from it.
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
// Drop notification callbacks to prevent cycles.
mCallbacks = nullptr;
CallbacksChanged();
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIStreamListener
NS_IMETHODIMP
nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
nsIInputStream *stream, uint64_t offset,
uint32_t count)
{
SUSPEND_PUMP_FOR_SCOPE();
nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
offset, count);
if (mSynthProgressEvents && NS_SUCCEEDED(rv)) {
int64_t prog = offset + count;
if (NS_IsMainThread()) {
OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength);
} else {
class OnTransportStatusAsyncEvent : public mozilla::Runnable
{
RefPtr<nsBaseChannel> mChannel;
int64_t mProgress;
int64_t mContentLength;
public:
OnTransportStatusAsyncEvent(nsBaseChannel* aChannel,
int64_t aProgress,
int64_t aContentLength)
: mChannel(aChannel),
mProgress(aProgress),
mContentLength(aContentLength)
{ }
NS_IMETHOD Run() override
{
return mChannel->OnTransportStatus(nullptr, NS_NET_STATUS_READING,
mProgress, mContentLength);
}
};
nsCOMPtr<nsIRunnable> runnable =
new OnTransportStatusAsyncEvent(this, prog, mContentLength);
NS_DispatchToMainThread(runnable);
}
}
return rv;
}
NS_IMETHODIMP
nsBaseChannel::OnRedirectVerifyCallback(nsresult result)
{
if (NS_SUCCEEDED(result))
result = ContinueRedirect();
if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) {
if (NS_SUCCEEDED(mStatus))
mStatus = result;
return NS_OK;
}
if (mWaitingOnAsyncRedirect)
ContinueHandleAsyncRedirect(result);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::RetargetDeliveryTo(nsIEventTarget* aEventTarget)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
if (!mAllowThreadRetargeting) {
return NS_ERROR_NOT_IMPLEMENTED;
}
return mPump->RetargetDeliveryTo(aEventTarget);
}
NS_IMETHODIMP
nsBaseChannel::CheckListenerChain()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAllowThreadRetargeting) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
do_QueryInterface(mListener);
if (!listener) {
return NS_ERROR_NO_INTERFACE;
}
return listener->CheckListenerChain();
}

View file

@ -0,0 +1,300 @@
/* -*- 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 nsBaseChannel_h__
#define nsBaseChannel_h__
#include "nsString.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsHashPropertyBag.h"
#include "nsInputStreamPump.h"
#include "nsIChannel.h"
#include "nsIURI.h"
#include "nsILoadGroup.h"
#include "nsILoadInfo.h"
#include "nsIStreamListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsIProgressEventSink.h"
#include "nsITransport.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "PrivateBrowsingChannel.h"
#include "nsThreadUtils.h"
class nsIInputStream;
//-----------------------------------------------------------------------------
// nsBaseChannel is designed to be subclassed. The subclass is responsible for
// implementing the OpenContentStream method, which will be called by the
// nsIChannel::AsyncOpen and nsIChannel::Open implementations.
//
// nsBaseChannel implements nsIInterfaceRequestor to provide a convenient way
// for subclasses to query both the nsIChannel::notificationCallbacks and
// nsILoadGroup::notificationCallbacks for supported interfaces.
//
// nsBaseChannel implements nsITransportEventSink to support progress & status
// notifications generated by the transport layer.
class nsBaseChannel : public nsHashPropertyBag
, public nsIChannel
, public nsIThreadRetargetableRequest
, public nsIInterfaceRequestor
, public nsITransportEventSink
, public nsIAsyncVerifyRedirectCallback
, public mozilla::net::PrivateBrowsingChannel<nsBaseChannel>
, protected nsIStreamListener
, protected nsIThreadRetargetableStreamListener
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIREQUEST
NS_DECL_NSICHANNEL
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
NS_DECL_NSITHREADRETARGETABLEREQUEST
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
nsBaseChannel();
// This method must be called to initialize the basechannel instance.
nsresult Init() {
return NS_OK;
}
protected:
// -----------------------------------------------
// Methods to be implemented by the derived class:
virtual ~nsBaseChannel();
private:
// Implemented by subclass to supply data stream. The parameter, async, is
// true when called from nsIChannel::AsyncOpen and false otherwise. When
// async is true, the resulting stream will be used with a nsIInputStreamPump
// instance. This means that if it is a non-blocking stream that supports
// nsIAsyncInputStream that it will be read entirely on the main application
// thread, and its AsyncWait method will be called whenever ReadSegments
// returns NS_BASE_STREAM_WOULD_BLOCK. Otherwise, if the stream is blocking,
// then it will be read on one of the background I/O threads, and it does not
// need to implement ReadSegments. If async is false, this method may return
// NS_ERROR_NOT_IMPLEMENTED to cause the basechannel to implement Open in
// terms of AsyncOpen (see NS_ImplementChannelOpen).
// A callee is allowed to return an nsIChannel instead of an nsIInputStream.
// That case will be treated as a redirect to the new channel. By default
// *channel will be set to null by the caller, so callees who don't want to
// return one an just not touch it.
virtual nsresult OpenContentStream(bool async, nsIInputStream **stream,
nsIChannel** channel) = 0;
// The basechannel calls this method from its OnTransportStatus method to
// determine whether to call nsIProgressEventSink::OnStatus in addition to
// nsIProgressEventSink::OnProgress. This method may be overriden by the
// subclass to enable nsIProgressEventSink::OnStatus events. If this method
// returns true, then the statusArg out param specifies the "statusArg" value
// to pass to the OnStatus method. By default, OnStatus messages are
// suppressed. The status parameter passed to this method is the status value
// from the OnTransportStatus method.
virtual bool GetStatusArg(nsresult status, nsString &statusArg) {
return false;
}
// Called when the callbacks available to this channel may have changed.
virtual void OnCallbacksChanged() {
}
// Called when our channel is done, to allow subclasses to drop resources.
virtual void OnChannelDone() {
}
public:
// ----------------------------------------------
// Methods provided for use by the derived class:
// Redirect to another channel. This method takes care of notifying
// observers of this redirect as well as of opening the new channel, if asked
// to do so. It also cancels |this| with the status code
// NS_BINDING_REDIRECTED. A failure return from this method means that the
// redirect could not be performed (no channel was opened; this channel
// wasn't canceled.) The redirectFlags parameter consists of the flag values
// defined on nsIChannelEventSink.
nsresult Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
bool openNewChannel);
// Tests whether a type hint was set. Subclasses can use this to decide
// whether to call SetContentType.
// NOTE: This is only reliable if the subclass didn't itself call
// SetContentType, and should also not be called after OpenContentStream.
bool HasContentTypeHint() const;
// The URI member should be initialized before the channel is used, and then
// it should never be changed again until the channel is destroyed.
nsIURI *URI() {
return mURI;
}
void SetURI(nsIURI *uri) {
NS_ASSERTION(uri, "must specify a non-null URI");
NS_ASSERTION(!mURI, "must not modify URI");
NS_ASSERTION(!mOriginalURI, "how did that get set so early?");
mURI = uri;
mOriginalURI = uri;
}
nsIURI *OriginalURI() {
return mOriginalURI;
}
// The security info is a property of the transport-layer, which should be
// assigned by the subclass.
nsISupports *SecurityInfo() {
return mSecurityInfo;
}
void SetSecurityInfo(nsISupports *info) {
mSecurityInfo = info;
}
// Test the load flags
bool HasLoadFlag(uint32_t flag) {
return (mLoadFlags & flag) != 0;
}
// This is a short-cut to calling nsIRequest::IsPending()
virtual bool Pending() const {
return mPump || mWaitingOnAsyncRedirect;
}
// Helper function for querying the channel's notification callbacks.
template <class T> void GetCallback(nsCOMPtr<T> &result) {
GetInterface(NS_GET_TEMPLATE_IID(T), getter_AddRefs(result));
}
// Helper function for calling QueryInterface on this.
nsQueryInterface do_QueryInterface() {
return nsQueryInterface(static_cast<nsIChannel *>(this));
}
// MSVC needs this:
nsQueryInterface do_QueryInterface(nsISupports *obj) {
return nsQueryInterface(obj);
}
// If a subclass does not want to feed transport-layer progress events to the
// base channel via nsITransportEventSink, then it may set this flag to cause
// the base channel to synthesize progress events when it receives data from
// the content stream. By default, progress events are not synthesized.
void EnableSynthesizedProgressEvents(bool enable) {
mSynthProgressEvents = enable;
}
// Some subclasses may wish to manually insert a stream listener between this
// and the channel's listener. The following methods make that possible.
void SetStreamListener(nsIStreamListener *listener) {
mListener = listener;
}
nsIStreamListener *StreamListener() {
return mListener;
}
// Pushes a new stream converter in front of the channel's stream listener.
// The fromType and toType values are passed to nsIStreamConverterService's
// AsyncConvertData method. If invalidatesContentLength is true, then the
// channel's content-length property will be assigned a value of -1. This is
// necessary when the converter changes the length of the resulting data
// stream, which is almost always the case for a "stream converter" ;-)
// This function optionally returns a reference to the new converter.
nsresult PushStreamConverter(const char *fromType, const char *toType,
bool invalidatesContentLength = true,
nsIStreamListener **converter = nullptr);
protected:
void DisallowThreadRetargeting() {
mAllowThreadRetargeting = false;
}
private:
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
// Called to setup mPump and call AsyncRead on it.
nsresult BeginPumpingData();
// Called when the callbacks available to this channel may have changed.
void CallbacksChanged() {
mProgressSink = nullptr;
mQueriedProgressSink = false;
OnCallbacksChanged();
}
// Called when our channel is done. This should drop no-longer-needed pointers.
void ChannelDone() {
mListener = nullptr;
mListenerContext = nullptr;
OnChannelDone();
}
// Handle an async redirect callback. This will only be called if we
// returned success from AsyncOpen while posting a redirect runnable.
void HandleAsyncRedirect(nsIChannel* newChannel);
void ContinueHandleAsyncRedirect(nsresult result);
nsresult ContinueRedirect();
// start URI classifier if requested
void ClassifyURI();
class RedirectRunnable : public mozilla::Runnable
{
public:
RedirectRunnable(nsBaseChannel* chan, nsIChannel* newChannel)
: mChannel(chan), mNewChannel(newChannel)
{
NS_PRECONDITION(newChannel, "Must have channel to redirect to");
}
NS_IMETHOD Run() override
{
mChannel->HandleAsyncRedirect(mNewChannel);
return NS_OK;
}
private:
RefPtr<nsBaseChannel> mChannel;
nsCOMPtr<nsIChannel> mNewChannel;
};
friend class RedirectRunnable;
RefPtr<nsInputStreamPump> mPump;
nsCOMPtr<nsIProgressEventSink> mProgressSink;
nsCOMPtr<nsIURI> mOriginalURI;
nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCString mContentType;
nsCString mContentCharset;
uint32_t mLoadFlags;
bool mQueriedProgressSink;
bool mSynthProgressEvents;
bool mAllowThreadRetargeting;
bool mWaitingOnAsyncRedirect;
bool mOpenRedirectChannel;
uint32_t mRedirectFlags;
protected:
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsILoadInfo> mLoadInfo;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsISupports> mListenerContext;
nsresult mStatus;
uint32_t mContentDispositionHint;
nsAutoPtr<nsString> mContentDispositionFilename;
int64_t mContentLength;
bool mWasOpened;
friend class mozilla::net::PrivateBrowsingChannel<nsBaseChannel>;
};
#endif // !nsBaseChannel_h__

View file

@ -0,0 +1,137 @@
/* -*- 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 "nsBaseContentStream.h"
#include "nsStreamUtils.h"
//-----------------------------------------------------------------------------
void
nsBaseContentStream::DispatchCallback(bool async)
{
if (!mCallback)
return;
// It's important to clear mCallback and mCallbackTarget up-front because the
// OnInputStreamReady implementation may call our AsyncWait method.
nsCOMPtr<nsIInputStreamCallback> callback;
if (async) {
callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
mCallback = nullptr;
} else {
callback.swap(mCallback);
}
mCallbackTarget = nullptr;
callback->OnInputStreamReady(this);
}
//-----------------------------------------------------------------------------
// nsBaseContentStream::nsISupports
NS_IMPL_ADDREF(nsBaseContentStream)
NS_IMPL_RELEASE(nsBaseContentStream)
// We only support nsIAsyncInputStream when we are in non-blocking mode.
NS_INTERFACE_MAP_BEGIN(nsBaseContentStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, mNonBlocking)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
NS_INTERFACE_MAP_END_THREADSAFE
//-----------------------------------------------------------------------------
// nsBaseContentStream::nsIInputStream
NS_IMETHODIMP
nsBaseContentStream::Close()
{
return IsClosed() ? NS_OK : CloseWithStatus(NS_BASE_STREAM_CLOSED);
}
NS_IMETHODIMP
nsBaseContentStream::Available(uint64_t *result)
{
*result = 0;
return mStatus;
}
NS_IMETHODIMP
nsBaseContentStream::Read(char *buf, uint32_t count, uint32_t *result)
{
return ReadSegments(NS_CopySegmentToBuffer, buf, count, result);
}
NS_IMETHODIMP
nsBaseContentStream::ReadSegments(nsWriteSegmentFun fun, void *closure,
uint32_t count, uint32_t *result)
{
*result = 0;
if (mStatus == NS_BASE_STREAM_CLOSED)
return NS_OK;
// No data yet
if (!IsClosed() && IsNonBlocking())
return NS_BASE_STREAM_WOULD_BLOCK;
return mStatus;
}
NS_IMETHODIMP
nsBaseContentStream::IsNonBlocking(bool *result)
{
*result = mNonBlocking;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsBaseContentStream::nsIAsyncInputStream
NS_IMETHODIMP
nsBaseContentStream::CloseWithStatus(nsresult status)
{
if (IsClosed())
return NS_OK;
NS_ENSURE_ARG(NS_FAILED(status));
mStatus = status;
DispatchCallback();
return NS_OK;
}
NS_IMETHODIMP
nsBaseContentStream::AsyncWait(nsIInputStreamCallback *callback,
uint32_t flags, uint32_t requestedCount,
nsIEventTarget *target)
{
// Our _only_ consumer is nsInputStreamPump, so we simplify things here by
// making assumptions about how we will be called.
NS_ASSERTION(target, "unexpected parameter");
NS_ASSERTION(flags == 0, "unexpected parameter");
NS_ASSERTION(requestedCount == 0, "unexpected parameter");
#ifdef DEBUG
bool correctThread;
target->IsOnCurrentThread(&correctThread);
NS_ASSERTION(correctThread, "event target must be on the current thread");
#endif
mCallback = callback;
mCallbackTarget = target;
if (!mCallback)
return NS_OK;
// If we're already closed, then dispatch this callback immediately.
if (IsClosed()) {
DispatchCallback();
return NS_OK;
}
OnCallbackPending();
return NS_OK;
}

View file

@ -0,0 +1,82 @@
/* -*- 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 nsBaseContentStream_h__
#define nsBaseContentStream_h__
#include "nsIAsyncInputStream.h"
#include "nsIEventTarget.h"
#include "nsCOMPtr.h"
//-----------------------------------------------------------------------------
// nsBaseContentStream is designed to be subclassed with the intention of being
// used to satisfy the nsBaseChannel::OpenContentStream method.
//
// The subclass typically overrides the default Available, ReadSegments and
// CloseWithStatus methods. By default, Read is implemented in terms of
// ReadSegments, and Close is implemented in terms of CloseWithStatus. If
// CloseWithStatus is overriden, then the subclass will usually want to call
// the base class' CloseWithStatus method before returning.
//
// If the stream is non-blocking, then readSegments may return the exception
// NS_BASE_STREAM_WOULD_BLOCK if there is no data available and the stream is
// not at the "end-of-file" or already closed. This error code must not be
// returned from the Available implementation. When the caller receives this
// error code, he may choose to call the stream's AsyncWait method, in which
// case the base stream will have a non-null PendingCallback. When the stream
// has data or encounters an error, it should be sure to dispatch a pending
// callback if one exists (see DispatchCallback). The implementation of the
// base stream's CloseWithStatus (and Close) method will ensure that any
// pending callback is dispatched. It is the responsibility of the subclass
// to ensure that the pending callback is dispatched when it wants to have its
// ReadSegments method called again.
class nsBaseContentStream : public nsIAsyncInputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIASYNCINPUTSTREAM
explicit nsBaseContentStream(bool nonBlocking)
: mStatus(NS_OK)
, mNonBlocking(nonBlocking) {
}
nsresult Status() { return mStatus; }
bool IsNonBlocking() { return mNonBlocking; }
bool IsClosed() { return NS_FAILED(mStatus); }
// Called to test if the stream has a pending callback.
bool HasPendingCallback() { return mCallback != nullptr; }
// The current dispatch target (may be null) for the pending callback if any.
nsIEventTarget *CallbackTarget() { return mCallbackTarget; }
// Called to dispatch a pending callback. If there is no pending callback,
// then this function does nothing. Pass true to this function to cause the
// callback to occur asynchronously; otherwise, the callback will happen
// before this function returns.
void DispatchCallback(bool async = true);
// Helper function to make code more self-documenting.
void DispatchCallbackSync() { DispatchCallback(false); }
protected:
virtual ~nsBaseContentStream() {}
private:
// Called from the base stream's AsyncWait method when a pending callback
// is installed on the stream.
virtual void OnCallbackPending() {}
private:
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mCallbackTarget;
nsresult mStatus;
bool mNonBlocking;
};
#endif // nsBaseContentStream_h__

View file

@ -0,0 +1,832 @@
/* -*- 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 "ipc/IPCMessageUtils.h"
#include "nsBufferedStreams.h"
#include "nsStreamUtils.h"
#include "nsNetCID.h"
#include "nsIClassInfoImpl.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include <algorithm>
#ifdef DEBUG_brendan
# define METERING
#endif
#ifdef METERING
# include <stdio.h>
# define METER(x) x
# define MAX_BIG_SEEKS 20
static struct {
uint32_t mSeeksWithinBuffer;
uint32_t mSeeksOutsideBuffer;
uint32_t mBufferReadUponSeek;
uint32_t mBufferUnreadUponSeek;
uint32_t mBytesReadFromBuffer;
uint32_t mBigSeekIndex;
struct {
int64_t mOldOffset;
int64_t mNewOffset;
} mBigSeek[MAX_BIG_SEEKS];
} bufstats;
#else
# define METER(x) /* nothing */
#endif
using namespace mozilla::ipc;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
////////////////////////////////////////////////////////////////////////////////
// nsBufferedStream
nsBufferedStream::nsBufferedStream()
: mBuffer(nullptr),
mBufferStartOffset(0),
mCursor(0),
mFillPoint(0),
mStream(nullptr),
mBufferDisabled(false),
mEOF(false),
mGetBufferCount(0)
{
}
nsBufferedStream::~nsBufferedStream()
{
Close();
}
NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream)
nsresult
nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize)
{
NS_ASSERTION(stream, "need to supply a stream");
NS_ASSERTION(mStream == nullptr, "already inited");
mStream = stream;
NS_IF_ADDREF(mStream);
mBufferSize = bufferSize;
mBufferStartOffset = 0;
mCursor = 0;
mBuffer = new (mozilla::fallible) char[bufferSize];
if (mBuffer == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
nsresult
nsBufferedStream::Close()
{
NS_IF_RELEASE(mStream);
if (mBuffer) {
delete[] mBuffer;
mBuffer = nullptr;
mBufferSize = 0;
mBufferStartOffset = 0;
mCursor = 0;
mFillPoint = 0;
}
#ifdef METERING
{
static FILE *tfp;
if (!tfp) {
tfp = fopen("/tmp/bufstats", "w");
if (tfp)
setvbuf(tfp, nullptr, _IOLBF, 0);
}
if (tfp) {
fprintf(tfp, "seeks within buffer: %u\n",
bufstats.mSeeksWithinBuffer);
fprintf(tfp, "seeks outside buffer: %u\n",
bufstats.mSeeksOutsideBuffer);
fprintf(tfp, "buffer read on seek: %u\n",
bufstats.mBufferReadUponSeek);
fprintf(tfp, "buffer unread on seek: %u\n",
bufstats.mBufferUnreadUponSeek);
fprintf(tfp, "bytes read from buffer: %u\n",
bufstats.mBytesReadFromBuffer);
for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) {
fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n",
i,
bufstats.mBigSeek[i].mOldOffset,
bufstats.mBigSeek[i].mNewOffset);
}
}
}
#endif
return NS_OK;
}
NS_IMETHODIMP
nsBufferedStream::Seek(int32_t whence, int64_t offset)
{
if (mStream == nullptr)
return NS_BASE_STREAM_CLOSED;
// If the underlying stream isn't a random access store, then fail early.
// We could possibly succeed for the case where the seek position denotes
// something that happens to be read into the buffer, but that would make
// the failure data-dependent.
nsresult rv;
nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
if (NS_FAILED(rv)) return rv;
int64_t absPos = 0;
switch (whence) {
case nsISeekableStream::NS_SEEK_SET:
absPos = offset;
break;
case nsISeekableStream::NS_SEEK_CUR:
absPos = mBufferStartOffset;
absPos += mCursor;
absPos += offset;
break;
case nsISeekableStream::NS_SEEK_END:
absPos = -1;
break;
default:
NS_NOTREACHED("bogus seek whence parameter");
return NS_ERROR_UNEXPECTED;
}
// Let mCursor point into the existing buffer if the new position is
// between the current cursor and the mFillPoint "fencepost" -- the
// client may never get around to a Read or Write after this Seek.
// Read and Write worry about flushing and filling in that event.
// But if we're at EOF, make sure to pass the seek through to the
// underlying stream, because it may have auto-closed itself and
// needs to reopen.
uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
if (offsetInBuffer <= mFillPoint && !mEOF) {
METER(bufstats.mSeeksWithinBuffer++);
mCursor = offsetInBuffer;
return NS_OK;
}
METER(bufstats.mSeeksOutsideBuffer++);
METER(bufstats.mBufferReadUponSeek += mCursor);
METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor);
rv = Flush();
if (NS_FAILED(rv)) return rv;
rv = ras->Seek(whence, offset);
if (NS_FAILED(rv)) return rv;
mEOF = false;
// Recompute whether the offset we're seeking to is in our buffer.
// Note that we need to recompute because Flush() might have
// changed mBufferStartOffset.
offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
if (offsetInBuffer <= mFillPoint) {
// It's safe to just set mCursor to offsetInBuffer. In particular, we
// want to avoid calling Fill() here since we already have the data that
// was seeked to and calling Fill() might auto-close our underlying
// stream in some cases.
mCursor = offsetInBuffer;
return NS_OK;
}
METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset =
mBufferStartOffset + int64_t(mCursor));
const int64_t minus1 = -1;
if (absPos == minus1) {
// then we had the SEEK_END case, above
int64_t tellPos;
rv = ras->Tell(&tellPos);
mBufferStartOffset = tellPos;
if (NS_FAILED(rv)) return rv;
}
else {
mBufferStartOffset = absPos;
}
METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset =
mBufferStartOffset);
mFillPoint = mCursor = 0;
return Fill();
}
NS_IMETHODIMP
nsBufferedStream::Tell(int64_t *result)
{
if (mStream == nullptr)
return NS_BASE_STREAM_CLOSED;
int64_t result64 = mBufferStartOffset;
result64 += mCursor;
*result = result64;
return NS_OK;
}
NS_IMETHODIMP
nsBufferedStream::SetEOF()
{
if (mStream == nullptr)
return NS_BASE_STREAM_CLOSED;
nsresult rv;
nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
if (NS_FAILED(rv)) return rv;
rv = ras->SetEOF();
if (NS_SUCCEEDED(rv))
mEOF = true;
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// nsBufferedInputStream
NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream)
NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream)
NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE,
NS_BUFFEREDINPUTSTREAM_CID)
NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream)
NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream,
nsIInputStream,
nsIBufferedInputStream,
nsISeekableStream,
nsIStreamBufferAccess)
nsresult
nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
NS_ENSURE_NO_AGGREGATION(aOuter);
nsBufferedInputStream* stream = new nsBufferedInputStream();
if (stream == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(stream);
nsresult rv = stream->QueryInterface(aIID, aResult);
NS_RELEASE(stream);
return rv;
}
NS_IMETHODIMP
nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize)
{
return nsBufferedStream::Init(stream, bufferSize);
}
NS_IMETHODIMP
nsBufferedInputStream::Close()
{
nsresult rv1 = NS_OK, rv2;
if (mStream) {
rv1 = Source()->Close();
NS_RELEASE(mStream);
}
rv2 = nsBufferedStream::Close();
if (NS_FAILED(rv1)) return rv1;
return rv2;
}
NS_IMETHODIMP
nsBufferedInputStream::Available(uint64_t *result)
{
nsresult rv = NS_OK;
*result = 0;
if (mStream) {
rv = Source()->Available(result);
}
*result += (mFillPoint - mCursor);
return rv;
}
NS_IMETHODIMP
nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result)
{
if (mBufferDisabled) {
if (!mStream) {
*result = 0;
return NS_OK;
}
nsresult rv = Source()->Read(buf, count, result);
if (NS_SUCCEEDED(rv)) {
mBufferStartOffset += *result; // so nsBufferedStream::Tell works
if (*result == 0) {
mEOF = true;
}
}
return rv;
}
return ReadSegments(NS_CopySegmentToBuffer, buf, count, result);
}
NS_IMETHODIMP
nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
uint32_t count, uint32_t *result)
{
*result = 0;
if (!mStream)
return NS_OK;
nsresult rv = NS_OK;
while (count > 0) {
uint32_t amt = std::min(count, mFillPoint - mCursor);
if (amt > 0) {
uint32_t read = 0;
rv = writer(this, closure, mBuffer + mCursor, *result, amt, &read);
if (NS_FAILED(rv)) {
// errors returned from the writer end here!
rv = NS_OK;
break;
}
*result += read;
count -= read;
mCursor += read;
}
else {
rv = Fill();
if (NS_FAILED(rv) || mFillPoint == mCursor)
break;
}
}
return (*result > 0) ? NS_OK : rv;
}
NS_IMETHODIMP
nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking)
{
if (mStream)
return Source()->IsNonBlocking(aNonBlocking);
return NS_ERROR_NOT_INITIALIZED;
}
NS_IMETHODIMP
nsBufferedInputStream::Fill()
{
if (mBufferDisabled)
return NS_OK;
NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED);
nsresult rv;
int32_t rem = int32_t(mFillPoint - mCursor);
if (rem > 0) {
// slide the remainder down to the start of the buffer
// |<------------->|<--rem-->|<--->|
// b c f s
memcpy(mBuffer, mBuffer + mCursor, rem);
}
mBufferStartOffset += mCursor;
mFillPoint = rem;
mCursor = 0;
uint32_t amt;
rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt);
if (NS_FAILED(rv)) return rv;
if (amt == 0)
mEOF = true;
mFillPoint += amt;
return NS_OK;
}
NS_IMETHODIMP_(char*)
nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
{
NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
if (mGetBufferCount != 0)
return nullptr;
if (mBufferDisabled)
return nullptr;
char* buf = mBuffer + mCursor;
uint32_t rem = mFillPoint - mCursor;
if (rem == 0) {
if (NS_FAILED(Fill()))
return nullptr;
buf = mBuffer + mCursor;
rem = mFillPoint - mCursor;
}
uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
if (mod) {
uint32_t pad = aAlignMask + 1 - mod;
if (pad > rem)
return nullptr;
memset(buf, 0, pad);
mCursor += pad;
buf += pad;
rem -= pad;
}
if (aLength > rem)
return nullptr;
mGetBufferCount++;
return buf;
}
NS_IMETHODIMP_(void)
nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
{
NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
if (--mGetBufferCount != 0)
return;
NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch");
mCursor += aLength;
}
NS_IMETHODIMP
nsBufferedInputStream::DisableBuffering()
{
NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
NS_ASSERTION(mGetBufferCount == 0,
"DisableBuffer call between GetBuffer and PutBuffer!");
if (mGetBufferCount != 0)
return NS_ERROR_UNEXPECTED;
// Empty the buffer so nsBufferedStream::Tell works.
mBufferStartOffset += mCursor;
mFillPoint = mCursor = 0;
mBufferDisabled = true;
return NS_OK;
}
NS_IMETHODIMP
nsBufferedInputStream::EnableBuffering()
{
NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
mBufferDisabled = false;
return NS_OK;
}
NS_IMETHODIMP
nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream)
{
// Empty the buffer so subsequent i/o trumps any buffered data.
mBufferStartOffset += mCursor;
mFillPoint = mCursor = 0;
*aStream = mStream;
NS_IF_ADDREF(*aStream);
return NS_OK;
}
void
nsBufferedInputStream::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors)
{
BufferedInputStreamParams params;
if (mStream) {
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
MOZ_ASSERT(stream);
InputStreamParams wrappedParams;
SerializeInputStream(stream, wrappedParams, aFileDescriptors);
params.optionalStream() = wrappedParams;
}
else {
params.optionalStream() = mozilla::void_t();
}
params.bufferSize() = mBufferSize;
aParams = params;
}
bool
nsBufferedInputStream::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& aFileDescriptors)
{
if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) {
NS_ERROR("Received unknown parameters from the other process!");
return false;
}
const BufferedInputStreamParams& params =
aParams.get_BufferedInputStreamParams();
const OptionalInputStreamParams& wrappedParams = params.optionalStream();
nsCOMPtr<nsIInputStream> stream;
if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
aFileDescriptors);
if (!stream) {
NS_WARNING("Failed to deserialize wrapped stream!");
return false;
}
}
else {
NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
"Unknown type for OptionalInputStreamParams!");
}
nsresult rv = Init(stream, params.bufferSize());
NS_ENSURE_SUCCESS(rv, false);
return true;
}
Maybe<uint64_t>
nsBufferedInputStream::ExpectedSerializedLength()
{
nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStream);
if (stream) {
return stream->ExpectedSerializedLength();
}
return Nothing();
}
////////////////////////////////////////////////////////////////////////////////
// nsBufferedOutputStream
NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
// This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
// non-nullness of mSafeStream.
NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream)
NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
nsresult
nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
NS_ENSURE_NO_AGGREGATION(aOuter);
nsBufferedOutputStream* stream = new nsBufferedOutputStream();
if (stream == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(stream);
nsresult rv = stream->QueryInterface(aIID, aResult);
NS_RELEASE(stream);
return rv;
}
NS_IMETHODIMP
nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize)
{
// QI stream to an nsISafeOutputStream, to see if we should support it
mSafeStream = do_QueryInterface(stream);
return nsBufferedStream::Init(stream, bufferSize);
}
NS_IMETHODIMP
nsBufferedOutputStream::Close()
{
nsresult rv1, rv2 = NS_OK, rv3;
rv1 = Flush();
// If we fail to Flush all the data, then we close anyway and drop the
// remaining data in the buffer. We do this because it's what Unix does
// for fclose and close. However, we report the error from Flush anyway.
if (mStream) {
rv2 = Sink()->Close();
NS_RELEASE(mStream);
}
rv3 = nsBufferedStream::Close();
if (NS_FAILED(rv1)) return rv1;
if (NS_FAILED(rv2)) return rv2;
return rv3;
}
NS_IMETHODIMP
nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
{
nsresult rv = NS_OK;
uint32_t written = 0;
while (count > 0) {
uint32_t amt = std::min(count, mBufferSize - mCursor);
if (amt > 0) {
memcpy(mBuffer + mCursor, buf + written, amt);
written += amt;
count -= amt;
mCursor += amt;
if (mFillPoint < mCursor)
mFillPoint = mCursor;
}
else {
NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!");
rv = Flush();
if (NS_FAILED(rv)) break;
}
}
*result = written;
return (written > 0) ? NS_OK : rv;
}
NS_IMETHODIMP
nsBufferedOutputStream::Flush()
{
nsresult rv;
uint32_t amt;
if (!mStream) {
// Stream already cancelled/flushed; probably because of previous error.
return NS_OK;
}
rv = Sink()->Write(mBuffer, mFillPoint, &amt);
if (NS_FAILED(rv)) return rv;
mBufferStartOffset += amt;
if (amt == mFillPoint) {
mFillPoint = mCursor = 0;
return NS_OK; // flushed everything
}
// slide the remainder down to the start of the buffer
// |<-------------->|<---|----->|
// b a c s
uint32_t rem = mFillPoint - amt;
memmove(mBuffer, mBuffer + amt, rem);
mFillPoint = mCursor = rem;
return NS_ERROR_FAILURE; // didn't flush all
}
// nsISafeOutputStream
NS_IMETHODIMP
nsBufferedOutputStream::Finish()
{
// flush the stream, to write out any buffered data...
nsresult rv = nsBufferedOutputStream::Flush();
if (NS_FAILED(rv))
NS_WARNING("failed to flush buffered data! possible dataloss");
// ... and finish the underlying stream...
if (NS_SUCCEEDED(rv))
rv = mSafeStream->Finish();
else
Sink()->Close();
// ... and close the buffered stream, so any further attempts to flush/close
// the buffered stream won't cause errors.
nsBufferedStream::Close();
return rv;
}
static nsresult
nsReadFromInputStream(nsIOutputStream* outStr,
void* closure,
char* toRawSegment,
uint32_t offset,
uint32_t count,
uint32_t *readCount)
{
nsIInputStream* fromStream = (nsIInputStream*)closure;
return fromStream->Read(toRawSegment, count, readCount);
}
NS_IMETHODIMP
nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
{
return WriteSegments(nsReadFromInputStream, inStr, count, _retval);
}
NS_IMETHODIMP
nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
{
*_retval = 0;
nsresult rv;
while (count > 0) {
uint32_t left = std::min(count, mBufferSize - mCursor);
if (left == 0) {
rv = Flush();
if (NS_FAILED(rv))
return (*_retval > 0) ? NS_OK : rv;
continue;
}
uint32_t read = 0;
rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read);
if (NS_FAILED(rv)) // If we have written some data, return ok
return (*_retval > 0) ? NS_OK : rv;
mCursor += read;
*_retval += read;
count -= read;
mFillPoint = std::max(mFillPoint, mCursor);
}
return NS_OK;
}
NS_IMETHODIMP
nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking)
{
if (mStream)
return Sink()->IsNonBlocking(aNonBlocking);
return NS_ERROR_NOT_INITIALIZED;
}
NS_IMETHODIMP_(char*)
nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
{
NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
if (mGetBufferCount != 0)
return nullptr;
if (mBufferDisabled)
return nullptr;
char* buf = mBuffer + mCursor;
uint32_t rem = mBufferSize - mCursor;
if (rem == 0) {
if (NS_FAILED(Flush()))
return nullptr;
buf = mBuffer + mCursor;
rem = mBufferSize - mCursor;
}
uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
if (mod) {
uint32_t pad = aAlignMask + 1 - mod;
if (pad > rem)
return nullptr;
memset(buf, 0, pad);
mCursor += pad;
buf += pad;
rem -= pad;
}
if (aLength > rem)
return nullptr;
mGetBufferCount++;
return buf;
}
NS_IMETHODIMP_(void)
nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
{
NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
if (--mGetBufferCount != 0)
return;
NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch");
mCursor += aLength;
if (mFillPoint < mCursor)
mFillPoint = mCursor;
}
NS_IMETHODIMP
nsBufferedOutputStream::DisableBuffering()
{
NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
NS_ASSERTION(mGetBufferCount == 0,
"DisableBuffer call between GetBuffer and PutBuffer!");
if (mGetBufferCount != 0)
return NS_ERROR_UNEXPECTED;
// Empty the buffer so nsBufferedStream::Tell works.
nsresult rv = Flush();
if (NS_FAILED(rv))
return rv;
mBufferDisabled = true;
return NS_OK;
}
NS_IMETHODIMP
nsBufferedOutputStream::EnableBuffering()
{
NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
mBufferDisabled = false;
return NS_OK;
}
NS_IMETHODIMP
nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream)
{
// Empty the buffer so subsequent i/o trumps any buffered data.
if (mFillPoint) {
nsresult rv = Flush();
if (NS_FAILED(rv))
return rv;
}
*aStream = mStream;
NS_IF_ADDREF(*aStream);
return NS_OK;
}
#undef METER
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,122 @@
/* -*- 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 nsBufferedStreams_h__
#define nsBufferedStreams_h__
#include "nsIBufferedStreams.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsISafeOutputStream.h"
#include "nsISeekableStream.h"
#include "nsIStreamBufferAccess.h"
#include "nsCOMPtr.h"
#include "nsIIPCSerializableInputStream.h"
////////////////////////////////////////////////////////////////////////////////
class nsBufferedStream : public nsISeekableStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISEEKABLESTREAM
nsBufferedStream();
nsresult Close();
protected:
virtual ~nsBufferedStream();
nsresult Init(nsISupports* stream, uint32_t bufferSize);
NS_IMETHOD Fill() = 0;
NS_IMETHOD Flush() = 0;
uint32_t mBufferSize;
char* mBuffer;
// mBufferStartOffset is the offset relative to the start of mStream.
int64_t mBufferStartOffset;
// mCursor is the read cursor for input streams, or write cursor for
// output streams, and is relative to mBufferStartOffset.
uint32_t mCursor;
// mFillPoint is the amount available in the buffer for input streams,
// or the high watermark of bytes written into the buffer, and therefore
// is relative to mBufferStartOffset.
uint32_t mFillPoint;
nsISupports* mStream; // cast to appropriate subclass
bool mBufferDisabled;
bool mEOF; // True if mStream is at EOF
uint8_t mGetBufferCount;
};
////////////////////////////////////////////////////////////////////////////////
class nsBufferedInputStream : public nsBufferedStream,
public nsIBufferedInputStream,
public nsIStreamBufferAccess,
public nsIIPCSerializableInputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIBUFFEREDINPUTSTREAM
NS_DECL_NSISTREAMBUFFERACCESS
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
nsBufferedInputStream() : nsBufferedStream() {}
static nsresult
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
nsIInputStream* Source() {
return (nsIInputStream*)mStream;
}
protected:
virtual ~nsBufferedInputStream() {}
NS_IMETHOD Fill() override;
NS_IMETHOD Flush() override { return NS_OK; } // no-op for input streams
};
////////////////////////////////////////////////////////////////////////////////
class nsBufferedOutputStream final : public nsBufferedStream,
public nsISafeOutputStream,
public nsIBufferedOutputStream,
public nsIStreamBufferAccess
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOUTPUTSTREAM
NS_DECL_NSISAFEOUTPUTSTREAM
NS_DECL_NSIBUFFEREDOUTPUTSTREAM
NS_DECL_NSISTREAMBUFFERACCESS
nsBufferedOutputStream() : nsBufferedStream() {}
static nsresult
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
nsIOutputStream* Sink() {
return (nsIOutputStream*)mStream;
}
protected:
virtual ~nsBufferedOutputStream() { nsBufferedOutputStream::Close(); }
NS_IMETHOD Fill() override { return NS_OK; } // no-op for output streams
nsCOMPtr<nsISafeOutputStream> mSafeStream; // QI'd from mStream
};
////////////////////////////////////////////////////////////////////////////////
#endif // nsBufferedStreams_h__

View file

@ -0,0 +1,696 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 sts=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsChannelClassifier.h"
#include "mozIThirdPartyUtil.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsICacheEntry.h"
#include "nsICachingChannel.h"
#include "nsIChannel.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIHttpChannelInternal.h"
#include "nsIIOService.h"
#include "nsIParentChannel.h"
#include "nsIPermissionManager.h"
#include "nsIPrivateBrowsingTrackingProtectionWhitelist.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsISecureBrowserUI.h"
#include "nsISecurityEventSink.h"
#include "nsIURL.h"
#include "nsIWebProgressListener.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsXULAppAPI.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
namespace mozilla {
namespace net {
//
// MOZ_LOG=nsChannelClassifier:5
//
static LazyLogModule gChannelClassifierLog("nsChannelClassifier");
#undef LOG
#define LOG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Debug)
NS_IMPL_ISUPPORTS(nsChannelClassifier,
nsIURIClassifierCallback)
nsChannelClassifier::nsChannelClassifier()
: mIsAllowListed(false),
mSuspendedChannel(false)
{
}
nsresult
nsChannelClassifier::ShouldEnableTrackingProtection(nsIChannel *aChannel,
bool *result)
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
NS_ENSURE_ARG(result);
*result = false;
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(aChannel, loadContext);
if (!loadContext || !(loadContext->UseTrackingProtection())) {
return NS_OK;
}
nsresult rv;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> topWinURI;
rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
NS_ENSURE_SUCCESS(rv, rv);
if (!topWinURI) {
LOG(("nsChannelClassifier[%p]: No window URI\n", this));
}
nsCOMPtr<nsIURI> chanURI;
rv = aChannel->GetURI(getter_AddRefs(chanURI));
NS_ENSURE_SUCCESS(rv, rv);
// Third party checks don't work for chrome:// URIs in mochitests, so just
// default to isThirdParty = true. We check isThirdPartyWindow to expand
// the list of domains that are considered first party (e.g., if
// facebook.com includes an iframe from fatratgames.com, all subsources
// included in that iframe are considered third-party with
// isThirdPartyChannel, even if they are not third-party w.r.t.
// facebook.com), and isThirdPartyChannel to prevent top-level navigations
// from being detected as third-party.
bool isThirdPartyChannel = true;
bool isThirdPartyWindow = true;
thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow);
thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel);
if (!isThirdPartyWindow || !isThirdPartyChannel) {
*result = false;
if (LOG_ENABLED()) {
LOG(("nsChannelClassifier[%p]: Skipping tracking protection checks "
"for first party or top-level load channel[%p] with uri %s",
this, aChannel, chanURI->GetSpecOrDefault().get()));
}
return NS_OK;
}
nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
const char ALLOWLIST_EXAMPLE_PREF[] = "channelclassifier.allowlist_example";
if (!topWinURI && Preferences::GetBool(ALLOWLIST_EXAMPLE_PREF, false)) {
LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this));
rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"),
nullptr, nullptr, getter_AddRefs(topWinURI));
NS_ENSURE_SUCCESS(rv, rv);
}
// Take the host/port portion so we can allowlist by site. Also ignore the
// scheme, since users who put sites on the allowlist probably don't expect
// allowlisting to depend on scheme.
nsCOMPtr<nsIURL> url = do_QueryInterface(topWinURI, &rv);
if (NS_FAILED(rv)) {
return rv; // normal for some loads, no need to print a warning
}
nsCString escaped(NS_LITERAL_CSTRING("https://"));
nsAutoCString temp;
rv = url->GetHostPort(temp);
NS_ENSURE_SUCCESS(rv, rv);
escaped.Append(temp);
// Stuff the whole thing back into a URI for the permission manager.
rv = ios->NewURI(escaped, nullptr, nullptr, getter_AddRefs(topWinURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPermissionManager> permMgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION;
rv = permMgr->TestPermission(topWinURI, "trackingprotection", &permissions);
NS_ENSURE_SUCCESS(rv, rv);
if (permissions == nsIPermissionManager::ALLOW_ACTION) {
LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] for %s", this,
aChannel, escaped.get()));
mIsAllowListed = true;
*result = false;
} else {
*result = true;
}
// In Private Browsing Mode we also check against an in-memory list.
if (NS_UsePrivateBrowsing(aChannel)) {
nsCOMPtr<nsIPrivateBrowsingTrackingProtectionWhitelist> pbmtpWhitelist =
do_GetService(NS_PBTRACKINGPROTECTIONWHITELIST_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
bool exists = false;
rv = pbmtpWhitelist->ExistsInAllowList(topWinURI, &exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
mIsAllowListed = true;
LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] in PBM for %s",
this, aChannel, escaped.get()));
}
*result = !exists;
}
// Tracking protection will be enabled so return without updating
// the security state. If any channels are subsequently cancelled
// (page elements blocked) the state will be then updated.
if (*result) {
if (LOG_ENABLED()) {
LOG(("nsChannelClassifier[%p]: Enabling tracking protection checks on "
"channel[%p] with uri %s for toplevel window %s", this, aChannel,
chanURI->GetSpecOrDefault().get(),
topWinURI->GetSpecOrDefault().get()));
}
return NS_OK;
}
// Tracking protection will be disabled so update the security state
// of the document and fire a secure change event. If we can't get the
// window for the channel, then the shield won't show up so we can't send
// an event to the securityUI anyway.
return NotifyTrackingProtectionDisabled(aChannel);
}
// static
nsresult
nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel)
{
// Can be called in EITHER the parent or child process.
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(aChannel, parentChannel);
if (parentChannel) {
// This channel is a parent-process proxy for a child process request.
// Tell the child process channel to do this instead.
parentChannel->NotifyTrackingProtectionDisabled();
return NS_OK;
}
nsresult rv;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIDOMWindowProxy> win;
rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
NS_ENSURE_SUCCESS(rv, rv);
auto* pwin = nsPIDOMWindowOuter::From(win);
nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
if (!docShell) {
return NS_OK;
}
nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
NS_ENSURE_TRUE(doc, NS_OK);
// Notify nsIWebProgressListeners of this security event.
// Can be used to change the UI state.
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS(rv, NS_OK);
uint32_t state = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return NS_OK;
}
doc->SetHasTrackingContentLoaded(true);
securityUI->GetState(&state);
state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
eventSink->OnSecurityChange(nullptr, state);
return NS_OK;
}
void
nsChannelClassifier::Start(nsIChannel *aChannel)
{
mChannel = aChannel;
nsresult rv = StartInternal();
if (NS_FAILED(rv)) {
// If we aren't getting a callback for any reason, assume a good verdict and
// make sure we resume the channel if necessary.
OnClassifyComplete(NS_OK);
}
}
nsresult
nsChannelClassifier::StartInternal()
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
// Don't bother to run the classifier on a load that has already failed.
// (this might happen after a redirect)
nsresult status;
mChannel->GetStatus(&status);
if (NS_FAILED(status))
return status;
// Don't bother to run the classifier on a cached load that was
// previously classified as good.
if (HasBeenClassified(mChannel)) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// Don't bother checking certain types of URIs.
bool hasFlags;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_ERROR_UNEXPECTED;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_LOCAL_FILE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_ERROR_UNEXPECTED;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_UI_RESOURCE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_ERROR_UNEXPECTED;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_ERROR_UNEXPECTED;
// Skip whitelisted hostnames.
nsAutoCString whitelisted;
Preferences::GetCString("urlclassifier.skipHostnames", &whitelisted);
if (!whitelisted.IsEmpty()) {
ToLowerCase(whitelisted);
LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
this, whitelisted.get()));
if (IsHostnameWhitelisted(uri, whitelisted)) {
return NS_ERROR_UNEXPECTED;
}
}
nsCOMPtr<nsIURIClassifier> uriClassifier =
do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
rv == NS_ERROR_NOT_AVAILABLE) {
// no URI classifier, ignore this failure.
return NS_ERROR_NOT_AVAILABLE;
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIScriptSecurityManager> securityManager =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> principal;
rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
bool expectCallback;
bool trackingProtectionEnabled = false;
(void)ShouldEnableTrackingProtection(mChannel, &trackingProtectionEnabled);
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> principalURI;
principal->GetURI(getter_AddRefs(principalURI));
LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel with "
"uri %s", this, principalURI->GetSpecOrDefault().get(),
uri->GetSpecOrDefault().get()));
}
rv = uriClassifier->Classify(principal, trackingProtectionEnabled, this,
&expectCallback);
if (NS_FAILED(rv)) {
return rv;
}
if (expectCallback) {
// Suspend the channel, it will be resumed when we get the classifier
// callback.
rv = mChannel->Suspend();
if (NS_FAILED(rv)) {
// Some channels (including nsJSChannel) fail on Suspend. This
// shouldn't be fatal, but will prevent malware from being
// blocked on these channels.
LOG(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
return rv;
}
mSuspendedChannel = true;
LOG(("nsChannelClassifier[%p]: suspended channel %p",
this, mChannel.get()));
} else {
LOG(("nsChannelClassifier[%p]: not expecting callback", this));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri,
const nsACString &aWhitelisted)
{
nsAutoCString host;
nsresult rv = aUri->GetHost(host);
if (NS_FAILED(rv) || host.IsEmpty()) {
return false;
}
ToLowerCase(host);
nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ',');
while (tokenizer.hasMoreTokens()) {
const nsCSubstring& token = tokenizer.nextToken();
if (token.Equals(host)) {
LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
this, host.get()));
return true;
}
}
return false;
}
// Note in the cache entry that this URL was classified, so that future
// cached loads don't need to be checked.
void
nsChannelClassifier::MarkEntryClassified(nsresult status)
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
// Don't cache tracking classifications because we support allowlisting.
if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) {
return;
}
if (LOG_ENABLED()) {
nsAutoCString errorName;
GetErrorName(status, errorName);
nsCOMPtr<nsIURI> uri;
mChannel->GetURI(getter_AddRefs(uri));
nsAutoCString spec;
uri->GetAsciiSpec(spec);
LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s",
errorName.get(), spec.get()));
}
nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
if (!cachingChannel) {
return;
}
nsCOMPtr<nsISupports> cacheToken;
cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
if (!cacheToken) {
return;
}
nsCOMPtr<nsICacheEntry> cacheEntry =
do_QueryInterface(cacheToken);
if (!cacheEntry) {
return;
}
cacheEntry->SetMetaDataElement("necko:classified",
NS_SUCCEEDED(status) ? "1" : nullptr);
}
bool
nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel)
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsICachingChannel> cachingChannel =
do_QueryInterface(aChannel);
if (!cachingChannel) {
return false;
}
// Only check the tag if we are loading from the cache without
// validation.
bool fromCache;
if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
return false;
}
nsCOMPtr<nsISupports> cacheToken;
cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
if (!cacheToken) {
return false;
}
nsCOMPtr<nsICacheEntry> cacheEntry =
do_QueryInterface(cacheToken);
if (!cacheEntry) {
return false;
}
nsXPIDLCString tag;
cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
return tag.EqualsLiteral("1");
}
//static
bool
nsChannelClassifier::SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel)
{
nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
if (!channelLoadInfo || !docURI) {
return false;
}
nsCOMPtr<nsIPrincipal> channelLoadingPrincipal = channelLoadInfo->LoadingPrincipal();
if (!channelLoadingPrincipal) {
// TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top level
// loads should not be blocked by Tracking Protection, so we will return
// false
return false;
}
nsCOMPtr<nsIURI> channelLoadingURI;
channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
if (!channelLoadingURI) {
return false;
}
bool equals = false;
nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
return NS_SUCCEEDED(rv) && equals;
}
// static
nsresult
nsChannelClassifier::SetBlockedTrackingContent(nsIChannel *channel)
{
// Can be called in EITHER the parent or child process.
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(channel, parentChannel);
if (parentChannel) {
// This channel is a parent-process proxy for a child process request. The
// actual channel will be notified via the status passed to
// nsIRequest::Cancel and do this for us.
return NS_OK;
}
nsresult rv;
nsCOMPtr<mozIDOMWindowProxy> win;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, NS_OK);
rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
NS_ENSURE_SUCCESS(rv, NS_OK);
auto* pwin = nsPIDOMWindowOuter::From(win);
nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
if (!docShell) {
return NS_OK;
}
nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
NS_ENSURE_TRUE(doc, NS_OK);
// This event might come after the user has navigated to another page.
// To prevent showing the TrackingProtection UI on the wrong page, we need to
// check that the loading URI for the channel is the same as the URI currently
// loaded in the document.
if (!SameLoadingURI(doc, channel)) {
return NS_OK;
}
// Notify nsIWebProgressListeners of this security event.
// Can be used to change the UI state.
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS(rv, NS_OK);
uint32_t state = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return NS_OK;
}
doc->SetHasTrackingContentBlocked(true);
securityUI->GetState(&state);
state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
eventSink->OnSecurityChange(nullptr, state);
// Log a warning to the web console.
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
const char16_t* params[] = { spec.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Tracking Protection"),
doc,
nsContentUtils::eNECKO_PROPERTIES,
"TrackingUriBlocked",
params, ArrayLength(params));
return NS_OK;
}
nsresult
nsChannelClassifier::IsTrackerWhitelisted()
{
nsresult rv;
nsCOMPtr<nsIURIClassifier> uriClassifier =
do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString tables;
Preferences::GetCString("urlclassifier.trackingWhitelistTable", &tables);
if (tables.IsEmpty()) {
LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled",
this));
return NS_ERROR_TRACKING_URI;
}
nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> topWinURI;
rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
NS_ENSURE_SUCCESS(rv, rv);
if (!topWinURI) {
LOG(("nsChannelClassifier[%p]: No window URI", this));
return NS_ERROR_TRACKING_URI;
}
nsCOMPtr<nsIScriptSecurityManager> securityManager =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> chanPrincipal;
rv = securityManager->GetChannelURIPrincipal(mChannel,
getter_AddRefs(chanPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
// Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
nsAutoCString pageHostname, resourceDomain;
rv = topWinURI->GetHost(pageHostname);
NS_ENSURE_SUCCESS(rv, rv);
rv = chanPrincipal->GetBaseDomain(resourceDomain);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") +
pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain;
LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist",
this, whitelistEntry.get()));
nsCOMPtr<nsIURI> whitelistURI;
rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
NS_ENSURE_SUCCESS(rv, rv);
// Check whether or not the tracker is in the entity whitelist
nsAutoCString results;
rv = uriClassifier->ClassifyLocalWithTables(whitelistURI, tables, results);
NS_ENSURE_SUCCESS(rv, rv);
if (!results.IsEmpty()) {
return NS_OK; // found it on the whitelist, must not be blocked
}
LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
this, whitelistEntry.get()));
return NS_ERROR_TRACKING_URI;
}
NS_IMETHODIMP
nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
if (aErrorCode == NS_ERROR_TRACKING_URI &&
NS_SUCCEEDED(IsTrackerWhitelisted())) {
LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
"in whitelist so we won't block it", this));
aErrorCode = NS_OK;
}
if (mSuspendedChannel) {
nsAutoCString errorName;
if (LOG_ENABLED()) {
GetErrorName(aErrorCode, errorName);
LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
this, errorName.get()));
}
MarkEntryClassified(aErrorCode);
if (NS_FAILED(aErrorCode)) {
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> uri;
mChannel->GetURI(getter_AddRefs(uri));
LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
"with error code %s", this, mChannel.get(),
uri->GetSpecOrDefault().get(), errorName.get()));
}
// Channel will be cancelled (page element blocked) due to tracking.
// Do update the security state of the document and fire a security
// change event.
if (aErrorCode == NS_ERROR_TRACKING_URI) {
SetBlockedTrackingContent(mChannel);
}
mChannel->Cancel(aErrorCode);
}
LOG(("nsChannelClassifier[%p]: resuming channel %p from "
"OnClassifyComplete", this, mChannel.get()));
mChannel->Resume();
}
mChannel = nullptr;
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,65 @@
/* 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 nsChannelClassifier_h__
#define nsChannelClassifier_h__
#include "nsIURIClassifier.h"
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
class nsIChannel;
class nsIHttpChannelInternal;
class nsIDocument;
namespace mozilla {
namespace net {
class nsChannelClassifier final : public nsIURIClassifierCallback
{
public:
nsChannelClassifier();
NS_DECL_ISUPPORTS
NS_DECL_NSIURICLASSIFIERCALLBACK
// Calls nsIURIClassifier.Classify with the principal of the given channel,
// and cancels the channel on a bad verdict.
void Start(nsIChannel *aChannel);
// Whether or not tracking protection should be enabled on this channel.
nsresult ShouldEnableTrackingProtection(nsIChannel *aChannel, bool *result);
private:
// True if the channel is on the allow list.
bool mIsAllowListed;
// True if the channel has been suspended.
bool mSuspendedChannel;
nsCOMPtr<nsIChannel> mChannel;
~nsChannelClassifier() {}
// Caches good classifications for the channel principal.
void MarkEntryClassified(nsresult status);
bool HasBeenClassified(nsIChannel *aChannel);
// Helper function so that we ensure we call ContinueBeginConnect once
// Start is called. Returns NS_OK if and only if we will get a callback
// from the classifier service.
nsresult StartInternal();
// Helper function to check a tracking URI against the whitelist
nsresult IsTrackerWhitelisted();
// Helper function to check a URI against the hostname whitelist
bool IsHostnameWhitelisted(nsIURI *aUri, const nsACString &aWhitelisted);
// Checks that the channel was loaded by the URI currently loaded in aDoc
static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
public:
// If we are blocking tracking content, update the corresponding flag in
// the respective docshell and call nsISecurityEventSink::onSecurityChange.
static nsresult SetBlockedTrackingContent(nsIChannel *channel);
static nsresult NotifyTrackingProtectionDisabled(nsIChannel *aChannel);
};
} // namespace net
} // namespace mozilla
#endif

View file

@ -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 "nsDNSPrefetch.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsIDNSListener.h"
#include "nsIDNSService.h"
#include "nsICancelable.h"
#include "nsIURI.h"
static nsIDNSService *sDNSService = nullptr;
nsresult
nsDNSPrefetch::Initialize(nsIDNSService *aDNSService)
{
NS_IF_RELEASE(sDNSService);
sDNSService = aDNSService;
NS_IF_ADDREF(sDNSService);
return NS_OK;
}
nsresult
nsDNSPrefetch::Shutdown()
{
NS_IF_RELEASE(sDNSService);
return NS_OK;
}
nsDNSPrefetch::nsDNSPrefetch(nsIURI *aURI,
nsIDNSListener *aListener,
bool storeTiming)
: mStoreTiming(storeTiming)
, mListener(do_GetWeakReference(aListener))
{
aURI->GetAsciiHost(mHostname);
}
nsresult
nsDNSPrefetch::Prefetch(uint16_t flags)
{
if (mHostname.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
if (!sDNSService)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsICancelable> tmpOutstanding;
if (mStoreTiming)
mStartTimestamp = mozilla::TimeStamp::Now();
// If AsyncResolve fails, for example because prefetching is disabled,
// then our timing will be useless. However, in such a case,
// mEndTimestamp will be a null timestamp and callers should check
// TimingsValid() before using the timing.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
return sDNSService->AsyncResolve(mHostname,
flags | nsIDNSService::RESOLVE_SPECULATE,
this, mainThread,
getter_AddRefs(tmpOutstanding));
}
nsresult
nsDNSPrefetch::PrefetchLow(bool refreshDNS)
{
return Prefetch(nsIDNSService::RESOLVE_PRIORITY_LOW |
(refreshDNS ? nsIDNSService::RESOLVE_BYPASS_CACHE : 0));
}
nsresult
nsDNSPrefetch::PrefetchMedium(bool refreshDNS)
{
return Prefetch(nsIDNSService::RESOLVE_PRIORITY_MEDIUM |
(refreshDNS ? nsIDNSService::RESOLVE_BYPASS_CACHE : 0));
}
nsresult
nsDNSPrefetch::PrefetchHigh(bool refreshDNS)
{
return Prefetch(refreshDNS ?
nsIDNSService::RESOLVE_BYPASS_CACHE : 0);
}
NS_IMPL_ISUPPORTS(nsDNSPrefetch, nsIDNSListener)
NS_IMETHODIMP
nsDNSPrefetch::OnLookupComplete(nsICancelable *request,
nsIDNSRecord *rec,
nsresult status)
{
MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
if (mStoreTiming) {
mEndTimestamp = mozilla::TimeStamp::Now();
}
nsCOMPtr<nsIDNSListener> listener = do_QueryReferent(mListener);
if (listener) {
listener->OnLookupComplete(request, rec, status);
}
return NS_OK;
}

View file

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDNSPrefetch_h___
#define nsDNSPrefetch_h___
#include "nsWeakReference.h"
#include "nsString.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Attributes.h"
#include "nsIDNSListener.h"
class nsIURI;
class nsIDNSService;
class nsDNSPrefetch final : public nsIDNSListener
{
~nsDNSPrefetch() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDNSLISTENER
nsDNSPrefetch(nsIURI *aURI, nsIDNSListener *aListener, bool storeTiming);
bool TimingsValid() const {
return !mStartTimestamp.IsNull() && !mEndTimestamp.IsNull();
}
// Only use the two timings if TimingsValid() returns true
const mozilla::TimeStamp& StartTimestamp() const { return mStartTimestamp; }
const mozilla::TimeStamp& EndTimestamp() const { return mEndTimestamp; }
static nsresult Initialize(nsIDNSService *aDNSService);
static nsresult Shutdown();
// Call one of the following methods to start the Prefetch.
nsresult PrefetchHigh(bool refreshDNS = false);
nsresult PrefetchMedium(bool refreshDNS = false);
nsresult PrefetchLow(bool refreshDNS = false);
private:
nsCString mHostname;
bool mStoreTiming;
mozilla::TimeStamp mStartTimestamp;
mozilla::TimeStamp mEndTimestamp;
nsWeakPtr mListener;
nsresult Prefetch(uint16_t flags);
};
#endif

View file

@ -0,0 +1,359 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set sw=4 sts=4 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/. */
/*
The converts a filesystem directory into an "HTTP index" stream per
Lou Montulli's original spec:
http://www.mozilla.org/projects/netlib/dirindexformat.html
*/
#include "nsEscape.h"
#include "nsDirectoryIndexStream.h"
#include "mozilla/Logging.h"
#include "prtime.h"
#include "nsISimpleEnumerator.h"
#ifdef THREADSAFE_I18N
#include "nsCollationCID.h"
#include "nsICollation.h"
#include "nsILocale.h"
#include "nsILocaleService.h"
#endif
#include "nsIFile.h"
#include "nsURLHelper.h"
#include "nsNativeCharsetUtils.h"
// NOTE: This runs on the _file transport_ thread.
// The problem is that now that we're actually doing something with the data,
// we want to do stuff like i18n sorting. However, none of the collation stuff
// is threadsafe.
// So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
// behaviour, though. See bug 99382.
// When this is fixed, #define THREADSAFE_I18N to get this code working
//#define THREADSAFE_I18N
using namespace mozilla;
static LazyLogModule gLog("nsDirectoryIndexStream");
nsDirectoryIndexStream::nsDirectoryIndexStream()
: mOffset(0), mStatus(NS_OK), mPos(0)
{
MOZ_LOG(gLog, LogLevel::Debug,
("nsDirectoryIndexStream[%p]: created", this));
}
static int compare(nsIFile* aElement1, nsIFile* aElement2, void* aData)
{
if (!NS_IsNativeUTF8()) {
// don't check for errors, because we can't report them anyway
nsAutoString name1, name2;
aElement1->GetLeafName(name1);
aElement2->GetLeafName(name2);
// Note - we should do the collation to do sorting. Why don't we?
// Because that is _slow_. Using TestProtocols to list file:///dev/
// goes from 3 seconds to 22. (This may be why nsXULSortService is
// so slow as well).
// Does this have bad effects? Probably, but since nsXULTree appears
// to use the raw RDF literal value as the sort key (which ammounts to an
// strcmp), it won't be any worse, I think.
// This could be made faster, by creating the keys once,
// but CompareString could still be smarter - see bug 99383 - bbaetz
// NB - 99393 has been WONTFIXed. So if the I18N code is ever made
// threadsafe so that this matters, we'd have to pass through a
// struct { nsIFile*, uint8_t* } with the pre-calculated key.
return Compare(name1, name2);
}
nsAutoCString name1, name2;
aElement1->GetNativeLeafName(name1);
aElement2->GetNativeLeafName(name2);
return Compare(name1, name2);
}
nsresult
nsDirectoryIndexStream::Init(nsIFile* aDir)
{
nsresult rv;
bool isDir;
rv = aDir->IsDirectory(&isDir);
if (NS_FAILED(rv)) return rv;
NS_PRECONDITION(isDir, "not a directory");
if (!isDir)
return NS_ERROR_ILLEGAL_VALUE;
if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
nsAutoCString path;
aDir->GetNativePath(path);
MOZ_LOG(gLog, LogLevel::Debug,
("nsDirectoryIndexStream[%p]: initialized on %s",
this, path.get()));
}
// Sigh. We have to allocate on the heap because there are no
// assignment operators defined.
nsCOMPtr<nsISimpleEnumerator> iter;
rv = aDir->GetDirectoryEntries(getter_AddRefs(iter));
if (NS_FAILED(rv)) return rv;
// Now lets sort, because clients expect it that way
// XXX - should we do so here, or when the first item is requested?
// XXX - use insertion sort instead?
bool more;
nsCOMPtr<nsISupports> elem;
while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
rv = iter->GetNext(getter_AddRefs(elem));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
if (file)
mArray.AppendObject(file); // addrefs
}
}
#ifdef THREADSAFE_I18N
nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID,
&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocale> locale;
rv = ls->GetApplicationLocale(getter_AddRefs(locale));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsICollationFactory> cf = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID,
&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsICollation> coll;
rv = cf->CreateCollation(locale, getter_AddRefs(coll));
if (NS_FAILED(rv)) return rv;
mArray.Sort(compare, coll);
#else
mArray.Sort(compare, nullptr);
#endif
mBuf.AppendLiteral("300: ");
nsAutoCString url;
rv = net_GetURLSpecFromFile(aDir, url);
if (NS_FAILED(rv)) return rv;
mBuf.Append(url);
mBuf.Append('\n');
mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
return NS_OK;
}
nsDirectoryIndexStream::~nsDirectoryIndexStream()
{
MOZ_LOG(gLog, LogLevel::Debug,
("nsDirectoryIndexStream[%p]: destroyed", this));
}
nsresult
nsDirectoryIndexStream::Create(nsIFile* aDir, nsIInputStream** aResult)
{
RefPtr<nsDirectoryIndexStream> result = new nsDirectoryIndexStream();
if (! result)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = result->Init(aDir);
if (NS_FAILED(rv)) {
return rv;
}
result.forget(aResult);
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsDirectoryIndexStream, nsIInputStream)
// The below routines are proxied to the UI thread!
NS_IMETHODIMP
nsDirectoryIndexStream::Close()
{
mStatus = NS_BASE_STREAM_CLOSED;
return NS_OK;
}
NS_IMETHODIMP
nsDirectoryIndexStream::Available(uint64_t* aLength)
{
if (NS_FAILED(mStatus))
return mStatus;
// If there's data in our buffer, use that
if (mOffset < (int32_t)mBuf.Length()) {
*aLength = mBuf.Length() - mOffset;
return NS_OK;
}
// Returning one byte is not ideal, but good enough
*aLength = (mPos < mArray.Count()) ? 1 : 0;
return NS_OK;
}
NS_IMETHODIMP
nsDirectoryIndexStream::Read(char* aBuf, uint32_t aCount, uint32_t* aReadCount)
{
if (mStatus == NS_BASE_STREAM_CLOSED) {
*aReadCount = 0;
return NS_OK;
}
if (NS_FAILED(mStatus))
return mStatus;
uint32_t nread = 0;
// If anything is enqueued (or left-over) in mBuf, then feed it to
// the reader first.
while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
*(aBuf++) = char(mBuf.CharAt(mOffset++));
--aCount;
++nread;
}
// Room left?
if (aCount > 0) {
mOffset = 0;
mBuf.Truncate();
// Okay, now we'll suck stuff off of our iterator into the mBuf...
while (uint32_t(mBuf.Length()) < aCount) {
bool more = mPos < mArray.Count();
if (!more) break;
// don't addref, for speed - an addref happened when it
// was placed in the array, so it's not going to go stale
nsIFile* current = mArray.ObjectAt(mPos);
++mPos;
if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
nsAutoCString path;
current->GetNativePath(path);
MOZ_LOG(gLog, LogLevel::Debug,
("nsDirectoryIndexStream[%p]: iterated %s",
this, path.get()));
}
// rjc: don't return hidden files/directories!
// bbaetz: why not?
nsresult rv;
#ifndef XP_UNIX
bool hidden = false;
current->IsHidden(&hidden);
if (hidden) {
MOZ_LOG(gLog, LogLevel::Debug,
("nsDirectoryIndexStream[%p]: skipping hidden file/directory",
this));
continue;
}
#endif
int64_t fileSize = 0;
current->GetFileSize( &fileSize );
PRTime fileInfoModifyTime = 0;
current->GetLastModifiedTime( &fileInfoModifyTime );
fileInfoModifyTime *= PR_USEC_PER_MSEC;
mBuf.AppendLiteral("201: ");
// The "filename" field
if (!NS_IsNativeUTF8()) {
nsAutoString leafname;
rv = current->GetLeafName(leafname);
if (NS_FAILED(rv)) return rv;
nsAutoCString escaped;
if (!leafname.IsEmpty() &&
NS_Escape(NS_ConvertUTF16toUTF8(leafname), escaped, url_Path)) {
mBuf.Append(escaped);
mBuf.Append(' ');
}
} else {
nsAutoCString leafname;
rv = current->GetNativeLeafName(leafname);
if (NS_FAILED(rv)) return rv;
nsAutoCString escaped;
if (!leafname.IsEmpty() &&
NS_Escape(leafname, escaped, url_Path)) {
mBuf.Append(escaped);
mBuf.Append(' ');
}
}
// The "content-length" field
mBuf.AppendInt(fileSize, 10);
mBuf.Append(' ');
// The "last-modified" field
PRExplodedTime tm;
PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
{
char buf[64];
PR_FormatTimeUSEnglish(buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
mBuf.Append(buf);
}
// The "file-type" field
bool isFile = true;
current->IsFile(&isFile);
if (isFile) {
mBuf.AppendLiteral("FILE ");
}
else {
bool isDir;
rv = current->IsDirectory(&isDir);
if (NS_FAILED(rv)) return rv;
if (isDir) {
mBuf.AppendLiteral("DIRECTORY ");
}
else {
bool isLink;
rv = current->IsSymlink(&isLink);
if (NS_FAILED(rv)) return rv;
if (isLink) {
mBuf.AppendLiteral("SYMBOLIC-LINK ");
}
}
}
mBuf.Append('\n');
}
// ...and once we've either run out of directory entries, or
// filled up the buffer, then we'll push it to the reader.
while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
*(aBuf++) = char(mBuf.CharAt(mOffset++));
--aCount;
++nread;
}
}
*aReadCount = nread;
return NS_OK;
}
NS_IMETHODIMP
nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDirectoryIndexStream::IsNonBlocking(bool *aNonBlocking)
{
*aNonBlocking = false;
return NS_OK;
}

View file

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDirectoryIndexStream_h__
#define nsDirectoryIndexStream_h__
#include "mozilla/Attributes.h"
#include "nsString.h"
#include "nsIInputStream.h"
#include "nsCOMArray.h"
class nsIFile;
class nsDirectoryIndexStream final : public nsIInputStream
{
private:
nsCString mBuf;
int32_t mOffset;
nsresult mStatus;
int32_t mPos; // position within mArray
nsCOMArray<nsIFile> mArray; // file objects within the directory
nsDirectoryIndexStream();
/**
* aDir will only be used on the calling thread.
*/
nsresult Init(nsIFile* aDir);
~nsDirectoryIndexStream();
public:
/**
* aDir will only be used on the calling thread.
*/
static nsresult
Create(nsIFile* aDir, nsIInputStream** aStreamResult);
// nsISupportsInterface
NS_DECL_THREADSAFE_ISUPPORTS
// nsIInputStream interface
NS_DECL_NSIINPUTSTREAM
};
#endif // nsDirectoryIndexStream_h__

View file

@ -0,0 +1,114 @@
/* 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 "nsDownloader.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsNetUtil.h"
#include "nsCRTGlue.h"
nsDownloader::~nsDownloader()
{
if (mLocation && mLocationIsTemp) {
// release the sink first since it may still hold an open file
// descriptor to mLocation. this needs to happen before the
// file can be removed otherwise the Remove call will fail.
if (mSink) {
mSink->Close();
mSink = nullptr;
}
nsresult rv = mLocation->Remove(false);
if (NS_FAILED(rv))
NS_ERROR("unable to remove temp file");
}
}
NS_IMPL_ISUPPORTS(nsDownloader,
nsIDownloader,
nsIStreamListener,
nsIRequestObserver)
NS_IMETHODIMP
nsDownloader::Init(nsIDownloadObserver *observer, nsIFile *location)
{
mObserver = observer;
mLocation = location;
return NS_OK;
}
NS_IMETHODIMP
nsDownloader::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
nsresult rv;
if (!mLocation) {
nsCOMPtr<nsIFile> location;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(location));
if (NS_FAILED(rv)) return rv;
char buf[13];
NS_MakeRandomString(buf, 8);
memcpy(buf+8, ".tmp", 5);
rv = location->AppendNative(nsDependentCString(buf, 12));
if (NS_FAILED(rv)) return rv;
rv = location->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
if (NS_FAILED(rv)) return rv;
location.swap(mLocation);
mLocationIsTemp = true;
}
rv = NS_NewLocalFileOutputStream(getter_AddRefs(mSink), mLocation);
if (NS_FAILED(rv)) return rv;
// we could wrap this output stream with a buffered output stream,
// but it shouldn't be necessary since we will be writing large
// chunks given to us via OnDataAvailable.
return NS_OK;
}
NS_IMETHODIMP
nsDownloader::OnStopRequest(nsIRequest *request,
nsISupports *ctxt,
nsresult status)
{
if (mSink) {
mSink->Close();
mSink = nullptr;
}
mObserver->OnDownloadComplete(this, request, ctxt, status, mLocation);
mObserver = nullptr;
return NS_OK;
}
nsresult
nsDownloader::ConsumeData(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
uint32_t toOffset,
uint32_t count,
uint32_t *writeCount)
{
nsDownloader *self = (nsDownloader *) closure;
if (self->mSink)
return self->mSink->Write(fromRawSegment, count, writeCount);
*writeCount = count;
return NS_OK;
}
NS_IMETHODIMP
nsDownloader::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
nsIInputStream *inStr,
uint64_t sourceOffset, uint32_t count)
{
uint32_t n;
return inStr->ReadSegments(ConsumeData, this, count, &n);
}

View file

@ -0,0 +1,40 @@
/* 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 nsDownloader_h__
#define nsDownloader_h__
#include "nsIDownloader.h"
#include "nsCOMPtr.h"
class nsIFile;
class nsIOutputStream;
class nsDownloader : public nsIDownloader
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOWNLOADER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
nsDownloader() : mLocationIsTemp(false) {}
protected:
virtual ~nsDownloader();
static nsresult ConsumeData(nsIInputStream *in,
void *closure,
const char *fromRawSegment,
uint32_t toOffset,
uint32_t count,
uint32_t *writeCount);
nsCOMPtr<nsIDownloadObserver> mObserver;
nsCOMPtr<nsIFile> mLocation;
nsCOMPtr<nsIOutputStream> mSink;
bool mLocationIsTemp;
};
#endif // nsDownloader_h__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,333 @@
// /* -*- 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 nsFileStreams_h__
#define nsFileStreams_h__
#include "nsAutoPtr.h"
#include "nsIFileStreams.h"
#include "nsIFile.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsISafeOutputStream.h"
#include "nsISeekableStream.h"
#include "nsILineInputStream.h"
#include "nsCOMPtr.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsReadLine.h"
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////
class nsFileStreamBase : public nsISeekableStream,
public nsIFileMetadata
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIFILEMETADATA
nsFileStreamBase();
protected:
virtual ~nsFileStreamBase();
nsresult Close();
nsresult Available(uint64_t* _retval);
nsresult Read(char* aBuf, uint32_t aCount, uint32_t* _retval);
nsresult ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* _retval);
nsresult IsNonBlocking(bool* _retval);
nsresult Flush();
nsresult Write(const char* aBuf, uint32_t aCount, uint32_t* _retval);
nsresult WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
uint32_t* _retval);
nsresult WriteSegments(nsReadSegmentFun aReader, void* aClosure,
uint32_t aCount, uint32_t* _retval);
PRFileDesc* mFD;
/**
* Flags describing our behavior. See the IDL file for possible values.
*/
int32_t mBehaviorFlags;
/**
* Whether we have a pending open (see DEFER_OPEN in the IDL file).
*/
bool mDeferredOpen;
struct OpenParams {
nsCOMPtr<nsIFile> localFile;
int32_t ioFlags;
int32_t perm;
};
/**
* Data we need to do an open.
*/
OpenParams mOpenParams;
/**
* Prepares the data we need to open the file, and either does the open now
* by calling DoOpen(), or leaves it to be opened later by a call to
* DoPendingOpen().
*/
nsresult MaybeOpen(nsIFile* aFile, int32_t aIoFlags, int32_t aPerm,
bool aDeferred);
/**
* Cleans up data prepared in MaybeOpen.
*/
void CleanUpOpen();
/**
* Open the file. This is called either from MaybeOpen (during Init)
* or from DoPendingOpen (if DEFER_OPEN is used when initializing this
* stream). The default behavior of DoOpen is to open the file and save the
* file descriptor.
*/
virtual nsresult DoOpen();
/**
* If there is a pending open, do it now. It's important for this to be
* inline since we do it in almost every stream API call.
*/
inline nsresult DoPendingOpen();
};
////////////////////////////////////////////////////////////////////////////////
class nsFileInputStream : public nsFileStreamBase,
public nsIFileInputStream,
public nsILineInputStream,
public nsIIPCSerializableInputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIFILEINPUTSTREAM
NS_DECL_NSILINEINPUTSTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
NS_IMETHOD Close() override;
NS_IMETHOD Tell(int64_t *aResult) override;
NS_IMETHOD Available(uint64_t* _retval) override;
NS_IMETHOD Read(char* aBuf, uint32_t aCount, uint32_t* _retval) override;
NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
uint32_t aCount, uint32_t* _retval) override
{
return nsFileStreamBase::ReadSegments(aWriter, aClosure, aCount,
_retval);
}
NS_IMETHOD IsNonBlocking(bool* _retval) override
{
return nsFileStreamBase::IsNonBlocking(_retval);
}
// Overrided from nsFileStreamBase
NS_IMETHOD Seek(int32_t aWhence, int64_t aOffset) override;
nsFileInputStream()
: mLineBuffer(nullptr), mIOFlags(0), mPerm(0), mCachedPosition(0)
{}
static nsresult
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
protected:
virtual ~nsFileInputStream()
{
Close();
}
nsresult SeekInternal(int32_t aWhence, int64_t aOffset, bool aClearBuf=true);
nsAutoPtr<nsLineBuffer<char> > mLineBuffer;
/**
* The file being opened.
*/
nsCOMPtr<nsIFile> mFile;
/**
* The IO flags passed to Init() for the file open.
*/
int32_t mIOFlags;
/**
* The permissions passed to Init() for the file open.
*/
int32_t mPerm;
/**
* Cached position for Tell for automatically reopening streams.
*/
int64_t mCachedPosition;
protected:
/**
* Internal, called to open a file. Parameters are the same as their
* Init() analogues.
*/
nsresult Open(nsIFile* file, int32_t ioFlags, int32_t perm);
};
////////////////////////////////////////////////////////////////////////////////
class nsPartialFileInputStream : public nsFileInputStream,
public nsIPartialFileInputStream
{
public:
using nsFileInputStream::Init;
using nsFileInputStream::Read;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPARTIALFILEINPUTSTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
nsPartialFileInputStream()
: mStart(0), mLength(0), mPosition(0), mDeferredSeek(false)
{ }
NS_IMETHOD Tell(int64_t *aResult) override;
NS_IMETHOD Available(uint64_t *aResult) override;
NS_IMETHOD Read(char* aBuf, uint32_t aCount, uint32_t* aResult) override;
NS_IMETHOD Seek(int32_t aWhence, int64_t aOffset) override;
static nsresult
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
protected:
~nsPartialFileInputStream()
{ }
inline nsresult DoPendingSeek();
private:
uint64_t TruncateSize(uint64_t aSize) {
return std::min<uint64_t>(mLength - mPosition, aSize);
}
uint64_t mStart;
uint64_t mLength;
uint64_t mPosition;
bool mDeferredSeek;
};
////////////////////////////////////////////////////////////////////////////////
class nsFileOutputStream : public nsFileStreamBase,
public nsIFileOutputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIFILEOUTPUTSTREAM
NS_FORWARD_NSIOUTPUTSTREAM(nsFileStreamBase::)
static nsresult
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
protected:
virtual ~nsFileOutputStream()
{
Close();
}
};
////////////////////////////////////////////////////////////////////////////////
/**
* A safe file output stream that overwrites the destination file only
* once writing is complete. This protects against incomplete writes
* due to the process or the thread being interrupted or crashed.
*/
class nsAtomicFileOutputStream : public nsFileOutputStream,
public nsISafeOutputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSISAFEOUTPUTSTREAM
nsAtomicFileOutputStream() :
mTargetFileExists(true),
mWriteResult(NS_OK) {}
virtual nsresult DoOpen() override;
NS_IMETHOD Close() override;
NS_IMETHOD Write(const char *buf, uint32_t count, uint32_t *result) override;
NS_IMETHOD Init(nsIFile* file, int32_t ioFlags, int32_t perm, int32_t behaviorFlags) override;
protected:
virtual ~nsAtomicFileOutputStream()
{
Close();
}
nsCOMPtr<nsIFile> mTargetFile;
nsCOMPtr<nsIFile> mTempFile;
bool mTargetFileExists;
nsresult mWriteResult; // Internally set in Write()
};
////////////////////////////////////////////////////////////////////////////////
/**
* A safe file output stream that overwrites the destination file only
* once writing + flushing is complete. This protects against more
* classes of software/hardware errors than nsAtomicFileOutputStream,
* at the expense of being more costly to the disk, OS and battery.
*/
class nsSafeFileOutputStream : public nsAtomicFileOutputStream
{
public:
NS_IMETHOD Finish() override;
};
////////////////////////////////////////////////////////////////////////////////
class nsFileStream : public nsFileStreamBase,
public nsIInputStream,
public nsIOutputStream,
public nsIFileStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIFILESTREAM
NS_FORWARD_NSIINPUTSTREAM(nsFileStreamBase::)
// Can't use NS_FORWARD_NSIOUTPUTSTREAM due to overlapping methods
// Close() and IsNonBlocking()
NS_IMETHOD Flush() override
{
return nsFileStreamBase::Flush();
}
NS_IMETHOD Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) override
{
return nsFileStreamBase::Write(aBuf, aCount, _retval);
}
NS_IMETHOD WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
uint32_t* _retval) override
{
return nsFileStreamBase::WriteFrom(aFromStream, aCount, _retval);
}
NS_IMETHOD WriteSegments(nsReadSegmentFun aReader, void* aClosure,
uint32_t aCount, uint32_t* _retval) override
{
return nsFileStreamBase::WriteSegments(aReader, aClosure, aCount,
_retval);
}
protected:
virtual ~nsFileStream()
{
Close();
}
};
////////////////////////////////////////////////////////////////////////////////
#endif // nsFileStreams_h__

View file

@ -0,0 +1,205 @@
/* -*- Mode: IDL; 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 "nsISupports.idl"
interface nsIArray;
interface nsIFile;
interface nsIURI;
/**
* Application caches can store a set of namespace entries that affect
* loads from the application cache. If a load from the cache fails
* to match an exact cache entry, namespaces entries will be searched
* for a substring match, and should be applied appropriately.
*/
[scriptable, uuid(96e4c264-2065-4ce9-93bb-43734c62c4eb)]
interface nsIApplicationCacheNamespace : nsISupports
{
/**
* Items matching this namespace can be fetched from the network
* when loading from this cache. The "data" attribute is unused.
*/
const unsigned long NAMESPACE_BYPASS = 1 << 0;
/**
* Items matching this namespace can be fetched from the network
* when loading from this cache. If the load fails, the cache entry
* specified by the "data" attribute should be loaded instead.
*/
const unsigned long NAMESPACE_FALLBACK = 1 << 1;
/**
* Items matching this namespace should be cached
* opportunistically. Successful toplevel loads of documents
* in this namespace should be placed in the application cache.
* Namespaces specifying NAMESPACE_OPPORTUNISTIC may also specify
* NAMESPACE_FALLBACK to supply a fallback entry.
*/
const unsigned long NAMESPACE_OPPORTUNISTIC = 1 << 2;
/**
* Initialize the namespace.
*/
void init(in unsigned long itemType,
in ACString namespaceSpec,
in ACString data);
/**
* The namespace type.
*/
readonly attribute unsigned long itemType;
/**
* The prefix of this namespace. This should be the asciiSpec of the
* URI prefix.
*/
readonly attribute ACString namespaceSpec;
/**
* Data associated with this namespace, such as a fallback. URI data should
* use the asciiSpec of the URI.
*/
readonly attribute ACString data;
};
/**
* Application caches store resources for offline use. Each
* application cache has a unique client ID for use with
* nsICacheService::openSession() to access the cache's entries.
*
* Each entry in the application cache can be marked with a set of
* types, as discussed in the WHAT-WG offline applications
* specification.
*
* All application caches with the same group ID belong to a cache
* group. Each group has one "active" cache that will service future
* loads. Inactive caches will be removed from the cache when they are
* no longer referenced.
*/
[scriptable, uuid(06568DAE-C374-4383-A122-0CC96C7177F2)]
interface nsIApplicationCache : nsISupports
{
/**
* Init this application cache instance to just hold the group ID and
* the client ID to work just as a handle to the real cache. Used on
* content process to simplify the application cache code.
*/
void initAsHandle(in ACString groupId, in ACString clientId);
/**
* Entries in an application cache can be marked as one or more of
* the following types.
*/
/* This item is the application manifest. */
const unsigned long ITEM_MANIFEST = 1 << 0;
/* This item was explicitly listed in the application manifest. */
const unsigned long ITEM_EXPLICIT = 1 << 1;
/* This item was navigated in a toplevel browsing context, and
* named this cache's group as its manifest. */
const unsigned long ITEM_IMPLICIT = 1 << 2;
/* This item was added by the dynamic scripting API */
const unsigned long ITEM_DYNAMIC = 1 << 3;
/* This item was listed in the application manifest, but named a
* different cache group as its manifest. */
const unsigned long ITEM_FOREIGN = 1 << 4;
/* This item was listed as a fallback entry. */
const unsigned long ITEM_FALLBACK = 1 << 5;
/* This item matched an opportunistic cache namespace and was
* cached accordingly. */
const unsigned long ITEM_OPPORTUNISTIC = 1 << 6;
/**
* URI of the manfiest specifying this application cache.
**/
readonly attribute nsIURI manifestURI;
/**
* The group ID for this cache group. It is an internally generated string
* and cannot be used as manifest URL spec.
**/
readonly attribute ACString groupID;
/**
* The client ID for this application cache. Clients can open a
* session with nsICacheService::createSession() using this client
* ID and a storage policy of STORE_OFFLINE to access this cache.
*/
readonly attribute ACString clientID;
/**
* TRUE if the cache is the active cache for this group.
*/
readonly attribute boolean active;
/**
* The disk usage of the application cache, in bytes.
*/
readonly attribute unsigned long usage;
/**
* Makes this cache the active application cache for this group.
* Future loads associated with this group will come from this
* cache. Other caches from this cache group will be deactivated.
*/
void activate();
/**
* Discard this application cache. Removes all cached resources
* for this cache. If this is the active application cache for the
* group, the group will be removed.
*/
void discard();
/**
* Adds item types to a given entry.
*/
void markEntry(in ACString key, in unsigned long typeBits);
/**
* Removes types from a given entry. If the resulting entry has
* no types left, the entry is removed.
*/
void unmarkEntry(in ACString key, in unsigned long typeBits);
/**
* Gets the types for a given entry.
*/
unsigned long getTypes(in ACString key);
/**
* Returns any entries in the application cache whose type matches
* one or more of the bits in typeBits.
*/
void gatherEntries(in uint32_t typeBits,
out unsigned long count,
[array, size_is(count)] out string keys);
/**
* Add a set of namespace entries to the application cache.
* @param namespaces
* An nsIArray of nsIApplicationCacheNamespace entries.
*/
void addNamespaces(in nsIArray namespaces);
/**
* Get the most specific namespace matching a given key.
*/
nsIApplicationCacheNamespace getMatchingNamespace(in ACString key);
/**
* If set, this offline cache is placed in a different directory
* than the current application profile.
*/
readonly attribute nsIFile profileDirectory;
};

View file

@ -0,0 +1,57 @@
/* -*- Mode: IDL; 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 "nsIApplicationCacheContainer.idl"
/**
* Interface implemented by channels that support application caches.
*/
[scriptable, uuid(6FA816B1-6D5F-4380-9704-054D0908CFA3)]
interface nsIApplicationCacheChannel : nsIApplicationCacheContainer
{
/**
* TRUE when the resource came from the application cache. This
* might be false even there is assigned an application cache
* e.g. in case of fallback of load of an entry matching bypass
* namespace.
*/
readonly attribute boolean loadedFromApplicationCache;
/**
* When true, the channel will ask its notification callbacks for
* an application cache if one is not explicitly provided. Default
* value is true.
*
* NS_ERROR_ALREADY_OPENED will be thrown if set after AsyncOpen()
* is called.
*/
attribute boolean inheritApplicationCache;
/**
* When true, the channel will choose an application cache if one
* was not explicitly provided and none is available from the
* notification callbacks. Default value is false.
*
* This attribute will not be transferred through a redirect.
*
* NS_ERROR_ALREADY_OPENED will be thrown if set after AsyncOpen()
* is called.
*/
attribute boolean chooseApplicationCache;
/**
* A shortcut method to mark the cache item of this channel as 'foreign'.
* See the 'cache selection algorithm' and CACHE_SELECTION_RELOAD
* action handling in nsContentSink.
*/
void markOfflineCacheEntryAsForeign();
/**
* Set offline application cache object to instruct the channel
* to cache for offline use using this application cache.
*/
attribute nsIApplicationCache applicationCacheForWrite;
};

View file

@ -0,0 +1,19 @@
/* -*- Mode: IDL; 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 "nsISupports.idl"
interface nsIApplicationCache;
/**
* Interface used by objects that can be associated with an
* application cache.
*/
[scriptable, uuid(bbb80700-1f7f-4258-aff4-1743cc5a7d23)]
interface nsIApplicationCacheContainer : nsISupports
{
attribute nsIApplicationCache applicationCache;
};

View file

@ -0,0 +1,110 @@
/* -*- Mode: IDL; 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 "nsISupports.idl"
interface nsIApplicationCache;
interface nsIFile;
interface nsIURI;
interface nsILoadContextInfo;
/**
* The application cache service manages the set of application cache
* groups.
*/
[scriptable, uuid(b8b6546c-6cec-4bda-82df-08e006a97b56)]
interface nsIApplicationCacheService : nsISupports
{
/**
* Create group string identifying cache group according the manifest
* URL and the given principal.
*/
ACString buildGroupIDForInfo(in nsIURI aManifestURL,
in nsILoadContextInfo aLoadContextInfo);
ACString buildGroupIDForSuffix(in nsIURI aManifestURL,
in ACString aOriginSuffix);
/**
* Create a new, empty application cache for the given cache
* group.
*/
nsIApplicationCache createApplicationCache(in ACString group);
/**
* Create a new, empty application cache for the given cache
* group residing in a custom directory with a custom quota.
*
* @param group
* URL of the manifest
* @param directory
* Actually a reference to a profile directory where to
* create the OfflineCache sub-dir.
* @param quota
* Optional override of the default quota.
*/
nsIApplicationCache createCustomApplicationCache(in ACString group,
in nsIFile profileDir,
in int32_t quota);
/**
* Get an application cache object for the given client ID.
*/
nsIApplicationCache getApplicationCache(in ACString clientID);
/**
* Get the currently active cache object for a cache group.
*/
nsIApplicationCache getActiveCache(in ACString group);
/**
* Deactivate the currently-active cache object for a cache group.
*/
void deactivateGroup(in ACString group);
/**
* Evict offline cache entries, either all of them or those belonging
* to the given origin.
*/
void evict(in nsILoadContextInfo aLoadContextInfo);
/**
* Delete caches whom origin attributes matches the given pattern.
*/
void evictMatchingOriginAttributes(in AString aPattern);
/**
* Try to find the best application cache to serve a resource.
*/
nsIApplicationCache chooseApplicationCache(in ACString key,
[optional] in nsILoadContextInfo aLoadContextInfo);
/**
* Flags the key as being opportunistically cached.
*
* This method should also propagate the entry to other
* application caches with the same opportunistic namespace, but
* this is not currently implemented.
*
* @param cache
* The cache in which the entry is cached now.
* @param key
* The cache entry key.
*/
void cacheOpportunistically(in nsIApplicationCache cache, in ACString key);
/**
* Get the list of application cache groups.
*/
void getGroups([optional] out unsigned long count,
[array, size_is(count), retval] out string groupIDs);
/**
* Get the list of application cache groups in the order of
* activating time.
*/
void getGroupsTimeOrdered([optional] out unsigned long count,
[array, size_is(count), retval] out string groupIDs);
};

View file

@ -0,0 +1,26 @@
/* -*- 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 "nsIInputStream.idl"
/**
* nsIArrayBufferInputStream
*
* Provides scriptable methods for initializing a nsIInputStream
* implementation with an ArrayBuffer.
*/
[scriptable, uuid(3014dde6-aa1c-41db-87d0-48764a3710f6)]
interface nsIArrayBufferInputStream : nsIInputStream
{
/**
* SetData - assign an ArrayBuffer to the input stream.
*
* @param buffer - stream data
* @param byteOffset - stream data offset
* @param byteLen - stream data length
*/
[implicit_jscontext]
void setData(in jsval buffer, in unsigned long byteOffset, in unsigned long byteLen);
};

View file

@ -0,0 +1,64 @@
/* 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 "nsIRequest.idl"
interface nsIInputStream;
interface nsIOutputStream;
interface nsIRequestObserver;
interface nsIEventTarget;
// You should prefer nsIAsyncStreamCopier2
[scriptable, uuid(5a19ca27-e041-4aca-8287-eb248d4c50c0)]
interface nsIAsyncStreamCopier : nsIRequest
{
/**
* Initialize the stream copier.
*
* @param aSource
* contains the data to be copied.
* @param aSink
* specifies the destination for the data.
* @param aTarget
* specifies the thread on which the copy will occur. a null value
* is permitted and will cause the copy to occur on an unspecified
* background thread.
* @param aSourceBuffered
* true if aSource implements ReadSegments.
* @param aSinkBuffered
* true if aSink implements WriteSegments.
* @param aChunkSize
* specifies how many bytes to read/write at a time. this controls
* the granularity of the copying. it should match the segment size
* of the "buffered" streams involved.
* @param aCloseSource
* true if aSource should be closed after copying.
* @param aCloseSink
* true if aSink should be closed after copying.
*
* NOTE: at least one of the streams must be buffered. If you do not know
* whether your streams are buffered, you should use nsIAsyncStreamCopier2
* instead.
*/
void init(in nsIInputStream aSource,
in nsIOutputStream aSink,
in nsIEventTarget aTarget,
in boolean aSourceBuffered,
in boolean aSinkBuffered,
in unsigned long aChunkSize,
in boolean aCloseSource,
in boolean aCloseSink);
/**
* asyncCopy triggers the start of the copy. The observer will be notified
* when the copy completes.
*
* @param aObserver
* receives notifications.
* @param aObserverContext
* passed to observer methods.
*/
void asyncCopy(in nsIRequestObserver aObserver,
in nsISupports aObserverContext);
};

View file

@ -0,0 +1,59 @@
/* 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 "nsIRequest.idl"
interface nsIInputStream;
interface nsIOutputStream;
interface nsIRequestObserver;
interface nsIEventTarget;
[scriptable, uuid(a5b2decf-4ede-4801-8b38-e5fe5db46bf2)]
interface nsIAsyncStreamCopier2 : nsIRequest
{
/**
* Initialize the stream copier.
*
* If neither the source nor the sink are buffered, buffering will
* be automatically added to the sink.
*
*
* @param aSource
* contains the data to be copied.
* @param aSink
* specifies the destination for the data.
* @param aTarget
* specifies the thread on which the copy will occur. a null value
* is permitted and will cause the copy to occur on an unspecified
* background thread.
* @param aChunkSize
* specifies how many bytes to read/write at a time. this controls
* the granularity of the copying. it should match the segment size
* of the "buffered" streams involved.
* @param aCloseSource
* true if aSource should be closed after copying (this is generally
* the desired behavior).
* @param aCloseSink
* true if aSink should be closed after copying (this is generally
* the desired behavior).
*/
void init(in nsIInputStream aSource,
in nsIOutputStream aSink,
in nsIEventTarget aTarget,
in unsigned long aChunkSize,
in boolean aCloseSource,
in boolean aCloseSink);
/**
* asyncCopy triggers the start of the copy. The observer will be notified
* when the copy completes.
*
* @param aObserver
* receives notifications.
* @param aObserverContext
* passed to observer methods.
*/
void asyncCopy(in nsIRequestObserver aObserver,
in nsISupports aObserverContext);
};

View file

@ -0,0 +1,19 @@
/* 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"
[scriptable, uuid(8d171460-a716-41f1-92be-8c659db39b45)]
interface nsIAsyncVerifyRedirectCallback : nsISupports
{
/**
* Complement to nsIChannelEventSink asynchronous callback. The result of
* the redirect decision is passed through this callback.
*
* @param result
* Result of the redirect veto decision. If FAILED the redirect has been
* vetoed. If SUCCEEDED the redirect has been allowed by all consumers.
*/
void onRedirectVerifyCallback(in nsresult result);
};

View file

@ -0,0 +1,118 @@
/* 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"
/**
* A object that hold authentication information. The caller of
* nsIAuthPrompt2::promptUsernameAndPassword or
* nsIAuthPrompt2::promptPasswordAsync provides an object implementing this
* interface; the prompt implementation can then read the values here to prefill
* the dialog. After the user entered the authentication information, it should
* set the attributes of this object to indicate to the caller what was entered
* by the user.
*/
[scriptable, uuid(0d73639c-2a92-4518-9f92-28f71fea5f20)]
interface nsIAuthInformation : nsISupports
{
/** @name Flags */
/* @{ */
/**
* This dialog belongs to a network host.
*/
const uint32_t AUTH_HOST = 1;
/**
* This dialog belongs to a proxy.
*/
const uint32_t AUTH_PROXY = 2;
/**
* This dialog needs domain information. The user interface should show a
* domain field, prefilled with the domain attribute's value.
*/
const uint32_t NEED_DOMAIN = 4;
/**
* This dialog only asks for password information. Authentication prompts
* SHOULD NOT show a username field. Attempts to change the username field
* will have no effect. nsIAuthPrompt2 implementations should, however, show
* its initial value to the user in some form. For example, a paragraph in
* the dialog might say "Please enter your password for user jsmith at
* server intranet".
*
* This flag is mutually exclusive with #NEED_DOMAIN.
*/
const uint32_t ONLY_PASSWORD = 8;
/**
* We have already tried to log in for this channel
* (with auth values from a previous promptAuth call),
* but it failed, so we now ask the user to provide a new, correct login.
*
* @see also RFC 2616, Section 10.4.2
*/
const uint32_t PREVIOUS_FAILED = 16;
/**
* A cross-origin sub-resource requests an authentication.
* The message presented to users must reflect that.
*/
const uint32_t CROSS_ORIGIN_SUB_RESOURCE = 32;
/* @} */
/**
* Flags describing this dialog. A bitwise OR of the flag values
* above.
*
* It is possible that neither #AUTH_HOST nor #AUTH_PROXY are set.
*
* Auth prompts should ignore flags they don't understand; especially, they
* should not throw an exception because of an unsupported flag.
*/
readonly attribute unsigned long flags;
/**
* The server-supplied realm of the authentication as defined in RFC 2617.
* Can be the empty string if the protocol does not support realms.
* Otherwise, this is a human-readable string like "Secret files".
*/
readonly attribute AString realm;
/**
* The authentication scheme used for this request, if applicable. If the
* protocol for this authentication does not support schemes, this will be
* the empty string. Otherwise, this will be a string such as "basic" or
* "digest". This string will always be in lowercase.
*/
readonly attribute AUTF8String authenticationScheme;
/**
* The initial value should be used to prefill the dialog or be shown
* in some other way to the user.
* On return, this parameter should contain the username entered by
* the user.
* This field can only be changed if the #ONLY_PASSWORD flag is not set.
*/
attribute AString username;
/**
* The initial value should be used to prefill the dialog or be shown
* in some other way to the user.
* The password should not be shown in clear.
* On return, this parameter should contain the password entered by
* the user.
*/
attribute AString password;
/**
* The initial value should be used to prefill the dialog or be shown
* in some other way to the user.
* On return, this parameter should contain the domain entered by
* the user.
* This attribute is only used if flags include #NEED_DOMAIN.
*/
attribute AString domain;
};

View file

@ -0,0 +1,145 @@
/* vim:set ts=4 sw=4 et cindent: */
/* 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"
[uuid(6e35dbc0-49ef-4e2c-b1ea-b72ec64450a2)]
interface nsIAuthModule : nsISupports
{
/**
* Default behavior.
*/
const unsigned long REQ_DEFAULT = 0;
/**
* Client and server will be authenticated.
*/
const unsigned long REQ_MUTUAL_AUTH = (1 << 0);
/**
* The server is allowed to impersonate the client. The REQ_MUTUAL_AUTH
* flag may also need to be specified in order for this flag to take
* effect.
*/
const unsigned long REQ_DELEGATE = (1 << 1);
/**
* The authentication is required for a proxy connection.
*/
const unsigned long REQ_PROXY_AUTH = (1 << 2);
/**
* Flags used for telemetry.
*/
const unsigned long NTLM_MODULE_SAMBA_AUTH_PROXY = 0;
const unsigned long NTLM_MODULE_SAMBA_AUTH_DIRECT = 1;
const unsigned long NTLM_MODULE_WIN_API_PROXY = 2;
const unsigned long NTLM_MODULE_WIN_API_DIRECT = 3;
const unsigned long NTLM_MODULE_GENERIC_PROXY = 4;
const unsigned long NTLM_MODULE_GENERIC_DIRECT = 5;
const unsigned long NTLM_MODULE_KERBEROS_PROXY = 6;
const unsigned long NTLM_MODULE_KERBEROS_DIRECT = 7;
/** Other flags may be defined in the future */
/**
* Called to initialize an auth module. The other methods cannot be called
* unless this method succeeds.
*
* @param aServiceName
* the service name, which may be null if not applicable (e.g., for
* NTLM, this parameter should be null).
* @param aServiceFlags
* a bitwise-or of the REQ_ flags defined above (pass REQ_DEFAULT
* for default behavior).
* @param aDomain
* the authentication domain, which may be null if not applicable.
* @param aUsername
* the user's login name
* @param aPassword
* the user's password
*/
void init(in string aServiceName,
in unsigned long aServiceFlags,
in wstring aDomain,
in wstring aUsername,
in wstring aPassword);
/**
* Called to get the next token in a sequence of authentication steps.
*
* @param aInToken
* A buffer containing the input token (e.g., a challenge from a
* server). This may be null.
* @param aInTokenLength
* The length of the input token.
* @param aOutToken
* If getNextToken succeeds, then aOutToken will point to a buffer
* to be sent in response to the server challenge. The length of
* this buffer is given by aOutTokenLength. The buffer at aOutToken
* must be recycled with a call to free.
* @param aOutTokenLength
* If getNextToken succeeds, then aOutTokenLength contains the
* length of the buffer (number of bytes) pointed to by aOutToken.
*/
void getNextToken([const] in voidPtr aInToken,
in unsigned long aInTokenLength,
out voidPtr aOutToken,
out unsigned long aOutTokenLength);
/**
* Once a security context has been established through calls to GetNextToken()
* it may be used to protect data exchanged between client and server. Calls
* to Wrap() are used to protect items of data to be sent to the server.
*
* @param aInToken
* A buffer containing the data to be sent to the server
* @param aInTokenLength
* The length of the input token
* @param confidential
* If set to true, Wrap() will encrypt the data, otherwise data will
* just be integrity protected (checksummed)
* @param aOutToken
* A buffer containing the resulting data to be sent to the server
* @param aOutTokenLength
* The length of the output token buffer
*
* Wrap() may return NS_ERROR_NOT_IMPLEMENTED, if the underlying authentication
* mechanism does not support security layers.
*/
void wrap([const] in voidPtr aInToken,
in unsigned long aInTokenLength,
in boolean confidential,
out voidPtr aOutToken,
out unsigned long aOutTokenLength);
/**
* Unwrap() is used to unpack, decrypt, and verify the checksums on data
* returned by a server when security layers are in use.
*
* @param aInToken
* A buffer containing the data received from the server
* @param aInTokenLength
* The length of the input token
* @param aOutToken
* A buffer containing the plaintext data from the server
* @param aOutTokenLength
* The length of the output token buffer
*
* Unwrap() may return NS_ERROR_NOT_IMPLEMENTED, if the underlying
* authentication mechanism does not support security layers.
*/
void unwrap([const] in voidPtr aInToken,
in unsigned long aInTokenLength,
out voidPtr aOutToken,
out unsigned long aOutTokenLength);
};
%{C++
/**
* nsIAuthModule implementations are registered under the following contract
* ID prefix:
*/
#define NS_AUTH_MODULE_CONTRACTID_PREFIX \
"@mozilla.org/network/auth-module;1?name="
%}

View file

@ -0,0 +1,80 @@
/* -*- 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 nsIPrompt;
[scriptable, uuid(358089f9-ee4b-4711-82fd-bcd07fc62061)]
interface nsIAuthPrompt : nsISupports
{
const uint32_t SAVE_PASSWORD_NEVER = 0;
const uint32_t SAVE_PASSWORD_FOR_SESSION = 1;
const uint32_t SAVE_PASSWORD_PERMANENTLY = 2;
/**
* Puts up a text input dialog with OK and Cancel buttons.
* Note: prompt uses separate args for the "in" and "out" values of the
* input field, whereas the other functions use a single inout arg.
* @param dialogText The title for the dialog.
* @param text The text to display in the dialog.
* @param passwordRealm The "realm" the password belongs to: e.g.
* ldap://localhost/dc=test
* @param savePassword One of the SAVE_PASSWORD_* options above.
* @param defaultText The default text to display in the text input box.
* @param result The value entered by the user if OK was
* selected.
* @return true for OK, false for Cancel
*/
boolean prompt(in wstring dialogTitle,
in wstring text,
in wstring passwordRealm,
in uint32_t savePassword,
in wstring defaultText,
out wstring result);
/**
* Puts up a username/password dialog with OK and Cancel buttons.
* Puts up a password dialog with OK and Cancel buttons.
* @param dialogText The title for the dialog.
* @param text The text to display in the dialog.
* @param passwordRealm The "realm" the password belongs to: e.g.
* ldap://localhost/dc=test
* @param savePassword One of the SAVE_PASSWORD_* options above.
* @param user The username entered in the dialog.
* @param pwd The password entered by the user if OK was
* selected.
* @return true for OK, false for Cancel
*/
boolean promptUsernameAndPassword(in wstring dialogTitle,
in wstring text,
in wstring passwordRealm,
in uint32_t savePassword,
inout wstring user,
inout wstring pwd);
/**
* Puts up a password dialog with OK and Cancel buttons.
* @param dialogText The title for the dialog.
* @param text The text to display in the dialog.
* @param passwordRealm The "realm" the password belongs to: e.g.
* ldap://localhost/dc=test. If a username is
* specified (http://user@site.com) it will be used
* when matching existing logins or saving new ones.
* If no username is specified, only password-only
* logins will be matched or saved.
* Note: if a username is specified, the username
* should be escaped.
* @param savePassword One of the SAVE_PASSWORD_* options above.
* @param pwd The password entered by the user if OK was
* selected.
* @return true for OK, false for Cancel
*/
boolean promptPassword(in wstring dialogTitle,
in wstring text,
in wstring passwordRealm,
in uint32_t savePassword,
inout wstring pwd);
};

View file

@ -0,0 +1,103 @@
/* 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 nsIAuthPromptCallback;
interface nsIChannel;
interface nsICancelable;
interface nsIAuthInformation;
/**
* An interface allowing to prompt for a username and password. This interface
* is usually acquired using getInterface on notification callbacks or similar.
* It can be used to prompt users for authentication information, either
* synchronously or asynchronously.
*/
[scriptable, uuid(651395EB-8612-4876-8AC0-A88D4DCE9E1E)]
interface nsIAuthPrompt2 : nsISupports
{
/** @name Security Levels */
/* @{ */
/**
* The password will be sent unencrypted. No security provided.
*/
const uint32_t LEVEL_NONE = 0;
/**
* Password will be sent encrypted, but the connection is otherwise
* insecure.
*/
const uint32_t LEVEL_PW_ENCRYPTED = 1;
/**
* The connection, both for password and data, is secure.
*/
const uint32_t LEVEL_SECURE = 2;
/* @} */
/**
* Requests a username and a password. Implementations will commonly show a
* dialog with a username and password field, depending on flags also a
* domain field.
*
* @param aChannel
* The channel that requires authentication.
* @param level
* One of the level constants from above. See there for descriptions
* of the levels.
* @param authInfo
* Authentication information object. The implementation should fill in
* this object with the information entered by the user before
* returning.
*
* @retval true
* Authentication can proceed using the values in the authInfo
* object.
* @retval false
* Authentication should be cancelled, usually because the user did
* not provide username/password.
*
* @note Exceptions thrown from this function will be treated like a
* return value of false.
*/
boolean promptAuth(in nsIChannel aChannel,
in uint32_t level,
in nsIAuthInformation authInfo);
/**
* Asynchronously prompt the user for a username and password.
* This has largely the same semantics as promptUsernameAndPassword(),
* but must return immediately after calling and return the entered
* data in a callback.
*
* If the user closes the dialog using a cancel button or similar,
* the callback's nsIAuthPromptCallback::onAuthCancelled method must be
* called.
* Calling nsICancelable::cancel on the returned object SHOULD close the
* dialog and MUST call nsIAuthPromptCallback::onAuthCancelled on the provided
* callback.
*
* This implementation may:
*
* 1) Coalesce identical prompts. This means prompts that are guaranteed to
* want the same auth information from the user. A single prompt will be
* shown; then the callbacks for all the coalesced prompts will be notified
* with the resulting auth information.
* 2) Serialize prompts that are all in the same "context" (this might mean
* application-wide, for a given window, or something else depending on
* the user interface) so that the user is not deluged with prompts.
*
* @throw
* This method may throw any exception when the prompt fails to queue e.g
* because of out-of-memory error. It must not throw when the prompt
* could already be potentially shown to the user. In that case information
* about the failure has to come through the callback. This way we
* prevent multiple dialogs shown to the user because consumer may fall
* back to synchronous prompt on synchronous failure of this method.
*/
nsICancelable asyncPromptAuth(in nsIChannel aChannel,
in nsIAuthPromptCallback aCallback,
in nsISupports aContext,
in uint32_t level,
in nsIAuthInformation authInfo);
};

View file

@ -0,0 +1,22 @@
/* 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 nsIAuthPrompt;
interface nsIAuthPrompt2;
/**
* An interface for wrapping nsIAuthPrompt interfaces to make
* them usable via an nsIAuthPrompt2 interface.
*/
[scriptable, uuid(60e46383-bb9a-4860-8962-80d9c5c05ddc)]
interface nsIAuthPromptAdapterFactory : nsISupports
{
/**
* Wrap an object implementing nsIAuthPrompt so that it's usable via
* nsIAuthPrompt2.
*/
nsIAuthPrompt2 createAdapter(in nsIAuthPrompt aPrompt);
};

View file

@ -0,0 +1,44 @@
/* 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 nsIAuthInformation;
/**
* Interface for callback methods for the asynchronous nsIAuthPrompt2 method.
* Callers MUST call exactly one method if nsIAuthPrompt2::promptPasswordAsync
* returns successfully. They MUST NOT call any method on this interface before
* promptPasswordAsync returns.
*/
[scriptable, uuid(bdc387d7-2d29-4cac-92f1-dd75d786631d)]
interface nsIAuthPromptCallback : nsISupports
{
/**
* Authentication information is available.
*
* @param aContext
* The context as passed to promptPasswordAsync
* @param aAuthInfo
* Authentication information. Must be the same object that was passed
* to promptPasswordAsync.
*
* @note Any exceptions thrown from this method should be ignored.
*/
void onAuthAvailable(in nsISupports aContext,
in nsIAuthInformation aAuthInfo);
/**
* Notification that the prompt was cancelled.
*
* @param aContext
* The context that was passed to promptPasswordAsync.
* @param userCancel
* If false, this prompt was cancelled by calling the
* the cancel method on the nsICancelable; otherwise,
* it was cancelled by the user.
*/
void onAuthCancelled(in nsISupports aContext, in boolean userCancel);
};

View file

@ -0,0 +1,34 @@
/* -*- Mode: idl; 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 "nsISupports.idl"
[scriptable, uuid(bd9dc0fa-68ce-47d0-8859-6418c2ae8576)]
interface nsIAuthPromptProvider : nsISupports
{
/**
* Normal (non-proxy) prompt request.
*/
const uint32_t PROMPT_NORMAL = 0;
/**
* Proxy auth request.
*/
const uint32_t PROMPT_PROXY = 1;
/**
* Request a prompt interface for the given prompt reason;
* @throws NS_ERROR_NOT_AVAILABLE if no prompt is allowed or
* available for the given reason.
*
* @param aPromptReason The reason for the auth prompt;
* one of #PROMPT_NORMAL or #PROMPT_PROXY
* @param iid The desired interface, e.g.
* NS_GET_IID(nsIAuthPrompt2).
* @returns an nsIAuthPrompt2 interface, or throws NS_ERROR_NOT_AVAILABLE
*/
void getAuthPrompt(in uint32_t aPromptReason, in nsIIDRef iid,
[iid_is(iid),retval] out nsQIResult result);
};

Some files were not shown because too many files have changed in this diff Show more