merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2017-03-28 13:03:25 +02:00
Родитель 2860ecd0e5 90cfaab28d
Коммит 5b9719c5fc
35 изменённых файлов: 966 добавлений и 207 удалений

Просмотреть файл

@ -3,7 +3,7 @@ support-files =
head.js
[browser_all_files_referenced.js]
skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
skip-if = debug || (os == 'linux' && bits == 32) # no point in running on both opt and debug, and will likely intermittently timeout on debug; oom crashes on linux32 (bug 1349307)
[browser_misused_characters_in_strings.js]
support-files =
bug1262648_string_with_newlines.dtd

Просмотреть файл

@ -1,6 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Slow on asan builds.
requestLongerTimeout(5);
var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
var gExceptionPaths = ["chrome://browser/content/defaultthemes/",

Просмотреть файл

@ -101,6 +101,22 @@ if test "$OS_TARGET" = "Android"; then
AC_MSG_ERROR([Couldn't find path to llvm-libc++ in the android ndk])
fi
if ! test -e "$cxx_include"; then
# NDK r13 removes the inner "libcxx" directory.
cxx_include="$cxx_base/include"
if ! test -e "$cxx_include"; then
AC_MSG_ERROR([Couldn't find path to libc++ includes in the android ndk])
fi
fi
if ! test -e "$cxxabi_include"; then
# NDK r13 removes the inner "libcxxabi" directory.
cxxabi_include="$cxxabi_base/include"
if ! test -e "$cxxabi_include"; then
AC_MSG_ERROR([Couldn't find path to libc++abi includes in the android ndk])
fi
fi
STLPORT_LIBS="-L$cxx_libs -lc++_static"
# NDK r12 split the libc++ runtime libraries into pieces.
for lib in c++abi unwind android_support; do
@ -111,7 +127,7 @@ if test "$OS_TARGET" = "Android"; then
# Add android/support/include/ for prototyping long double math
# functions, locale-specific C library functions, multibyte support,
# etc.
STLPORT_CPPFLAGS="-I$android_ndk/sources/android/support/include -I$cxx_include -I$cxxabi_include"
STLPORT_CPPFLAGS="-I$cxx_include -I$android_ndk/sources/android/support/include -I$cxxabi_include"
;;
*)
AC_MSG_ERROR([Bad value for --enable-android-cxx-stl])

Просмотреть файл

@ -40,12 +40,20 @@ struct OwningAnimationTarget
struct NonOwningAnimationTarget
{
NonOwningAnimationTarget() = default;
NonOwningAnimationTarget(dom::Element* aElement, CSSPseudoElementType aType)
: mElement(aElement), mPseudoType(aType) { }
explicit NonOwningAnimationTarget(const OwningAnimationTarget& aOther)
: mElement(aOther.mElement), mPseudoType(aOther.mPseudoType) { }
bool operator==(const NonOwningAnimationTarget& aOther) const
{
return mElement == aOther.mElement &&
mPseudoType == aOther.mPseudoType;
}
// mElement represents the parent element of a pseudo-element, not the
// generated content element.
dom::Element* MOZ_NON_OWNING_REF mElement = nullptr;

Просмотреть файл

@ -1495,30 +1495,37 @@ DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
return;
}
stream->SetInputStream(stringStream);
rv = stream->SetInputStream(stringStream);
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t type;
do {
stream->Read32(&type);
rv = stream->Read32(&type);
NS_ENSURE_SUCCESS_VOID(rv);
if (type == eCustomClipboardTypeId_String) {
uint32_t formatLength;
stream->Read32(&formatLength);
rv = stream->Read32(&formatLength);
NS_ENSURE_SUCCESS_VOID(rv);
char* formatBytes;
stream->ReadBytes(formatLength, &formatBytes);
rv = stream->ReadBytes(formatLength, &formatBytes);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString format;
format.Adopt(reinterpret_cast<char16_t*>(formatBytes),
formatLength / sizeof(char16_t));
uint32_t dataLength;
stream->Read32(&dataLength);
rv = stream->Read32(&dataLength);
NS_ENSURE_SUCCESS_VOID(rv);
char* dataBytes;
stream->ReadBytes(dataLength, &dataBytes);
rv = stream->ReadBytes(dataLength, &dataBytes);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString data;
data.Adopt(reinterpret_cast<char16_t*>(dataBytes),
dataLength / sizeof(char16_t));
RefPtr<nsVariantCC> variant = new nsVariantCC();
variant->SetAsAString(data);
rv = variant->SetAsAString(data);
NS_ENSURE_SUCCESS_VOID(rv);
SetDataWithPrincipal(format, variant, aIndex, aPrincipal);
}

Просмотреть файл

@ -548,7 +548,7 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"HTMLVideoElement",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "IdleDeadline", nightly: true},
{name: "IdleDeadline"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBCursor",
// IMPORTANT: Do not change this list without review from a DOM peer!

Просмотреть файл

@ -675,7 +675,6 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
do {
nsXBLBinding *binding = content->GetXBLBinding();
if (binding) {
aData->mTreeMatchContext.mScopedRoot = content;
binding->WalkRules(aFunc, aData);
// If we're not looking at our original content, allow the binding to cut
// off style inheritance
@ -698,9 +697,6 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
// in the loop.
*aCutOffInheritance = (content != nullptr);
// Null out the scoped root that we set repeatedly
aData->mTreeMatchContext.mScopedRoot = nullptr;
return NS_OK;
}

Просмотреть файл

@ -737,20 +737,21 @@ DXGITextureHostD3D11::GetDevice()
if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
return nullptr;
}
return mProvider->GetD3D11Device();
return mDevice;
}
void
DXGITextureHostD3D11::SetTextureSourceProvider(TextureSourceProvider* aProvider)
{
if (!aProvider || !aProvider->GetD3D11Device()) {
mDevice = nullptr;
mProvider = nullptr;
mTextureSource = nullptr;
return;
}
mProvider = aProvider;
mDevice = aProvider->GetD3D11Device();
if (mTextureSource) {
mTextureSource->SetTextureSourceProvider(aProvider);
@ -776,6 +777,9 @@ DXGITextureHostD3D11::LockWithoutCompositor()
// Unlike the normal Lock() function, this function may be called when
// mCompositor is nullptr such as during WebVR frame submission. So, there is
// no 'mCompositor' checking here.
if (!mDevice) {
mDevice = DeviceManagerDx::Get()->GetCompositorDevice();
}
return LockInternal();
}
@ -805,7 +809,11 @@ DXGITextureHostD3D11::LockInternal()
return false;
}
mTextureSource = new DataTextureSourceD3D11(mFormat, mProvider, mTexture);
if (mProvider) {
mTextureSource = new DataTextureSourceD3D11(mFormat, mProvider, mTexture);
} else {
mTextureSource = new DataTextureSourceD3D11(mDevice, mFormat, mTexture);
}
}
mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
@ -895,12 +903,15 @@ void
DXGIYCbCrTextureHostD3D11::SetTextureSourceProvider(TextureSourceProvider* aProvider)
{
if (!aProvider || !aProvider->GetD3D11Device()) {
mProvider = nullptr;
mTextureSources[0] = nullptr;
mTextureSources[1] = nullptr;
mTextureSources[2] = nullptr;
return;
}
mProvider = aProvider;
if (mTextureSources[0]) {
mTextureSources[0]->SetTextureSourceProvider(aProvider);
}

Просмотреть файл

@ -333,6 +333,7 @@ protected:
bool OpenSharedHandle();
RefPtr<ID3D11Device> mDevice;
RefPtr<ID3D11Texture2D> mTexture;
RefPtr<DataTextureSourceD3D11> mTextureSource;
gfx::IntSize mSize;

Просмотреть файл

@ -1938,9 +1938,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
return true;
error:
flags.isDirtyLine = true;
tp->pos.end = userbuf.offset();
MOZ_MAKE_MEM_UNDEFINED(&tp->type, sizeof(tp->type));
// We didn't get a token, so don't set |flags.isDirtyLine|. And don't
// poison any of |*tp|: if we haven't allocated a token, |tp| could be
// uninitialized.
flags.hadError = true;
#ifdef DEBUG
// Poisoning userbuf on error establishes an invariant: once an erroneous

Просмотреть файл

@ -0,0 +1,143 @@
// Constraints on this test's appearance:
//
// * |TokenStream::SourceCoords::add| must try to allocate memory. (This test
// ensures this happens by making the function below >=128 lines long so
// that |SourceCoords::lineStartOffsets_| must convert to heap storage. The
// precise approach doesn't matter.)
// * That allocation attempt must fail (by forced simulated OOM, here).
//
// It'd be nice to build up the function programmatically, but it appears that
// the above only happens if the provided function has a lazy script. Cursory
// attempts to relazify |Function("...")| didn't work, so this fuzzer-like
// version had to be used instead.
if ("oomTest" in this) {
oomTest(function() {
try {
} catch(e) {
;
}
})
}

Просмотреть файл

@ -13,6 +13,7 @@
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/AnimationTarget.h"
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
#include "mozilla/Assertions.h"
#include "mozilla/TimingParams.h"
@ -111,48 +112,48 @@ protected:
class OwningElementRef final
{
public:
OwningElementRef()
: mElement(nullptr)
, mPseudoType(CSSPseudoElementType::NotPseudo)
OwningElementRef() = default;
explicit OwningElementRef(const NonOwningAnimationTarget& aTarget)
: mTarget(aTarget)
{ }
OwningElementRef(dom::Element& aElement,
CSSPseudoElementType aPseudoType)
: mElement(&aElement)
, mPseudoType(aPseudoType)
: mTarget(&aElement, aPseudoType)
{ }
bool Equals(const OwningElementRef& aOther) const
{
return mElement == aOther.mElement &&
mPseudoType == aOther.mPseudoType;
return mTarget == aOther.mTarget;
}
bool LessThan(const OwningElementRef& aOther) const
{
MOZ_ASSERT(mElement && aOther.mElement,
MOZ_ASSERT(mTarget.mElement && aOther.mTarget.mElement,
"Elements to compare should not be null");
if (mElement != aOther.mElement) {
return nsContentUtils::PositionIsBefore(mElement, aOther.mElement);
if (mTarget.mElement != aOther.mTarget.mElement) {
return nsContentUtils::PositionIsBefore(mTarget.mElement,
aOther.mTarget.mElement);
}
return mPseudoType == CSSPseudoElementType::NotPseudo ||
(mPseudoType == CSSPseudoElementType::before &&
aOther.mPseudoType == CSSPseudoElementType::after);
return mTarget.mPseudoType == CSSPseudoElementType::NotPseudo ||
(mTarget.mPseudoType == CSSPseudoElementType::before &&
aOther.mTarget.mPseudoType == CSSPseudoElementType::after);
}
bool IsSet() const { return !!mElement; }
bool IsSet() const { return !!mTarget.mElement; }
void GetElement(dom::Element*& aElement,
CSSPseudoElementType& aPseudoType) const {
aElement = mElement;
aPseudoType = mPseudoType;
CSSPseudoElementType& aPseudoType) const
{
aElement = mTarget.mElement;
aPseudoType = mTarget.mPseudoType;
}
private:
dom::Element* MOZ_NON_OWNING_REF mElement;
CSSPseudoElementType mPseudoType;
NonOwningAnimationTarget mTarget;
};
template <class EventInfo>

Просмотреть файл

@ -70,7 +70,6 @@ CSS_PSEUDO_CLASS(empty, ":empty", 0, "")
CSS_PSEUDO_CLASS(mozOnlyWhitespace, ":-moz-only-whitespace", 0, "")
CSS_PSEUDO_CLASS(mozEmptyExceptChildrenWithLocalname, ":-moz-empty-except-children-with-localname", 0, "")
CSS_PSEUDO_CLASS(lang, ":lang", 0, "")
CSS_PSEUDO_CLASS(mozBoundElement, ":-moz-bound-element", 0, "")
CSS_PSEUDO_CLASS(root, ":root", 0, "")
CSS_PSEUDO_CLASS(any, ":-moz-any", 0, "")

Просмотреть файл

@ -1968,12 +1968,6 @@ static bool SelectorMatches(Element* aElement,
}
break;
case CSSPseudoClassType::mozBoundElement:
if (aTreeMatchContext.mScopedRoot != aElement) {
return false;
}
break;
case CSSPseudoClassType::root:
if (aElement != aElement->OwnerDoc()->GetRootElement()) {
return false;

Просмотреть файл

@ -342,10 +342,6 @@ struct MOZ_STACK_CLASS TreeMatchContext {
// The document we're working with.
nsIDocument* const mDocument;
// Root of scoped stylesheet (set and unset by the supplier of the
// scoped stylesheet).
nsIContent* mScopedRoot;
// Whether our document is HTML (as opposed to XML of some sort,
// including XHTML).
// XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
@ -396,7 +392,6 @@ struct MOZ_STACK_CLASS TreeMatchContext {
, mHaveSpecifiedScope(false)
, mVisitedHandling(aVisitedHandling)
, mDocument(aDocument)
, mScopedRoot(nullptr)
, mIsHTMLDocument(aDocument->IsHTMLDocument())
, mCompatMode(aDocument->GetCompatibilityMode())
, mUsingPrivateBrowsing(false)

Просмотреть файл

@ -1624,6 +1624,10 @@ pref("network.http.keep_empty_response_headers_as_empty_string", true);
// Max size, in bytes, for received HTTP response header.
pref("network.http.max_response_header_size", 393216);
// The ratio of the transaction count for the focused window and the count of
// all available active connections.
pref("network.http.focused_window_transaction_ratio", "0.9");
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)

Просмотреть файл

@ -58,7 +58,7 @@ nsHttpConnectionMgr::OnMsgPrintDiagnostics(int32_t, ARefBase *)
mLogData.AppendPrintf(" RestrictConnections = %d\n",
RestrictConnections(ent));
mLogData.AppendPrintf(" Pending Q Length = %" PRIuSIZE "\n",
ent->mPendingQ.Length());
ent->PendingQLength());
mLogData.AppendPrintf(" Active Conns Length = %" PRIuSIZE "\n",
ent->mActiveConns.Length());
mLogData.AppendPrintf(" Idle Conns Length = %" PRIuSIZE "\n",
@ -83,9 +83,17 @@ nsHttpConnectionMgr::OnMsgPrintDiagnostics(int32_t, ARefBase *)
mLogData.AppendPrintf(" :: Half Open #%u\n", i);
ent->mHalfOpens[i]->PrintDiagnostics(mLogData);
}
for (i = 0; i < ent->mPendingQ.Length(); ++i) {
mLogData.AppendPrintf(" :: Pending Transaction #%u\n", i);
ent->mPendingQ[i]->PrintDiagnostics(mLogData);
i = 0;
for (auto it = ent->mPendingTransactionTable.Iter();
!it.Done();
it.Next()) {
mLogData.AppendPrintf(" :: Pending Transactions with Window ID = %"
PRIu64 "\n", it.Key());
for (uint32_t j = 0; j < it.UserData()->Length(); ++j) {
mLogData.AppendPrintf(" ::: Pending Transaction #%u\n", i);
it.UserData()->ElementAt(j)->PrintDiagnostics(mLogData);
++i;
}
}
for (i = 0; i < ent->mCoalescingKeys.Length(); ++i) {
mLogData.AppendPrintf(" :: Coalescing Key #%u %s\n",
@ -198,10 +206,10 @@ nsHttpTransaction::PrintDiagnostics(nsCString &log)
nsAutoCString requestURI;
mRequestHead->RequestURI(requestURI);
log.AppendPrintf(" ::: uri = %s\n", requestURI.get());
log.AppendPrintf(" caps = 0x%x\n", mCaps);
log.AppendPrintf(" priority = %d\n", mPriority);
log.AppendPrintf(" restart count = %u\n", mRestartCount);
log.AppendPrintf(" :::: uri = %s\n", requestURI.get());
log.AppendPrintf(" caps = 0x%x\n", mCaps);
log.AppendPrintf(" priority = %d\n", mPriority);
log.AppendPrintf(" restart count = %u\n", mRestartCount);
}
void

Просмотреть файл

@ -306,6 +306,13 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(primingChannel);
NS_ENSURE_STATE(internal);
// Since this is a perfomrance critical request (blocks the page load) we
// want to get the response ASAP.
nsCOMPtr<nsIClassOfService> classOfService(do_QueryInterface(primingChannel));
if (classOfService) {
classOfService->AddClassFlags(nsIClassOfService::UrgentStart);
}
// Currently using HEAD per the draft, but under discussion to change to GET
// with credentials so if the upgrade is approved the result is already cached.
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));

Просмотреть файл

@ -1372,6 +1372,19 @@ NS_IMETHODIMP HttpBaseChannel::GetTopLevelContentWindowId(uint64_t *aWindowId)
return NS_OK;
}
NS_IMETHODIMP HttpBaseChannel::SetTopLevelOuterContentWindowId(uint64_t aWindowId)
{
mTopLevelOuterContentWindowId = aWindowId;
return NS_OK;
}
NS_IMETHODIMP HttpBaseChannel::GetTopLevelOuterContentWindowId(uint64_t *aWindowId)
{
EnsureTopLevelOuterContentWindowId();
*aWindowId = mTopLevelOuterContentWindowId;
return NS_OK;
}
NS_IMETHODIMP HttpBaseChannel::SetTopLevelContentWindowId(uint64_t aWindowId)
{
mContentWindowId = aWindowId;

Просмотреть файл

@ -206,6 +206,8 @@ public:
NS_IMETHOD SetChannelId(const nsACString& aChannelId) override;
NS_IMETHOD GetTopLevelContentWindowId(uint64_t *aContentWindowId) override;
NS_IMETHOD SetTopLevelContentWindowId(uint64_t aContentWindowId) override;
NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t *aWindowId) override;
NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override;
NS_IMETHOD GetIsTrackingResource(bool* aIsTrackingResource) override;
// nsIHttpChannelInternal
@ -360,11 +362,6 @@ public: /* Necko internal use only... */
mIsTrackingResource = true;
}
void SetTopLevelOuterContentWindowId(uint64_t aTopLevelOuterContentWindowId)
{
mTopLevelOuterContentWindowId = aTopLevelOuterContentWindowId;
}
protected:
// Handle notifying listener, removing from loadgroup if request failed.
void DoNotifyListener();

Просмотреть файл

@ -81,6 +81,18 @@ NullHttpChannel::SetTopLevelContentWindowId(uint64_t aWindowId)
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::GetTopLevelOuterContentWindowId(uint64_t *aWindowId)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::SetTopLevelOuterContentWindowId(uint64_t aWindowId)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::GetIsTrackingResource(bool* aIsTrackingResource)
{

Просмотреть файл

@ -50,6 +50,12 @@ public:
return PR_SecondsToInterval(15);
}
// We have to override this function because |mTransaction| in nsHalfOpenSocket
// could be either nsHttpTransaction or NullHttpTransaction.
// NullHttpTransaction will be activated on the connection immediately after
// creation and be never put in a pending queue, so it's OK to just return 0.
uint64_t TopLevelOuterContentWindowId() override { return 0; }
protected:
virtual ~NullHttpTransaction();

Просмотреть файл

@ -183,6 +183,11 @@ public:
virtual MOZ_MUST_USE nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) {
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual uint64_t TopLevelOuterContentWindowId() {
MOZ_ASSERT(false);
return 0;
}
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID)

Просмотреть файл

@ -35,6 +35,7 @@
#include "mozilla/Unused.h"
#include "nsIURI.h"
#include "mozilla/Move.h"
#include "mozilla/Telemetry.h"
namespace mozilla {
@ -550,7 +551,7 @@ nsHttpConnectionMgr::ClearConnectionHistory()
ent->mActiveConns.Length() == 0 &&
ent->mHalfOpens.Length() == 0 &&
ent->mUrgentStartQ.Length() == 0 &&
ent->mPendingQ.Length() == 0) {
ent->PendingQLength() == 0) {
iter.Remove();
}
}
@ -635,10 +636,18 @@ nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
return preferred;
}
if (trans &&
(preferred->mPendingQ.Contains(trans, PendingComparator()) ||
preferred->mUrgentStartQ.Contains(trans, PendingComparator()))) {
return preferred;
if (trans) {
if (preferred->mUrgentStartQ.Contains(trans, PendingComparator())) {
return preferred;
}
nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
if (preferred->mPendingTransactionTable.Get(
trans->TopLevelOuterContentWindowId(), &infoArray)) {
if (infoArray->Contains(trans, PendingComparator())) {
return preferred;
}
}
}
// Neither conn nor trans found in preferred, use the default entry
@ -879,15 +888,17 @@ nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
}
//-----------------------------------------------------------------------------
void
bool
nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
nsConnectionEntry *ent,
bool &dispatchedSuccessfully,
bool considerAll)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
PendingTransactionInfo *pendingTransInfo = nullptr;
nsresult rv;
bool dispatchedSuccessfully = false;
// if !considerAll iterate the pending list until one is dispatched successfully.
// Keep iterating afterwards only until a transaction fails to dispatch.
// if considerAll == true then try and dispatch all items.
@ -971,6 +982,35 @@ nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::Pendi
++i;
}
return dispatchedSuccessfully;
}
uint32_t
nsHttpConnectionMgr::AvailableNewConnectionCount(nsConnectionEntry * ent)
{
// Add in the in-progress tcp connections, we will assume they are
// keepalive enabled.
// Exclude half-open's that has already created a usable connection.
// This prevents the limit being stuck on ipv6 connections that
// eventually time out after typical 21 seconds of no ACK+SYN reply.
uint32_t totalCount =
ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
uint16_t maxPersistConns;
if (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) {
maxPersistConns = mMaxPersistConnsPerProxy;
} else {
maxPersistConns = mMaxPersistConnsPerHost;
}
LOG(("nsHttpConnectionMgr::AvailableNewConnectionCount "
"total connection count = %d, limit %d\n",
totalCount, maxPersistConns));
return maxPersistConns > totalCount
? maxPersistConns - totalCount
: 0;
}
bool
@ -983,17 +1023,112 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool consid
"queued=%" PRIuSIZE "]\n",
ent->mConnInfo->HashKey().get(), ent, ent->mActiveConns.Length(),
ent->mIdleConns.Length(), ent->mUrgentStartQ.Length(),
ent->mPendingQ.Length()));
ent->PendingQLength()));
ProcessSpdyPendingQ(ent);
bool dispatchedSuccessfully = false;
DispatchPendingQ(ent->mUrgentStartQ, ent, dispatchedSuccessfully, considerAll);
if (!ent->mUrgentStartQ.IsEmpty()) {
dispatchedSuccessfully = DispatchPendingQ(ent->mUrgentStartQ,
ent,
considerAll);
}
if (dispatchedSuccessfully && !considerAll) {
return dispatchedSuccessfully;
}
DispatchPendingQ(ent->mPendingQ, ent, dispatchedSuccessfully, considerAll);
uint32_t availableConnections = AvailableNewConnectionCount(ent);
// No need to try dispatching if we reach the active connection limit.
if (!availableConnections) {
return dispatchedSuccessfully;
}
uint32_t maxFocusedWindowConnections =
availableConnections * gHttpHandler->FocusedWindowTransactionRatio();
MOZ_ASSERT(maxFocusedWindowConnections < availableConnections);
if (!maxFocusedWindowConnections) {
maxFocusedWindowConnections = 1;
}
// Only need to dispatch transactions for either focused or
// non-focused window because considerAll is false.
if (!considerAll) {
nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
ent->AppendPendingQForFocusedWindow(
mCurrentTopLevelOuterContentWindowId,
pendingQ,
maxFocusedWindowConnections);
if (pendingQ.IsEmpty()) {
ent->AppendPendingQForNonFocusedWindows(
mCurrentTopLevelOuterContentWindowId,
pendingQ,
availableConnections);
}
dispatchedSuccessfully |=
DispatchPendingQ(pendingQ, ent, considerAll);
// Put the leftovers into connection entry
for (const auto& transactionInfo : pendingQ) {
ent->InsertTransaction(transactionInfo);
}
return dispatchedSuccessfully;
}
uint32_t maxNonFocusedWindowConnections =
availableConnections - maxFocusedWindowConnections;
nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
nsTArray<RefPtr<PendingTransactionInfo>> remainingPendingQ;
ent->AppendPendingQForFocusedWindow(
mCurrentTopLevelOuterContentWindowId,
pendingQ,
maxFocusedWindowConnections);
if (maxNonFocusedWindowConnections) {
ent->AppendPendingQForNonFocusedWindows(
mCurrentTopLevelOuterContentWindowId,
remainingPendingQ,
maxNonFocusedWindowConnections);
}
// If the slots for the non-focused window are not filled up to
// the availability, try to use the remaining available connections
// for the focused window (with preference for the focused window).
if (remainingPendingQ.Length() < maxNonFocusedWindowConnections) {
ent->AppendPendingQForFocusedWindow(
mCurrentTopLevelOuterContentWindowId,
pendingQ,
maxNonFocusedWindowConnections - remainingPendingQ.Length());
}
MOZ_ASSERT(pendingQ.Length() + remainingPendingQ.Length() <=
availableConnections);
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
"pendingQ.Length()=%" PRIuSIZE
", remainingPendingQ.Length()=%" PRIuSIZE "\n",
pendingQ.Length(), remainingPendingQ.Length()));
// Append elements in |remainingPendingQ| to |pendingQ|. The order in
// |pendingQ| is like: [focusedWindowTrans...nonFocusedWindowTrans].
pendingQ.AppendElements(Move(remainingPendingQ));
dispatchedSuccessfully |=
DispatchPendingQ(pendingQ, ent, considerAll);
// Put the leftovers into connection entry
for (const auto& transactionInfo : pendingQ) {
ent->InsertTransaction(transactionInfo);
}
// Only remove empty pendingQ when considerAll is true.
ent->RemoveEmptyPendingQ();
return dispatchedSuccessfully;
}
@ -1021,23 +1156,10 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t ca
LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
ci->HashKey().get(), caps));
// Add in the in-progress tcp connections, we will assume they are
// keepalive enabled.
// Exclude half-open's that has already created a usable connection.
// This prevents the limit being stuck on ipv6 connections that
// eventually time out after typical 21 seconds of no ACK+SYN reply.
uint32_t totalCount =
ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
uint16_t maxPersistConns;
if (ci->UsingHttpProxy() && !ci->UsingConnect())
maxPersistConns = mMaxPersistConnsPerProxy;
else
maxPersistConns = mMaxPersistConnsPerHost;
uint32_t availableConnections = AvailableNewConnectionCount(ent);
if (caps & NS_HTTP_URGENT_START) {
if (totalCount >= static_cast<uint32_t>(mMaxUrgentStartQ + maxPersistConns)) {
if (availableConnections > static_cast<uint32_t>(mMaxUrgentStartQ)) {
LOG(("The number of total connections are greater than or equal to sum of "
"max urgent-start queue length and the number of max persistent connections.\n"));
return true;
@ -1062,11 +1184,8 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t ca
return true;
}
LOG((" connection count = %d, limit %d\n", totalCount, maxPersistConns));
// use >= just to be safe
bool result = (totalCount >= maxPersistConns);
LOG((" result: %s", result ? "true" : "false"));
bool result = !availableConnections;
LOG(("AtActiveConnectionLimit result: %s", result ? "true" : "false"));
return result;
}
@ -1711,15 +1830,15 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
if (trans->Caps() & NS_HTTP_URGENT_START) {
LOG((" adding transaction to pending queue "
"[trans=%p urgent-start-count=%" PRIuSIZE "]\n",
trans, ent->mUrgentStartQ.Length()+1));
trans, ent->mUrgentStartQ.Length() + 1));
// put this transaction on the urgent-start queue...
InsertTransactionSorted(ent->mUrgentStartQ, pendingTransInfo);
} else {
LOG((" adding transaction to pending queue "
"[trans=%p pending-count=%" PRIuSIZE "]\n",
trans, ent->mPendingQ.Length()+1));
trans, ent->PendingQLength() + 1));
// put this transaction on the pending queue...
InsertTransactionSorted(ent->mPendingQ, pendingTransInfo);
ent->InsertTransaction(pendingTransInfo);
}
return NS_OK;
}
@ -1893,11 +2012,21 @@ nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
if (!conn || !conn->CanDirectlyActivate()) {
return;
}
DispatchSpdyPendingQ(ent->mUrgentStartQ, ent, conn);
if (!conn->CanDirectlyActivate()) {
return;
}
DispatchSpdyPendingQ(ent->mPendingQ, ent, conn);
nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
// XXX Get all transactions for SPDY currently.
ent->AppendPendingQForNonFocusedWindows(0, pendingQ);
DispatchSpdyPendingQ(pendingQ, ent, conn);
// Put the leftovers back in the pending queue.
for (const auto& transactionInfo : pendingQ) {
ent->InsertTransaction(transactionInfo);
}
}
void
@ -2012,11 +2141,16 @@ nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
}
// Close all pending transactions.
while (ent->mPendingQ.Length()) {
PendingTransactionInfo *pendingTransInfo = ent->mPendingQ[0];
pendingTransInfo->mTransaction->Close(NS_ERROR_ABORT);
ent->mPendingQ.RemoveElementAt(0);
for (auto it = ent->mPendingTransactionTable.Iter();
!it.Done();
it.Next()) {
while (it.UserData()->Length()) {
PendingTransactionInfo *pendingTransInfo = (*it.UserData())[0];
pendingTransInfo->mTransaction->Close(NS_ERROR_ABORT);
it.UserData()->RemoveElementAt(0);
}
}
ent->mPendingTransactionTable.Clear();
// Close all half open tcp connections.
for (int32_t i = int32_t(ent->mHalfOpens.Length()) - 1; i >= 0; i--) {
@ -2083,14 +2217,18 @@ nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
if (ent) {
int32_t caps = trans->Caps();
nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ;
nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ = nullptr;
if (caps & NS_HTTP_URGENT_START) {
pendingQ = &(ent->mUrgentStartQ);
} else {
pendingQ = &(ent->mPendingQ);
pendingQ =
ent->mPendingTransactionTable.Get(
trans->TopLevelOuterContentWindowId());
}
int32_t index = pendingQ->IndexOf(trans, 0, PendingComparator());
int32_t index = pendingQ
? pendingQ->IndexOf(trans, 0, PendingComparator())
: -1;
if (index >= 0) {
RefPtr<PendingTransactionInfo> pendingTransInfo = (*pendingQ)[index];
pendingQ->RemoveElementAt(index);
@ -2127,29 +2265,25 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, ARefBase *param)
int32_t transIndex;
// We will abandon all half-open sockets belonging to the given
// transaction.
nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
RefPtr<PendingTransactionInfo> pendingTransInfo;
if (caps & NS_HTTP_URGENT_START) {
transIndex = ent->mUrgentStartQ.IndexOf(trans, 0,
PendingComparator());
if (transIndex >=0) {
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
" found in urgentStart queue\n", trans));
pendingTransInfo = ent->mUrgentStartQ[transIndex];
// We do not need to ReleaseClaimedSockets while we are
// going to close them all any way!
ent->mUrgentStartQ.RemoveElementAt(transIndex);
}
infoArray = &ent->mUrgentStartQ;
} else {
transIndex = ent->mPendingQ.IndexOf(trans, 0,
PendingComparator());
if (transIndex >=0) {
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
" found in pending queue\n", trans));
pendingTransInfo = ent->mPendingQ[transIndex];
// We do not need to ReleaseClaimedSockets while we are
// going to close them all any way!
ent->mPendingQ.RemoveElementAt(transIndex);
}
infoArray = ent->mPendingTransactionTable.Get(
trans->TopLevelOuterContentWindowId());
}
transIndex = infoArray
? infoArray->IndexOf(trans, 0, PendingComparator())
: -1;
if (transIndex >=0) {
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
" found in urgentStart queue\n", trans));
pendingTransInfo = (*infoArray)[transIndex];
// We do not need to ReleaseClaimedSockets while we are
// going to close them all any way!
infoArray->RemoveElementAt(transIndex);
}
// Abandon all half-open sockets belonging to the given transaction.
@ -2228,6 +2362,21 @@ nsHttpConnectionMgr::CancelTransactions(nsHttpConnectionInfo *ci, nsresult code)
return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransactions, intReason, ci);
}
void
nsHttpConnectionMgr::CancelTransactionsHelper(
nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo>> &pendingQ,
const nsHttpConnectionInfo *ci,
const nsHttpConnectionMgr::nsConnectionEntry *ent,
nsresult reason)
{
for (const auto& pendingTransInfo : pendingQ) {
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
ci->HashKey().get(), ent, pendingTransInfo->mTransaction.get()));
pendingTransInfo->mTransaction->Close(reason);
}
pendingQ.Clear();
}
void
nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, ARefBase *param)
{
@ -2240,23 +2389,14 @@ nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, ARefBase *param)
return;
}
for (uint32_t i = ent->mUrgentStartQ.Length(); i > 0;) {
--i;
PendingTransactionInfo *pendingTransInfo = ent->mUrgentStartQ[i];
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
ci->HashKey().get(), ent, pendingTransInfo->mTransaction.get()));
pendingTransInfo->mTransaction->Close(reason);
ent->mUrgentStartQ.RemoveElementAt(i);
}
CancelTransactionsHelper(ent->mUrgentStartQ, ci, ent, reason);
for (uint32_t i = ent->mPendingQ.Length(); i > 0;) {
--i;
PendingTransactionInfo *pendingTransInfo = ent->mPendingQ[i];
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
ci->HashKey().get(), ent, pendingTransInfo->mTransaction.get()));
pendingTransInfo->mTransaction->Close(reason);
ent->mPendingQ.RemoveElementAt(i);
for (auto it = ent->mPendingTransactionTable.Iter();
!it.Done();
it.Next()) {
CancelTransactionsHelper(*it.UserData(), ci, ent, reason);
}
ent->mPendingTransactionTable.Clear();
}
void
@ -2326,13 +2466,15 @@ nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, ARefBase *)
ConditionallyStopPruneDeadConnectionsTimer();
}
ent->RemoveEmptyPendingQ();
// If this entry is empty, we have too many entries busy then
// we can clean it up and restart
if (mCT.Count() > 125 &&
ent->mIdleConns.Length() == 0 &&
ent->mActiveConns.Length() == 0 &&
ent->mHalfOpens.Length() == 0 &&
ent->mPendingQ.Length() == 0 &&
ent->PendingQLength() == 0 &&
ent->mUrgentStartQ.Length() == 0 &&
(!ent->mUsingSpdy || mCT.Count() > 300)) {
LOG((" removing empty connection entry\n"));
@ -2344,7 +2486,12 @@ nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, ARefBase *)
ent->mIdleConns.Compact();
ent->mActiveConns.Compact();
ent->mUrgentStartQ.Compact();
ent->mPendingQ.Compact();
for (auto it = ent->mPendingTransactionTable.Iter();
!it.Done();
it.Next()) {
it.UserData()->Compact();
}
}
}
}
@ -2692,7 +2839,7 @@ nsHttpConnectionMgr::TimeoutTick()
" urgentStart pending=%" PRIuSIZE "\n",
this, ent->mConnInfo->Origin(), ent->mIdleConns.Length(),
ent->mActiveConns.Length(), ent->mHalfOpens.Length(),
ent->mPendingQ.Length(), ent->mUrgentStartQ.Length()));
ent->PendingQLength(), ent->mUrgentStartQ.Length()));
// First call the tick handler for each active connection.
PRIntervalTime tickTime = PR_IntervalNow();
@ -3225,6 +3372,34 @@ nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
return NS_OK;
}
already_AddRefed<nsHttpConnectionMgr::PendingTransactionInfo>
nsHttpConnectionMgr::
nsHalfOpenSocket::FindTransactionHelper(bool removeWhenFound)
{
uint32_t caps = mTransaction->Caps();
nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ = nullptr;
if (caps & NS_HTTP_URGENT_START) {
pendingQ = &mEnt->mUrgentStartQ;
} else {
pendingQ =
mEnt->mPendingTransactionTable.Get(
mTransaction->TopLevelOuterContentWindowId());
}
int32_t index = pendingQ
? pendingQ->IndexOf(mTransaction, 0, PendingComparator())
: -1;
RefPtr<PendingTransactionInfo> info;
if (index != -1) {
info = (*pendingQ)[index];
if (removeWhenFound) {
pendingQ->RemoveElementAt(index);
}
}
return info.forget();
}
// method for nsIAsyncOutputStreamCallback
NS_IMETHODIMP
nsHttpConnectionMgr::
@ -3305,28 +3480,14 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
mHasConnected = true;
// if this is still in the pending list, remove it and dispatch it
uint32_t caps = mTransaction->Caps();
int32_t index;
if (caps & NS_HTTP_URGENT_START) {
index = mEnt->mUrgentStartQ.IndexOf(mTransaction, 0,
PendingComparator());
} else {
index = mEnt->mPendingQ.IndexOf(mTransaction, 0, PendingComparator());
}
if (index > -1) {
RefPtr<PendingTransactionInfo> pendingTransInfo = FindTransactionHelper(true);
if (pendingTransInfo) {
MOZ_ASSERT(!mSpeculative,
"Speculative Half Open found mTransaction");
RefPtr<PendingTransactionInfo> temp;
if (caps & NS_HTTP_URGENT_START) {
temp = mEnt->mUrgentStartQ[index];
mEnt->mUrgentStartQ.RemoveElementAt(index);
} else {
temp = mEnt->mPendingQ[index];
mEnt->mPendingQ.RemoveElementAt(index);
}
gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt,
temp->mTransaction,
pendingTransInfo->mTransaction,
conn);
} else {
// this transaction was dispatched off the pending q before all the
@ -3344,7 +3505,7 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
// NullHttpTransaction does not know how to drive Connect
if (mEnt->mConnInfo->FirstHopSSL() &&
!mEnt->mUrgentStartQ.Length() &&
!mEnt->mPendingQ.Length() &&
!mEnt->PendingQLength() &&
!mEnt->mConnInfo->UsingConnect()) {
LOG(("nsHalfOpenSocket::OnOutputStreamReady null transaction will "
"be used to finish SSL handshake on conn %p\n", conn.get()));
@ -3383,9 +3544,10 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mTransaction) {
RefPtr<PendingTransactionInfo> info = FindTransactionHelper(false);
if ((trans == mSocketTransport) ||
((trans == mBackupTransport) && (status == NS_NET_STATUS_CONNECTED_TO) &&
mEnt->mPendingQ.Contains(mTransaction, PendingComparator()))) {
info)) {
// Send this status event only if the transaction is still panding,
// i.e. it has not found a free already connected socket.
// Sockets in halfOpen state can only get following events:
@ -3656,6 +3818,115 @@ nsConnectionEntry::ResetIPFamilyPreference()
mPreferIPv6 = false;
}
size_t
nsHttpConnectionMgr::nsConnectionEntry::PendingQLength() const
{
size_t length = 0;
for (auto it = mPendingTransactionTable.ConstIter(); !it.Done(); it.Next()) {
length += it.UserData()->Length();
}
return length;
}
void
nsHttpConnectionMgr::
nsConnectionEntry::InsertTransaction(PendingTransactionInfo *info)
{
LOG(("nsHttpConnectionMgr::nsConnectionEntry::InsertTransaction"
" trans=%p, windowId=%" PRIu64 "\n",
info->mTransaction.get(),
info->mTransaction->TopLevelOuterContentWindowId()));
uint64_t windowId = info->mTransaction->TopLevelOuterContentWindowId();
nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
if (!mPendingTransactionTable.Get(windowId, &infoArray)) {
infoArray = new nsTArray<RefPtr<PendingTransactionInfo>>();
mPendingTransactionTable.Put(windowId, infoArray);
}
gHttpHandler->ConnMgr()->InsertTransactionSorted(*infoArray, info);
}
void
nsHttpConnectionMgr::
nsConnectionEntry::AppendPendingQForFocusedWindow(
uint64_t windowId,
nsTArray<RefPtr<PendingTransactionInfo>> &result,
uint32_t maxCount)
{
nsTArray<RefPtr<PendingTransactionInfo>> *infoArray = nullptr;
if (!mPendingTransactionTable.Get(windowId, &infoArray)) {
result.Clear();
return;
}
uint32_t countToAppend = maxCount;
countToAppend =
countToAppend > infoArray->Length() || countToAppend == 0 ?
infoArray->Length() :
countToAppend;
result.InsertElementsAt(result.Length(),
infoArray->Elements(),
countToAppend);
infoArray->RemoveElementsAt(0, countToAppend);
LOG(("nsConnectionEntry::AppendPendingQForFocusedWindow [ci=%s], "
"pendingQ count=%" PRIuSIZE " for focused window (id=%" PRIu64 ")\n",
mConnInfo->HashKey().get(), result.Length(),
windowId));
}
void
nsHttpConnectionMgr::
nsConnectionEntry::AppendPendingQForNonFocusedWindows(
uint64_t windowId,
nsTArray<RefPtr<PendingTransactionInfo>> &result,
uint32_t maxCount)
{
// XXX Adjust the order of transactions in a smarter manner.
uint32_t totalCount = 0;
for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) {
if (windowId && it.Key() == windowId) {
continue;
}
uint32_t count = 0;
for (; count < it.UserData()->Length(); ++count) {
if (maxCount && totalCount == maxCount) {
break;
}
// Because elements in |result| could come from multiple penndingQ,
// call |InsertTransactionSorted| to make sure the order is correct.
gHttpHandler->ConnMgr()->InsertTransactionSorted(
result,
it.UserData()->ElementAt(count));
++totalCount;
}
it.UserData()->RemoveElementsAt(0, count);
if (maxCount && totalCount == maxCount) {
break;
}
}
LOG(("nsConnectionEntry::AppendPendingQForNonFocusedWindows [ci=%s], "
"pendingQ count=%" PRIuSIZE " for non focused window\n",
mConnInfo->HashKey().get(), result.Length()));
}
void
nsHttpConnectionMgr::nsConnectionEntry::RemoveEmptyPendingQ()
{
for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) {
if (it.UserData()->IsEmpty()) {
it.Remove();
}
}
}
void
nsHttpConnectionMgr::MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
nsHttpConnectionInfo *wildCardCI,
@ -3686,12 +3957,12 @@ nsHttpConnectionMgr::MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard ent %p "
"idle=%" PRIuSIZE " active=%" PRIuSIZE " half=%" PRIuSIZE " pending=%" PRIuSIZE "\n",
ent, ent->mIdleConns.Length(), ent->mActiveConns.Length(),
ent->mHalfOpens.Length(), ent->mPendingQ.Length()));
ent->mHalfOpens.Length(), ent->PendingQLength()));
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard wc-ent %p "
"idle=%" PRIuSIZE " active=%" PRIuSIZE " half=%" PRIuSIZE " pending=%" PRIuSIZE "\n",
wcEnt, wcEnt->mIdleConns.Length(), wcEnt->mActiveConns.Length(),
wcEnt->mHalfOpens.Length(), wcEnt->mPendingQ.Length()));
wcEnt->mHalfOpens.Length(), wcEnt->PendingQLength()));
int32_t count = ent->mActiveConns.Length();
RefPtr<nsHttpConnection> deleteProtector(proxyConn);

Просмотреть файл

@ -221,7 +221,13 @@ private:
RefPtr<nsHttpConnectionInfo> mConnInfo;
nsTArray<RefPtr<PendingTransactionInfo> > mUrgentStartQ;// the urgent start transaction queue
nsTArray<RefPtr<PendingTransactionInfo> > mPendingQ; // pending transaction queue
// This table provides a mapping from top level outer content window id
// to a queue of pending transaction information.
// Note that the window id could be 0 if the http request
// is initialized without a window.
nsClassHashtable<nsUint64HashKey,
nsTArray<RefPtr<PendingTransactionInfo>>> mPendingTransactionTable;
nsTArray<RefPtr<nsHttpConnection> > mActiveConns; // active connections
nsTArray<RefPtr<nsHttpConnection> > mIdleConns; // idle persistent connections
nsTArray<nsHalfOpenSocket*> mHalfOpens; // half open connections
@ -269,6 +275,33 @@ private:
void RecordIPFamilyPreference(uint16_t family);
// Resets all flags to their default values
void ResetIPFamilyPreference();
// Return the count of pending transactions for all window ids.
size_t PendingQLength() const;
// Add a transaction information into the pending queue in
// |mPendingTransactionTable| according to the transaction's
// top level outer content window id.
void InsertTransaction(PendingTransactionInfo *info);
// Append transactions to the |result| whose window id
// is equal to |windowId|.
// NOTE: maxCount == 0 will get all transactions in the queue.
void AppendPendingQForFocusedWindow(
uint64_t windowId,
nsTArray<RefPtr<PendingTransactionInfo>> &result,
uint32_t maxCount = 0);
// Append transactions whose window id isn't equal to |windowId|.
// NOTE: windowId == 0 will get all transactions for both
// focused and non-focused windows.
void AppendPendingQForNonFocusedWindows(
uint64_t windowId,
nsTArray<RefPtr<PendingTransactionInfo>> &result,
uint32_t maxCount = 0);
// Remove the empty pendingQ in |mPendingTransactionTable|.
void RemoveEmptyPendingQ();
};
public:
@ -327,8 +360,14 @@ private:
void PrintDiagnostics(nsCString &log);
private:
// To find out whether |mTransaction| is still in the connection entry's
// pending queue. If the transaction is found and |removeWhenFound| is
// true, the transaction will be removed from the pending queue.
already_AddRefed<PendingTransactionInfo>
FindTransactionHelper(bool removeWhenFound);
nsConnectionEntry *mEnt;
RefPtr<nsAHttpTransaction> mTransaction;
RefPtr<nsAHttpTransaction> mTransaction;
bool mDispatchedMTransaction;
nsCOMPtr<nsISocketTransport> mSocketTransport;
nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
@ -419,10 +458,14 @@ private:
MOZ_MUST_USE bool ProcessPendingQForEntry(nsConnectionEntry *,
bool considerAll);
void DispatchPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
bool DispatchPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
nsConnectionEntry *ent,
bool &dispatchedSuccessfully,
bool considerAll);
// Given the connection entry, return the available count for creating
// new connections. Return 0 if the active connection count is
// exceeded |mMaxPersistConnsPerProxy| or |mMaxPersistConnsPerHost|.
uint32_t AvailableNewConnectionCount(nsConnectionEntry * ent);
bool AtActiveConnectionLimit(nsConnectionEntry *, uint32_t caps);
MOZ_MUST_USE nsresult TryDispatchTransaction(nsConnectionEntry *ent,
bool onlyReusedConnection,
@ -484,6 +527,14 @@ private:
int32_t iparam = 0,
ARefBase *vparam = nullptr);
// Used to close all transactions in the |pendingQ| with the given |reason|.
// Note that the |pendingQ| will be also cleared.
void CancelTransactionsHelper(
nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
const nsHttpConnectionInfo *ci,
const nsConnectionEntry *ent,
nsresult reason);
// message handlers
void OnMsgShutdown (int32_t, ARefBase *);
void OnMsgShutdownConfirm (int32_t, ARefBase *);

Просмотреть файл

@ -238,6 +238,7 @@ nsHttpHandler::nsHttpHandler()
, mKeepEmptyResponseHeadersAsEmtpyString(false)
, mDefaultHpackBuffer(4096)
, mMaxHttpResponseHeaderSize(393216)
, mFocusedWindowTransactionRatio(0.9f)
{
LOG(("Creating nsHttpHandler [this=%p].\n", this));
@ -1448,6 +1449,19 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
mMaxHttpResponseHeaderSize = val;
}
}
if (PREF_CHANGED(HTTP_PREF("focused_window_transaction_ratio"))) {
float ratio = 0;
rv = prefs->GetFloatPref(HTTP_PREF("focused_window_transaction_ratio"), &ratio);
if (NS_SUCCEEDED(rv)) {
if (ratio > 0 && ratio < 1) {
mFocusedWindowTransactionRatio = ratio;
} else {
NS_WARNING("Wrong value for focused_window_transaction_ratio");
}
}
}
//
// INTL options
//

Просмотреть файл

@ -358,6 +358,11 @@ public:
return mMaxHttpResponseHeaderSize;
}
float FocusedWindowTransactionRatio() const
{
return mFocusedWindowTransactionRatio;
}
private:
virtual ~nsHttpHandler();
@ -566,6 +571,9 @@ private:
// The max size (in bytes) for received Http response header.
uint32_t mMaxHttpResponseHeaderSize;
// The ratio for dispatching transactions from the focused window.
float mFocusedWindowTransactionRatio;
private:
// For Rate Pacing Certain Network Events. Only assign this pointer on
// socket thread.

Просмотреть файл

@ -171,7 +171,7 @@ public:
MOZ_MUST_USE bool Do0RTT() override;
MOZ_MUST_USE nsresult Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */) override;
uint64_t TopLevelOuterContentWindowId()
uint64_t TopLevelOuterContentWindowId() override
{
return mTopLevelOuterContentWindowId;
}

Просмотреть файл

@ -469,4 +469,13 @@ interface nsIHttpChannel : nsIChannel
* should only be relied on after the channel has established a connection.
*/
[infallible] readonly attribute boolean isTrackingResource;
/**
* ID of the top-level outer content window. Identifies this channel's
* top-level window it comes from.
*
* NOTE: The setter of this attribute is currently for xpcshell test only.
* Don't alter it otherwise.
*/
[must_use] attribute uint64_t topLevelOuterContentWindowId;
};

Просмотреть файл

@ -739,6 +739,20 @@ nsViewSourceChannel::SetTopLevelContentWindowId(uint64_t aWindowId)
mHttpChannel->SetTopLevelContentWindowId(aWindowId);
}
NS_IMETHODIMP
nsViewSourceChannel::GetTopLevelOuterContentWindowId(uint64_t *aWindowId)
{
return !mHttpChannel ? NS_ERROR_NULL_POINTER :
mHttpChannel->GetTopLevelOuterContentWindowId(aWindowId);
}
NS_IMETHODIMP
nsViewSourceChannel::SetTopLevelOuterContentWindowId(uint64_t aWindowId)
{
return !mHttpChannel ? NS_ERROR_NULL_POINTER :
mHttpChannel->SetTopLevelOuterContentWindowId(aWindowId);
}
NS_IMETHODIMP
nsViewSourceChannel::GetIsTrackingResource(bool* aIsTrackingResource)
{

Просмотреть файл

@ -69,6 +69,7 @@ function urgentStartHttpRequest(id) {
}
function setup_httpRequests() {
log("setup_httpRequests");
for (var i = 0; i < maxConnections ; i++) {
commonHttpRequest(i);
do_test_pending();
@ -105,10 +106,12 @@ HttpResponseListener.prototype =
var responseQueue = new Array();
function setup_http_server()
{
log("setup_http_server");
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
maxConnections = prefs.getIntPref("network.http.max-persistent-connections-per-server");
urgentRequests = 2;
totalRequests = maxConnections + urgentRequests;
var allCommonHttpRequestReceived = false;
// Start server; will be stopped at test cleanup time.
server.registerPathHandler('/', function(metadata, response)
{
@ -116,6 +119,11 @@ function setup_http_server()
log("Server recived the response id=" + id);
response.processAsync();
responseQueue.push(response);
if (responseQueue.length == maxConnections && !allCommonHttpRequestReceived) {
allCommonHttpRequestReceived = true;
setup_urgentStartRequests();
}
// Wait for all expected requests to come but don't process then.
// Collect them in a queue for later processing. We don't want to
// respond to the client until all the expected requests are made
@ -140,6 +148,5 @@ function processResponse() {
function run_test() {
setup_http_server();
setup_httpRequests();
setup_urgentStartRequests();
}

Просмотреть файл

@ -0,0 +1,181 @@
// test bug 1312782.
//
// Summary:
// Assume we have 6 http requests in queue, 4 are from the focused window and
// the other 2 are from the non-focused window. We want to test that the server
// should receive 4 requests from the focused window first and then receive the
// rest 2 requests.
//
// Test step:
// 1. Create 6 dummy http requests. Server would not process responses until get
// all 6 requests.
// 2. Once server receive 6 dummy requests, create 4 http requests with the focused
// window id and 2 requests with non-focused window id. Note that the requets's
// id is a serial number starting from the focused window id.
// 3. Server starts to process the 6 dummy http requests, so the client can start to
// process the pending queue. Server will queue those http requests again and wait
// until get all 6 requests.
// 4. When the server receive all 6 requests, starts to check that the request ids of
// the first 4 requests in the queue should be all less than focused window id
// plus 4. Also, the request ids of the rest requests should be less than non-focused
// window id + 2.
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
var Cc = Components.classes;
var Ci = Components.interfaces;
var server = new HttpServer();
server.start(-1);
var baseURL = "http://localhost:" + server.identity.primaryPort + "/";
var maxConnections = 0;
var debug = false;
const FOCUSED_WINDOW_ID = 123;
var NON_FOCUSED_WINDOW_ID;
var FOCUSED_WINDOW_REQUEST_COUNT;
var NON_FOCUSED_WINDOW_REQUEST_COUNT;
function log(msg) {
if (!debug) {
return;
}
if (msg) {
dump("TEST INFO | " + msg + "\n");
}
}
function make_channel(url) {
var request = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
request.QueryInterface(Ci.nsIHttpChannel);
return request;
}
function serverStopListener() {
server.stop();
}
function createHttpRequest(windowId, requestId) {
let uri = baseURL;
var chan = make_channel(uri);
chan.topLevelOuterContentWindowId = windowId;
var listner = new HttpResponseListener(requestId);
chan.setRequestHeader("X-ID", requestId, false);
chan.setRequestHeader("Cache-control", "no-store", false);
chan.asyncOpen2(listner);
log("Create http request id=" + requestId);
}
function setup_dummyHttpRequests() {
log("setup_dummyHttpRequests");
for (var i = 0; i < maxConnections ; i++) {
createHttpRequest(0, i);
do_test_pending();
}
}
function setup_focusedWindowHttpRequests() {
log("setup_focusedWindowHttpRequests");
for (var i = 0; i < FOCUSED_WINDOW_REQUEST_COUNT ; i++) {
createHttpRequest(FOCUSED_WINDOW_ID, FOCUSED_WINDOW_ID + i);
do_test_pending();
}
}
function setup_nonFocusedWindowHttpRequests() {
log("setup_nonFocusedWindowHttpRequests");
for (var i = 0; i < NON_FOCUSED_WINDOW_REQUEST_COUNT ; i++) {
createHttpRequest(NON_FOCUSED_WINDOW_ID, NON_FOCUSED_WINDOW_ID + i);
do_test_pending();
}
}
function HttpResponseListener(id)
{
this.id = id
};
HttpResponseListener.prototype =
{
onStartRequest: function (request, ctx) {
},
onDataAvailable: function (request, ctx, stream, off, cnt) {
},
onStopRequest: function (request, ctx, status) {
log("STOP id=" + this.id);
do_test_finished();
}
};
function check_response_id(responses, maxWindowId)
{
for (var i = 0; i < responses.length; i++) {
var id = responses[i].getHeader("X-ID");
log("response id=" + id + " maxWindowId=" + maxWindowId);
do_check_true(id < maxWindowId);
}
}
var responseQueue = new Array();
function setup_http_server()
{
log("setup_http_server");
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
maxConnections = prefs.getIntPref("network.http.max-persistent-connections-per-server");
FOCUSED_WINDOW_REQUEST_COUNT = Math.floor(maxConnections * 0.8);
NON_FOCUSED_WINDOW_REQUEST_COUNT = maxConnections - FOCUSED_WINDOW_REQUEST_COUNT;
NON_FOCUSED_WINDOW_ID = FOCUSED_WINDOW_ID + FOCUSED_WINDOW_REQUEST_COUNT;
var allDummyHttpRequestReceived = false;
// Start server; will be stopped at test cleanup time.
server.registerPathHandler('/', function(metadata, response)
{
var id = metadata.getHeader("X-ID");
log("Server recived the response id=" + id);
response.processAsync();
response.setHeader("X-ID", id);
responseQueue.push(response);
if (responseQueue.length == maxConnections && !allDummyHttpRequestReceived) {
log("received all dummy http requets");
allDummyHttpRequestReceived = true;
setup_nonFocusedWindowHttpRequests();
setup_focusedWindowHttpRequests();
processResponses();
} else if (responseQueue.length == maxConnections) {
var focusedWindowResponses = responseQueue.slice(0, FOCUSED_WINDOW_REQUEST_COUNT);
var nonFocusedWindowResponses = responseQueue.slice(FOCUSED_WINDOW_REQUEST_COUNT, responseQueue.length);
check_response_id(focusedWindowResponses, FOCUSED_WINDOW_ID + FOCUSED_WINDOW_REQUEST_COUNT);
check_response_id(nonFocusedWindowResponses, NON_FOCUSED_WINDOW_ID + NON_FOCUSED_WINDOW_REQUEST_COUNT);
processResponses();
}
});
do_register_cleanup(function() {
server.stop(serverStopListener);
});
}
function processResponses() {
while (responseQueue.length) {
var resposne = responseQueue.pop();
resposne.finish();
}
}
function run_test() {
setup_http_server();
setup_dummyHttpRequests();
var windowIdWrapper = Cc["@mozilla.org/supports-PRUint64;1"]
.createInstance(Ci.nsISupportsPRUint64);
windowIdWrapper.data = FOCUSED_WINDOW_ID;
var obsvc = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
obsvc.notifyObservers(windowIdWrapper, "net:current-toplevel-outer-content-windowid", null);
}

Просмотреть файл

@ -384,3 +384,4 @@ skip-if = os == "android"
[test_race_cache_network.js]
[test_channel_priority.js]
[test_bug1312774_http1.js]
[test_bug1312782_http1.js]

Просмотреть файл

@ -53,16 +53,19 @@
#if defined(MOZ_PROFILING) && \
(defined(GP_OS_windows) || defined(GP_OS_darwin))
# define HAVE_NATIVE_UNWIND
# define USE_NS_STACKWALK
#endif
// This should also work on ARM Linux, but not tested there yet.
#if defined(GP_PLAT_arm_android)
# define HAVE_NATIVE_UNWIND
# define USE_EHABI_STACKWALK
# include "EHABIStackWalk.h"
#endif
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux)
# define HAVE_NATIVE_UNWIND
# define USE_LUL_STACKWALK
# include "lul/LulMain.h"
# include "lul/platform-linux-lul.h"
@ -972,16 +975,14 @@ Tick(PS::LockRef aLock, ProfileBuffer* aBuffer, TickSample* aSample)
NotNull<PseudoStack*> stack = threadInfo.Stack();
#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
defined(USE_LUL_STACKWALK)
#if defined(HAVE_NATIVE_UNWIND)
if (gPS->FeatureStackWalk(aLock)) {
DoNativeBacktrace(aLock, aBuffer, aSample);
} else {
} else
#endif
{
DoSampleStackTrace(aLock, aBuffer, aSample);
}
#else
DoSampleStackTrace(aLock, aBuffer, aSample);
#endif
// Don't process the PeudoStack's markers if we're synchronously sampling the
// current thread.
@ -1461,16 +1462,6 @@ set_profiler_entries(PS::LockRef aLock, const char* aEntries)
return true;
}
static bool
is_native_unwinding_avail()
{
# if defined(HAVE_NATIVE_UNWIND)
return true;
#else
return false;
#endif
}
static void
profiler_usage(int aExitCode)
{
@ -1508,7 +1499,11 @@ profiler_usage(int aExitCode)
"\n"
" This platform %s native unwinding.\n"
"\n",
is_native_unwinding_avail() ? "supports" : "does not support\n"
#if defined(HAVE_NATIVE_UNWIND)
"supports"
#else
"does not support"
#endif
);
exit(aExitCode);
@ -2295,7 +2290,7 @@ profiler_get_features()
MOZ_RELEASE_ASSERT(gPS);
static const char* features[] = {
#if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
#if defined(HAVE_NATIVE_UNWIND)
// Walk the C++ stack.
"stackwalk",
#endif

Просмотреть файл

@ -111,24 +111,6 @@ public:
static tid_t GetCurrentId();
};
// ----------------------------------------------------------------------------
// HAVE_NATIVE_UNWIND
//
// Pseudo backtraces are available on all platforms. Native
// backtraces are available only on selected platforms. Breakpad is
// the only supported native unwinder. HAVE_NATIVE_UNWIND is set at
// build time to indicate whether native unwinding is possible on this
// platform.
#undef HAVE_NATIVE_UNWIND
#if defined(MOZ_PROFILING) && \
(defined(GP_OS_windows) || \
defined(GP_OS_darwin) || \
defined(GP_OS_linux) || \
defined(GP_PLAT_arm_android))
# define HAVE_NATIVE_UNWIND
#endif
// ----------------------------------------------------------------------------
// ProfilerState auxiliaries